Skip to content
This repository was archived by the owner on Mar 5, 2022. It is now read-only.

Commit 78dad0d

Browse files
committed
chore: move files around
1 parent bc9e239 commit 78dad0d

File tree

5 files changed

+169
-198
lines changed

5 files changed

+169
-198
lines changed

cypress/fixtures/modules.json

Lines changed: 0 additions & 12 deletions
This file was deleted.

cypress/support/commands.js

Lines changed: 0 additions & 155 deletions
This file was deleted.

cypress/support/index.js

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,3 @@
1-
/*
2-
Before All
3-
- Load and cache UMD modules specified in fixtures/modules.json
4-
These scripts are inlined in the document during unit tests
5-
modules.json should be an array, which implicitly sets the loading order
6-
Format: [{name, type, location}, ...]
7-
*/
8-
before(() => {
9-
Cypress.modules = []
10-
cy.log('Initializing UMD module cache')
11-
.fixture('modules')
12-
.then((modules = []) => {
13-
for (const module of modules) {
14-
let { name, type, location } = module
15-
cy.log(`Loading ${name} via ${type}`)
16-
.readFile(location)
17-
.then(source => Cypress.modules.push({ name, type, location, source }))
18-
}
19-
})
20-
})
21-
221
// ***********************************************************
232
// This example support/index.js is processed and
243
// loaded automatically before your test files.
@@ -34,8 +13,4 @@ before(() => {
3413
// https://on.cypress.io/configuration
3514
// ***********************************************************
3615

37-
// Import commands.js using ES2015 syntax:
38-
import './commands'
39-
40-
// Alternatively you can use CommonJS syntax:
41-
// require('./commands')
16+
import '../../lib'

lib/index.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// I hope to get types and docs from functions imported from ./commands one day
1+
// I hope to get types and docs from functions imported from ./index one day
22
// but for now have to document methods in both places
3-
// like this: import {mount} from './commands'
3+
// like this: import {mount} from './index'
44

55
declare namespace Cypress {
66
interface Cypress {

lib/index.js

Lines changed: 166 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,179 @@
11
// having weak reference to styles prevents garbage collection
22
// and "losing" styles when the next test starts
3-
export const stylesCache = new Map()
3+
// import { stylesCache, setXMLHttpRequest, setAlert } from './index'
44

5-
export const setXMLHttpRequest = (w) => {
5+
const stylesCache = new Map()
6+
7+
const setXMLHttpRequest = w => {
68
// by grabbing the XMLHttpRequest from app's iframe
79
// and putting it here - in the test iframe
810
// we suddenly get spying and stubbing 😁
911
window.XMLHttpRequest = w.XMLHttpRequest
1012
return w
1113
}
1214

13-
export const setAlert = (w) => {
15+
const setAlert = w => {
1416
window.alert = w.alert
1517
return w
1618
}
19+
20+
/** Initialize an empty document w/ ReactDOM and DOM events.
21+
@function cy.injectReactDOM
22+
**/
23+
Cypress.Commands.add('injectReactDOM', () => {
24+
return cy.log('Injecting ReactDOM for Unit Testing').then(() => {
25+
// Generate inline script tags for UMD modules
26+
const scripts = Cypress.modules
27+
.map(module => `<script>${module.source}</script>`)
28+
.join('')
29+
// include React and ReactDOM to force DOM to register all DOM event listeners
30+
// otherwise the component will NOT be able to dispatch any events
31+
// when it runs the second time
32+
// https://github.com/bahmutov/cypress-react-unit-test/issues/3
33+
var html = `<body>
34+
<div id="cypress-jsdom"></div>
35+
${scripts}
36+
</body>`
37+
const document = cy.state('document')
38+
document.write(html)
39+
document.close()
40+
})
41+
})
42+
43+
cy.stylesCache = stylesCache
44+
/** Caches styles from previously compiled components for reuse
45+
@function cy.copyComponentStyles
46+
@param {Object} component
47+
**/
48+
Cypress.Commands.add('copyComponentStyles', component => {
49+
// need to find same component when component is recompiled
50+
// by the JSX preprocessor. Thus have to use something else,
51+
// like component name
52+
const hash = component.type.name
53+
const document = cy.state('document')
54+
let styles = document.querySelectorAll('head style')
55+
if (styles.length) {
56+
cy.log('injected %d styles', styles.length)
57+
cy.stylesCache.set(hash, styles)
58+
} else {
59+
cy.log('No styles injected for this component, checking cache')
60+
if (cy.stylesCache.has(hash)) {
61+
styles = cy.stylesCache.get(hash)
62+
} else {
63+
styles = null
64+
}
65+
}
66+
if (!styles) {
67+
return
68+
}
69+
const parentDocument = window.parent.document
70+
const projectName = Cypress.config('projectName')
71+
const appIframeId = "Your App: '" + projectName + "'"
72+
const appIframe = parentDocument.getElementById(appIframeId)
73+
var head = appIframe.contentDocument.querySelector('head')
74+
styles.forEach(function (style) {
75+
head.appendChild(style)
76+
})
77+
})
78+
79+
/**
80+
* Mount a React component in a blank document; register it as an alias
81+
* To access: use an alias or original component reference
82+
* @function cy.mount
83+
* @param {Object} jsx - component to mount
84+
* @param {string} [Component] - alias to use later
85+
* @example
86+
```
87+
import Hello from './hello.jsx'
88+
// mount and access by alias
89+
cy.mount(<Hello />, 'Hello')
90+
// using default alias
91+
cy.get('@Component')
92+
// using specified alias
93+
cy.get('@Hello').its('state').should(...)
94+
// using original component
95+
cy.get(Hello)
96+
```
97+
**/
98+
export const mount = (jsx, alias) => {
99+
// Get the display name property via the component constructor
100+
const displayname = alias || jsx.type.prototype.constructor.name
101+
cy.injectReactDOM()
102+
.log(`ReactDOM.render(<${displayname} ... />)`, jsx.props)
103+
.window({ log: false })
104+
.then(setXMLHttpRequest)
105+
.then(setAlert)
106+
.then(win => {
107+
const { ReactDOM } = win
108+
const document = cy.state('document')
109+
const component = ReactDOM.render(
110+
jsx,
111+
document.getElementById('cypress-jsdom')
112+
)
113+
cy.wrap(component, { log: false }).as(displayname)
114+
})
115+
cy.copyComponentStyles(jsx)
116+
}
117+
118+
Cypress.Commands.add('mount', mount)
119+
120+
/** Get one or more DOM elements by selector or alias.
121+
Features extended support for JSX and React.Component
122+
@function cy.get
123+
@param {string|object|function} selector
124+
@param {object} options
125+
@example cy.get('@Component')
126+
@example cy.get(<Component />)
127+
@example cy.get(Component)
128+
**/
129+
Cypress.Commands.overwrite('get', (originalFn, selector, options) => {
130+
switch (typeof selector) {
131+
case 'object':
132+
// If attempting to use JSX as a selector, reference the displayname
133+
if (
134+
selector.$$typeof &&
135+
selector.$$typeof.toString().startsWith('Symbol(react')
136+
) {
137+
const displayname = selector.type.prototype.constructor.name
138+
return originalFn(`@${displayname}`, options)
139+
}
140+
case 'function':
141+
// If attempting to use the component name without JSX (testing in .js/.ts files)
142+
const displayname = selector.prototype.constructor.name
143+
return originalFn(`@${displayname}`, options)
144+
default:
145+
return originalFn(selector, options)
146+
}
147+
})
148+
149+
const moduleNames = [
150+
{
151+
name: 'react',
152+
type: 'file',
153+
location: 'node_modules/react/umd/react.development.js'
154+
},
155+
{
156+
name: 'react-dom',
157+
type: 'file',
158+
location: 'node_modules/react-dom/umd/react-dom.development.js'
159+
}
160+
]
161+
162+
/*
163+
Before All
164+
- Load and cache UMD modules specified in fixtures/modules.json
165+
These scripts are inlined in the document during unit tests
166+
modules.json should be an array, which implicitly sets the loading order
167+
Format: [{name, type, location}, ...]
168+
*/
169+
before(() => {
170+
Cypress.modules = []
171+
cy.log('Initializing UMD module cache').then(() => {
172+
for (const module of moduleNames) {
173+
let { name, type, location } = module
174+
cy.log(`Loading ${name} via ${type}`)
175+
.readFile(location)
176+
.then(source => Cypress.modules.push({ name, type, location, source }))
177+
}
178+
})
179+
})

0 commit comments

Comments
 (0)