cypress-react-unit-test
Advanced tools
Comparing version 2.2.0 to 2.3.0
"use strict"; | ||
/// <reference path="./index.d.ts" /> | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
// having weak reference to styles prevents garbage collection | ||
// and "losing" styles when the next test starts | ||
exports.stylesCache = new Map(); | ||
exports.setXMLHttpRequest = function (w) { | ||
var stylesCache = new Map(); | ||
var setXMLHttpRequest = function (w) { | ||
// by grabbing the XMLHttpRequest from app's iframe | ||
// and putting it here - in the test iframe | ||
// we suddenly get spying and stubbing 😁 | ||
// @ts-ignore | ||
window.XMLHttpRequest = w.XMLHttpRequest; | ||
return w; | ||
}; | ||
exports.setAlert = function (w) { | ||
var setAlert = function (w) { | ||
window.alert = w.alert; | ||
return w; | ||
}; | ||
/** Initialize an empty document w/ ReactDOM and DOM events. | ||
@function cy.injectReactDOM | ||
**/ | ||
Cypress.Commands.add('injectReactDOM', function () { | ||
return cy.log('Injecting ReactDOM for Unit Testing').then(function () { | ||
// Generate inline script tags for UMD modules | ||
var scripts = Cypress.modules | ||
.map(function (module) { return "<script>" + module.source + "</script>"; }) | ||
.join(''); | ||
// include React and ReactDOM to force DOM to register all DOM event listeners | ||
// otherwise the component will NOT be able to dispatch any events | ||
// when it runs the second time | ||
// https://github.com/bahmutov/cypress-react-unit-test/issues/3 | ||
var html = "<body>\n <div id=\"cypress-jsdom\"></div>\n " + scripts + "\n </body>"; | ||
var document = cy.state('document'); | ||
document.write(html); | ||
document.close(); | ||
}); | ||
}); | ||
Cypress.stylesCache = stylesCache; | ||
/** Caches styles from previously compiled components for reuse | ||
@function cy.copyComponentStyles | ||
@param {Object} component | ||
**/ | ||
Cypress.Commands.add('copyComponentStyles', function (component) { | ||
// need to find same component when component is recompiled | ||
// by the JSX preprocessor. Thus have to use something else, | ||
// like component name | ||
var hash = component.type.name; | ||
var document = cy.state('document'); | ||
var styles = document.querySelectorAll('head style'); | ||
if (styles.length) { | ||
cy.log('injected %d styles', styles.length); | ||
Cypress.stylesCache.set(hash, styles); | ||
} | ||
else { | ||
cy.log('No styles injected for this component, checking cache'); | ||
if (Cypress.stylesCache.has(hash)) { | ||
styles = Cypress.stylesCache.get(hash); | ||
} | ||
else { | ||
styles = null; | ||
} | ||
} | ||
if (!styles) { | ||
return; | ||
} | ||
var parentDocument = window.parent.document; | ||
// hmm, is this really project name? | ||
// @ts-ignore | ||
var projectName = Cypress.config('projectName'); | ||
var appIframeId = "Your App: '" + projectName + "'"; | ||
var appIframe = parentDocument.getElementById(appIframeId); | ||
// @ts-ignore | ||
var head = appIframe.contentDocument.querySelector('head'); | ||
styles.forEach(function (style) { | ||
head.appendChild(style); | ||
}); | ||
}); | ||
/** | ||
* Mount a React component in a blank document; register it as an alias | ||
* To access: use an alias or original component reference | ||
* @function cy.mount | ||
* @param {Object} jsx - component to mount | ||
* @param {string} [Component] - alias to use later | ||
* @example | ||
``` | ||
import Hello from './hello.jsx' | ||
// mount and access by alias | ||
cy.mount(<Hello />, 'Hello') | ||
// using default alias | ||
cy.get('@Component') | ||
// using specified alias | ||
cy.get('@Hello').its('state').should(...) | ||
// using original component | ||
cy.get(Hello) | ||
``` | ||
**/ | ||
exports.mount = function (jsx, alias) { | ||
// Get the display name property via the component constructor | ||
var displayname = alias || jsx.type.prototype.constructor.name; | ||
cy.injectReactDOM() | ||
.log("ReactDOM.render(<" + displayname + " ... />)", jsx.props) | ||
.window({ log: false }) | ||
.then(setXMLHttpRequest) | ||
.then(setAlert) | ||
.then(function (win) { | ||
var ReactDOM = win.ReactDOM; | ||
var document = cy.state('document'); | ||
var component = ReactDOM.render(jsx, document.getElementById('cypress-jsdom')); | ||
cy.wrap(component, { log: false }).as(displayname); | ||
}); | ||
cy.copyComponentStyles(jsx); | ||
}; | ||
Cypress.Commands.add('mount', exports.mount); | ||
/** Get one or more DOM elements by selector or alias. | ||
Features extended support for JSX and React.Component | ||
@function cy.get | ||
@param {string|object|function} selector | ||
@param {object} options | ||
@example cy.get('@Component') | ||
@example cy.get(<Component />) | ||
@example cy.get(Component) | ||
**/ | ||
Cypress.Commands.overwrite('get', function (originalFn, selector, options) { | ||
switch (typeof selector) { | ||
case 'object': | ||
// If attempting to use JSX as a selector, reference the displayname | ||
if (selector.$$typeof && | ||
selector.$$typeof.toString().startsWith('Symbol(react')) { | ||
var displayname_1 = selector.type.prototype.constructor.name; | ||
return originalFn("@" + displayname_1, options); | ||
} | ||
case 'function': | ||
// If attempting to use the component name without JSX (testing in .js/.ts files) | ||
var displayname = selector.prototype.constructor.name; | ||
return originalFn("@" + displayname, options); | ||
default: | ||
return originalFn(selector, options); | ||
} | ||
}); | ||
var moduleNames = [ | ||
{ | ||
name: 'react', | ||
type: 'file', | ||
location: 'node_modules/react/umd/react.development.js' | ||
}, | ||
{ | ||
name: 'react-dom', | ||
type: 'file', | ||
location: 'node_modules/react-dom/umd/react-dom.development.js' | ||
} | ||
]; | ||
/* | ||
Before All | ||
- Load and cache UMD modules specified in fixtures/modules.json | ||
These scripts are inlined in the document during unit tests | ||
modules.json should be an array, which implicitly sets the loading order | ||
Format: [{name, type, location}, ...] | ||
*/ | ||
before(function () { | ||
Cypress.modules = []; | ||
cy.log('Initializing UMD module cache').then(function () { | ||
var _loop_1 = function (module) { | ||
var name_1 = module.name, type = module.type, location_1 = module.location; | ||
cy.log("Loading " + name_1 + " via " + type) | ||
.readFile(location_1) | ||
.then(function (source) { return Cypress.modules.push({ name: name_1, type: type, location: location_1, source: source }); }); | ||
}; | ||
for (var _i = 0, moduleNames_1 = moduleNames; _i < moduleNames_1.length; _i++) { | ||
var module = moduleNames_1[_i]; | ||
_loop_1(module); | ||
} | ||
}); | ||
}); |
@@ -1,5 +0,12 @@ | ||
// I hope to get types and docs from functions imported from ./commands one day | ||
// I hope to get types and docs from functions imported from ./index one day | ||
// but for now have to document methods in both places | ||
// like this: import {mount} from './commands' | ||
// like this: import {mount} from './index' | ||
interface ReactModule { | ||
name: string | ||
type: string | ||
location: string | ||
source: string | ||
} | ||
declare namespace Cypress { | ||
@@ -11,6 +18,9 @@ interface Cypress { | ||
Styles: string | ||
modules: ReactModule[] | ||
} | ||
// NOTE: By default, avoiding React.Component/Element typings | ||
// for many cases, we don't want to import @types/react | ||
interface Chainable<Subject = any> { | ||
interface Chainable<Subject> { | ||
state: (key) => any, | ||
injectReactDOM: () => Chainable<void> | ||
@@ -17,0 +27,0 @@ copyComponentStyles: (component: Symbol) => Chainable<void> |
{ | ||
"name": "cypress-react-unit-test", | ||
"version": "2.2.0", | ||
"version": "2.3.0", | ||
"description": "Unit test React components using Cypress", | ||
@@ -5,0 +5,0 @@ "main": "dist", |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
21133
392
1