Skip to content

Commit

Permalink
fix: make sure scrollTo, window, document, title, go, reload, locatio…
Browse files Browse the repository at this point in the history
…n, hash, and url commands can communicate with the AUT
  • Loading branch information
AtofStryker committed Jan 13, 2025
1 parent 0e8a766 commit d2aaba8
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 32 deletions.
1 change: 1 addition & 0 deletions cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ in this [GitHub issue](/~https://github.com/cypress-io/cypress/issues/30447). Addr
- Fixed a visibility issue for elements with `textContent` but without a width or height. Fixed in [#29688](/~https://github.com/cypress-io/cypress/pull/29688). Fixes [#29687](/~https://github.com/cypress-io/cypress/issues/29687).
- 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).
- `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

0 comments on commit d2aaba8

Please sign in to comment.