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

fix: commands not chained off of cy.get() or queries properly error when cy.origin() is not used and the AUT has navigated away from the primary origin #30858

Merged
merged 1 commit into from
Jan 13, 2025
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
1 change: 1 addition & 0 deletions cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ in this [GitHub issue](https://github.com/cypress-io/cypress/issues/30447). Addr
- Elements whose parent elements has `overflow: clip` and no height/width will now correctly show as hidden. Fixed in [#29778](https://github.com/cypress-io/cypress/pull/29778). Fixes [#23852](https://github.com/cypress-io/cypress/issues/23852).
- The CSS pseudo-class `:dir()` is now supported when testing in Electron. Addresses [#29766](https://github.com/cypress-io/cypress/issues/29766).
- Fixed an issue where the spec filename was not updating correctly when changing specs in `open` mode. Fixes [#30852](https://github.com/cypress-io/cypress/issues/30852).
- `cy.origin()` now correctly errors when the [`cy.window()`](https://docs.cypress.io/api/commands/window), [`cy.document()`](https://docs.cypress.io/api/commands/document), [`cy.title()`](https://docs.cypress.io/api/commands/title), [`cy.url()`](https://docs.cypress.io/api/commands/url), [`cy.location()`](https://docs.cypress.io/api/commands/location) ,[`cy.hash()`](https://docs.cypress.io/api/commands/hash), [`cy.go()`](https://docs.cypress.io/api/commands/go), [`cy.reload()`](https://docs.cypress.io/api/commands/reload), and [`cy.scrollTo()`](https://docs.cypress.io/api/commands/scrollTo) commands are used outside of the `cy.origin()` command after the AUT has navigated away from the primary origin. Fixes [#30848](https://github.com/cypress-io/cypress/issues/30848). Fixed in [#30858](https://github.com/cypress-io/cypress/pull/30858).

**Misc:**

Expand Down
104 changes: 95 additions & 9 deletions packages/driver/cypress/e2e/e2e/origin/commands/actions.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,23 +190,109 @@ context('cy.origin actions', { browser: '!webkit' }, () => {

context('cross-origin AUT errors', () => {
// We only need to check .get here because the other commands are chained off of it.
// the exceptions are window(), document(), title(), url(), hash(), location(), go(), reload(), and scrollTo()
const assertOriginFailure = (err: Error, done: () => void) => {
expect(err.message).to.include(`The command was expected to run against origin \`http://localhost:3500\` but the application is at origin \`http://www.foobar.com:3500\`.`)
expect(err.message).to.include(`This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.`)
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.foobar.com:3500\` will likely fix this issue.`)
expect(err.message).to.include(`cy.origin('http://www.foobar.com:3500', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})`)

// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
done()
}

it('.get()', { defaultCommandTimeout: 50 }, (done) => {
cy.on('fail', (err) => {
expect(err.message).to.include(`Timed out retrying after 50ms:`)
expect(err.message).to.include(`The command was expected to run against origin \`http://localhost:3500\` but the application is at origin \`http://www.foobar.com:3500\`.`)
expect(err.message).to.include(`This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.`)
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.foobar.com:3500\` will likely fix this issue.`)
expect(err.message).to.include(`cy.origin('http://www.foobar.com:3500', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})`)

// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
done()
assertOriginFailure(err, done)
})

cy.get('a[data-cy="dom-link"]').click()
cy.get('#button')
})

it('.window()', (done) => {
cy.on('fail', (err) => {
assertOriginFailure(err, done)
})

cy.get('a[data-cy="dom-link"]').click()
cy.window()
})

it('.document()', (done) => {
cy.on('fail', (err) => {
assertOriginFailure(err, done)
})

cy.get('a[data-cy="dom-link"]').click()
cy.document()
})

it('.title()', (done) => {
cy.on('fail', (err) => {
assertOriginFailure(err, done)
})

cy.get('a[data-cy="dom-link"]').click()
cy.title()
})

it('.url()', (done) => {
cy.on('fail', (err) => {
assertOriginFailure(err, done)
})

cy.get('a[data-cy="dom-link"]').click()
cy.url()
})

it('.hash()', (done) => {
cy.on('fail', (err) => {
assertOriginFailure(err, done)
})

cy.get('a[data-cy="dom-link"]').click()
cy.hash()
})

it('.location()', (done) => {
cy.on('fail', (err) => {
assertOriginFailure(err, done)
})

cy.get('a[data-cy="dom-link"]').click()
cy.location()
})

it('.go()', (done) => {
cy.on('fail', (err) => {
assertOriginFailure(err, done)
})

cy.get('a[data-cy="dom-link"]').click()
cy.go('back')
})

it('.reload()', (done) => {
cy.on('fail', (err) => {
assertOriginFailure(err, done)
})

cy.get('a[data-cy="dom-link"]').click()
cy.reload()
})

it('.scrollTo()', (done) => {
cy.on('fail', (err) => {
assertOriginFailure(err, done)
})

cy.get('a[data-cy="dom-link"]').click()
cy.scrollTo('bottom')
})
})

context('#consoleProps', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,10 @@ context('cy.origin viewport', { browser: '!webkit' }, () => {

cy.window().its('innerHeight').should('eq', 480)
cy.window().its('innerWidth').should('eq', 320)
})

cy.window().then((win) => {
win.location.href = 'http://www.idp.com:3500/fixtures/primary-origin.html'
cy.window().then((win) => {
win.location.href = 'http://www.idp.com:3500/fixtures/primary-origin.html'
})
})

cy.origin('http://www.idp.com:3500', () => {
Expand Down
10 changes: 5 additions & 5 deletions packages/driver/cypress/e2e/e2e/origin/navigation.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,11 @@ describe('event timing', { browser: '!webkit' }, () => {

cy.origin('http://www.foobar.com:3500', () => {
cy.log('inside cy.origin foobar')
})

// This command is run from localhost against the cross-origin aut. Updating href is one of the few allowed commands. See https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#location
cy.window().then((win) => {
win.location.href = 'http://www.idp.com:3500/fixtures/primary-origin.html'
// Updating href is one of the few allowed commands. See https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#location
// However, not everything on the window is accessible. Therefore, we force window() to only run on the same origin as the AUT context
cy.window().then((win) => {
win.location.href = 'http://www.idp.com:3500/fixtures/primary-origin.html'
})
})

cy.origin('http://www.idp.com:3500', () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/driver/src/cy/commands/actions/scroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,9 @@ export default (Commands, Cypress, cy, state) => {
const subjectChain = cy.subjectChain()

const ensureScrollability = () => {
// Make sure the scroll command can communicate with the AUT
Cypress.ensure.commandCanCommunicateWithAUT(cy)

try {
subject = cy.getSubjectFromChain(subjectChain)

Expand Down
9 changes: 9 additions & 0 deletions packages/driver/src/cy/commands/location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import $errUtils from '../../cypress/error_utils'

export default (Commands, Cypress, cy) => {
Commands.addQuery('url', function url (options: Partial<Cypress.UrlOptions> = {}) {
// Make sure the url command can communicate with the AUT.
// otherwise, it yields an empty string
Cypress.ensure.commandCanCommunicateWithAUT(cy)
this.set('timeout', options.timeout)

Cypress.log({ message: '', hidden: options.log === false, timeout: options.timeout })
Expand All @@ -16,6 +19,8 @@ export default (Commands, Cypress, cy) => {
})

Commands.addQuery('hash', function url (options: Partial<Cypress.Loggable & Cypress.Timeoutable> = {}) {
// Make sure the hash command can communicate with the AUT.
Cypress.ensure.commandCanCommunicateWithAUT(cy)
this.set('timeout', options.timeout)

Cypress.log({ message: '', hidden: options.log === false, timeout: options.timeout })
Expand All @@ -26,6 +31,10 @@ export default (Commands, Cypress, cy) => {
Commands.addQuery('location', function location (key, options: Partial<Cypress.Loggable & Cypress.Timeoutable> = {}) {
// normalize arguments allowing key + options to be undefined
// key can represent the options

// Make sure the location command can communicate with the AUT.
// otherwise the command just yields 'null' and the reason may be unclear to the user.
Cypress.ensure.commandCanCommunicateWithAUT(cy)
if (_.isObject(key)) {
options = key
}
Expand Down
7 changes: 7 additions & 0 deletions packages/driver/src/cy/commands/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,10 @@ export default (Commands, Cypress, cy, state, config) => {
cleanup()
}

// Make sure the reload command can communicate with the AUT.
// if we failed for any other reason, we need to display the correct error to the user.
Cypress.ensure.commandCanCommunicateWithAUT(cy)

return null
})
},
Expand Down Expand Up @@ -700,6 +704,9 @@ export default (Commands, Cypress, cy, state, config) => {
cleanup()
}

// Make sure the go command can communicate with the AUT.
Cypress.ensure.commandCanCommunicateWithAUT(cy)

return null
})
}
Expand Down
7 changes: 7 additions & 0 deletions packages/driver/src/cy/commands/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,18 @@ export default (Commands, Cypress, cy, state) => {
}

Commands.addQuery('title', function title (options: Partial<Cypress.Loggable & Cypress.Timeoutable> = {}) {
// Make sure the window command can communicate with the AUT.
// otherwise, it yields an empty string
Cypress.ensure.commandCanCommunicateWithAUT(cy)
this.set('timeout', options.timeout)
Cypress.log({ timeout: options.timeout, hidden: options.log === false })

return () => (state('document')?.title || '')
})

Commands.addQuery('window', function windowFn (options: Partial<Cypress.Loggable & Cypress.Timeoutable> = {}) {
// Make sure the window command can communicate with the AUT.
Cypress.ensure.commandCanCommunicateWithAUT(cy)
this.set('timeout', options.timeout)
Cypress.log({
hidden: options.log === false,
Expand All @@ -114,6 +119,8 @@ export default (Commands, Cypress, cy, state) => {
})

Commands.addQuery('document', function documentFn (options: Partial<Cypress.Loggable & Cypress.Timeoutable> = {}) {
// Make sure the document command can communicate with the AUT.
Cypress.ensure.commandCanCommunicateWithAUT(cy)
this.set('timeout', options.timeout)
Cypress.log({
hidden: options.log === false,
Expand Down
6 changes: 3 additions & 3 deletions packages/graphql/schemas/cloud.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ type CloudProjectNotFound {
}

union CloudProjectResult =
CloudProject
| CloudProject
| CloudProjectNotFound
| CloudProjectUnauthorized

Expand Down Expand Up @@ -456,7 +456,7 @@ type CloudProjectSpec implements Node {
}

union CloudProjectSpecFlakyResult =
CloudFeatureNotEnabled
| CloudFeatureNotEnabled
| CloudProjectSpecFlakyStatus

type CloudProjectSpecFlakyStatus {
Expand Down Expand Up @@ -500,7 +500,7 @@ type CloudProjectSpecNotFound {
}

union CloudProjectSpecResult =
CloudProjectSpec
| CloudProjectSpec
| CloudProjectSpecNotFound
| CloudProjectUnauthorized

Expand Down
39 changes: 27 additions & 12 deletions system-tests/__snapshots__/web_security_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,32 +30,47 @@ exports['e2e web security / when enabled / fails'] = `

1) web security
fails when clicking <a> to another origin:
CypressError: The command was expected to run against origin \`http://localhost:4466\` but the application is at origin \`https://www.foo.com:44665\`.

Timed out retrying after 4000ms
+ expected - actual
This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.

+'https://www.foo.com:44665/cross_origin'

Using \`cy.origin()\` to wrap the commands run on \`https://www.foo.com:44665\` will likely fix this issue.

\`cy.origin('https://www.foo.com:44665', () => {\`
\` <commands targeting https://www.foo.com:44665 go here>\`
\`})\`

https://on.cypress.io/cy-visit-succeeded-but-commands-fail
[stack trace lines]

2) web security
fails when submitted a form and being redirected to another origin:
CypressError: The command was expected to run against origin \`http://localhost:4466\` but the application is at origin \`https://www.foo.com:44665\`.

This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.

Timed out retrying after 4000ms
+ expected - actual
Using \`cy.origin()\` to wrap the commands run on \`https://www.foo.com:44665\` will likely fix this issue.

+'https://www.foo.com:44665/cross_origin'

\`cy.origin('https://www.foo.com:44665', () => {\`
\` <commands targeting https://www.foo.com:44665 go here>\`
\`})\`

https://on.cypress.io/cy-visit-succeeded-but-commands-fail
[stack trace lines]

3) web security
fails when using a javascript redirect to another origin:
CypressError: The command was expected to run against origin \`http://localhost:4466\` but the application is at origin \`https://www.foo.com:44665\`.

This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.

Using \`cy.origin()\` to wrap the commands run on \`https://www.foo.com:44665\` will likely fix this issue.

Timed out retrying after 4000ms
+ expected - actual
\`cy.origin('https://www.foo.com:44665', () => {\`
\` <commands targeting https://www.foo.com:44665 go here>\`
\`})\`

+'https://www.foo.com:44665/cross_origin'

https://on.cypress.io/cy-visit-succeeded-but-commands-fail
[stack trace lines]

4) web security
Expand Down
Loading