mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-21 14:41:00 -06:00
chore: Update switchToDomain signature with args key (#20722)
* chore: update data argument to be object containing args key over being an array * chore: remove done_reference_mismatch in error_messages as done is now removed * fix: alias args as data going through communicator to keep common interface and exclude user serialized data * rename data references to options in switchToDomain * use isPlainObject to simplify conditional in validator * refactor switchToDomain options validation to check for invalid keys in options argument over missing the args key Co-authored-by: Matt Henkes <mjhenkes@gmail.com>
This commit is contained in:
10
cli/types/cypress.d.ts
vendored
10
cli/types/cypress.d.ts
vendored
@@ -1912,16 +1912,22 @@ declare namespace Cypress {
|
||||
* })
|
||||
*/
|
||||
switchToDomain(originOrDomain: string, fn: () => void): Chainable
|
||||
|
||||
// TODO: when we find other options to put into the 'data' argument of switchToDomain, we may want to overload this type with
|
||||
// a 'data' paramater that contains all data options, including args, and one that contains all data options, excluding args.
|
||||
// This will provide better typings support for whatever args is set to as opposed to an optional undefined
|
||||
/**
|
||||
* Enables running Cypress commands in a secondary domain
|
||||
* @see https://on.cypress.io/switchToDomain
|
||||
* @example
|
||||
* cy.switchToDomain('example.com', [{ key: 'value' }, 'foo'], ([{ key }, foo]) => {
|
||||
* cy.switchToDomain('example.com', { args: { key: 'value', foo: 'foo' } }, ({ key, foo }) => {
|
||||
* expect(key).to.equal('value')
|
||||
* expect(foo).to.equal('foo')
|
||||
* })
|
||||
*/
|
||||
switchToDomain<T>(originOrDomain: string, data: T[], fn: (data: T[]) => void): Chainable
|
||||
switchToDomain<T>(originOrDomain: string, options: {
|
||||
args: T
|
||||
}, fn: (args: T) => void): Chainable
|
||||
|
||||
/**
|
||||
* Run a task in Node via the plugins file.
|
||||
|
||||
@@ -830,17 +830,18 @@ namespace CypressKeyboardTests {
|
||||
|
||||
namespace CypressMultiDomainTests {
|
||||
cy.switchToDomain('example.com', () => {})
|
||||
cy.switchToDomain('example.com', [{}], (value: object[]) => {})
|
||||
cy.switchToDomain('example.com', [], (value: any[]) => {})
|
||||
cy.switchToDomain('example.com', [1, 'value', {}, true], (value: Array<string | number | boolean | {}>) => {})
|
||||
cy.switchToDomain('example.com', ['value'], (value: string[]) => {})
|
||||
cy.switchToDomain('example.com', [1], (value: number[]) => {})
|
||||
cy.switchToDomain('example.com', [true], (value: boolean[]) => {})
|
||||
cy.switchToDomain('example.com', { args: {}}, (value: object) => {})
|
||||
cy.switchToDomain('example.com', { args: { one: 1, key: 'value', bool: true } }, (value: { one: number, key: string, bool: boolean}) => {})
|
||||
cy.switchToDomain('example.com', { args: [1, 'value', true ] }, (value: Array<(number | string | boolean)>) => {})
|
||||
cy.switchToDomain('example.com', { args : 'value'}, (value: string) => {})
|
||||
cy.switchToDomain('example.com', { args: 1 }, (value: number) => {})
|
||||
cy.switchToDomain('example.com', { args: true }, (value: boolean) => {})
|
||||
|
||||
cy.switchToDomain() // $ExpectError
|
||||
cy.switchToDomain('example.com') // $ExpectError
|
||||
cy.switchToDomain(true) // $ExpectError
|
||||
cy.switchToDomain('example.com', {}) // $ExpectError
|
||||
cy.switchToDomain('example.com', {}, {}) // $ExpectError
|
||||
cy.switchToDomain('example.com', ['value'], (value: boolean[]) => {}) // $ExpectError
|
||||
cy.switchToDomain('example.com', { args: ['value'] }, (value: boolean[]) => {}) // $ExpectError
|
||||
cy.switchToDomain('example.com', {}, (value: undefined) => {}) // $ExpectError
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ describe('basic login', { experimentalSessionSupport: true }, () => {
|
||||
const login = (name) => {
|
||||
cy.session(name, () => {
|
||||
// Note, this assumes localhost is the primary domain, ideally we'd be able to specify this directly.
|
||||
cy.switchToDomain('http://idp.com:3500', [name], ([name]) => {
|
||||
cy.switchToDomain('http://idp.com:3500', { args: name }, (name) => {
|
||||
cy.visit('http://www.idp.com:3500/fixtures/auth/idp.html')
|
||||
cy.get('[data-cy="username"]').type(name)
|
||||
cy.get('[data-cy="login"]').click()
|
||||
|
||||
@@ -22,7 +22,7 @@ context('multi-domain screenshot', { experimentalSessionSupport: true }, () => {
|
||||
})
|
||||
|
||||
it('captures the fullPage', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [this.serverResult], ([serverResult]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
const automationStub = cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
|
||||
cy.screenshot({ capture: 'fullPage' })
|
||||
@@ -36,7 +36,7 @@ context('multi-domain screenshot', { experimentalSessionSupport: true }, () => {
|
||||
})
|
||||
|
||||
it('captures the runner', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [this.serverResult], ([serverResult]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
const automationStub = cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
|
||||
cy.screenshot({ capture: 'runner' })
|
||||
@@ -48,7 +48,7 @@ context('multi-domain screenshot', { experimentalSessionSupport: true }, () => {
|
||||
})
|
||||
|
||||
it('captures the viewport', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [this.serverResult], ([serverResult]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
const automationStub = cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
|
||||
cy.screenshot({ capture: 'viewport' })
|
||||
@@ -80,7 +80,7 @@ context('multi-domain screenshot', { experimentalSessionSupport: true }, () => {
|
||||
})
|
||||
|
||||
it('supports multiple titles', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [this.serverResult], ([serverResult]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
const automationStub = cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
|
||||
cy.screenshot()
|
||||
@@ -91,7 +91,7 @@ context('multi-domain screenshot', { experimentalSessionSupport: true }, () => {
|
||||
})
|
||||
|
||||
it('supports the blackout option', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [this.serverResult], ([serverResult]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
|
||||
cy.screenshot({
|
||||
@@ -109,7 +109,7 @@ context('multi-domain screenshot', { experimentalSessionSupport: true }, () => {
|
||||
})
|
||||
|
||||
it('supports element screenshots', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [this.serverResult], ([serverResult]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
const automationStub = cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
|
||||
cy.get('.tall-element').screenshot()
|
||||
@@ -121,7 +121,7 @@ context('multi-domain screenshot', { experimentalSessionSupport: true }, () => {
|
||||
})
|
||||
|
||||
it('supports screenshot retrying with appropriate naming', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [this.serverResult], ([serverResult]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
const automationStub = cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
|
||||
cy.state('runnable')._currentRetry = 2
|
||||
@@ -134,7 +134,7 @@ context('multi-domain screenshot', { experimentalSessionSupport: true }, () => {
|
||||
})
|
||||
|
||||
it('calls the onBeforeScreenshot callback', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [this.serverResult], ([serverResult]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
const onBeforeScreenshot = cy.stub()
|
||||
|
||||
@@ -144,7 +144,7 @@ context('multi-domain screenshot', { experimentalSessionSupport: true }, () => {
|
||||
})
|
||||
|
||||
it('calls the onAfterScreenshot callback', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [this.serverResult], ([serverResult]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
const onAfterScreenshot = cy.stub()
|
||||
|
||||
@@ -154,7 +154,7 @@ context('multi-domain screenshot', { experimentalSessionSupport: true }, () => {
|
||||
})
|
||||
|
||||
it('supports the Cypress.screenshot callbacks', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [this.serverResult], ([serverResult]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
const onAfterScreenshot = cy.stub()
|
||||
const onBeforeScreenshot = cy.stub()
|
||||
@@ -171,7 +171,7 @@ context('multi-domain screenshot', { experimentalSessionSupport: true }, () => {
|
||||
})
|
||||
|
||||
it('supports pausing timers', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [this.serverResult], ([serverResult]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').returns(Cypress.Promise.delay(500, serverResult))
|
||||
|
||||
cy.window().then((win) => {
|
||||
@@ -204,7 +204,7 @@ context('multi-domain screenshot', { experimentalSessionSupport: true }, () => {
|
||||
})
|
||||
|
||||
it('does not pause timers when disableTimersAndAnimations is false', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [this.serverResult], ([serverResult]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').returns(Cypress.Promise.delay(500, serverResult))
|
||||
|
||||
cy.window().then((win) => {
|
||||
@@ -234,7 +234,7 @@ context('multi-domain screenshot', { experimentalSessionSupport: true }, () => {
|
||||
expect(err.message).to.include('setTimeout error after screenshot')
|
||||
})
|
||||
|
||||
cy.switchToDomain('http://foobar.com:3500', [this.serverResult], ([serverResult]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').returns(Cypress.Promise.delay(100, serverResult))
|
||||
|
||||
cy.window().then((win) => {
|
||||
@@ -259,7 +259,7 @@ context('multi-domain screenshot', { experimentalSessionSupport: true }, () => {
|
||||
expect(err.docsUrl).to.deep.eq(['https://on.cypress.io/uncaught-exception-from-application'])
|
||||
})
|
||||
|
||||
cy.switchToDomain('http://foobar.com:3500', [this.serverResult], ([serverResult]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').returns(Cypress.Promise.delay(100, serverResult))
|
||||
|
||||
cy.window().then((win) => {
|
||||
|
||||
@@ -42,8 +42,7 @@
|
||||
context('serializable', () => {
|
||||
it(`syncs initial Cypress.${fnName}() from the primary domain to the secondary (synchronously)`, () => {
|
||||
Cypress[fnName](USED_KEYS.foo, 'bar')
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
// @ts-ignore
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const bar = Cypress[fnName](USED_KEYS.foo)
|
||||
|
||||
expect(bar).to.equal('bar')
|
||||
@@ -52,8 +51,7 @@
|
||||
|
||||
it(`syncs serializable values in the Cypress.${fnName}() again to the secondary even after spec bridge is created`, () => {
|
||||
Cypress[fnName](USED_KEYS.foo, 'baz')
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
// @ts-ignore
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const baz = Cypress[fnName](USED_KEYS.foo)
|
||||
|
||||
expect(baz).to.equal('baz')
|
||||
@@ -61,8 +59,7 @@
|
||||
})
|
||||
|
||||
it(`syncs serializable Cypress.${fnName}() values outwards from secondary (synchronously)`, () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
// @ts-ignore
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
Cypress[fnName](USED_KEYS.bar, 'baz')
|
||||
}).then(() => {
|
||||
const baz = Cypress[fnName](USED_KEYS.bar)
|
||||
@@ -72,11 +69,9 @@
|
||||
})
|
||||
|
||||
it(`syncs serializable Cypress.${fnName}() values outwards from secondary even if the value is undefined`, () => {
|
||||
// @ts-ignore
|
||||
Cypress[fnName](USED_KEYS.foo, 'bar')
|
||||
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
// @ts-ignore
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
Cypress[fnName](USED_KEYS.foo, undefined)
|
||||
}).then(() => {
|
||||
expect(Cypress[fnName]('foo')).to.be.undefined
|
||||
@@ -84,9 +79,8 @@
|
||||
})
|
||||
|
||||
it(`syncs serializable Cypress.${fnName}() values outwards from secondary (commands/async)`, () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
cy.then(() => {
|
||||
// @ts-ignore
|
||||
Cypress[fnName](USED_KEYS.bar, 'quux')
|
||||
})
|
||||
}).then(() => {
|
||||
@@ -97,8 +91,7 @@
|
||||
})
|
||||
|
||||
it(`persists Cypress.${fnName}() changes made in the secondary, assuming primary has not overwritten with a serializable value`, () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
// @ts-ignore
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const quux = Cypress[fnName](USED_KEYS.bar)
|
||||
|
||||
expect(quux).to.equal('quux')
|
||||
@@ -112,14 +105,12 @@
|
||||
},
|
||||
}, () => {
|
||||
return new Promise<void>((resolve) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
setTimeout(() => {
|
||||
// this value STILL gets set, but will be blown away on the next switchToDomain with whatever exists in the primary
|
||||
// @ts-ignore
|
||||
Cypress[fnName](USED_KEYS.baz, 'qux')
|
||||
}, 100)
|
||||
|
||||
// @ts-ignore
|
||||
Cypress[fnName](USED_KEYS.baz, 'quux')
|
||||
}).then(() => {
|
||||
const quux = Cypress[fnName](USED_KEYS.baz)
|
||||
@@ -131,16 +122,14 @@
|
||||
})
|
||||
|
||||
it('overwrites different values in secondary if one exists in the primary', {
|
||||
// @ts-ignore
|
||||
[USED_KEYS.baz]: 'quux',
|
||||
env: {
|
||||
[USED_KEYS.baz]: 'quux',
|
||||
},
|
||||
}, () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
// in previous test, 'baz' was set to 'qux' after the callback window was closed.
|
||||
// this should be overwritten by 'quux' that exists in the primary
|
||||
// @ts-ignore
|
||||
const quux = Cypress[fnName](USED_KEYS.baz)
|
||||
|
||||
expect(quux).to.equal('quux')
|
||||
@@ -148,14 +137,12 @@
|
||||
})
|
||||
|
||||
it(`overwrites different values in secondary, even if the Cypress.${fnName}() value does not exist in the primary`, {
|
||||
// @ts-ignore
|
||||
[USED_KEYS.baz]: undefined,
|
||||
env: {
|
||||
[USED_KEYS.baz]: undefined,
|
||||
},
|
||||
}, () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
// @ts-ignore
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const isNowUndefined = Cypress[fnName](USED_KEYS.baz)
|
||||
|
||||
expect(isNowUndefined).to.be.undefined
|
||||
@@ -168,8 +155,7 @@
|
||||
|
||||
it('does not sync unserializable values from the primary to the secondary', () => {
|
||||
Cypress[fnName](USED_KEYS.unserializable, unserializableFunc)
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
// @ts-ignore
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const isUndefined = Cypress[fnName](USED_KEYS.unserializable)
|
||||
|
||||
expect(isUndefined).to.be.undefined
|
||||
@@ -187,8 +173,7 @@
|
||||
|
||||
it('overwrites unserializable values in the primary when serializable values of same key exist in secondary', () => {
|
||||
Cypress[fnName](USED_KEYS.unserializable, unserializableFunc)
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
// @ts-ignore
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
Cypress[fnName](USED_KEYS.unserializable, undefined)
|
||||
}).then(() => {
|
||||
const isNowUndefined = Cypress[fnName](USED_KEYS.unserializable)
|
||||
@@ -198,17 +183,15 @@
|
||||
})
|
||||
|
||||
it('overwrites unserializable values in the secondary when serializable values of same key exist in primary', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const unserializableFuncSecondary = () => undefined
|
||||
|
||||
// @ts-ignore
|
||||
Cypress[fnName](USED_KEYS.unserializable, unserializableFuncSecondary)
|
||||
}).then(() => {
|
||||
Cypress[fnName](USED_KEYS.unserializable, undefined)
|
||||
})
|
||||
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
// @ts-ignore
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const isUndefined = Cypress[fnName](USED_KEYS.unserializable)
|
||||
|
||||
expect(isUndefined).to.be.undefined
|
||||
@@ -221,10 +204,9 @@
|
||||
|
||||
it('does not overwrite unserializable values in the primary when unserializable values of same key exist in secondary', () => {
|
||||
Cypress[fnName](USED_KEYS.unserializable, unserializableFunc)
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const unserializableFuncSecondary = () => undefined
|
||||
|
||||
// @ts-ignore
|
||||
Cypress[fnName](USED_KEYS.unserializable, unserializableFuncSecondary)
|
||||
}).then(() => {
|
||||
const isFunc = Cypress[fnName](USED_KEYS.unserializable)
|
||||
@@ -241,13 +223,11 @@
|
||||
|
||||
Cypress[fnName](USED_KEYS.unserializable, partiallyUnserializableObject)
|
||||
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
// @ts-ignore
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const doesNotContainPartialAProperty = Cypress[fnName](USED_KEYS.unserializable)
|
||||
|
||||
expect(doesNotContainPartialAProperty?.a).to.be.undefined
|
||||
|
||||
// @ts-ignore
|
||||
Cypress[fnName](USED_KEYS.unserializable, {
|
||||
a: 3,
|
||||
c: document.createElement('h1'),
|
||||
@@ -266,8 +246,7 @@
|
||||
}, function () {
|
||||
Cypress[fnName](USED_KEYS.error, new Error('error'))
|
||||
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
// @ts-ignore
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const isUndefined = Cypress[fnName](USED_KEYS.error)
|
||||
|
||||
expect(isUndefined).to.be.undefined
|
||||
@@ -280,8 +259,7 @@
|
||||
}, () => {
|
||||
Cypress[fnName](USED_KEYS.error, new Error('error'))
|
||||
|
||||
cy.switchToDomain('http://foobar.com:3500', [fnName, USED_KEYS], ([fnName, USED_KEYS]) => {
|
||||
// @ts-ignore
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const isErrorObj = Cypress[fnName](USED_KEYS.error)
|
||||
|
||||
// We preserve the error structure, but on preprocessing to the spec bridge, the error is converted to a flat object
|
||||
|
||||
@@ -45,7 +45,7 @@ describe('multi-domain Cypress API', { experimentalSessionSupport: true }, () =>
|
||||
keystrokeDelay: 30,
|
||||
})
|
||||
|
||||
cy.switchToDomain('http://foobar.com:3500', [defaults], ([primaryKeyboardDefaults]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: defaults }, (primaryKeyboardDefaults) => {
|
||||
const multiDomainKeyboardDefaults = Cypress.Keyboard.defaults({})
|
||||
|
||||
expect(multiDomainKeyboardDefaults).to.not.deep.equal(primaryKeyboardDefaults)
|
||||
@@ -141,37 +141,37 @@ describe('multi-domain Cypress API', { experimentalSessionSupport: true }, () =>
|
||||
|
||||
context('properties', () => {
|
||||
it('has arch property synced from primary', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [Cypress.arch], ([theArch]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: Cypress.arch }, (theArch) => {
|
||||
expect(Cypress.arch).to.equal(theArch)
|
||||
})
|
||||
})
|
||||
|
||||
it('has browser property synced from primary', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [Cypress.browser], ([theBrowser]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: Cypress.browser }, (theBrowser) => {
|
||||
expect(Cypress.browser).to.deep.equal(theBrowser)
|
||||
})
|
||||
})
|
||||
|
||||
it('has currentTest property synced from primary', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [Cypress.currentTest], ([theCurrentTest]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: Cypress.currentTest }, (theCurrentTest) => {
|
||||
expect(Cypress.currentTest).to.deep.equal(theCurrentTest)
|
||||
})
|
||||
})
|
||||
|
||||
it('has platform property synced from primary', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [Cypress.platform], ([thePlatform]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: Cypress.platform }, (thePlatform) => {
|
||||
expect(Cypress.platform).to.equal(thePlatform)
|
||||
})
|
||||
})
|
||||
|
||||
it('has testingType property synced from primary', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [Cypress.testingType], ([theTestingType]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: Cypress.testingType }, (theTestingType) => {
|
||||
expect(Cypress.testingType).to.deep.equal(theTestingType)
|
||||
})
|
||||
})
|
||||
|
||||
it('has spec property synced from primary', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [Cypress.spec], ([theSpec]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: Cypress.spec }, (theSpec) => {
|
||||
expect(Cypress.spec).to.deep.equal(theSpec)
|
||||
})
|
||||
})
|
||||
@@ -185,7 +185,7 @@ describe('multi-domain Cypress API', { experimentalSessionSupport: true }, () =>
|
||||
})
|
||||
|
||||
it('isBrowser()', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [Cypress.browser], ([theBrowser]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: Cypress.browser }, (theBrowser) => {
|
||||
expect(Cypress.isBrowser(theBrowser.name)).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('multi-domain', { experimentalSessionSupport: true }, () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="multi-domain-secondary-link"]').click()
|
||||
|
||||
cy.switchToDomain('http://foobar.com:3500', [expectedViewport], ([expectedViewport]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: expectedViewport }, (expectedViewport) => {
|
||||
const secondaryViewport = [cy.state('viewportWidth'), cy.state('viewportHeight')]
|
||||
|
||||
expect(secondaryViewport).to.deep.equal(expectedViewport)
|
||||
@@ -72,7 +72,7 @@ describe('multi-domain', { experimentalSessionSupport: true }, () => {
|
||||
ctx: {},
|
||||
}
|
||||
|
||||
cy.switchToDomain('http://foobar.com:3500', [expectedRunnable], ([expectedRunnable]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: expectedRunnable }, (expectedRunnable) => {
|
||||
const actualRunnable = cy.state('runnable')
|
||||
|
||||
expect(actualRunnable.titlePath()).to.deep.equal(expectedRunnable.titlePath)
|
||||
@@ -112,39 +112,39 @@ describe('multi-domain', { experimentalSessionSupport: true }, () => {
|
||||
|
||||
describe('data argument', () => {
|
||||
it('passes object to callback function', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [{ foo: 'foo', bar: 'bar' }], ([{ foo, bar }]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { foo: 'foo', bar: 'bar' } }, ({ foo, bar }) => {
|
||||
expect(foo).to.equal('foo')
|
||||
expect(bar).to.equal('bar')
|
||||
})
|
||||
})
|
||||
|
||||
it('passes array to callback function', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', ['foo', 'bar'], ([foo, bar]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: ['foo', 'bar'] }, ([foo, bar]) => {
|
||||
expect(foo).to.equal('foo')
|
||||
expect(bar).to.equal('bar')
|
||||
})
|
||||
})
|
||||
|
||||
it('passes string to callback function', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', ['foo'], ([foo]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: 'foo' }, (foo) => {
|
||||
expect(foo).to.equal('foo')
|
||||
})
|
||||
})
|
||||
|
||||
it('passes number to callback function', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [1], ([num]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: 1 }, (num) => {
|
||||
expect(num).to.equal(1)
|
||||
})
|
||||
})
|
||||
|
||||
it('passes boolean to callback function', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', [true], ([bool]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: true }, (bool) => {
|
||||
expect(bool).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
it('passes mixed types to callback function', () => {
|
||||
cy.switchToDomain('http://foobar.com:3500', ['foo', 1, true], ([foo, num, bool]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: { foo: 'foo', num: 1, bool: true } }, ({ foo, num, bool }) => {
|
||||
expect(foo).to.equal('foo')
|
||||
expect(num).to.equal(1)
|
||||
expect(bool).to.be.true
|
||||
@@ -204,7 +204,7 @@ describe('multi-domain', { experimentalSessionSupport: true }, () => {
|
||||
done()
|
||||
})
|
||||
|
||||
cy.switchToDomain('http://foobar.com:3500', [timeout], ([timeout]) => {
|
||||
cy.switchToDomain('http://foobar.com:3500', { args: timeout }, (timeout) => {
|
||||
cy.get('#doesnt-exist', {
|
||||
timeout,
|
||||
})
|
||||
|
||||
@@ -179,7 +179,7 @@ describe('multi-domain', { experimentalSessionSupport: true }, () => {
|
||||
|
||||
it('errors passing non-array to callback function', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`cy.switchToDomain()` requires the \'data\' argument to be an array. You passed: `foo`')
|
||||
expect(err.message).to.equal('`cy.switchToDomain()` requires the \'options\' argument to be an object. You passed: `foo`')
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -188,9 +188,27 @@ describe('multi-domain', { experimentalSessionSupport: true }, () => {
|
||||
cy.switchToDomain('foobar.com', 'foo', () => {})
|
||||
})
|
||||
|
||||
it('errors if passed a non-serializable data value', (done) => {
|
||||
it('errors passing in invalid config object to callback function', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('data argument specified is not serializable')
|
||||
expect(err.message).to.include('`cy.switchToDomain()` detected extraneous keys in your options configuration.')
|
||||
expect(err.message).to.include('The extraneous keys detected were:')
|
||||
expect(err.message).to.include('> `foo, bar`')
|
||||
expect(err.message).to.include('Valid keys include the following:')
|
||||
expect(err.message).to.include('> `args`')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.switchToDomain('foobar.com', {
|
||||
// @ts-ignore
|
||||
foo: 'foo',
|
||||
bar: 'bar',
|
||||
}, () => {})
|
||||
})
|
||||
|
||||
it('errors if passed a non-serializable args value', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('arguments specified are not serializable')
|
||||
|
||||
if (Cypress.browser.family === 'chromium') {
|
||||
expect(err.message).to.include('HTMLDivElement object could not be cloned')
|
||||
@@ -203,7 +221,7 @@ describe('multi-domain', { experimentalSessionSupport: true }, () => {
|
||||
|
||||
const el = document.createElement('div')
|
||||
|
||||
cy.switchToDomain('foobar.com', ['foo', '1', el], () => {})
|
||||
cy.switchToDomain('foobar.com', { args: ['foo', '1', el] }, () => {})
|
||||
})
|
||||
|
||||
it('errors if last argument is absent', (done) => {
|
||||
|
||||
@@ -45,7 +45,7 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy,
|
||||
})
|
||||
|
||||
Commands.addAll({
|
||||
switchToDomain<T> (originOrDomain: string, dataOrFn: T[] | (() => {}), fn?: (data?: T[]) => {}) {
|
||||
switchToDomain<T> (originOrDomain: string, optionsOrFn: { args: T } | (() => {}), fn?: (args?: T) => {}) {
|
||||
// store the invocation stack in the case that `switchToDomain` errors
|
||||
communicator.userInvocationStack = state('current').get('userInvocationStack')
|
||||
|
||||
@@ -58,15 +58,17 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy,
|
||||
$errUtils.throwErrByPath('switchToDomain.experiment_not_enabled')
|
||||
}
|
||||
|
||||
let data
|
||||
let options
|
||||
let callbackFn
|
||||
|
||||
if (fn) {
|
||||
callbackFn = fn
|
||||
data = dataOrFn
|
||||
options = optionsOrFn
|
||||
} else {
|
||||
callbackFn = dataOrFn
|
||||
data = []
|
||||
callbackFn = optionsOrFn
|
||||
options = {
|
||||
args: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
const log = Cypress.log({
|
||||
@@ -83,7 +85,7 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy,
|
||||
|
||||
validator.validate({
|
||||
callbackFn,
|
||||
data,
|
||||
options,
|
||||
originOrDomain,
|
||||
})
|
||||
|
||||
@@ -173,7 +175,7 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy,
|
||||
// user-specified callback to run in that domain
|
||||
try {
|
||||
communicator.toSpecBridge(domain, 'run:domain:fn', {
|
||||
data,
|
||||
args: options?.args || undefined,
|
||||
fn: callbackFn.toString(),
|
||||
// let the spec bridge version of Cypress know if config read-only values can be overwritten since window.top cannot be accessed in cross-origin iframes
|
||||
// this should only be used for internal testing. Cast to boolean to guarantee serialization
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import $utils from '../../cypress/utils'
|
||||
import $errUtils from '../../cypress/error_utils'
|
||||
import { isString } from 'lodash'
|
||||
import { difference, isPlainObject, isString } from 'lodash'
|
||||
|
||||
const validOptionKeys = Object.freeze(['args'])
|
||||
|
||||
export class Validator {
|
||||
log: Cypress.Log
|
||||
@@ -11,7 +13,7 @@ export class Validator {
|
||||
this.onFailure = onFailure
|
||||
}
|
||||
|
||||
validate ({ callbackFn, data, originOrDomain }) {
|
||||
validate ({ callbackFn, options, originOrDomain }) {
|
||||
if (!isString(originOrDomain)) {
|
||||
this.onFailure()
|
||||
|
||||
@@ -21,13 +23,29 @@ export class Validator {
|
||||
})
|
||||
}
|
||||
|
||||
if (data && !Array.isArray(data)) {
|
||||
this.onFailure()
|
||||
if (options) {
|
||||
if (!isPlainObject(options)) {
|
||||
this.onFailure()
|
||||
|
||||
$errUtils.throwErrByPath('switchToDomain.invalid_data_argument', {
|
||||
onFail: this.log,
|
||||
args: { arg: $utils.stringify(data) },
|
||||
})
|
||||
$errUtils.throwErrByPath('switchToDomain.invalid_options_argument', {
|
||||
onFail: this.log,
|
||||
args: { arg: $utils.stringify(options) },
|
||||
})
|
||||
}
|
||||
|
||||
const extraneousKeys = difference(Object.keys(options), validOptionKeys)
|
||||
|
||||
if (extraneousKeys.length) {
|
||||
this.onFailure()
|
||||
|
||||
$errUtils.throwErrByPath('switchToDomain.extraneous_options_argument', {
|
||||
onFail: this.log,
|
||||
args: {
|
||||
extraneousKeys: extraneousKeys.join(', '),
|
||||
validOptionKeys: validOptionKeys.join(', '),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof callbackFn !== 'function') {
|
||||
|
||||
@@ -1712,17 +1712,27 @@ export default {
|
||||
|
||||
switchToDomain: {
|
||||
docsUrl: 'https://on.cypress.io/switchToDomain',
|
||||
done_reference_mismatch: {
|
||||
message: `${cmd('switchToDomain')} must have done as its second argument when three or more arguments are used.`,
|
||||
},
|
||||
experiment_not_enabled: {
|
||||
message: `${cmd('switchToDomain')} requires enabling the experimentalMultiDomain flag`,
|
||||
},
|
||||
invalid_origin_argument: {
|
||||
message: `${cmd('switchToDomain')} requires the first argument to be either an origin ('https://app.example.com') or a domain name ('example.com'). The origin or domain name must not contain a path, hash, or query parameters. You passed: \`{{arg}}\``,
|
||||
},
|
||||
invalid_data_argument: {
|
||||
message: `${cmd('switchToDomain')} requires the 'data' argument to be an array. You passed: \`{{arg}}\``,
|
||||
invalid_options_argument: {
|
||||
message: `${cmd('switchToDomain')} requires the 'options' argument to be an object. You passed: \`{{arg}}\``,
|
||||
},
|
||||
extraneous_options_argument ({ extraneousKeys, validOptionKeys }) {
|
||||
return stripIndent`\
|
||||
${cmd('switchToDomain')} detected extraneous keys in your options configuration.
|
||||
|
||||
The extraneous keys detected were:
|
||||
|
||||
> \`${extraneousKeys}\`
|
||||
|
||||
Valid keys include the following:
|
||||
|
||||
> \`${validOptionKeys}\`
|
||||
`
|
||||
},
|
||||
invalid_fn_argument: {
|
||||
message: `${cmd('switchToDomain')} requires the last argument to be a function. You passed: \`{{arg}}\``,
|
||||
@@ -1733,7 +1743,7 @@ export default {
|
||||
|
||||
> {{error}}
|
||||
|
||||
This is likely because the data argument specified is not serializable. Note that functions and DOM objects cannot be serialized.`,
|
||||
This is likely because the arguments specified are not serializable. Note that functions and DOM objects cannot be serialized.`,
|
||||
},
|
||||
callback_mixes_sync_and_async: {
|
||||
message: stripIndent`\
|
||||
|
||||
@@ -63,9 +63,9 @@ export class PrimaryDomainCommunicator extends EventEmitter {
|
||||
|
||||
const preprocessedData = preprocessForSerialization<any>(data)
|
||||
|
||||
// if user defined data is passed in, do NOT sanitize it.
|
||||
if (data?.data) {
|
||||
preprocessedData.data = data.data
|
||||
// if user defined arguments are passed in, do NOT sanitize it.
|
||||
if (data?.args) {
|
||||
preprocessedData.args = data.args
|
||||
}
|
||||
|
||||
// If there is no crossDomainDriverWindow, there is no need to send the message.
|
||||
@@ -82,9 +82,9 @@ export class PrimaryDomainCommunicator extends EventEmitter {
|
||||
|
||||
const preprocessedData = preprocessForSerialization<any>(data)
|
||||
|
||||
// if user defined data is passed in, do NOT sanitize it.
|
||||
if (data?.data) {
|
||||
preprocessedData.data = data.data
|
||||
// if user defined arguments are passed in, do NOT sanitize it.
|
||||
if (data?.args) {
|
||||
preprocessedData.args = data.args
|
||||
}
|
||||
|
||||
// If there is no crossDomainDriverWindow, there is no need to send the message.
|
||||
|
||||
@@ -7,7 +7,7 @@ import { LogUtils } from '../cypress/log'
|
||||
|
||||
interface RunDomainFnOptions {
|
||||
config: Cypress.Config
|
||||
data: any[]
|
||||
args: any
|
||||
env: Cypress.ObjectLike
|
||||
fn: string
|
||||
skipConfigValidation: boolean
|
||||
@@ -83,7 +83,7 @@ export const handleDomainFn = (Cypress: Cypress.Cypress, cy: $Cy) => {
|
||||
}
|
||||
|
||||
Cypress.specBridgeCommunicator.on('run:domain:fn', async (options: RunDomainFnOptions) => {
|
||||
const { config, data, env, fn, state, skipConfigValidation, logCounter } = options
|
||||
const { config, args, env, fn, state, skipConfigValidation, logCounter } = options
|
||||
|
||||
let queueFinished = false
|
||||
|
||||
@@ -114,7 +114,7 @@ export const handleDomainFn = (Cypress: Cypress.Cypress, cy: $Cy) => {
|
||||
})
|
||||
|
||||
try {
|
||||
const value = window.eval(`(${fn})`)(data)
|
||||
const value = window.eval(`(${fn})`)(args)
|
||||
|
||||
// If we detect a non promise value with commands in queue, throw an error
|
||||
if (value && cy.queue.length > 0 && !value.then) {
|
||||
|
||||
Reference in New Issue
Block a user