From d7ce86541db67561a51bc20d165144e27b5b1423 Mon Sep 17 00:00:00 2001 From: Matt Schile Date: Wed, 20 Apr 2022 23:03:08 -0600 Subject: [PATCH] chore: (cross-origin) add support for redirecting back to primary (#21144) --- .../integration/commands/navigation_spec.js | 57 ++---- .../commands/multi_domain_navigation.spec.ts | 83 +++++++- packages/driver/src/cy/commands/navigation.ts | 61 +++--- packages/driver/src/cy/multi-domain/index.ts | 16 +- packages/driver/src/cypress/error_messages.ts | 24 ++- packages/driver/types/internal-types.d.ts | 3 +- .../proxy/lib/http/response-middleware.ts | 6 +- .../unit/http/response-middleware.spec.ts | 44 +---- packages/server/lib/experiments.ts | 2 +- packages/server/lib/remote_states.ts | 10 +- packages/server/lib/server-base.ts | 4 +- packages/server/lib/server-e2e.ts | 13 +- packages/server/lib/socket-base.ts | 6 +- .../test/integration/http_requests_spec.js | 2 +- .../server/test/integration/server_spec.js | 181 +++++++++++++----- .../server/test/unit/remote_states.spec.ts | 49 ++--- packages/server/test/unit/socket_spec.js | 21 +- .../runnable_execution_spec.ts.js | 8 +- 18 files changed, 371 insertions(+), 219 deletions(-) diff --git a/packages/driver/cypress/integration/commands/navigation_spec.js b/packages/driver/cypress/integration/commands/navigation_spec.js index b4bda839fbc1..b6b4f0155e3b 100644 --- a/packages/driver/cypress/integration/commands/navigation_spec.js +++ b/packages/driver/cypress/integration/commands/navigation_spec.js @@ -1414,10 +1414,10 @@ describe('src/cy/commands/navigation', () => { \`cy.visit()\` failed because you are attempting to visit a URL that is of a different origin.\n You likely forgot to use \`cy.origin()\`:\n \`cy.visit('http://localhost:3500/fixtures/generic.html')\` - \`\`\n + \`\`\n \`cy.origin('http://localhost:3501', () => {\` \` cy.visit('http://localhost:3501/fixtures/generic.html')\` - \` \` + \` \` \`})\`\n The new URL is considered a different origin because the following parts of the URL are different:\n > port\n @@ -1446,18 +1446,18 @@ describe('src/cy/commands/navigation', () => { \`cy.visit()\` failed because you are attempting to visit a URL that is of a different origin.\n You likely forgot to use \`cy.origin()\`:\n \`cy.visit('http://localhost:3500/fixtures/generic.html')\` - \`\`\n - \`cy.origin('https://localhost:3500', () => {\` - \` cy.visit('https://localhost:3500/fixtures/generic.html')\` - \` \` + \`\`\n + \`cy.origin('https://localhost:3502', () => {\` + \` cy.visit('https://localhost:3502/fixtures/generic.html')\` + \` \` \`})\`\n The new URL is considered a different origin because the following parts of the URL are different:\n - > protocol\n + > protocol, port\n You may only \`cy.visit()\` same-origin URLs within a single test.\n The previous URL you visited was:\n > 'http://localhost:3500'\n You're attempting to visit this URL:\n - > 'https://localhost:3500'`) + > 'https://localhost:3502'`) expect(err.docsUrl).to.eq('https://on.cypress.io/cannot-visit-different-origin-domain') assertLogLength(this.logs, 2) @@ -1467,7 +1467,7 @@ describe('src/cy/commands/navigation', () => { }) cy.visit('http://localhost:3500/fixtures/generic.html') - cy.visit('https://localhost:3500/fixtures/generic.html') + cy.visit('https://localhost:3502/fixtures/generic.html') }) it('throws when attempting to visit a 2nd domain on different superdomain', function (done) { @@ -1478,10 +1478,10 @@ describe('src/cy/commands/navigation', () => { \`cy.visit()\` failed because you are attempting to visit a URL that is of a different origin.\n You likely forgot to use \`cy.origin()\`:\n \`cy.visit('http://localhost:3500/fixtures/generic.html')\` - \`\`\n - \`cy.origin('http://google.com:3500', () => {\` - \` cy.visit('http://google.com:3500/fixtures/generic.html')\` - \` \` + \`\`\n + \`cy.origin('http://foobar.com:3500', () => {\` + \` cy.visit('http://www.foobar.com:3500/fixtures/generic.html')\` + \` \` \`})\`\n The new URL is considered a different origin because the following parts of the URL are different:\n > superdomain\n @@ -1489,7 +1489,7 @@ describe('src/cy/commands/navigation', () => { The previous URL you visited was:\n > 'http://localhost:3500'\n You're attempting to visit this URL:\n - > 'http://google.com:3500'`) + > 'http://www.foobar.com:3500'`) expect(err.docsUrl).to.eq('https://on.cypress.io/cannot-visit-different-origin-domain') assertLogLength(this.logs, 2) @@ -1499,7 +1499,7 @@ describe('src/cy/commands/navigation', () => { }) cy.visit('http://localhost:3500/fixtures/generic.html') - cy.visit('http://google.com:3500/fixtures/generic.html') + cy.visit('http://www.foobar.com:3500/fixtures/generic.html') }) it('throws attempting to visit 2 unique ip addresses', function (done) { @@ -1510,10 +1510,10 @@ describe('src/cy/commands/navigation', () => { \`cy.visit()\` failed because you are attempting to visit a URL that is of a different origin.\n You likely forgot to use \`cy.origin()\`:\n \`cy.visit('http://127.0.0.1:3500/fixtures/generic.html')\` - \`\`\n - \`cy.origin('http://126.0.0.1:3500', () => {\` - \` cy.visit('http://126.0.0.1:3500/fixtures/generic.html')\` - \` \` + \`\`\n + \`cy.origin('http://0.0.0.0:3500', () => {\` + \` cy.visit('http://0.0.0.0:3500/fixtures/generic.html')\` + \` \` \`})\`\n The new URL is considered a different origin because the following parts of the URL are different:\n > superdomain\n @@ -1521,7 +1521,7 @@ describe('src/cy/commands/navigation', () => { The previous URL you visited was:\n > 'http://127.0.0.1:3500'\n You're attempting to visit this URL:\n - > 'http://126.0.0.1:3500'`) + > 'http://0.0.0.0:3500'`) expect(err.docsUrl).to.eq('https://on.cypress.io/cannot-visit-different-origin-domain') assertLogLength(this.logs, 2) @@ -1532,22 +1532,7 @@ describe('src/cy/commands/navigation', () => { cy .visit('http://127.0.0.1:3500/fixtures/generic.html') - .visit('http://126.0.0.1:3500/fixtures/generic.html') - }) - - it('does not call resolve:url when throws attempting to visit a 2nd domain', (done) => { - const backend = cy.spy(Cypress, 'backend') - - cy.on('fail', (err) => { - expect(backend).to.be.calledWithMatch('resolve:url', 'http://localhost:3500/fixtures/generic.html') - expect(backend).not.to.be.calledWithMatch('resolve:url', 'http://google.com:3500/fixtures/generic.html') - - done() - }) - - cy - .visit('http://localhost:3500/fixtures/generic.html') - .visit('http://google.com:3500/fixtures/generic.html') + .visit('http://0.0.0.0:3500/fixtures/generic.html') }) it('displays loading_network_failed when _resolveUrl throws', function (done) { diff --git a/packages/driver/cypress/integration/e2e/multi-domain/commands/multi_domain_navigation.spec.ts b/packages/driver/cypress/integration/e2e/multi-domain/commands/multi_domain_navigation.spec.ts index febb6e19e1af..e6bc322cdb45 100644 --- a/packages/driver/cypress/integration/e2e/multi-domain/commands/multi_domain_navigation.spec.ts +++ b/packages/driver/cypress/integration/e2e/multi-domain/commands/multi_domain_navigation.spec.ts @@ -178,11 +178,11 @@ context('cy.origin navigation', () => { You likely forgot to use \`cy.origin()\`:\n \`cy.origin('http://foobar.com:3500', () => {\` \` cy.visit('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')\` - \` \` + \` \` \`})\`\n \`cy.origin('http://idp.com:3500', () => {\` \` cy.visit('http://www.idp.com:3500/fixtures/dom.html')\` - \` \` + \` \` \`})\`\n The new URL is considered a different origin because the following parts of the URL are different:\n > superdomain\n @@ -211,10 +211,10 @@ context('cy.origin navigation', () => { \`cy.visit()\` failed because you are attempting to visit a URL that is of a different origin.\n In order to visit a different origin, you can enable the \`experimentalSessionAndOrigin\` flag and use \`cy.origin()\`:\n \`cy.visit('http://localhost:3500/fixtures/multi-domain.html')\` - \`\`\n + \`\`\n \`cy.origin('http://foobar.com:3500', () => {\` \` cy.visit('http://www.foobar.com:3500/fixtures/dom.html')\` - \` \` + \` \` \`})\`\n The new URL is considered a different origin because the following parts of the URL are different:\n > superdomain\n @@ -312,7 +312,7 @@ context('cy.origin navigation', () => { }) }) - it('supports visit redirects', () => { + it('supports redirecting from primary to secondary in cy.origin', () => { cy.visit('/fixtures/multi-domain.html') cy.origin('http://www.foobar.com:3500', () => { @@ -321,6 +321,79 @@ context('cy.origin navigation', () => { }) }) + it('supports redirecting from secondary to primary outside of cy.origin', () => { + cy.visit('/fixtures/multi-domain.html') + cy.visit('http://www.foobar.com:3500/redirect?href=http://localhost:3500/fixtures/generic.html') + }) + + it('errors when trying to redirect from secondary to primary in cy.origin', (done) => { + cy.on('fail', (e) => { + expect(e.message).to.equal(stripIndent` + \`cy.visit()\` failed because you are attempting to visit a URL from a previous origin inside of \`cy.origin()\`.\n + Instead of placing the \`cy.visit()\` inside of \`cy.origin()\`, the \`cy.visit()\` should be placed outside of the \`cy.origin()\` block.\n + \`\`\n + \`cy.origin('http://foobar.com:3500', () => {\` + \` \` + \`})\`\n + \`cy.visit('http://www.foobar.com:3500/redirect?href=http://localhost:3500/fixtures/generic.html')\``) + + done() + }) + + cy.visit('http://localhost:3500/fixtures/multi-domain.html') + + cy.origin('http://www.foobar.com:3500', () => { + cy.visit('/redirect?href=http://localhost:3500/fixtures/generic.html') + }) + }) + + it('errors when trying to visit primary in cy.origin', (done) => { + cy.on('fail', (e) => { + expect(e.message).to.equal(stripIndent` + \`cy.visit()\` failed because you are attempting to visit a URL from a previous origin inside of \`cy.origin()\`.\n + Instead of placing the \`cy.visit()\` inside of \`cy.origin()\`, the \`cy.visit()\` should be placed outside of the \`cy.origin()\` block.\n + \`\`\n + \`cy.origin('http://foobar.com:3500', () => {\` + \` \` + \`})\`\n + \`cy.visit('http://localhost:3500/fixtures/generic.html')\``) + + done() + }) + + cy.visit('http://localhost:3500/fixtures/multi-domain.html') + + cy.origin('http://www.foobar.com:3500', () => { + cy.visit('http://localhost:3500/fixtures/generic.html') + }) + }) + + it('errors when trying to redirect from primary to secondary outside of cy.origin', (done) => { + cy.on('fail', (e) => { + expect(e.message).to.equal(stripIndent`\ + \`cy.visit()\` failed because you are attempting to visit a URL that is of a different origin.\n + You likely forgot to use \`cy.origin()\`:\n + \`cy.visit('http://localhost:3500/fixtures/multi-domain.html')\` + \`\`\n + \`cy.origin('http://foobar.com:3500', () => {\` + \` cy.visit('http://localhost:3500/redirect?href=http://www.foobar.com:3500/fixtures/generic.html')\` + \` \` + \`})\`\n + The new URL is considered a different origin because the following parts of the URL are different:\n + > superdomain\n + You may only \`cy.visit()\` same-origin URLs within a single test.\n + The previous URL you visited was:\n + > 'http://localhost:3500'\n + You're attempting to visit this URL:\n + > 'http://www.foobar.com:3500'`) + + done() + }) + + cy.visit('/fixtures/multi-domain.html') + cy.visit('http://localhost:3500/redirect?href=http://www.foobar.com:3500/fixtures/generic.html') + }) + it('supports auth options and adding auth to subsequent requests', () => { cy.origin('http://foobar.com:3500', () => { cy.visit('http://www.foobar.com:3500/basic_auth', { diff --git a/packages/driver/src/cy/commands/navigation.ts b/packages/driver/src/cy/commands/navigation.ts index 8e6f2d4a64ef..99c64ea9f369 100644 --- a/packages/driver/src/cy/commands/navigation.ts +++ b/packages/driver/src/cy/commands/navigation.ts @@ -90,7 +90,7 @@ const timedOutWaitingForPageLoad = (ms, log) => { } } -const cannotVisitDifferentOrigin = ({ remote, existing, previousUrlVisited, log, isCrossOriginSpecBridge = false }) => { +const cannotVisitDifferentOrigin = ({ remote, existing, originalUrl, previousUrlVisited, log, isCrossOriginSpecBridge = false }) => { const differences: string[] = [] if (remote.protocol !== existing.protocol) { @@ -111,6 +111,7 @@ const cannotVisitDifferentOrigin = ({ remote, existing, previousUrlVisited, log, differences: differences.join(', '), previousUrl: previousUrlVisited, attemptedUrl: remote, + originalUrl, isCrossOriginSpecBridge, experimentalSessionAndOrigin: Cypress.config('experimentalSessionAndOrigin'), }, @@ -122,6 +123,22 @@ const cannotVisitDifferentOrigin = ({ remote, existing, previousUrlVisited, log, $errUtils.throwErrByPath('visit.cannot_visit_different_origin', errOpts) } +const cannotVisitPreviousOrigin = ({ remote, originalUrl, previousUrlVisited, log }) => { + const errOpts = { + onFail: log, + args: { + attemptedUrl: remote, + previousUrl: previousUrlVisited, + originalUrl, + }, + errProps: { + isCrossOrigin: true, + }, + } + + $errUtils.throwErrByPath('origin.cannot_visit_previous_origin', errOpts) +} + const specifyFileByRelativePath = (url, log) => { $errUtils.throwErrByPath('visit.specify_file_by_relative_path', { onFail: log, @@ -494,6 +511,7 @@ const normalizeOptions = (options) => { .extend({ timeout: options.responseTimeout, isCrossOrigin: Cypress.isCrossOriginSpecBridge, + hasAlreadyVisitedUrl: options.hasAlreadyVisitedUrl, }) .value() } @@ -833,6 +851,8 @@ export default (Commands, Cypress, cy, state, config) => { onLoad () {}, }) + options.hasAlreadyVisitedUrl = !!previousUrlVisited + if (!_.isUndefined(options.qs) && !_.isObject(options.qs)) { $errUtils.throwErrByPath('visit.invalid_qs', { args: { qs: String(options.qs) } }) } @@ -1026,17 +1046,6 @@ export default (Commands, Cypress, cy, state, config) => { const existingHash = remote.hash || '' const existingAuth = remote.auth || '' - if (previousUrlVisited && (remote.originPolicy !== existing.originPolicy)) { - // if we've already visited a new superDomain - // then die else we'd be in a terrible endless loop - // we also need to disable retries to prevent the endless loop - $utils.getTestFromRunnable(state('runnable'))._retries = 0 - - const params = { remote, existing, previousUrlVisited, log: options._log } - - return cannotVisitDifferentOrigin(params) - } - // in a cross origin spec bridge, the window may not have been set yet if nothing has been loaded in the secondary origin, // it's also possible for a new test to start and for a cross-origin failure to occur if the win is set but // the AUT hasn't yet navigated to the secondary origin @@ -1082,7 +1091,7 @@ export default (Commands, Cypress, cy, state, config) => { return requestUrl(url, options) .then((resp: any = {}) => { - let { url, originalUrl, cookies, redirects, filePath } = resp + let { url, originalUrl, cookies, redirects, filePath, isPrimaryOrigin } = resp // reapply the existing hash url += existingHash @@ -1114,7 +1123,6 @@ export default (Commands, Cypress, cy, state, config) => { // if the origin currently matches // then go ahead and change the iframe's src - // and we're good to go if (remote.originPolicy === existing.originPolicy) { previousUrlVisited = remote @@ -1126,22 +1134,25 @@ export default (Commands, Cypress, cy, state, config) => { }) } - // if we are in a cross origin spec bridge and the origin policies weren't the same, - // we need to throw an error since the user tried to visit a new - // origin which isn't allowed within a cy.origin block - if (Cypress.isCrossOriginSpecBridge && win) { - const existingAutOrigin = $Location.create(win.location.href) - const params = { remote, existing, previousUrlVisited: existingAutOrigin, log: options._log, isCrossOriginSpecBridge: true } + // if we've already cy.visit'ed in the test and we are visiting a new origin, + // throw an error, else we'd be in a endless loop, + // we also need to disable retries to prevent the endless loop + if (previousUrlVisited) { + $utils.getTestFromRunnable(state('runnable'))._retries = 0 + + const params = { remote, existing, originalUrl, previousUrlVisited, log: options._log } return cannotVisitDifferentOrigin(params) } - // if we've already visited a new origin - // then die else we'd be in a terrible endless loop - if (previousUrlVisited) { - const params = { remote, existing, previousUrlVisited, log: options._log } + // if we are in a cross origin spec bridge and the origin policies weren't the same, + // we need to throw an error since the user tried to visit a new + // origin which isn't allowed within a cy.origin block + if (Cypress.isCrossOriginSpecBridge) { + const existingAutOrigin = win ? $Location.create(win.location.href) : $Location.create(Cypress.state('currentActiveOriginPolicy')) + const params = { remote, existing, originalUrl, previousUrlVisited: existingAutOrigin, log: options._log, isCrossOriginSpecBridge: true, isPrimaryOrigin } - return cannotVisitDifferentOrigin(params) + return isPrimaryOrigin ? cannotVisitPreviousOrigin(params) : cannotVisitDifferentOrigin(params) } // tell our backend we're changing origins diff --git a/packages/driver/src/cy/multi-domain/index.ts b/packages/driver/src/cy/multi-domain/index.ts index 44299336918b..70ce6fd5df35 100644 --- a/packages/driver/src/cy/multi-domain/index.ts +++ b/packages/driver/src/cy/multi-domain/index.ts @@ -43,11 +43,11 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy, } // If we haven't seen a cy.origin and cleared the timeout within 300ms, - // go ahead and inform the server 'ready:for:origin' failed and to release the - // response. This typically happens during a redirect where the user does + // go ahead and inform the server to release the response. + // This typically happens during a redirect where the user does // not have a cy.origin for the intermediary origin. timeoutId = setTimeout(() => { - Cypress.backend('ready:for:origin', { failed: true }) + Cypress.backend('cross:origin:release:html') }, 300) }) @@ -94,7 +94,7 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy, const validator = new Validator({ log, onFailure: () => { - Cypress.backend('ready:for:origin', { failed: true }) + Cypress.backend('cross:origin:release:html') }, }) @@ -163,7 +163,7 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy, // lets the proxy know to allow the response for the secondary // origin html through, so the page will finish loading - Cypress.backend('ready:for:origin', { originPolicy: location.originPolicy }) + Cypress.backend('cross:origin:release:html') if (err) { if (err?.name === 'ReferenceError') { @@ -202,7 +202,7 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy, } // fired once the spec bridge is set up and ready to receive messages - communicator.once('bridge:ready', (_data, specBridgeOriginPolicy) => { + communicator.once('bridge:ready', async (_data, specBridgeOriginPolicy) => { if (specBridgeOriginPolicy === originPolicy) { // now that the spec bridge is ready, instantiate Cypress with the current app config and environment variables for initial sync when creating the instance communicator.toSpecBridge(originPolicy, 'initialize:cypress', { @@ -210,6 +210,8 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy, env: preprocessEnv(Cypress.env()), }) + await Cypress.backend('cross:origin:bridge:ready', { originPolicy }) + // once the secondary origin page loads, send along the // user-specified callback to run in that origin try { @@ -237,7 +239,7 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy, }) } catch (err: any) { // Release the request if 'run:origin:fn' fails - Cypress.backend('ready:for:origin', { failed: true }) + Cypress.backend('cross:origin:release:html') const wrappedErr = $errUtils.errByPath('origin.run_origin_fn_errored', { error: err.message, diff --git a/packages/driver/src/cypress/error_messages.ts b/packages/driver/src/cypress/error_messages.ts index f7f41d972415..937a349e2a17 100644 --- a/packages/driver/src/cypress/error_messages.ts +++ b/packages/driver/src/cypress/error_messages.ts @@ -1237,6 +1237,22 @@ export default { docsUrl: 'https://on.cypress.io/session-api', }, }, + cannot_visit_previous_origin (args) { + return { + message: stripIndent`\ + ${cmd('visit')} failed because you are attempting to visit a URL from a previous origin inside of ${cmd('origin')}. + + Instead of placing the ${cmd('visit')} inside of ${cmd('origin')}, the ${cmd('visit')} should be placed outside of the ${cmd('origin')} block. + + \`\` + + \`cy.origin('${args.previousUrl.originPolicy}', () => {\` + \` \` + \`})\` + + \`cy.visit('${args.originalUrl}')\``, + } + }, }, proxy: { @@ -2105,15 +2121,15 @@ export default { ${args.isCrossOriginSpecBridge ? `\`cy.origin('${args.previousUrl.originPolicy}', () => {\` \` cy.visit('${args.previousUrl}')\` - \` \` + \` \` \`})\`` : `\`cy.visit('${args.previousUrl}')\` - \`\`` + \`\`` } \`cy.origin('${args.attemptedUrl.originPolicy}', () => {\` - \` cy.visit('${args.attemptedUrl}')\` - \` \` + \` cy.visit('${args.originalUrl}')\` + \` \` \`})\` The new URL is considered a different origin because the following parts of the URL are different: diff --git a/packages/driver/types/internal-types.d.ts b/packages/driver/types/internal-types.d.ts index 6f4697214b9d..82a0754ded3e 100644 --- a/packages/driver/types/internal-types.d.ts +++ b/packages/driver/types/internal-types.d.ts @@ -23,7 +23,8 @@ declare namespace Cypress { } interface Backend { - (task: 'ready:for:origin', args: { originPolicy?: string , failed?: boolean}): boolean + (task: 'cross:origin:release:html'): boolean + (task: 'cross:origin:bridge:ready', args: { originPolicy?: string }): boolean (task: 'cross:origin:finished', originPolicy: string): boolean } diff --git a/packages/proxy/lib/http/response-middleware.ts b/packages/proxy/lib/http/response-middleware.ts index 5768bc4dc3df..82697b51c37c 100644 --- a/packages/proxy/lib/http/response-middleware.ts +++ b/packages/proxy/lib/http/response-middleware.ts @@ -238,10 +238,10 @@ const MaybeDelayForCrossOrigin: ResponseMiddleware = function () { // delay the response if this is a cross-origin (and not returning to a previous origin) html request from the AUT iframe if (this.config.experimentalSessionAndOrigin && isCrossOrigin && !isPreviousOrigin && isAUTFrame && (isHTML || isRenderedHTML)) { - this.debug('is cross-origin, delay until ready:for:origin event') + this.debug('is cross-origin, delay until cross:origin:release:html event') - this.serverBus.once('ready:for:origin', ({ failed }) => { - this.debug(`received ready:for:origin${failed ? ' failed' : ''}, let the response proceed`) + this.serverBus.once('cross:origin:release:html', () => { + this.debug(`received cross:origin:release:html, let the response proceed`) this.next() }) diff --git a/packages/proxy/test/unit/http/response-middleware.spec.ts b/packages/proxy/test/unit/http/response-middleware.spec.ts index 5c9f215cb956..a3faa680f77c 100644 --- a/packages/proxy/test/unit/http/response-middleware.spec.ts +++ b/packages/proxy/test/unit/http/response-middleware.spec.ts @@ -196,7 +196,7 @@ describe('http/response-middleware', function () { }) }) - it('waits for server signal if req is not of a previous origin, letting it continue after receiving ready:for:origin', function () { + it('waits for server signal if req is not of a previous origin, letting it continue after receiving cross:origin:release:html', function () { prepareContext({ req: { isAUTFrame: true, @@ -217,12 +217,12 @@ describe('http/response-middleware', function () { expect(ctx.serverBus.emit).to.be.calledWith('cross:origin:delaying:html', { href: 'http://www.idp.com/test' }) - ctx.serverBus.once.withArgs('ready:for:origin').args[0][1]({ originPolicy: 'http://idp.com' }) + ctx.serverBus.once.withArgs('cross:origin:release:html').args[0][1]() return promise }) - it('waits for server signal if res is html, letting it continue after receiving ready:for:origin', function () { + it('waits for server signal if res is html, letting it continue after receiving cross:origin:release:html', function () { prepareContext({ incomingRes: { headers: { @@ -242,12 +242,12 @@ describe('http/response-middleware', function () { expect(ctx.serverBus.emit).to.be.calledWith('cross:origin:delaying:html', { href: 'http://www.foobar.com/test' }) - ctx.serverBus.once.withArgs('ready:for:origin').args[0][1]({ originPolicy: 'http://foobar.com' }) + ctx.serverBus.once.withArgs('cross:origin:release:html').args[0][1]() return promise }) - it('waits for server signal if incomingRes is rendered html, letting it continue after receiving ready:for:origin', function () { + it('waits for server signal if incomingRes is rendered html, letting it continue after receiving cross:origin:release:html', function () { prepareContext({ req: { headers: { @@ -268,33 +268,7 @@ describe('http/response-middleware', function () { expect(ctx.serverBus.emit).to.be.calledWith('cross:origin:delaying:html', { href: 'http://www.foobar.com/test' }) - ctx.serverBus.once.withArgs('ready:for:origin').args[0][1]({ originPolicy: 'http://foobar.com' }) - - return promise - }) - - it('waits for server signal, letting it continue after receiving ready:for:origin failed', function () { - prepareContext({ - req: { - isAUTFrame: true, - proxiedUrl: 'http://www.idp.com/test', - }, - incomingRes: { - headers: { - 'content-type': 'text/html', - }, - }, - secondaryOrigins: ['http://foobar.com', 'http://example.com'], - config: { - experimentalSessionAndOrigin: true, - }, - }) - - const promise = testMiddleware([MaybeDelayForCrossOrigin], ctx) - - expect(ctx.serverBus.emit).to.be.calledWith('cross:origin:delaying:html', { href: 'http://www.idp.com/test' }) - - ctx.serverBus.once.withArgs('ready:for:origin').args[0][1]({ failed: true }) + ctx.serverBus.once.withArgs('cross:origin:release:html').args[0][1]() return promise }) @@ -309,7 +283,7 @@ describe('http/response-middleware', function () { // set the secondary remote states remoteStates.addEventListeners(eventEmitter) props.secondaryOrigins?.forEach((originPolicy) => { - eventEmitter.emit('ready:for:origin', { originPolicy }) + eventEmitter.emit('cross:origin:bridge:ready', { originPolicy }) }) ctx = { @@ -592,7 +566,7 @@ describe('http/response-middleware', function () { // set the secondary remote states remoteStates.addEventListeners(eventEmitter) props.secondaryOrigins?.forEach((originPolicy) => { - eventEmitter.emit('ready:for:origin', { originPolicy }) + eventEmitter.emit('cross:origin:bridge:ready', { originPolicy }) }) ctx = { @@ -914,7 +888,7 @@ describe('http/response-middleware', function () { // set the secondary remote states remoteStates.addEventListeners(eventEmitter) props.secondaryOrigins?.forEach((originPolicy) => { - eventEmitter.emit('ready:for:origin', { originPolicy }) + eventEmitter.emit('cross:origin:bridge:ready', { originPolicy }) }) return { diff --git a/packages/server/lib/experiments.ts b/packages/server/lib/experiments.ts index b962cbdb8b2e..0a0e876c76f0 100644 --- a/packages/server/lib/experiments.ts +++ b/packages/server/lib/experiments.ts @@ -71,7 +71,7 @@ const _summaries: StringValues = { const _names: StringValues = { experimentalFetchPolyfill: 'Fetch Polyfill', experimentalInteractiveRunEvents: 'Interactive Mode Run Events', - experimentalSessionAndOrigin: 'Login Flows', + experimentalSessionAndOrigin: 'Cross-origin and Session', experimentalSourceRewriting: 'Improved Source Rewriting', experimentalStudio: 'Studio', } diff --git a/packages/server/lib/remote_states.ts b/packages/server/lib/remote_states.ts index 7049e78067c1..f9ceb3e6a056 100644 --- a/packages/server/lib/remote_states.ts +++ b/packages/server/lib/remote_states.ts @@ -139,14 +139,8 @@ export class RemoteStates { } addEventListeners (eventEmitter: EventEmitter) { - eventEmitter.on('ready:for:origin', ({ originPolicy, failed }) => { - if (failed) { - debug('received ready:for:origin failed, don\'t add origin to remote states') - - return - } - - debug(`received ready:for:origin, add origin ${originPolicy} to remote states`) + eventEmitter.on('cross:origin:bridge:ready', ({ originPolicy }) => { + debug(`received cross:origin:bridge:ready, add origin ${originPolicy} to remote states`) const existingOrigin = this.remoteStates.get(originPolicy) diff --git a/packages/server/lib/server-base.ts b/packages/server/lib/server-base.ts index cf87e986f852..edad746878e2 100644 --- a/packages/server/lib/server-base.ts +++ b/packages/server/lib/server-base.ts @@ -166,8 +166,8 @@ export abstract class ServerBase { setupCrossOriginRequestHandling () { this._eventBus.on('cross:origin:delaying:html', (request) => { - this.socket.localBus.once('ready:for:origin', (args) => { - this._eventBus.emit('ready:for:origin', args) + this.socket.localBus.once('cross:origin:release:html', () => { + this._eventBus.emit('cross:origin:release:html') }) this.socket.toDriver('cross:origin:delaying:html', request) diff --git a/packages/server/lib/server-e2e.ts b/packages/server/lib/server-e2e.ts index a87a8cc37210..8dadffa18768 100644 --- a/packages/server/lib/server-e2e.ts +++ b/packages/server/lib/server-e2e.ts @@ -291,11 +291,12 @@ export class ServerE2E extends ServerBase { details.totalTime = Date.now() - startTime - // TODO: think about moving this logic back into the - // frontend so that the driver can be in control of - // when the server should cache the request buffer - // and set the domain vs not - if (isOk && details.isHtml) { + // buffer the response and set the remote state if this is a successful html response that is for the same + // origin if the user has already visited an origin or if this is a request from within cy.origin + // TODO: think about moving this logic back into the frontend so that the driver can be in control + // of when to buffer and set the remote state + if (isOk && details.isHtml && + !((options.hasAlreadyVisitedUrl || options.isCrossOrigin) && !cors.urlOriginsMatch(previousRemoteState.origin, newUrl))) { // if we're not handling a local file set the remote state if (!handlingLocalFile) { this.remoteStates.set(newUrl as string, options) @@ -321,6 +322,8 @@ export class ServerE2E extends ServerBase { restorePreviousRemoteState(previousRemoteState, previousRemoteStateIsPrimary) } + details.isPrimaryOrigin = this.remoteStates.isPrimaryOrigin(newUrl!) + return resolve(details) }) diff --git a/packages/server/lib/socket-base.ts b/packages/server/lib/socket-base.ts index 5e5c563d2395..96a14a6b3a98 100644 --- a/packages/server/lib/socket-base.ts +++ b/packages/server/lib/socket-base.ts @@ -414,8 +414,10 @@ export class SocketBase { return } - case 'ready:for:origin': - return this.localBus.emit('ready:for:origin', args[0]) + case 'cross:origin:bridge:ready': + return this.localBus.emit('cross:origin:bridge:ready', args[0]) + case 'cross:origin:release:html': + return this.localBus.emit('cross:origin:release:html') case 'cross:origin:finished': return this.localBus.emit('cross:origin:finished', args[0]) default: diff --git a/packages/server/test/integration/http_requests_spec.js b/packages/server/test/integration/http_requests_spec.js index 047656c7f88e..d1ef6e02e7e3 100644 --- a/packages/server/test/integration/http_requests_spec.js +++ b/packages/server/test/integration/http_requests_spec.js @@ -3028,7 +3028,7 @@ describe('Routes', () => { }) this.server._eventBus.on('cross:origin:delaying:html', () => { - this.server._eventBus.emit('ready:for:origin', { originPolicy: 'http://foobar.com' }) + this.server._eventBus.emit('cross:origin:release:html') }) return this.rp({ diff --git a/packages/server/test/integration/server_spec.js b/packages/server/test/integration/server_spec.js index d51fc8537bf6..b6f9196349fc 100644 --- a/packages/server/test/integration/server_spec.js +++ b/packages/server/test/integration/server_spec.js @@ -145,6 +145,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://localhost:2000/index.html', @@ -176,6 +177,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: false, contentType: 'application/json', url: 'http://localhost:2000/assets/foo.json', @@ -196,6 +198,7 @@ describe('Server', () => { .then((obj = {}) => { expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://localhost:2000/index.html', @@ -213,6 +216,7 @@ describe('Server', () => { .then((obj = {}) => { expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://localhost:2000/index.html', @@ -244,6 +248,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://localhost:2000/sub/', @@ -276,6 +281,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: false, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://localhost:2000/does-not-exist', @@ -303,6 +309,7 @@ describe('Server', () => { .then((obj = {}) => { expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://localhost:2000/index.html', @@ -384,6 +391,7 @@ describe('Server', () => { }).then((obj) => { expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: `http://localhost:${this.httpPort}/${path}/100`, @@ -430,6 +438,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://getbootstrap.com/', @@ -464,6 +473,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: false, isHtml: false, contentType: 'application/json', url: 'http://getbootstrap.com/user.json', @@ -508,6 +518,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: undefined, url: 'http://example.com/', @@ -529,6 +540,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: false, isHtml: false, contentType: undefined, url: 'http://example.com/', @@ -562,6 +574,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://espn.go.com/', @@ -625,6 +638,7 @@ describe('Server', () => { .then((obj = {}) => { expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://espn.go.com/', @@ -644,6 +658,7 @@ describe('Server', () => { .then((obj = {}) => { expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://espn.go.com/', @@ -698,6 +713,7 @@ describe('Server', () => { .then((obj = {}) => { expectToEqDetails(obj, { isOkStatusCode: false, + isPrimaryOrigin: false, isHtml: false, contentType: undefined, url: 'http://espn.com/', @@ -712,6 +728,7 @@ describe('Server', () => { .then((obj = {}) => { expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://espn.go.com/', @@ -747,6 +764,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: false, + isPrimaryOrigin: false, isHtml: true, contentType: 'text/html', url: 'http://mlb.mlb.com/', @@ -780,6 +798,7 @@ describe('Server', () => { .then((obj = {}) => { expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://getbootstrap.com/', @@ -820,6 +839,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://google.com/foo', @@ -876,6 +896,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://google.com/index', @@ -907,7 +928,7 @@ describe('Server', () => { }) context('cross-origin', () => { - it('adds a secondary remote state', function () { + it('adds a remote state and buffers the response when the request is from within cy.origin and the origins match', function () { nock('http://www.cypress.io/') .get('/') .reply(200, 'content', { @@ -923,7 +944,7 @@ describe('Server', () => { fileServer: this.fileServer, }) - this.server.socket.localBus.emit('ready:for:origin', { originPolicy: 'http://cypress.io' }) + this.server.socket.localBus.emit('cross:origin:bridge:ready', { originPolicy: 'http://cypress.io' }) expect(this.server.remoteStates.current()).to.deep.eq({ auth: undefined, @@ -944,6 +965,7 @@ describe('Server', () => { .then((obj = {}) => { expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: false, isHtml: true, contentType: 'text/html', url: 'http://www.cypress.io/', @@ -990,34 +1012,54 @@ describe('Server', () => { }) }) - it('doesn\'t override existing remote state on ready:for:origin', function () { - nock('http://www.cypress.io/') + it('adds a remote state and buffers the response when a url has already been visited and the origins match', function () { + nock('http://localhost:3500/') .get('/') .reply(200, 'content', { 'Content-Type': 'text/html', }) - this.server.socket.localBus.emit('ready:for:origin', { originPolicy: 'http://cypress.io' }) + // this will be the current origin + this.server.remoteStates.set('http://localhost:3500/') + + return this.server._onResolveUrl('http://localhost:3500/', {}, this.automationRequest, { hasAlreadyVisitedUrl: true }) + .then((obj = {}) => { + // Verify the cross origin request was buffered + const buffer = this.buffers.take('http://localhost:3500/') + + expect(buffer).to.not.be.empty - return this.server._onResolveUrl('http://www.cypress.io/', {}, this.automationRequest, { isCrossOrigin: true }) - .then(() => { // Verify the secondary remote state is returned expect(this.server.remoteStates.current()).to.deep.eq({ auth: undefined, props: { - domain: 'cypress', - port: '80', - tld: 'io', + domain: '', + port: '3500', + tld: 'localhost', }, - origin: 'http://www.cypress.io', + origin: 'http://localhost:3500', strategy: 'http', - domainName: 'cypress.io', + domainName: 'localhost', fileServer: null, }) + }) + }) + + it('doesn\'t set a remote state or buffer the response when a url has already been visited and the origins don\'t match', function () { + nock('http://localhost:3500/') + .get('/') + .reply(200, 'content', { + 'Content-Type': 'text/html', + }) - this.server.socket.localBus.emit('ready:for:origin', { originPolicy: 'http://cypress.io' }) + this.server.remoteStates.set('http://localhost:3500/') - // Verify the existing secondary remote state is not overridden + // this will be the current origin + this.server.socket.localBus.emit('cross:origin:bridge:ready', { originPolicy: 'http://cypress.io' }) + + return this.server._onResolveUrl('http://localhost:3500/', {}, this.automationRequest, { hasAlreadyVisitedUrl: true }) + .then(() => { + // Verify the remote state was not updated expect(this.server.remoteStates.current()).to.deep.eq({ auth: undefined, props: { @@ -1025,65 +1067,100 @@ describe('Server', () => { port: '80', tld: 'io', }, - origin: 'http://www.cypress.io', + origin: 'http://cypress.io', strategy: 'http', domainName: 'cypress.io', fileServer: null, }) - }) - }) - context('#get()', () => { - it('returns undefined for not found remote state', function () { - this.server.remoteStates.set('http://www.cypress.io/') + // Verify the cross origin request was not buffered + const buffer = this.buffers.take('http://localhost:3500/') - expect(this.server.remoteStates.get('http://notfound.com/')).to.be.undefined + expect(buffer).to.be.empty }) + }) - it('returns primary remote state', function () { - this.server.remoteStates.set('http://www.cypress.io/', { isCrossOrigin: true }) - - expect(this.server.remoteStates.get('http://localhost:2000')).to.deep.eq({ - auth: undefined, - props: null, - origin: 'http://localhost:2000', - strategy: 'file', - domainName: 'localhost', - fileServer: this.fileServer, - }) + it('doesn\'t set a remote state or buffer the response when the request is from within cy.origin and the origins don\'t match', function () { + nock('http://localhost:3500/') + .get('/') + .reply(200, 'content', { + 'Content-Type': 'text/html', }) - it('returns secondary remote state', function () { - this.server.remoteStates.set('http://www.cypress.io/', { isCrossOrigin: true }) + this.server.remoteStates.set('http://localhost:3500/') - expect(this.server.remoteStates.get('http://cypress.io')).to.deep.eq({ + // this will be the current origin + this.server.socket.localBus.emit('cross:origin:bridge:ready', { originPolicy: 'http://cypress.io' }) + + return this.server._onResolveUrl('http://localhost:3500/', {}, this.automationRequest, { isCrossOrigin: true }) + .then(() => { + // Verify the remote state was not updated + expect(this.server.remoteStates.current()).to.deep.eq({ auth: undefined, props: { domain: 'cypress', port: '80', tld: 'io', }, - origin: 'http://www.cypress.io', + origin: 'http://cypress.io', strategy: 'http', domainName: 'cypress.io', fileServer: null, }) + + // Verify the cross origin request was not buffered + const buffer = this.buffers.take('http://localhost:3500/') + + expect(buffer).to.be.empty }) }) - context('#reset()', () => { - it('returns undefined for not found remote state', function () { - this.server.socket.localBus.emit('ready:for:origin', { originPolicy: 'http://cypress.io' }) + it('doesn\'t override existing remote state on cross:origin:bridge:ready', function () { + nock('http://www.cypress.io/') + .get('/') + .reply(200, 'content', { + 'Content-Type': 'text/html', + }) + + this.server.socket.localBus.emit('cross:origin:bridge:ready', { originPolicy: 'http://cypress.io' }) - expect(this.server.remoteStates.isSecondaryOrigin('http://cypress.io')).to.be.true - expect(this.server.remoteStates.get('http://cypress.io')).to.not.be.undefined + return this.server._onResolveUrl('http://www.cypress.io/', {}, this.automationRequest, { isCrossOrigin: true, auth: { username: 'u', password: 'p' } }) + .then(() => { + // Verify the secondary remote state is returned + expect(this.server.remoteStates.current()).to.deep.eq({ + auth: { + username: 'u', + password: 'p', + }, + props: { + domain: 'cypress', + port: '80', + tld: 'io', + }, + origin: 'http://www.cypress.io', + strategy: 'http', + domainName: 'cypress.io', + fileServer: null, + }) - this.server.remoteStates.reset() + this.server.socket.localBus.emit('cross:origin:bridge:ready', { originPolicy: 'http://cypress.io' }) - expect(this.server.remoteStates.isSecondaryOrigin('http://cypress.io')).to.be.false - expect(this.server.remoteStates.get('http://cypress.io')).to.be.undefined - expect(this.server.remoteStates.isPrimaryOrigin('http://localhost:2000')).to.be.true - expect(this.server.remoteStates.get('http://localhost:2000')).to.not.be.undefined + // Verify the existing secondary remote state is not overridden + expect(this.server.remoteStates.current()).to.deep.eq({ + auth: { + username: 'u', + password: 'p', + }, + props: { + domain: 'cypress', + port: '80', + tld: 'io', + }, + origin: 'http://www.cypress.io', + strategy: 'http', + domainName: 'cypress.io', + fileServer: null, + }) }) }) }) @@ -1113,6 +1190,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://localhost:2000/index.html', @@ -1133,6 +1211,7 @@ describe('Server', () => { }).then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://www.google.com/', @@ -1165,6 +1244,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://localhost:2000/index.html', @@ -1208,6 +1288,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://www.google.com/', @@ -1245,6 +1326,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://localhost:2000/index.html', @@ -1279,6 +1361,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://www.google.com/', @@ -1322,6 +1405,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'https://www.foobar.com:8443/', @@ -1359,6 +1443,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://localhost:2000/index.html', @@ -1393,6 +1478,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'https://www.foobar.com:8443/', @@ -1436,6 +1522,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: s3StaticHtmlUrl, @@ -1481,6 +1568,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: 'http://localhost:2000/index.html', @@ -1515,6 +1603,7 @@ describe('Server', () => { .then((obj = {}) => { return expectToEqDetails(obj, { isOkStatusCode: true, + isPrimaryOrigin: true, isHtml: true, contentType: 'text/html', url: s3StaticHtmlUrl, diff --git a/packages/server/test/unit/remote_states.spec.ts b/packages/server/test/unit/remote_states.spec.ts index 021f74c37d7c..6ad429044c4d 100644 --- a/packages/server/test/unit/remote_states.spec.ts +++ b/packages/server/test/unit/remote_states.spec.ts @@ -94,21 +94,21 @@ describe('remote states', () => { context('#isSecondaryOrigin', () => { it('returns true when the requested url is a secondary origin', function () { - this.eventEmitter.emit('ready:for:origin', { originPolicy: 'https://google.com' }) + this.eventEmitter.emit('cross:origin:bridge:ready', { originPolicy: 'https://google.com' }) const isSecondaryOrigin = this.remoteStates.isSecondaryOrigin('https://staging.google.com') expect(isSecondaryOrigin).to.be.true }) it('returns false when the requested url is the primary origin', function () { - this.eventEmitter.emit('ready:for:origin', { originPolicy: 'https://google.com' }) + this.eventEmitter.emit('cross:origin:bridge:ready', { originPolicy: 'https://google.com' }) const isSecondaryOrigin = this.remoteStates.isSecondaryOrigin('http://localhost:3500') expect(isSecondaryOrigin).to.be.false }) it('returns false when the requested url is not in the origin stack', function () { - this.eventEmitter.emit('ready:for:origin', { originPolicy: 'https://google.com' }) + this.eventEmitter.emit('cross:origin:bridge:ready', { originPolicy: 'https://google.com' }) const isSecondaryOrigin = this.remoteStates.isSecondaryOrigin('https://foobar.com') expect(isSecondaryOrigin).to.be.false @@ -123,7 +123,7 @@ describe('remote states', () => { }) it('returns false when the requested url is not the primary origin', function () { - this.eventEmitter.emit('ready:for:origin', { originPolicy: 'https://google.com' }) + this.eventEmitter.emit('cross:origin:bridge:ready', { originPolicy: 'https://google.com' }) const isPrimaryOrigin = this.remoteStates.isPrimaryOrigin('http://google.com') expect(isPrimaryOrigin).to.be.false @@ -132,7 +132,7 @@ describe('remote states', () => { context('#removeCurrentOrigin', () => { it('removes the current origin from the stack', function () { - this.eventEmitter.emit('ready:for:origin', { originPolicy: 'https://google.com' }) + this.eventEmitter.emit('cross:origin:bridge:ready', { originPolicy: 'https://google.com' }) expect(this.remoteStates.isInOriginStack('https://google.com')).to.be.true this.remoteStates.removeCurrentOrigin('https://google.com') @@ -141,7 +141,7 @@ describe('remote states', () => { }) it('throws an error when trying to remove the incorrect origin', function () { - this.eventEmitter.emit('ready:for:origin', { originPolicy: 'https://google.com' }) + this.eventEmitter.emit('cross:origin:bridge:ready', { originPolicy: 'https://google.com' }) expect(this.remoteStates.isInOriginStack('https://google.com')).to.be.true expect(() => this.remoteStates.removeCurrentOrigin('http://notfound.com')) @@ -151,9 +151,10 @@ describe('remote states', () => { context('#reset', () => { it('resets the origin stack and remote states to the primary', function () { - this.eventEmitter.emit('ready:for:origin', { originPolicy: 'https://google.com' }) + this.eventEmitter.emit('cross:origin:bridge:ready', { originPolicy: 'https://google.com' }) expect(this.remoteStates.isInOriginStack('https://google.com')).to.be.true + expect(this.remoteStates.get('https://google.com')).to.not.be.undefined this.remoteStates.reset() @@ -164,7 +165,7 @@ describe('remote states', () => { context('#current', () => { it('returns the remote state for the current origin in the stack', function () { - this.eventEmitter.emit('ready:for:origin', { originPolicy: 'https://google.com' }) + this.eventEmitter.emit('cross:origin:bridge:ready', { originPolicy: 'https://google.com' }) this.remoteStates.set('https://staging.google.com/foo/bar', { isCrossOrigin: true }) const state = this.remoteStates.current() @@ -203,6 +204,8 @@ describe('remote states', () => { }, }) + expect(this.remoteStates.get('https://staging.google.com')).to.deep.equal(state) + expect(this.remoteStates.isPrimaryOrigin('https://staging.google.com')).to.be.true }) @@ -226,6 +229,8 @@ describe('remote states', () => { }, }) + expect(this.remoteStates.get('https://staging.google.com')).to.deep.equal(state) + expect(this.remoteStates.isPrimaryOrigin('http://localhost:3500')).to.be.true expect(this.remoteStates.isPrimaryOrigin('https://staging.google.com')).to.be.false }) @@ -366,36 +371,24 @@ describe('remote states', () => { }) context('events', () => { - it('can add a secondary remote state on ready:for:origin', function () { + it('can add a secondary remote state on cross:origin:bridge:ready', function () { let currentState = this.remoteStates.current() expect(currentState.origin).to.equal('http://localhost:3500') - this.eventEmitter.emit('ready:for:origin', { originPolicy: 'http://cypress.io' }) + this.eventEmitter.emit('cross:origin:bridge:ready', { originPolicy: 'http://cypress.io' }) currentState = this.remoteStates.current() expect(currentState.origin).to.equal('http://cypress.io') expect(this.remoteStates.isSecondaryOrigin(currentState.origin)).to.be.true }) - it('doesn\'t do anything if ready:for:origin failed', function () { - let currentState = this.remoteStates.current() - - expect(currentState.origin).to.equal('http://localhost:3500') - - this.eventEmitter.emit('ready:for:origin', { failed: true }) - - currentState = this.remoteStates.current() - expect(currentState.origin).to.equal('http://localhost:3500') - expect(this.remoteStates.isSecondaryOrigin(currentState.origin)).to.be.false - }) - it('removes the current origin when cross:origin:finished is received', function () { let currentState = this.remoteStates.current() expect(currentState.origin).to.equal('http://localhost:3500') - this.eventEmitter.emit('ready:for:origin', { originPolicy: 'http://cypress.io' }) + this.eventEmitter.emit('cross:origin:bridge:ready', { originPolicy: 'http://cypress.io' }) currentState = this.remoteStates.current() expect(currentState.origin).to.equal('http://cypress.io') @@ -406,22 +399,22 @@ describe('remote states', () => { expect(currentState.origin).to.equal('http://localhost:3500') }) - it('doesn\'t override an existing secondary remote state on ready:for:origin', function () { + it('doesn\'t override an existing secondary remote state on cross:origin:bridge:ready', function () { let currentState = this.remoteStates.current() expect(currentState.origin).to.equal('http://localhost:3500') - // simulate a cy.origin by calling ready:for:origin followed by setting + // simulate a cy.origin by calling cross:origin:bridge:ready followed by setting // the origin with specific auth options and finally calling cross:origin:finished - this.eventEmitter.emit('ready:for:origin', { originPolicy: 'http://cypress.io' }) + this.eventEmitter.emit('cross:origin:bridge:ready', { originPolicy: 'http://cypress.io' }) this.remoteStates.set('http://cypress.io', { auth: { username: 'u', password: 'p' }, isCrossOrigin: true }) currentState = this.remoteStates.current() expect(currentState.origin).to.equal('http://cypress.io') expect(currentState.auth).to.deep.equal({ username: 'u', password: 'p' }) this.eventEmitter.emit('cross:origin:finished', 'http://cypress.io') - // verify calling ready:for:origin doesn't reset the previous state - this.eventEmitter.emit('ready:for:origin', { originPolicy: 'http://cypress.io' }) + // verify calling cross:origin:bridge:ready doesn't reset the previous state + this.eventEmitter.emit('cross:origin:bridge:ready', { originPolicy: 'http://cypress.io' }) currentState = this.remoteStates.current() expect(currentState.origin).to.equal('http://cypress.io') diff --git a/packages/server/test/unit/socket_spec.js b/packages/server/test/unit/socket_spec.js index 8a4d659bfaa0..93e4666beca2 100644 --- a/packages/server/test/unit/socket_spec.js +++ b/packages/server/test/unit/socket_spec.js @@ -563,16 +563,25 @@ describe('lib/socket', () => { }) }) - context('on(ready:for:origin)', () => { - it('emits ready:for:origin on local bus', function (done) { - this.server.socket.localBus.once('ready:for:origin', ({ originPolicy, failed }) => { + context('on(cross:origin:bridge:ready)', () => { + it('emits cross:origin:bridge:ready on local bus', function (done) { + this.server.socket.localBus.once('cross:origin:bridge:ready', ({ originPolicy }) => { expect(originPolicy).to.equal('http://foobar.com') - expect(failed).to.be.false done() }) - this.client.emit('backend:request', 'ready:for:origin', { originPolicy: 'http://foobar.com', failed: false }, () => {}) + this.client.emit('backend:request', 'cross:origin:bridge:ready', { originPolicy: 'http://foobar.com' }, () => {}) + }) + }) + + context('on(cross:origin:release:html)', () => { + it('emits cross:origin:release:html on local bus', function (done) { + this.server.socket.localBus.once('cross:origin:release:html', () => { + done() + }) + + this.client.emit('backend:request', 'cross:origin:release:html', () => {}) }) }) @@ -585,7 +594,7 @@ describe('lib/socket', () => { }) // add the origin before calling cross:origin:finished (otherwise we'll fail trying to remove the origin) - this.client.emit('backend:request', 'ready:for:origin', { originPolicy: 'http://foobar.com' }, () => {}) + this.client.emit('backend:request', 'cross:origin:bridge:ready', { originPolicy: 'http://foobar.com' }, () => {}) this.client.emit('backend:request', 'cross:origin:finished', 'http://foobar.com', () => {}) }) diff --git a/system-tests/__snapshots__/runnable_execution_spec.ts.js b/system-tests/__snapshots__/runnable_execution_spec.ts.js index d8e37324a4fd..0d6074bf3faf 100644 --- a/system-tests/__snapshots__/runnable_execution_spec.ts.js +++ b/system-tests/__snapshots__/runnable_execution_spec.ts.js @@ -38,11 +38,11 @@ exports['e2e runnable execution / cannot navigate in before hook and test'] = ` In order to visit a different origin, you can enable the \`experimentalSessionAndOrigin\` flag and use \`cy.origin()\`: \`cy.visit('http://localhost:4545/')\` -\`\` +\`\` \`cy.origin('http://localhost:5656', () => {\` \` cy.visit('http://localhost:5656/')\` -\` \` +\` \` \`})\` The new URL is considered a different origin because the following parts of the URL are different: @@ -69,11 +69,11 @@ https://on.cypress.io/cannot-visit-different-origin-domain In order to visit a different origin, you can enable the \`experimentalSessionAndOrigin\` flag and use \`cy.origin()\`: \`cy.visit('http://localhost:4545/')\` -\`\` +\`\` \`cy.origin('http://localhost:5656', () => {\` \` cy.visit('http://localhost:5656/')\` -\` \` +\` \` \`})\` The new URL is considered a different origin because the following parts of the URL are different: