Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: (cross-origin) add support for redirecting back to primary #21144

Merged
merged 9 commits into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 21 additions & 36 deletions packages/driver/cypress/integration/commands/navigation_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')\`
\`<other commands targeting http://localhost:3500 go here>\`\n
\`<commands targeting http://localhost:3500 go here>\`\n
\`cy.origin('http://localhost:3501', () => {\`
\` cy.visit('http://localhost:3501/fixtures/generic.html')\`
\` <other commands targeting http://localhost:3501 go here>\`
\` <commands targeting http://localhost:3501 go here>\`
\`})\`\n
The new URL is considered a different origin because the following parts of the URL are different:\n
> port\n
Expand Down Expand Up @@ -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')\`
\`<other commands targeting http://localhost:3500 go here>\`\n
\`cy.origin('https://localhost:3500', () => {\`
\` cy.visit('https://localhost:3500/fixtures/generic.html')\`
\` <other commands targeting https://localhost:3500 go here>\`
\`<commands targeting http://localhost:3500 go here>\`\n
\`cy.origin('https://localhost:3502', () => {\`
\` cy.visit('https://localhost:3502/fixtures/generic.html')\`
\` <commands targeting https://localhost:3502 go here>\`
\`})\`\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)
Expand All @@ -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) {
Expand All @@ -1478,18 +1478,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')\`
\`<other commands targeting http://localhost:3500 go here>\`\n
\`cy.origin('http://google.com:3500', () => {\`
\` cy.visit('http://google.com:3500/fixtures/generic.html')\`
\` <other commands targeting http://google.com:3500 go here>\`
\`<commands targeting http://localhost:3500 go here>\`\n
\`cy.origin('http://foobar.com:3500', () => {\`
\` cy.visit('http://www.foobar.com:3500/fixtures/generic.html')\`
\` <commands targeting http://www.foobar.com:3500 go here>\`
\`})\`\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://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)
Expand All @@ -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) {
Expand All @@ -1510,18 +1510,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://127.0.0.1:3500/fixtures/generic.html')\`
\`<other commands targeting http://127.0.0.1:3500 go here>\`\n
\`cy.origin('http://126.0.0.1:3500', () => {\`
\` cy.visit('http://126.0.0.1:3500/fixtures/generic.html')\`
\` <other commands targeting http://126.0.0.1:3500 go here>\`
\`<commands targeting http://127.0.0.1:3500 go here>\`\n
\`cy.origin('http://0.0.0.0:3500', () => {\`
\` cy.visit('http://0.0.0.0:3500/fixtures/generic.html')\`
\` <commands targeting http://0.0.0.0:3500 go here>\`
\`})\`\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://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)
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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')\`
\` <other commands targeting http://www.foobar.com:3500 go here>\`
\` <commands targeting http://www.foobar.com:3500 go here>\`
\`})\`\n
\`cy.origin('http://idp.com:3500', () => {\`
\` cy.visit('http://www.idp.com:3500/fixtures/dom.html')\`
\` <other commands targeting http://www.idp.com:3500 go here>\`
\` <commands targeting http://www.idp.com:3500 go here>\`
\`})\`\n
The new URL is considered a different origin because the following parts of the URL are different:\n
> superdomain\n
Expand Down Expand Up @@ -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')\`
\`<other commands targeting http://localhost:3500 go here>\`\n
\`<commands targeting http://localhost:3500 go here>\`\n
\`cy.origin('http://foobar.com:3500', () => {\`
\` cy.visit('http://www.foobar.com:3500/fixtures/dom.html')\`
\` <other commands targeting http://www.foobar.com:3500 go here>\`
\` <commands targeting http://www.foobar.com:3500 go here>\`
\`})\`\n
The new URL is considered a different origin because the following parts of the URL are different:\n
> superdomain\n
Expand Down Expand Up @@ -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', () => {
Expand All @@ -321,6 +321,79 @@ context('cy.origin navigation', () => {
})
})

it('supports redirecting from secondary to primary outside of cy.origin', () => {
mschile marked this conversation as resolved.
Show resolved Hide resolved
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
\`<commands targeting http://localhost:3500 go here>\`\n
\`cy.origin('http://foobar.com:3500', () => {\`
\` <commands targeting http://foobar.com:3500 go here>\`
\`})\`\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
\`<commands targeting http://localhost:3500 go here>\`\n
\`cy.origin('http://foobar.com:3500', () => {\`
\` <commands targeting http://foobar.com:3500 go here>\`
\`})\`\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')\`
\`<commands targeting http://localhost:3500 go here>\`\n
\`cy.origin('http://foobar.com:3500', () => {\`
\` cy.visit('http://localhost:3500/redirect?href=http://www.foobar.com:3500/fixtures/generic.html')\`
\` <commands targeting http://www.foobar.com:3500 go here>\`
\`})\`\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', {
Expand Down
61 changes: 36 additions & 25 deletions packages/driver/src/cy/commands/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -111,6 +111,7 @@ const cannotVisitDifferentOrigin = ({ remote, existing, previousUrlVisited, log,
differences: differences.join(', '),
previousUrl: previousUrlVisited,
attemptedUrl: remote,
originalUrl,
isCrossOriginSpecBridge,
experimentalSessionAndOrigin: Cypress.config('experimentalSessionAndOrigin'),
},
Expand All @@ -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,
Expand Down Expand Up @@ -494,6 +511,7 @@ const normalizeOptions = (options) => {
.extend({
timeout: options.responseTimeout,
isCrossOrigin: Cypress.isCrossOriginSpecBridge,
hasAlreadyVisitedUrl: options.hasAlreadyVisitedUrl,
})
.value()
}
Expand Down Expand Up @@ -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) } })
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
mschile marked this conversation as resolved.
Show resolved Hide resolved
if (remote.originPolicy === existing.originPolicy) {
previousUrlVisited = remote

Expand All @@ -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
Expand Down
Loading