-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: [Multi-domain]: Yield subject from
switchToDomain
(#19936)
* first pass at yielding a subject * Remove logs and add comments * remove duplicate test * Add debug logging for aborted * Apply suggestions from code review Co-authored-by: Bill Glesias <bglesias@gmail.com> * New strategy for serializing subjects * handle 'ran:domain:fn' event * Fix lint * Now with proxy error handling! * Break yields tests out into their own file. * updated test * Update packages/driver/src/cy/multi-domain/failedSerializeSubjectProxy.ts Co-authored-by: Matt Schile <mschile@gmail.com> * add a param for the malformed should * Apply suggestions from code review Co-authored-by: Matt Schile <mschile@gmail.com> * update test to work cross browsers * fix test strings * Update packages/driver/src/util/queue.ts Co-authored-by: Matt Schile <mschile@gmail.com> * optional chaining ! * Apply suggestions from code review Co-authored-by: Bill Glesias <bglesias@gmail.com> * code review changes * Whoops * whoops, renamed the wrong test file * Update packages/driver/src/cy/multi-domain/failedSerializeSubjectProxy.ts Co-authored-by: Matt Schile <mschile@gmail.com> Co-authored-by: Bill Glesias <bglesias@gmail.com> Co-authored-by: Matt Schile <mschile@gmail.com>
- Loading branch information
1 parent
4070270
commit c58a0ee
Showing
12 changed files
with
445 additions
and
20 deletions.
There are no files selected for viewing
253 changes: 253 additions & 0 deletions
253
packages/driver/cypress/integration/e2e/multi-domain/multi_domain_yield_spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,253 @@ | ||
const { assertLogLength } = require('../../../support/utils') | ||
|
||
// @ts-ignore / session support is needed for visiting about:blank between tests | ||
describe('multi-domain yields', { experimentalSessionSupport: true, experimentalMultiDomain: true }, () => { | ||
let logs: any = [] | ||
|
||
beforeEach(() => { | ||
logs = [] | ||
|
||
cy.on('log:added', (attrs, log) => { | ||
logs.push(log) | ||
}) | ||
|
||
cy.visit('/fixtures/multi-domain.html') | ||
cy.get('a[data-cy="multi-domain-secondary-link"]').click() | ||
}) | ||
|
||
it('yields a value', () => { | ||
cy.switchToDomain('foobar.com', () => { | ||
cy | ||
.get('[data-cy="dom-check"]') | ||
.invoke('text') | ||
}).should('equal', 'From a secondary domain') | ||
}) | ||
|
||
it('yields the cy value even if a return is present', () => { | ||
cy.switchToDomain('foobar.com', () => { | ||
cy | ||
.get('[data-cy="dom-check"]') | ||
.invoke('text') | ||
|
||
const p = new Promise((resolve, reject) => { | ||
setTimeout(() => { | ||
resolve('text') | ||
}, 50) | ||
}) | ||
|
||
return p | ||
}).should('equal', 'From a secondary domain') | ||
}) | ||
|
||
it('errors if a cy command is present and it returns a sync value', (done) => { | ||
cy.on('fail', (err) => { | ||
assertLogLength(logs, 6) | ||
expect(logs[5].get('error')).to.eq(err) | ||
expect(err.message).to.include('`cy.switchToDomain()` failed because you are mixing up async and sync code.') | ||
|
||
done() | ||
}) | ||
|
||
cy.switchToDomain('foobar.com', () => { | ||
cy | ||
.get('[data-cy="dom-check"]') | ||
.invoke('text') | ||
|
||
return 'text' | ||
}) | ||
}) | ||
|
||
it('yields synchronously', () => { | ||
cy.switchToDomain('foobar.com', () => { | ||
return 'From a secondary domain' | ||
}).should('equal', 'From a secondary domain') | ||
}) | ||
|
||
it('yields asynchronously', () => { | ||
cy.switchToDomain('foobar.com', () => { | ||
return new Promise((resolve: (val: string) => any, reject) => { | ||
setTimeout(() => { | ||
resolve('From a secondary domain') | ||
}, 50) | ||
}) | ||
}).should('equal', 'From a secondary domain') | ||
}) | ||
|
||
it('succeeds if subject cannot be serialized and is not accessed synchronously', () => { | ||
cy.switchToDomain('foobar.com', () => { | ||
return { | ||
symbol: Symbol(''), | ||
} | ||
}).then((obj) => { | ||
return 'object not accessed' | ||
}).should('equal', 'object not accessed') | ||
}) | ||
|
||
it('throws if subject cannot be serialized and is accessed synchronously', (done) => { | ||
cy.on('fail', (err) => { | ||
assertLogLength(logs, 6) | ||
expect(logs[5].get('error')).to.eq(err) | ||
expect(err.message).to.include('`cy.switchToDomain()` could not serialize the subject due to one of its properties not being supported by the structured clone algorithm.') | ||
|
||
done() | ||
}) | ||
|
||
cy.switchToDomain('foobar.com', () => { | ||
return { | ||
symbol: Symbol(''), | ||
} | ||
}).then((obj) => { | ||
// This will fail accessing the symbol | ||
// @ts-ignore | ||
return obj.symbol | ||
}) | ||
}) | ||
|
||
it('succeeds if subject cannot be serialized and is not accessed', () => { | ||
cy.switchToDomain('foobar.com', () => { | ||
cy | ||
.get('[data-cy="dom-check"]') | ||
}).then((obj) => { | ||
return 'object not accessed' | ||
}).should('equal', 'object not accessed') | ||
}) | ||
|
||
it('throws if subject cannot be serialized and is accessed', (done) => { | ||
cy.on('fail', (err) => { | ||
assertLogLength(logs, 7) | ||
expect(logs[6].get('error')).to.eq(err) | ||
expect(err.message).to.include('`cy.switchToDomain()` could not serialize the subject due to one of its properties not being supported by the structured clone algorithm.') | ||
|
||
done() | ||
}) | ||
|
||
cy.switchToDomain('foobar.com', () => { | ||
cy | ||
.get('[data-cy="dom-check"]') | ||
}).invoke('text') | ||
.should('equal', 'From a secondary domain') | ||
}) | ||
|
||
it('throws if an object contains a function', (done) => { | ||
cy.on('fail', (err) => { | ||
assertLogLength(logs, 7) | ||
expect(logs[6].get('error')).to.eq(err) | ||
expect(err.message).to.include('`cy.switchToDomain()` could not serialize the subject due to one of its properties not being supported by the structured clone algorithm.') | ||
|
||
done() | ||
}) | ||
|
||
cy.switchToDomain('foobar.com', () => { | ||
cy.wrap({ | ||
key: () => { | ||
return 'whoops' | ||
}, | ||
}) | ||
}).invoke('key').should('equal', 'whoops') | ||
}) | ||
|
||
it('throws if an object contains a symbol', (done) => { | ||
cy.on('fail', (err) => { | ||
assertLogLength(logs, 7) | ||
expect(logs[6].get('error')).to.eq(err) | ||
expect(err.message).to.include('`cy.switchToDomain()` could not serialize the subject due to one of its properties not being supported by the structured clone algorithm.') | ||
|
||
done() | ||
}) | ||
|
||
cy.switchToDomain('foobar.com', () => { | ||
cy.wrap({ | ||
key: Symbol('whoops'), | ||
}) | ||
}).should('equal', undefined) | ||
}) | ||
|
||
it('throws if an object is a function', (done) => { | ||
cy.on('fail', (err) => { | ||
assertLogLength(logs, 7) | ||
expect(logs[6].get('error')).to.eq(err) | ||
expect(err.message).to.include('`cy.switchToDomain()` could not serialize the subject due to functions not being supported by the structured clone algorithm.') | ||
|
||
done() | ||
}) | ||
|
||
cy.switchToDomain('foobar.com', () => { | ||
cy.wrap(() => { | ||
return 'text' | ||
}) | ||
}).then((obj) => { | ||
// @ts-ignore | ||
obj() | ||
}) | ||
}) | ||
|
||
it('throws if an object is a symbol', (done) => { | ||
cy.on('fail', (err) => { | ||
assertLogLength(logs, 7) | ||
expect(logs[6].get('error')).to.eq(err) | ||
expect(err.message).to.include('`cy.switchToDomain()` could not serialize the subject due to symbols not being supported by the structured clone algorithm.') | ||
|
||
done() | ||
}) | ||
|
||
cy.switchToDomain('foobar.com', () => { | ||
cy.wrap(Symbol('symbol')) | ||
}).should('equal', 'symbol') | ||
}) | ||
|
||
// NOTE: Errors can only be serialized on chromium browsers. | ||
it('yields an error if an object contains an error', (done) => { | ||
const isChromium = Cypress.isBrowser({ family: 'chromium' }) | ||
|
||
cy.on('fail', (err) => { | ||
if (!isChromium) { | ||
assertLogLength(logs, 7) | ||
expect(logs[6].get('error')).to.eq(err) | ||
expect(err.message).to.include('`cy.switchToDomain()` could not serialize the subject due to one of its properties not being supported by the structured clone algorithm.') | ||
} | ||
|
||
done() | ||
}) | ||
|
||
cy.switchToDomain('foobar.com', () => { | ||
cy.wrap({ | ||
key: new Error('Boom goes the dynamite'), | ||
}) | ||
}).its('key.message') | ||
.should('equal', 'Boom goes the dynamite').then(() => { | ||
done() | ||
}) | ||
}) | ||
|
||
it('yields an object containing valid types', () => { | ||
cy.switchToDomain('foobar.com', () => { | ||
cy.wrap({ | ||
array: [ | ||
1, | ||
2, | ||
], | ||
undefined, | ||
bool: true, | ||
null: null, | ||
number: 12, | ||
object: { | ||
key: 'key', | ||
}, | ||
string: 'string', | ||
}) | ||
}).should('deep.equal', { | ||
array: [ | ||
1, | ||
2, | ||
], | ||
undefined, | ||
bool: true, | ||
null: null, | ||
number: 12, | ||
object: { | ||
key: 'key', | ||
}, | ||
string: 'string', | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
packages/driver/src/cy/multi-domain/failedSerializeSubjectProxy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import $errUtils from '../../cypress/error_utils' | ||
|
||
// These properties are required to avoid failing prior to attempting to use the subject. | ||
// If Symbol.toStringTag is passed through to the target we will not properly fail the 'cy.invoke' command. | ||
const passThroughProps = [ | ||
'then', | ||
Symbol.isConcatSpreadable, | ||
'jquery', | ||
'nodeType', | ||
'window', | ||
'document', | ||
'inspect', | ||
'isSinonProxy', | ||
'_spreadArray', | ||
'selector', | ||
] | ||
|
||
/** | ||
* Create a proxy object to fail when accessed or called. | ||
* @param type The type of operand that failed to serialize | ||
* @returns A proxy object that will fail when accessed. | ||
*/ | ||
const failedToSerializeSubject = (type: string) => { | ||
let target = {} | ||
|
||
// If the failed subject is a function, use a function as the target. | ||
if (type === 'function') { | ||
target = () => {} | ||
} | ||
|
||
// Symbol note: The target can't be a symbol, but we can use an object until the symbol is accessed, then provide a different error. | ||
|
||
return new Proxy(target, { | ||
/** | ||
* Throw an error if the proxy is called like a function. | ||
* @param target the proxy target | ||
* @param thisArg this | ||
* @param argumentsList args passed. | ||
*/ | ||
apply (target, thisArg, argumentsList) { | ||
$errUtils.throwErrByPath('switchToDomain.failed_to_serialize_function') | ||
}, | ||
|
||
/** | ||
* Throw an error if any properties besides the listed ones are accessed. | ||
* @param target The proxy target | ||
* @param prop The property being accessed | ||
* @param receiver Either the proxy or an object that inherits from the proxy. | ||
* @returns either an error or the result of the allowed get on the target. | ||
*/ | ||
get (target, prop, receiver) { | ||
if (passThroughProps.includes(prop)) { | ||
return target[prop] | ||
} | ||
|
||
// Provide a slightly different message if the object was meant to be a symbol. | ||
if (type === 'symbol') { | ||
$errUtils.throwErrByPath('switchToDomain.failed_to_serialize_symbol') | ||
} else { | ||
$errUtils.throwErrByPath('switchToDomain.failed_to_serialize_object') | ||
} | ||
}, | ||
}) | ||
} | ||
|
||
export { failedToSerializeSubject } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.