Skip to content

Commit 92932c5

Browse files
committed
Merge branch 'feature-multidomain' of github.com:cypress-io/cypress into feature-multidomain
2 parents 4e140a2 + 16ad6b4 commit 92932c5

File tree

16 files changed

+371
-72
lines changed

16 files changed

+371
-72
lines changed

cli/types/cypress.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ declare namespace Cypress {
275275
* Currently executing test runnable instance.
276276
*/
277277
currentTest: {
278-
title: string,
278+
title: string
279279
titlePath: string[]
280280
}
281281

@@ -5612,6 +5612,7 @@ declare namespace Cypress {
56125612
}
56135613

56145614
interface LogConfig extends Timeoutable {
5615+
id: number
56155616
/** The JQuery element for the command. This will highlight the command in the main window when debugging */
56165617
$el: JQuery
56175618
/** The scope of the log entry. If child, will appear nested below parents, prefixed with '-' */

packages/driver/cypress/integration/e2e/multidomain_spec.ts

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,25 @@ describe('multidomain', () => {
2121
cy.get('a').click()
2222
})
2323

24-
it('runs synchronous commands in secondary domain', (done) => {
25-
expectTextMessage('From a secondary domain', done)
26-
24+
it('runs commands in secondary domain', () => {
2725
// @ts-ignore
2826
cy.switchToDomain('127.0.0.1:3501', () => {
29-
// @ts-ignore
30-
cy.now('get', '[data-cy="dom-check"]').then(($el) => {
31-
top.postMessage({ host: location.host, actual: $el.text() }, '*')
32-
})
27+
cy
28+
.get('[data-cy="dom-check"]')
29+
.invoke('text')
30+
.should('equal', 'From a secondary domain')
3331
})
34-
})
3532

36-
it('sets up window.Cypress in secondary domain', (done) => {
37-
expectTextMessage('Has window.Cypress', done)
33+
cy.log('after switchToDomain')
34+
})
3835

36+
it('sets up window.Cypress in secondary domain', () => {
3937
// @ts-ignore
4038
cy.switchToDomain('127.0.0.1:3501', () => {
41-
// @ts-ignore
42-
cy.now('get', '[data-cy="cypress-check"]').then(($el) => {
43-
top.postMessage({ host: location.host, actual: $el.text() }, '*')
44-
})
39+
cy
40+
.get('[data-cy="cypress-check"]')
41+
.invoke('text')
42+
.should('equal', 'Has window.Cypress')
4543
})
4644
})
4745

@@ -55,10 +53,7 @@ describe('multidomain', () => {
5553
top.postMessage({ host: location.host, actual: 'form:submitted' }, '*')
5654
})
5755

58-
// @ts-ignore
59-
cy.now('get', 'button[type=submit]').then(($el) => {
60-
$el.trigger('click')
61-
})
56+
cy.get('form').submit()
6257
})
6358
})
6459

@@ -71,8 +66,7 @@ describe('multidomain', () => {
7166
top.postMessage({ host: location.host, actual: 'window:before:unload' }, '*')
7267
})
7368

74-
// @ts-ignore
75-
cy.now('window').then((window) => {
69+
cy.window().then((window) => {
7670
window.location.href = '/fixtures/multidomain.html'
7771
})
7872
})
@@ -87,8 +81,7 @@ describe('multidomain', () => {
8781
top.postMessage({ host: location.host, actual: 'window:unload' }, '*')
8882
})
8983

90-
// @ts-ignore
91-
cy.now('window').then((window) => {
84+
cy.window().then((window) => {
9285
window.location.href = '/fixtures/multidomain.html'
9386
})
9487
})
@@ -103,8 +96,7 @@ describe('multidomain', () => {
10396
top.postMessage({ host: location.host, actual: 'navigation:changed' }, '*')
10497
})
10598

106-
// @ts-ignore
107-
cy.now('window').then((window) => {
99+
cy.window().then((window) => {
108100
window.location.hash = '#hashbrowns'
109101
})
110102
})
@@ -119,8 +111,7 @@ describe('multidomain', () => {
119111
top.postMessage({ host: location.host, actual: `window:alert ${text}` }, '*')
120112
})
121113

122-
// @ts-ignore
123-
cy.now('get', '[data-cy="alert"]').then(($el) => {
114+
cy.get('[data-cy="alert"]').then(($el) => {
124115
$el.trigger('click')
125116
})
126117
})
@@ -135,8 +126,7 @@ describe('multidomain', () => {
135126
top.postMessage({ host: location.host, actual: `window:confirm ${text}` }, '*')
136127
})
137128

138-
// @ts-ignore
139-
cy.now('get', '[data-cy="confirm"]').then(($el) => {
129+
cy.get('[data-cy="confirm"]').then(($el) => {
140130
$el.trigger('click')
141131
})
142132
})
@@ -157,7 +147,7 @@ describe('multidomain', () => {
157147
})
158148

159149
// @ts-ignore
160-
cy.now('get', '[data-cy="confirm"]').then(($el) => {
150+
cy.get('[data-cy="confirm"]').then(($el) => {
161151
$el.trigger('click')
162152
})
163153
})
@@ -179,7 +169,7 @@ describe('multidomain', () => {
179169
Cypress.on('window:confirm', () => {})
180170

181171
// @ts-ignore
182-
cy.now('get', '[data-cy="confirm"]').then(($el) => {
172+
cy.get('[data-cy="confirm"]').then(($el) => {
183173
$el.trigger('click')
184174
})
185175
})

packages/driver/src/cy/commands/navigation.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,9 @@ const stabilityChanged = (Cypress, state, config, stable) => {
340340
debug('waiting for window:load')
341341

342342
return new Promise((resolve) => {
343-
return cy.once('window:load', (e) => {
343+
const onWindowLoad = (e) => {
344+
cy.off('cross:domain:window:load', onCrossDomainWindowLoad)
345+
344346
// this prevents a log occurring when we navigate to about:blank inbetween tests
345347
if (!state('duringUserTestExecution')) return
346348

@@ -353,8 +355,19 @@ const stabilityChanged = (Cypress, state, config, stable) => {
353355
options._log.set('message', '--page loaded--').snapshot().end()
354356
}
355357

356-
return resolve()
357-
})
358+
resolve()
359+
}
360+
361+
const onCrossDomainWindowLoad = () => {
362+
cy.off('window:load', onWindowLoad)
363+
364+
options._log.set('message', '--page loaded--').snapshot().end()
365+
366+
resolve()
367+
}
368+
369+
cy.once('window:load', onWindowLoad)
370+
cy.once('cross:domain:window:load', onCrossDomainWindowLoad)
358371
})
359372
}
360373

packages/driver/src/cy/listeners.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ export default {
6565
callbacks.onBeforeUnload(e)
6666
})
6767

68+
addListener(contentWindow, 'load', (e) => {
69+
callbacks.onLoad(e)
70+
})
71+
6872
addListener(contentWindow, 'unload', (e) => {
6973
// when we unload we need to remove all of the event listeners
7074
removeAllListeners()
Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,39 @@
11
import Bluebird from 'bluebird'
2+
import $Log from '../../cypress/log'
3+
import { createDeferred } from '../../util/deferred'
4+
5+
const readiedDomains = {}
26

37
export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy, state: Cypress.State) {
48
Commands.addAll({
9+
// this is a stop-gap command temporarily in use until looking ahead for
10+
// switchToDomain, etc is implemented
511
anticipateMultidomain () {
12+
const domain = '127.0.0.1:3501'
13+
614
state('anticipateMultidomain', true)
715

816
return new Bluebird((resolve) => {
17+
// the spec bridge iframe only loads once, and that's when it sends
18+
// 'cross:domain:bridge:ready', so if we've already readied it,
19+
// there's no need to wait
20+
if (readiedDomains[domain]) {
21+
return resolve()
22+
}
23+
924
// @ts-ignore
1025
Cypress.once('cross:domain:bridge:ready', () => {
26+
readiedDomains[domain] = true
27+
1128
resolve()
1229
})
1330

14-
Cypress.action('cy:expect:domain', '127.0.0.1:3501')
31+
Cypress.action('cy:expect:domain', domain)
1532
})
1633
},
1734

35+
// this isn't fully implemented, but in place to be able to test out
36+
// the other parts of multidomain
1837
switchToDomain (domain, fn) {
1938
Cypress.log({
2039
name: 'switchToDomain',
@@ -23,7 +42,98 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy,
2342
end: true,
2443
})
2544

26-
Cypress.action('cy:cross:domain:message', 'run:in:domain', fn.toString())
45+
// these are proxy commands that represent real commands in a
46+
// secondary domain. this way, the queue runs in the primary domain
47+
// with all commands, making it possible to sync up timing for
48+
// the reporter command log, etc
49+
const commands = {}
50+
const logs = {}
51+
52+
const addCommand = (attrs) => {
53+
const deferred = createDeferred()
54+
55+
commands[attrs.id] = {
56+
deferred,
57+
name: attrs.name,
58+
}
59+
60+
attrs.fn = () => {
61+
// the real command running in the secondary domain handles its
62+
// own timeout
63+
// TODO: add a special, long timeout in case inter-domain
64+
// communication breaks down somehow
65+
// @ts-ignore
66+
cy.clearTimeout()
67+
68+
Cypress.action('cy:cross:domain:message', {
69+
message: 'run:command',
70+
})
71+
72+
return deferred.promise
73+
}
74+
75+
Cypress.action('cy:enqueue:command', attrs)
76+
}
77+
78+
const updateCommand = (details) => {
79+
if (details.logAdded) {
80+
const attrs = details.logAdded
81+
82+
attrs.consoleProps = () => details.logAdded.consoleProps
83+
attrs.renderProps = () => details.logAdded.renderProps
84+
85+
const log = Cypress.log(attrs)
86+
87+
logs[log.get('id')] = log
88+
89+
return
90+
}
91+
92+
if (details.logChanged) {
93+
const log = logs[details.logChanged.id]
94+
95+
if (log) {
96+
log.set(details.logChanged)
97+
}
98+
99+
return
100+
}
101+
102+
if (details.end) {
103+
const command = commands[details.id]
104+
105+
if (command) {
106+
delete commands[details.id]
107+
command.deferred.resolve()
108+
}
109+
}
110+
}
111+
112+
// @ts-ignore
113+
Cypress.on('cross:domain:command:enqueued', addCommand)
114+
// @ts-ignore
115+
Cypress.on('cross:domain:command:update', updateCommand)
116+
117+
return new Bluebird((resolve) => {
118+
// @ts-ignore
119+
Cypress.once('cross:domain:ran:domain:fn', () => {
120+
resolve()
121+
})
122+
123+
// @ts-ignore
124+
Cypress.once('cross:domain:queue:finished', () => {
125+
// @ts-ignore
126+
Cypress.off('cross:domain:command:enqueued', addCommand)
127+
// @ts-ignore
128+
Cypress.off('cross:domain:command:update', updateCommand)
129+
})
130+
131+
Cypress.action('cy:cross:domain:message', {
132+
message: 'run:domain:fn',
133+
logCounter: $Log.getCounter(),
134+
fn: fn.toString(),
135+
})
136+
})
27137
},
28138
})
29139
}

packages/driver/src/cypress.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,8 @@ class $Cypress {
203203
// or parsed. we have not received any custom commands
204204
// at this point
205205
onSpecWindow (specWindow, scripts) {
206-
const logFn = (...args) => {
207-
return this.log.apply(this, args)
208-
}
209-
210206
// create cy and expose globally
211-
this.cy = $Cy.create(specWindow, this, this.Cookies, this.state, this.config, logFn)
207+
this.cy = $Cy.create(specWindow, this, this.Cookies, this.state, this.config)
212208
window.cy = this.cy
213209
this.isCy = this.cy.isCy
214210
this.log = $Log.create(this, this.cy, this.state, this.config)
@@ -474,6 +470,9 @@ class $Cypress {
474470
case 'cy:command:end':
475471
return this.emit('command:end', ...args)
476472

473+
case 'cy:skipped:command:end':
474+
return this.emit('skipped:command:end', ...args)
475+
477476
case 'cy:command:retry':
478477
return this.emit('command:retry', ...args)
479478

@@ -486,6 +485,9 @@ class $Cypress {
486485
case 'cy:command:queue:end':
487486
return this.emit('command:queue:end')
488487

488+
case 'cy:enqueue:command':
489+
return this.emit('enqueue:command', ...args)
490+
489491
case 'cy:url:changed':
490492
return this.emit('url:changed', args[0])
491493

@@ -554,6 +556,21 @@ class $Cypress {
554556
case 'runner:cross:domain:bridge:ready':
555557
return this.emit('cross:domain:bridge:ready')
556558

559+
case 'runner:cross:domain:window:load':
560+
return this.emit('cross:domain:window:load')
561+
562+
case 'runner:cross:domain:ran:domain:fn':
563+
return this.emit('cross:domain:ran:domain:fn')
564+
565+
case 'runner:cross:domain:queue:finished':
566+
return this.emit('cross:domain:queue:finished')
567+
568+
case 'runner:cross:domain:command:enqueued':
569+
return this.emit('cross:domain:command:enqueued', ...args)
570+
571+
case 'runner:cross:domain:command:update':
572+
return this.emit('cross:domain:command:update', ...args)
573+
557574
case 'cy:cross:domain:message':
558575
return this.emit('cross:domain:message', ...args)
559576

packages/driver/src/cypress/chainer.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ export class $Chainer {
66
constructor (userInvocationStack, specWindow) {
77
this.userInvocationStack = userInvocationStack
88
this.specWindow = specWindow
9-
this.chainerId = _.uniqueId('chainer')
9+
// the id prefix needs to be unique per domain, so there are not
10+
// collisions when chainers created in a secondary domain are passed
11+
// to the primary domain for the command log, etc.
12+
this.chainerId = _.uniqueId(`ch-${window.location.origin}-`)
1013
this.firstCall = true
1114
}
1215

0 commit comments

Comments
 (0)