mirror of
https://github.com/cypress-io/cypress.git
synced 2026-05-06 15:00:50 -05:00
Merge branch 'develop' into md-10.0-merge
This commit is contained in:
Vendored
+2
-1
@@ -5,7 +5,8 @@
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"name": "Attach by Process ID",
|
||||
"processId": "${command:PickProcess}"
|
||||
"processId": "${command:PickProcess}",
|
||||
"continueOnAttach": true
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"chrome:beta": "101.0.4951.34",
|
||||
"chrome:beta": "101.0.4951.41",
|
||||
"chrome:stable": "100.0.4896.127"
|
||||
}
|
||||
|
||||
+81
-4
@@ -38,6 +38,8 @@ linuxWorkflowExcludeFilters: &linux-workflow-exclude-filters
|
||||
or:
|
||||
- false
|
||||
# - equal: [ 'tgriesser/chore/fix-windows-build', << pipeline.git.branch >> ]
|
||||
- feature-multidomain
|
||||
- unify-1449-beta-slug-length
|
||||
|
||||
# usually we don't build Mac app - it takes a long time
|
||||
# but sometimes we want to really confirm we are doing the right thing
|
||||
@@ -48,6 +50,8 @@ macWorkflowFilters: &mac-workflow-filters
|
||||
- equal: [ develop, << pipeline.git.branch >> ]
|
||||
- equal: [ '10.0-release', << pipeline.git.branch >> ]
|
||||
- equal: [ chore/cutover-to-bundled-react-mount, << pipeline.git.branch >> ]
|
||||
- equal: [ feature-multidomain, << pipeline.git.branch >> ]
|
||||
- equal: [ unify-1449-beta-slug-length, << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: "-release$"
|
||||
value: << pipeline.git.branch >>
|
||||
@@ -59,6 +63,8 @@ windowsWorkflowFilters: &windows-workflow-filters
|
||||
- equal: [ '10.0-release', << pipeline.git.branch >> ]
|
||||
- equal: [ chore/cutover-to-bundled-react-mount, << pipeline.git.branch >> ]
|
||||
- equal: [ 'unify-1036-windows-test-projects', << pipeline.git.branch >> ]
|
||||
- equal: [ feature-multidomain, << pipeline.git.branch >> ]
|
||||
- equal: [ unify-1449-beta-slug-length, << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: "-release$"
|
||||
value: << pipeline.git.branch >>
|
||||
@@ -416,6 +422,10 @@ commands:
|
||||
description: chrome channel to install
|
||||
type: string
|
||||
default: ''
|
||||
experimentalSessionAndOrigin:
|
||||
description: experimental flag to apply
|
||||
type: boolean
|
||||
default: false
|
||||
steps:
|
||||
- restore_cached_workspace
|
||||
- when:
|
||||
@@ -433,8 +443,13 @@ commands:
|
||||
|
||||
if [[ -v MAIN_RECORD_KEY ]]; then
|
||||
# internal PR
|
||||
CYPRESS_RECORD_KEY=$MAIN_RECORD_KEY \
|
||||
yarn cypress:run --record --parallel --group 5x-driver-<<parameters.browser>> --browser <<parameters.browser>>
|
||||
if <<parameters.experimentalSessionAndOrigin>>; then
|
||||
CYPRESS_RECORD_KEY=$MAIN_RECORD_KEY \
|
||||
yarn cypress:run-experimentalSessionAndOrigin --record --parallel --group 5x-driver-<<parameters.browser>>-experimentalSessionAndOrigin --browser <<parameters.browser>>
|
||||
else
|
||||
CYPRESS_RECORD_KEY=$MAIN_RECORD_KEY \
|
||||
yarn cypress:run --record --parallel --group 5x-driver-<<parameters.browser>> --browser <<parameters.browser>>
|
||||
fi
|
||||
else
|
||||
# external PR
|
||||
TESTFILES=$(circleci tests glob "cypress/e2e/**/*.cy.*" | circleci tests split --total=$CIRCLE_NODE_TOTAL)
|
||||
@@ -443,7 +458,11 @@ commands:
|
||||
if [[ -z "$TESTFILES" ]]; then
|
||||
echo "Empty list of test files"
|
||||
fi
|
||||
yarn cypress:run --browser <<parameters.browser>> --spec $TESTFILES
|
||||
if <<parameters.experimentalSessionAndOrigin>>; then
|
||||
yarn cypress:run-experimentalSessionAndOrigin --browser <<parameters.browser>> --spec $TESTFILES
|
||||
else
|
||||
yarn cypress:run --browser <<parameters.browser>> --spec $TESTFILES
|
||||
fi
|
||||
fi
|
||||
working_directory: packages/driver
|
||||
- verify-mocha-results
|
||||
@@ -1415,6 +1434,44 @@ jobs:
|
||||
- run-driver-integration-tests:
|
||||
browser: electron
|
||||
|
||||
driver-integration-tests-chrome-experimentalSessionAndOrigin:
|
||||
<<: *defaults
|
||||
resource_class: medium
|
||||
parallelism: 5
|
||||
steps:
|
||||
- run-driver-integration-tests:
|
||||
browser: chrome
|
||||
install-chrome-channel: stable
|
||||
experimentalSessionAndOrigin: true
|
||||
|
||||
driver-integration-tests-chrome-beta-experimentalSessionAndOrigin:
|
||||
<<: *defaults
|
||||
resource_class: medium
|
||||
parallelism: 5
|
||||
steps:
|
||||
- run-driver-integration-tests:
|
||||
browser: chrome:beta
|
||||
install-chrome-channel: beta
|
||||
experimentalSessionAndOrigin: true
|
||||
|
||||
driver-integration-tests-firefox-experimentalSessionAndOrigin:
|
||||
<<: *defaults
|
||||
resource_class: medium
|
||||
parallelism: 5
|
||||
steps:
|
||||
- run-driver-integration-tests:
|
||||
browser: firefox
|
||||
experimentalSessionAndOrigin: true
|
||||
|
||||
driver-integration-tests-electron-experimentalSessionAndOrigin:
|
||||
<<: *defaults
|
||||
resource_class: medium
|
||||
parallelism: 5
|
||||
steps:
|
||||
- run-driver-integration-tests:
|
||||
browser: electron
|
||||
experimentalSessionAndOrigin: true
|
||||
|
||||
reporter-integration-tests:
|
||||
<<: *defaults
|
||||
parallelism: 3
|
||||
@@ -1719,7 +1776,7 @@ jobs:
|
||||
- run:
|
||||
name: Check current branch to persist artifacts
|
||||
command: |
|
||||
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "chore/cutover-to-bundled-react-mount" && "$CIRCLE_BRANCH" != "unify-1036-windows-test-projects" && "$CIRCLE_BRANCH" != "10.0-release" ]]; then
|
||||
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "chore/cutover-to-bundled-react-mount" && "$CIRCLE_BRANCH" != "unify-1036-windows-test-projects" && "$CIRCLE_BRANCH" != "10.0-release" && "$CIRCLE_BRANCH" != "unify-1449-beta-slug-length" && "$CIRCLE_BRANCH" != "feature-multidomain" ]]; then
|
||||
echo "Not uploading artifacts or posting install comment for this branch."
|
||||
circleci-agent step halt
|
||||
fi
|
||||
@@ -2242,6 +2299,22 @@ linux-workflow: &linux-workflow
|
||||
context: test-runner:cypress-record-key
|
||||
requires:
|
||||
- build
|
||||
- driver-integration-tests-chrome-experimentalSessionAndOrigin:
|
||||
context: test-runner:cypress-record-key
|
||||
requires:
|
||||
- build
|
||||
- driver-integration-tests-chrome-beta-experimentalSessionAndOrigin:
|
||||
context: test-runner:cypress-record-key
|
||||
requires:
|
||||
- build
|
||||
- driver-integration-tests-firefox-experimentalSessionAndOrigin:
|
||||
context: test-runner:cypress-record-key
|
||||
requires:
|
||||
- build
|
||||
- driver-integration-tests-electron-experimentalSessionAndOrigin:
|
||||
context: test-runner:cypress-record-key
|
||||
requires:
|
||||
- build
|
||||
- run-frontend-shared-component-tests-chrome:
|
||||
context: [test-runner:launchpad-tests, test-runner:percy]
|
||||
percy: true
|
||||
@@ -2346,6 +2419,10 @@ linux-workflow: &linux-workflow
|
||||
- driver-integration-tests-chrome
|
||||
- driver-integration-tests-chrome-beta
|
||||
- driver-integration-tests-electron
|
||||
- driver-integration-tests-firefox-experimentalSessionAndOrigin
|
||||
- driver-integration-tests-chrome-experimentalSessionAndOrigin
|
||||
- driver-integration-tests-chrome-beta-experimentalSessionAndOrigin
|
||||
- driver-integration-tests-electron-experimentalSessionAndOrigin
|
||||
- system-tests-non-root
|
||||
- system-tests-firefox
|
||||
- system-tests-electron
|
||||
|
||||
Vendored
+68
-26
@@ -10,10 +10,13 @@ declare namespace Cypress {
|
||||
type PrevSubject = keyof PrevSubjectMap
|
||||
type TestingType = 'e2e' | 'component'
|
||||
type PluginConfig = (on: PluginEvents, config: PluginConfigOptions) => void | ConfigOptions | Promise<ConfigOptions>
|
||||
interface JQueryWithSelector<TElement = HTMLElement> extends JQuery<TElement> {
|
||||
selector?: string | null
|
||||
}
|
||||
|
||||
interface PrevSubjectMap<O = unknown> {
|
||||
optional: O
|
||||
element: JQuery
|
||||
element: JQueryWithSelector
|
||||
document: Document
|
||||
window: Window
|
||||
}
|
||||
@@ -53,19 +56,6 @@ declare namespace Cypress {
|
||||
password: string
|
||||
}
|
||||
|
||||
interface RemoteState {
|
||||
auth?: {
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
domainName: string
|
||||
strategy: 'file' | 'http'
|
||||
origin: string
|
||||
fileServer: string
|
||||
props: Record<string, any>
|
||||
visiting: string
|
||||
}
|
||||
|
||||
interface Backend {
|
||||
/**
|
||||
* Firefox only: Force Cypress to run garbage collection routines.
|
||||
@@ -467,16 +457,18 @@ declare namespace Cypress {
|
||||
Commands: {
|
||||
add<T extends keyof Chainable>(name: T, fn: CommandFn<T>): void
|
||||
add<T extends keyof Chainable>(name: T, options: CommandOptions & {prevSubject: false}, fn: CommandFn<T>): void
|
||||
add<T extends keyof Chainable, S = any>(name: T, options: CommandOptions & {prevSubject: true}, fn: CommandFnWithSubject<T, S>): void
|
||||
add<T extends keyof Chainable, S extends PrevSubject>(
|
||||
name: T, options: CommandOptions & { prevSubject: true | S | ['optional'] }, fn: CommandFnWithSubject<T, PrevSubjectMap[S]>,
|
||||
name: T, options: CommandOptions & { prevSubject: S | ['optional'] }, fn: CommandFnWithSubject<T, PrevSubjectMap[S]>,
|
||||
): void
|
||||
add<T extends keyof Chainable, S extends PrevSubject>(
|
||||
name: T, options: CommandOptions & { prevSubject: S[] }, fn: CommandFnWithSubject<T, PrevSubjectMap<void>[S]>,
|
||||
): void
|
||||
addAll<T extends keyof Chainable>(fns: CommandFns): void
|
||||
addAll<T extends keyof Chainable>(options: CommandOptions & {prevSubject: false}, fns: CommandFns): void
|
||||
addAll<T extends keyof Chainable, S = any>(options: CommandOptions & { prevSubject: true }, fns: CommandFnsWithSubject<S>): void
|
||||
addAll<T extends keyof Chainable, S extends PrevSubject>(
|
||||
options: CommandOptions & { prevSubject: true | S | ['optional'] }, fns: CommandFnsWithSubject<PrevSubjectMap[S]>,
|
||||
options: CommandOptions & { prevSubject: S | ['optional'] }, fns: CommandFnsWithSubject<PrevSubjectMap[S]>,
|
||||
): void
|
||||
addAll<T extends keyof Chainable, S extends PrevSubject>(
|
||||
options: CommandOptions & { prevSubject: S[] }, fns: CommandFnsWithSubject<PrevSubjectMap<void>[S]>,
|
||||
@@ -634,7 +626,7 @@ declare namespace Cypress {
|
||||
* Trigger action
|
||||
* @private
|
||||
*/
|
||||
action: (action: string, ...args: any[]) => void
|
||||
action: (action: string, ...args: any[]) => any[] | void
|
||||
|
||||
/**
|
||||
* Load files
|
||||
@@ -1059,7 +1051,7 @@ declare namespace Cypress {
|
||||
/**
|
||||
* Save/Restore browser Cookies, LocalStorage, and SessionStorage data resulting from the supplied `setup` function.
|
||||
*
|
||||
* Only available if the `experimentalSessionSupport` config option is enabled.
|
||||
* Only available if the `experimentalSessionAndOrigin` config option is enabled.
|
||||
*
|
||||
* @see https://on.cypress.io/session
|
||||
*/
|
||||
@@ -1421,6 +1413,29 @@ declare namespace Cypress {
|
||||
*/
|
||||
off: Actions
|
||||
|
||||
/**
|
||||
* Enables running Cypress commands in a secondary origin.
|
||||
* @see https://on.cypress.io/origin
|
||||
* @example
|
||||
* cy.origin('example.com', () => {
|
||||
* cy.get('h1').should('equal', 'Example Domain')
|
||||
* })
|
||||
*/
|
||||
origin<T extends any>(urlOrDomain: string, fn: () => void): Chainable<T>
|
||||
|
||||
/**
|
||||
* Enables running Cypress commands in a secondary origin.
|
||||
* @see https://on.cypress.io/origin
|
||||
* @example
|
||||
* cy.origin('example.com', { args: { key: 'value', foo: 'foo' } }, ({ key, foo }) => {
|
||||
* expect(key).to.equal('value')
|
||||
* expect(foo).to.equal('foo')
|
||||
* })
|
||||
*/
|
||||
origin<T, S extends any>(urlOrDomain: string, options: {
|
||||
args: T
|
||||
}, fn: (args: T) => void): Chainable<S>
|
||||
|
||||
/**
|
||||
* Get the parent DOM element of a set of DOM elements.
|
||||
*
|
||||
@@ -2514,7 +2529,7 @@ declare namespace Cypress {
|
||||
action: 'select' | 'drag-drop'
|
||||
}
|
||||
|
||||
interface BlurOptions extends Loggable, Forceable { }
|
||||
interface BlurOptions extends Loggable, Timeoutable, Forceable { }
|
||||
|
||||
interface CheckOptions extends Loggable, Timeoutable, ActionableOptions {
|
||||
interval: number
|
||||
@@ -2806,16 +2821,16 @@ declare namespace Cypress {
|
||||
* @default 'top'
|
||||
*/
|
||||
scrollBehavior: scrollBehaviorOptions
|
||||
/**
|
||||
* Enable experimental session support. See https://on.cypress.io/session
|
||||
* @default false
|
||||
*/
|
||||
experimentalSessionSupport: boolean
|
||||
/**
|
||||
* Allows listening to the `before:run`, `after:run`, `before:spec`, and `after:spec` events in the plugins file during interactive mode.
|
||||
* @default false
|
||||
*/
|
||||
experimentalInteractiveRunEvents: boolean
|
||||
/**
|
||||
* Enables cross-origin and improved session support, including the `cy.origin` and `cy.session` commands. See https://on.cypress.io/origin and https://on.cypress.io/session.
|
||||
* @default false
|
||||
*/
|
||||
experimentalSessionAndOrigin: boolean
|
||||
/**
|
||||
* Generate and save commands directly to your test suite by interacting with your app as an end user would.
|
||||
* @default false
|
||||
@@ -2956,7 +2971,6 @@ declare namespace Cypress {
|
||||
parentTestsFolderDisplay: string
|
||||
projectName: string
|
||||
proxyUrl: string
|
||||
remote: RemoteState
|
||||
report: boolean
|
||||
reporterRoute: string
|
||||
reporterUrl: string
|
||||
@@ -5487,6 +5501,21 @@ declare namespace Cypress {
|
||||
(action: 'task', tasks: Tasks): void
|
||||
}
|
||||
|
||||
interface CodeFrame {
|
||||
frame: string
|
||||
language: string
|
||||
line: number
|
||||
column: number
|
||||
absoluteFile: string
|
||||
originalFile: string
|
||||
relativeFile: string
|
||||
}
|
||||
|
||||
interface CypressError extends Error {
|
||||
docsUrl?: string
|
||||
codeFrame?: CodeFrame
|
||||
}
|
||||
|
||||
// for just a few events like "window:alert" it makes sense to allow passing cy.stub() or
|
||||
// a user callback function. Others probably only need a callback function.
|
||||
|
||||
@@ -5592,7 +5621,7 @@ declare namespace Cypress {
|
||||
* This event exists because it's extremely useful for debugging purposes.
|
||||
* @see https://on.cypress.io/catalog-of-events#App-Events
|
||||
*/
|
||||
(action: 'fail', fn: (error: Error, mocha: Mocha.Runnable) => void): Cypress
|
||||
(action: 'fail', fn: (error: CypressError, mocha: Mocha.Runnable) => void): Cypress
|
||||
/**
|
||||
* Fires whenever the viewport changes via a `cy.viewport()` or naturally when
|
||||
* Cypress resets the viewport to the default between tests. Useful for debugging purposes.
|
||||
@@ -5626,6 +5655,12 @@ declare namespace Cypress {
|
||||
* @see https://on.cypress.io/catalog-of-events#App-Events
|
||||
*/
|
||||
(action: 'command:end', fn: (command: CommandQueue) => void): Cypress
|
||||
/**
|
||||
* Fires when a command is skipped, namely the `should` command.
|
||||
* Useful for debugging and understanding how commands are handled.
|
||||
* @see https://on.cypress.io/catalog-of-events#App-Events
|
||||
*/
|
||||
(action: 'skipped:command:end', fn: (command: CommandQueue) => void): Cypress
|
||||
/**
|
||||
* Fires whenever a command begins its retrying routines.
|
||||
* This is called on the trailing edge after Cypress has internally
|
||||
@@ -5716,10 +5751,13 @@ declare namespace Cypress {
|
||||
}
|
||||
|
||||
interface EnqueuedCommand {
|
||||
id: string
|
||||
name: string
|
||||
args: any[]
|
||||
type: string
|
||||
chainerId: string
|
||||
injected: boolean
|
||||
userInvocationStack?: string
|
||||
fn(...args: any[]): any
|
||||
}
|
||||
|
||||
@@ -5758,6 +5796,8 @@ declare namespace Cypress {
|
||||
}
|
||||
|
||||
interface LogConfig extends Timeoutable {
|
||||
/** Unique id for the log, in the form of '<origin>-<number>' */
|
||||
id: string
|
||||
/** The JQuery element for the command. This will highlight the command in the main window when debugging */
|
||||
$el: JQuery
|
||||
/** The scope of the log entry. If child, will appear nested below parents, prefixed with '-' */
|
||||
@@ -5770,6 +5810,8 @@ declare namespace Cypress {
|
||||
message: any
|
||||
/** Set to false if you want to control the finishing of the command in the log yourself */
|
||||
autoEnd: boolean
|
||||
/** Set to true to immediately finish the log */
|
||||
end: boolean
|
||||
/** Return an object that will be printed in the dev tools console */
|
||||
consoleProps(): ObjectLike
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ Cypress.on('url:changed', (url) => {
|
||||
})
|
||||
|
||||
Cypress.on('fail', (error, mocha) => {
|
||||
error // $ExpectType Error
|
||||
error // $ExpectType CypressError
|
||||
mocha // $ExpectType Runnable
|
||||
})
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace CypressCommandsTests {
|
||||
arg // $ExpectType string
|
||||
})
|
||||
Cypress.Commands.add('newCommand', { prevSubject: true }, (subject, arg) => {
|
||||
subject // $ExpectType unknown
|
||||
subject // $ExpectType any
|
||||
arg // $ExpectType string
|
||||
return
|
||||
})
|
||||
@@ -115,11 +115,11 @@ namespace CypressCommandsTests {
|
||||
arg // $ExpectType string
|
||||
})
|
||||
Cypress.Commands.add('newCommand', { prevSubject: 'element' }, (subject, arg) => {
|
||||
subject // $ExpectType JQuery<HTMLElement>
|
||||
subject // $ExpectType JQueryWithSelector<HTMLElement>
|
||||
arg // $ExpectType string
|
||||
})
|
||||
Cypress.Commands.add('newCommand', { prevSubject: ['element'] }, (subject, arg) => {
|
||||
subject // $ExpectType JQuery<HTMLElement>
|
||||
subject // $ExpectType JQueryWithSelector<HTMLElement>
|
||||
arg // $ExpectType string
|
||||
})
|
||||
Cypress.Commands.add('newCommand', { prevSubject: ['element', 'document', 'window'] }, (subject, arg) => {
|
||||
@@ -128,7 +128,7 @@ namespace CypressCommandsTests {
|
||||
} else if (subject instanceof Document) {
|
||||
subject // $ExpectType Document
|
||||
} else {
|
||||
subject // $ExpectType JQuery<HTMLElement>
|
||||
subject // $ExpectType JQueryWithSelector<HTMLElement>
|
||||
}
|
||||
arg // $ExpectType string
|
||||
})
|
||||
@@ -138,7 +138,7 @@ namespace CypressCommandsTests {
|
||||
} else if (subject instanceof Document) {
|
||||
subject // $ExpectType Document
|
||||
} else if (subject) {
|
||||
subject // $ExpectType JQuery<HTMLElement>
|
||||
subject // $ExpectType JQueryWithSelector<HTMLElement>
|
||||
} else {
|
||||
subject // $ExpectType void
|
||||
}
|
||||
@@ -175,7 +175,7 @@ namespace CypressCommandsTests {
|
||||
})
|
||||
Cypress.Commands.addAll({ prevSubject: true }, {
|
||||
newCommand: (subject, arg) => {
|
||||
subject // $ExpectType unknown
|
||||
subject // $ExpectType any
|
||||
arg // $ExpectType any
|
||||
return
|
||||
},
|
||||
@@ -217,13 +217,13 @@ namespace CypressCommandsTests {
|
||||
})
|
||||
Cypress.Commands.addAll({ prevSubject: 'element' }, {
|
||||
newCommand: (subject, arg) => {
|
||||
subject // $ExpectType JQuery<HTMLElement>
|
||||
subject // $ExpectType JQueryWithSelector<HTMLElement>
|
||||
arg // $ExpectType any
|
||||
}
|
||||
})
|
||||
Cypress.Commands.addAll({ prevSubject: ['element'] }, {
|
||||
newCommand: (subject, arg) => {
|
||||
subject // $ExpectType JQuery<HTMLElement>
|
||||
subject // $ExpectType JQueryWithSelector<HTMLElement>
|
||||
arg // $ExpectType any
|
||||
}
|
||||
})
|
||||
@@ -234,7 +234,7 @@ namespace CypressCommandsTests {
|
||||
} else if (subject instanceof Document) {
|
||||
subject // $ExpectType Document
|
||||
} else {
|
||||
subject // $ExpectType JQuery<HTMLElement>
|
||||
subject // $ExpectType JQueryWithSelector<HTMLElement>
|
||||
}
|
||||
arg // $ExpectType any
|
||||
}
|
||||
@@ -246,7 +246,7 @@ namespace CypressCommandsTests {
|
||||
} else if (subject instanceof Document) {
|
||||
subject // $ExpectType Document
|
||||
} else if (subject) {
|
||||
subject // $ExpectType JQuery<HTMLElement>
|
||||
subject // $ExpectType JQueryWithSelector<HTMLElement>
|
||||
} else {
|
||||
subject // $ExpectType void
|
||||
}
|
||||
@@ -273,7 +273,7 @@ namespace CypressCommandsTests {
|
||||
originalFn.apply(this, [arg]) // $ExpectType Chainable<number>
|
||||
})
|
||||
Cypress.Commands.overwrite<'type', 'element'>('type', (originalFn, element, text, options?: Partial<Cypress.TypeOptions & {sensitive: boolean}>) => {
|
||||
element // $ExpectType JQuery<HTMLElement>
|
||||
element // $ExpectType JQueryWithSelector<HTMLElement>
|
||||
text // $ExpectType string
|
||||
|
||||
if (options && options.sensitive) {
|
||||
@@ -903,7 +903,6 @@ namespace CypressTaskTests {
|
||||
}
|
||||
|
||||
namespace CypressSessionsTests {
|
||||
Cypress.config('experimentalSessionSupport') // $ExpectType boolean
|
||||
cy.session('user')
|
||||
cy.session('user', () => {})
|
||||
cy.session({ name: 'bob' }, () => {})
|
||||
@@ -938,3 +937,21 @@ namespace CypressKeyboardTests {
|
||||
delay: 500 // $ExpectError
|
||||
})
|
||||
}
|
||||
|
||||
namespace CypressOriginTests {
|
||||
cy.origin('example.com', () => {})
|
||||
cy.origin('example.com', { args: {}}, (value: object) => {})
|
||||
cy.origin('example.com', { args: { one: 1, key: 'value', bool: true } }, (value: { one: number, key: string, bool: boolean}) => {})
|
||||
cy.origin('example.com', { args: [1, 'value', true ] }, (value: Array<(number | string | boolean)>) => {})
|
||||
cy.origin('example.com', { args : 'value'}, (value: string) => {})
|
||||
cy.origin('example.com', { args: 1 }, (value: number) => {})
|
||||
cy.origin('example.com', { args: true }, (value: boolean) => {})
|
||||
|
||||
cy.origin() // $ExpectError
|
||||
cy.origin('example.com') // $ExpectError
|
||||
cy.origin(true) // $ExpectError
|
||||
cy.origin('example.com', {}) // $ExpectError
|
||||
cy.origin('example.com', {}, {}) // $ExpectError
|
||||
cy.origin('example.com', { args: ['value'] }, (value: boolean[]) => {}) // $ExpectError
|
||||
cy.origin('example.com', {}, (value: undefined) => {}) // $ExpectError
|
||||
}
|
||||
|
||||
+1
-5
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cypress",
|
||||
"version": "9.5.4",
|
||||
"version": "9.6.0",
|
||||
"description": "Cypress.io end to end testing tool",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -77,7 +77,6 @@
|
||||
"devDependencies": {
|
||||
"@aws-sdk/credential-providers": "3.53.0",
|
||||
"@cypress/commit-message-install": "3.1.3",
|
||||
"@cypress/env-or-json-file": "2.0.0",
|
||||
"@cypress/github-commit-status-check": "1.5.0",
|
||||
"@cypress/questions-remain": "1.0.1",
|
||||
"@cypress/request": "2.88.10",
|
||||
@@ -198,11 +197,8 @@
|
||||
"mock-fs": "5.1.1",
|
||||
"p-defer": "^3.0.0",
|
||||
"patch-package": "6.4.7",
|
||||
"plist": "3.0.5",
|
||||
"pluralize": "8.0.0",
|
||||
"postinstall-postinstall": "2.0.0",
|
||||
"prefixed-list": "1.0.1",
|
||||
"pretty-ms": "7.0.0",
|
||||
"print-arch": "1.0.0",
|
||||
"proxyquire": "2.1.3",
|
||||
"rimraf": "3.0.2",
|
||||
|
||||
@@ -173,7 +173,7 @@ export const sessionLifecycle = () => {
|
||||
<div class='container'>
|
||||
${svgCy}
|
||||
<br/>
|
||||
<p class="warn">Because <code><b>experimentalSessionSupport</b></code> is enabled, Cypress navigates to the default blank page <span class="em">before each test</span> to ensure test reliability.</p>
|
||||
<p class="warn">Because <code><b>experimentalSessionAndOrigin</b></code> is enabled, Cypress navigates to the default blank page <span class="em">before each test</span> to ensure test reliability.</p>
|
||||
<p>This is the default blank page.</p>
|
||||
<p>To test your web application:</p>
|
||||
<ul>
|
||||
|
||||
@@ -82,6 +82,40 @@ export class AutIframe {
|
||||
return Cypress.cy.detachDom(this._contents())
|
||||
}
|
||||
|
||||
/**
|
||||
* If the AUT is cross origin relative to top, a security error is thrown and the method returns false
|
||||
* If the AUT is cross origin relative to top and chromeWebSecurity is false, origins of the AUT and top need to be compared and returns false
|
||||
* Otherwise, if top and the AUT match origins, the method returns true.
|
||||
* If the AUT origin is "about://blank", that means the src attribute has been stripped off the iframe and is adhering to same origin policy
|
||||
*/
|
||||
doesAUTMatchTopOriginPolicy = () => {
|
||||
const Cypress = this.eventManager.getCypress()
|
||||
|
||||
if (!Cypress) return true
|
||||
|
||||
try {
|
||||
const { href: currentHref } = (this.$iframe as any)[0].contentWindow.document.location
|
||||
const locationTop = Cypress.Location.create(window.location.href)
|
||||
const locationAUT = Cypress.Location.create(currentHref)
|
||||
|
||||
return locationTop.originPolicy === locationAUT.originPolicy || locationAUT.originPolicy === 'about://blank'
|
||||
} catch (err) {
|
||||
if (err.name === 'SecurityError') {
|
||||
return false
|
||||
}
|
||||
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the src attribute from the AUT iframe, resulting in 'about:blank' being loaded into the iframe
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-src for more details
|
||||
*/
|
||||
removeSrcAttribute = () => {
|
||||
this.$iframe?.removeAttr('src')
|
||||
}
|
||||
|
||||
visitBlank = ({ type }: { type?: 'session' | 'session-lifecycle' }) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
if (!this.$iframe) {
|
||||
@@ -108,6 +142,23 @@ export class AutIframe {
|
||||
}
|
||||
|
||||
restoreDom = (snapshot) => {
|
||||
if (!this.doesAUTMatchTopOriginPolicy()) {
|
||||
/**
|
||||
* A load event fires here when the src is removed (as does an unload event).
|
||||
* This is equivalent to loading about:blank (see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-src).
|
||||
* This doesn't resort in a log message being generated for a new page.
|
||||
* In the event-manager code, we stop adding logs from other domains once the spec is finished.
|
||||
*/
|
||||
this.$iframe?.one('load', () => {
|
||||
this.restoreDom(snapshot)
|
||||
})
|
||||
|
||||
// The iframe is in a cross origin state. Remove the src attribute to adhere to same origin policy. NOTE: This should only be done ONCE.
|
||||
this.removeSrcAttribute()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const Cypress = this.eventManager.getCypress()
|
||||
const { headStyles = undefined, bodyStyles = undefined } = Cypress ? Cypress.cy.getStyles(snapshot) : {}
|
||||
const { body, htmlAttrs } = snapshot
|
||||
@@ -425,40 +476,6 @@ export class AutIframe {
|
||||
})
|
||||
}
|
||||
|
||||
beforeScreenshot = (config) => {
|
||||
// could fail if iframe is cross-origin, so fail gracefully
|
||||
try {
|
||||
if (config.disableTimersAndAnimations) {
|
||||
this.dom.addCssAnimationDisabler(this._body())
|
||||
}
|
||||
|
||||
_.each(config.blackout, (selector) => {
|
||||
this.dom.addBlackout(this._body(), selector)
|
||||
})
|
||||
} catch (err) {
|
||||
/* eslint-disable no-console */
|
||||
console.error('Failed to modify app this.dom:')
|
||||
console.error(err)
|
||||
/* eslint-disable no-console */
|
||||
}
|
||||
}
|
||||
|
||||
afterScreenshot = (config) => {
|
||||
// could fail if iframe is cross-origin, so fail gracefully
|
||||
try {
|
||||
if (config.disableTimersAndAnimations) {
|
||||
this.dom.removeCssAnimationDisabler(this._body())
|
||||
}
|
||||
|
||||
this.dom.removeBlackouts(this._body())
|
||||
} catch (err) {
|
||||
/* eslint-disable no-console */
|
||||
console.error('Failed to modify app this.dom:')
|
||||
console.error(err)
|
||||
/* eslint-disable no-console */
|
||||
}
|
||||
}
|
||||
|
||||
startStudio = () => {
|
||||
if (this.studioRecorder.isLoading) {
|
||||
this.studioRecorder.start(this._body()?.[0])
|
||||
|
||||
@@ -8,7 +8,9 @@ import type { AutomationElementId, FileDetails } from '@packages/types'
|
||||
|
||||
import { logger } from './logger'
|
||||
import type { Socket } from '@packages/socket/lib/browser'
|
||||
import * as cors from '@packages/network/lib/cors'
|
||||
import { automation, useRunnerUiStore } from '../store'
|
||||
import { useScreenshotStore } from '../store/screenshot-store'
|
||||
|
||||
export type CypressInCypressMochaEvent = Array<Array<string | Record<string, any>>>
|
||||
|
||||
@@ -159,6 +161,10 @@ export class EventManager {
|
||||
})
|
||||
})
|
||||
|
||||
this.ws.on('cross:origin:delaying:html', (request) => {
|
||||
Cypress.primaryOriginCommunicator.emit('delaying:html', request)
|
||||
})
|
||||
|
||||
localToReporterEvents.forEach((event) => {
|
||||
this.localBus.on(event, (...args) => {
|
||||
this.reporterBus.emit(event, ...args)
|
||||
@@ -350,6 +356,14 @@ export class EventManager {
|
||||
this._clearAllCookies()
|
||||
this._setUnload()
|
||||
})
|
||||
|
||||
// The window.top should not change between test reloads, and we only need to bind the message event once
|
||||
// Forward all message events to the current instance of the multi-origin communicator
|
||||
if (!window.top) throw new Error('missing window.top in event-manager')
|
||||
|
||||
window.top.addEventListener('message', ({ data, source }) => {
|
||||
Cypress?.primaryOriginCommunicator.onMessage({ data, source })
|
||||
}, false)
|
||||
}
|
||||
|
||||
start (config) {
|
||||
@@ -482,7 +496,14 @@ export class EventManager {
|
||||
this.reporterBus.emit('reporter:log:state:changed', displayProps)
|
||||
})
|
||||
|
||||
Cypress.on('before:screenshot', (config, cb) => {
|
||||
// TODO: MOVE BACK INTO useEventManager. Verify this works
|
||||
const screenshotStore = useScreenshotStore()
|
||||
|
||||
const handleBeforeScreenshot = (config, cb) => {
|
||||
if (config.appOnly) {
|
||||
screenshotStore.setScreenshotting(true)
|
||||
}
|
||||
|
||||
const beforeThenCb = () => {
|
||||
this.localBus.emit('before:screenshot', config)
|
||||
cb()
|
||||
@@ -501,11 +522,16 @@ export class EventManager {
|
||||
}
|
||||
|
||||
if (!wait) beforeThenCb()
|
||||
})
|
||||
}
|
||||
|
||||
Cypress.on('after:screenshot', (config) => {
|
||||
Cypress.on('before:screenshot', handleBeforeScreenshot)
|
||||
|
||||
const handleAfterScreenshot = (config) => {
|
||||
screenshotStore.setScreenshotting(false)
|
||||
this.localBus.emit('after:screenshot', config)
|
||||
})
|
||||
}
|
||||
|
||||
Cypress.on('after:screenshot', handleAfterScreenshot)
|
||||
|
||||
driverToReporterEvents.forEach((event) => {
|
||||
Cypress.on(event, (...args) => {
|
||||
@@ -564,6 +590,90 @@ export class EventManager {
|
||||
this.studioRecorder.testFailed()
|
||||
}
|
||||
})
|
||||
|
||||
Cypress.on('test:before:run', (...args) => {
|
||||
Cypress.primaryOriginCommunicator.toAllSpecBridges('test:before:run', ...args)
|
||||
})
|
||||
|
||||
Cypress.on('test:before:run:async', (...args) => {
|
||||
Cypress.primaryOriginCommunicator.toAllSpecBridges('test:before:run:async', ...args)
|
||||
})
|
||||
|
||||
// Inform all spec bridges that the primary origin has begun to unload.
|
||||
Cypress.on('window:before:unload', () => {
|
||||
Cypress.primaryOriginCommunicator.toAllSpecBridges('before:unload')
|
||||
})
|
||||
|
||||
Cypress.primaryOriginCommunicator.on('window:load', ({ url }, originPolicy) => {
|
||||
// Sync stable if the expected origin has loaded.
|
||||
// Only listen to window load events from the most recent secondary origin, This prevents nondeterminism in the case where we redirect to an already
|
||||
// established spec bridge, but one that is not the current or next cy.origin command.
|
||||
if (cy.state('latestActiveOriginPolicy') === originPolicy) {
|
||||
// We remain in an anticipating state until either a load even happens or a timeout.
|
||||
cy.state('autOrigin', cy.state('autOrigin', cors.getOriginPolicy(url)))
|
||||
cy.isAnticipatingCrossOriginResponseFor(undefined)
|
||||
cy.isStable(true, 'load')
|
||||
// Prints out the newly loaded URL
|
||||
Cypress.emit('internal:window:load', { type: 'cross:origin', url })
|
||||
// Re-broadcast to any other specBridges.
|
||||
Cypress.primaryOriginCommunicator.toAllSpecBridges('window:load', { url })
|
||||
}
|
||||
})
|
||||
|
||||
Cypress.primaryOriginCommunicator.on('before:unload', () => {
|
||||
// We specifically don't call 'cy.isStable' here because we don't want to inject another load event.
|
||||
// Unstable is unstable regardless of where it initiated from.
|
||||
cy.state('isStable', false)
|
||||
// Re-broadcast to any other specBridges.
|
||||
Cypress.primaryOriginCommunicator.toAllSpecBridges('before:unload')
|
||||
})
|
||||
|
||||
Cypress.primaryOriginCommunicator.on('expect:origin', (originPolicy) => {
|
||||
this.localBus.emit('expect:origin', originPolicy)
|
||||
})
|
||||
|
||||
Cypress.primaryOriginCommunicator.on('viewport:changed', (viewport, originPolicy) => {
|
||||
const callback = () => {
|
||||
Cypress.primaryOriginCommunicator.toSpecBridge(originPolicy, 'viewport:changed:end')
|
||||
}
|
||||
|
||||
Cypress.primaryOriginCommunicator.emit('sync:viewport', viewport)
|
||||
this.localBus.emit('viewport:changed', viewport, callback)
|
||||
})
|
||||
|
||||
Cypress.primaryOriginCommunicator.on('before:screenshot', (config, originPolicy) => {
|
||||
const callback = () => {
|
||||
Cypress.primaryOriginCommunicator.toSpecBridge(originPolicy, 'before:screenshot:end')
|
||||
}
|
||||
|
||||
handleBeforeScreenshot(config, callback)
|
||||
})
|
||||
|
||||
Cypress.primaryOriginCommunicator.on('url:changed', ({ url }) => {
|
||||
this.localBus.emit('url:changed', url)
|
||||
})
|
||||
|
||||
Cypress.primaryOriginCommunicator.on('after:screenshot', handleAfterScreenshot)
|
||||
|
||||
const crossOriginLogs = {}
|
||||
|
||||
Cypress.primaryOriginCommunicator.on('log:added', (attrs) => {
|
||||
// If the test is over and the user enters interactive snapshot mode, do not add cross origin logs to the test runner.
|
||||
if (Cypress.state('test')?.final) return
|
||||
|
||||
// Create a new local log representation of the cross origin log.
|
||||
// It will be attached to the current command.
|
||||
// We also keep a reference to it to update it in the future.
|
||||
crossOriginLogs[attrs.id] = Cypress.log(attrs)
|
||||
})
|
||||
|
||||
Cypress.primaryOriginCommunicator.on('log:changed', (attrs) => {
|
||||
// Retrieve the referenced log and update it.
|
||||
const log = crossOriginLogs[attrs.id]
|
||||
|
||||
// this will trigger a log changed event for the log itself.
|
||||
log?.set(attrs)
|
||||
})
|
||||
}
|
||||
|
||||
_runDriver (state) {
|
||||
@@ -619,6 +729,7 @@ export class EventManager {
|
||||
// but we want to be aggressive here
|
||||
// and force GC early and often
|
||||
Cypress.removeAllListeners()
|
||||
Cypress.primaryOriginCommunicator.removeAllListeners()
|
||||
|
||||
this.localBus.emit('restart')
|
||||
}
|
||||
@@ -676,6 +787,11 @@ export class EventManager {
|
||||
this.ws.emit('spec:changed', specFile)
|
||||
}
|
||||
|
||||
notifyCrossOriginBridgeReady (originPolicy) {
|
||||
// Any multi-origin event appends the origin as the third parameter and we do the same here for this short circuit
|
||||
Cypress.primaryOriginCommunicator.emit('bridge:ready', undefined, originPolicy)
|
||||
}
|
||||
|
||||
snapshotUnpinned () {
|
||||
this._unpinSnapshot()
|
||||
this._hideSnapshot()
|
||||
|
||||
@@ -34,6 +34,7 @@ export class IframeModel {
|
||||
private detachDom: () => AutSnapshot,
|
||||
private restoreDom: (snapshot: any) => void,
|
||||
private highlightEl: ({ body }: any, opts: any) => void,
|
||||
private isAUTSameOrigin: () => boolean,
|
||||
private eventManager: EventManager,
|
||||
private studio: {
|
||||
selectorPlaygroundModel: any
|
||||
@@ -247,6 +248,35 @@ export class IframeModel {
|
||||
|
||||
_storeOriginalState () {
|
||||
const autStore = useAutStore()
|
||||
|
||||
if (!this.isAUTSameOrigin()) {
|
||||
const Cypress = this.eventManager.getCypress()
|
||||
|
||||
/**
|
||||
* This only happens if the AUT ends in a cross origin state that the primary doesn't have access to.
|
||||
* In this case, the final snapshot request from the primary is sent out to the cross-origin spec bridges.
|
||||
* The spec bridge that matches the origin policy will take a snapshot and send it back to the primary for the runner to store in originalState.
|
||||
*/
|
||||
Cypress.primaryOriginCommunicator.toAllSpecBridges('generate:final:snapshot', autStore.url || '')
|
||||
Cypress.primaryOriginCommunicator.once('final:snapshot:generated', (finalSnapshot) => {
|
||||
// todo(lachlan): UNIFY-1318 - find correct default, if they are even needed, for required fields ($el, coords...)
|
||||
// @ts-ignore
|
||||
this.originalState = {
|
||||
body: finalSnapshot.body,
|
||||
htmlAttrs: finalSnapshot.htmlAttrs,
|
||||
snapshot: finalSnapshot,
|
||||
snapshots: [],
|
||||
url: autStore.url || '',
|
||||
// TODO: use same attr for both runner and runner-ct states.
|
||||
// these refer to the same thing - the viewport dimensions.
|
||||
viewportWidth: autStore.viewportWidth,
|
||||
viewportHeight: autStore.viewportHeight,
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const finalSnapshot = this.detachDom()
|
||||
|
||||
if (!finalSnapshot) return
|
||||
|
||||
@@ -100,6 +100,7 @@ function createIframeModel () {
|
||||
autIframe.detachDom,
|
||||
autIframe.restoreDom,
|
||||
autIframe.highlightEl,
|
||||
autIframe.doesAUTMatchTopOriginPolicy,
|
||||
getEventManager(),
|
||||
{
|
||||
recorder: getEventManager().studioRecorder,
|
||||
@@ -185,6 +186,30 @@ export async function teardown () {
|
||||
isTorndown = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a cross origin iframe for multi-domain
|
||||
*/
|
||||
export function addCrossOriginIframe (location) {
|
||||
const id = `Spec Bridge: ${location.originPolicy}`
|
||||
|
||||
// if it already exists, don't add another one
|
||||
if (document.getElementById(id)) {
|
||||
getEventManager().notifyCrossOriginBridgeReady(location.originPolicy)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
addIframe({
|
||||
id,
|
||||
// the cross origin iframe is added to the document body instead of the
|
||||
// container since it needs to match the size of the top window for screenshots
|
||||
$container: document.body,
|
||||
className: 'spec-bridge-iframe',
|
||||
// TODO: verify window.UnifiedRunner.config.namespace
|
||||
src: `${location.originPolicy}/${window.UnifiedRunner.config.namespace}/multi-domain-iframes`,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a spec by creating a fresh AUT and initializing
|
||||
* Cypress on it.
|
||||
@@ -228,15 +253,17 @@ function runSpecCT (spec: SpecFile) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Spec IFrame. Used for loading the spec to execute in E2E
|
||||
* Create an IFrame. If the Iframe is the spec iframe,
|
||||
* this function is used for loading the spec to execute in E2E
|
||||
*/
|
||||
function createSpecIFrame (specSrc: string) {
|
||||
const el = document.createElement('iframe')
|
||||
function addIframe ({ $container, id, src, className }) {
|
||||
const $addedIframe = document.createElement('iframe')
|
||||
|
||||
el.id = `Your Spec: '${specSrc}'`,
|
||||
el.className = 'spec-iframe'
|
||||
$addedIframe.id = id,
|
||||
$addedIframe.className = className
|
||||
|
||||
return el
|
||||
$container.appendChild($addedIframe)
|
||||
$addedIframe.setAttribute('src', src)
|
||||
}
|
||||
|
||||
// this is how the Cypress driver knows which spec to run.
|
||||
@@ -282,17 +309,30 @@ function runSpecE2E (spec: SpecFile) {
|
||||
|
||||
const $autIframe: JQuery<HTMLIFrameElement> = autIframe.create().appendTo($container)
|
||||
|
||||
// Remove the spec bridge iframe
|
||||
document.querySelectorAll('iframe.spec-bridge-iframe').forEach((el) => {
|
||||
el.remove()
|
||||
})
|
||||
|
||||
autIframe.showInitialBlankContentsE2E()
|
||||
|
||||
// create Spec IFrame
|
||||
const specSrc = getSpecUrl(config.namespace, encodeURIComponent(spec.relative))
|
||||
|
||||
const $specIframe = createSpecIFrame(specSrc)
|
||||
// FIXME: BILL Determine where to call client with to force browser repaint
|
||||
/**
|
||||
* call the clientWidth to force the browser to repaint for viewport changes
|
||||
* otherwise firefox may fail when changing the viewport in between origins
|
||||
* this.refs.container.clientWidth
|
||||
*/
|
||||
|
||||
// append to document, so the iframe will execute the spec
|
||||
$container.appendChild($specIframe)
|
||||
|
||||
$specIframe.src = specSrc
|
||||
addIframe({
|
||||
$container,
|
||||
src: specSrc,
|
||||
id: `Your Spec: '${specSrc}'`,
|
||||
className: 'spec-iframe',
|
||||
})
|
||||
|
||||
// initialize Cypress (driver) with the AUT!
|
||||
getEventManager().initialize($autIframe, config)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { watch } from 'vue'
|
||||
import { getAutIframeModel, getEventManager, UnifiedRunnerAPI } from '.'
|
||||
import { addCrossOriginIframe, getAutIframeModel, getEventManager, UnifiedRunnerAPI } from '.'
|
||||
import { useAutStore, useSpecStore } from '../store'
|
||||
import { useScreenshotStore } from '../store/screenshot-store'
|
||||
import { empty, getReporterElement, getRunnerElement } from './utils'
|
||||
|
||||
export function useEventManager () {
|
||||
@@ -20,26 +19,11 @@ export function useEventManager () {
|
||||
}
|
||||
|
||||
function initializeRunnerLifecycleEvents () {
|
||||
const screenshotStore = useScreenshotStore()
|
||||
|
||||
// these events do not use GraphQL
|
||||
eventManager.on('restart', () => {
|
||||
runSpec()
|
||||
})
|
||||
|
||||
eventManager.on('before:screenshot', (payload) => {
|
||||
if (payload.appOnly) {
|
||||
screenshotStore.setScreenshotting(true)
|
||||
}
|
||||
|
||||
getAutIframeModel().beforeScreenshot(payload)
|
||||
})
|
||||
|
||||
eventManager.on('after:screenshot', (config) => {
|
||||
screenshotStore.setScreenshotting(false)
|
||||
getAutIframeModel().afterScreenshot(config)
|
||||
})
|
||||
|
||||
eventManager.on('script:error', (err) => {
|
||||
autStore.setScriptError(err)
|
||||
})
|
||||
@@ -51,6 +35,8 @@ export function useEventManager () {
|
||||
eventManager.on('visit:blank', ({ type }) => {
|
||||
getAutIframeModel().visitBlank({ type })
|
||||
})
|
||||
|
||||
eventManager.on('expect:origin', addCrossOriginIframe)
|
||||
}
|
||||
|
||||
const startSpecWatcher = () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
exports['src/index .getBreakingKeys returns list of breaking config keys 1'] = [
|
||||
exports['config/lib/index .getBreakingKeys returns list of breaking config keys 1'] = [
|
||||
"componentFolder",
|
||||
"integrationFolder",
|
||||
"testFiles",
|
||||
@@ -16,7 +16,7 @@ exports['src/index .getBreakingKeys returns list of breaking config keys 1'] = [
|
||||
"nodeVersion"
|
||||
]
|
||||
|
||||
exports['src/index .getDefaultValues returns list of public config keys 1'] = {
|
||||
exports['config/lib/index .getDefaultValues returns list of public config keys 1'] = {
|
||||
"animationDistanceThreshold": 5,
|
||||
"baseUrl": null,
|
||||
"blockHosts": null,
|
||||
@@ -79,7 +79,7 @@ exports['src/index .getDefaultValues returns list of public config keys 1'] = {
|
||||
"browsers": [],
|
||||
"clientRoute": "/__/",
|
||||
"configFile": "cypress.config.js",
|
||||
"cypressBinaryRoot": "/root/cypress",
|
||||
"cypressBinaryRoot": "/Users/bill/Repositories/cypress",
|
||||
"devServerPublicPathRoute": "/__cypress/src",
|
||||
"hosts": null,
|
||||
"isInteractive": true,
|
||||
@@ -93,7 +93,85 @@ exports['src/index .getDefaultValues returns list of public config keys 1'] = {
|
||||
"xhrRoute": "/xhrs/"
|
||||
}
|
||||
|
||||
exports['src/index .getPublicConfigKeys returns list of public config keys 1'] = [
|
||||
exports['config/lib/index .getDefaultValues returns list of public config keys for selected testing type 1'] = {
|
||||
"animationDistanceThreshold": 5,
|
||||
"baseUrl": null,
|
||||
"blockHosts": null,
|
||||
"chromeWebSecurity": true,
|
||||
"clientCertificates": [],
|
||||
"component": {
|
||||
"specPattern": "**/*.cy.{js,jsx,ts,tsx}",
|
||||
"indexHtmlFile": "cypress/support/component-index.html"
|
||||
},
|
||||
"defaultCommandTimeout": 4000,
|
||||
"downloadsFolder": "cypress/downloads",
|
||||
"e2e": {
|
||||
"specPattern": "cypress/e2e/**/*.cy.{js,jsx,ts,tsx}"
|
||||
},
|
||||
"env": {},
|
||||
"execTimeout": 60000,
|
||||
"experimentalFetchPolyfill": false,
|
||||
"experimentalInteractiveRunEvents": false,
|
||||
"experimentalSessionSupport": false,
|
||||
"experimentalSourceRewriting": false,
|
||||
"fileServerFolder": "",
|
||||
"fixturesFolder": "cypress/fixtures",
|
||||
"excludeSpecPattern": "*.hot-update.js",
|
||||
"includeShadowDom": false,
|
||||
"keystrokeDelay": 0,
|
||||
"modifyObstructiveCode": true,
|
||||
"numTestsKeptInMemory": 50,
|
||||
"pageLoadTimeout": 60000,
|
||||
"port": null,
|
||||
"projectId": null,
|
||||
"redirectionLimit": 20,
|
||||
"reporter": "spec",
|
||||
"reporterOptions": null,
|
||||
"requestTimeout": 5000,
|
||||
"resolvedNodePath": null,
|
||||
"resolvedNodeVersion": null,
|
||||
"responseTimeout": 30000,
|
||||
"retries": {
|
||||
"runMode": 0,
|
||||
"openMode": 0
|
||||
},
|
||||
"screenshotOnRunFailure": true,
|
||||
"screenshotsFolder": "cypress/screenshots",
|
||||
"slowTestThreshold": 10000,
|
||||
"scrollBehavior": "top",
|
||||
"supportFile": "cypress/support/e2e.{js,jsx,ts,tsx}",
|
||||
"supportFolder": false,
|
||||
"taskTimeout": 60000,
|
||||
"trashAssetsBeforeRuns": true,
|
||||
"userAgent": null,
|
||||
"video": true,
|
||||
"videoCompression": 32,
|
||||
"videosFolder": "cypress/videos",
|
||||
"videoUploadOnPasses": true,
|
||||
"viewportHeight": 660,
|
||||
"viewportWidth": 1000,
|
||||
"waitForAnimations": true,
|
||||
"watchForFileChanges": true,
|
||||
"autoOpen": false,
|
||||
"browsers": [],
|
||||
"clientRoute": "/__/",
|
||||
"configFile": "cypress.config.js",
|
||||
"cypressBinaryRoot": "/Users/bill/Repositories/cypress",
|
||||
"devServerPublicPathRoute": "/__cypress/src",
|
||||
"hosts": null,
|
||||
"isInteractive": true,
|
||||
"isTextTerminal": false,
|
||||
"morgan": true,
|
||||
"namespace": "__cypress",
|
||||
"reporterRoute": "/__cypress/reporter",
|
||||
"socketId": null,
|
||||
"socketIoCookie": "__socket",
|
||||
"socketIoRoute": "/__socket",
|
||||
"xhrRoute": "/xhrs/",
|
||||
"specPattern": "cypress/e2e/**/*.cy.{js,jsx,ts,tsx}"
|
||||
}
|
||||
|
||||
exports['config/lib/index .getPublicConfigKeys returns list of public config keys 1'] = [
|
||||
"animationDistanceThreshold",
|
||||
"arch",
|
||||
"baseUrl",
|
||||
@@ -153,81 +231,3 @@ exports['src/index .getPublicConfigKeys returns list of public config keys 1'] =
|
||||
"modifyObstructiveCode",
|
||||
"specPattern"
|
||||
]
|
||||
|
||||
exports['src/index .getDefaultValues returns list of public config keys for selected testing type 1'] = {
|
||||
"animationDistanceThreshold": 5,
|
||||
"baseUrl": null,
|
||||
"blockHosts": null,
|
||||
"chromeWebSecurity": true,
|
||||
"clientCertificates": [],
|
||||
"component": {
|
||||
"specPattern": "**/*.cy.{js,jsx,ts,tsx}",
|
||||
"indexHtmlFile": "cypress/support/component-index.html"
|
||||
},
|
||||
"defaultCommandTimeout": 4000,
|
||||
"downloadsFolder": "cypress/downloads",
|
||||
"e2e": {
|
||||
"specPattern": "cypress/e2e/**/*.cy.{js,jsx,ts,tsx}"
|
||||
},
|
||||
"env": {},
|
||||
"execTimeout": 60000,
|
||||
"experimentalFetchPolyfill": false,
|
||||
"experimentalInteractiveRunEvents": false,
|
||||
"experimentalSessionSupport": false,
|
||||
"experimentalSourceRewriting": false,
|
||||
"fileServerFolder": "",
|
||||
"fixturesFolder": "cypress/fixtures",
|
||||
"excludeSpecPattern": "*.hot-update.js",
|
||||
"includeShadowDom": false,
|
||||
"keystrokeDelay": 0,
|
||||
"modifyObstructiveCode": true,
|
||||
"numTestsKeptInMemory": 50,
|
||||
"pageLoadTimeout": 60000,
|
||||
"port": null,
|
||||
"projectId": null,
|
||||
"redirectionLimit": 20,
|
||||
"reporter": "spec",
|
||||
"reporterOptions": null,
|
||||
"requestTimeout": 5000,
|
||||
"resolvedNodePath": null,
|
||||
"resolvedNodeVersion": null,
|
||||
"responseTimeout": 30000,
|
||||
"retries": {
|
||||
"runMode": 0,
|
||||
"openMode": 0
|
||||
},
|
||||
"screenshotOnRunFailure": true,
|
||||
"screenshotsFolder": "cypress/screenshots",
|
||||
"slowTestThreshold": 10000,
|
||||
"scrollBehavior": "top",
|
||||
"supportFile": "cypress/support/e2e.{js,jsx,ts,tsx}",
|
||||
"supportFolder": false,
|
||||
"taskTimeout": 60000,
|
||||
"trashAssetsBeforeRuns": true,
|
||||
"userAgent": null,
|
||||
"video": true,
|
||||
"videoCompression": 32,
|
||||
"videosFolder": "cypress/videos",
|
||||
"videoUploadOnPasses": true,
|
||||
"viewportHeight": 660,
|
||||
"viewportWidth": 1000,
|
||||
"waitForAnimations": true,
|
||||
"watchForFileChanges": true,
|
||||
"autoOpen": false,
|
||||
"browsers": [],
|
||||
"clientRoute": "/__/",
|
||||
"configFile": "cypress.config.js",
|
||||
"cypressBinaryRoot": "/root/cypress",
|
||||
"devServerPublicPathRoute": "/__cypress/src",
|
||||
"hosts": null,
|
||||
"isInteractive": true,
|
||||
"isTextTerminal": false,
|
||||
"morgan": true,
|
||||
"namespace": "__cypress",
|
||||
"reporterRoute": "/__cypress/reporter",
|
||||
"socketId": null,
|
||||
"socketIoCookie": "__socket",
|
||||
"socketIoRoute": "/__socket",
|
||||
"xhrRoute": "/xhrs/",
|
||||
"specPattern": "cypress/e2e/**/*.cy.{js,jsx,ts,tsx}"
|
||||
}
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
exports['src/validation .isValidClientCertificatesSet returns error message for certs not passed as an array array 1'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": "1",
|
||||
"type": "a positive number or null or an object with keys \"openMode\" and \"runMode\" with values of numbers or nulls"
|
||||
}
|
||||
|
||||
exports['src/validation .isValidClientCertificatesSet returns error message for certs object without url 1'] = {
|
||||
"key": "clientCertificates[0].url",
|
||||
"type": "a URL matcher"
|
||||
}
|
||||
|
||||
exports['missing https protocol'] = {
|
||||
"key": "clientCertificates[0].url",
|
||||
"value": "http://url.com",
|
||||
@@ -21,7 +10,111 @@ exports['invalid url'] = {
|
||||
"type": "a valid URL"
|
||||
}
|
||||
|
||||
exports['src/validation .isValidBrowser passes valid browsers and forms error messages for invalid ones isValidBrowser 1'] = {
|
||||
exports['undefined browsers'] = `
|
||||
Missing browsers list
|
||||
`
|
||||
|
||||
exports['empty list of browsers'] = `
|
||||
Expected at least one browser
|
||||
`
|
||||
|
||||
exports['browsers list with a string'] = {
|
||||
"key": "name",
|
||||
"value": "foo",
|
||||
"type": "a non-empty string",
|
||||
"list": "browsers"
|
||||
}
|
||||
|
||||
exports['invalid retry value'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": "1",
|
||||
"type": "a positive number or null or an object with keys \"openMode\" and \"runMode\" with values of numbers or nulls"
|
||||
}
|
||||
|
||||
exports['invalid retry object'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": {
|
||||
"fakeMode": 1
|
||||
},
|
||||
"type": "a positive number or null or an object with keys \"openMode\" and \"runMode\" with values of numbers or nulls"
|
||||
}
|
||||
|
||||
exports['not qualified url'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": "url.com",
|
||||
"type": "a fully qualified URL (starting with `http://` or `https://`)"
|
||||
}
|
||||
|
||||
exports['empty string'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": "",
|
||||
"type": "a fully qualified URL (starting with `http://` or `https://`)"
|
||||
}
|
||||
|
||||
exports['not string or array'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": null,
|
||||
"type": "a string or an array of strings"
|
||||
}
|
||||
|
||||
exports['array of non-strings'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"type": "a string or an array of strings"
|
||||
}
|
||||
|
||||
exports['not one of the strings error message'] = {
|
||||
"key": "test",
|
||||
"value": "nope",
|
||||
"type": "one of these values: \"foo\", \"bar\""
|
||||
}
|
||||
|
||||
exports['number instead of string'] = {
|
||||
"key": "test",
|
||||
"value": 42,
|
||||
"type": "one of these values: \"foo\", \"bar\""
|
||||
}
|
||||
|
||||
exports['null instead of string'] = {
|
||||
"key": "test",
|
||||
"value": null,
|
||||
"type": "one of these values: \"foo\", \"bar\""
|
||||
}
|
||||
|
||||
exports['not one of the numbers error message'] = {
|
||||
"key": "test",
|
||||
"value": 4,
|
||||
"type": "one of these values: 1, 2, 3"
|
||||
}
|
||||
|
||||
exports['string instead of a number'] = {
|
||||
"key": "test",
|
||||
"value": "foo",
|
||||
"type": "one of these values: 1, 2, 3"
|
||||
}
|
||||
|
||||
exports['null instead of a number'] = {
|
||||
"key": "test",
|
||||
"value": null,
|
||||
"type": "one of these values: 1, 2, 3"
|
||||
}
|
||||
|
||||
exports['config/lib/validation .isValidClientCertificatesSet returns error message for certs not passed as an array array 1'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": "1",
|
||||
"type": "a positive number or null or an object with keys \"openMode\" and \"runMode\" with values of numbers or nulls"
|
||||
}
|
||||
|
||||
exports['config/lib/validation .isValidClientCertificatesSet returns error message for certs object without url 1'] = {
|
||||
"key": "clientCertificates[0].url",
|
||||
"type": "a URL matcher"
|
||||
}
|
||||
|
||||
exports['config/lib/validation .isValidBrowser passes valid browsers and forms error messages for invalid ones isValidBrowser 1'] = {
|
||||
"name": "isValidBrowser",
|
||||
"behavior": [
|
||||
{
|
||||
@@ -90,137 +183,44 @@ exports['src/validation .isValidBrowser passes valid browsers and forms error me
|
||||
]
|
||||
}
|
||||
|
||||
exports['undefined browsers'] = `
|
||||
Missing browsers list
|
||||
`
|
||||
|
||||
exports['empty list of browsers'] = `
|
||||
Expected at least one browser
|
||||
`
|
||||
|
||||
exports['browsers list with a string'] = {
|
||||
"key": "name",
|
||||
"value": "foo",
|
||||
"type": "a non-empty string",
|
||||
"list": "browsers"
|
||||
}
|
||||
|
||||
exports['invalid retry value'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": "1",
|
||||
"type": "a positive number or null or an object with keys \"openMode\" and \"runMode\" with values of numbers or nulls"
|
||||
}
|
||||
|
||||
exports['invalid retry object'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": {
|
||||
"fakeMode": 1
|
||||
},
|
||||
"type": "a positive number or null or an object with keys \"openMode\" and \"runMode\" with values of numbers or nulls"
|
||||
}
|
||||
|
||||
exports['src/validation .isPlainObject returns error message when value is a not an object 1'] = {
|
||||
exports['config/lib/validation .isPlainObject returns error message when value is a not an object 1'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": 1,
|
||||
"type": "a plain object"
|
||||
}
|
||||
|
||||
exports['src/validation .isNumber returns error message when value is a not a number 1'] = {
|
||||
exports['config/lib/validation .isNumber returns error message when value is a not a number 1'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": "string",
|
||||
"type": "a number"
|
||||
}
|
||||
|
||||
exports['src/validation .isNumberOrFalse returns error message when value is a not number or false 1'] = {
|
||||
exports['config/lib/validation .isNumberOrFalse returns error message when value is a not number or false 1'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": null,
|
||||
"type": "a number or false"
|
||||
}
|
||||
|
||||
exports['not qualified url'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": "url.com",
|
||||
"type": "a fully qualified URL (starting with `http://` or `https://`)"
|
||||
}
|
||||
|
||||
exports['empty string'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": "",
|
||||
"type": "a fully qualified URL (starting with `http://` or `https://`)"
|
||||
}
|
||||
|
||||
exports['src/validation .isBoolean returns error message when value is a not a string 1'] = {
|
||||
exports['config/lib/validation .isBoolean returns error message when value is a not a string 1'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": 1,
|
||||
"type": "a string"
|
||||
}
|
||||
|
||||
exports['src/validation .isString returns error message when value is a not a string 1'] = {
|
||||
exports['config/lib/validation .isString returns error message when value is a not a string 1'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": 1,
|
||||
"type": "a string"
|
||||
}
|
||||
|
||||
exports['src/validation .isArray returns error message when value is a non-array 1'] = {
|
||||
exports['config/lib/validation .isArray returns error message when value is a non-array 1'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": 1,
|
||||
"type": "an array"
|
||||
}
|
||||
|
||||
exports['src/validation .isStringOrFalse returns error message when value is neither string nor false 1'] = {
|
||||
exports['config/lib/validation .isStringOrFalse returns error message when value is neither string nor false 1'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": null,
|
||||
"type": "a string or false"
|
||||
}
|
||||
|
||||
exports['not string or array'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": null,
|
||||
"type": "a string or an array of strings"
|
||||
}
|
||||
|
||||
exports['array of non-strings'] = {
|
||||
"key": "mockConfigKey",
|
||||
"value": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"type": "a string or an array of strings"
|
||||
}
|
||||
|
||||
exports['not one of the strings error message'] = {
|
||||
"key": "test",
|
||||
"value": "nope",
|
||||
"type": "one of these values: \"foo\", \"bar\""
|
||||
}
|
||||
|
||||
exports['number instead of string'] = {
|
||||
"key": "test",
|
||||
"value": 42,
|
||||
"type": "one of these values: \"foo\", \"bar\""
|
||||
}
|
||||
|
||||
exports['null instead of string'] = {
|
||||
"key": "test",
|
||||
"value": null,
|
||||
"type": "one of these values: \"foo\", \"bar\""
|
||||
}
|
||||
|
||||
exports['not one of the numbers error message'] = {
|
||||
"key": "test",
|
||||
"value": 4,
|
||||
"type": "one of these values: 1, 2, 3"
|
||||
}
|
||||
|
||||
exports['string instead of a number'] = {
|
||||
"key": "test",
|
||||
"value": "foo",
|
||||
"type": "one of these values: 1, 2, 3"
|
||||
}
|
||||
|
||||
exports['null instead of a number'] = {
|
||||
"key": "test",
|
||||
"value": null,
|
||||
"type": "one of these values: 1, 2, 3"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,528 @@
|
||||
const validate = require('./validation')
|
||||
|
||||
interface ResolvedConfigOption {
|
||||
name: string
|
||||
defaultValue?: any
|
||||
validation: Function
|
||||
isFolder?: boolean
|
||||
isExperimental?: boolean
|
||||
/**
|
||||
* Can be mutated with Cypress.config() or test-specific configuration overrides
|
||||
*/
|
||||
canUpdateDuringTestTime?: boolean
|
||||
}
|
||||
|
||||
interface RuntimeConfigOption {
|
||||
name: string
|
||||
defaultValue: any
|
||||
validation: Function
|
||||
isInternal?: boolean
|
||||
/**
|
||||
* Can be mutated with Cypress.config() or test-specific configuration overrides
|
||||
*/
|
||||
canUpdateDuringTestTime?: boolean
|
||||
}
|
||||
|
||||
interface BreakingOption {
|
||||
/**
|
||||
* The non-passive configuration option.
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* String to summarize the error messaging that is logged.
|
||||
*/
|
||||
errorKey: string
|
||||
/**
|
||||
* Configuration value of the configuration option to check against.
|
||||
*/
|
||||
value?: string
|
||||
/**
|
||||
* The new configuration key that is replacing the existing configuration key.
|
||||
*/
|
||||
newName?: string
|
||||
/**
|
||||
* Whether to log the error message as a warning instead of throwing an error.
|
||||
*/
|
||||
isWarning?: boolean
|
||||
}
|
||||
|
||||
const isValidConfig = (key, config) => {
|
||||
const status = validate.isPlainObject(key, config)
|
||||
|
||||
if (status !== true) {
|
||||
return status
|
||||
}
|
||||
|
||||
for (const rule of options) {
|
||||
if (rule.name in config && rule.validation) {
|
||||
const status = rule.validation(`${key}.${rule.name}`, config[rule.name])
|
||||
|
||||
if (status !== true) {
|
||||
return status
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// NOTE:
|
||||
// If you add/remove/change a config value, make sure to update the following
|
||||
// - cli/types/index.d.ts (including allowed config options on TestOptions)
|
||||
// - cypress.schema.json
|
||||
//
|
||||
// Add options in alphabetical order for better readability
|
||||
|
||||
// TODO - add boolean attribute to indicate read-only / static vs mutable options
|
||||
// that can be updated during test executions
|
||||
const resolvedOptions: Array<ResolvedConfigOption> = [
|
||||
{
|
||||
name: 'animationDistanceThreshold',
|
||||
defaultValue: 5,
|
||||
validation: validate.isNumber,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'baseUrl',
|
||||
defaultValue: null,
|
||||
validation: validate.isFullyQualifiedUrl,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'blockHosts',
|
||||
defaultValue: null,
|
||||
validation: validate.isStringOrArrayOfStrings,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'chromeWebSecurity',
|
||||
defaultValue: true,
|
||||
validation: validate.isBoolean,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'clientCertificates',
|
||||
defaultValue: [],
|
||||
validation: validate.isValidClientCertificatesSet,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'component',
|
||||
// runner-ct overrides
|
||||
defaultValue: {},
|
||||
validation: isValidConfig,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'componentFolder',
|
||||
defaultValue: 'cypress/component',
|
||||
validation: validate.isStringOrFalse,
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'defaultCommandTimeout',
|
||||
defaultValue: 4000,
|
||||
validation: validate.isNumber,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'downloadsFolder',
|
||||
defaultValue: 'cypress/downloads',
|
||||
validation: validate.isString,
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'e2e',
|
||||
// e2e runner overrides
|
||||
defaultValue: {},
|
||||
validation: isValidConfig,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'env',
|
||||
defaultValue: {},
|
||||
validation: validate.isPlainObject,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'execTimeout',
|
||||
defaultValue: 60000,
|
||||
validation: validate.isNumber,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'experimentalFetchPolyfill',
|
||||
defaultValue: false,
|
||||
validation: validate.isBoolean,
|
||||
isExperimental: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'experimentalInteractiveRunEvents',
|
||||
defaultValue: false,
|
||||
validation: validate.isBoolean,
|
||||
isExperimental: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'experimentalSessionAndOrigin',
|
||||
defaultValue: false,
|
||||
validation: validate.isBoolean,
|
||||
isExperimental: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'experimentalSourceRewriting',
|
||||
defaultValue: false,
|
||||
validation: validate.isBoolean,
|
||||
isExperimental: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'experimentalStudio',
|
||||
defaultValue: false,
|
||||
validation: validate.isBoolean,
|
||||
isExperimental: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'fileServerFolder',
|
||||
defaultValue: '',
|
||||
validation: validate.isString,
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'fixturesFolder',
|
||||
defaultValue: 'cypress/fixtures',
|
||||
validation: validate.isStringOrFalse,
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'ignoreTestFiles',
|
||||
defaultValue: '*.hot-update.js',
|
||||
validation: validate.isStringOrArrayOfStrings,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'includeShadowDom',
|
||||
defaultValue: false,
|
||||
validation: validate.isBoolean,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'integrationFolder',
|
||||
defaultValue: 'cypress/integration',
|
||||
validation: validate.isString,
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'keystrokeDelay',
|
||||
defaultValue: 0,
|
||||
validation: validate.isNumberOrFalse,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'modifyObstructiveCode',
|
||||
defaultValue: true,
|
||||
validation: validate.isBoolean,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'nodeVersion',
|
||||
validation: validate.isOneOf('bundled', 'system'),
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'numTestsKeptInMemory',
|
||||
defaultValue: 50,
|
||||
validation: validate.isNumber,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'pageLoadTimeout',
|
||||
defaultValue: 60000,
|
||||
validation: validate.isNumber,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'pluginsFile',
|
||||
defaultValue: 'cypress/plugins',
|
||||
validation: validate.isStringOrFalse,
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'port',
|
||||
defaultValue: null,
|
||||
validation: validate.isNumber,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'projectId',
|
||||
defaultValue: null,
|
||||
validation: validate.isString,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'redirectionLimit',
|
||||
defaultValue: 20,
|
||||
validation: validate.isNumber,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'reporter',
|
||||
defaultValue: 'spec',
|
||||
validation: validate.isString,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'reporterOptions',
|
||||
defaultValue: null,
|
||||
validation: validate.isPlainObject,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'requestTimeout',
|
||||
defaultValue: 5000,
|
||||
validation: validate.isNumber,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'resolvedNodePath',
|
||||
defaultValue: null,
|
||||
validation: validate.isString,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'resolvedNodeVersion',
|
||||
defaultValue: null,
|
||||
validation: validate.isString,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'responseTimeout',
|
||||
defaultValue: 30000,
|
||||
validation: validate.isNumber,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'retries',
|
||||
defaultValue: {
|
||||
runMode: 0,
|
||||
openMode: 0,
|
||||
},
|
||||
validation: validate.isValidRetriesConfig,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'screenshotOnRunFailure',
|
||||
defaultValue: true,
|
||||
validation: validate.isBoolean,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'screenshotsFolder',
|
||||
defaultValue: 'cypress/screenshots',
|
||||
validation: validate.isStringOrFalse,
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'slowTestThreshold',
|
||||
defaultValue: (options: Record<string, any> = {}) => options.testingType === 'component' ? 250 : 10000,
|
||||
validation: validate.isNumber,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'scrollBehavior',
|
||||
defaultValue: 'top',
|
||||
validation: validate.isOneOf('center', 'top', 'bottom', 'nearest', false),
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'supportFile',
|
||||
defaultValue: 'cypress/support',
|
||||
validation: validate.isStringOrFalse,
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'supportFolder',
|
||||
defaultValue: false,
|
||||
validation: validate.isStringOrFalse,
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'taskTimeout',
|
||||
defaultValue: 60000,
|
||||
validation: validate.isNumber,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'testFiles',
|
||||
defaultValue: '**/*.*',
|
||||
validation: validate.isStringOrArrayOfStrings,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'trashAssetsBeforeRuns',
|
||||
defaultValue: true,
|
||||
validation: validate.isBoolean,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'userAgent',
|
||||
defaultValue: null,
|
||||
validation: validate.isString,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'video',
|
||||
defaultValue: true,
|
||||
validation: validate.isBoolean,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'videoCompression',
|
||||
defaultValue: 32,
|
||||
validation: validate.isNumberOrFalse,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'videosFolder',
|
||||
defaultValue: 'cypress/videos',
|
||||
validation: validate.isString,
|
||||
isFolder: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'videoUploadOnPasses',
|
||||
defaultValue: true,
|
||||
validation: validate.isBoolean,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'viewportHeight',
|
||||
defaultValue: 660,
|
||||
validation: validate.isNumber,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'viewportWidth',
|
||||
defaultValue: 1000,
|
||||
validation: validate.isNumber,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'waitForAnimations',
|
||||
defaultValue: true,
|
||||
validation: validate.isBoolean,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'watchForFileChanges',
|
||||
defaultValue: true,
|
||||
validation: validate.isBoolean,
|
||||
canUpdateDuringTestTime: false,
|
||||
},
|
||||
]
|
||||
|
||||
const runtimeOptions: Array<RuntimeConfigOption> = [
|
||||
{
|
||||
name: 'autoOpen',
|
||||
defaultValue: false,
|
||||
validation: validate.isBoolean,
|
||||
isInternal: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'browsers',
|
||||
defaultValue: [],
|
||||
validation: validate.isValidBrowserList,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'clientRoute',
|
||||
defaultValue: '/__/',
|
||||
validation: validate.isString,
|
||||
isInternal: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'configFile',
|
||||
defaultValue: 'cypress.json',
|
||||
validation: validate.isStringOrFalse,
|
||||
// not truly internal, but can only be set via cli,
|
||||
// so we don't consider it a "public" option
|
||||
isInternal: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'devServerPublicPathRoute',
|
||||
defaultValue: '/__cypress/src',
|
||||
validation: validate.isString,
|
||||
isInternal: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'hosts',
|
||||
defaultValue: null,
|
||||
validation: validate.isPlainObject,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'isInteractive',
|
||||
defaultValue: true,
|
||||
validation: validate.isBoolean,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'isTextTerminal',
|
||||
defaultValue: false,
|
||||
validation: validate.isBoolean,
|
||||
isInternal: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'morgan',
|
||||
defaultValue: true,
|
||||
validation: validate.isBoolean,
|
||||
isInternal: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'modifyObstructiveCode',
|
||||
defaultValue: true,
|
||||
validation: validate.isBoolean,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'namespace',
|
||||
defaultValue: '__cypress',
|
||||
validation: validate.isString,
|
||||
isInternal: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'reporterRoute',
|
||||
defaultValue: '/__cypress/reporter',
|
||||
validation: validate.isString,
|
||||
isInternal: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'socketId',
|
||||
defaultValue: null,
|
||||
validation: validate.isString,
|
||||
isInternal: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'socketIoCookie',
|
||||
defaultValue: '__socket.io',
|
||||
validation: validate.isString,
|
||||
isInternal: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'socketIoRoute',
|
||||
defaultValue: '/__socket.io',
|
||||
validation: validate.isString,
|
||||
isInternal: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'xhrRoute',
|
||||
defaultValue: '/xhrs/',
|
||||
validation: validate.isString,
|
||||
isInternal: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
},
|
||||
]
|
||||
|
||||
export const options: Array<ResolvedConfigOption|RuntimeConfigOption> = [
|
||||
...resolvedOptions,
|
||||
...runtimeOptions,
|
||||
]
|
||||
|
||||
export const breakingOptions: Array<BreakingOption> = [
|
||||
{
|
||||
name: 'blacklistHosts',
|
||||
errorKey: 'RENAMED_CONFIG_OPTION',
|
||||
newName: 'blockHosts',
|
||||
}, {
|
||||
name: 'experimentalComponentTesting',
|
||||
errorKey: 'EXPERIMENTAL_COMPONENT_TESTING_REMOVED',
|
||||
isWarning: false,
|
||||
}, {
|
||||
name: 'experimentalGetCookiesSameSite',
|
||||
errorKey: 'EXPERIMENTAL_SAMESITE_REMOVED',
|
||||
isWarning: true,
|
||||
}, {
|
||||
name: 'experimentalNetworkStubbing',
|
||||
errorKey: 'EXPERIMENTAL_NETWORK_STUBBING_REMOVED',
|
||||
isWarning: true,
|
||||
}, {
|
||||
name: 'experimentalRunEvents',
|
||||
errorKey: 'EXPERIMENTAL_RUN_EVENTS_REMOVED',
|
||||
isWarning: true,
|
||||
}, {
|
||||
name: 'experimentalSessionSupport',
|
||||
errorKey: 'EXPERIMENTAL_SESSION_SUPPORT_REMOVED',
|
||||
isWarning: true,
|
||||
}, {
|
||||
name: 'experimentalShadowDomSupport',
|
||||
errorKey: 'EXPERIMENTAL_SHADOW_DOM_REMOVED',
|
||||
isWarning: true,
|
||||
}, {
|
||||
name: 'firefoxGcInterval',
|
||||
errorKey: 'FIREFOX_GC_INTERVAL_REMOVED',
|
||||
isWarning: true,
|
||||
}, {
|
||||
name: 'nodeVersion',
|
||||
value: 'system',
|
||||
errorKey: 'NODE_VERSION_DEPRECATION_SYSTEM',
|
||||
isWarning: true,
|
||||
}, {
|
||||
name: 'nodeVersion',
|
||||
value: 'bundled',
|
||||
errorKey: 'NODE_VERSION_DEPRECATION_BUNDLED',
|
||||
isWarning: true,
|
||||
},
|
||||
]
|
||||
@@ -8,7 +8,7 @@ import * as configUtil from '../../src/index'
|
||||
chai.use(sinonChai)
|
||||
const { expect } = chai
|
||||
|
||||
describe('src/index', () => {
|
||||
describe('config/lib/index', () => {
|
||||
describe('.allowed', () => {
|
||||
it('returns filter config only containing allowed keys', () => {
|
||||
const keys = configUtil.allowed({
|
||||
|
||||
@@ -3,7 +3,7 @@ import { expect } from 'chai'
|
||||
|
||||
import * as validation from '../../src/validation'
|
||||
|
||||
describe('src/validation', () => {
|
||||
describe('config/lib/validation', () => {
|
||||
const mockKey = 'mockConfigKey'
|
||||
|
||||
describe('.isValidClientCertificatesSet', () => {
|
||||
|
||||
@@ -4,6 +4,7 @@ export default defineConfig({
|
||||
'projectId': 'ypt4pf',
|
||||
'hosts': {
|
||||
'*.foobar.com': '127.0.0.1',
|
||||
'*.idp.com': '127.0.0.1',
|
||||
},
|
||||
'reporter': 'cypress-multi-reporters',
|
||||
'reporterOptions': {
|
||||
|
||||
@@ -2,18 +2,8 @@ const { assertLogLength } = require('../../../support/utils')
|
||||
const { _, Promise, $ } = Cypress
|
||||
|
||||
describe('src/cy/commands/actions/check', () => {
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/dom.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
cy.visit('/fixtures/dom.html')
|
||||
})
|
||||
|
||||
context('#check', () => {
|
||||
|
||||
@@ -6,18 +6,8 @@ const getActiveElement = () => {
|
||||
}
|
||||
|
||||
describe('src/cy/commands/actions/focus', () => {
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/dom.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
})
|
||||
|
||||
context('#focus', () => {
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
const { $ } = Cypress
|
||||
|
||||
describe('src/cy/commands/actions/hover', () => {
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/dom.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
})
|
||||
|
||||
context('#hover', () => {
|
||||
|
||||
@@ -1,19 +1,8 @@
|
||||
const { $ } = window.Cypress.$Cypress
|
||||
const { _ } = window.Cypress
|
||||
const { _, $ } = Cypress
|
||||
|
||||
describe('src/cy/commands/actions/scroll', () => {
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/scrolling.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/scrolling.html')
|
||||
|
||||
cy.viewport(600, 200)
|
||||
})
|
||||
|
||||
@@ -2,18 +2,8 @@ const { assertLogLength } = require('../../../support/utils')
|
||||
const { _, $ } = Cypress
|
||||
|
||||
describe('src/cy/commands/actions/select', () => {
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/dom.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
})
|
||||
|
||||
context('#select', () => {
|
||||
@@ -563,7 +553,7 @@ describe('src/cy/commands/actions/select', () => {
|
||||
done()
|
||||
})
|
||||
|
||||
cy.get('select[name=fielset-disabled]').select('foo')
|
||||
cy.get('select[name=fieldset-disabled]').select('foo')
|
||||
})
|
||||
|
||||
it('throws when optgroup is disabled', (done) => {
|
||||
|
||||
@@ -2,18 +2,8 @@ const { assertLogLength } = require('../../../support/utils')
|
||||
const { _, $, Promise } = Cypress
|
||||
|
||||
describe('src/cy/commands/actions/submit', () => {
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/dom.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
cy.visit('/fixtures/dom.html')
|
||||
})
|
||||
|
||||
context('#submit', () => {
|
||||
|
||||
@@ -2,18 +2,8 @@ const { assertLogLength } = require('../../../support/utils')
|
||||
const { _, $ } = Cypress
|
||||
|
||||
describe('src/cy/commands/actions/trigger', () => {
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/dom.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
cy.visit('/fixtures/dom.html')
|
||||
})
|
||||
|
||||
context('#trigger', () => {
|
||||
|
||||
@@ -2,18 +2,8 @@ const { assertLogLength } = require('../../support/utils')
|
||||
const { _, $ } = Cypress
|
||||
|
||||
describe('src/cy/commands/aliasing', () => {
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/dom.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
})
|
||||
|
||||
context('#as', () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ const { assertLogLength } = require('../../support/utils')
|
||||
const { _, $ } = Cypress
|
||||
|
||||
describe('src/cy/commands/angular', () => {
|
||||
before(() => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/angular.html')
|
||||
})
|
||||
|
||||
|
||||
@@ -35,20 +35,10 @@ const captureCommands = () => {
|
||||
}
|
||||
|
||||
describe('src/cy/commands/assertions', () => {
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/jquery.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
let testCommands
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
cy.visit('/fixtures/jquery.html')
|
||||
|
||||
testCommands = captureCommands()
|
||||
})
|
||||
@@ -70,6 +60,7 @@ describe('src/cy/commands/assertions', () => {
|
||||
.noop({ foo: 'bar' }).should('deep.eq', { foo: 'bar' })
|
||||
.then((obj) => {
|
||||
expect(testCommands()).to.eql([
|
||||
{ name: 'visit', snapshots: 1, retries: 0 },
|
||||
{ name: 'noop', snapshots: 0, retries: 0 },
|
||||
{ name: 'should', snapshots: 1, retries: 0 },
|
||||
{ name: 'then', snapshots: 0, retries: 0 },
|
||||
@@ -204,6 +195,7 @@ describe('src/cy/commands/assertions', () => {
|
||||
})
|
||||
.then(() => {
|
||||
expect(testCommands()).to.eql([
|
||||
{ name: 'visit', snapshots: 1, retries: 0 },
|
||||
// cy.get() has 2 snapshots, 1 for itself, and 1
|
||||
// for the .should(...) assertion.
|
||||
|
||||
@@ -2997,11 +2989,10 @@ describe('src/cy/commands/assertions', () => {
|
||||
// should be taken.
|
||||
it('only snapshots once when failing to find DOM elements and not retrying', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(testCommands()).to.eql([{
|
||||
name: 'get',
|
||||
snapshots: 1,
|
||||
retries: 0,
|
||||
}])
|
||||
expect(testCommands()).to.eql([
|
||||
{ name: 'visit', snapshots: 1, retries: 0 },
|
||||
{ name: 'get', snapshots: 1, retries: 0 },
|
||||
])
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -115,6 +115,7 @@ describe('src/cy/commands/clock', () => {
|
||||
|
||||
// this test was written to catch a bug in lolex (dep, now @sinonjs/fake-timers) 3 and was fixed by lolex 4 upgrade,
|
||||
it(`doesn't override window.performance members`, () => {
|
||||
cy.visit('/fixtures/empty.html')
|
||||
cy.clock()
|
||||
.then((clock) => {
|
||||
cy.window().then((win) => {
|
||||
|
||||
@@ -1,18 +1,8 @@
|
||||
const { _, $ } = Cypress
|
||||
const { _ } = Cypress
|
||||
|
||||
describe('src/cy/commands/commands', () => {
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/dom.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
})
|
||||
|
||||
it('can invoke commands by name', () => {
|
||||
@@ -43,7 +33,7 @@ describe('src/cy/commands/commands', () => {
|
||||
cy.command('get', 'body').then(() => {
|
||||
const names = cy.queue.names()
|
||||
|
||||
expect(names).to.deep.eq(['get', 'then'])
|
||||
expect(names).to.deep.eq(['visit', 'get', 'then'])
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -3,18 +3,8 @@ const { _, Promise, $ } = Cypress
|
||||
|
||||
describe('src/cy/commands/connectors', () => {
|
||||
describe('with jquery', () => {
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/jquery.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
cy.visit('/fixtures/jquery.html')
|
||||
})
|
||||
|
||||
context('#spread', () => {
|
||||
@@ -90,7 +80,8 @@ describe('src/cy/commands/connectors', () => {
|
||||
|
||||
it('does not insert a mocha callback', () => {
|
||||
cy.noop().then(() => {
|
||||
expect(cy.queue.length).to.eq(2)
|
||||
// queue: visit -> noop -> then
|
||||
expect(cy.queue.length).to.eq(3)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1803,18 +1794,8 @@ describe('src/cy/commands/connectors', () => {
|
||||
})
|
||||
|
||||
describe('without jquery', () => {
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/dom.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
})
|
||||
|
||||
context('#each', () => {
|
||||
|
||||
@@ -161,6 +161,8 @@ describe('src/cy/commands/files', () => {
|
||||
defaultCommandTimeout: 50,
|
||||
}, () => {
|
||||
beforeEach(function () {
|
||||
cy.visit('/fixtures/empty.html')
|
||||
|
||||
this.logs = []
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
|
||||
@@ -2,18 +2,8 @@ const { assertLogLength } = require('../../support/utils')
|
||||
const { _, $, dom } = Cypress
|
||||
|
||||
describe('src/cy/commands/misc', () => {
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/jquery.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
cy.visit('/fixtures/jquery.html')
|
||||
})
|
||||
|
||||
context('#end', () => {
|
||||
|
||||
@@ -6,24 +6,8 @@ const { _, Promise, $ } = Cypress
|
||||
|
||||
describe('src/cy/commands/navigation', () => {
|
||||
context('#reload', () => {
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/generic.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
this.win = cy.state('window')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
cy.state('window', this.win)
|
||||
cy.visit('/fixtures/generic.html')
|
||||
})
|
||||
|
||||
it('calls into window.location.reload', () => {
|
||||
@@ -288,24 +272,11 @@ describe('src/cy/commands/navigation', () => {
|
||||
})
|
||||
|
||||
context('#go', () => {
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/generic.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
})
|
||||
|
||||
// TODO: fix this
|
||||
it('sets timeout to Cypress.config(pageLoadTimeout)', {
|
||||
pageLoadTimeout: 4567,
|
||||
}, () => {
|
||||
cy.visit('/fixtures/generic.html')
|
||||
const timeout = cy.spy(Promise.prototype, 'timeout')
|
||||
|
||||
cy
|
||||
@@ -508,7 +479,7 @@ describe('src/cy/commands/navigation', () => {
|
||||
})
|
||||
|
||||
it('only logs once on error', function (done) {
|
||||
cy.on('fail', (err) => {
|
||||
cy.once('fail', (err) => {
|
||||
assertLogLength(this.logs, 1)
|
||||
expect(this.logs[0].get('error')).to.eq(err)
|
||||
|
||||
@@ -814,7 +785,7 @@ describe('src/cy/commands/navigation', () => {
|
||||
})
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/1311
|
||||
it(`window immediately resolves and doesn't reload when visiting the same URL with hashes`, () => {
|
||||
it('window immediately resolves and doesn\'t reload when visiting the same URL with hashes', () => {
|
||||
const onLoad = cy.stub()
|
||||
|
||||
cy
|
||||
@@ -825,7 +796,7 @@ describe('src/cy/commands/navigation', () => {
|
||||
onLoad,
|
||||
})
|
||||
.then((win) => {
|
||||
expect(win.bar).to.not.exist
|
||||
expect(win.foo).to.equal('bar')
|
||||
expect(onLoad).not.to.have.been.called
|
||||
})
|
||||
})
|
||||
@@ -971,7 +942,7 @@ describe('src/cy/commands/navigation', () => {
|
||||
})
|
||||
|
||||
describe('location getter overrides', () => {
|
||||
before(() => {
|
||||
beforeEach(function () {
|
||||
cy
|
||||
.visit('/fixtures/jquery.html?foo=bar#dashboard?baz=quux')
|
||||
.window().as('win').then((win) => {
|
||||
@@ -980,9 +951,7 @@ describe('src/cy/commands/navigation', () => {
|
||||
// overriding the location getters
|
||||
expect(win.location.href).to.include('/fixtures/jquery.html?foo=bar#dashboard?baz=quux')
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
this.win = cy.state('window')
|
||||
|
||||
this.eq = (attr, str) => {
|
||||
@@ -1440,10 +1409,25 @@ describe('src/cy/commands/navigation', () => {
|
||||
it('throws when attempting to visit a 2nd domain on different port', function (done) {
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
const experimentalMessage = Cypress.config('experimentalSessionAndOrigin') ? `You likely forgot to use \`cy.origin()\`:\n` : `In order to visit a different origin, you can enable the \`experimentalSessionAndOrigin\` flag and use \`cy.origin()\`:\n`
|
||||
|
||||
expect(err.message).to.equal(stripIndent`\
|
||||
\`cy.visit()\` failed because you are attempting to visit a URL that is of a different origin.\n
|
||||
${experimentalMessage}
|
||||
\`cy.visit('http://localhost:3500/fixtures/generic.html')\`
|
||||
\`<commands targeting http://localhost:3500 go here>\`\n
|
||||
\`cy.origin('http://localhost:3501', () => {\`
|
||||
\` cy.visit('http://localhost:3501/fixtures/generic.html')\`
|
||||
\` <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
|
||||
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://localhost:3501'`)
|
||||
|
||||
expect(err.message).to.include('`cy.visit()` failed because you are attempting to visit a URL that is of a different origin.')
|
||||
expect(err.message).to.include('The new URL is considered a different origin because the following parts of the URL are different:')
|
||||
expect(err.message).to.include('> port')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/cannot-visit-different-origin-domain')
|
||||
assertLogLength(this.logs, 2)
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
@@ -1452,17 +1436,31 @@ describe('src/cy/commands/navigation', () => {
|
||||
})
|
||||
|
||||
cy.visit('http://localhost:3500/fixtures/generic.html')
|
||||
|
||||
cy.visit('http://localhost:3501/fixtures/generic.html')
|
||||
})
|
||||
|
||||
it('throws when attempting to visit a 2nd domain on different protocol', function (done) {
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
const experimentalMessage = Cypress.config('experimentalSessionAndOrigin') ? `You likely forgot to use \`cy.origin()\`:\n` : `In order to visit a different origin, you can enable the \`experimentalSessionAndOrigin\` flag and use \`cy.origin()\`:\n`
|
||||
|
||||
expect(err.message).to.equal(stripIndent`\
|
||||
\`cy.visit()\` failed because you are attempting to visit a URL that is of a different origin.\n
|
||||
${experimentalMessage}
|
||||
\`cy.visit('http://localhost:3500/fixtures/generic.html')\`
|
||||
\`<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, 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:3502'`)
|
||||
|
||||
expect(err.message).to.include('`cy.visit()` failed because you are attempting to visit a URL that is of a different origin.')
|
||||
expect(err.message).to.include('The new URL is considered a different origin because the following parts of the URL are different:')
|
||||
expect(err.message).to.include('> protocol')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/cannot-visit-different-origin-domain')
|
||||
assertLogLength(this.logs, 2)
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
@@ -1471,16 +1469,31 @@ 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) {
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
const experimentalMessage = Cypress.config('experimentalSessionAndOrigin') ? `You likely forgot to use \`cy.origin()\`:\n` : `In order to visit a different origin, you can enable the \`experimentalSessionAndOrigin\` flag and use \`cy.origin()\`:\n`
|
||||
|
||||
expect(err.message).to.equal(stripIndent`\
|
||||
\`cy.visit()\` failed because you are attempting to visit a URL that is of a different origin.\n
|
||||
${experimentalMessage}
|
||||
\`cy.visit('http://localhost:3500/fixtures/generic.html')\`
|
||||
\`<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://www.foobar.com:3500'`)
|
||||
|
||||
expect(err.message).to.include('`cy.visit()` failed because you are attempting to visit a URL that is of a different origin.')
|
||||
expect(err.message).to.include('The new URL is considered a different origin because the following parts of the URL are different:')
|
||||
expect(err.message).to.include('> superdomain')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/cannot-visit-different-origin-domain')
|
||||
assertLogLength(this.logs, 2)
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
@@ -1489,14 +1502,31 @@ 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) {
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
const experimentalMessage = Cypress.config('experimentalSessionAndOrigin') ? `You likely forgot to use \`cy.origin()\`:\n` : `In order to visit a different origin, you can enable the \`experimentalSessionAndOrigin\` flag and use \`cy.origin()\`:\n`
|
||||
|
||||
expect(err.message).to.equal(stripIndent`\
|
||||
\`cy.visit()\` failed because you are attempting to visit a URL that is of a different origin.\n
|
||||
${experimentalMessage}
|
||||
\`cy.visit('http://127.0.0.1:3500/fixtures/generic.html')\`
|
||||
\`<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://0.0.0.0:3500'`)
|
||||
|
||||
expect(err.message).to.include('`cy.visit()` failed because you are attempting to visit a URL that is of a different origin.')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/cannot-visit-different-origin-domain')
|
||||
assertLogLength(this.logs, 2)
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
@@ -1506,22 +1536,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) {
|
||||
@@ -2134,31 +2149,51 @@ describe('src/cy/commands/navigation', () => {
|
||||
.get('#does-not-exist', { timeout: 200 }).should('have.class', 'foo')
|
||||
})
|
||||
|
||||
it('captures cross origin failures', function (done) {
|
||||
cy.once('fail', (err) => {
|
||||
it('displays cross origin failures when navigating to a cross origin', { pageLoadTimeout: 3000 }, function (done) {
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
|
||||
assertLogLength(this.logs, 2)
|
||||
expect(err.message).to.include('Cypress detected a cross origin error happened on page load')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/cross-origin-violation')
|
||||
expect(lastLog.get('name')).to.eq('page load')
|
||||
expect(lastLog.get('state')).to.eq('failed')
|
||||
if (Cypress.config('experimentalSessionAndOrigin')) {
|
||||
// When the experimentalSessionAndOrigin feature is enabled, we will timeout and display this message.
|
||||
expect(err.message).to.equal(stripIndent`\
|
||||
Timed out after waiting \`3000ms\` for your remote page to load on origin(s):\n
|
||||
- \`http://localhost:3500\`\n
|
||||
A cross-origin request for \`http://www.foobar.com:3500/fixtures/multi-domain-secondary.html\` was detected.\n
|
||||
A command that triggers cross-origin navigation must be immediately followed by a \`cy.origin()\` command:\n
|
||||
\`cy.origin(\'http://foobar.com:3500\', () => {\`
|
||||
\` <commands targeting http://www.foobar.com:3500 go here>\`
|
||||
\`})\`\n
|
||||
If the cross-origin request was an intermediary state, you can try increasing the \`pageLoadTimeout\` value in \`cypress.json\` to wait longer.\n
|
||||
Browsers will not fire the \`load\` event until all stylesheets and scripts are done downloading.\n
|
||||
When this \`load\` event occurs, Cypress will continue running commands.`)
|
||||
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/origin')
|
||||
} else {
|
||||
const error = Cypress.isBrowser('firefox') ? 'Permission denied to access property "document" on cross-origin object' : 'Blocked a frame with origin "http://localhost:3500" from accessing a cross-origin frame.'
|
||||
|
||||
// When the experimentalSessionAndOrigin feature is disabled, we will immediately and display this message.
|
||||
expect(err.message).to.equal(stripIndent`\
|
||||
Cypress detected a cross origin error happened on page load:\n
|
||||
> ${error}\n
|
||||
Before the page load, you were bound to the origin policy:\n
|
||||
> http://localhost:3500\n
|
||||
A cross origin error happens when your application navigates to a new URL which does not match the origin policy above.\n
|
||||
A new URL does not match the origin policy if the 'protocol', 'port' (if specified), and/or 'host' (unless of the same superdomain) are different.\n
|
||||
Cypress does not allow you to navigate to a different origin URL within a single test.\n
|
||||
You may need to restructure some of your test code to avoid this problem.\n
|
||||
Alternatively you can also disable Chrome Web Security in Chromium-based browsers which will turn off this restriction by setting { chromeWebSecurity: false } in \`cypress.json\`.`)
|
||||
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/cross-origin-violation')
|
||||
}
|
||||
|
||||
assertLogLength(this.logs, 6)
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
expect(cy.state('onPageLoadErr')).to.be.null
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy
|
||||
.visit('/fixtures/jquery.html')
|
||||
.window({ log: false }).then((win) => {
|
||||
const url = 'http://localhost:3501/fixtures/generic.html'
|
||||
|
||||
const $a = win.$(`<a href='${url}'>jquery</a>`)
|
||||
.appendTo(win.document.body)
|
||||
|
||||
causeSynchronousBeforeUnload($a)
|
||||
})
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
return null
|
||||
@@ -2285,20 +2320,33 @@ describe('src/cy/commands/navigation', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('waits for stability at the end of the command queue when not stable', (done) => {
|
||||
it('tests waiting on stability at the end of the command queue', (done) => {
|
||||
cy
|
||||
.visit('/fixtures/generic.html')
|
||||
.then((win) => {
|
||||
cy.on('window:load', () => {
|
||||
// We do not wait if the experimentalSessionAndOrigin feature is enabled
|
||||
if (Cypress.config('experimentalSessionAndOrigin')) {
|
||||
const onLoad = cy.spy()
|
||||
|
||||
cy.on('window:load', onLoad)
|
||||
|
||||
cy.on('command:queue:end', () => {
|
||||
expect(onLoad).not.have.been.called
|
||||
done()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
// We do wait if the experimentalSessionAndOrigin feature is not enabled
|
||||
cy.on('window:load', () => {
|
||||
cy.on('command:queue:end', () => {
|
||||
done()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
cy.on('command:queue:before:end', () => {
|
||||
// force us to become unstable immediately
|
||||
// else the beforeunload event fires at the end
|
||||
// of the tick which is too late
|
||||
// force us to become unstable immediately
|
||||
// else the beforeunload event fires at the end
|
||||
// of the tick which is too late
|
||||
cy.isStable(false, 'testing')
|
||||
|
||||
win.location.href = '/timeout?ms=100'
|
||||
|
||||
@@ -1011,7 +1011,7 @@ describe('network stubbing', function () {
|
||||
// using "hosts" setting in the "cypress.json" file
|
||||
const corsUrl = 'http://diff.foobar.com:3501/no-cors'
|
||||
|
||||
before(() => {
|
||||
beforeEach(() => {
|
||||
cy.visit('http://127.0.0.1:3500/fixtures/dom.html')
|
||||
})
|
||||
|
||||
@@ -1070,7 +1070,7 @@ describe('network stubbing', function () {
|
||||
// a different domain from the page own domain
|
||||
const corsUrl = 'http://diff.foobar.com:3501/cors'
|
||||
|
||||
before(() => {
|
||||
beforeEach(() => {
|
||||
cy.visit('http://127.0.0.1:3500/fixtures/dom.html')
|
||||
})
|
||||
|
||||
@@ -1278,6 +1278,8 @@ describe('network stubbing', function () {
|
||||
|
||||
// @see https://github.com/cypress-io/cypress/issues/15841
|
||||
it('prevents requests from reaching destination server', function () {
|
||||
cy.visit('/fixtures/empty.html')
|
||||
|
||||
const v = String(Date.now())
|
||||
|
||||
// this test creates server-side state via /set-var to test if requests are being sent or not
|
||||
@@ -1991,6 +1993,7 @@ describe('network stubbing', function () {
|
||||
.intercept(`${url}*`, (req) => {
|
||||
// @ts-ignore
|
||||
req.on(eventName, (res) => {
|
||||
res.headers['content-type'] = 'application/json'
|
||||
res.send({
|
||||
statusCode: 200,
|
||||
fixture: 'valid.json',
|
||||
@@ -2616,6 +2619,8 @@ describe('network stubbing', function () {
|
||||
it('intercepts cached responses as expected', {
|
||||
browser: '!firefox', // TODO: why does firefox behave differently? transparently returns cached response
|
||||
}, function () {
|
||||
cy.visit('/fixtures/empty.html')
|
||||
|
||||
// use a queryparam to bust cache from previous runs of this test
|
||||
const url = `/fixtures/generic.html?t=${Date.now()}`
|
||||
let hits = 0
|
||||
@@ -2688,6 +2693,7 @@ describe('network stubbing', function () {
|
||||
|
||||
return Promise.delay(delay)
|
||||
.then(() => {
|
||||
res.headers['content-type'] = 'text/plain'
|
||||
res.send('Promise.delay worked')
|
||||
})
|
||||
})
|
||||
@@ -2731,6 +2737,7 @@ describe('network stubbing', function () {
|
||||
req.reply((res) => {
|
||||
this.start = Date.now()
|
||||
|
||||
res.headers['content-type'] = 'text/plain'
|
||||
res.setThrottle(kbps).send(payload)
|
||||
})
|
||||
}).then(() => {
|
||||
@@ -2755,6 +2762,7 @@ describe('network stubbing', function () {
|
||||
req.reply((res) => {
|
||||
this.start = Date.now()
|
||||
|
||||
res.headers['content-type'] = 'text/plain'
|
||||
res.setThrottle(kbps).setDelay(delay).send({
|
||||
statusCode: 200,
|
||||
body: payload,
|
||||
@@ -2929,6 +2937,7 @@ describe('network stubbing', function () {
|
||||
it('res.send(body)', function () {
|
||||
cy.intercept('/custom-headers*', function (req) {
|
||||
req.reply((res) => {
|
||||
res.headers['content-type'] = 'text/plain'
|
||||
res.send('baz')
|
||||
})
|
||||
})
|
||||
@@ -2985,6 +2994,7 @@ describe('network stubbing', function () {
|
||||
it('res.send(status, body)', function (done) {
|
||||
cy.intercept('/custom-headers*', function (req) {
|
||||
req.reply((res) => {
|
||||
res.headers['content-type'] = 'text/plain'
|
||||
res.send(777, 'bar')
|
||||
})
|
||||
})
|
||||
@@ -3047,6 +3057,7 @@ describe('network stubbing', function () {
|
||||
|
||||
cy.intercept(`${url}*`, function (req) {
|
||||
req.reply((res) => {
|
||||
res.headers['content-type'] = 'application/json'
|
||||
res.send({
|
||||
statusCode: 200,
|
||||
fixture: 'valid.json',
|
||||
@@ -3090,6 +3101,7 @@ describe('network stubbing', function () {
|
||||
req.reply((res) => {
|
||||
this.start = Date.now()
|
||||
|
||||
res.headers['content-type'] = 'text/plain'
|
||||
// ensure .throttle and .delay are overridden
|
||||
res.setThrottle(1e6).setDelay(1).send({
|
||||
statusCode: 200,
|
||||
|
||||
@@ -761,9 +761,9 @@ describe('src/cy/commands/screenshot', () => {
|
||||
cy.get('.short-element').within(() => {
|
||||
cy.screenshot({ capture: 'runner' })
|
||||
}).then(() => {
|
||||
// the runner was captured
|
||||
expect(Cypress.action.withArgs('cy:before:screenshot').args[0][1].appOnly).to.be.true
|
||||
expect(Cypress.automation.withArgs('take:screenshot').args[0][1].capture).to.equal('viewport')
|
||||
// the runner was captured ("appOnly === true" means to hide the runner UI)
|
||||
expect(Cypress.action.withArgs('cy:before:screenshot').args[0][1].appOnly).to.be.false
|
||||
expect(Cypress.automation.withArgs('take:screenshot').args[0][1].capture).to.equal('runner')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
const {
|
||||
getSessionDetails,
|
||||
getConsoleProps,
|
||||
navigateAboutBlank,
|
||||
} = require('@packages/driver/src/cy/commands/sessions/utils')
|
||||
|
||||
describe('src/cy/commands/sessions/utils.ts', () => {
|
||||
describe('.getSessionDetails', () => {
|
||||
it('for one domain with neither cookies or local storage set', () => {
|
||||
const sessionState = {
|
||||
id: 'session1',
|
||||
}
|
||||
|
||||
const details = getSessionDetails(sessionState)
|
||||
|
||||
expect(details.id).to.eq('session1')
|
||||
expect(Object.keys(details.data)).to.have.length(0)
|
||||
})
|
||||
|
||||
it('for one domain with only cookies set', () => {
|
||||
const sessionState = {
|
||||
id: 'session1',
|
||||
cookies: [
|
||||
{ name: 'foo', value: 'f', path: '/', domain: 'localhost', secure: true, httpOnly: true, expiry: 123 },
|
||||
],
|
||||
}
|
||||
|
||||
const details = getSessionDetails(sessionState)
|
||||
|
||||
expect(details.id).to.eq('session1')
|
||||
expect(Object.keys(details.data)).to.have.length(1)
|
||||
expect(details.data).to.have.property('localhost')
|
||||
expect(details.data.localhost).to.deep.eq({
|
||||
cookies: 1,
|
||||
})
|
||||
})
|
||||
|
||||
it('for one domain with only local storage set', () => {
|
||||
const sessionState = {
|
||||
id: 'session1',
|
||||
localStorage: [
|
||||
{ origin: 'localhost', value: { 'stor-foo': 's-f' } },
|
||||
],
|
||||
}
|
||||
|
||||
const details = getSessionDetails(sessionState)
|
||||
|
||||
expect(details.id).to.eq('session1')
|
||||
expect(Object.keys(details.data)).to.have.length(1)
|
||||
expect(details.data).to.have.property('localhost')
|
||||
expect(details.data.localhost).to.deep.eq({
|
||||
localStorage: 1,
|
||||
})
|
||||
})
|
||||
|
||||
it('for one domain with both cookies and localStorage', () => {
|
||||
const sessionState = {
|
||||
id: 'session1',
|
||||
cookies: [
|
||||
{ name: 'foo', value: 'f', path: '/', domain: 'localhost', secure: true, httpOnly: true, expiry: 123 },
|
||||
],
|
||||
localStorage: [
|
||||
{ origin: 'localhost', value: { 'stor-foo': 's-f' } },
|
||||
],
|
||||
}
|
||||
|
||||
const details = getSessionDetails(sessionState)
|
||||
|
||||
expect(details.id).to.eq('session1')
|
||||
expect(Object.keys(details.data)).to.have.length(1)
|
||||
expect(details.data).to.have.property('localhost')
|
||||
expect(details.data.localhost).to.deep.eq({
|
||||
cookies: 1,
|
||||
localStorage: 1,
|
||||
})
|
||||
})
|
||||
|
||||
it('for multiple domains', () => {
|
||||
const sessionState = {
|
||||
id: 'session1',
|
||||
cookies: [
|
||||
{ name: 'foo', value: 'f', path: '/', domain: 'localhost', secure: true, httpOnly: true, expiry: 123 },
|
||||
{ name: 'bar', value: 'b', path: '/', domain: 'localhost', secure: false, httpOnly: false, expiry: 456 },
|
||||
],
|
||||
localStorage: [
|
||||
{ origin: 'localhost', value: { 'stor-foo': 's-f' } },
|
||||
{ origin: 'http://example.com', value: { 'random': 'hi' } },
|
||||
],
|
||||
}
|
||||
|
||||
const details = getSessionDetails(sessionState)
|
||||
|
||||
expect(details.id).to.eq('session1')
|
||||
expect(Object.keys(details.data)).to.have.length(2)
|
||||
expect(details.data).to.have.property('localhost')
|
||||
expect(details.data.localhost).to.deep.eq({
|
||||
cookies: 2,
|
||||
localStorage: 1,
|
||||
})
|
||||
|
||||
expect(details.data).to.have.property('example.com')
|
||||
expect(details.data['example.com']).to.deep.eq({
|
||||
localStorage: 1,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('.getConsoleProps', () => {
|
||||
it('for one domain with neither cookies or localStorage set', () => {
|
||||
const sessionState = {
|
||||
id: 'session1',
|
||||
}
|
||||
|
||||
const consoleProps = getConsoleProps(sessionState)
|
||||
|
||||
expect(consoleProps.id).to.eq('session1')
|
||||
expect(consoleProps.table).to.have.length(0)
|
||||
})
|
||||
|
||||
it('for one domain with only cookies set', () => {
|
||||
const sessionState = {
|
||||
id: 'session1',
|
||||
cookies: [
|
||||
{ name: 'foo', value: 'f', path: '/', domain: 'localhost', secure: true, httpOnly: true, expiry: 123 },
|
||||
],
|
||||
}
|
||||
|
||||
const consoleProps = getConsoleProps(sessionState)
|
||||
|
||||
expect(consoleProps.id).to.eq('session1')
|
||||
expect(consoleProps.table).to.have.length(1)
|
||||
const cookiesTable = consoleProps.table[0]()
|
||||
|
||||
expect(cookiesTable.name).to.contain('Cookies - localhost (1)')
|
||||
expect(cookiesTable.data).to.deep.eq(sessionState.cookies)
|
||||
})
|
||||
|
||||
it('for one domain with only localStorage set', () => {
|
||||
const sessionState = {
|
||||
id: 'session1',
|
||||
localStorage: [
|
||||
{ origin: 'localhost', value: { 'stor-foo': 's-f' } },
|
||||
],
|
||||
}
|
||||
const consoleProps = getConsoleProps(sessionState)
|
||||
|
||||
expect(consoleProps.id).to.eq('session1')
|
||||
expect(consoleProps.table).to.have.length(1)
|
||||
const localStorageTable = consoleProps.table[0]()
|
||||
|
||||
expect(localStorageTable.name).to.contain('Storage - localhost (1)')
|
||||
expect(localStorageTable.data).to.have.length(1)
|
||||
expect(localStorageTable.data).to.deep.eq([{ key: 'stor-foo', value: 's-f' }])
|
||||
})
|
||||
|
||||
it('for one domain with both cookies and localStorage set', () => {
|
||||
const sessionState = {
|
||||
id: 'session1',
|
||||
cookies: [
|
||||
{ name: 'foo', value: 'f', path: '/', domain: 'localhost', secure: true, httpOnly: true, expiry: 123 },
|
||||
],
|
||||
localStorage: [
|
||||
{ origin: 'localhost', value: { 'stor-foo': 's-f' } },
|
||||
],
|
||||
}
|
||||
|
||||
const consoleProps = getConsoleProps(sessionState)
|
||||
|
||||
expect(consoleProps.id).to.eq('session1')
|
||||
expect(consoleProps.table).to.have.length(2)
|
||||
let table = consoleProps.table[0]()
|
||||
|
||||
expect(table.name).to.contain('Cookies - localhost (1)')
|
||||
expect(table.data).to.have.length(1)
|
||||
expect(table.data).to.deep.eq(sessionState.cookies)
|
||||
|
||||
table = consoleProps.table[1]()
|
||||
expect(table.name).to.contain('Storage - localhost (1)')
|
||||
expect(table.data).to.have.length(1)
|
||||
expect(table.data).to.deep.eq([{ key: 'stor-foo', value: 's-f' }])
|
||||
})
|
||||
|
||||
it('for multiple domains', () => {
|
||||
const sessionState = {
|
||||
id: 'session1',
|
||||
cookies: [
|
||||
{ name: 'foo', value: 'f', path: '/', domain: 'localhost', secure: true, httpOnly: true, expiry: 123 },
|
||||
{ name: 'bar', value: 'b', path: '/', domain: 'localhost', secure: false, httpOnly: false, expiry: 456 },
|
||||
],
|
||||
localStorage: [
|
||||
{ origin: 'localhost', value: { 'stor-foo': 's-f' } },
|
||||
{ origin: 'http://example.com', value: { 'random': 'hi' } },
|
||||
],
|
||||
}
|
||||
|
||||
const consoleProps = getConsoleProps(sessionState)
|
||||
|
||||
expect(consoleProps.id).to.eq('session1')
|
||||
expect(consoleProps.table).to.have.length(3)
|
||||
let table = consoleProps.table[0]()
|
||||
|
||||
expect(table.name).to.contain('Cookies - localhost (2)')
|
||||
expect(table.data).to.have.length(2)
|
||||
expect(table.data).to.deep.eq(sessionState.cookies)
|
||||
|
||||
table = consoleProps.table[1]()
|
||||
expect(table.name).to.contain('Storage - localhost (1)')
|
||||
expect(table.data).to.have.length(1)
|
||||
expect(table.data).to.deep.eq([{ key: 'stor-foo', value: 's-f' }])
|
||||
|
||||
table = consoleProps.table[2]()
|
||||
expect(table.name).to.contain('Storage - example.com (1)')
|
||||
expect(table.data).to.have.length(1)
|
||||
expect(table.data).to.deep.eq([{ key: 'random', value: 'hi' }])
|
||||
})
|
||||
})
|
||||
|
||||
describe('.navigateAboutBlank', () => {
|
||||
it('triggers session blank page visit', () => {
|
||||
const stub = cy.stub(Cypress, 'action').log(false)
|
||||
.callThrough()
|
||||
.withArgs('cy:visit:blank')
|
||||
|
||||
cy.then(() => {
|
||||
navigateAboutBlank()
|
||||
navigateAboutBlank(true)
|
||||
expect(stub).to.have.been.calledTwice
|
||||
expect(stub.args[0]).to.deep.eq(['cy:visit:blank', { type: 'session' }])
|
||||
expect(stub.args[1]).to.deep.eq(['cy:visit:blank', { type: 'session' }])
|
||||
})
|
||||
})
|
||||
|
||||
it('triggers session-lifecycle blank page visit', () => {
|
||||
const stub = cy.stub(Cypress, 'action').log(false)
|
||||
.callThrough()
|
||||
.withArgs('cy:visit:blank')
|
||||
|
||||
cy.then(() => {
|
||||
navigateAboutBlank(false)
|
||||
expect(stub).to.have.been.calledWith('cy:visit:blank', { type: 'session-lifecycle' })
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -53,7 +53,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
describe('alias argument', () => {
|
||||
before(() => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/jquery.html')
|
||||
})
|
||||
|
||||
@@ -750,7 +750,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
describe('multiple alias arguments', () => {
|
||||
before(() => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/jquery.html')
|
||||
})
|
||||
|
||||
@@ -776,7 +776,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
describe('multiple separate alias waits', () => {
|
||||
before(() => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/jquery.html')
|
||||
})
|
||||
|
||||
@@ -1022,6 +1022,8 @@ describe('src/cy/commands/waiting', () => {
|
||||
done()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/empty.html')
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, {}).as('getFoo')
|
||||
@@ -1042,6 +1044,10 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
describe('alias argument', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/empty.html')
|
||||
})
|
||||
|
||||
it('is a parent command', () => {
|
||||
cy
|
||||
.server()
|
||||
@@ -1133,6 +1139,10 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
describe('timeouts', function () {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/empty.html')
|
||||
})
|
||||
|
||||
it('sets default requestTimeout', {
|
||||
requestTimeout: 199,
|
||||
}, function (done) {
|
||||
|
||||
@@ -364,26 +364,16 @@ describe('src/cy/commands/window', () => {
|
||||
})
|
||||
|
||||
context('#title', () => {
|
||||
before(() => {
|
||||
beforeEach(() => {
|
||||
cy
|
||||
.visit('/fixtures/generic.html')
|
||||
.then(function (win) {
|
||||
const h = $(win.document.head)
|
||||
|
||||
h.find('script').remove()
|
||||
|
||||
this.head = h.prop('outerHTML')
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.head).empty().html(this.head)
|
||||
$(doc.body).empty().html(this.body)
|
||||
})
|
||||
|
||||
it('returns the pages title as a string', () => {
|
||||
const title = cy.$$('title').text()
|
||||
|
||||
|
||||
@@ -3,26 +3,16 @@ const { assertLogLength } = require('../../support/utils')
|
||||
const { _, $, Promise } = Cypress
|
||||
|
||||
describe('src/cy/commands/xhr', () => {
|
||||
before(() => {
|
||||
beforeEach(function () {
|
||||
cy
|
||||
.visit('/fixtures/jquery.html')
|
||||
.then(function (win) {
|
||||
const h = $(win.document.head)
|
||||
|
||||
h.find('script').remove()
|
||||
|
||||
this.head = h.prop('outerHTML')
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.head).empty().html(this.head)
|
||||
$(doc.body).empty().html(this.body)
|
||||
})
|
||||
|
||||
context('#startXhrServer', () => {
|
||||
it('continues to be a defined properties', () => {
|
||||
cy
|
||||
|
||||
@@ -32,28 +32,16 @@ describe('driver/src/cy/snapshots', () => {
|
||||
})
|
||||
|
||||
context('snapshot el', () => {
|
||||
before(() => {
|
||||
beforeEach(() => {
|
||||
cy
|
||||
.visit('/fixtures/generic.html')
|
||||
.then(function (win) {
|
||||
const h = $(win.document.head)
|
||||
|
||||
h.find('script').remove()
|
||||
|
||||
this.head = h.prop('outerHTML')
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.head).empty().html(this.head)
|
||||
$(doc.body).empty().html(this.body)
|
||||
|
||||
this.$el = $('<span id=\'snapshot\'>snapshot</span>').appendTo(cy.$$('body'))
|
||||
})
|
||||
|
||||
it('does not clone scripts', function () {
|
||||
$('<script type=\'text/javascript\' />').appendTo(cy.$$('body'))
|
||||
|
||||
@@ -95,17 +83,20 @@ describe('driver/src/cy/snapshots', () => {
|
||||
})
|
||||
|
||||
it('sets data-cypress-el attr', function () {
|
||||
const attr = cy.spy(this.$el, 'attr')
|
||||
const $el = $('<span id=\'snapshot\'>snapshot</span>').appendTo(cy.$$('body'))
|
||||
const attr = cy.spy($el, 'attr')
|
||||
|
||||
cy.createSnapshot(null, this.$el)
|
||||
cy.createSnapshot(null, $el)
|
||||
|
||||
expect(attr).to.be.calledWith('data-cypress-el', 'true')
|
||||
})
|
||||
|
||||
it('removes data-cypress-el attr', function () {
|
||||
cy.createSnapshot(null, this.$el)
|
||||
const $el = $('<span id=\'snapshot\'>snapshot</span>').appendTo(cy.$$('body'))
|
||||
|
||||
expect(this.$el.attr('data-cypress-el')).to.be.undefined
|
||||
cy.createSnapshot(null, $el)
|
||||
|
||||
expect($el.attr('data-cypress-el')).to.be.undefined
|
||||
})
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/8679
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
const { $ } = Cypress
|
||||
|
||||
describe('driver/src/cypress/cy', () => {
|
||||
let body
|
||||
|
||||
before(() => {
|
||||
cy
|
||||
.visit('/fixtures/dom.html')
|
||||
.then((win) => {
|
||||
body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(body)
|
||||
cy.visit('/fixtures/dom.html')
|
||||
})
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/7731
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { create } = require('@packages/driver/src/cypress/log')
|
||||
const { create, LogUtils } = require('@packages/driver/src/cypress/log')
|
||||
|
||||
describe('src/cypress/log', function () {
|
||||
context('#snapshot', function () {
|
||||
@@ -54,4 +54,51 @@ describe('src/cypress/log', function () {
|
||||
expect(result).to.equal(log)
|
||||
})
|
||||
})
|
||||
|
||||
context('countLogsByTests', () => {
|
||||
it('returns zero if tests is empty', () => {
|
||||
const tests = {}
|
||||
|
||||
expect(LogUtils.countLogsByTests(tests)).to.equal(0)
|
||||
})
|
||||
|
||||
it('finds the highest id amongst the different types', () => {
|
||||
const tests = {
|
||||
a: {
|
||||
agents: [{
|
||||
id: 'log-idp.com-1',
|
||||
}],
|
||||
routes: [{
|
||||
id: 'log-idp.com-2',
|
||||
}],
|
||||
commands: [{
|
||||
id: 'log-idp.com-3',
|
||||
}],
|
||||
prevAttempts: [{
|
||||
agents: [{
|
||||
id: 'log-idp.com-4',
|
||||
}],
|
||||
routes: [{
|
||||
id: 'log-idp.com-5',
|
||||
}],
|
||||
commands: [{
|
||||
id: 'log-idp.com-6',
|
||||
}],
|
||||
}],
|
||||
},
|
||||
}
|
||||
|
||||
expect(LogUtils.countLogsByTests(tests)).to.equal(6)
|
||||
})
|
||||
|
||||
it('returns zero if there are no agents, routes, or commands', () => {
|
||||
const tests = {
|
||||
a: {
|
||||
notAThing: true,
|
||||
},
|
||||
}
|
||||
|
||||
expect(LogUtils.countLogsByTests(tests)).to.equal(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -104,6 +104,8 @@ describe('Proxy Logging', () => {
|
||||
|
||||
// @see https://github.com/cypress-io/cypress/issues/18757 and https://github.com/cypress-io/cypress/issues/17656
|
||||
it('xhr log has response body/status code when xhr response is logged first', (done) => {
|
||||
cy.visit('/fixtures/empty.html')
|
||||
|
||||
cy.window()
|
||||
.then({ timeout: 10000 }, (win) => {
|
||||
cy.on('log:changed', (log) => {
|
||||
@@ -149,6 +151,8 @@ describe('Proxy Logging', () => {
|
||||
|
||||
// @see https://github.com/cypress-io/cypress/issues/18757 and https://github.com/cypress-io/cypress/issues/17656
|
||||
it('xhr log has response body/status code when xhr response is logged second', (done) => {
|
||||
cy.visit('/fixtures/empty.html')
|
||||
|
||||
cy.window()
|
||||
.then({ timeout: 10000 }, (win) => {
|
||||
cy.on('log:changed', (log) => {
|
||||
@@ -460,6 +464,7 @@ describe('Proxy Logging', () => {
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/empty.html')
|
||||
cy.window()
|
||||
.then(({ XMLHttpRequest }) => {
|
||||
$XMLHttpRequest = XMLHttpRequest
|
||||
|
||||
@@ -1,25 +1,16 @@
|
||||
import $coordinates from '../../../src/dom/coordinates'
|
||||
import $elements from '../../../src/dom/elements'
|
||||
|
||||
const { $ } = Cypress
|
||||
|
||||
export {}
|
||||
|
||||
describe('src/dom/coordinates', () => {
|
||||
let doc: Document
|
||||
|
||||
before(() => {
|
||||
return cy
|
||||
.visit('/fixtures/generic.html')
|
||||
.then(function (win) {
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
doc = cy.state('document')
|
||||
|
||||
$(doc.body).empty().html(this.body)
|
||||
|
||||
this.$button = $('<button style=\'position: absolute; top: 25px; left: 50px; width: 100px; line-height: 50px; padding: 10px; margin: 10px; border: 10px solid black\'>foo</button>')
|
||||
.appendTo(cy.$$('body'))
|
||||
cy.visit('/fixtures/generic.html').then(() => {
|
||||
this.$button = $('<button style=\'position: absolute; top: 25px; left: 50px; width: 100px; line-height: 50px; padding: 10px; margin: 10px; border: 10px solid black\'>foo</button>')
|
||||
.appendTo(cy.$$('body'))
|
||||
})
|
||||
})
|
||||
|
||||
// this is necessary so that document.elementFromPoint
|
||||
@@ -107,17 +98,17 @@ describe('src/dom/coordinates', () => {
|
||||
|
||||
context('.getElementAtPointFromViewport', () => {
|
||||
it('returns same element based on x/y coords', function () {
|
||||
expect(Cypress.dom.getElementAtPointFromViewport(doc, 100, 60)).to.eq(this.$button.get(0))
|
||||
expect(Cypress.dom.getElementAtPointFromViewport(cy.state('document'), 100, 60)).to.eq(this.$button.get(0))
|
||||
})
|
||||
|
||||
it('does not return if element is hidden', function () {
|
||||
this.$button.hide()
|
||||
|
||||
expect(Cypress.dom.getElementAtPointFromViewport(doc, 100, 60)).not.to.eq(this.$button.get(0))
|
||||
expect(Cypress.dom.getElementAtPointFromViewport(cy.state('document'), 100, 60)).not.to.eq(this.$button.get(0))
|
||||
})
|
||||
|
||||
it('returns null if no element was found', function () {
|
||||
expect(Cypress.dom.getElementAtPointFromViewport(doc, 1e9, 1e9)).to.be.null
|
||||
expect(Cypress.dom.getElementAtPointFromViewport(cy.state('document'), 1e9, 1e9)).to.be.null
|
||||
})
|
||||
})
|
||||
|
||||
@@ -233,6 +224,51 @@ describe('src/dom/coordinates', () => {
|
||||
})
|
||||
})
|
||||
|
||||
context('isAUTFrame', () => {
|
||||
const { isAUTFrame } = $coordinates
|
||||
|
||||
// our test for a window is that it has a `window` that refers
|
||||
// to itself
|
||||
const getWindowLikeObject = () => {
|
||||
const win = { parent: {} as any }
|
||||
|
||||
win.parent.window = win.parent
|
||||
|
||||
return win
|
||||
}
|
||||
|
||||
it('returns true if parent is a window and not an iframe', () => {
|
||||
const win = cy.state('window')
|
||||
|
||||
expect(isAUTFrame(win)).to.be.true
|
||||
})
|
||||
|
||||
it('returns true if parent is a window and getting its frameElement property throws a cross-origin error', () => {
|
||||
const win = getWindowLikeObject()
|
||||
const err = new Error('cross-origin error')
|
||||
|
||||
err.name = 'SecurityError'
|
||||
|
||||
cy.stub($elements, 'getNativeProp').throws(err)
|
||||
|
||||
expect(isAUTFrame(win)).to.be.true
|
||||
})
|
||||
|
||||
it('returns false if parent is not a window', () => {
|
||||
const win = { parent: {} }
|
||||
|
||||
expect(isAUTFrame(win)).to.be.false
|
||||
})
|
||||
|
||||
it('returns false if parent is an iframe', () => {
|
||||
const win = getWindowLikeObject()
|
||||
|
||||
cy.stub($elements, 'getNativeProp').returns(true)
|
||||
|
||||
expect(isAUTFrame(win)).to.be.false
|
||||
})
|
||||
})
|
||||
|
||||
context('span spanning multiple lines', () => {
|
||||
it('gets first dom rect in multiline text', () => {
|
||||
$(`\
|
||||
|
||||
@@ -135,38 +135,43 @@ describe('src/dom/elements', () => {
|
||||
})
|
||||
|
||||
it('DOM element does not exist', () => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
const $el = $('foo-bar-baz')
|
||||
cy.visit('/fixtures/dom.html').then(() => {
|
||||
const $el = $('foo-bar-baz')
|
||||
|
||||
expect(Cypress.dom.isUndefinedOrHTMLBodyDoc($el)).to.be.true
|
||||
expect(Cypress.dom.isUndefinedOrHTMLBodyDoc($el)).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
it('when html', () => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
const $el = $('html')
|
||||
cy.visit('/fixtures/dom.html').then(() => {
|
||||
const $el = $('html')
|
||||
|
||||
expect(Cypress.dom.isUndefinedOrHTMLBodyDoc($el)).to.be.true
|
||||
expect(Cypress.dom.isUndefinedOrHTMLBodyDoc($el)).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
it('when body', () => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
const $el = $('body')
|
||||
cy.visit('/fixtures/dom.html').then(() => {
|
||||
const $el = $('body')
|
||||
|
||||
expect(Cypress.dom.isUndefinedOrHTMLBodyDoc($el)).to.be.true
|
||||
expect(Cypress.dom.isUndefinedOrHTMLBodyDoc($el)).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
it('when document', () => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
const $el = $('document')
|
||||
cy.visit('/fixtures/dom.html').then(() => {
|
||||
const $el = $('document')
|
||||
|
||||
expect(Cypress.dom.isUndefinedOrHTMLBodyDoc($el)).to.be.true
|
||||
expect(Cypress.dom.isUndefinedOrHTMLBodyDoc($el)).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
it('when existing DOM element', () => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
const $el = $('input')
|
||||
cy.visit('/fixtures/dom.html').then(() => {
|
||||
const $el = $('input')
|
||||
|
||||
expect(Cypress.dom.isUndefinedOrHTMLBodyDoc($el)).to.be.false
|
||||
expect(Cypress.dom.isUndefinedOrHTMLBodyDoc($el)).to.be.false
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
describe('basic login', () => {
|
||||
// Scenario, Token based auth. Visit site, redirect to IDP hosted on secondary origin, login and redirect back to site.
|
||||
describe('visit primary first', () => {
|
||||
it('logs in with idp redirect', () => {
|
||||
cy.visit('/fixtures/auth/index.html') // Establishes primary origin
|
||||
cy.get('[data-cy="login-idp"]').click() // Takes you to idp.com
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('BJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
// Verify that the user has logged in on localhost
|
||||
cy.get('[data-cy="welcome"]') // Stability is false, this command is prevented from running until stability is achieved.
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome BJohnson')
|
||||
})
|
||||
|
||||
// Scenario, Token based auth. Visit site, manually redirect to IDP hosted on secondary origin, login and redirect back to site.
|
||||
it('does not redirect', () => {
|
||||
cy.visit('/fixtures/auth/index.html') // Establishes primary origin
|
||||
// Missing the call to go to idp.com
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://www.idp.com:3500/fixtures/auth/idp.html'
|
||||
})
|
||||
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('FJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
// Verify that the user has logged in on localhost
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome FJohnson')
|
||||
})
|
||||
|
||||
it('visits foobar first', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/auth/index.html') // Establishes primary origin
|
||||
cy.get('[data-cy="login-idp"]').click() // Takes you to idp.com
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('BJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
// Verify that the user has logged in on localhost
|
||||
cy.get('[data-cy="welcome"]') // Stability is false, this command is prevented from running until stability is achieved.
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome BJohnson')
|
||||
})
|
||||
})
|
||||
|
||||
// Scenario, Token based auth. Visit IDP hosted on secondary origin, login and redirect back to site.
|
||||
describe('visit secondary first', () => {
|
||||
describe('How to determine primary origin', () => {
|
||||
// NOTE: Enable to set the top origin to foobar before running the next test.
|
||||
it.skip('reset top', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/auth/index.html')
|
||||
})
|
||||
|
||||
// Primary established via base url
|
||||
// TODO: baseUrl does not establish primary without a visit
|
||||
it.skip('logs in with primary set via baseurl', { baseUrl: 'http://localhost:3500' }, () => {
|
||||
cy.origin('http://idp.com:3500', () => { // primary origin is localhost
|
||||
cy.visit('http://www.idp.com:3500/fixtures/auth/idp.html')
|
||||
cy.get('[data-cy="username"]').type('FJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome FJohnson')
|
||||
})
|
||||
|
||||
it('reset top', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/auth/index.html')
|
||||
})
|
||||
|
||||
it('logs in with primary set via visit', () => {
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.origin('http://idp.com:3500', () => { // primary origin is localhost
|
||||
cy.visit('http://www.idp.com:3500/fixtures/auth/idp.html')
|
||||
cy.get('[data-cy="username"]').type('FJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome FJohnson')
|
||||
})
|
||||
})
|
||||
|
||||
describe('session', () => {
|
||||
// Custom login command that establishes a session
|
||||
const login = (name) => {
|
||||
cy.session(name, () => {
|
||||
// Note, this assumes localhost is the primary origin, ideally we'd be able to specify this directly.
|
||||
cy.origin('http://idp.com:3500', { args: name }, (name) => {
|
||||
cy.visit('http://www.idp.com:3500/fixtures/auth/idp.html')
|
||||
cy.get('[data-cy="username"]').type(name)
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
cy.url().should('contain', '/index.html')
|
||||
}, {
|
||||
validate: () => {
|
||||
cy.window().then((win) => {
|
||||
const cypressAuthToken = win.sessionStorage.getItem('cypressAuthToken')
|
||||
|
||||
return !!cypressAuthToken
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Scenario, Token based auth. Establish session using custom login command (login through IDP hosted on secondary origin), and verify to site.
|
||||
it('establishes a session', () => {
|
||||
login('BJohnson')
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
// Verify that the user has logged in on localhost
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome BJohnson')
|
||||
})
|
||||
|
||||
// Scenario, Token based auth. use previously established session, and verify to site.
|
||||
it('uses established session', () => {
|
||||
login('BJohnson')
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome BJohnson')
|
||||
})
|
||||
})
|
||||
|
||||
// What we don't want them to do, but should still work
|
||||
// Visit IDP first
|
||||
it('logs in and runs the test in cy.origin', () => { // Setting the base url
|
||||
cy.visit('http://www.idp.com:3500/fixtures/auth/idp.html') // Visit idp.com
|
||||
cy.get('[data-cy="username"]').type('FJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
|
||||
cy.origin('http://localhost:3500', () => {
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome FJohnson')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Multi-step Auth', () => {
|
||||
// TODO: cy.origin does not work in cy.origin yet.
|
||||
it.skip('final auth redirects back to localhost - nested', () => {
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.get('[data-cy="login-with-approval"]').click() // takes you to foobar.com.../approval
|
||||
cy.url() //fail
|
||||
cy.origin('http://foobar.com:3500', () => { // Parent origin is localhost
|
||||
cy.get('[data-cy="approve-orig"]').click() // takes you to idp.com
|
||||
cy.origin('http://idp.com:3500', () => { // Parent origin is foobar.com
|
||||
cy.get('[data-cy="username"]').type('MarkyMark')
|
||||
cy.get('[data-cy="login"]').click() // Takes you back to localhost
|
||||
}) // Does not wait on foobar.com because there are no subsequent commands (would wait forever)
|
||||
}) // Waits on localhost because there are subsequent commands
|
||||
|
||||
// Verify that the user has logged in
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome MarkyMark')
|
||||
})
|
||||
|
||||
it('final-auth redirects back to localhost - flat', () => {
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.get('[data-cy="login-with-approval"]').click() // takes you to foobar.com.../approval
|
||||
cy.origin('http://foobar.com:3500', () => { // Parent origin is localhost
|
||||
cy.get('[data-cy="approve-orig"]').click() // takes you to idp.com
|
||||
}) // Exits and moves on to the next command
|
||||
|
||||
cy.origin('http://idp.com:3500', () => { // Parent origin is localhost
|
||||
cy.get('[data-cy="username"]').type('MarkyMark')
|
||||
cy.get('[data-cy="login"]').click() // Takes you back to localhost
|
||||
}) // Exits and moves on to the next command
|
||||
|
||||
// Verify that the user has logged in
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome MarkyMark')
|
||||
})
|
||||
|
||||
// TODO: cy.origin does not work in cy.origin yet.
|
||||
it.skip('final auth redirects back to localhost - nested - approval first', () => {
|
||||
cy.origin('http://foobar.com:3500', () => { // parent origin is localhost
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/auth/approval.html')
|
||||
cy.get('[data-cy="approve-orig"]').click() // takes you to idp.com
|
||||
cy.origin('http://idp.com:3500', () => { // parent origin is foobar.com
|
||||
cy.get('[data-cy="username"]').type('MarkyMark')
|
||||
cy.get('[data-cy="login"]').click() // Takes you back to localhost
|
||||
}) // Does not wait on foobar.com because there are no subsequent commands (would wait forever)
|
||||
}) // Exits and moves on to the next command
|
||||
|
||||
// Verify that the user has logged in
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome MarkyMark')
|
||||
})
|
||||
|
||||
// TODO: Switch to origin does not work in switch to origin yet.
|
||||
it.skip('final auth redirects back to approval page - nested', () => {
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.get('[data-cy="login-with-approval"]').click() // takes you to foobar.com.../approval
|
||||
cy.origin('http://foobar.com:3500', () => { // parent origin is localhost
|
||||
cy.get('[data-cy="approve-me"]').click() // takes you to idp.com
|
||||
cy.origin('http://idp.com:3500', () => { // parent origin is foobar.com
|
||||
cy.get('[data-cy="username"]').type('MarkyMark')
|
||||
cy.get('[data-cy="login"]').click() // Takes you back to foobar.com.../approval
|
||||
}) // Exits and moves on to the next command
|
||||
|
||||
cy.get('[data-cy="login-success"]').click() // Takes you back to localhost
|
||||
}) // Exits and moves on to the next command
|
||||
|
||||
// Verify that the user has logged in
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome MarkyMark')
|
||||
})
|
||||
|
||||
it('final auth redirects back to approval page - flat', () => {
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.get('[data-cy="login-with-approval"]').click() // takes you to foobar.com.../approval
|
||||
cy.origin('http://foobar.com:3500', () => { // parent origin is localhost
|
||||
cy.get('[data-cy="approve-me"]').click() // takes you to idp.com
|
||||
}) // waits on localhost forever, this breaks
|
||||
|
||||
cy.origin('http://idp.com:3500', () => { // parent origin is localhost
|
||||
cy.get('[data-cy="username"]').type('MarkyMark')
|
||||
cy.get('[data-cy="login"]').click() // Takes you back to foobar.com.../approval
|
||||
}) // Exits and moves on to the next command
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => { // parent origin is localhost
|
||||
cy.get('[data-cy="login-success"]').click() // Takes you back to localhost
|
||||
}) // Exits and moves on to the next command
|
||||
|
||||
// Verify that the user has logged in
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome MarkyMark')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,809 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin actions', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
})
|
||||
|
||||
it('.type()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#input').type('foo')
|
||||
.should('have.value', 'foo')
|
||||
})
|
||||
})
|
||||
|
||||
it('.focus()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#input').focus()
|
||||
.should('be.focused')
|
||||
})
|
||||
})
|
||||
|
||||
it('.blur()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#input').type('foo').blur()
|
||||
.should('not.be.focused')
|
||||
})
|
||||
})
|
||||
|
||||
it('.clear()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#input')
|
||||
.type('foo').should('have.value', 'foo')
|
||||
.clear().should('have.value', '')
|
||||
})
|
||||
})
|
||||
|
||||
it('.submit()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const afterFormSubmitted = new Promise<void>((resolve) => {
|
||||
cy.once('form:submitted', resolve)
|
||||
})
|
||||
|
||||
cy.get('#input-type-submit').submit()
|
||||
cy.wrap(afterFormSubmitted)
|
||||
})
|
||||
})
|
||||
|
||||
it('.click()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').then(($btn) => {
|
||||
const onClick = new Promise<void>((resolve) => {
|
||||
$btn.on('click', () => resolve())
|
||||
})
|
||||
|
||||
cy.wrap($btn).click()
|
||||
cy.wrap(onClick)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.dblclick()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').then(($btn) => {
|
||||
const afterDblClick = new Promise<void>((resolve) => {
|
||||
$btn.on('dblclick', () => resolve())
|
||||
})
|
||||
|
||||
cy.wrap($btn).dblclick()
|
||||
cy.wrap(afterDblClick)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.rightclick()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').then(($btn) => {
|
||||
const afterContextmenu = new Promise<void>((resolve) => {
|
||||
$btn.on('contextmenu', () => resolve())
|
||||
})
|
||||
|
||||
cy.wrap($btn).rightclick()
|
||||
cy.wrap(afterContextmenu)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.check()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get(':checkbox[name="colors"][value="blue"]')
|
||||
.check().should('be.checked')
|
||||
})
|
||||
})
|
||||
|
||||
it('.uncheck()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get(':checkbox[name="colors"][value="blue"]')
|
||||
.check().should('be.checked')
|
||||
.uncheck().should('not.be.checked')
|
||||
})
|
||||
})
|
||||
|
||||
it('.select()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('select[name="foods"]')
|
||||
.select('Japanese').should('have.value', 'Japanese')
|
||||
})
|
||||
})
|
||||
|
||||
it('.scrollIntoView()', () => {
|
||||
cy.get('a[data-cy="scrolling-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#scroll-into-view-vertical h5')
|
||||
.should('not.be.visible')
|
||||
.scrollIntoView().should('be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
it('.scrollTo()', () => {
|
||||
cy.get('a[data-cy="scrolling-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#scroll-into-view-vertical h5').should('not.be.visible')
|
||||
cy.get('#scroll-into-view-vertical').scrollTo(0, 300)
|
||||
cy.get('#scroll-into-view-vertical h5').should('be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
it('.trigger()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').then(($btn) => {
|
||||
const afterClick = new Promise<void>((resolve) => {
|
||||
$btn.on('click', () => resolve())
|
||||
})
|
||||
|
||||
cy.wrap($btn).trigger('click')
|
||||
cy.wrap(afterClick)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.selectFile()', () => {
|
||||
cy.get('a[data-cy="files-form-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.wrap(Cypress.Buffer.from('foo')).as('foo')
|
||||
|
||||
cy.get('#basic')
|
||||
.selectFile({ contents: '@foo', fileName: 'foo.txt' })
|
||||
.should(($input) => {
|
||||
const input = $input[0] as HTMLInputElement
|
||||
const file = input!.files![0]
|
||||
|
||||
expect(file.name).to.equal('foo.txt')
|
||||
|
||||
return file.arrayBuffer()
|
||||
.then((arrayBuffer) => {
|
||||
const decoder = new TextDecoder('utf8')
|
||||
const contents = decoder.decode(arrayBuffer)
|
||||
|
||||
expect(contents).to.equal('foo')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
const { _ } = Cypress
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.get()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps, $el } = findCrossOriginLogs('get', logs, 'foobar.com')
|
||||
|
||||
// make sure $el is in fact a jquery instance to keep the logs happy
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('get')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Selector).to.equal('#button')
|
||||
|
||||
// The Yielded value here SHOULD be correct as it will be reified from its props as it should not be found in the current DOM state
|
||||
expect(consoleProps.Yielded.tagName).to.equal('BUTTON')
|
||||
expect(consoleProps.Yielded.getAttribute('id')).to.equal('button')
|
||||
})
|
||||
})
|
||||
|
||||
it('.alias()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').as('buttonAlias')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { alias, aliasType, consoleProps, $el } = findCrossOriginLogs('get', logs, 'foobar.com')
|
||||
|
||||
// make sure $el is in fact a jquery instance to keep the logs happy
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(alias).to.equal('buttonAlias')
|
||||
expect(aliasType).to.equal('dom')
|
||||
expect(consoleProps.Command).to.equal('get')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Selector).to.equal('#button')
|
||||
|
||||
// The Yielded value here SHOULD be correct as it will be reified from its props as it should not be found in the current DOM state
|
||||
expect(consoleProps.Yielded.tagName).to.equal('BUTTON')
|
||||
expect(consoleProps.Yielded.getAttribute('id')).to.equal('button')
|
||||
})
|
||||
})
|
||||
|
||||
it('.click()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button-inside-a').click()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { alias, aliasType, consoleProps, $el } = findCrossOriginLogs('click', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(alias).to.equal(undefined)
|
||||
expect(aliasType).to.equal(undefined)
|
||||
|
||||
expect(consoleProps.Command).to.equal('click')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Coords).to.have.property('x').that.is.a('number')
|
||||
expect(consoleProps.Coords).to.have.property('y').that.is.a('number')
|
||||
|
||||
expect(consoleProps.Selector).to.be.undefined
|
||||
expect(consoleProps.Yielded).to.be.undefined
|
||||
expect(consoleProps.Options).to.be.undefined
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('FORM')
|
||||
// TODO: test class list serialization
|
||||
// expect(consoleProps['Applied To']).to.have.property('classList').that.contains(['my-custom-button-css', 'class2', '@class3'])
|
||||
expect(consoleProps['Applied To'].getAttribute('id')).to.contain('button-inside-a')
|
||||
expect(consoleProps['Applied To'].innerHTML).to.contain('<span>click button</span>')
|
||||
|
||||
expect(consoleProps.table[1]).to.be.a('function')
|
||||
|
||||
const tableContents = consoleProps.table[1]()
|
||||
|
||||
expect(tableContents.name).to.equal('Mouse Events')
|
||||
// can't exactly assert that is an Array because it is a Proxy object to an array
|
||||
expect(tableContents.data).to.be.a('object')
|
||||
|
||||
tableContents.data.forEach((datum) => {
|
||||
expect(datum).to.have.property('Active Modifiers').that.equals(null)
|
||||
expect(datum).to.have.property('Event Type').that.is.oneOf(['pointerover', 'mouseover', 'pointermove', 'mousemove', 'pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click'])
|
||||
expect(datum).to.have.property('Prevented Default').that.equals(null)
|
||||
expect(datum).to.have.property('Stopped Propagation').that.equals(null)
|
||||
expect(datum).to.have.property('Target Element').that.deep.equals(consoleProps['Applied To'])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.dblclick()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').dblclick()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps, $el } = findCrossOriginLogs('dblclick', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('dblclick')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Coords).to.have.property('x').that.is.a('number')
|
||||
expect(consoleProps.Coords).to.have.property('y').that.is.a('number')
|
||||
|
||||
expect(consoleProps.Selector).to.be.undefined
|
||||
expect(consoleProps.Yielded).to.be.undefined
|
||||
expect(consoleProps.Options).to.be.undefined
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('BUTTON')
|
||||
expect(consoleProps['Applied To'].getAttribute('id')).to.contain('button')
|
||||
expect(consoleProps['Applied To'].innerHTML).to.contain('button')
|
||||
|
||||
expect(consoleProps.table[1]).to.be.a('function')
|
||||
|
||||
const tableContents = consoleProps.table[1]()
|
||||
|
||||
expect(tableContents.name).to.equal('Mouse Events')
|
||||
// can't exactly assert that is an Array because it is a Proxy object to an array
|
||||
expect(tableContents.data).to.be.a('object')
|
||||
|
||||
tableContents.data.forEach((datum) => {
|
||||
expect(datum).to.have.property('Active Modifiers').that.equals(null)
|
||||
expect(datum).to.have.property('Event Type').that.is.oneOf(['pointerover', 'mouseover', 'pointermove', 'mousemove', 'pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click', 'dblclick'])
|
||||
expect(datum).to.have.property('Prevented Default').that.equals(null)
|
||||
expect(datum).to.have.property('Stopped Propagation').that.equals(null)
|
||||
expect(datum).to.have.property('Target Element').that.deep.equals(consoleProps['Applied To'])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.rightclick()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').rightclick()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps, $el } = findCrossOriginLogs('rightclick', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('rightclick')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Coords).to.have.property('x').that.is.a('number')
|
||||
expect(consoleProps.Coords).to.have.property('y').that.is.a('number')
|
||||
|
||||
expect(consoleProps.Selector).to.be.undefined
|
||||
expect(consoleProps.Yielded).to.be.undefined
|
||||
expect(consoleProps.Options).to.be.undefined
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('BUTTON')
|
||||
expect(consoleProps['Applied To'].getAttribute('id')).to.contain('button')
|
||||
expect(consoleProps['Applied To'].innerHTML).to.contain('button')
|
||||
|
||||
expect(consoleProps.table[1]).to.be.a('function')
|
||||
|
||||
const tableContents = consoleProps.table[1]()
|
||||
|
||||
expect(tableContents.name).to.equal('Mouse Events')
|
||||
// can't exactly assert that is an Array because it is a Proxy object to an array
|
||||
expect(tableContents.data).to.be.a('object')
|
||||
|
||||
tableContents.data.forEach((datum) => {
|
||||
expect(datum).to.have.property('Active Modifiers').that.equals(null)
|
||||
expect(datum).to.have.property('Event Type').that.is.oneOf(['pointerover', 'mouseover', 'pointermove', 'mousemove', 'pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click', 'contextmenu'])
|
||||
expect(datum).to.have.property('Prevented Default').that.equals(null)
|
||||
expect(datum).to.have.property('Stopped Propagation').that.equals(null)
|
||||
expect(datum).to.have.property('Target Element').that.deep.equals(consoleProps['Applied To'])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.type()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('input#input').type('foo')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { alias, aliasType, consoleProps, $el } = findCrossOriginLogs('type', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(alias).to.equal(undefined)
|
||||
expect(aliasType).to.equal(undefined)
|
||||
|
||||
expect(consoleProps.Command).to.equal('type')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Coords).to.have.property('x').that.is.a('number')
|
||||
expect(consoleProps.Coords).to.have.property('y').that.is.a('number')
|
||||
|
||||
expect(consoleProps.Typed).to.equal('foo')
|
||||
expect(consoleProps.Options).to.be.undefined
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'].getAttribute('id')).to.equal('input')
|
||||
expect(consoleProps['Applied To'].innerHTML).to.equals('')
|
||||
expect(consoleProps['Applied To'].type).to.equal('text')
|
||||
|
||||
expect(consoleProps.table[1]).to.be.a('function')
|
||||
expect(consoleProps.table[2]).to.be.a('function')
|
||||
|
||||
const mouseEventsTable = consoleProps.table[1]()
|
||||
const KeyboardEventsTable = consoleProps.table[2]()
|
||||
|
||||
expect(mouseEventsTable.name).to.equal('Mouse Events')
|
||||
expect(KeyboardEventsTable.name).to.equal('Keyboard Events')
|
||||
// can't exactly assert that is an Array because it is a Proxy object to an array
|
||||
expect(mouseEventsTable.data).to.be.a('object')
|
||||
expect(KeyboardEventsTable.data).to.be.a('object')
|
||||
|
||||
_.forEach(mouseEventsTable.data, (datum) => {
|
||||
expect(datum).to.have.property('Active Modifiers').that.equals(null)
|
||||
expect(datum).to.have.property('Event Type').that.is.oneOf(['pointerover', 'mouseover', 'pointermove', 'mousemove', 'pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click'])
|
||||
expect(datum).to.have.property('Prevented Default').that.equals(null)
|
||||
expect(datum).to.have.property('Stopped Propagation').that.equals(null)
|
||||
expect(datum).to.have.property('Target Element').that.deep.equals(consoleProps['Applied To'])
|
||||
})
|
||||
|
||||
_.forEach(KeyboardEventsTable.data, (datum) => {
|
||||
expect(datum).to.have.property('Active Modifiers').that.equals(null)
|
||||
expect(datum).to.have.property('Events Fired').that.equals('keydown, keypress, beforeinput, textInput, input, keyup')
|
||||
expect(datum).to.have.property('Prevented Default').that.equals(null)
|
||||
expect(datum).to.have.property('Target Element').that.deep.equals(consoleProps['Applied To'])
|
||||
})
|
||||
|
||||
expect(KeyboardEventsTable.data[1]).to.have.property('Details').that.equals('{ code: KeyF, which: 70 }')
|
||||
expect(KeyboardEventsTable.data[1]).to.have.property('Typed').that.equals('f')
|
||||
|
||||
expect(KeyboardEventsTable.data[2]).to.have.property('Details').that.equals('{ code: KeyO, which: 79 }')
|
||||
expect(KeyboardEventsTable.data[2]).to.have.property('Typed').that.equals('o')
|
||||
|
||||
expect(KeyboardEventsTable.data[3]).to.have.property('Details').that.equals('{ code: KeyO, which: 79 }')
|
||||
expect(KeyboardEventsTable.data[3]).to.have.property('Typed').that.equals('o')
|
||||
})
|
||||
})
|
||||
|
||||
it('.submit()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('form#multiple-inputs-and-input-submit input[name="fname"]').type('foo')
|
||||
cy.get('form#multiple-inputs-and-input-submit input[name="lname"]').type('bar')
|
||||
cy.get('form#multiple-inputs-and-input-submit').submit()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps, $el } = findCrossOriginLogs('submit', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('submit')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('FORM')
|
||||
expect(consoleProps['Applied To'].getAttribute('id')).to.equal('multiple-inputs-and-input-submit')
|
||||
expect(consoleProps['Applied To'].querySelector('input[name="fname"]')).to.be.ok
|
||||
expect(consoleProps['Applied To'].querySelector('input[name="lname"]')).to.be.ok
|
||||
expect(consoleProps['Applied To'].querySelector('input[type="submit"]')).to.be.ok
|
||||
|
||||
// make sure input values are passed along into the serialized snapshot/element
|
||||
expect(consoleProps['Applied To'].querySelector('input[name="fname"]').value).to.equal('foo')
|
||||
expect(consoleProps['Applied To'].querySelector('input[name="lname"]').value).to.equal('bar')
|
||||
expect(consoleProps['Applied To'].querySelector('input[type="submit"]').value).to.equal('submit me')
|
||||
})
|
||||
})
|
||||
|
||||
it('.focus()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#input').focus()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps, $el } = findCrossOriginLogs('focus', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('focus')
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'].getAttribute('id')).to.equal('input')
|
||||
})
|
||||
})
|
||||
|
||||
it('.blur()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
// FIXME: snapshot shows the primary domain (before type). Should be secondary
|
||||
cy.get('#input').type('foo').blur()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps, $el } = findCrossOriginLogs('blur', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('blur')
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'].getAttribute('id')).to.equal('input')
|
||||
})
|
||||
})
|
||||
|
||||
it('.clear()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
// FIXME: snapshot shows the primary domain. Should be secondary
|
||||
cy.get('#input').type('foo').clear()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps, $el } = findCrossOriginLogs('clear', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('clear')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Options).to.be.undefined
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'].getAttribute('id')).to.equal('input')
|
||||
})
|
||||
})
|
||||
|
||||
it('.check()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get(':checkbox[name="colors"][value="blue"]').check()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps, $el } = findCrossOriginLogs('check', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('check')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Coords).to.have.property('x').that.is.a('number')
|
||||
expect(consoleProps.Coords).to.have.property('y').that.is.a('number')
|
||||
|
||||
expect(consoleProps.Options).to.be.undefined
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('INPUT')
|
||||
|
||||
expect(consoleProps.table[1]).to.be.a('function')
|
||||
|
||||
const tableContents = consoleProps.table[1]()
|
||||
|
||||
expect(tableContents.name).to.equal('Mouse Events')
|
||||
// can't exactly assert that is an Array because it is a Proxy object to an array
|
||||
expect(tableContents.data).to.be.a('object')
|
||||
|
||||
tableContents.data.forEach((datum) => {
|
||||
expect(datum).to.have.property('Active Modifiers').that.equals(null)
|
||||
expect(datum).to.have.property('Event Type').that.is.oneOf(['pointerover', 'mouseover', 'pointermove', 'mousemove', 'pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click', 'contextmenu'])
|
||||
expect(datum).to.have.property('Prevented Default').that.equals(null)
|
||||
expect(datum).to.have.property('Stopped Propagation').that.equals(null)
|
||||
expect(datum).to.have.property('Target Element').that.deep.equals(consoleProps['Applied To'])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.uncheck()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get(':checkbox[name="colors"][value="blue"]')
|
||||
.check().uncheck()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps, $el } = findCrossOriginLogs('uncheck', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('uncheck')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Coords).to.have.property('x').that.is.a('number')
|
||||
expect(consoleProps.Coords).to.have.property('y').that.is.a('number')
|
||||
|
||||
expect(consoleProps.Options).to.be.undefined
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('INPUT')
|
||||
|
||||
expect(consoleProps.table[1]).to.be.a('function')
|
||||
|
||||
const tableContents = consoleProps.table[1]()
|
||||
|
||||
expect(tableContents.name).to.equal('Mouse Events')
|
||||
// can't exactly assert that is an Array because it is a Proxy object to an array
|
||||
expect(tableContents.data).to.be.a('object')
|
||||
|
||||
tableContents.data.forEach((datum) => {
|
||||
expect(datum).to.have.property('Active Modifiers').that.equals(null)
|
||||
expect(datum).to.have.property('Event Type').that.is.oneOf(['pointerover', 'mouseover', 'pointermove', 'mousemove', 'pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click', 'contextmenu'])
|
||||
expect(datum).to.have.property('Prevented Default').that.equals(null)
|
||||
expect(datum).to.have.property('Stopped Propagation').that.equals(null)
|
||||
expect(datum).to.have.property('Target Element').that.deep.equals(consoleProps['Applied To'])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.select()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
// TODO: wrong selected value is displayed in the snapshot after
|
||||
cy.get('select[name="foods"]').select('Japanese')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps, $el } = findCrossOriginLogs('select', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('select')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Coords).to.have.property('x').that.is.a('number')
|
||||
expect(consoleProps.Coords).to.have.property('y').that.is.a('number')
|
||||
|
||||
expect(consoleProps.Options).to.be.undefined
|
||||
expect(consoleProps.Selected[0]).to.equal('Japanese')
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('SELECT')
|
||||
|
||||
expect(consoleProps.table[1]).to.be.a('function')
|
||||
|
||||
const tableContents = consoleProps.table[1]()
|
||||
|
||||
expect(tableContents.name).to.equal('Mouse Events')
|
||||
// can't exactly assert that is an Array because it is a Proxy object to an array
|
||||
expect(tableContents.data).to.be.a('object')
|
||||
|
||||
tableContents.data.forEach((datum) => {
|
||||
expect(datum).to.have.property('Active Modifiers').that.equals(null)
|
||||
expect(datum).to.have.property('Event Type').that.is.oneOf(['pointerover', 'mouseover', 'pointermove', 'mousemove', 'pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click', 'contextmenu'])
|
||||
expect(datum).to.have.property('Prevented Default').that.equals(null)
|
||||
expect(datum).to.have.property('Stopped Propagation').that.equals(null)
|
||||
expect(datum).to.have.property('Target Element').that.deep.equals(consoleProps['Applied To'])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.scrollIntoView()', () => {
|
||||
cy.get('a[data-cy="scrolling-link"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
// FIXME: snapshot of primary is showing for scrollIntoView
|
||||
cy.get('#scroll-into-view-vertical h5')
|
||||
.should('not.be.visible')
|
||||
.scrollIntoView()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps, $el } = findCrossOriginLogs('scrollIntoView', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('scrollIntoView')
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('H5')
|
||||
expect(consoleProps['Scrolled Element']).to.have.property('tagName').that.equals('H5')
|
||||
})
|
||||
})
|
||||
|
||||
it('.scrollTo()', () => {
|
||||
cy.get('a[data-cy="scrolling-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#scroll-into-view-vertical h5').should('not.be.visible')
|
||||
cy.get('#scroll-into-view-vertical').scrollTo(0, 300)
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps, $el } = findCrossOriginLogs('scrollTo', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('scrollTo')
|
||||
expect(consoleProps.X).to.equal(0)
|
||||
expect(consoleProps.Y).to.equal(300)
|
||||
expect(consoleProps['Scrolled Element']).to.have.property('tagName').that.equals('DIV')
|
||||
})
|
||||
})
|
||||
|
||||
it('.trigger()', () => {
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').trigger('click')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps, $el } = findCrossOriginLogs('trigger', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('trigger')
|
||||
expect(consoleProps['Event options']).to.have.property('bubbles').that.is.a('boolean')
|
||||
expect(consoleProps['Event options']).to.have.property('cancelable').that.is.a('boolean')
|
||||
expect(consoleProps['Event options']).to.have.property('clientX').that.is.a('number')
|
||||
expect(consoleProps['Event options']).to.have.property('clientY').that.is.a('number')
|
||||
expect(consoleProps['Event options']).to.have.property('pageX').that.is.a('number')
|
||||
expect(consoleProps['Event options']).to.have.property('pageY').that.is.a('number')
|
||||
expect(consoleProps['Event options']).to.have.property('screenX').that.is.a('number')
|
||||
expect(consoleProps['Event options']).to.have.property('screenY').that.is.a('number')
|
||||
expect(consoleProps.Yielded[0]).to.have.property('tagName').that.equals('BUTTON')
|
||||
})
|
||||
})
|
||||
|
||||
it('.selectFile()', () => {
|
||||
cy.get('a[data-cy="files-form-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#basic').selectFile({ contents: Cypress.Buffer.from('foo'), fileName: 'foo.txt' })
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps, $el } = findCrossOriginLogs('selectFile', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('selectFile')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Target).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Target).to.have.property('id').that.equals('basic')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,50 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin aliasing', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
})
|
||||
|
||||
it('.as()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get(':checkbox[name="colors"][value="blue"]').as('checkbox')
|
||||
cy.get('@checkbox').click().should('be.checked')
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.as()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').as('buttonAlias')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { alias, aliasType, consoleProps, $el } = findCrossOriginLogs('get', logs, 'foobar.com')
|
||||
|
||||
// make sure $el is in fact a jquery instance to keep the logs happy
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(alias).to.equal('buttonAlias')
|
||||
expect(aliasType).to.equal('dom')
|
||||
expect(consoleProps.Command).to.equal('get')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Selector).to.equal('#button')
|
||||
|
||||
// The Yielded value here SHOULD be correct as it will be reified from its props as it should not be found in the current DOM state
|
||||
expect(consoleProps.Yielded.tagName).to.equal('BUTTON')
|
||||
expect(consoleProps.Yielded.getAttribute('id')).to.equal('button')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,56 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin assertions', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
})
|
||||
|
||||
it('.should() and .and()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get(':checkbox[name="colors"][value="blue"]')
|
||||
.should('not.be.checked').and('not.be.disabled')
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.should() and .and()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get(':checkbox[name="colors"][value="blue"]')
|
||||
.should('not.be.checked').and('not.be.disabled')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const assertionLogs = findCrossOriginLogs('assert', logs, 'foobar.com')
|
||||
|
||||
expect(assertionLogs[0].consoleProps.Message).to.equal('expected <input> not to be checked')
|
||||
expect(assertionLogs[1].consoleProps.Message).to.equal('expected <input> not to be disabled')
|
||||
|
||||
assertionLogs.forEach(({ $el, consoleProps }) => {
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('assert')
|
||||
expect(consoleProps.subject[0]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.subject[0]).to.have.property('value').that.equals('blue')
|
||||
expect(consoleProps.subject[0].getAttribute('name')).to.equal('colors')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,119 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin connectors', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
})
|
||||
|
||||
it('.each()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-name>[name="colors"]').each(($element, index) => {
|
||||
expect($element.prop('type')).to.equal('checkbox')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.its()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id>input').its('length').should('eq', 3)
|
||||
})
|
||||
})
|
||||
|
||||
it('.invoke()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').invoke('text').should('eq', 'button')
|
||||
})
|
||||
})
|
||||
|
||||
it('.spread()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const arr = ['foo', 'bar', 'baz']
|
||||
|
||||
cy.wrap(arr).spread((foo, bar, baz) => {
|
||||
expect(foo).to.equal('foo')
|
||||
expect(bar).to.equal('bar')
|
||||
expect(baz).to.equal('baz')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.then()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id>input').then(($list) => {
|
||||
expect($list).to.have.length(3)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.its()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id>input').its('length')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps, $el } = findCrossOriginLogs('its', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('its')
|
||||
expect(consoleProps.Property).to.equal('.length')
|
||||
expect(consoleProps.Yielded).to.equal(3)
|
||||
|
||||
expect(consoleProps.Subject.length).to.equal(3)
|
||||
|
||||
// make sure subject elements are indexed in the correct order
|
||||
expect(consoleProps.Subject[0]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Subject[0]).to.have.property('id').that.equals('input')
|
||||
|
||||
expect(consoleProps.Subject[1]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Subject[1]).to.have.property('id').that.equals('name')
|
||||
|
||||
expect(consoleProps.Subject[2]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Subject[2]).to.have.property('id').that.equals('age')
|
||||
})
|
||||
})
|
||||
|
||||
it('.invoke()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').invoke('text')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps, $el } = findCrossOriginLogs('invoke', logs, 'foobar.com')
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
|
||||
expect(consoleProps.Command).to.equal('invoke')
|
||||
expect(consoleProps.Function).to.equal('.text()')
|
||||
expect(consoleProps.Yielded).to.equal('button')
|
||||
|
||||
expect(consoleProps.Subject).to.have.property('tagName').that.equals('BUTTON')
|
||||
expect(consoleProps.Subject).to.have.property('id').that.equals('button')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,177 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin cookies', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
it('.getCookie(), .getCookies(), and .setCookie()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.getCookies().should('be.empty')
|
||||
|
||||
cy.setCookie('foo', 'bar')
|
||||
|
||||
cy.getCookie('foo').should('have.property', 'value', 'bar')
|
||||
cy.getCookies().should('have.length', 1)
|
||||
})
|
||||
})
|
||||
|
||||
it('.clearCookie()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.getCookie('foo').should('not.be.null')
|
||||
cy.clearCookie('foo')
|
||||
cy.getCookie('foo').should('be.null')
|
||||
})
|
||||
})
|
||||
|
||||
it('.clearCookies()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.setCookie('faz', 'baz')
|
||||
|
||||
cy.getCookies().should('have.length', 2)
|
||||
cy.clearCookies()
|
||||
cy.getCookies().should('be.empty')
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
const { _ } = Cypress
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.getCookie()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.getCookies().should('be.empty')
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.getCookie('foo')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('getCookie', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('getCookie')
|
||||
expect(consoleProps.Yielded).to.have.property('domain').that.includes('foobar.com')
|
||||
expect(consoleProps.Yielded).to.have.property('expiry').that.is.a('number')
|
||||
expect(consoleProps.Yielded).to.have.property('httpOnly').that.equals(false)
|
||||
expect(consoleProps.Yielded).to.have.property('secure').that.equals(false)
|
||||
expect(consoleProps.Yielded).to.have.property('name').that.equals('foo')
|
||||
expect(consoleProps.Yielded).to.have.property('value').that.equals('bar')
|
||||
expect(consoleProps.Yielded).to.have.property('path').that.is.a('string')
|
||||
})
|
||||
})
|
||||
|
||||
it('.getCookies()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.getCookies().should('be.empty')
|
||||
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.getCookies()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// get the last 'getCookies' command, which is the one we care about for this test
|
||||
const allGetCookieLogs = findCrossOriginLogs('getCookies', logs, 'foobar.com')
|
||||
|
||||
const { consoleProps } = allGetCookieLogs.pop() as any
|
||||
|
||||
expect(consoleProps.Command).to.equal('getCookies')
|
||||
expect(consoleProps['Num Cookies']).to.equal(1)
|
||||
|
||||
// can't exactly assert on length() as this is a array proxy object
|
||||
expect(consoleProps.Yielded.length).to.equal(1)
|
||||
expect(consoleProps.Yielded[0]).to.have.property('expiry').that.is.a('number')
|
||||
expect(consoleProps.Yielded[0]).to.have.property('httpOnly').that.equals(false)
|
||||
expect(consoleProps.Yielded[0]).to.have.property('secure').that.equals(false)
|
||||
expect(consoleProps.Yielded[0]).to.have.property('name').that.equals('foo')
|
||||
expect(consoleProps.Yielded[0]).to.have.property('value').that.equals('bar')
|
||||
expect(consoleProps.Yielded[0]).to.have.property('path').that.is.a('string')
|
||||
})
|
||||
})
|
||||
|
||||
it('.setCookie()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.getCookies().should('be.empty')
|
||||
|
||||
cy.setCookie('foo', 'bar')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('setCookie', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('setCookie')
|
||||
expect(consoleProps.Yielded).to.have.property('domain').that.includes('foobar.com')
|
||||
expect(consoleProps.Yielded).to.have.property('expiry').that.is.a('number')
|
||||
expect(consoleProps.Yielded).to.have.property('httpOnly').that.equals(false)
|
||||
expect(consoleProps.Yielded).to.have.property('secure').that.equals(false)
|
||||
expect(consoleProps.Yielded).to.have.property('name').that.equals('foo')
|
||||
expect(consoleProps.Yielded).to.have.property('value').that.equals('bar')
|
||||
expect(consoleProps.Yielded).to.have.property('path').that.is.a('string')
|
||||
})
|
||||
})
|
||||
|
||||
it('.clearCookie()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.getCookie('foo').should('not.be.null')
|
||||
cy.clearCookie('foo')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('clearCookie', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('clearCookie')
|
||||
expect(consoleProps.Yielded).to.equal('null')
|
||||
expect(consoleProps['Cleared Cookie']).to.have.property('domain').that.includes('foobar.com')
|
||||
expect(consoleProps['Cleared Cookie']).to.have.property('expiry').that.is.a('number')
|
||||
expect(consoleProps['Cleared Cookie']).to.have.property('httpOnly').that.equals(false)
|
||||
expect(consoleProps['Cleared Cookie']).to.have.property('secure').that.equals(false)
|
||||
expect(consoleProps['Cleared Cookie']).to.have.property('name').that.equals('foo')
|
||||
expect(consoleProps['Cleared Cookie']).to.have.property('value').that.equals('bar')
|
||||
expect(consoleProps['Cleared Cookie']).to.have.property('path').that.is.a('string')
|
||||
})
|
||||
})
|
||||
|
||||
it('.clearCookies()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.setCookie('faz', 'baz')
|
||||
|
||||
cy.getCookies().should('have.length', 2)
|
||||
cy.clearCookies()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('clearCookies', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('clearCookies')
|
||||
expect(consoleProps['Num Cookies']).to.equal(2)
|
||||
|
||||
expect(consoleProps.Yielded).to.equal('null')
|
||||
|
||||
expect(consoleProps['Cleared Cookies'].length).to.equal(2)
|
||||
|
||||
expect(consoleProps['Cleared Cookies'][0]).to.have.property('name').that.equals('foo')
|
||||
expect(consoleProps['Cleared Cookies'][0]).to.have.property('value').that.equals('bar')
|
||||
|
||||
expect(consoleProps['Cleared Cookies'][1]).to.have.property('name').that.equals('faz')
|
||||
expect(consoleProps['Cleared Cookies'][1]).to.have.property('value').that.equals('baz')
|
||||
|
||||
_.forEach(consoleProps['Cleared Cookies'], (clearedCookie) => {
|
||||
expect(clearedCookie).to.have.property('httpOnly').that.equals(false)
|
||||
expect(clearedCookie).to.have.property('secure').that.equals(false)
|
||||
expect(clearedCookie).to.have.property('path').that.is.a('string')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,96 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin files', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
it('.fixture()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.fixture('example.json').then((json) => {
|
||||
expect(json).to.be.an('object')
|
||||
expect(json.example).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.readFile()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.readFile('cypress/fixtures/example.json').then((json) => {
|
||||
expect(json).to.be.an('object')
|
||||
expect(json.example).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.writeFile()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const contents = JSON.stringify({ foo: 'bar' })
|
||||
|
||||
cy.stub(Cypress, 'backend').resolves({
|
||||
contents,
|
||||
filePath: 'foo.json',
|
||||
})
|
||||
|
||||
cy.writeFile('foo.json', contents).then(() => {
|
||||
expect(Cypress.backend).to.be.calledWith(
|
||||
'write:file',
|
||||
'foo.json',
|
||||
contents,
|
||||
{
|
||||
encoding: 'utf8',
|
||||
flag: 'w',
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.readFile()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.readFile('cypress/fixtures/example.json')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('readFile', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('readFile')
|
||||
expect(consoleProps['File Path']).to.include('cypress/fixtures/example.json')
|
||||
expect(consoleProps.Contents).to.deep.equal({ example: true })
|
||||
})
|
||||
})
|
||||
|
||||
it('.writeFile()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const contents = JSON.stringify({ foo: 'bar' })
|
||||
|
||||
cy.stub(Cypress, 'backend').resolves({
|
||||
contents,
|
||||
filePath: 'foo.json',
|
||||
})
|
||||
|
||||
cy.writeFile('foo.json', contents)
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('writeFile', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('writeFile')
|
||||
expect(consoleProps['File Path']).to.equal('foo.json')
|
||||
expect(consoleProps.Contents).to.equal('{"foo":"bar"}')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin local storage', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
it('.clearLocalStorage()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.window().then((win) => {
|
||||
win.localStorage.setItem('foo', 'bar')
|
||||
expect(win.localStorage.getItem('foo')).to.equal('bar')
|
||||
})
|
||||
|
||||
cy.clearLocalStorage().should((localStorage) => {
|
||||
expect(localStorage.length).to.equal(0)
|
||||
expect(localStorage.getItem('foo')).to.be.null
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.clearLocalStorage()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.window().then((win) => {
|
||||
win.localStorage.setItem('foo', 'bar')
|
||||
expect(win.localStorage.getItem('foo')).to.equal('bar')
|
||||
})
|
||||
|
||||
cy.clearLocalStorage()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('clearLocalStorage', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('clearLocalStorage')
|
||||
expect(consoleProps.Yielded).to.be.null
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,93 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin location', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
it('.hash()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.hash().should('be.empty')
|
||||
})
|
||||
})
|
||||
|
||||
it('.location()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.location().should((location) => {
|
||||
expect(location.href).to.equal('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
expect(location.origin).to.equal('http://www.foobar.com:3500')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.url()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.url().should('equal', 'http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.hash()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.hash()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('hash', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('hash')
|
||||
})
|
||||
})
|
||||
|
||||
it('.location()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.location()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('location', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('location')
|
||||
|
||||
expect(consoleProps.Yielded).to.have.property('auth').that.is.a('string')
|
||||
expect(consoleProps.Yielded).to.have.property('authObj').that.is.undefined
|
||||
expect(consoleProps.Yielded).to.have.property('hash').that.is.a('string')
|
||||
expect(consoleProps.Yielded).to.have.property('host').that.is.a('string')
|
||||
expect(consoleProps.Yielded).to.have.property('hostname').that.is.a('string')
|
||||
expect(consoleProps.Yielded).to.have.property('href').that.is.a('string')
|
||||
expect(consoleProps.Yielded).to.have.property('origin').that.is.a('string')
|
||||
expect(consoleProps.Yielded).to.have.property('originPolicy').that.is.a('string')
|
||||
expect(consoleProps.Yielded).to.have.property('pathname').that.is.a('string')
|
||||
expect(consoleProps.Yielded).to.have.property('port').that.is.a('string')
|
||||
expect(consoleProps.Yielded).to.have.property('protocol').that.is.a('string')
|
||||
expect(consoleProps.Yielded).to.have.property('search').that.is.a('string')
|
||||
expect(consoleProps.Yielded).to.have.property('superDomain').that.is.a('string')
|
||||
})
|
||||
})
|
||||
|
||||
it('.url()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.url()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('url', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('url')
|
||||
|
||||
expect(consoleProps.Yielded).to.equal('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,69 @@
|
||||
import { assertLogLength } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin log', () => {
|
||||
let logs: any = []
|
||||
let lastTestLogId = ''
|
||||
|
||||
beforeEach(() => {
|
||||
logs = []
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
logs.push(log)
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
})
|
||||
|
||||
it('logs in primary and secondary origins', () => {
|
||||
cy.origin<string>('http://foobar.com:3500', () => {
|
||||
const afterLogAdded = new Promise<void>((resolve) => {
|
||||
const listener = (attrs) => {
|
||||
if (attrs.message === 'test log in cy.origin') {
|
||||
expect(attrs.message).to.eq('test log in cy.origin')
|
||||
cy.removeListener('log:added', listener)
|
||||
resolve(attrs.id)
|
||||
}
|
||||
}
|
||||
|
||||
cy.on('log:added', listener)
|
||||
})
|
||||
|
||||
cy.log('test log in cy.origin')
|
||||
cy.wrap(afterLogAdded)
|
||||
}).then((id) => {
|
||||
lastTestLogId = id
|
||||
// Verify the log is also fired in the primary origin.
|
||||
expect(logs[6].get('message')).to.eq('test log in cy.origin')
|
||||
// Verify the log has the same ID as was generated in the cross-origin
|
||||
expect(logs[6].get('id')).to.equal(id)
|
||||
assertLogLength(logs, 11)
|
||||
})
|
||||
})
|
||||
|
||||
it('has a different id in a second test', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const afterLogAdded = new Promise<void>((resolve) => {
|
||||
const listener = (attrs) => {
|
||||
if (attrs.message === 'test log in cy.origin') {
|
||||
expect(attrs.message).to.eq('test log in cy.origin')
|
||||
cy.removeListener('log:added', listener)
|
||||
resolve(attrs.id)
|
||||
}
|
||||
}
|
||||
|
||||
cy.on('log:added', listener)
|
||||
})
|
||||
|
||||
cy.log('test log in cy.origin')
|
||||
cy.wrap(afterLogAdded)
|
||||
}).then((id) => {
|
||||
// Verify the log is also fired in the primary origin.
|
||||
expect(logs[6].get('message')).to.eq('test log in cy.origin')
|
||||
// Verify the log has the same ID as was generated in the cross-origin
|
||||
expect(logs[6].get('id')).to.equal(id)
|
||||
expect(logs[6].get('id')).to.not.equal(lastTestLogId)
|
||||
assertLogLength(logs, 12)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,229 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin misc', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
})
|
||||
|
||||
it('.end()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').end().should('be.null')
|
||||
})
|
||||
})
|
||||
|
||||
it('.exec()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.exec('echo foobar').its('stdout').should('contain', 'foobar')
|
||||
})
|
||||
})
|
||||
|
||||
it('.focused()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').click().focused().should('have.id', 'button')
|
||||
})
|
||||
})
|
||||
|
||||
it('.wrap()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.wrap({ foo: 'bar' }).should('deep.equal', { foo: 'bar' })
|
||||
})
|
||||
})
|
||||
|
||||
it('.debug()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').debug().should('have.id', 'button')
|
||||
})
|
||||
})
|
||||
|
||||
it('.pause()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const afterPaused = new Promise<void>((resolve) => {
|
||||
cy.once('paused', () => {
|
||||
Cypress.emit('resume:all')
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
cy.pause().wrap({}).should('deep.eq', {})
|
||||
// pause is a noop in run mode, so only wait for it if in open mode
|
||||
if (Cypress.config('isInteractive')) {
|
||||
cy.wrap(afterPaused)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('.task()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.task('return:arg', 'works').should('eq', 'works')
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.exec()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.exec('echo foobar')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('exec', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('exec')
|
||||
expect(consoleProps['Shell Used']).to.be.undefined
|
||||
expect(consoleProps.Yielded).to.have.property('code').that.equals(0)
|
||||
expect(consoleProps.Yielded).to.have.property('stderr').that.equals('')
|
||||
expect(consoleProps.Yielded).to.have.property('stdout').that.equals('foobar')
|
||||
})
|
||||
})
|
||||
|
||||
it('.focused()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').click().focused()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('focused', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('focused')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('BUTTON')
|
||||
expect(consoleProps.Yielded).to.have.property('id').that.equals('button')
|
||||
})
|
||||
})
|
||||
|
||||
it('.wrap()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const arr = ['foo', 'bar', 'baz']
|
||||
|
||||
cy.wrap(arr).spread((foo, bar, baz) => {
|
||||
expect(foo).to.equal('foo')
|
||||
expect(bar).to.equal('bar')
|
||||
expect(baz).to.equal('baz')
|
||||
})
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('wrap', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('wrap')
|
||||
expect(consoleProps.Yielded[0]).to.equal('foo')
|
||||
expect(consoleProps.Yielded[1]).to.equal('bar')
|
||||
expect(consoleProps.Yielded[2]).to.equal('baz')
|
||||
})
|
||||
})
|
||||
|
||||
it('.debug()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#button').debug()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('debug', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('debug')
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('BUTTON')
|
||||
expect(consoleProps.Yielded).to.have.property('id').that.equals('button')
|
||||
})
|
||||
})
|
||||
|
||||
it('.pause()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const afterPaused = new Promise<void>((resolve) => {
|
||||
cy.once('paused', () => {
|
||||
Cypress.emit('resume:all')
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
cy.pause().wrap({}).should('deep.eq', {})
|
||||
// pause is a noop in run mode, so only wait for it if in open mode
|
||||
if (Cypress.config('isInteractive')) {
|
||||
cy.wrap(afterPaused)
|
||||
}
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
if (Cypress.config('isInteractive')) {
|
||||
// if `isInteractive`, the .pause() will NOT show up in the command log in this case. Essentially a no-op.
|
||||
return
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('pause', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('pause')
|
||||
expect(consoleProps.Yielded).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
it('.task()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.task('return:arg', 'works')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('task', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('task')
|
||||
expect(consoleProps.Yielded).to.equal('works')
|
||||
expect(consoleProps.arg).to.equal('works')
|
||||
expect(consoleProps.task).to.equal('return:arg')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('verifies number of cy commands', () => {
|
||||
// @ts-ignore
|
||||
// remove 'getAll' and 'shouldWithTimeout' commands since they are custom commands we added for our own testing and are not actual cy commands
|
||||
const actualCommands = Cypress._.reject(Object.keys(cy.commandFns), (command) => command === 'getAll' || command === 'shouldWithTimeout')
|
||||
const expectedCommands = [
|
||||
'check', 'uncheck', 'click', 'dblclick', 'rightclick', 'focus', 'blur', 'hover', 'scrollIntoView', 'scrollTo', 'select',
|
||||
'selectFile', 'submit', 'type', 'clear', 'trigger', 'as', 'ng', 'should', 'and', 'clock', 'tick', 'spread', 'each', 'then',
|
||||
'invoke', 'its', 'getCookie', 'getCookies', 'setCookie', 'clearCookie', 'clearCookies', 'pause', 'debug', 'exec', 'readFile',
|
||||
'writeFile', 'fixture', 'clearLocalStorage', 'url', 'hash', 'location', 'end', 'noop', 'log', 'wrap', 'reload', 'go', 'visit',
|
||||
'focused', 'get', 'contains', 'root', 'shadow', 'within', 'request', 'session', 'screenshot', 'task', 'find', 'filter', 'not',
|
||||
'children', 'eq', 'closest', 'first', 'last', 'next', 'nextAll', 'nextUntil', 'parent', 'parents', 'parentsUntil', 'prev',
|
||||
'prevAll', 'prevUntil', 'siblings', 'wait', 'title', 'window', 'document', 'viewport', 'server', 'route', 'intercept', 'origin',
|
||||
]
|
||||
const addedCommands = Cypress._.difference(actualCommands, expectedCommands)
|
||||
const removedCommands = Cypress._.difference(expectedCommands, actualCommands)
|
||||
|
||||
if (addedCommands.length && removedCommands.length) {
|
||||
throw new Error(`Commands have been added to and removed from Cypress.
|
||||
|
||||
The following command(s) were added: ${addedCommands.join(', ')}
|
||||
The following command(s) were removed: ${removedCommands.join(', ')}
|
||||
|
||||
Update this test accordingly.`)
|
||||
}
|
||||
|
||||
if (addedCommands.length) {
|
||||
throw new Error(`The following command(s) have been added to Cypress: ${addedCommands.join(', ')}. Please add tests for the command(s) in cy.origin and add the command(s) to this test.`)
|
||||
}
|
||||
|
||||
if (removedCommands.length) {
|
||||
throw new Error(`The following command(s) have been removed from Cypress: ${removedCommands.join(', ')}. Please remove the command(s) from this test.`)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,574 @@
|
||||
const { stripIndent } = require('common-tags')
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin navigation', () => {
|
||||
it('.go()', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/dom.html')
|
||||
|
||||
cy.go('back')
|
||||
cy.location('pathname').should('include', 'multi-domain-secondary.html')
|
||||
|
||||
cy.go('forward')
|
||||
cy.location('pathname').should('include', 'dom.html')
|
||||
})
|
||||
})
|
||||
|
||||
it('.reload()', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get(':checkbox[name="colors"][value="blue"]').check().should('be.checked')
|
||||
cy.reload()
|
||||
cy.get(':checkbox[name="colors"][value="blue"]').should('not.be.checked')
|
||||
})
|
||||
})
|
||||
|
||||
context('.visit()', () => {
|
||||
it('calls the correct load handlers', () => {
|
||||
const primaryCyBeforeLoadSpy = cy.spy()
|
||||
const primaryCyLoadSpy = cy.spy()
|
||||
const primaryVisitBeforeLoadSpy = cy.spy()
|
||||
const primaryVisitLoadSpy = cy.spy()
|
||||
|
||||
cy.on('window:before:load', primaryCyBeforeLoadSpy)
|
||||
cy.on('window:load', primaryCyLoadSpy)
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html', {
|
||||
onBeforeLoad: primaryVisitBeforeLoadSpy,
|
||||
onLoad: primaryVisitLoadSpy,
|
||||
}).then(() => {
|
||||
expect(primaryCyBeforeLoadSpy).to.be.calledOnce
|
||||
expect(primaryCyLoadSpy).to.be.calledTwice // twice because it's also called for 'about:blank'
|
||||
expect(primaryVisitBeforeLoadSpy).to.be.calledOnce
|
||||
expect(primaryVisitLoadSpy).to.be.calledOnce
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const secondaryCyBeforeLoadSpy = cy.spy()
|
||||
const secondaryCyLoadSpy = cy.spy()
|
||||
const secondaryVisitBeforeLoadSpy = cy.spy()
|
||||
const secondaryVisitLoadSpy = cy.spy()
|
||||
|
||||
cy.on('window:before:load', secondaryCyBeforeLoadSpy)
|
||||
cy.on('window:load', secondaryCyLoadSpy)
|
||||
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/dom.html', {
|
||||
onBeforeLoad: secondaryVisitBeforeLoadSpy,
|
||||
onLoad: secondaryVisitLoadSpy,
|
||||
}).then(() => {
|
||||
expect(secondaryCyBeforeLoadSpy).to.be.calledOnce
|
||||
expect(secondaryCyLoadSpy).to.be.calledOnce
|
||||
expect(secondaryVisitBeforeLoadSpy).to.be.calledOnce
|
||||
expect(secondaryVisitLoadSpy).to.be.calledOnce
|
||||
})
|
||||
}).then(() => {
|
||||
expect(primaryCyBeforeLoadSpy).to.be.calledOnce
|
||||
expect(primaryCyLoadSpy).to.be.calledTwice
|
||||
expect(primaryVisitBeforeLoadSpy).to.be.calledOnce
|
||||
expect(primaryVisitLoadSpy).to.be.calledOnce
|
||||
})
|
||||
})
|
||||
|
||||
it('supports visiting primary first', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
|
||||
cy.get('[data-cy="dom-check"]').should('have.text', 'From a secondary origin')
|
||||
})
|
||||
})
|
||||
|
||||
it('supports skipping visiting primary first', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
|
||||
cy.get('[data-cy="dom-check"]').should('have.text', 'From a secondary origin')
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: we don't support nested cy.origin yet...
|
||||
it.skip('supports nesting a third origin', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
|
||||
cy.get('[data-cy="dom-check"]').should('have.text', 'From a secondary origin')
|
||||
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.visit('http://www.idp.com:3500/fixtures/dom.html')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('supports navigating to secondary through button and then visiting', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="dom-check"]').should('have.text', 'From a secondary origin')
|
||||
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/dom.html')
|
||||
cy.location('href').should('equal', 'http://www.foobar.com:3500/fixtures/dom.html')
|
||||
})
|
||||
})
|
||||
|
||||
it('supports relative urls within secondary', () => {
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
cy.location('href').should('equal', 'http://www.foobar.com:3500/fixtures/dom.html')
|
||||
})
|
||||
})
|
||||
|
||||
it('supports relative urls with path within secondary', () => {
|
||||
cy.origin('http://www.foobar.com:3500/fixtures', () => {
|
||||
cy.visit('/dom.html')
|
||||
cy.location('href').should('equal', 'http://www.foobar.com:3500/fixtures/dom.html')
|
||||
})
|
||||
})
|
||||
|
||||
it('supports relative urls with hash within secondary', () => {
|
||||
cy.origin('http://www.foobar.com:3500/#hash', () => {
|
||||
cy.visit('/more-hash')
|
||||
cy.location('href').should('equal', 'http://www.foobar.com:3500/#hash/more-hash')
|
||||
})
|
||||
})
|
||||
|
||||
it('supports relative urls with path and hash within secondary', () => {
|
||||
cy.origin('http://www.foobar.com:3500/welcome/#hash', () => {
|
||||
cy.visit('/more-hash')
|
||||
cy.location('href').should('equal', 'http://www.foobar.com:3500/welcome/#hash/more-hash')
|
||||
})
|
||||
})
|
||||
|
||||
it('supports hash change within secondary', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html#hashchange')
|
||||
|
||||
cy.location('hash').should('equal', '#hashchange')
|
||||
})
|
||||
})
|
||||
|
||||
it('navigates back to primary', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
cy.get('[data-cy="dom-check"]').should('have.text', 'From a secondary origin')
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').should('have.text', 'http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
cy.location('href').should('equal', 'http://localhost:3500/fixtures/multi-domain.html')
|
||||
})
|
||||
|
||||
it('errors when visiting a new origin within 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.origin('http://foobar.com:3500', () => {\`
|
||||
\` cy.visit('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')\`
|
||||
\` <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')\`
|
||||
\` <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
|
||||
You may only \`cy.visit()\` same-origin URLs within \`cy.origin()\`.\n
|
||||
The previous URL you visited was:\n
|
||||
> 'http://www.foobar.com:3500'\n
|
||||
You're attempting to visit this URL:\n
|
||||
> 'http://www.idp.com:3500'`)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
// this call should error since we can't visit a cross-origin
|
||||
cy.visit('http://www.idp.com:3500/fixtures/dom.html')
|
||||
})
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
it('informs user to use cy.origin with experimental flag off', { experimentalSessionAndOrigin: false }, (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
|
||||
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')\`
|
||||
\`<commands targeting http://localhost:3500 go here>\`\n
|
||||
\`cy.origin('http://foobar.com:3500', () => {\`
|
||||
\` cy.visit('http://www.foobar.com:3500/fixtures/dom.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')
|
||||
|
||||
// this call should error since we can't visit a cross-origin
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/dom.html')
|
||||
})
|
||||
|
||||
it('supports the query string option', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html', { qs: { foo: 'bar' } })
|
||||
|
||||
cy.location('search').should('equal', '?foo=bar')
|
||||
})
|
||||
})
|
||||
|
||||
it('can send a POST request', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/post-only', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
bar: 'baz',
|
||||
}),
|
||||
})
|
||||
|
||||
cy.contains('it worked!').contains('{"bar":"baz"}')
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds when the AUT window in the secondary is undefined', () => {
|
||||
// manually remove the spec bridge iframe to ensure Cypress.state('window') is not already set
|
||||
window.top?.document.getElementById('Spec\ Bridge:\ foobar.com')?.remove()
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
|
||||
cy.get('[data-cy="dom-check"]').should('have.text', 'From a secondary origin')
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds when the secondary is already defined but the AUT is still on the primary', () => {
|
||||
// setup the secondary to be on the secondary origin
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
|
||||
cy.get('[data-cy="dom-check"]').should('have.text', 'From a secondary origin')
|
||||
})
|
||||
|
||||
// update the AUT to be on the primary origin
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
|
||||
// verify there aren't any issues when the AUT is on primary but the spec bridge is on secondary (cross-origin)
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
|
||||
cy.get('[data-cy="dom-check"]').should('have.text', 'From a secondary origin')
|
||||
})
|
||||
})
|
||||
|
||||
it('does not navigate to about:blank in secondary', () => {
|
||||
const primaryCyLoadSpy = cy.spy()
|
||||
|
||||
cy.on('window:load', primaryCyLoadSpy)
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const secondaryCyLoadSpy = cy.spy()
|
||||
|
||||
cy.on('window:load', secondaryCyLoadSpy)
|
||||
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html').then(() => {
|
||||
expect(secondaryCyLoadSpy).to.have.been.calledOnce
|
||||
expect(secondaryCyLoadSpy.args[0][0].location.href).to.equal('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
})
|
||||
})
|
||||
|
||||
cy.then(() => {
|
||||
expect(primaryCyLoadSpy).to.not.have.been.called
|
||||
})
|
||||
})
|
||||
|
||||
it('supports redirecting from primary to secondary in cy.origin', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://localhost:3500/redirect?href=http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
cy.get('[data-cy="dom-check"]').should('have.text', 'From a secondary origin')
|
||||
})
|
||||
})
|
||||
|
||||
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
|
||||
\`<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', {
|
||||
auth: {
|
||||
username: 'cypress',
|
||||
password: 'password123',
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('body').should('have.text', 'basic auth worked')
|
||||
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://www.foobar.com:3500/basic_auth'
|
||||
})
|
||||
|
||||
cy.get('body').should('have.text', 'basic auth worked')
|
||||
})
|
||||
|
||||
// attaches the auth options for the foobar origin even from another origin
|
||||
cy.origin('http://www.idp.com:3500', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://www.foobar.com:3500/basic_auth'
|
||||
})
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('body').should('have.text', 'basic auth worked')
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
|
||||
// attaches the auth options for the foobar origin from the top-level
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://www.foobar.com:3500/basic_auth'
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('body').should('have.text', 'basic auth worked')
|
||||
})
|
||||
})
|
||||
|
||||
it('does not propagate the auth options across tests', (done) => {
|
||||
cy.intercept('/basic_auth', (req) => {
|
||||
req.on('response', (res) => {
|
||||
// clear the www-authenticate header so the browser doesn't prompt for username/password
|
||||
res.headers['www-authenticate'] = ''
|
||||
expect(res.statusCode).to.equal(401)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://www.foobar.com:3500/fixtures/multi-domain.html'
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://www.foobar.com:3500/basic_auth'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds when visiting local file server first', { baseUrl: undefined }, () => {
|
||||
cy.visit('cypress/fixtures/multi-domain.html')
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('/fixtures/multi-domain-secondary.html')
|
||||
cy.get('[data-cy="dom-check"]').should('have.text', 'From a secondary origin')
|
||||
})
|
||||
})
|
||||
|
||||
it('handles visit failures', { baseUrl: undefined }, (done) => {
|
||||
cy.on('fail', (e) => {
|
||||
expect(e.message).to.include('failed trying to load:\n\nhttp://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
expect(e.message).to.include('500: Internal Server Error')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.intercept('*/multi-domain-secondary.html', { statusCode: 500 })
|
||||
|
||||
cy.visit('cypress/fixtures/multi-domain.html')
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('fixtures/multi-domain-secondary.html')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('supports navigating through changing the window.location.href', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://www.foobar.com:3500/fixtures/dom.html'
|
||||
})
|
||||
|
||||
cy.location('pathname').should('equal', '/fixtures/dom.html')
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.go()', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/dom.html')
|
||||
|
||||
cy.go('back')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps, ...attrs } = findCrossOriginLogs('go', logs, 'foobar.com')
|
||||
|
||||
expect(attrs.name).to.equal('go')
|
||||
expect(attrs.message).to.equal('back')
|
||||
|
||||
expect(consoleProps.Command).to.equal('go')
|
||||
expect(consoleProps.Yielded).to.be.null
|
||||
})
|
||||
})
|
||||
|
||||
it('.reload()', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.reload()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps, ...attrs } = findCrossOriginLogs('reload', logs, 'foobar.com')
|
||||
|
||||
expect(attrs.name).to.equal('reload')
|
||||
expect(attrs.message).to.equal('')
|
||||
|
||||
expect(consoleProps.Command).to.equal('reload')
|
||||
expect(consoleProps.Yielded).to.be.null
|
||||
})
|
||||
})
|
||||
|
||||
it('visit()', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
|
||||
cy.get('[data-cy="dom-check"]').should('have.text', 'From a secondary origin')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps, ...attrs } = findCrossOriginLogs('visit', logs, 'foobar.com')
|
||||
|
||||
expect(attrs.name).to.equal('visit')
|
||||
expect(attrs.message).to.equal('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
|
||||
expect(consoleProps.Command).to.equal('visit')
|
||||
expect(consoleProps).to.have.property('Cookies Set').that.is.an('object')
|
||||
expect(consoleProps).to.have.property('Redirects').that.is.an('object')
|
||||
expect(consoleProps).to.have.property('Resolved Url').that.equals('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin network requests', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="request-link"]').click()
|
||||
})
|
||||
|
||||
it('.request() to secondary origin', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.request('http://www.foobar.com:3500/fixtures/example.json').should((response) => {
|
||||
expect(response.status).to.equal(200)
|
||||
expect(response.allRequestResponses[0]['Request URL']).to.equal('http://www.foobar.com:3500/fixtures/example.json')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.request() to secondary origin with relative path', () => {
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.request('/fixtures/example.json').should((response) => {
|
||||
expect(response.status).to.equal(200)
|
||||
expect(response.allRequestResponses[0]['Request URL']).to.equal('http://www.foobar.com:3500/fixtures/example.json')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.request() to primary origin', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.request('http://localhost:3500/fixtures/example.json').should((response) => {
|
||||
expect(response.status).to.equal(200)
|
||||
expect(response.allRequestResponses[0]['Request URL']).to.equal('http://localhost:3500/fixtures/example.json')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.request()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.request('http://www.foobar.com:3500/fixtures/example.json')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps, renderProps } = findCrossOriginLogs('request', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('request')
|
||||
|
||||
expect(consoleProps.Request).to.have.property('Request Body').that.equals(null)
|
||||
expect(consoleProps.Request).to.have.property('Request Headers').that.is.a('object')
|
||||
expect(consoleProps.Request).to.have.property('Request URL').that.equals('http://www.foobar.com:3500/fixtures/example.json')
|
||||
expect(consoleProps.Request).to.have.property('Response Body').that.is.a('string')
|
||||
expect(consoleProps.Request).to.have.property('Response Headers').that.is.a('object')
|
||||
expect(consoleProps.Request).to.have.property('Response Status').that.equals(200)
|
||||
|
||||
expect(consoleProps.Yielded).to.have.property('body').that.deep.equals({ example: true })
|
||||
expect(consoleProps.Yielded).to.have.property('duration').that.is.a('number')
|
||||
expect(consoleProps.Yielded).to.have.property('headers').that.is.a('object')
|
||||
expect(consoleProps.Yielded).to.have.property('status').that.equals(200)
|
||||
|
||||
expect(renderProps).to.have.property('indicator').that.equals('successful')
|
||||
expect(renderProps).to.have.property('message').that.equals('GET 200 http://www.foobar.com:3500/fixtures/example.json')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,104 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin querying', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
})
|
||||
|
||||
it('.get()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#input')
|
||||
})
|
||||
})
|
||||
|
||||
it('.contains()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.contains('Nested Find')
|
||||
})
|
||||
})
|
||||
|
||||
it('.within()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id').within(() => {
|
||||
cy.get('#input')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.root()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.root().should('match', 'html')
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.contains()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.contains('Nested Find')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('contains', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('contains')
|
||||
expect(consoleProps['Applied To']).to.be.undefined
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Content).to.equal('Nested Find')
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('DIV')
|
||||
expect(consoleProps.Yielded).to.have.property('id').that.equals('nested-find')
|
||||
})
|
||||
})
|
||||
|
||||
it('.within()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id').within(() => {
|
||||
cy.get('#input')
|
||||
})
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('within', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('within')
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('FORM')
|
||||
expect(consoleProps.Yielded).to.have.property('id').that.equals('by-id')
|
||||
})
|
||||
})
|
||||
|
||||
it('.root()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.root()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('root', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('root')
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('HTML')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin shadow dom', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="shadow-dom-link"]').click()
|
||||
})
|
||||
|
||||
it('.shadow()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#shadow-element-1').shadow().find('p.shadow-1')
|
||||
.should('have.text', 'Shadow Content 1')
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.shadow()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#shadow-element-1').shadow()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('shadow', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('shadow')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('CY-TEST-ELEMENT')
|
||||
expect(consoleProps['Applied To']).to.have.property('id').that.equals('shadow-element-1')
|
||||
|
||||
expect(consoleProps.Yielded).to.be.null
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,321 @@
|
||||
context('cy.origin screenshot', () => {
|
||||
context('set viewport', () => {
|
||||
beforeEach(() => {
|
||||
this.serverResult = {
|
||||
path: '/path/to/screenshot',
|
||||
size: 12,
|
||||
dimensions: { width: 20, height: 20 },
|
||||
multipart: false,
|
||||
pixelRatio: 1,
|
||||
takenAt: new Date().toISOString(),
|
||||
name: 'name',
|
||||
blackout: [],
|
||||
testAttemptIndex: 0,
|
||||
duration: 100,
|
||||
}
|
||||
|
||||
cy.viewport(600, 200)
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="screenshots-link"]').click()
|
||||
})
|
||||
|
||||
it('captures the fullPage', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
const automationStub = cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
|
||||
cy.screenshot({ capture: 'fullPage' })
|
||||
.then(() => {
|
||||
expect(automationStub).to.be.calledThrice
|
||||
expect(automationStub.args[0][1].capture).to.equal('fullPage')
|
||||
expect(automationStub.args[1][1].capture).to.equal('fullPage')
|
||||
expect(automationStub.args[2][1].capture).to.equal('fullPage')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('captures the runner', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
const automationStub = cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
|
||||
cy.screenshot({ capture: 'runner' })
|
||||
.then(() => {
|
||||
expect(automationStub).to.be.calledOnce
|
||||
expect(automationStub.args[0][1].capture).to.equal('runner')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('captures the viewport', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
const automationStub = cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
|
||||
cy.screenshot({ capture: 'viewport' })
|
||||
.then(() => {
|
||||
expect(automationStub).to.be.calledOnce
|
||||
expect(automationStub.args[0][1].capture).to.equal('viewport')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('without setting viewport', () => {
|
||||
beforeEach(() => {
|
||||
this.serverResult = {
|
||||
path: '/path/to/screenshot',
|
||||
size: 12,
|
||||
dimensions: { width: 20, height: 20 },
|
||||
multipart: false,
|
||||
pixelRatio: 1,
|
||||
takenAt: new Date().toISOString(),
|
||||
name: 'name',
|
||||
blackout: [],
|
||||
testAttemptIndex: 0,
|
||||
duration: 100,
|
||||
}
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="screenshots-link"]').click()
|
||||
})
|
||||
|
||||
it('supports multiple titles', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
const automationStub = cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
|
||||
cy.screenshot()
|
||||
.then(() => {
|
||||
expect(automationStub.args[0][1].titles).to.deep.equal(['cy.origin screenshot', 'without setting viewport', 'supports multiple titles'])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('supports the blackout option', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
|
||||
cy.screenshot({
|
||||
blackout: ['.short-element'],
|
||||
onBeforeScreenshot: ($el) => {
|
||||
const $blackoutElement = $el.find('.__cypress-blackout')
|
||||
const $shortElement = $el.find('.short-element')
|
||||
|
||||
expect($blackoutElement.outerHeight()).to.equal($shortElement.outerHeight())
|
||||
expect($blackoutElement.outerWidth()).to.equal($shortElement.outerWidth())
|
||||
expect($blackoutElement.offset()).to.deep.equal($shortElement.offset())
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('supports element screenshots', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
const automationStub = cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
|
||||
cy.get('.tall-element').screenshot()
|
||||
.then(() => {
|
||||
expect(automationStub.args[0][1].clip.x).to.be.greaterThan(0)
|
||||
expect(automationStub.args[0][1].clip.y).to.be.greaterThan(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('supports screenshot retrying with appropriate naming', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
const automationStub = cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
|
||||
cy.state('runnable')._currentRetry = 2
|
||||
|
||||
cy.screenshot()
|
||||
.then(() => {
|
||||
expect(automationStub.args[0][1].testAttemptIndex).to.equal(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('calls the onBeforeScreenshot callback', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
const onBeforeScreenshot = cy.stub()
|
||||
|
||||
cy.screenshot({ onBeforeScreenshot })
|
||||
cy.wrap(onBeforeScreenshot).should('be.called')
|
||||
})
|
||||
})
|
||||
|
||||
it('calls the onAfterScreenshot callback', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
const onAfterScreenshot = cy.stub()
|
||||
|
||||
cy.screenshot({ onAfterScreenshot })
|
||||
cy.wrap(onAfterScreenshot).should('be.called')
|
||||
})
|
||||
})
|
||||
|
||||
it('supports the Cypress.screenshot callbacks', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').resolves(serverResult)
|
||||
const onAfterScreenshot = cy.stub()
|
||||
const onBeforeScreenshot = cy.stub()
|
||||
|
||||
Cypress.Screenshot.defaults({
|
||||
onBeforeScreenshot,
|
||||
onAfterScreenshot,
|
||||
})
|
||||
|
||||
cy.screenshot()
|
||||
cy.wrap(onBeforeScreenshot).should('be.called')
|
||||
cy.wrap(onAfterScreenshot).should('be.called')
|
||||
})
|
||||
})
|
||||
|
||||
it('supports pausing timers', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').returns(Cypress.Promise.delay(500, serverResult))
|
||||
|
||||
cy.window().then((win) => {
|
||||
// Hide the element using setTimeout
|
||||
win.setTimeout(() => {
|
||||
(win.document.getElementsByClassName('tall-element')[0] as HTMLElement).style.display = 'none'
|
||||
}, 50)
|
||||
})
|
||||
|
||||
cy.screenshot({
|
||||
onBeforeScreenshot: ($el) => {
|
||||
// Set the timeout to be longer than the element hiding timeout so if the timer was
|
||||
// not paused, it would've hidden the element but since we are pausing the
|
||||
// timers, the style is still 'block'
|
||||
setTimeout(() => {
|
||||
expect($el.find('.tall-element').css('display')).to.equal('block')
|
||||
}, 100)
|
||||
},
|
||||
onAfterScreenshot: ($el) => {
|
||||
// Set the timeout to be longer than the element hiding timeout so when the timers
|
||||
// are unpaused, this will run after the timeout to hide the element
|
||||
setTimeout(() => {
|
||||
expect($el.find('.tall-element').css('display')).to.equal('none')
|
||||
}, 100)
|
||||
},
|
||||
})
|
||||
|
||||
cy.wait(200)
|
||||
})
|
||||
})
|
||||
|
||||
it('does not pause timers when disableTimersAndAnimations is false', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').returns(Cypress.Promise.delay(500, serverResult))
|
||||
|
||||
cy.window().then((win) => {
|
||||
// Hide the element using setTimeout
|
||||
win.setTimeout(() => {
|
||||
(win.document.getElementsByClassName('tall-element')[0] as HTMLElement).style.display = 'none'
|
||||
}, 50)
|
||||
})
|
||||
|
||||
cy.screenshot({
|
||||
disableTimersAndAnimations: false,
|
||||
onBeforeScreenshot: ($el) => {
|
||||
// Set the timeout to be longer than the element hiding timeout so it has time to run and hide the element
|
||||
setTimeout(() => {
|
||||
expect($el.find('.tall-element').css('display')).to.equal('none')
|
||||
}, 100)
|
||||
},
|
||||
})
|
||||
|
||||
cy.wait(200)
|
||||
})
|
||||
})
|
||||
|
||||
it('handles errors thrown from setTimeout after the timer is paused', () => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.eq('Error')
|
||||
expect(err.message).to.include('setTimeout error after screenshot')
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').returns(Cypress.Promise.delay(100, serverResult))
|
||||
|
||||
cy.window().then((win) => {
|
||||
// Add a timeout error
|
||||
win.setTimeout(() => {
|
||||
throw new Error('setTimeout error after screenshot')
|
||||
}, 50)
|
||||
})
|
||||
|
||||
cy.screenshot()
|
||||
|
||||
// wait to ensure the timeout error has time to process
|
||||
cy.wait(100)
|
||||
})
|
||||
})
|
||||
|
||||
it('handles errors thrown from setTimeout when the timer is NOT paused', () => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.eq('Error')
|
||||
expect(err.message).to.include('setTimeout error during screenshot')
|
||||
expect(err.message).to.include('The following error originated from your application code, not from Cypress.')
|
||||
expect(err.docsUrl).to.deep.eq(['https://on.cypress.io/uncaught-exception-from-application'])
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', { args: this.serverResult }, (serverResult) => {
|
||||
cy.stub(Cypress, 'automation').withArgs('take:screenshot').returns(Cypress.Promise.delay(100, serverResult))
|
||||
|
||||
cy.window().then((win) => {
|
||||
// Add a timeout error
|
||||
win.setTimeout(() => {
|
||||
throw new Error('setTimeout error during screenshot')
|
||||
}, 50)
|
||||
})
|
||||
|
||||
cy.screenshot({ disableTimersAndAnimations: false })
|
||||
|
||||
// wait to ensure the timeout error has time to process
|
||||
cy.wait(100)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
const { findCrossOriginLogs } = require('../../../../support/utils')
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="screenshots-link"]').click()
|
||||
})
|
||||
|
||||
it('.screenshot()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.screenshot({ capture: 'fullPage' })
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('screenshot', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('screenshot')
|
||||
|
||||
expect(consoleProps).to.have.property('blackout')
|
||||
expect(consoleProps).to.have.property('capture').that.equals('fullPage')
|
||||
expect(consoleProps).to.have.property('dimensions').that.is.a('string')
|
||||
expect(consoleProps).to.have.property('disableTimersAndAnimations').that.is.a('boolean')
|
||||
expect(consoleProps).to.have.property('duration').that.is.a('string')
|
||||
expect(consoleProps).to.have.property('multipart').that.is.a('boolean')
|
||||
expect(consoleProps).to.have.property('name').to.be.null
|
||||
expect(consoleProps).to.have.property('path').that.is.a('string')
|
||||
expect(consoleProps).to.have.property('pixelRatio').that.is.a('number')
|
||||
expect(consoleProps).to.have.property('scaled').that.is.a('boolean')
|
||||
expect(consoleProps).to.have.property('size').that.is.a('string')
|
||||
expect(consoleProps).to.have.property('specName').that.is.a('string')
|
||||
expect(consoleProps).to.have.property('takenAt').that.is.a('string')
|
||||
expect(consoleProps).to.have.property('testAttemptIndex').that.is.a('number')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin spies, stubs, and clock', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
it('spy()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const foo = { bar () { } }
|
||||
|
||||
cy.spy(foo, 'bar')
|
||||
foo.bar()
|
||||
expect(foo.bar).to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
it('stub()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const foo = { bar () { } }
|
||||
|
||||
cy.stub(foo, 'bar')
|
||||
foo.bar()
|
||||
expect(foo.bar).to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
context('resets stubs', () => {
|
||||
it('creates the stub', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const stubEnv = cy.stub(Cypress, 'env').withArgs('foo').returns('bar')
|
||||
|
||||
expect(Cypress.env('foo')).to.equal('bar')
|
||||
expect(stubEnv).to.be.calledOnce
|
||||
// @ts-ignore
|
||||
expect(Cypress.env.isSinonProxy).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
it('verifies the stub got restored', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
expect(Cypress.env('foo')).to.be.undefined
|
||||
// @ts-ignore
|
||||
expect(Cypress.env.isSinonProxy).to.be.undefined
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('resets spies', () => {
|
||||
it('creates the spy', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const stubEnv = cy.spy(Cypress, 'env')
|
||||
|
||||
Cypress.env()
|
||||
expect(stubEnv).to.be.calledOnce
|
||||
// @ts-ignore
|
||||
expect(Cypress.env.isSinonProxy).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
it('verifies the spy got restored', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
// @ts-ignore
|
||||
expect(Cypress.env.isSinonProxy).to.be.undefined
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('clock() and tick()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const now = Date.UTC(2022, 0, 12)
|
||||
|
||||
cy.clock(now)
|
||||
cy.window().then((win) => {
|
||||
expect(win.Date.now()).to.equal(now)
|
||||
})
|
||||
|
||||
cy.tick(10000) // 10 seconds passed
|
||||
cy.window().then((win) => {
|
||||
expect(win.Date.now()).to.equal(now + 10000)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
const { _ } = Cypress
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
// cy.clock only adds a log and does NOT update
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('spy()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const foo = { bar () { } }
|
||||
|
||||
cy.spy(foo, 'bar')
|
||||
foo.bar()
|
||||
expect(foo.bar).to.be.called
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const spyLog = findCrossOriginLogs('spy-1', logs, 'foobar.com')
|
||||
|
||||
expect(spyLog.consoleProps.Command).to.equal('spy-1')
|
||||
expect(spyLog.callCount).to.be.a('number')
|
||||
expect(spyLog.functionName).to.equal('bar')
|
||||
})
|
||||
})
|
||||
|
||||
it('.stub()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const foo = { bar () { } }
|
||||
|
||||
cy.stub(foo, 'bar')
|
||||
foo.bar()
|
||||
expect(foo.bar).to.be.called
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const stubLog = findCrossOriginLogs('stub-1', logs, 'foobar.com')
|
||||
|
||||
expect(stubLog.consoleProps.Command).to.equal('stub-1')
|
||||
expect(stubLog.callCount).to.be.a('number')
|
||||
expect(stubLog.functionName).to.equal('bar')
|
||||
})
|
||||
})
|
||||
|
||||
it('.clock()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const now = Date.UTC(2022, 0, 12)
|
||||
|
||||
cy.clock(now)
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const clockLog = findCrossOriginLogs('clock', logs, 'foobar.com')
|
||||
|
||||
expect(clockLog.name).to.equal('clock')
|
||||
|
||||
const consoleProps = clockLog.consoleProps()
|
||||
|
||||
expect(consoleProps.Command).to.equal('clock')
|
||||
expect(consoleProps).to.have.property('Methods replaced').that.is.a('object')
|
||||
expect(consoleProps).to.have.property('Now').that.is.a('number')
|
||||
})
|
||||
})
|
||||
|
||||
it('.tick()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const now = Date.UTC(2022, 0, 12)
|
||||
|
||||
cy.clock(now)
|
||||
|
||||
cy.tick(10000)
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const tickLog = findCrossOriginLogs('tick', logs, 'foobar.com')
|
||||
|
||||
expect(tickLog.name).to.equal('tick')
|
||||
|
||||
const consoleProps = _.isFunction(tickLog.consoleProps) ? tickLog.consoleProps() : tickLog.consoleProps
|
||||
|
||||
expect(consoleProps.Command).to.equal('tick')
|
||||
expect(consoleProps).to.have.property('Methods replaced').that.is.a('object')
|
||||
expect(consoleProps).to.have.property('Now').that.is.a('number')
|
||||
expect(consoleProps).to.have.property('Ticked').that.is.a('string')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,644 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin traversal', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
})
|
||||
|
||||
it('.children()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id').children().should('have.length', 3)
|
||||
})
|
||||
})
|
||||
|
||||
it('.closest()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id').closest('form')
|
||||
})
|
||||
})
|
||||
|
||||
it('.eq()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id>input').eq(1).should('have.id', 'name')
|
||||
})
|
||||
})
|
||||
|
||||
it('.filter()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-name>input')
|
||||
.filter('[name="dogs"]').should('have.length', 4)
|
||||
})
|
||||
})
|
||||
|
||||
it('.find()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id').find('input').should('have.length', 3)
|
||||
})
|
||||
})
|
||||
|
||||
it('.first()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id>input').first().should('have.id', 'input')
|
||||
})
|
||||
})
|
||||
|
||||
it('.last()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id>input').last().should('have.id', 'age')
|
||||
})
|
||||
})
|
||||
|
||||
it('.next()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#input').next().should('have.id', 'name')
|
||||
})
|
||||
})
|
||||
|
||||
it('.nextAll()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#input').nextAll().should('have.length', 2)
|
||||
})
|
||||
})
|
||||
|
||||
it('.nextUntil()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#input').nextUntil('#age').should('have.length', 1)
|
||||
})
|
||||
})
|
||||
|
||||
it('.not()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id>input').not('#age').should('have.length', 2)
|
||||
})
|
||||
})
|
||||
|
||||
it('.parent()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id').parent().should('have.id', 'dom')
|
||||
})
|
||||
})
|
||||
|
||||
it('.parents()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id').parents().should('have.length', 3)
|
||||
})
|
||||
})
|
||||
|
||||
it('.parentsUntil()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id').parentsUntil('body').should('have.length', 1)
|
||||
})
|
||||
})
|
||||
|
||||
it('.prev()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#age').prev().should('have.id', 'name')
|
||||
})
|
||||
})
|
||||
|
||||
it('.prevAll()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#age').prevAll().should('have.length', 2)
|
||||
})
|
||||
})
|
||||
|
||||
it('.prevUntil()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#age').prevUntil('#input').should('have.length', 1)
|
||||
})
|
||||
})
|
||||
|
||||
it('.siblings()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#input').siblings().should('have.length', 2)
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
const { _ } = Cypress
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.children()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id').children()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('children', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('FORM')
|
||||
expect(consoleProps['Applied To']).to.have.property('id').that.equals('by-id')
|
||||
expect(consoleProps.Command).to.equal('children')
|
||||
expect(consoleProps.Elements).to.equal(3)
|
||||
expect(consoleProps.Selector).to.equal('')
|
||||
expect(consoleProps.Yielded.length).to.equal(3)
|
||||
expect(consoleProps.Yielded[0]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded[0]).to.have.property('id').that.equals('input')
|
||||
expect(consoleProps.Yielded[1]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded[1]).to.have.property('id').that.equals('name')
|
||||
expect(consoleProps.Yielded[2]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded[2]).to.have.property('id').that.equals('age')
|
||||
})
|
||||
})
|
||||
|
||||
it('.closest()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id').closest('form')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('closest', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('FORM')
|
||||
expect(consoleProps['Applied To']).to.have.property('id').that.equals('by-id')
|
||||
expect(consoleProps.Command).to.equal('closest')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Selector).to.equal('form')
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('FORM')
|
||||
expect(consoleProps.Yielded).to.have.property('id').that.equals('by-id')
|
||||
expect(consoleProps.Yielded.querySelector('input#input')).to.be.ok
|
||||
expect(consoleProps.Yielded.querySelector('input#name')).to.be.ok
|
||||
expect(consoleProps.Yielded.querySelector('input#age')).to.be.ok
|
||||
})
|
||||
})
|
||||
|
||||
it('.eq()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id>input').eq(1)
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('eq', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To'].length).to.equal(3)
|
||||
expect(consoleProps['Applied To'][0]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'][0]).to.have.property('id').that.equals('input')
|
||||
expect(consoleProps['Applied To'][1]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'][1]).to.have.property('id').that.equals('name')
|
||||
expect(consoleProps['Applied To'][2]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'][2]).to.have.property('id').that.equals('age')
|
||||
|
||||
expect(consoleProps.Command).to.equal('eq')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Selector).to.equal('1')
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded).to.have.property('id').that.equals('name')
|
||||
})
|
||||
})
|
||||
|
||||
it('.filter()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-name>input')
|
||||
.filter('[name="dogs"]')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('filter', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To'].length).to.equal(12)
|
||||
expect(consoleProps.Command).to.equal('filter')
|
||||
expect(consoleProps.Elements).to.equal(4)
|
||||
expect(consoleProps.Selector).to.equal('[name="dogs"]')
|
||||
|
||||
expect(consoleProps.Yielded.length).to.equal(4)
|
||||
|
||||
_.forEach(consoleProps.Yielded, (yielded) => {
|
||||
expect(yielded).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(yielded).to.have.property('name').that.equals('dogs')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('.find()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id').find('input')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('find', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('FORM')
|
||||
expect(consoleProps['Applied To']).to.have.property('id').that.equals('by-id')
|
||||
|
||||
expect(consoleProps.Command).to.equal('find')
|
||||
expect(consoleProps.Elements).to.equal(3)
|
||||
expect(consoleProps.Selector).to.equal('input')
|
||||
|
||||
expect(consoleProps.Yielded.length).to.equal(3)
|
||||
expect(consoleProps.Yielded[0]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded[0]).to.have.property('id').that.equals('input')
|
||||
expect(consoleProps.Yielded[1]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded[1]).to.have.property('id').that.equals('name')
|
||||
expect(consoleProps.Yielded[2]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded[2]).to.have.property('id').that.equals('age')
|
||||
})
|
||||
})
|
||||
|
||||
it('.first()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id>input').first()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('first', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To'].length).to.equal(3)
|
||||
expect(consoleProps['Applied To'][0]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'][0]).to.have.property('id').that.equals('input')
|
||||
expect(consoleProps['Applied To'][1]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'][1]).to.have.property('id').that.equals('name')
|
||||
expect(consoleProps['Applied To'][2]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'][2]).to.have.property('id').that.equals('age')
|
||||
|
||||
expect(consoleProps.Command).to.equal('first')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Selector).to.equal('')
|
||||
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded).to.have.property('id').that.equals('input')
|
||||
})
|
||||
})
|
||||
|
||||
it('.last()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id>input').last()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('last', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To'].length).to.equal(3)
|
||||
expect(consoleProps['Applied To'][0]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'][0]).to.have.property('id').that.equals('input')
|
||||
expect(consoleProps['Applied To'][1]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'][1]).to.have.property('id').that.equals('name')
|
||||
expect(consoleProps['Applied To'][2]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'][2]).to.have.property('id').that.equals('age')
|
||||
|
||||
expect(consoleProps.Command).to.equal('last')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Selector).to.equal('')
|
||||
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded).to.have.property('id').that.equals('age')
|
||||
})
|
||||
})
|
||||
|
||||
it('.next()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#input').next()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('next', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To']).to.have.property('id').that.equals('input')
|
||||
|
||||
expect(consoleProps.Command).to.equal('next')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Selector).to.equal('')
|
||||
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded).to.have.property('id').that.equals('name')
|
||||
})
|
||||
})
|
||||
|
||||
it('.nextAll()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#input').nextAll()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('nextAll', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To']).to.have.property('id').that.equals('input')
|
||||
|
||||
expect(consoleProps.Command).to.equal('nextAll')
|
||||
expect(consoleProps.Elements).to.equal(2)
|
||||
expect(consoleProps.Selector).to.equal('')
|
||||
|
||||
expect(consoleProps.Yielded.length).to.equal(2)
|
||||
expect(consoleProps.Yielded[0]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded[0]).to.have.property('id').that.equals('name')
|
||||
expect(consoleProps.Yielded[1]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded[1]).to.have.property('id').that.equals('age')
|
||||
})
|
||||
})
|
||||
|
||||
it('.nextUntil()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#input').nextUntil('#age')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('nextUntil', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To']).to.have.property('id').that.equals('input')
|
||||
|
||||
expect(consoleProps.Command).to.equal('nextUntil')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Selector).to.equal('#age')
|
||||
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded).to.have.property('id').that.equals('name')
|
||||
})
|
||||
})
|
||||
|
||||
it('.not()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id>input').not('#age')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('not', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To'].length).to.equal(3)
|
||||
expect(consoleProps['Applied To'][0]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'][0]).to.have.property('id').that.equals('input')
|
||||
expect(consoleProps['Applied To'][1]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'][1]).to.have.property('id').that.equals('name')
|
||||
expect(consoleProps['Applied To'][2]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To'][2]).to.have.property('id').that.equals('age')
|
||||
|
||||
expect(consoleProps.Command).to.equal('not')
|
||||
expect(consoleProps.Elements).to.equal(2)
|
||||
expect(consoleProps.Selector).to.equal('#age')
|
||||
|
||||
expect(consoleProps.Yielded.length).to.equal(2)
|
||||
expect(consoleProps.Yielded[0]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded[0]).to.have.property('id').that.equals('input')
|
||||
expect(consoleProps.Yielded[1]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded[1]).to.have.property('id').that.equals('name')
|
||||
})
|
||||
})
|
||||
|
||||
it('.parent()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id').parent()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('parent', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('FORM')
|
||||
expect(consoleProps['Applied To']).to.have.property('id').that.equals('by-id')
|
||||
|
||||
expect(consoleProps.Command).to.equal('parent')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Selector).to.equal('')
|
||||
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('DIV')
|
||||
expect(consoleProps.Yielded).to.have.property('id').that.equals('dom')
|
||||
})
|
||||
})
|
||||
|
||||
it('.parents()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id').parents()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('parents', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('FORM')
|
||||
expect(consoleProps['Applied To']).to.have.property('id').that.equals('by-id')
|
||||
|
||||
expect(consoleProps.Command).to.equal('parents')
|
||||
expect(consoleProps.Elements).to.equal(3)
|
||||
expect(consoleProps.Selector).to.equal('')
|
||||
|
||||
expect(consoleProps.Yielded.length).to.equal(3)
|
||||
expect(consoleProps.Yielded[0]).to.have.property('tagName').that.equals('DIV')
|
||||
expect(consoleProps.Yielded[0]).to.have.property('id').that.equals('dom')
|
||||
expect(consoleProps.Yielded[1]).to.have.property('tagName').that.equals('BODY')
|
||||
expect(consoleProps.Yielded[2]).to.have.property('tagName').that.equals('HTML')
|
||||
})
|
||||
})
|
||||
|
||||
it('.parentsUntil()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#by-id').parentsUntil('body')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('parentsUntil', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('FORM')
|
||||
expect(consoleProps['Applied To']).to.have.property('id').that.equals('by-id')
|
||||
|
||||
expect(consoleProps.Command).to.equal('parentsUntil')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Selector).to.equal('body')
|
||||
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('DIV')
|
||||
expect(consoleProps.Yielded).to.have.property('id').that.equals('dom')
|
||||
})
|
||||
})
|
||||
|
||||
it('.prev()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#age').prev()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('prev', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To']).to.have.property('id').that.equals('age')
|
||||
|
||||
expect(consoleProps.Command).to.equal('prev')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Selector).to.equal('')
|
||||
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded).to.have.property('id').that.equals('name')
|
||||
})
|
||||
})
|
||||
|
||||
it('.prevAll()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#age').prevAll()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('prevAll', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To']).to.have.property('id').that.equals('age')
|
||||
|
||||
expect(consoleProps.Command).to.equal('prevAll')
|
||||
expect(consoleProps.Elements).to.equal(2)
|
||||
expect(consoleProps.Selector).to.equal('')
|
||||
|
||||
expect(consoleProps.Yielded.length).to.equal(2)
|
||||
expect(consoleProps.Yielded[0]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded[0]).to.have.property('id').that.equals('name')
|
||||
expect(consoleProps.Yielded[1]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded[1]).to.have.property('id').that.equals('input')
|
||||
})
|
||||
})
|
||||
|
||||
it('.prevUntil()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#age').prevUntil('#input')
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('prevUntil', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To']).to.have.property('id').that.equals('age')
|
||||
|
||||
expect(consoleProps.Command).to.equal('prevUntil')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Selector).to.equal('#input')
|
||||
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded).to.have.property('id').that.equals('name')
|
||||
})
|
||||
})
|
||||
|
||||
it('.siblings()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#input').siblings()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
// in the case of some firefox browsers, the document state is left in a cross origin context when running these assertions
|
||||
// set to context to undefined to run the assertions
|
||||
if (Cypress.isBrowser('firefox')) {
|
||||
cy.state('document', undefined)
|
||||
}
|
||||
|
||||
const { consoleProps } = findCrossOriginLogs('siblings', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps['Applied To']).to.have.property('id').that.equals('input')
|
||||
|
||||
expect(consoleProps.Command).to.equal('siblings')
|
||||
expect(consoleProps.Elements).to.equal(2)
|
||||
expect(consoleProps.Selector).to.equal('')
|
||||
|
||||
expect(consoleProps.Yielded.length).to.equal(2)
|
||||
expect(consoleProps.Yielded[0]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded[0]).to.have.property('id').that.equals('name')
|
||||
expect(consoleProps.Yielded[1]).to.have.property('tagName').that.equals('INPUT')
|
||||
expect(consoleProps.Yielded[1]).to.have.property('id').that.equals('age')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
context('cy.origin unsupported commands', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
it('cy.route() method is deprecated', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`cy.route()` has been deprecated and its use is not supported in the `cy.origin()` callback. Consider using `cy.intercept()` (outside of the callback) instead.')
|
||||
expect(err.docsUrl).to.equal('https://on.cypress.io/intercept')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.route('api')
|
||||
})
|
||||
})
|
||||
|
||||
it('cy.server() method is deprecated', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`cy.server()` has been deprecated and its use is not supported in the `cy.origin()` callback. Consider using `cy.intercept()` (outside of the callback) instead.')
|
||||
expect(err.docsUrl).to.equal('https://on.cypress.io/intercept')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.server()
|
||||
})
|
||||
})
|
||||
|
||||
it('cy.origin() is not yet supported', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`cy.origin()` use is not currently supported in the `cy.origin()` callback, but is planned for a future release. Please 👍 the following issue and leave a comment with your use-case:')
|
||||
expect(err.docsUrl).to.equal('https://on.cypress.io/github-issue/20718')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.origin('barbaz.com', () => {})
|
||||
})
|
||||
})
|
||||
|
||||
it('cy.intercept() is not supported', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`cy.intercept()` use is not supported in the `cy.origin()` callback. Consider using it outside of the callback instead. Otherwise, please 👍 the following issue and leave a comment with your use-case:')
|
||||
expect(err.docsUrl).to.equal('https://on.cypress.io/github-issue/20720')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.intercept('/foo')
|
||||
})
|
||||
})
|
||||
|
||||
it('cy.session() is not supported', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`cy.session()` use is not supported in the `cy.origin()` callback. Consider using it outside of the callback instead. Otherwise, please 👍 the following issue and leave a comment with your use-case:')
|
||||
expect(err.docsUrl).to.equal('https://on.cypress.io/github-issue/20721')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.session('/foo')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,210 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin viewport', () => {
|
||||
it('syncs the viewport from the primary to secondary', () => {
|
||||
// change the viewport in the primary first
|
||||
cy.viewport(320, 480)
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const viewportChangedSpy = cy.spy()
|
||||
|
||||
cy.on('viewport:changed', viewportChangedSpy)
|
||||
|
||||
// changing the viewport to the same size shouldn't do anything
|
||||
cy.viewport(320, 480).then(() => {
|
||||
expect(viewportChangedSpy).not.to.be.called
|
||||
})
|
||||
|
||||
cy.window().then((win) => {
|
||||
expect(win.innerWidth).to.equal(320)
|
||||
expect(win.innerHeight).to.equal(480)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('with out pre-set viewport', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
context('.viewport()', () => {
|
||||
it('changes the viewport', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.window().then((win) => {
|
||||
expect(win.innerHeight).to.equal(660)
|
||||
expect(win.innerWidth).to.equal(1000)
|
||||
})
|
||||
|
||||
cy.viewport(320, 480)
|
||||
|
||||
cy.window().then((win) => {
|
||||
expect(win.innerHeight).to.equal(480)
|
||||
expect(win.innerWidth).to.equal(320)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('resets the viewport between tests', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.window().then((win) => {
|
||||
expect(win.innerHeight).to.equal(660)
|
||||
expect(win.innerWidth).to.equal(1000)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('cy.on(\'viewport:changed\')', () => {
|
||||
it('calls viewport:changed handler in cy.origin', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const viewportChangedSpy = cy.spy()
|
||||
|
||||
cy.on('viewport:changed', viewportChangedSpy)
|
||||
|
||||
cy.viewport(320, 480).then(() => {
|
||||
expect(viewportChangedSpy).to.be.calledOnce
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('does NOT call viewport:changed handler of primary', () => {
|
||||
const viewportChangedSpy = cy.spy()
|
||||
|
||||
cy.on('viewport:changed', viewportChangedSpy)
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.viewport(320, 480)
|
||||
}).then(() => {
|
||||
expect(viewportChangedSpy).not.to.be.called
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('Cypress.on(\'viewport:changed\')', () => {
|
||||
let viewportChangedSpyPrimary
|
||||
|
||||
before(() => {
|
||||
viewportChangedSpyPrimary = cy.spy()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
// using global since a function can't be passed to cy.origin
|
||||
// and we need to be able to remove the listener in the 'after' hook
|
||||
globalThis.viewportChangedSpySecondary = cy.spy()
|
||||
})
|
||||
})
|
||||
|
||||
after(() => {
|
||||
Cypress.off('viewport:changed', viewportChangedSpyPrimary)
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
Cypress.off('viewport:changed', globalThis.viewportChangedSpySecondary)
|
||||
})
|
||||
|
||||
delete globalThis.viewportChangedSpySecondary
|
||||
})
|
||||
|
||||
it('calls viewport:changed handler in cy.origin', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
Cypress.on('viewport:changed', globalThis.viewportChangedSpySecondary)
|
||||
|
||||
cy.viewport(320, 480).then(() => {
|
||||
expect(globalThis.viewportChangedSpySecondary).to.be.calledOnce
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('does NOT call viewport:changed handler of primary', () => {
|
||||
Cypress.on('viewport:changed', viewportChangedSpyPrimary)
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.viewport(320, 480)
|
||||
}).then(() => {
|
||||
expect(viewportChangedSpyPrimary).not.to.be.called
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('syncs the viewport from the secondary to primary', () => {
|
||||
const viewportChangedSpy = cy.spy()
|
||||
|
||||
cy.on('viewport:changed', viewportChangedSpy)
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
// change the viewport in the secondary first
|
||||
cy.viewport(320, 480)
|
||||
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://localhost:3500/fixtures/multi-domain.html'
|
||||
})
|
||||
})
|
||||
|
||||
// changing the viewport to the same size shouldn't do anything
|
||||
cy.viewport(320, 480).then(() => {
|
||||
expect(viewportChangedSpy).not.to.be.called
|
||||
})
|
||||
|
||||
cy.window().then((win) => {
|
||||
expect(win.innerWidth).to.equal(320)
|
||||
expect(win.innerHeight).to.equal(480)
|
||||
})
|
||||
})
|
||||
|
||||
it('syncs the viewport across multiple origins', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.viewport(320, 480)
|
||||
cy.window().then((win) => {
|
||||
expect(win.innerWidth).to.equal(320)
|
||||
expect(win.innerHeight).to.equal(480)
|
||||
})
|
||||
})
|
||||
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://www.idp.com:3500/fixtures/multi-domain.html'
|
||||
})
|
||||
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
const viewportChangedSpy = cy.spy()
|
||||
|
||||
cy.on('viewport:changed', viewportChangedSpy)
|
||||
|
||||
// changing the viewport to the same size shouldn't do anything
|
||||
cy.viewport(320, 480).then(() => {
|
||||
expect(viewportChangedSpy).not.to.be.called
|
||||
})
|
||||
|
||||
cy.window().then((win) => {
|
||||
expect(win.innerWidth).to.equal(320)
|
||||
expect(win.innerHeight).to.equal(480)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.viewport()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.viewport(320, 480)
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('viewport', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('viewport')
|
||||
expect(consoleProps.Width).to.equal(320)
|
||||
expect(consoleProps.Height).to.equal(480)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,43 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin waiting', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
it('.wait()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const delay = cy.spy(Cypress.Promise, 'delay')
|
||||
|
||||
cy.wait(50).then(() => {
|
||||
expect(delay).to.be.calledWith(50, 'wait')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.wait()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.wait(200)
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('wait', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('wait')
|
||||
expect(consoleProps).to.have.property('Waited For').to.equal('200ms before continuing')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,77 @@
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin window', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="dom-link"]').click()
|
||||
})
|
||||
|
||||
it('.window()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.window().should('have.property', 'top')
|
||||
})
|
||||
})
|
||||
|
||||
it('.document()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.document().should('have.property', 'charset').and('eq', 'UTF-8')
|
||||
})
|
||||
})
|
||||
|
||||
it('.title()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.title().should('include', 'DOM Fixture')
|
||||
})
|
||||
})
|
||||
|
||||
context('#consoleProps', () => {
|
||||
let logs: Map<string, any>
|
||||
|
||||
beforeEach(() => {
|
||||
logs = new Map()
|
||||
|
||||
cy.on('log:changed', (attrs, log) => {
|
||||
logs.set(attrs.id, log)
|
||||
})
|
||||
})
|
||||
|
||||
it('.window()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.window()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('window', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('window')
|
||||
expect(consoleProps.Yielded).to.be.null
|
||||
})
|
||||
})
|
||||
|
||||
it('.document()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.document()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('document', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('document')
|
||||
expect(consoleProps.Yielded).to.be.null
|
||||
})
|
||||
})
|
||||
|
||||
it('.title()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.title()
|
||||
})
|
||||
|
||||
cy.shouldWithTimeout(() => {
|
||||
const { consoleProps } = findCrossOriginLogs('title', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('title')
|
||||
expect(consoleProps.Yielded).to.equal('DOM Fixture')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,95 @@
|
||||
describe('cy.origin - cookie login', () => {
|
||||
const verifyLoggedIn = (username) => {
|
||||
cy.get('h1')
|
||||
.invoke('text')
|
||||
.should('equal', `Welcome, ${username}!`)
|
||||
}
|
||||
|
||||
it('works in a session', () => {
|
||||
cy.session('ZJohnson', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('[data-cy="cookie-login"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('ZJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
}, {
|
||||
validate () {
|
||||
cy.getCookie('user').its('value').should('equal', 'ZJohnson')
|
||||
},
|
||||
})
|
||||
|
||||
cy.visit('/welcome')
|
||||
verifyLoggedIn('ZJohnson')
|
||||
})
|
||||
|
||||
describe('SameSite handling', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('[data-cy="cookie-login"]').click()
|
||||
})
|
||||
|
||||
it('works with no SameSite, no Secure', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('AJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
verifyLoggedIn('AJohnson')
|
||||
})
|
||||
|
||||
it('works with SameSite=None, Secure', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="sameSite"]').select('None')
|
||||
cy.get('[data-cy="secure"]').check()
|
||||
cy.get('[data-cy="username"]').type('BJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
verifyLoggedIn('BJohnson')
|
||||
})
|
||||
|
||||
it('works with SameSite=None, no Secure', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="sameSite"]').select('None')
|
||||
cy.get('[data-cy="username"]').type('CJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
verifyLoggedIn('CJohnson')
|
||||
})
|
||||
|
||||
it('works with SameSite=Lax, Secure', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="sameSite"]').select('Lax')
|
||||
cy.get('[data-cy="secure"]').check()
|
||||
cy.get('[data-cy="username"]').type('DJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
verifyLoggedIn('DJohnson')
|
||||
})
|
||||
|
||||
it('works with SameSite=Strict, Secure', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="sameSite"]').select('Strict')
|
||||
cy.get('[data-cy="secure"]').check()
|
||||
cy.get('[data-cy="username"]').type('EJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
verifyLoggedIn('EJohnson')
|
||||
})
|
||||
|
||||
it('works with invalid SameSite, Secure', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="sameSite"]').select('Invalid')
|
||||
cy.get('[data-cy="secure"]').check()
|
||||
cy.get('[data-cy="username"]').type('FJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
verifyLoggedIn('FJohnson')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,148 @@
|
||||
describe('cy.origin logging', () => {
|
||||
const { _ } = Cypress
|
||||
|
||||
it('groups callback commands on a passing test', () => {
|
||||
const logs: any[] = []
|
||||
|
||||
cy.on('log:added', (attrs) => {
|
||||
logs.push(attrs)
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="dom-check"]').invoke('text')
|
||||
})
|
||||
|
||||
cy.log('after').should(() => {
|
||||
const originLog = _.find(logs, { name: 'origin' })
|
||||
const getLog = _.find(logs, { name: 'get', message: '[data-cy="dom-check"]' })
|
||||
const invokeLog = _.find(logs, { name: 'invoke', message: '.text()' })
|
||||
const newUrlLog = _.find(logs, { name: 'new url' })
|
||||
const logLog = _.find(logs, { name: 'log' })
|
||||
|
||||
expect(originLog.groupStart).to.be.true
|
||||
expect(getLog.group).to.equal(originLog.id)
|
||||
expect(invokeLog.group).to.equal(originLog.id)
|
||||
expect(newUrlLog.group).to.equal(originLog.id)
|
||||
expect(logLog.group).to.be.undefined // ensure the group has ended
|
||||
})
|
||||
})
|
||||
|
||||
it('logs cy.origin as group when failing with validation failure', (done) => {
|
||||
const logs: any[] = []
|
||||
|
||||
cy.on('log:added', (attrs) => {
|
||||
logs.push(attrs)
|
||||
})
|
||||
|
||||
cy.on('fail', () => {
|
||||
const originLog = _.find(logs, { name: 'origin' })
|
||||
|
||||
expect(originLog.groupStart).to.be.true
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
|
||||
// @ts-ignore
|
||||
cy.origin(false, () => {})
|
||||
})
|
||||
|
||||
it('logs cy.origin as group when failing with serialization failure', (done) => {
|
||||
const logs: any[] = []
|
||||
|
||||
cy.on('log:added', (attrs) => {
|
||||
logs.push(attrs)
|
||||
})
|
||||
|
||||
cy.on('fail', () => {
|
||||
const originLog = _.find(logs, { name: 'origin' })
|
||||
|
||||
expect(originLog.groupStart).to.be.true
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
|
||||
const options = { args: { div: Cypress.$('div') } }
|
||||
|
||||
cy.origin('http://foobar.com:3500', options, () => {})
|
||||
})
|
||||
|
||||
it('groups callback commands when failing with inner command failure', (done) => {
|
||||
const logs: any[] = []
|
||||
|
||||
cy.on('log:added', (attrs) => {
|
||||
logs.push(attrs)
|
||||
})
|
||||
|
||||
cy.on('fail', () => {
|
||||
const originLog = _.find(logs, { name: 'origin' })
|
||||
const getLog = _.find(logs, { name: 'get', message: '[data-cy="dom-check"]' })
|
||||
const invokeLog = _.find(logs, { name: 'invoke', message: '.text()' })
|
||||
const newUrlLog = _.find(logs, { name: 'new url' })
|
||||
const failingGetLog = _.find(logs, { name: 'get', message: '#does-not-exist' })
|
||||
|
||||
expect(originLog.groupStart).to.be.true
|
||||
expect(getLog.group).to.equal(originLog.id)
|
||||
expect(invokeLog.group).to.equal(originLog.id)
|
||||
expect(newUrlLog.group).to.equal(originLog.id)
|
||||
expect(failingGetLog.group).to.equal(originLog.id)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="dom-check"]').invoke('text')
|
||||
cy.get('#does-not-exist', { timeout: 1 })
|
||||
})
|
||||
|
||||
cy.log('after')
|
||||
})
|
||||
|
||||
it('groups callback commands when failing with async failure', (done) => {
|
||||
const logs: any[] = []
|
||||
|
||||
cy.on('log:added', (attrs) => {
|
||||
logs.push(attrs)
|
||||
})
|
||||
|
||||
cy.on('fail', () => {
|
||||
const originLog = _.find(logs, { name: 'origin' })
|
||||
const getLog = _.find(logs, { name: 'get', message: '[data-cy="dom-check"]' })
|
||||
const invokeLog = _.find(logs, { name: 'invoke', message: '.text()' })
|
||||
const newUrlLog = _.find(logs, { name: 'new url' })
|
||||
|
||||
expect(originLog.groupStart).to.be.true
|
||||
expect(getLog.group).to.equal(originLog.id)
|
||||
expect(invokeLog.group).to.equal(originLog.id)
|
||||
expect(newUrlLog.group).to.equal(originLog.id)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="dom-check"]').invoke('text').then(() => {
|
||||
setTimeout(() => {
|
||||
throw new Error('async error')
|
||||
})
|
||||
})
|
||||
|
||||
cy.wait(300)
|
||||
})
|
||||
|
||||
cy.log('after')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,251 @@
|
||||
describe('cy.origin', () => {
|
||||
it('passes viewportWidth/Height state to the secondary origin', () => {
|
||||
const expectedViewport = [320, 480]
|
||||
|
||||
cy.viewport(320, 480).then(() => {
|
||||
const primaryViewport = [cy.state('viewportWidth'), cy.state('viewportHeight')]
|
||||
|
||||
expect(primaryViewport).to.deep.equal(expectedViewport)
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
|
||||
cy.origin('http://foobar.com:3500', { args: expectedViewport }, (expectedViewport) => {
|
||||
const secondaryViewport = [cy.state('viewportWidth'), cy.state('viewportHeight')]
|
||||
|
||||
expect(secondaryViewport).to.deep.equal(expectedViewport)
|
||||
})
|
||||
})
|
||||
|
||||
context('withBeforeEach', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
it('runs commands in secondary origin', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy
|
||||
.get('[data-cy="dom-check"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'From a secondary origin')
|
||||
})
|
||||
|
||||
cy.log('after cy.origin')
|
||||
})
|
||||
|
||||
it('passes runnable state to the secondary origin', () => {
|
||||
const runnable = cy.state('runnable')
|
||||
const expectedRunnable = {
|
||||
clearTimeout: null,
|
||||
isPending: null,
|
||||
resetTimeout: null,
|
||||
timeout: null,
|
||||
id: runnable.id,
|
||||
_currentRetry: runnable._currentRetry,
|
||||
_timeout: 4000,
|
||||
type: 'test',
|
||||
title: 'passes runnable state to the secondary origin',
|
||||
titlePath: [
|
||||
'cy.origin',
|
||||
'withBeforeEach',
|
||||
'passes runnable state to the secondary origin',
|
||||
],
|
||||
parent: {
|
||||
id: runnable.parent.id,
|
||||
type: 'suite',
|
||||
title: 'withBeforeEach',
|
||||
titlePath: [
|
||||
'withBeforeEach',
|
||||
],
|
||||
parent: {
|
||||
id: runnable.parent.parent.id,
|
||||
type: 'suite',
|
||||
title: '',
|
||||
titlePath: undefined,
|
||||
ctx: {},
|
||||
},
|
||||
ctx: {},
|
||||
},
|
||||
ctx: {},
|
||||
}
|
||||
|
||||
cy.origin('http://foobar.com:3500', { args: expectedRunnable }, (expectedRunnable) => {
|
||||
const actualRunnable = cy.state('runnable')
|
||||
|
||||
expect(actualRunnable.titlePath()).to.deep.equal(expectedRunnable.titlePath)
|
||||
expectedRunnable.titlePath = actualRunnable.titlePath
|
||||
|
||||
expect(actualRunnable.title).to.equal(expectedRunnable.title)
|
||||
expect(actualRunnable.id).to.equal(expectedRunnable.id)
|
||||
expect(actualRunnable.ctx).to.deep.equal(expectedRunnable.ctx)
|
||||
expect(actualRunnable._timeout).to.equal(expectedRunnable._timeout)
|
||||
expect(actualRunnable.type).to.equal(expectedRunnable.type)
|
||||
expect(actualRunnable.callback).to.exist
|
||||
expect(actualRunnable.timeout).to.exist
|
||||
expect(actualRunnable.parent.title).to.equal(expectedRunnable.parent.title)
|
||||
expect(actualRunnable.parent.type).to.equal(expectedRunnable.parent.type)
|
||||
})
|
||||
})
|
||||
|
||||
it('handles querying nested elements', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy
|
||||
.get('form button')
|
||||
.invoke('text')
|
||||
.should('equal', 'Submit')
|
||||
})
|
||||
|
||||
cy.log('after cy.origin')
|
||||
})
|
||||
|
||||
it('sets up window.Cypress in secondary origin', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy
|
||||
.get('[data-cy="cypress-check"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Has window.Cypress')
|
||||
})
|
||||
})
|
||||
|
||||
describe('data argument', () => {
|
||||
it('passes object to callback function', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: { foo: 'foo', bar: 'bar' } }, ({ foo, bar }) => {
|
||||
expect(foo).to.equal('foo')
|
||||
expect(bar).to.equal('bar')
|
||||
})
|
||||
})
|
||||
|
||||
it('passes array to callback function', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: ['foo', 'bar'] }, ([foo, bar]) => {
|
||||
expect(foo).to.equal('foo')
|
||||
expect(bar).to.equal('bar')
|
||||
})
|
||||
})
|
||||
|
||||
it('passes string to callback function', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: 'foo' }, (foo) => {
|
||||
expect(foo).to.equal('foo')
|
||||
})
|
||||
})
|
||||
|
||||
it('passes number to callback function', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: 1 }, (num) => {
|
||||
expect(num).to.equal(1)
|
||||
})
|
||||
})
|
||||
|
||||
it('passes boolean to callback function', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: true }, (bool) => {
|
||||
expect(bool).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
it('passes mixed types to callback function', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: { foo: 'foo', num: 1, bool: true } }, ({ foo, num, bool }) => {
|
||||
expect(foo).to.equal('foo')
|
||||
expect(num).to.equal(1)
|
||||
expect(bool).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('errors', () => {
|
||||
it('propagates secondary origin errors to the primary that occur within the test', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('variable is not defined')
|
||||
expect(err.message).to.include(`Variables must either be defined within the \`cy.origin()\` command or passed in using the args option.`)
|
||||
expect(err.stack).to.include(`Variables must either be defined within the \`cy.origin()\` command or passed in using the args option.`)
|
||||
// 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`)
|
||||
expect(err.codeFrame).to.exist
|
||||
expect(err.codeFrame!.frame).to.include('cy.origin')
|
||||
done()
|
||||
})
|
||||
|
||||
const variable = 'string'
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.log(variable)
|
||||
})
|
||||
})
|
||||
|
||||
it('propagates thrown errors in the secondary origin back to the primary w/ done', (done) => {
|
||||
cy.on('fail', (e) => {
|
||||
expect(e.message).to.equal('oops')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
throw 'oops'
|
||||
})
|
||||
})
|
||||
|
||||
it('propagates thrown errors in the secondary origin back to the primary w/o done', () => {
|
||||
return new Promise((resolve) => {
|
||||
cy.on('fail', (e) => {
|
||||
expect(e.message).to.equal('oops')
|
||||
resolve(undefined)
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
throw 'oops'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('receives command failures from the secondary origin', (done) => {
|
||||
const timeout = 50
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out retrying after ${timeout}ms: Expected to find element: \`#doesnt-exist\`, but never found it`)
|
||||
// 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()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', { args: timeout }, (timeout) => {
|
||||
cy.get('#doesnt-exist', {
|
||||
timeout,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('receives command failures from the secondary origin with the default timeout', { defaultCommandTimeout: 50 }, (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms: Expected to find element: \`#doesnt-exist\`, but never found it`)
|
||||
// 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()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('#doesnt-exist')
|
||||
})
|
||||
})
|
||||
|
||||
it('has non serializable arguments', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`This is likely because the arguments specified are not serializable. Note that functions and DOM objects cannot be serialized.`)
|
||||
expect(err.stack).to.include(`This is likely because the arguments specified are not serializable. Note that functions and DOM objects cannot be serialized.`)
|
||||
// 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`)
|
||||
expect(err.codeFrame).to.exist
|
||||
expect(err.codeFrame!.frame).to.include('cy.origin')
|
||||
done()
|
||||
})
|
||||
|
||||
const variable = () => {}
|
||||
|
||||
cy.origin('http://idp.com:3500', { args: variable }, (variable) => {
|
||||
variable()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,271 @@
|
||||
['config', 'env'].forEach((fnName) => {
|
||||
describe(`cy.origin- Cypress.${fnName}()`, () => {
|
||||
const USED_KEYS = {
|
||||
foo: 'cy-origin-foo',
|
||||
bar: 'cy-origin-bar',
|
||||
baz: 'cy-origin-baz',
|
||||
qux: 'cy-origin-qux',
|
||||
quux: 'cy-origin-quux',
|
||||
unserializable: 'cy-origin-unserializable',
|
||||
error: 'cy-origin-error',
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
// @ts-ignore
|
||||
window.top.__cySkipValidateConfig = true
|
||||
})
|
||||
|
||||
if (fnName === 'config') {
|
||||
it(`throws if mutating read-only config values with Cypress.config()`, () => {
|
||||
return new Promise<void>((resolve) => {
|
||||
// @ts-ignore
|
||||
window.top.__cySkipValidateConfig = false
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`Cypress.config()` cannot mutate option `chromeWebSecurity` because it is a read-only property.')
|
||||
resolve()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
// @ts-ignore
|
||||
Cypress.config('chromeWebSecurity', false)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
context('serializable', () => {
|
||||
it(`syncs initial Cypress.${fnName}() from the primary origin to the secondary (synchronously)`, () => {
|
||||
Cypress[fnName](USED_KEYS.foo, 'bar')
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const bar = Cypress[fnName](USED_KEYS.foo)
|
||||
|
||||
expect(bar).to.equal('bar')
|
||||
})
|
||||
})
|
||||
|
||||
it(`syncs serializable values in the Cypress.${fnName}() again to the secondary even after spec bridge is created`, () => {
|
||||
Cypress[fnName](USED_KEYS.foo, 'baz')
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const baz = Cypress[fnName](USED_KEYS.foo)
|
||||
|
||||
expect(baz).to.equal('baz')
|
||||
})
|
||||
})
|
||||
|
||||
it(`syncs serializable Cypress.${fnName}() values outwards from secondary (synchronously)`, () => {
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
Cypress[fnName](USED_KEYS.bar, 'baz')
|
||||
}).then(() => {
|
||||
const baz = Cypress[fnName](USED_KEYS.bar)
|
||||
|
||||
expect(baz).to.equal('baz')
|
||||
})
|
||||
})
|
||||
|
||||
it(`syncs serializable Cypress.${fnName}() values outwards from secondary even if the value is undefined`, () => {
|
||||
Cypress[fnName](USED_KEYS.foo, 'bar')
|
||||
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
Cypress[fnName](USED_KEYS.foo, undefined)
|
||||
}).then(() => {
|
||||
expect(Cypress[fnName]('foo')).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
it(`syncs serializable Cypress.${fnName}() values outwards from secondary (commands/async)`, () => {
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
cy.then(() => {
|
||||
Cypress[fnName](USED_KEYS.bar, 'quux')
|
||||
})
|
||||
}).then(() => {
|
||||
const quux = Cypress[fnName](USED_KEYS.bar)
|
||||
|
||||
expect(quux).to.equal('quux')
|
||||
})
|
||||
})
|
||||
|
||||
it(`persists Cypress.${fnName}() changes made in the secondary, assuming primary has not overwritten with a serializable value`, () => {
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const quux = Cypress[fnName](USED_KEYS.bar)
|
||||
|
||||
expect(quux).to.equal('quux')
|
||||
})
|
||||
})
|
||||
|
||||
it(`does NOT sync Cypress.${fnName}() changes made in the secondary after the command queue is finished and the callback window is closed`, {
|
||||
[USED_KEYS.baz]: undefined,
|
||||
env: {
|
||||
[USED_KEYS.baz]: undefined,
|
||||
},
|
||||
}, () => {
|
||||
return new Promise<void>((resolve) => {
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
setTimeout(() => {
|
||||
// this value STILL gets set, but will be blown away on the next origin with whatever exists in the primary
|
||||
Cypress[fnName](USED_KEYS.baz, 'qux')
|
||||
}, 100)
|
||||
|
||||
Cypress[fnName](USED_KEYS.baz, 'quux')
|
||||
}).then(() => {
|
||||
const quux = Cypress[fnName](USED_KEYS.baz)
|
||||
|
||||
expect(quux).to.equal('quux')
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('overwrites different values in secondary if one exists in the primary', {
|
||||
[USED_KEYS.baz]: 'quux',
|
||||
env: {
|
||||
[USED_KEYS.baz]: 'quux',
|
||||
},
|
||||
}, () => {
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
// in previous test, 'baz' was set to 'qux' after the callback window was closed.
|
||||
// this should be overwritten by 'quux' that exists in the primary
|
||||
const quux = Cypress[fnName](USED_KEYS.baz)
|
||||
|
||||
expect(quux).to.equal('quux')
|
||||
})
|
||||
})
|
||||
|
||||
it(`overwrites different values in secondary, even if the Cypress.${fnName}() value does not exist in the primary`, {
|
||||
[USED_KEYS.baz]: undefined,
|
||||
env: {
|
||||
[USED_KEYS.baz]: undefined,
|
||||
},
|
||||
}, () => {
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const isNowUndefined = Cypress[fnName](USED_KEYS.baz)
|
||||
|
||||
expect(isNowUndefined).to.be.undefined
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('unserializable', () => {
|
||||
const unserializableFunc = () => undefined
|
||||
|
||||
it('does not sync unserializable values from the primary to the secondary', () => {
|
||||
Cypress[fnName](USED_KEYS.unserializable, unserializableFunc)
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const isUndefined = Cypress[fnName](USED_KEYS.unserializable)
|
||||
|
||||
expect(isUndefined).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
it('does not overwrite unserializable values in the primary when none exist in the secondary', () => {
|
||||
Cypress[fnName](USED_KEYS.unserializable, unserializableFunc)
|
||||
cy.origin('http://foobar.com:3500', () => undefined)
|
||||
|
||||
const isFunc = Cypress[fnName](USED_KEYS.unserializable)
|
||||
|
||||
expect(isFunc).to.equal(unserializableFunc)
|
||||
})
|
||||
|
||||
it('overwrites unserializable values in the primary when serializable values of same key exist in secondary', () => {
|
||||
Cypress[fnName](USED_KEYS.unserializable, unserializableFunc)
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
Cypress[fnName](USED_KEYS.unserializable, undefined)
|
||||
}).then(() => {
|
||||
const isNowUndefined = Cypress[fnName](USED_KEYS.unserializable)
|
||||
|
||||
expect(isNowUndefined).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
it('overwrites unserializable values in the secondary when serializable values of same key exist in primary', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const unserializableFuncSecondary = () => undefined
|
||||
|
||||
Cypress[fnName](USED_KEYS.unserializable, unserializableFuncSecondary)
|
||||
}).then(() => {
|
||||
Cypress[fnName](USED_KEYS.unserializable, undefined)
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const isUndefined = Cypress[fnName](USED_KEYS.unserializable)
|
||||
|
||||
expect(isUndefined).to.be.undefined
|
||||
}).then(() => {
|
||||
const isUndefined = Cypress[fnName](USED_KEYS.unserializable)
|
||||
|
||||
expect(isUndefined).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
it('does not overwrite unserializable values in the primary when unserializable values of same key exist in secondary', () => {
|
||||
Cypress[fnName](USED_KEYS.unserializable, unserializableFunc)
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const unserializableFuncSecondary = () => undefined
|
||||
|
||||
Cypress[fnName](USED_KEYS.unserializable, unserializableFuncSecondary)
|
||||
}).then(() => {
|
||||
const isFunc = Cypress[fnName](USED_KEYS.unserializable)
|
||||
|
||||
expect(isFunc).to.equal(unserializableFunc)
|
||||
})
|
||||
})
|
||||
|
||||
it('does not try to merge objects that contain unserializable values', () => {
|
||||
const partiallyUnserializableObject = {
|
||||
a: 1,
|
||||
b: document.createElement('h1'),
|
||||
}
|
||||
|
||||
Cypress[fnName](USED_KEYS.unserializable, partiallyUnserializableObject)
|
||||
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const doesNotContainPartialAProperty = Cypress[fnName](USED_KEYS.unserializable)
|
||||
|
||||
expect(doesNotContainPartialAProperty?.a).to.be.undefined
|
||||
|
||||
Cypress[fnName](USED_KEYS.unserializable, {
|
||||
a: 3,
|
||||
c: document.createElement('h1'),
|
||||
})
|
||||
}).then(() => {
|
||||
const isPartiallyUnserializableObject = Cypress[fnName](USED_KEYS.unserializable)
|
||||
|
||||
expect(isPartiallyUnserializableObject.a).to.equal(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('structuredClone()', () => {
|
||||
it('(firefox) uses native structuredClone in firefox and does NOT serialize Error objects in config', {
|
||||
browser: 'firefox',
|
||||
}, function () {
|
||||
Cypress[fnName](USED_KEYS.error, new Error('error'))
|
||||
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const isUndefined = Cypress[fnName](USED_KEYS.error)
|
||||
|
||||
expect(isUndefined).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
// NOTE: chrome 98 and above uses a native structuredClone() method, but that method CAN clone Error objects
|
||||
it('(chromium) uses ponyfilled or native structuredClone that can serialize Error objects in config', {
|
||||
browser: { family: 'chromium' },
|
||||
}, () => {
|
||||
Cypress[fnName](USED_KEYS.error, new Error('error'))
|
||||
|
||||
cy.origin('http://foobar.com:3500', { args: { fnName, USED_KEYS } }, ({ fnName, USED_KEYS }) => {
|
||||
const isErrorObj = Cypress[fnName](USED_KEYS.error)
|
||||
|
||||
// We preserve the error structure, but on preprocessing to the spec bridge, the error is converted to a flat object
|
||||
expect(isErrorObj).to.be.an.instanceof(Object)
|
||||
expect(isErrorObj.message).to.eq('error')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,244 @@
|
||||
const { assertLogLength } = require('../../../support/utils')
|
||||
|
||||
describe('cy.origin Cypress API', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
context('Commands', () => {
|
||||
it('adds a custom command', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
// @ts-ignore
|
||||
Cypress.Commands.add('foo', () => 'bar')
|
||||
|
||||
// @ts-ignore
|
||||
cy.foo().should('equal', 'bar')
|
||||
})
|
||||
|
||||
// persists added command through spec bridge
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
// @ts-ignore
|
||||
cy.foo().should('equal', 'bar')
|
||||
})
|
||||
})
|
||||
|
||||
it('overwrites an existing command in the spec bridge', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
// @ts-ignore
|
||||
Cypress.Commands.overwrite('foo', () => 'baz')
|
||||
|
||||
// @ts-ignore
|
||||
cy.foo().should('equal', 'baz')
|
||||
})
|
||||
|
||||
// persists overwritten command through spec bridge
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
// @ts-ignore
|
||||
cy.foo().should('equal', 'baz')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('Keyboard', () => {
|
||||
it('does NOT sync defaults', () => {
|
||||
const defaults = Cypress.Keyboard.defaults({
|
||||
keystrokeDelay: 30,
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', { args: defaults }, (primaryKeyboardDefaults) => {
|
||||
const crossOriginKeyboardDefaults = Cypress.Keyboard.defaults({})
|
||||
|
||||
expect(crossOriginKeyboardDefaults).to.not.deep.equal(primaryKeyboardDefaults)
|
||||
})
|
||||
})
|
||||
|
||||
it('allows a user to configure defaults', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const crossOriginKeyboardDefaults = Cypress.Keyboard.defaults({
|
||||
keystrokeDelay: 60,
|
||||
})
|
||||
|
||||
expect(crossOriginKeyboardDefaults).to.deep.include({
|
||||
keystrokeDelay: 60,
|
||||
})
|
||||
})
|
||||
|
||||
// persists default configuration changes through spec bridge
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const crossOriginKeyboardDefaults = Cypress.Keyboard.defaults({})
|
||||
|
||||
expect(crossOriginKeyboardDefaults).to.deep.include({
|
||||
keystrokeDelay: 60,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('Screenshot', () => {
|
||||
it('does NOT sync defaults', () => {
|
||||
Cypress.Screenshot.defaults({
|
||||
blackout: ['foo'],
|
||||
overwrite: true,
|
||||
onBeforeScreenshot: () => undefined,
|
||||
onAfterScreenshot: () => undefined,
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const crossOriginScreenshotDefaults = Cypress.Screenshot.defaults({})
|
||||
|
||||
expect(crossOriginScreenshotDefaults).to.not.deep.include({
|
||||
blackout: ['foo'],
|
||||
overwrite: true,
|
||||
onBeforeScreenshot: () => undefined,
|
||||
onAfterScreenshot: () => undefined,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('allows a user to configure defaults', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const crossOriginScreenshotDefaults = Cypress.Screenshot.defaults({
|
||||
blackout: ['foo'],
|
||||
overwrite: true,
|
||||
})
|
||||
|
||||
expect(crossOriginScreenshotDefaults).to.deep.include({
|
||||
blackout: ['foo'],
|
||||
overwrite: true,
|
||||
})
|
||||
})
|
||||
|
||||
// persists default configuration changes through spec bridge
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const crossOriginScreenshotDefaults = Cypress.Screenshot.defaults({})
|
||||
|
||||
expect(crossOriginScreenshotDefaults).to.deep.include({
|
||||
blackout: ['foo'],
|
||||
overwrite: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('dom', () => {
|
||||
it('provides a sanity check that the dom API exists on Cypress.*', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="dom-check"]').then(($el) => {
|
||||
expect(Cypress.dom.isAttached($el)).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('properties', () => {
|
||||
it('has arch property synced from primary', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: Cypress.arch }, (theArch) => {
|
||||
expect(Cypress.arch).to.equal(theArch)
|
||||
})
|
||||
})
|
||||
|
||||
it('has browser property synced from primary', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: Cypress.browser }, (theBrowser) => {
|
||||
expect(Cypress.browser).to.deep.equal(theBrowser)
|
||||
})
|
||||
})
|
||||
|
||||
it('has currentTest property synced from primary', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: Cypress.currentTest }, (theCurrentTest) => {
|
||||
expect(Cypress.currentTest).to.deep.equal(theCurrentTest)
|
||||
})
|
||||
})
|
||||
|
||||
it('has platform property synced from primary', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: Cypress.platform }, (thePlatform) => {
|
||||
expect(Cypress.platform).to.equal(thePlatform)
|
||||
})
|
||||
})
|
||||
|
||||
it('has testingType property synced from primary', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: Cypress.testingType }, (theTestingType) => {
|
||||
expect(Cypress.testingType).to.deep.equal(theTestingType)
|
||||
})
|
||||
})
|
||||
|
||||
it('has spec property synced from primary', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: Cypress.spec }, (theSpec) => {
|
||||
expect(Cypress.spec).to.deep.equal(theSpec)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('methods', () => {
|
||||
it('isCy()', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
expect(Cypress.isCy(cy)).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
it('isBrowser()', () => {
|
||||
cy.origin('http://foobar.com:3500', { args: Cypress.browser }, (theBrowser) => {
|
||||
expect(Cypress.isBrowser(theBrowser.name)).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
it('log()', () => {
|
||||
const logs: Cypress.Log[] = []
|
||||
|
||||
cy.on('log:added', (attrs, log: Cypress.Log) => {
|
||||
logs.push(log)
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
Cypress.log({
|
||||
name: 'log',
|
||||
message: 'test log',
|
||||
})
|
||||
})
|
||||
.then(() => {
|
||||
assertLogLength(logs, 3)
|
||||
expect(logs[1].get('name')).to.equal('log')
|
||||
expect(logs[1].get('message')).to.equal('test log')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('not supported', () => {
|
||||
it('throws an error when a user attempts to configure Cypress.Server.defaults() inside of cy.origin', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`Cypress.Server.*` has been deprecated and its use is not supported in the `cy.origin()` callback. Consider using `cy.intercept()` (outside of the callback) instead.')
|
||||
expect(err.docsUrl).to.equal('https://on.cypress.io/intercept')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
Cypress.Server.defaults({})
|
||||
})
|
||||
})
|
||||
|
||||
it('throws an error when a user attempts to configure Cypress.Cookies.preserveOnce() inside of cy.origin', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`Cypress.Cookies.preserveOnce` use is not supported in the `cy.origin()` callback. Consider using `cy.session()` (outside of the callback) instead.')
|
||||
expect(err.docsUrl).to.equal('https://on.cypress.io/session')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
// @ts-ignore
|
||||
Cypress.Cookies.preserveOnce({})
|
||||
})
|
||||
})
|
||||
|
||||
it('throws an error when a user attempts to call Cypress.session.clearAllSavedSessions() inside of multi-domain', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`Cypress.session.*` methods are not supported in the `cy.switchToDomain()` callback. Consider using them outside of the callback instead.')
|
||||
expect(err.docsUrl).to.equal('https://on.cypress.io/session-api')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
Cypress.session.clearAllSavedSessions()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,152 @@
|
||||
describe('cy.origin', () => {
|
||||
it('window:before:load event', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.on('window:before:load', (win: {testPrimaryOriginBeforeLoad: boolean}) => {
|
||||
win.testPrimaryOriginBeforeLoad = true
|
||||
})
|
||||
|
||||
cy.window().its('testPrimaryOriginBeforeLoad').should('be.true')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.on('window:before:load', (win: {testSecondaryWindowBeforeLoad: boolean}) => {
|
||||
win.testSecondaryWindowBeforeLoad = true
|
||||
})
|
||||
|
||||
cy.window().its('testSecondaryWindowBeforeLoad').should('be.true')
|
||||
cy.window().its('testPrimaryOriginBeforeLoad').should('be.undefined')
|
||||
cy
|
||||
.get('[data-cy="window-before-load"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Window Before Load Called')
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
|
||||
cy.window().its('testPrimaryOriginBeforeLoad').should('be.true')
|
||||
cy.window().its('testSecondaryWindowBeforeLoad').should('be.undefined')
|
||||
})
|
||||
|
||||
describe('post window load events', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
it('form:submitted', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const afterFormSubmitted = new Promise<void>((resolve) => {
|
||||
Cypress.once('form:submitted', (e) => {
|
||||
const $form = cy.$$('form')
|
||||
|
||||
expect(e.target).to.eq($form.get(0))
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
cy.get('form').submit()
|
||||
cy.wrap(afterFormSubmitted)
|
||||
})
|
||||
})
|
||||
|
||||
it('window:before:unload', () => {
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
const afterWindowBeforeUnload = new Promise<void>((resolve) => {
|
||||
Cypress.once('window:before:unload', () => {
|
||||
expect(location.host).to.equal('foobar.com:3500')
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
|
||||
cy.wrap(afterWindowBeforeUnload)
|
||||
})
|
||||
})
|
||||
|
||||
it('window:unload', () => {
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
const afterWindowUnload = new Promise<void>((resolve) => {
|
||||
Cypress.once('window:unload', () => {
|
||||
expect(location.host).to.equal('foobar.com:3500')
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
|
||||
cy.wrap(afterWindowUnload)
|
||||
})
|
||||
})
|
||||
|
||||
it('window:alert', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const afterWindowAlert = new Promise<void>((resolve) => {
|
||||
Cypress.once('window:alert', (text) => {
|
||||
expect(location.host).to.equal('foobar.com:3500')
|
||||
expect(`window:alert ${text}`).to.equal('window:alert the alert text')
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
cy.get('[data-cy="alert"]').click()
|
||||
cy.wrap(afterWindowAlert)
|
||||
})
|
||||
})
|
||||
|
||||
it('window:confirm', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const afterWindowConfirm = new Promise<void>((resolve) => {
|
||||
Cypress.once('window:confirm', (text) => {
|
||||
expect(location.host).to.equal('foobar.com:3500')
|
||||
expect(`window:confirm ${text}`).to.equal('window:confirm the confirm text')
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
cy.get('[data-cy="confirm"]').click()
|
||||
cy.wrap(afterWindowConfirm)
|
||||
})
|
||||
})
|
||||
|
||||
it('window:confirmed - true when no window:confirm listeners return false', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const afterWindowConfirmed = new Promise<void>((resolve) => {
|
||||
Cypress.once('window:confirmed', (text, returnedFalse) => {
|
||||
expect(location.host).to.equal('foobar.com:3500')
|
||||
expect(`window:confirmed ${text} - ${returnedFalse}`).to.equal('window:confirmed the confirm text - true')
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.on('window:confirm', () => {})
|
||||
Cypress.on('window:confirm', () => {
|
||||
return true
|
||||
})
|
||||
|
||||
cy.get('[data-cy="confirm"]').click()
|
||||
cy.wrap(afterWindowConfirmed)
|
||||
})
|
||||
})
|
||||
|
||||
it('window:confirmed - false when any window:confirm listeners return false', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const afterWindowConfirmed = new Promise<void>((resolve) => {
|
||||
Cypress.once('window:confirmed', (text, returnedFalse) => {
|
||||
expect(location.host).to.equal('foobar.com:3500')
|
||||
expect(`window:confirmed ${text} - ${returnedFalse}`).to.equal('window:confirmed the confirm text - false')
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.on('window:confirm', () => {
|
||||
return false
|
||||
})
|
||||
|
||||
Cypress.on('window:confirm', () => {})
|
||||
|
||||
cy.get('[data-cy="confirm"]').click()
|
||||
cy.wrap(afterWindowConfirmed)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,31 @@
|
||||
// @ts-ignore
|
||||
describe('cy.origin - rerun', { }, () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
it('successfully reruns tests', () => {
|
||||
// @ts-ignore
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="dom-check"]')
|
||||
})
|
||||
.then(() => {
|
||||
const top = window.top!
|
||||
|
||||
// @ts-ignore
|
||||
if (!top.hasRunOnce) {
|
||||
// @ts-ignore
|
||||
top.hasRunOnce = true
|
||||
|
||||
// cause a rerun event to occur by triggering a hash change
|
||||
top.dispatchEvent(new Event('hashchange'))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// this only executes after the test has been rerun
|
||||
expect(true).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,464 @@
|
||||
describe('cy.origin - uncaught errors', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="errors-link"]').click()
|
||||
})
|
||||
|
||||
describe('sync errors', () => {
|
||||
it('appropriately reports negative assertions', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.eq('AssertionError')
|
||||
expect(err.message).to.include('expected true to be false')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.then(() => {
|
||||
expect(true).to.be.false
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('fails the current test/command if sync errors are thrown from the cy.origin callback', (done) => {
|
||||
const uncaughtExceptionSpy = cy.spy()
|
||||
const r = cy.state('runnable')
|
||||
|
||||
cy.on('uncaught:exception', uncaughtExceptionSpy)
|
||||
cy.on('fail', (err, runnable) => {
|
||||
// TODO: we likely need to change the messaging around this error to make it clear to cy.origin users that
|
||||
// this behavior is configurable with 'uncaught:exception', but it MUST be declared inside the cy.origin callback
|
||||
// and that 'uncaught:exception' will NOT be triggered outside that callback (inside the primary origin)
|
||||
// https://github.com/cypress-io/cypress/issues/20969
|
||||
expect(err.name).to.eq('Error')
|
||||
expect(err.message).to.include('sync error')
|
||||
expect(err.message).to.include('The following error originated from your application code, not from Cypress.')
|
||||
expect(err.message).to.not.include('https://on.cypress.io/uncaught-exception-from-application')
|
||||
expect(err.docsUrl).to.deep.eq(['https://on.cypress.io/uncaught-exception-from-application'])
|
||||
|
||||
// lastly, make sure the `uncaught:exception' handler is NOT called in the primary
|
||||
expect(uncaughtExceptionSpy).not.to.be.called
|
||||
expect(runnable).to.be.equal(r)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('.trigger-sync-error').click()
|
||||
})
|
||||
})
|
||||
|
||||
it('returns false from cy.on(uncaught:exception), resulting in cy:fail to not be called in the primary and passes the test', () => {
|
||||
const uncaughtExceptionSpy = cy.spy()
|
||||
const failureSpy = cy.spy()
|
||||
|
||||
cy.on('uncaught:exception', uncaughtExceptionSpy)
|
||||
|
||||
cy.on('fail', failureSpy)
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.on('uncaught:exception', () => false)
|
||||
cy.get('.trigger-sync-error').click()
|
||||
}).then(() => {
|
||||
expect(uncaughtExceptionSpy).not.to.be.called
|
||||
expect(failureSpy).not.to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
it('returns true from cy.on(uncaught:exception), resulting in cy:fail to be called in the primary', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.eq('Error')
|
||||
expect(err.message).to.include('sync error')
|
||||
expect(err.message).to.include('The following error originated from your application code, not from Cypress.')
|
||||
expect(err.message).to.not.include('https://on.cypress.io/uncaught-exception-from-application')
|
||||
expect(err.docsUrl).to.deep.eq(['https://on.cypress.io/uncaught-exception-from-application'])
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.once('uncaught:exception', () => true)
|
||||
cy.get('.trigger-sync-error').click()
|
||||
})
|
||||
})
|
||||
|
||||
// if we mutate the error, the app's listeners for 'error' or
|
||||
// 'unhandledrejection' will have our wrapped error instead of the original
|
||||
it('original error is not mutated for "error" in the origin', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.once('uncaught:exception', () => false)
|
||||
|
||||
cy.get('.trigger-sync-error').click()
|
||||
cy.get('.error-one').invoke('text').should('equal', 'sync error')
|
||||
cy.get('.error-two').invoke('text').should('equal', 'sync error')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('async errors', () => {
|
||||
it('fails the current test/command if async errors are thrown from the cy.origin callback', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.eq('Error')
|
||||
expect(err.message).to.include('setTimeout error')
|
||||
expect(err.message).to.include('The following error originated from your test code, not from Cypress.')
|
||||
// ensure error doesn't get wrapped twice
|
||||
expect(err.message).not.to.include('> The following error originated')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
setTimeout(() => {
|
||||
throw new Error('setTimeout error')
|
||||
}, 50)
|
||||
|
||||
// add the cy.wait here to keep commands streaming in, forcing the
|
||||
// cy.origin callback window to be open long enough for the error to occur
|
||||
cy.wait(250)
|
||||
})
|
||||
})
|
||||
|
||||
it('fails the current test/command if async errors are thrown from the secondary origin AUT', (done) => {
|
||||
const uncaughtExceptionSpy = cy.spy()
|
||||
const r = cy.state('runnable')
|
||||
|
||||
cy.on('uncaught:exception', uncaughtExceptionSpy)
|
||||
|
||||
cy.on('fail', (err, runnable) => {
|
||||
expect(err.name).to.eq('Error')
|
||||
expect(err.message).to.include('async error')
|
||||
expect(err.message).to.include('The following error originated from your application code, not from Cypress.')
|
||||
// ensure error doesn't get wrapped twice
|
||||
expect(err.message).not.to.include('> The following error originated')
|
||||
expect(err.message).to.not.include('https://on.cypress.io/uncaught-exception-from-application')
|
||||
expect(err.docsUrl).to.deep.eq(['https://on.cypress.io/uncaught-exception-from-application'])
|
||||
|
||||
expect(uncaughtExceptionSpy).not.to.be.called
|
||||
expect(runnable).to.be.equal(r)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('.trigger-async-error').click()
|
||||
|
||||
// add the cy.wait here to keep commands streaming in,
|
||||
// forcing the cy.origin callback window to be open long enough for an async error to occur
|
||||
cy.wait(1000)
|
||||
})
|
||||
})
|
||||
|
||||
it('passes the current test/command if async errors are thrown from the secondary origin AUT, but the cy.origin callback is finished running', () => {
|
||||
const uncaughtExceptionSpy = cy.spy()
|
||||
const failureSpy = cy.spy()
|
||||
|
||||
cy.on('uncaught:exception', uncaughtExceptionSpy)
|
||||
|
||||
cy.on('fail', failureSpy)
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
// the async error here should be thrown AFTER the current command and test has finished, resulting in a passed test with no fail being triggered in the primary
|
||||
cy.get('.trigger-async-error').click()
|
||||
}).then(() => {
|
||||
expect(uncaughtExceptionSpy).not.to.be.called
|
||||
expect(failureSpy).not.to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
it('fails the current test/command if async errors are thrown from the cy.origin callback after it is finished running', (done) => {
|
||||
cy.once('fail', (err) => {
|
||||
expect(err.name).to.eq('Error')
|
||||
expect(err.message).to.include('setTimeout error')
|
||||
expect(err.message).to.include('The following error originated from your test code, not from Cypress.')
|
||||
// ensure error doesn't get wrapped twice
|
||||
expect(err.message).not.to.include('> The following error originated')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
setTimeout(() => {
|
||||
throw new Error('setTimeout error')
|
||||
}, 50)
|
||||
})
|
||||
|
||||
cy.wait(250)
|
||||
})
|
||||
})
|
||||
|
||||
describe('unhandled rejections', () => {
|
||||
it('unhandled rejection triggers uncaught:exception and has promise as third argument', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const r = cy.state('runnable')
|
||||
|
||||
const afterUncaughtException = new Promise<void>((resolve) => {
|
||||
cy.once('uncaught:exception', (err, runnable, promise) => {
|
||||
expect(err.stack).to.include('promise rejection')
|
||||
expect(err.stack).to.include('one')
|
||||
expect(err.stack).to.include('two')
|
||||
expect(err.stack).to.include('three')
|
||||
expect(runnable).to.be.equal(r)
|
||||
expect(promise).to.be.a('promise')
|
||||
|
||||
resolve()
|
||||
|
||||
return false
|
||||
})
|
||||
})
|
||||
|
||||
cy.get('.trigger-unhandled-rejection').click()
|
||||
cy.wrap(afterUncaughtException)
|
||||
})
|
||||
})
|
||||
|
||||
it('original error is not mutated for "unhandledrejection"', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.once('uncaught:exception', () => false)
|
||||
|
||||
cy.get('.trigger-unhandled-rejection').click()
|
||||
cy.get('.error-one').invoke('text').should('equal', 'promise rejection')
|
||||
cy.get('.error-two').invoke('text').should('equal', 'promise rejection')
|
||||
})
|
||||
})
|
||||
|
||||
it('fails the current test/command if a promise is rejected from the test code in cy.origin', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.eq('Error')
|
||||
expect(err.message).to.include('rejected promise')
|
||||
expect(err.message).to.include('The following error originated from your test code, not from Cypress. It was caused by an unhandled promise rejection.')
|
||||
expect(err.message).to.not.include('https://on.cypress.io/uncaught-exception-from-application')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
Promise.reject(new Error('rejected promise'))
|
||||
|
||||
// add the cy.wait here to keep commands streaming in, forcing the
|
||||
// cy.origin callback window to be open long enough for the error to occur
|
||||
cy.wait(250)
|
||||
})
|
||||
})
|
||||
|
||||
it('fails the current test/command if a promise is rejected from the cy.origin callback after it is finished running', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.eq('Error')
|
||||
expect(err.message).to.include('rejected promise')
|
||||
expect(err.message).to.include('The following error originated from your test code, not from Cypress. It was caused by an unhandled promise rejection.')
|
||||
expect(err.message).to.not.include('https://on.cypress.io/uncaught-exception-from-application')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
Promise.reject(new Error('rejected promise'))
|
||||
})
|
||||
|
||||
cy.wait(250)
|
||||
})
|
||||
})
|
||||
|
||||
describe('unserializable errors', () => {
|
||||
it('handles users throwing dom elements', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.equal('CypressError')
|
||||
expect(err.message).to.equal('`cy.origin()` could not serialize the thrown value. Please make sure the value being thrown is supported by the structured clone algorithm.')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
throw document.createElement('h1')
|
||||
})
|
||||
})
|
||||
|
||||
it('handles users throwing functions', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.equal('CypressError')
|
||||
expect(err.message).to.equal('`cy.origin()` could not serialize the thrown value. Please make sure the value being thrown is supported by the structured clone algorithm.')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
throw () => undefined
|
||||
})
|
||||
})
|
||||
|
||||
it('handles users throwing symbols', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.equal('CypressError')
|
||||
expect(err.message).to.equal('`cy.origin()` could not serialize the thrown value. Please make sure the value being thrown is supported by the structured clone algorithm.')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
throw Symbol('foo')
|
||||
})
|
||||
})
|
||||
|
||||
it('handles users throwing promises', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.equal('CypressError')
|
||||
expect(err.message).to.equal('`cy.origin()` could not serialize the thrown value. Please make sure the value being thrown is supported by the structured clone algorithm.')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
throw new Promise(() => {})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('serializable errors', () => {
|
||||
it('handles users throwing complex errors/classes', (done) => {
|
||||
cy.on('fail', (err: any) => {
|
||||
expect(err.name).to.equal('CustomError')
|
||||
expect(err.message).to.equal('custom error')
|
||||
expect(err._name).to.equal('CustomError')
|
||||
expect(err.foo).to.equal('bar')
|
||||
|
||||
const { writable } = Object.getOwnPropertyDescriptor(err, 'name') as PropertyDescriptor
|
||||
|
||||
// After serialization, read-only properties are now writable
|
||||
expect(writable).to.be.true
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
class CustomError extends Error {
|
||||
private _name = 'CustomError'
|
||||
get name () {
|
||||
return this._name
|
||||
}
|
||||
set name (name: string) {
|
||||
this._name = name
|
||||
}
|
||||
}
|
||||
|
||||
const customErrorInstance = new CustomError('custom error')
|
||||
|
||||
// @ts-ignore
|
||||
customErrorInstance.foo = 'bar'
|
||||
|
||||
const { writable } = Object.getOwnPropertyDescriptor(CustomError, 'name') as PropertyDescriptor
|
||||
|
||||
// make sure the name property is read-only before serializing it through postMessage
|
||||
expect(writable).to.be.false
|
||||
|
||||
cy.then(() => {
|
||||
throw customErrorInstance
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('handles users throwing complex objects/classes', (done) => {
|
||||
cy.on('fail', (err: any) => {
|
||||
expect(err.customMethod).to.be.undefined
|
||||
expect(err.customProp).to.equal('foobar')
|
||||
expect(err._metasyntaticList).to.deep.equal(['foo', 'bar'])
|
||||
expect(err.metasyntaticList).to.deep.equal(['foo', 'bar'])
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
class FooBar {
|
||||
private _metasyntaticList = ['foo']
|
||||
get metasyntaticList (): string[] {
|
||||
return this._metasyntaticList
|
||||
}
|
||||
set metasyntaticList (itemsToAdd: string[]) {
|
||||
this._metasyntaticList = this._metasyntaticList.concat(itemsToAdd)
|
||||
}
|
||||
}
|
||||
|
||||
const foobarInstance: any = new FooBar
|
||||
|
||||
foobarInstance.customProp = 'foobar'
|
||||
foobarInstance.metasyntaticList = ['bar']
|
||||
foobarInstance.customMethod = () => undefined
|
||||
|
||||
throw foobarInstance
|
||||
})
|
||||
})
|
||||
|
||||
it('handles users throwing strings', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.equal('Error')
|
||||
expect(err.message).to.equal(`oops`)
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
throw 'oops'
|
||||
})
|
||||
})
|
||||
|
||||
it('handles users throwing arrays', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.equal('Error')
|
||||
expect(err.message).to.equal('why would anyone do this?,this is odd')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
throw ['why would anyone do this?', 'this is odd']
|
||||
})
|
||||
})
|
||||
|
||||
it('handles users throwing numbers', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.equal('Error')
|
||||
expect(err.message).to.equal('2')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
throw 2
|
||||
})
|
||||
})
|
||||
|
||||
it('handles users throwing booleans', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.equal('Error')
|
||||
expect(err.message).to.equal('true')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
throw true
|
||||
})
|
||||
})
|
||||
|
||||
it('handles users throwing null', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.equal('Error')
|
||||
expect(err.message).to.equal('null')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
throw null
|
||||
})
|
||||
})
|
||||
|
||||
it('handles users throwing undefined', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.equal('Error')
|
||||
expect(err.message).to.equal('undefined')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
throw undefined
|
||||
})
|
||||
})
|
||||
|
||||
it('handles throwing of arbitrary data types that are serializable but cannot be mapped to an error', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.name).to.equal('CypressError')
|
||||
expect(err.message).to.equal('`cy.origin()` could not serialize the thrown value. Please make sure the value being thrown is supported by the structured clone algorithm.')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
throw new Date()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,358 @@
|
||||
describe('cy.origin', () => {
|
||||
describe('successes', () => {
|
||||
it('succeeds on a localhost domain name', () => {
|
||||
cy.origin('localhost', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `https://localhost/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://localhost') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds on an ip address', () => {
|
||||
cy.origin('127.0.0.1', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `https://127.0.0.1/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://127.0.0.1') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: $Location does not support ipv6
|
||||
// https://github.com/cypress-io/cypress/issues/20970
|
||||
it.skip('succeeds on an ipv6 address', () => {
|
||||
cy.origin('0000:0000:0000:0000:0000:0000:0000:0001', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `https://[::1]/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://[::1]') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds on a unicode domain', () => {
|
||||
cy.origin('はじめよう.みんな', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `https://xn--p8j9a0d9c9a.xn--q9jyb4c/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://xn--p8j9a0d9c9a.xn--q9jyb4c') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds on a complete origin', () => {
|
||||
cy.origin('http://foobar1.com:3500', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `http://foobar1.com:3500/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ http://foobar1.com:3500') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds on a complete origin using https', () => {
|
||||
cy.origin('https://foobar2.com:3500', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `https://foobar2.com:3500/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://foobar2.com:3500') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds on a hostname and port', () => {
|
||||
cy.origin('foobar3.com:3500', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `https://foobar3.com:3500/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://foobar3.com:3500') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds on a protocol and hostname', () => {
|
||||
cy.origin('http://foobar4.com', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `http://foobar4.com/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ http://foobar4.com') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds on a subdomain', () => {
|
||||
cy.origin('app.foobar5.com', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `https://foobar5.com/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://foobar5.com') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds when only domain is passed', () => {
|
||||
cy.origin('foobar6.com', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `https://foobar6.com/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://foobar6.com') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds on a url with path', () => {
|
||||
cy.origin('http://www.foobar7.com/login', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `http://foobar7.com/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ http://foobar7.com') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds on a url with a hash', () => {
|
||||
cy.origin('http://www.foobar8.com/#hash', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `http://foobar8.com/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ http://foobar8.com') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds on a url with a path and hash', () => {
|
||||
cy.origin('http://www.foobar9.com/login/#hash', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `http://foobar9.com/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ http://foobar9.com') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds on a domain with path', () => {
|
||||
cy.origin('foobar10.com/login', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `https://foobar10.com/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://foobar10.com') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds on a domain with a hash', () => {
|
||||
cy.origin('foobar11.com/#hash', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `https://foobar11.com/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://foobar11.com') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds on a domain with a path and hash', () => {
|
||||
cy.origin('foobar12.com/login/#hash', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `https://foobar12.com/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://foobar12.com') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds on a public suffix with a subdomain', () => {
|
||||
cy.origin('app.foobar.herokuapp.com', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `https://foobar.herokuapp.com/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://foobar.herokuapp.com') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds on a machine name', () => {
|
||||
cy.origin('machine-name', () => undefined)
|
||||
cy.then(() => {
|
||||
const expectedSrc = `https://machine-name/__cypress/multi-domain-iframes`
|
||||
const iframe = window.top?.document.getElementById('Spec\ Bridge:\ https://machine-name') as HTMLIFrameElement
|
||||
|
||||
expect(iframe.src).to.equal(expectedSrc)
|
||||
})
|
||||
})
|
||||
|
||||
it('finds the right spec bridge with a subdomain', () => {
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://baz.foobar.com:3500/fixtures/auth/idp.html'
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('TJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome TJohnson')
|
||||
})
|
||||
|
||||
it('finds the correct spec bridge even if a previous spec bridge host is a subset of the current host', () => {
|
||||
// Establish a spec bridge with a 'bar.com' host prior to loading 'foobar.com'
|
||||
cy.origin('http://www.bar.com:3500', () => undefined)
|
||||
|
||||
cy.origin('http://www.app.foobar.com:3500', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
})
|
||||
})
|
||||
|
||||
it('uses cy.origin twice', () => {
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.get('[data-cy="login-idp"]').click()
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('BJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome BJohnson')
|
||||
|
||||
cy.get('[data-cy="logout"]').click()
|
||||
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://baz.foobar.com:3500/fixtures/auth/idp.html'
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('TJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome TJohnson')
|
||||
})
|
||||
|
||||
it('creates a spec bridge for https://idp.com:3502', () => {
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.origin('idp.com:3502', () => {
|
||||
cy.visit('https://www.idp.com:3502/fixtures/auth/index.html')
|
||||
cy.get('[data-cy="login-idp"]').invoke('text').should('equal', 'Login IDP')
|
||||
})
|
||||
})
|
||||
|
||||
it('creates a spec bridge for http://idp.com:3500', () => {
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.visit('http://www.idp.com:3500/fixtures/auth/index.html')
|
||||
cy.get('[data-cy="login-idp"]').invoke('text').should('equal', 'Login IDP')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('errors', () => {
|
||||
// @ts-ignore
|
||||
it('errors if experimental flag is not enabled', { experimentalSessionAndOrigin: false }, (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`cy.origin()` requires enabling the experimentalSessionAndOrigin flag')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
cy.origin()
|
||||
})
|
||||
|
||||
it('errors if passed a non-string for the origin argument', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`cy.origin()` requires the first argument to be either a url (`https://www.example.com/path`) or a domain name (`example.com`). Query parameters are not allowed. You passed: ``')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
cy.origin()
|
||||
})
|
||||
|
||||
it('errors if query params are provided', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`cy.origin()` requires the first argument to be either a url (`https://www.example.com/path`) or a domain name (`example.com`). Query parameters are not allowed. You passed: `http://www.foobar.com?foo=bar`')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://www.foobar.com?foo=bar', () => undefined)
|
||||
})
|
||||
|
||||
it('errors passing non-array to callback function', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`cy.origin()` requires the \'options\' argument to be an object. You passed: `foo`')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
cy.origin('foobar.com', 'foo', () => {})
|
||||
})
|
||||
|
||||
it('errors passing in invalid config object to callback function', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.origin()` detected extraneous keys in your options configuration.')
|
||||
expect(err.message).to.include('The extraneous keys detected were:')
|
||||
expect(err.message).to.include('> `foo, bar`')
|
||||
expect(err.message).to.include('Valid keys include the following:')
|
||||
expect(err.message).to.include('> `args`')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('foobar.com', {
|
||||
// @ts-ignore
|
||||
foo: 'foo',
|
||||
bar: 'bar',
|
||||
}, () => {})
|
||||
})
|
||||
|
||||
it('errors if passed a non-serializable args value', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('arguments specified are not serializable')
|
||||
|
||||
if (Cypress.browser.family === 'chromium') {
|
||||
expect(err.message).to.include('HTMLDivElement object could not be cloned')
|
||||
} else if (Cypress.browser.family === 'firefox') {
|
||||
expect(err.message).to.include('The object could not be cloned')
|
||||
}
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
const el = document.createElement('div')
|
||||
|
||||
cy.origin('foobar.com', { args: ['foo', '1', el] }, () => {})
|
||||
})
|
||||
|
||||
it('errors if last argument is absent', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`cy.origin()` requires the last argument to be a function. You passed: ``')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
cy.origin('foobar.com')
|
||||
})
|
||||
|
||||
it('errors if last argument is not a function', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`cy.origin()` requires the last argument to be a function. You passed: `{}`')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
cy.origin('foobar.com', {})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,257 @@
|
||||
import { assertLogLength } from '../../../support/utils'
|
||||
|
||||
describe('cy.origin yields', () => {
|
||||
let logs: any = []
|
||||
|
||||
beforeEach(() => {
|
||||
logs = []
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
logs.push(log)
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
it('yields a value', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy
|
||||
.get('[data-cy="dom-check"]')
|
||||
.invoke('text')
|
||||
}).should('equal', 'From a secondary origin')
|
||||
})
|
||||
|
||||
it('yields the cy value even if a return is present', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy
|
||||
.get('[data-cy="dom-check"]')
|
||||
.invoke('text')
|
||||
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve('text')
|
||||
}, 50)
|
||||
})
|
||||
}).should('equal', 'From a secondary origin')
|
||||
})
|
||||
|
||||
it('errors if a cy command is present and it returns a sync value', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
assertLogLength(logs, 5)
|
||||
expect(logs[4].get('error')).to.eq(err)
|
||||
expect(err.message).to.include('`cy.origin()` failed because you are mixing up async and sync code.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy
|
||||
.get('[data-cy="dom-check"]')
|
||||
.invoke('text')
|
||||
|
||||
return 'text'
|
||||
})
|
||||
})
|
||||
|
||||
it('yields synchronously', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
return 'From a secondary origin'
|
||||
}).should('equal', 'From a secondary origin')
|
||||
})
|
||||
|
||||
it('yields asynchronously', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
return new Promise((resolve: (val: string) => any, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve('From a secondary origin')
|
||||
}, 50)
|
||||
})
|
||||
}).should('equal', 'From a secondary origin')
|
||||
})
|
||||
|
||||
it('succeeds if subject cannot be serialized and is not accessed synchronously', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
return {
|
||||
symbol: Symbol(''),
|
||||
}
|
||||
}).then((obj) => {
|
||||
return 'object not accessed'
|
||||
}).should('equal', 'object not accessed')
|
||||
})
|
||||
|
||||
it('throws if subject cannot be serialized and is accessed synchronously', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
assertLogLength(logs, 7)
|
||||
expect(logs[6].get('error')).to.eq(err)
|
||||
expect(err.message).to.include('`cy.origin()` could not serialize the subject due to one of its properties not being supported by the structured clone algorithm.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
return {
|
||||
symbol: Symbol(''),
|
||||
}
|
||||
}).then((obj) => {
|
||||
// @ts-ignore
|
||||
return obj.symbol // This will fail accessing the symbol
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds if subject cannot be serialized and is not accessed', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="dom-check"]')
|
||||
})
|
||||
.then(() => {
|
||||
return 'object not accessed'
|
||||
})
|
||||
.should('equal', 'object not accessed')
|
||||
})
|
||||
|
||||
it('throws if subject cannot be serialized and is accessed', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
assertLogLength(logs, 8)
|
||||
expect(logs[7].get('error')).to.eq(err)
|
||||
expect(err.message).to.include('`cy.origin()` could not serialize the subject due to one of its properties not being supported by the structured clone algorithm.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin<JQuery>('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="dom-check"]')
|
||||
})
|
||||
.then((subject) => subject.text())
|
||||
.should('equal', 'From a secondary origin')
|
||||
})
|
||||
|
||||
it('throws if an object contains a function', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
assertLogLength(logs, 8)
|
||||
expect(logs[7].get('error')).to.eq(err)
|
||||
expect(err.message).to.include('`cy.origin()` could not serialize the subject due to one of its properties not being supported by the structured clone algorithm.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin<{ key: Function }>('http://foobar.com:3500', () => {
|
||||
cy.wrap({
|
||||
key: () => {
|
||||
return 'whoops'
|
||||
},
|
||||
})
|
||||
})
|
||||
.then((subject) => subject.key())
|
||||
.should('equal', 'whoops')
|
||||
})
|
||||
|
||||
it('throws if an object contains a symbol', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
assertLogLength(logs, 8)
|
||||
expect(logs[7].get('error')).to.eq(err)
|
||||
expect(err.message).to.include('`cy.origin()` could not serialize the subject due to one of its properties not being supported by the structured clone algorithm.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.wrap({
|
||||
key: Symbol('whoops'),
|
||||
})
|
||||
})
|
||||
.should('equal', undefined)
|
||||
})
|
||||
|
||||
it('throws if an object is a function', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
assertLogLength(logs, 8)
|
||||
expect(logs[7].get('error')).to.eq(err)
|
||||
expect(err.message).to.include('`cy.origin()` could not serialize the subject due to functions not being supported by the structured clone algorithm.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.wrap(() => {
|
||||
return 'text'
|
||||
})
|
||||
})
|
||||
.then((obj) => {
|
||||
// @ts-ignore
|
||||
obj()
|
||||
})
|
||||
})
|
||||
|
||||
it('throws if an object is a symbol', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
assertLogLength(logs, 8)
|
||||
expect(logs[7].get('error')).to.eq(err)
|
||||
expect(err.message).to.include('`cy.origin()` could not serialize the subject due to symbols not being supported by the structured clone algorithm.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.wrap(Symbol('symbol'))
|
||||
})
|
||||
.should('equal', 'symbol')
|
||||
})
|
||||
|
||||
// NOTE: Errors can only be serialized on chromium browsers.
|
||||
it('yields an error if an object contains an error', (done) => {
|
||||
const isChromium = Cypress.isBrowser({ family: 'chromium' })
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
if (!isChromium) {
|
||||
assertLogLength(logs, 8)
|
||||
expect(logs[7].get('error')).to.eq(err)
|
||||
expect(err.message).to.include('`cy.origin()` could not serialize the subject due to one of its properties not being supported by the structured clone algorithm.')
|
||||
}
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.wrap({
|
||||
key: new Error('Boom goes the dynamite'),
|
||||
})
|
||||
})
|
||||
.its('key.message')
|
||||
.should('equal', 'Boom goes the dynamite').then(() => {
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('yields an object containing valid types', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.wrap({
|
||||
array: [
|
||||
1,
|
||||
2,
|
||||
],
|
||||
undefined,
|
||||
bool: true,
|
||||
null: null,
|
||||
number: 12,
|
||||
object: {
|
||||
key: 'key',
|
||||
},
|
||||
string: 'string',
|
||||
})
|
||||
})
|
||||
.should('deep.equal', {
|
||||
array: [
|
||||
1,
|
||||
2,
|
||||
],
|
||||
undefined,
|
||||
bool: true,
|
||||
null: null,
|
||||
number: 12,
|
||||
object: {
|
||||
key: 'key',
|
||||
},
|
||||
string: 'string',
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,539 @@
|
||||
import { assertLogLength } from '../../../support/utils'
|
||||
|
||||
// makes logs coming from secondary origin work with `assertLogLength`
|
||||
const reifyLogs = (logs) => {
|
||||
return logs.map((attrs) => {
|
||||
return {
|
||||
get (name) {
|
||||
return attrs[name]
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
describe('navigation events', () => {
|
||||
let logs: any = []
|
||||
|
||||
beforeEach(() => {
|
||||
logs = []
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
logs.push(log)
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
describe('navigation:changed', () => {
|
||||
it('navigation:changed via hashChange', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const afterNavigationChanged = new Promise<void>((resolve) => {
|
||||
const listener = () => {
|
||||
cy.location().should((loc) => {
|
||||
expect(loc.host).to.equal('www.foobar.com:3500')
|
||||
expect(loc.pathname).to.equal('/fixtures/multi-domain-secondary.html')
|
||||
expect(loc.hash).to.equal('#hashChange')
|
||||
})
|
||||
|
||||
resolve()
|
||||
}
|
||||
|
||||
cy.once('navigation:changed', listener)
|
||||
})
|
||||
|
||||
cy.get('a[data-cy="hashChange"]').click()
|
||||
cy.wrap(afterNavigationChanged)
|
||||
})
|
||||
})
|
||||
|
||||
it('navigates forward and back using history', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const onLoad = (cb) => {
|
||||
const onNavChanged = (event) => {
|
||||
if (event === 'page navigation event (\'load\')') {
|
||||
cy.off('navigation:changed', onNavChanged)
|
||||
cb()
|
||||
}
|
||||
}
|
||||
|
||||
cy.on('navigation:changed', onNavChanged)
|
||||
}
|
||||
|
||||
cy.get('a[data-cy="cross-origin-page"]').click()
|
||||
.window().then((win) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
onLoad(resolve)
|
||||
|
||||
win.history.back()
|
||||
}).then(() => {
|
||||
return new Promise<void>((resolve) => {
|
||||
onLoad(resolve)
|
||||
|
||||
win.history.forward()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('window:load', () => {
|
||||
it('reloads', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const logs: any[] = []
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
logs.push(log)
|
||||
})
|
||||
|
||||
const afterWindowLoad = new Promise<void>((resolve) => {
|
||||
let times = 0
|
||||
const listener = (win) => {
|
||||
times++
|
||||
expect(win.location.host).to.equal('www.foobar.com:3500')
|
||||
expect(win.location.pathname).to.equal('/fixtures/multi-domain-secondary.html')
|
||||
|
||||
if (times === 2) {
|
||||
cy.removeListener('window:load', listener)
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
|
||||
cy.on('window:load', listener)
|
||||
})
|
||||
|
||||
cy.get('button[data-cy="reload"]').click()
|
||||
cy.wrap(afterWindowLoad).then(() => {
|
||||
return logs.map((log) => ({ name: log.get('name') }))
|
||||
})
|
||||
})
|
||||
.then(reifyLogs)
|
||||
.then((secondaryLogs) => {
|
||||
assertLogLength(secondaryLogs, 9)
|
||||
expect(secondaryLogs[5].get('name')).to.eq('page load')
|
||||
})
|
||||
})
|
||||
|
||||
it('navigates to a new page', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const logs: any[] = []
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
logs.push(log)
|
||||
})
|
||||
|
||||
const afterWindowLoad = new Promise<void>((resolve) => {
|
||||
let times = 0
|
||||
const listener = (win) => {
|
||||
times++
|
||||
if (times === 1) {
|
||||
expect(win.location.host).to.equal('www.foobar.com:3500')
|
||||
expect(win.location.pathname).to.equal('/fixtures/multi-domain-secondary.html')
|
||||
}
|
||||
|
||||
if (times === 2) {
|
||||
cy.removeListener('window:load', listener)
|
||||
expect(win.location.host).to.equal('www.foobar.com:3500')
|
||||
expect(win.location.pathname).to.equal('/fixtures/multi-domain.html')
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
|
||||
cy.on('window:load', listener)
|
||||
})
|
||||
|
||||
cy.get('a[data-cy="cross-origin-page"]').click()
|
||||
cy.get('a[data-cy="cross-origin-secondary-link').invoke('text').should('equal', 'http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
cy.wrap(afterWindowLoad).then(() => {
|
||||
return logs.map((log) => ({ name: log.get('name'), message: log.get('message') }))
|
||||
})
|
||||
})
|
||||
.then(reifyLogs)
|
||||
.then((secondaryLogs) => {
|
||||
assertLogLength(secondaryLogs, 13)
|
||||
expect(secondaryLogs[5].get('name')).to.eq('page load')
|
||||
expect(secondaryLogs[6].get('name')).to.eq('new url')
|
||||
expect(secondaryLogs[6].get('message')).to.eq('http://www.foobar.com:3500/fixtures/multi-domain.html')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('url:changed', () => {
|
||||
it('reloads', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const afterUrlChanged = new Promise<void>((resolve) => {
|
||||
cy.once('url:changed', (url) => {
|
||||
expect(url).to.equal('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
cy.get('button[data-cy="reload"]').click()
|
||||
cy.wrap(afterUrlChanged)
|
||||
})
|
||||
})
|
||||
|
||||
it('navigates to a new page', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
const afterUrlChanged = new Promise<void>((resolve) => {
|
||||
let times = 0
|
||||
const listener = (url) => {
|
||||
times++
|
||||
if (times === 1) {
|
||||
expect(url).to.equal('http://www.foobar.com:3500/fixtures/multi-domain-secondary.html')
|
||||
}
|
||||
|
||||
if (times === 2) {
|
||||
cy.removeListener('url:changed', listener)
|
||||
expect(url).to.equal('http://www.foobar.com:3500/fixtures/multi-domain.html')
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
|
||||
cy.on('url:changed', listener)
|
||||
})
|
||||
|
||||
cy.get('a[data-cy="cross-origin-page"]').click()
|
||||
cy.wrap(afterUrlChanged)
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: this test should re revisited with the cypress in cypress tests available in 10.0
|
||||
// https://github.com/cypress-io/cypress/issues/20973
|
||||
it.skip('the runner url updates appropriately', () => {
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('a[data-cy="cross-origin-page"]').click()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// @ts-ignore / session support is needed for visiting about:blank between tests
|
||||
describe('event timing', () => {
|
||||
it('does not timeout when receiving a delaying:html event after cy.origin has started, but before the spec bridge is ready', () => {
|
||||
cy.visit('/fixtures/multi-domain.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
|
||||
cy.origin('http://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/multi-domain.html'
|
||||
})
|
||||
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.log('inside cy.origin idp')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// @ts-ignore / session support is needed for visiting about:blank between tests
|
||||
describe('delayed navigation', { defaultCommandTimeout: 2000 }, () => {
|
||||
it('localhost -> localhost', () => {
|
||||
cy.visit('/fixtures/auth/delayedNavigate.html')
|
||||
cy.get('[data-cy="to-localhost"]').click()
|
||||
cy.get('[data-cy="login-idp"]')
|
||||
})
|
||||
|
||||
it('localhost -> foobar, delay in', () => {
|
||||
cy.visit('/fixtures/auth/delayedNavigate.html')
|
||||
cy.get('[data-cy="to-foobar"]').click()
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="login-idp"]')
|
||||
})
|
||||
})
|
||||
|
||||
it('foobar -> localhost, delay out', () => {
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/auth/delayedNavigate.html')
|
||||
cy.get('[data-cy="to-localhost"]').click()
|
||||
})
|
||||
|
||||
cy.get('[data-cy="login-idp"]')
|
||||
})
|
||||
|
||||
it('foobar -> idp, delay out', () => {
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/auth/delayedNavigate.html')
|
||||
cy.get('[data-cy="to-idp"]').click()
|
||||
})
|
||||
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.get('[data-cy="login-idp"]')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// @ts-ignore / session support is needed for visiting about:blank between tests
|
||||
describe('errors', () => {
|
||||
it('never calls cy.origin', { pageLoadTimeout: 5000 }, (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out after waiting \`5000ms\` for your remote page to load on origin(s):`)
|
||||
expect(err.message).to.include(`\n- \`http://localhost:3500\`\n`)
|
||||
expect(err.message).to.include(`A cross-origin request for \`http://www.foobar.com:3500/fixtures/auth/idp.html?redirect=http%3A%2F%2Flocalhost%3A3500%2Ffixtures%2Fauth%2Findex.html\` was detected.`)
|
||||
expect(err.message).to.include(`A command that triggers cross-origin navigation must be immediately followed by a \`cy.origin()\` command:`)
|
||||
expect(err.message).to.include(`\`cy.origin(\'http://foobar.com:3500\', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})\`\n`)
|
||||
expect(err.message).to.include(`If the cross-origin request was an intermediary state, you can try increasing the \`pageLoadTimeout\` value in \`cypress.json\` to wait longer`)
|
||||
|
||||
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()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.get('[data-cy="login-foobar"]').click() // Timeout on page load here, we never reach the expected origin
|
||||
cy.get('[data-cy="login-foobar"]')
|
||||
})
|
||||
|
||||
it('never redirects to the cross-origin', { defaultCommandTimeout: 50 }, (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms: Expected to find element: \`[data-cy="username"]\`, but never found it`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://idp.com:3500\` but the application is at origin \`http://localhost:3500\`.`)
|
||||
expect(err.message).to.include(`This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.`)
|
||||
// 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()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.get('[data-cy="login-idp"]')
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('BJohnson') // Timeout here on command, cannot find element
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome BJohnson')
|
||||
})
|
||||
|
||||
it('redirects to the wrong cross-origin', { pageLoadTimeout: 5000 }, (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out after waiting \`5000ms\` for your remote page to load on origin(s):`)
|
||||
expect(err.message).to.include(`\n- \`http://idp.com:3500\`\n`)
|
||||
expect(err.message).to.include(`A cross-origin request for \`http://www.foobar.com:3500/fixtures/auth/idp.html?redirect=http%3A%2F%2Flocalhost%3A3500%2Ffixtures%2Fauth%2Findex.html\` was detected.`)
|
||||
expect(err.message).to.include(`\`cy.origin(\'http://foobar.com:3500\', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})\`\n`)
|
||||
|
||||
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()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.get('[data-cy="login-foobar"]').click() // Timeout on page load here, we never reach the expected origin
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('BJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
// Verify that the user has logged in on localhost
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome BJohnson')
|
||||
})
|
||||
|
||||
it('never returns to the primary origin', { defaultCommandTimeout: 50 }, (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms: Expected to find element: \`[data-cy="welcome"]\`, but never found it`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://localhost:3500\` but the application is at origin \`http://idp.com:3500\`.`)
|
||||
// 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()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.get('[data-cy="login-idp"]').click()
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('BJohnson')
|
||||
}) // cy.origin is stable so the command exits
|
||||
|
||||
// Verify that the user has logged in on localhost
|
||||
cy.get('[data-cy="welcome"]') // Timeout here on command, cannot find element
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome BJohnson')
|
||||
})
|
||||
|
||||
it('redirects to an unexpected cross-origin', { pageLoadTimeout: 5000 }, (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out after waiting \`5000ms\` for your remote page to load on origin(s):`)
|
||||
expect(err.message).to.include(`\n- \`http://localhost:3500\`\n`)
|
||||
expect(err.message).to.include(`A cross-origin request for \`http://www.foobar.com:3500/fixtures/auth/index.html\` was detected.`)
|
||||
expect(err.message).to.include(`\`cy.origin(\'http://foobar.com:3500\', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})\`\n`)
|
||||
|
||||
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()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.get('[data-cy="login-idp"]').click()
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('BJohnson')
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://www.foobar.com:3500/fixtures/auth/index.html'
|
||||
})
|
||||
})
|
||||
|
||||
// Verify that the user has logged in on localhost
|
||||
cy.get('[data-cy="welcome"]') // Stability is false, this command is prevented from running until stability is achieved.
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome BJohnson')
|
||||
})
|
||||
|
||||
it('redirects to an unexpected cross-origin and calls another command in the cy.origin command', { pageLoadTimeout: 5000 }, (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out after waiting \`5000ms\` for your remote page to load on origin(s):`)
|
||||
expect(err.message).to.include(`\n- \`http://idp.com:3500\`\n`)
|
||||
expect(err.message).to.include(`A cross-origin request for \`http://www.foobar.com:3500/fixtures/auth/index.html\` was detected.`)
|
||||
expect(err.message).to.include(`\`cy.origin(\'http://foobar.com:3500\', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})\`\n`)
|
||||
|
||||
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()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.get('[data-cy="login-idp"]').click()
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('BJohnson')
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://www.foobar.com:3500/fixtures/auth/index.html'
|
||||
})
|
||||
|
||||
cy.get('[data-cy="welcome"]') // Timeout here on command, cannot find element
|
||||
})
|
||||
|
||||
// Verify that the user has logged in on localhost
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome BJohnson')
|
||||
})
|
||||
|
||||
it('fails in cy.origin when a command is run after we return to localhost', { defaultCommandTimeout: 50 }, (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms: Expected to find element: \`[data-cy="cannot_find"]\`, but never found it`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://idp.com:3500\` but the application is at origin \`http://localhost:3500\`.`)
|
||||
// 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()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/auth/index.html') // Establishes primary origin
|
||||
cy.get('[data-cy="login-idp"]').click() // Takes you to idp.com
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('BJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
cy.get('[data-cy="cannot_find"]') // Timeout here on command stability achieved by primary origin, this command times out.
|
||||
})
|
||||
|
||||
// Verify that the user has logged in on localhost
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome BJohnson')
|
||||
})
|
||||
|
||||
it('fails with a normal timeout', { defaultCommandTimeout: 50 }, (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms: Expected to find element: \`[data-cy="cannot_find"]\`, but never found it`)
|
||||
// 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`)
|
||||
expect(err.message).not.to.include(`The command was expected to run against origin:`)
|
||||
done()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/auth/index.html') // Establishes primary origin
|
||||
cy.get('[data-cy="login-idp"]').click() // Takes you to idp.com
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.get('[data-cy="cannot_find"]') // Timeout here on command stability achieved by primary origin, this command times out.
|
||||
})
|
||||
})
|
||||
|
||||
describe('Pre established spec bridge', () => {
|
||||
// These next three tests test and edge case where we want to prevent a load event from an established spec bridge that is not part of the test.
|
||||
// This test removes the foobar spec bridge, navigates to idp, then navigates to foobar and attempts to access selectors on localhost.
|
||||
it('times out in cy.origin with foobar spec bridge undefined', { pageLoadTimeout: 5000 }, (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out after waiting \`5000ms\` for your remote page to load on origin(s):`)
|
||||
expect(err.message).to.include(`\n- \`http://localhost:3500\`\n`)
|
||||
expect(err.message).to.include(`A cross-origin request for \`http://www.foobar.com:3500/fixtures/auth/index.html\` was detected.`)
|
||||
expect(err.message).to.include(`\`cy.origin(\'http://foobar.com:3500\', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})\`\n`)
|
||||
|
||||
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()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/auth/index.html') // Establishes primary origin
|
||||
cy.window().then(() => {
|
||||
// Force remove the spec bridge
|
||||
window?.top?.document.getElementById('Spec Bridge: foobar.com')?.remove()
|
||||
})
|
||||
|
||||
cy.get('[data-cy="login-idp"]').click() // Takes you to idp.com
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('BJohnson')
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://www.foobar.com:3500/fixtures/auth/index.html'
|
||||
})//
|
||||
})
|
||||
|
||||
// Verify that the user has logged in on localhost
|
||||
cy.get('[data-cy="welcome"]') // Stability is false, this command is prevented from running until stability is achieved.
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome BJohnson')
|
||||
})
|
||||
|
||||
// this test just needs to establish the foobar spec bridge.
|
||||
it('establishes foobar spec bridge', () => {
|
||||
cy.visit('/fixtures/auth/index.html') // Establishes primary origin
|
||||
cy.get('[data-cy="login-foobar"]').click() // Takes you to idp.com
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('BJohnson')
|
||||
cy.get('[data-cy="login"]').click()
|
||||
})
|
||||
|
||||
// Verify that the user has logged in on localhost
|
||||
cy.get('[data-cy="welcome"]')
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome BJohnson')
|
||||
})
|
||||
|
||||
// This test is the same as the first test but the foobar spec bridge has been established and we expect it to behave the same as the first test.
|
||||
// The primary origin should ignore the load event from the foobar spec bridge and load should timeout in the idp cy.origin command..
|
||||
it('times out in cy.origin with foobar spec bridge defined', { pageLoadTimeout: 5000 }, (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out after waiting \`5000ms\` for your remote page to load on origin(s):`)
|
||||
expect(err.message).to.include(`\n- \`http://localhost:3500\`\n`)
|
||||
expect(err.message).to.include(`A cross-origin request for \`http://www.foobar.com:3500/fixtures/auth/index.html\` was detected.`)
|
||||
expect(err.message).to.include(`\`cy.origin(\'http://foobar.com:3500\', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})\`\n`)
|
||||
|
||||
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()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/auth/index.html') // Establishes primary origin
|
||||
cy.get('[data-cy="login-idp"]').click() // Takes you to idp.com
|
||||
cy.origin('http://idp.com:3500', () => {
|
||||
cy.get('[data-cy="username"]').type('BJohnson')
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://www.foobar.com:3500/fixtures/auth/index.html'
|
||||
})
|
||||
})
|
||||
|
||||
// Verify that the user has logged in on localhost
|
||||
cy.get('[data-cy="welcome"]') // Stability is false, this command is prevented from running until stability is achieved.
|
||||
.invoke('text')
|
||||
.should('equal', 'Welcome BJohnson')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
describe('stability', () => {
|
||||
describe('before each transitions', () => {
|
||||
describe('transitioning from a before block to an it block while unstable', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/auth/index.html')
|
||||
cy.window().then((win) => {
|
||||
win.location.href = 'http://localhost:3500/timeout?ms=1000'
|
||||
})
|
||||
})
|
||||
|
||||
it('fails if the page does not load within the page load timeout', { defaultCommandTimeout: 50, pageLoadTimeout: 500 }, (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out after waiting \`500ms\` for your remote page to load.`)
|
||||
done()
|
||||
})
|
||||
|
||||
cy.get('[data-cy="login-idp"]').click() // Takes you to idp.com
|
||||
})
|
||||
|
||||
it('waits for the page to load before running the command', { defaultCommandTimeout: 50 }, () => {
|
||||
cy.get('body').invoke('text').should('equal', 'timeout')
|
||||
})
|
||||
|
||||
it('will retry and fail the command after the page loads', { defaultCommandTimeout: 50 }, (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms: expected 'timeout' to equal 'not timeout'`)
|
||||
done()
|
||||
})
|
||||
|
||||
cy.get('body').invoke('text').should('equal', 'not timeout')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,9 +1,12 @@
|
||||
let changed = 0
|
||||
|
||||
describe('src/cy/commands/actions/type text_mask_spec', () => {
|
||||
before(() => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/text-mask.html')
|
||||
|
||||
// reset number of change events before test
|
||||
changed = 0
|
||||
|
||||
// count the number of change events
|
||||
return cy.get('input').then(($els) => {
|
||||
return $els.change(($el) => {
|
||||
@@ -12,11 +15,6 @@ describe('src/cy/commands/actions/type text_mask_spec', () => {
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
// reset number of change events before test
|
||||
changed = 0
|
||||
})
|
||||
|
||||
context('#type', () => {
|
||||
it('can type into phone', () => {
|
||||
cy.get('#phone')
|
||||
|
||||
@@ -2,26 +2,16 @@
|
||||
const { $ } = Cypress
|
||||
|
||||
describe('issue 3890 overwriting cy.route command', () => {
|
||||
before(() => {
|
||||
beforeEach(function () {
|
||||
cy
|
||||
.visit('/fixtures/jquery.html')
|
||||
.then(function (win) {
|
||||
const h = $(win.document.head)
|
||||
|
||||
h.find('script').remove()
|
||||
|
||||
this.head = h.prop('outerHTML')
|
||||
this.body = win.document.body.outerHTML
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
const doc = cy.state('document')
|
||||
|
||||
$(doc.head).empty().html(this.head)
|
||||
$(doc.body).empty().html(this.body)
|
||||
})
|
||||
|
||||
it('stores route as an alias', () => {
|
||||
// sanity test before overwriting cy.route in the next test
|
||||
cy
|
||||
|
||||
@@ -201,4 +201,16 @@ describe('src/util/queue', () => {
|
||||
expect(queue.stopped).to.false
|
||||
})
|
||||
})
|
||||
|
||||
context('.last', () => {
|
||||
it('returns the last item', () => {
|
||||
expect(queue.last()).to.deep.equal({ id: '3' })
|
||||
|
||||
queue.add({ id: '4' })
|
||||
expect(queue.last()).to.deep.equal({ id: '4' })
|
||||
|
||||
queue.clear()
|
||||
expect(queue.last()).to.equal(undefined)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,688 @@
|
||||
import { reifyDomElement, preprocessDomElement, preprocessLogLikeForSerialization, preprocessLogForSerialization, reifyLogFromSerialization } from '../../../src/util/serialization/log'
|
||||
|
||||
describe('Log Serialization', () => {
|
||||
const buildSnapshot = (innerSnapshotElement) => {
|
||||
const mockSnapshot = document.createElement('body')
|
||||
|
||||
// populate some items into the mockSnapshot that would mimic what the DOM might actually look like, along with our inner snapshot element
|
||||
const mockContainer = document.createElement('div')
|
||||
const mockInnerHeader = document.createElement('h1')
|
||||
const mockTextNode = document.createTextNode('Mock Snapshot Header')
|
||||
|
||||
mockInnerHeader.appendChild(mockTextNode)
|
||||
mockContainer.appendChild(mockInnerHeader)
|
||||
mockContainer.appendChild(innerSnapshotElement)
|
||||
|
||||
mockSnapshot.appendChild(mockContainer)
|
||||
|
||||
return mockSnapshot
|
||||
}
|
||||
|
||||
it('preprocesses complex log-like data structures by preprocessing log DOM elements and table functions', () => {
|
||||
const mockSpan = document.createElement('span')
|
||||
|
||||
mockSpan.innerHTML = 'click button'
|
||||
|
||||
const mockButton = document.createElement('button')
|
||||
|
||||
mockButton.appendChild(mockSpan)
|
||||
|
||||
const mockClickedElement = document.createElement('form')
|
||||
|
||||
mockClickedElement.appendChild(mockButton)
|
||||
mockClickedElement.id = 'button-inside-a'
|
||||
|
||||
const mockSnapshot = buildSnapshot(mockClickedElement)
|
||||
|
||||
const mockSnapshots = ['before', 'after'].map((snapshotName) => {
|
||||
return {
|
||||
name: snapshotName,
|
||||
htmlAttrs: {},
|
||||
body: {
|
||||
get: () => Cypress.$(mockSnapshot),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
// mockLogAttrs should look just like log attributes that are emitted from log:changed/log:added events. This example is what a 'click' log may look like
|
||||
const mockLogAttrs = {
|
||||
$el: Cypress.$(mockClickedElement),
|
||||
alias: undefined,
|
||||
chainerId: 'mock-chainer-id',
|
||||
consoleProps: {
|
||||
['Applied To']: mockClickedElement,
|
||||
Command: 'click',
|
||||
Coords: {
|
||||
x: 100,
|
||||
y: 50,
|
||||
},
|
||||
Options: undefined,
|
||||
Yielded: undefined,
|
||||
table: {
|
||||
1: () => {
|
||||
return {
|
||||
name: 'Mouse Events',
|
||||
// NOTE: click data length is truncated for test readability
|
||||
data: [
|
||||
{
|
||||
'Active Modifiers': null,
|
||||
'Event Type': 'pointerover',
|
||||
'Prevented Default': null,
|
||||
'Stopped Propagation': null,
|
||||
'Target Element': mockClickedElement,
|
||||
},
|
||||
{
|
||||
'Active Modifiers': null,
|
||||
'Event Type': 'mouseover',
|
||||
'Prevented Default': null,
|
||||
'Stopped Propagation': null,
|
||||
'Target Element': mockClickedElement,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
coords: { top: 50, left: 50, topCenter: 100, leftCenter: 1000, x: 100, y: 50 },
|
||||
ended: true,
|
||||
err: undefined,
|
||||
event: false,
|
||||
highlightAttr: 'data-cypress-el',
|
||||
hookId: 'r4',
|
||||
id: 'mock-log-id',
|
||||
instrument: 'command',
|
||||
message: '',
|
||||
name: 'click',
|
||||
numElements: 1,
|
||||
referencesAlias: undefined,
|
||||
renderProps: {},
|
||||
snapshots: mockSnapshots,
|
||||
state: 'passed',
|
||||
testCurrentRetry: 0,
|
||||
testId: 'r4',
|
||||
timeout: 4000,
|
||||
type: 'child',
|
||||
url: 'http://www.foobar.com',
|
||||
viewportHeight: 660,
|
||||
viewportWidth: 1000,
|
||||
visible: true,
|
||||
wallClockStartedAt: '2022-04-18T21:52:37.833Z',
|
||||
}
|
||||
|
||||
const { consoleProps, snapshots, $el, ...logAttrs } = preprocessLogForSerialization(mockLogAttrs)
|
||||
|
||||
expect(logAttrs).to.deep.equal({
|
||||
alias: undefined,
|
||||
chainerId: 'mock-chainer-id',
|
||||
coords: { top: 50, left: 50, topCenter: 100, leftCenter: 1000, x: 100, y: 50 },
|
||||
ended: true,
|
||||
err: undefined,
|
||||
event: false,
|
||||
highlightAttr: 'data-cypress-el',
|
||||
hookId: 'r4',
|
||||
id: 'mock-log-id',
|
||||
instrument: 'command',
|
||||
message: '',
|
||||
name: 'click',
|
||||
numElements: 1,
|
||||
referencesAlias: undefined,
|
||||
renderProps: {},
|
||||
state: 'passed',
|
||||
testCurrentRetry: 0,
|
||||
testId: 'r4',
|
||||
timeout: 4000,
|
||||
type: 'child',
|
||||
url: 'http://www.foobar.com',
|
||||
viewportHeight: 660,
|
||||
viewportWidth: 1000,
|
||||
visible: true,
|
||||
wallClockStartedAt: '2022-04-18T21:52:37.833Z',
|
||||
})
|
||||
|
||||
expect($el).to.deep.equal([
|
||||
{
|
||||
attributes: {
|
||||
id: 'button-inside-a',
|
||||
},
|
||||
innerHTML: '<button><span>click button</span></button>',
|
||||
serializationKey: 'dom',
|
||||
tagName: 'FORM',
|
||||
},
|
||||
])
|
||||
|
||||
expect(consoleProps).to.deep.equal({
|
||||
['Applied To']: {
|
||||
attributes: {
|
||||
id: 'button-inside-a',
|
||||
},
|
||||
innerHTML: '<button><span>click button</span></button>',
|
||||
serializationKey: 'dom',
|
||||
tagName: 'FORM',
|
||||
},
|
||||
Command: 'click',
|
||||
Coords: {
|
||||
x: 100,
|
||||
y: 50,
|
||||
},
|
||||
Options: undefined,
|
||||
Yielded: undefined,
|
||||
table: {
|
||||
1: {
|
||||
serializationKey: 'function',
|
||||
value: {
|
||||
name: 'Mouse Events',
|
||||
data: [
|
||||
{
|
||||
'Active Modifiers': null,
|
||||
'Event Type': 'pointerover',
|
||||
'Prevented Default': null,
|
||||
'Stopped Propagation': null,
|
||||
'Target Element': {
|
||||
attributes: {
|
||||
id: 'button-inside-a',
|
||||
},
|
||||
innerHTML: '<button><span>click button</span></button>',
|
||||
serializationKey: 'dom',
|
||||
tagName: 'FORM',
|
||||
},
|
||||
},
|
||||
{
|
||||
'Active Modifiers': null,
|
||||
'Event Type': 'mouseover',
|
||||
'Prevented Default': null,
|
||||
'Stopped Propagation': null,
|
||||
'Target Element': {
|
||||
attributes: {
|
||||
id: 'button-inside-a',
|
||||
},
|
||||
innerHTML: '<button><span>click button</span></button>',
|
||||
serializationKey: 'dom',
|
||||
tagName: 'FORM',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(snapshots).to.deep.equal([
|
||||
{
|
||||
name: 'before',
|
||||
htmlAttrs: {},
|
||||
styles: {},
|
||||
body: {
|
||||
get: {
|
||||
serializationKey: 'function',
|
||||
value: [{
|
||||
attributes: {},
|
||||
innerHTML: `<div><h1>Mock Snapshot Header</h1><form id="button-inside-a"><button><span>click button</span></button></form></div>`,
|
||||
serializationKey: 'dom',
|
||||
tagName: 'BODY',
|
||||
}],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'after',
|
||||
htmlAttrs: {},
|
||||
styles: {},
|
||||
body: {
|
||||
get: {
|
||||
serializationKey: 'function',
|
||||
value: [{
|
||||
attributes: {},
|
||||
innerHTML: `<div><h1>Mock Snapshot Header</h1><form id="button-inside-a"><button><span>click button</span></button></form></div>`,
|
||||
serializationKey: 'dom',
|
||||
tagName: 'BODY',
|
||||
}],
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('reifies complex log-like data structures by reifying serialized DOM elements and table functions back into native data types, respectively', () => {
|
||||
// this should log identical to the test output above from what a preprocessed click log looks like after postMessage()
|
||||
const mockPreprocessedLogAttrs = {
|
||||
$el: [
|
||||
{
|
||||
attributes: {
|
||||
id: 'button-inside-a',
|
||||
},
|
||||
innerHTML: '<button><span>click button</span></button>',
|
||||
serializationKey: 'dom',
|
||||
tagName: 'FORM',
|
||||
},
|
||||
],
|
||||
alias: undefined,
|
||||
chainerId: 'mock-chainer-id',
|
||||
consoleProps: {
|
||||
['Applied To']: {
|
||||
attributes: {
|
||||
id: 'button-inside-a',
|
||||
},
|
||||
innerHTML: '<button><span>click button</span></button>',
|
||||
serializationKey: 'dom',
|
||||
tagName: 'FORM',
|
||||
},
|
||||
Command: 'click',
|
||||
Coords: {
|
||||
x: 100,
|
||||
y: 50,
|
||||
},
|
||||
Options: undefined,
|
||||
Yielded: undefined,
|
||||
table: {
|
||||
1: {
|
||||
serializationKey: 'function',
|
||||
value: {
|
||||
name: 'Mouse Events',
|
||||
// NOTE: click data length is truncated for test readability
|
||||
data: [
|
||||
{
|
||||
'Active Modifiers': null,
|
||||
'Event Type': 'pointerover',
|
||||
'Prevented Default': null,
|
||||
'Stopped Propagation': null,
|
||||
'Target Element': {
|
||||
attributes: {
|
||||
id: 'button-inside-a',
|
||||
},
|
||||
innerHTML: '<button><span>click button</span></button>',
|
||||
serializationKey: 'dom',
|
||||
tagName: 'FORM',
|
||||
},
|
||||
},
|
||||
{
|
||||
'Active Modifiers': null,
|
||||
'Event Type': 'mouseover',
|
||||
'Prevented Default': null,
|
||||
'Stopped Propagation': null,
|
||||
'Target Element': {
|
||||
attributes: {
|
||||
id: 'button-inside-a',
|
||||
},
|
||||
innerHTML: '<button><span>click button</span></button>',
|
||||
serializationKey: 'dom',
|
||||
tagName: 'FORM',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
coords: { top: 50, left: 50, topCenter: 100, leftCenter: 1000, x: 100, y: 50 },
|
||||
ended: true,
|
||||
err: undefined,
|
||||
event: false,
|
||||
highlightAttr: 'data-cypress-el',
|
||||
hookId: 'r4',
|
||||
id: 'mock-log-id',
|
||||
instrument: 'command',
|
||||
message: '',
|
||||
name: 'click',
|
||||
numElements: 1,
|
||||
referencesAlias: undefined,
|
||||
renderProps: {},
|
||||
snapshots: [
|
||||
{
|
||||
name: 'before',
|
||||
htmlAttrs: {},
|
||||
styles: {},
|
||||
body: {
|
||||
get: {
|
||||
serializationKey: 'function',
|
||||
value: [{
|
||||
attributes: {},
|
||||
innerHTML: `<div><h1>Mock Snapshot Header</h1><form id="button-inside-a"><button><span>click button</span></button></form></div>`,
|
||||
serializationKey: 'dom',
|
||||
tagName: 'BODY',
|
||||
}],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'after',
|
||||
htmlAttrs: {},
|
||||
styles: {},
|
||||
body: {
|
||||
get: {
|
||||
serializationKey: 'function',
|
||||
value: [{
|
||||
attributes: {},
|
||||
innerHTML: `<div><h1>Mock Snapshot Header</h1><form id="button-inside-a"><button><span>click button</span></button></form></div>`,
|
||||
serializationKey: 'dom',
|
||||
tagName: 'BODY',
|
||||
}],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
state: 'passed',
|
||||
testCurrentRetry: 0,
|
||||
testId: 'r4',
|
||||
timeout: 4000,
|
||||
type: 'child',
|
||||
url: 'http://www.foobar.com',
|
||||
viewportHeight: 660,
|
||||
viewportWidth: 1000,
|
||||
visible: true,
|
||||
wallClockStartedAt: '2022-04-18T21:52:37.833Z',
|
||||
}
|
||||
|
||||
const { consoleProps, snapshots, $el, ...logAttrs } = reifyLogFromSerialization(mockPreprocessedLogAttrs)
|
||||
|
||||
expect(logAttrs).to.deep.equal({
|
||||
alias: undefined,
|
||||
chainerId: 'mock-chainer-id',
|
||||
coords: { top: 50, left: 50, topCenter: 100, leftCenter: 1000, x: 100, y: 50 },
|
||||
ended: true,
|
||||
err: undefined,
|
||||
event: false,
|
||||
highlightAttr: 'data-cypress-el',
|
||||
hookId: 'r4',
|
||||
id: 'mock-log-id',
|
||||
instrument: 'command',
|
||||
message: '',
|
||||
name: 'click',
|
||||
numElements: 1,
|
||||
referencesAlias: undefined,
|
||||
renderProps: {},
|
||||
state: 'passed',
|
||||
testCurrentRetry: 0,
|
||||
testId: 'r4',
|
||||
timeout: 4000,
|
||||
type: 'child',
|
||||
url: 'http://www.foobar.com',
|
||||
viewportHeight: 660,
|
||||
viewportWidth: 1000,
|
||||
visible: true,
|
||||
wallClockStartedAt: '2022-04-18T21:52:37.833Z',
|
||||
})
|
||||
|
||||
expect($el.jquery).to.be.ok
|
||||
expect($el.length).to.equal(1)
|
||||
expect($el[0]).to.be.instanceOf(HTMLFormElement)
|
||||
expect($el[0].id).to.equal('button-inside-a')
|
||||
expect($el[0].textContent).to.equal('click button')
|
||||
|
||||
// most of the consoleProps logic is tested in the e2e/multi-domain folder. focus in this test will be mostly snapshot serialization
|
||||
expect(consoleProps['Applied To']).to.be.instanceOf(HTMLFormElement)
|
||||
expect(consoleProps['Applied To']).to.have.property('id').that.equals('button-inside-a')
|
||||
expect(consoleProps['Applied To']).to.have.property('textContent').that.equals('click button')
|
||||
|
||||
expect(consoleProps.table).to.have.property('1')
|
||||
expect(consoleProps.table[1]).to.be.a('function')
|
||||
|
||||
expect(snapshots).to.have.lengthOf(2)
|
||||
|
||||
expect(snapshots[0]).to.have.property('name').that.equals('before')
|
||||
expect(snapshots[0]).to.have.property('htmlAttrs').that.deep.equals({})
|
||||
// styles should now live in the CSS map after a snapshot is processed through createSnapshot and snapshots should exist in document map
|
||||
expect(snapshots[0]).to.not.have.property('styles')
|
||||
expect(snapshots[0]).to.have.property('body').that.has.property('get').that.is.a('function')
|
||||
|
||||
const snapshotBodyBefore = snapshots[0].body.get()
|
||||
|
||||
expect(snapshotBodyBefore.length).to.equal(1)
|
||||
|
||||
expect(snapshotBodyBefore[0]).to.be.instanceOf(HTMLBodyElement)
|
||||
// verify to some degree that the reified elements above can be matched into the snapshot
|
||||
expect(snapshotBodyBefore[0].querySelector('form#button-inside-a')).to.be.instanceOf(HTMLFormElement)
|
||||
|
||||
expect(snapshots[1]).to.have.property('name').that.equals('after')
|
||||
expect(snapshots[1]).to.have.property('htmlAttrs').that.deep.equals({})
|
||||
expect(snapshots[1]).to.not.have.property('styles')
|
||||
expect(snapshots[1]).to.have.property('body').that.has.property('get').that.is.a('function')
|
||||
|
||||
const snapshotBodyAfter = snapshots[1].body.get()
|
||||
|
||||
expect(snapshotBodyAfter.length).to.equal(1)
|
||||
|
||||
expect(snapshotBodyAfter[0]).to.be.instanceOf(HTMLBodyElement)
|
||||
// verify to some degree that the reified elements above can be matched into the snapshot
|
||||
expect(snapshotBodyAfter[0].querySelector('form#button-inside-a')).to.be.instanceOf(HTMLFormElement)
|
||||
})
|
||||
|
||||
// purpose of these 'DOM Elements' tests is to give a very basic understanding of how DOM element serialization works in the log serializer
|
||||
context('DOM Elements- preprocesses/reifies a given DOM element with stateful', () => {
|
||||
context('input', () => {
|
||||
it('preprocess', () => {
|
||||
const inputElement = document.createElement('input')
|
||||
|
||||
inputElement.type = 'text'
|
||||
inputElement.value = 'foo'
|
||||
inputElement.setAttribute('data-cy', 'bar')
|
||||
|
||||
const snapshot = buildSnapshot(inputElement)
|
||||
|
||||
snapshot.setAttribute('foo', 'bar')
|
||||
|
||||
const preprocessedSnapshot = preprocessDomElement(snapshot)
|
||||
|
||||
expect(preprocessedSnapshot).to.have.property('tagName').that.equals('BODY')
|
||||
expect(preprocessedSnapshot).to.have.property('serializationKey').that.equals('dom')
|
||||
expect(preprocessedSnapshot).to.have.property('attributes').that.deep.equals({
|
||||
foo: 'bar',
|
||||
})
|
||||
|
||||
expect(preprocessedSnapshot).to.have.property('innerHTML').that.equals(`<div><h1>Mock Snapshot Header</h1><input type="text" data-cy="bar" value="foo"></div>`)
|
||||
})
|
||||
|
||||
it('reifies', () => {
|
||||
const preprocessedSnapshot = {
|
||||
tagName: 'BODY',
|
||||
serializationKey: 'dom',
|
||||
attributes: {
|
||||
foo: 'bar',
|
||||
},
|
||||
innerHTML: `<div><h1>Mock Snapshot Header</h1><input type="text" data-cy="bar" value="foo"></div>`,
|
||||
}
|
||||
|
||||
const reifiedSnapshot = reifyDomElement(preprocessedSnapshot)
|
||||
|
||||
expect(reifiedSnapshot).to.be.instanceOf(HTMLBodyElement)
|
||||
expect(reifiedSnapshot.getAttribute('foo')).to.equal('bar')
|
||||
expect(reifiedSnapshot.querySelector('input[type="text"][value="foo"][data-cy="bar"]')).to.be.instanceOf(HTMLInputElement)
|
||||
})
|
||||
})
|
||||
|
||||
context('select', () => {
|
||||
it('preprocess', () => {
|
||||
const selectElement = document.createElement('select')
|
||||
|
||||
selectElement.id = 'metasyntactic-variables'
|
||||
selectElement.name = 'Metasyntactic Variables'
|
||||
|
||||
const options = ['Hank Hill', 'Buck Strickland', 'Donna', 'Old Donna'].map((val) => {
|
||||
const option = document.createElement('option')
|
||||
|
||||
option.value = val
|
||||
|
||||
return option
|
||||
})
|
||||
|
||||
options.forEach((option) => selectElement.appendChild(option))
|
||||
|
||||
selectElement.selectedIndex = 1
|
||||
|
||||
const snapshot = buildSnapshot(selectElement)
|
||||
|
||||
const preprocessedSnapshot = preprocessDomElement(snapshot)
|
||||
|
||||
expect(preprocessedSnapshot).to.have.property('tagName').that.equals('BODY')
|
||||
expect(preprocessedSnapshot).to.have.property('serializationKey').that.equals('dom')
|
||||
expect(preprocessedSnapshot).to.have.property('innerHTML').that.equals(`<div><h1>Mock Snapshot Header</h1><select id="metasyntactic-variables" name="Metasyntactic Variables"><option value="Hank Hill"></option><option value="Buck Strickland" selected="true"></option><option value="Donna"></option><option value="Old Donna"></option></select></div>`)
|
||||
})
|
||||
|
||||
it('reifies', () => {
|
||||
const preprocessedSnapshot = {
|
||||
tagName: 'BODY',
|
||||
serializationKey: 'dom',
|
||||
attributes: {},
|
||||
innerHTML: `<div><h1>Mock Snapshot Header</h1><select id="metasyntactic-variables" name="Metasyntactic Variables"><option value="Hank Hill"></option><option value="Buck Strickland" selected="true"></option><option value="Donna"></option><option value="Old Donna"></option></select></div>`,
|
||||
}
|
||||
|
||||
const reifiedSnapshot = reifyDomElement(preprocessedSnapshot)
|
||||
|
||||
expect(reifiedSnapshot).to.be.instanceOf(HTMLBodyElement)
|
||||
expect(reifiedSnapshot.querySelector('select#metasyntactic-variables option[selected]')).to.have.property('value').that.equals('Buck Strickland')
|
||||
})
|
||||
})
|
||||
|
||||
context('textarea', () => {
|
||||
it('preprocess', () => {
|
||||
const textAreaElement = document.createElement('textarea')
|
||||
|
||||
textAreaElement.rows = 4
|
||||
textAreaElement.cols = 20
|
||||
textAreaElement.value = 'Generic variable names that function as placeholders'
|
||||
|
||||
const snapshot = buildSnapshot(textAreaElement)
|
||||
|
||||
const preprocessedSnapshot = preprocessDomElement(snapshot)
|
||||
|
||||
expect(preprocessedSnapshot).to.have.property('tagName').that.equals('BODY')
|
||||
expect(preprocessedSnapshot).to.have.property('serializationKey').that.equals('dom')
|
||||
expect(preprocessedSnapshot).to.have.property('innerHTML').that.equals(`<div><h1>Mock Snapshot Header</h1><textarea rows="4" cols="20">Generic variable names that function as placeholders</textarea></div>`)
|
||||
})
|
||||
|
||||
it('reifies', () => {
|
||||
const preprocessedSnapshot = {
|
||||
tagName: 'BODY',
|
||||
serializationKey: 'dom',
|
||||
attributes: {},
|
||||
innerHTML: `<div><h1>Mock Snapshot Header</h1><textarea rows="4" cols="20">Generic variable names that function as placeholders</textarea></div>`,
|
||||
}
|
||||
|
||||
const reifiedSnapshot = reifyDomElement(preprocessedSnapshot)
|
||||
|
||||
expect(reifiedSnapshot).to.be.instanceOf(HTMLBodyElement)
|
||||
expect(reifiedSnapshot.querySelector('textarea[rows="4"]')).to.have.property('textContent').that.equals('Generic variable names that function as placeholders')
|
||||
})
|
||||
})
|
||||
|
||||
context('radio', () => {
|
||||
it('preprocess', () => {
|
||||
const formElement = document.createElement('form')
|
||||
|
||||
const radioInputs = ['foo', 'bar', 'baz'].map((val) => {
|
||||
const radioInput = document.createElement('input')
|
||||
|
||||
radioInput.type = 'radio'
|
||||
radioInput.value = val
|
||||
|
||||
return radioInput
|
||||
})
|
||||
|
||||
radioInputs[1].checked = true
|
||||
|
||||
radioInputs.forEach((radioInput) => formElement.appendChild(radioInput))
|
||||
|
||||
const snapshot = buildSnapshot(formElement)
|
||||
|
||||
const preprocessedSnapshot = preprocessDomElement(snapshot)
|
||||
|
||||
expect(preprocessedSnapshot).to.have.property('tagName').that.equals('BODY')
|
||||
expect(preprocessedSnapshot).to.have.property('serializationKey').that.equals('dom')
|
||||
expect(preprocessedSnapshot).to.have.property('innerHTML').that.equals(`<div><h1>Mock Snapshot Header</h1><form><input type="radio" value="foo"><input type="radio" value="bar" checked=""><input type="radio" value="baz"></form></div>`)
|
||||
})
|
||||
|
||||
it('reifies', () => {
|
||||
const preprocessedSnapshot = {
|
||||
tagName: 'BODY',
|
||||
serializationKey: 'dom',
|
||||
attributes: {},
|
||||
innerHTML: `<div><h1>Mock Snapshot Header</h1><form><input type="radio" value="foo"><input type="radio" value="bar" checked=""><input type="radio" value="baz"></form></div>`,
|
||||
}
|
||||
|
||||
const reifiedSnapshot = reifyDomElement(preprocessedSnapshot)
|
||||
|
||||
expect(reifiedSnapshot).to.be.instanceOf(HTMLBodyElement)
|
||||
expect(reifiedSnapshot.querySelector('form input[value="bar"]')).to.have.property('checked').that.equals(true)
|
||||
})
|
||||
})
|
||||
|
||||
context('checkbox', () => {
|
||||
it('preprocess', () => {
|
||||
const formElement = document.createElement('form')
|
||||
|
||||
const checkboxInputs = ['foo', 'bar', 'bar'].map((val) => {
|
||||
const checkboxInput = document.createElement('input')
|
||||
|
||||
checkboxInput.type = 'checkbox'
|
||||
checkboxInput.value = val
|
||||
|
||||
return checkboxInput
|
||||
})
|
||||
|
||||
checkboxInputs[1].checked = true
|
||||
|
||||
checkboxInputs.forEach((checkboxInput) => formElement.appendChild(checkboxInput))
|
||||
|
||||
const snapshot = buildSnapshot(formElement)
|
||||
|
||||
const preprocessedSnapshot = preprocessDomElement(snapshot)
|
||||
|
||||
expect(preprocessedSnapshot).to.have.property('tagName').that.equals('BODY')
|
||||
expect(preprocessedSnapshot).to.have.property('serializationKey').that.equals('dom')
|
||||
expect(preprocessedSnapshot).to.have.property('innerHTML').that.equals(`<div><h1>Mock Snapshot Header</h1><form><input type="checkbox" value="foo"><input type="checkbox" value="bar" checked=""><input type="checkbox" value="bar"></form></div>`)
|
||||
})
|
||||
|
||||
it('reifies', () => {
|
||||
const preprocessedSnapshot = {
|
||||
tagName: 'BODY',
|
||||
serializationKey: 'dom',
|
||||
attributes: {},
|
||||
innerHTML: `"<div><h1>Mock Snapshot Header</h1><form><input type="checkbox" value="foo"><input type="checkbox" value="bar" checked=""><input type="checkbox" value="bar"></form></div>"`,
|
||||
}
|
||||
|
||||
const reifiedSnapshot = reifyDomElement(preprocessedSnapshot)
|
||||
|
||||
expect(reifiedSnapshot).to.be.instanceOf(HTMLBodyElement)
|
||||
expect(reifiedSnapshot.querySelector('form input[value="bar"]')).to.have.property('checked').that.equals(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// purpose of these 'DOM Elements' tests is to give a very basic understanding of how DOM element serialization works in the log serializer
|
||||
context('Functions', () => {
|
||||
it('does NOT try to serialize a function unless `attemptToSerializeFunctions` is set to true', () => {
|
||||
const serializedFunction = preprocessLogLikeForSerialization(() => 'foo')
|
||||
|
||||
expect(serializedFunction).to.be.null
|
||||
})
|
||||
|
||||
it('Tries to serialize EXPLICIT/KNOWN serializable functions by setting `attemptToSerializeFunctions` to true', () => {
|
||||
const functionContents = [
|
||||
'foo',
|
||||
{
|
||||
bar: 'baz',
|
||||
},
|
||||
document.createElement('html'),
|
||||
]
|
||||
|
||||
const myKnownSerializableFunction = () => functionContents
|
||||
|
||||
const serializedFunction = preprocessLogLikeForSerialization(myKnownSerializableFunction, true)
|
||||
|
||||
expect(serializedFunction).to.deep.equal({
|
||||
serializationKey: 'function',
|
||||
value: [
|
||||
'foo',
|
||||
{
|
||||
bar: 'baz',
|
||||
},
|
||||
{
|
||||
tagName: 'HTML',
|
||||
serializationKey: 'dom',
|
||||
attributes: {},
|
||||
innerHTML: '',
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<button data-cy="approve-orig">Approve Redirect to originator after auth</button>
|
||||
<button data-cy="approve-me">Approve Redirect to me after auth</button>
|
||||
|
||||
<script>
|
||||
const queryString = location.search;
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
const token = urlParams.get('token')
|
||||
|
||||
const addTokenToRedirect = (href, jwt) => {
|
||||
const redirectUrl = new URL(href)
|
||||
const redirectUrlParams = new URLSearchParams(redirectUrl.search);
|
||||
redirectUrlParams.append('token', jwt)
|
||||
redirectUrl.search = redirectUrlParams.toString()
|
||||
return redirectUrl.href
|
||||
}
|
||||
|
||||
const getRedirectHref = () => {
|
||||
const queryString = window.location.search;
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
let redirect = urlParams.get('redirect')
|
||||
|
||||
if (redirect){
|
||||
return decodeURIComponent(redirect)
|
||||
}
|
||||
|
||||
return 'http://localhost:3500/fixtures/auth/index.html'
|
||||
}
|
||||
|
||||
if (token) {
|
||||
// Add Login button that redirects to the idp
|
||||
const loginBtn = document.createElement("button");
|
||||
loginBtn.innerHTML = "Login Successful - return to main site"
|
||||
loginBtn.dataset.cy = "login-success"
|
||||
loginBtn.onclick = function () {
|
||||
const redirectHref = getRedirectHref()
|
||||
|
||||
window.location.href = addTokenToRedirect(redirectHref, token)
|
||||
};
|
||||
document.body.appendChild(loginBtn);
|
||||
}
|
||||
|
||||
document.querySelector('[data-cy="approve-orig"]').addEventListener('click', () => {
|
||||
|
||||
const redirectHref = getRedirectHref()
|
||||
|
||||
window.location.href = `http://www.idp.com:3500/fixtures/auth/idp.html?redirect=${encodeURIComponent(redirectHref)}`
|
||||
})
|
||||
|
||||
document.querySelector('[data-cy="approve-me"]').addEventListener('click', () => {
|
||||
window.location.href = `http://www.idp.com:3500/fixtures/auth/idp.html?redirect=${encodeURIComponent(window.location.href)}`
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<select data-cy="sameSite">
|
||||
<option value="">Empty</option>
|
||||
<option value="None">None</option>
|
||||
<option value="Lax">Lax</option>
|
||||
<option value="Strict">Strict</option>
|
||||
<option value="Invalid">Invalid</option>
|
||||
</select>
|
||||
<label>
|
||||
<input type="checkbox" data-cy="secure" /> Secure
|
||||
</label>
|
||||
<label for="name">Username: </label>
|
||||
<input data-cy="username" type="text" required>
|
||||
<button data-cy="login">Login</button>
|
||||
|
||||
<script>
|
||||
document.querySelector('[data-cy="login"]').addEventListener('click', () => {
|
||||
const queryString = window.location.search;
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
const redirect = urlParams.get('redirect')
|
||||
const username = document.querySelector('[data-cy=username]').value
|
||||
const sameSite = document.querySelector('[data-cy=sameSite]').value
|
||||
const secure = document.querySelector('[data-cy=secure]').checked
|
||||
let url = `https://www.foobar.com:3502/cookie-login?redirect=${redirect}&username=${username}`
|
||||
|
||||
if (sameSite) {
|
||||
url += `&sameSite=${sameSite}`
|
||||
}
|
||||
|
||||
if (secure) {
|
||||
url += `&secure=true`
|
||||
}
|
||||
|
||||
window.location.href = url
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<button data-cy="to-localhost" onclick="setTimeout(()=>{window.location.href = 'http://localhost:3500/fixtures/auth/index.html'}, 1500)">navigate to localhost</button>
|
||||
<button data-cy="to-foobar" onclick="setTimeout(()=>{window.location.href = 'http://www.foobar.com:3500/fixtures/auth/index.html'}, 1500)">navigate to foobar</button>
|
||||
<button data-cy="to-idp" onclick="setTimeout(()=>{window.location.href = 'http://www.idp.com:3500/fixtures/auth/index.html'}, 1500)">navigate to idp</button>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<label for="name">Username: </label>
|
||||
<input data-cy="username" type="text" name="name" id="name" required>
|
||||
<button data-cy="login">Login</button>
|
||||
<button data-cy="slow-login">Slow Login</button>
|
||||
|
||||
<script>
|
||||
const createToken = (username) => {
|
||||
const expiry = new Date()
|
||||
// Expire in 10 minutes
|
||||
expiry.setMinutes(expiry.getMinutes() + 10)
|
||||
// create faux jwt
|
||||
return {
|
||||
header: {
|
||||
alg: 'string',
|
||||
type: 'JWT'
|
||||
},
|
||||
body: {
|
||||
username,
|
||||
expiry: expiry.toISOString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getRedirectHref = () => {
|
||||
const queryString = window.location.search;
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
let redirect = urlParams.get('redirect')
|
||||
|
||||
if (redirect){
|
||||
return decodeURIComponent(redirect)
|
||||
}
|
||||
|
||||
return 'http://localhost:3500/fixtures/auth/index.html'
|
||||
}
|
||||
|
||||
const addTokenToRedirect = (href, token) => {
|
||||
const redirectUrl = new URL(href)
|
||||
const redirectUrlParams = new URLSearchParams(redirectUrl.search);
|
||||
redirectUrlParams.append('token', encodeURIComponent(JSON.stringify(token)))
|
||||
redirectUrl.search = redirectUrlParams.toString()
|
||||
return redirectUrl.href
|
||||
}
|
||||
|
||||
document.querySelector('[data-cy="login"]').addEventListener('click', () => {
|
||||
const token = createToken(document.querySelector('[data-cy="username"]').value)
|
||||
|
||||
const redirectHref = getRedirectHref()
|
||||
|
||||
window.location.href = addTokenToRedirect(redirectHref, token)
|
||||
})
|
||||
|
||||
document.querySelector('[data-cy="slow-login"]').addEventListener('click', () => {
|
||||
const token = createToken(document.querySelector('[data-cy="username"]').value)
|
||||
|
||||
const redirectHref = getRedirectHref()
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = addTokenToRedirect(redirectHref, token)
|
||||
}, 5000);
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,94 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const sessionStorage = window.sessionStorage;
|
||||
|
||||
// If there is a new token, set it. This is clearly not secure and no one should setup real auth this way
|
||||
const queryString = location.search;
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
const newToken = urlParams.get('token')
|
||||
|
||||
|
||||
if (newToken) {
|
||||
sessionStorage.setItem('cypressAuthToken', decodeURIComponent(newToken));
|
||||
urlParams.delete('token')
|
||||
const newSearchParams = urlParams.toString()
|
||||
|
||||
let newLocation = `${location.origin}${location.pathname}`
|
||||
|
||||
if(newSearchParams){
|
||||
newLocation = `${newLocation}?${newSearchParams}`
|
||||
}
|
||||
|
||||
if(location.hash){
|
||||
newLocation = `${newLocation}${location.hash}`
|
||||
}
|
||||
|
||||
location.replace(newLocation)
|
||||
}
|
||||
|
||||
const cypressAuthToken = sessionStorage.getItem('cypressAuthToken');
|
||||
|
||||
// If a token doesn't exist we aren't logged in
|
||||
if (!cypressAuthToken){
|
||||
// Add Message
|
||||
const tag = document.createElement("p");
|
||||
const text = document.createTextNode("You are not logged in");
|
||||
tag.appendChild(text)
|
||||
const body = document.body
|
||||
body.appendChild(tag)
|
||||
|
||||
// Add Login button that redirects to the idp
|
||||
const loginBtn = document.createElement("button");
|
||||
loginBtn.innerHTML = "Login IDP"
|
||||
loginBtn.dataset.cy = "login-idp"
|
||||
loginBtn.onclick = function () {
|
||||
window.location.href = `http://www.idp.com:3500/fixtures/auth/idp.html?redirect=${encodeURIComponent(window.location.href)}`
|
||||
};
|
||||
document.body.appendChild(loginBtn);
|
||||
|
||||
// Add Login button that redirects to the idp
|
||||
const loginFoobarBtn = document.createElement("button");
|
||||
loginFoobarBtn.innerHTML = "Login Foobar"
|
||||
loginFoobarBtn.dataset.cy = "login-foobar"
|
||||
loginFoobarBtn.onclick = function () {
|
||||
window.location.href = `http://www.foobar.com:3500/fixtures/auth/idp.html?redirect=${encodeURIComponent(window.location.href)}`
|
||||
};
|
||||
document.body.appendChild(loginFoobarBtn);
|
||||
|
||||
// Add Login button that redirects to the idp
|
||||
const loginWithApprovalBtn = document.createElement("button");
|
||||
loginWithApprovalBtn.innerHTML = "Login With Approval"
|
||||
loginWithApprovalBtn.dataset.cy = "login-with-approval"
|
||||
loginWithApprovalBtn.onclick = function () {
|
||||
window.location.href = `http://wwww.foobar.com:3500/fixtures/auth/approval.html?redirect=${encodeURIComponent(window.location.href)}`
|
||||
};
|
||||
document.body.appendChild(loginWithApprovalBtn);
|
||||
|
||||
} else {
|
||||
const token = JSON.parse(cypressAuthToken)
|
||||
// If the token exists, hooray, give them a logout button to destroy the token and refresh.
|
||||
const tag = document.createElement("p");
|
||||
const text = document.createTextNode(`Welcome ${token.body.username}`);
|
||||
tag.dataset.cy = "welcome"
|
||||
tag.appendChild(text)
|
||||
const body = document.body
|
||||
body.appendChild(tag)
|
||||
|
||||
// Add log out button
|
||||
const btn = document.createElement("button");
|
||||
btn.innerHTML = "Logout";
|
||||
btn.dataset.cy = "logout"
|
||||
btn.onclick = function () {
|
||||
sessionStorage.removeItem('cypressAuthToken');
|
||||
location.reload()
|
||||
};
|
||||
document.body.appendChild(btn);
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -437,8 +437,8 @@
|
||||
</select>
|
||||
|
||||
<select name="foods">
|
||||
<option value="Japanese">Ramen</option>
|
||||
<option value="Ramen">Japanese</option>
|
||||
<option value="Ramen">Ramen</option>
|
||||
<option value="Japanese">Japanese</option>
|
||||
</select>
|
||||
|
||||
<select name="names">
|
||||
@@ -463,7 +463,7 @@
|
||||
</select>
|
||||
|
||||
<fieldset disabled>
|
||||
<select name="fielset-disabled">
|
||||
<select name="fieldset-disabled">
|
||||
<option value="foo">foo</option>
|
||||
<option value="bar">bar</option>
|
||||
</select>
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>DOM Fixture</title>
|
||||
<script type="text/javascript" src="/node_modules/sinon/dist/sinon.js"></script>
|
||||
<script type="text/javascript" src="/node_modules/jquery/dist/jquery.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="background">
|
||||
<link rel="stylesheet" href="/node_modules/@cypress/bower-kendo-ui/styles/kendo.common.min.css">
|
||||
<link rel="stylesheet" href="/node_modules/@cypress/bower-kendo-ui/styles/kendo.default.min.css">
|
||||
|
||||
<style>
|
||||
#background {
|
||||
position: fixed;
|
||||
background-color: gray;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<input id="dropdown" />
|
||||
|
||||
<script type="text/javascript" src="/node_modules/@cypress/bower-kendo-ui/js/kendo.ui.core.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(function(){
|
||||
$("#dropdown").kendoDropDownList({
|
||||
optionLabel: "--Select--",
|
||||
dataTextField: "t",
|
||||
dataValueField: "v",
|
||||
dataSource: [
|
||||
{v: 1, t: "Apples"},
|
||||
{v: 2, t: "Bananas"},
|
||||
{v: 3, t: "Pears"},
|
||||
{v: 4, t: "Watermelon"},
|
||||
{v: 5, t: "Grapes"},
|
||||
{v: 6, t: "Strawberries"}
|
||||
]
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,32 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>DOM Fixture</title>
|
||||
<script type="text/javascript" src="/node_modules/sinon/dist/sinon.js"></script>
|
||||
<script type="text/javascript" src="/node_modules/jquery/dist/jquery.js"></script>
|
||||
<script type="text/javascript" src="/node_modules/bootstrap/dist/js/bootstrap.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="clicks">
|
||||
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
|
||||
<style>
|
||||
body {
|
||||
padding-top: 70px;
|
||||
}
|
||||
#clicks {
|
||||
min-height: 2000px;
|
||||
}
|
||||
</style>
|
||||
<nav class="navbar navbar-default navbar-fixed-top">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="#">Brand</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container">
|
||||
<button class="btn btn-primary">click me</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<p data-cy="dom-check">From a secondary origin</p>
|
||||
<p data-cy="cypress-check"></p>
|
||||
<p data-cy="window-before-load"></p>
|
||||
<form>
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
<button data-cy="alert">Alert</button>
|
||||
<button data-cy="confirm">Confirm</button>
|
||||
<button data-cy="reload">reload</button>
|
||||
<a data-cy="hashChange" href='#hashChange'>hashChange</a>
|
||||
<a data-cy="cross-origin-page"
|
||||
href="/fixtures/multi-domain.html">/fixtures/multi-domain.html</a>
|
||||
|
||||
<script>
|
||||
if (window.Cypress) {
|
||||
document.querySelector('[data-cy="cypress-check"]').innerText = 'Has window.Cypress'
|
||||
}
|
||||
|
||||
if (window.testSecondaryWindowBeforeLoad){
|
||||
document.querySelector('[data-cy="window-before-load"]').innerText = 'Window Before Load Called'
|
||||
}
|
||||
|
||||
document.querySelector('[data-cy="alert"]').addEventListener('click', () => {
|
||||
window.alert('the alert text')
|
||||
})
|
||||
|
||||
document.querySelector('[data-cy="confirm"]').addEventListener('click', () => {
|
||||
window.confirm('the confirm text')
|
||||
})
|
||||
|
||||
document.querySelector('[data-cy="reload"]').addEventListener('click', () => {
|
||||
window.location.reload()
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
Go to different origin:
|
||||
<a data-cy="cross-origin-secondary-link"
|
||||
href="http://www.foobar.com:3500/fixtures/multi-domain-secondary.html">http://www.foobar.com:3500/fixtures/multi-domain-secondary.html</a>
|
||||
<a data-cy="dom-link" href="http://www.foobar.com:3500/fixtures/dom.html">http://www.foobar.com:3500/fixtures/dom.html</a>
|
||||
<a data-cy="scrolling-link" href="http://www.foobar.com:3500/fixtures/scrolling.html">http://www.foobar.com:3500/fixtures/scrolling.html</a>
|
||||
<a data-cy="request-link" href="http://www.foobar.com:3500/fixtures/request.html">http://www.foobar.com:3500/fixtures/request.html</a>
|
||||
<a data-cy="shadow-dom-link" href="http://www.foobar.com:3500/fixtures/shadow-dom.html">http://www.foobar.com:3500/fixtures/shadow-dom.html</a>
|
||||
<a data-cy="files-form-link" href="http://www.foobar.com:3500/fixtures/files-form.html">http://www.foobar.com:3500/fixtures/files-form.html</a>
|
||||
<a data-cy="errors-link" href="http://www.foobar.com:3500/fixtures/errors.html">http://www.foobar.com:3500/fixtures/errors.html</a>
|
||||
<a data-cy="screenshots-link" href="http://www.foobar.com:3500/fixtures/screenshots.html">http://www.foobar.com:3500/fixtures/screenshots.html</a>
|
||||
<a data-cy="cookie-login" href="http://www.foobar.com:3500/fixtures/auth/cookie-login.html?redirect=http://localhost:3500/login">Login with Social</a>
|
||||
<a data-cy="welcome" href="http://localhost:3500/welcome">Go to Welcome</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -10,6 +10,7 @@ const multer = require('multer')
|
||||
const upload = multer({ dest: 'cypress/_test-output/' })
|
||||
|
||||
const PATH_TO_SERVER_PKG = path.dirname(require.resolve('@packages/server'))
|
||||
|
||||
const httpPorts = [3500, 3501]
|
||||
const httpsPort = 3502
|
||||
|
||||
@@ -25,6 +26,7 @@ const createApp = (port) => {
|
||||
})
|
||||
|
||||
app.use(require('cors')())
|
||||
app.use(require('cookie-parser')())
|
||||
app.use(require('compression')())
|
||||
app.use(bodyParser.urlencoded({ extended: false }))
|
||||
app.use(bodyParser.json())
|
||||
@@ -35,6 +37,10 @@ const createApp = (port) => {
|
||||
return res.sendStatus(200)
|
||||
})
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
return res.send('<html><body>root page</body></html>')
|
||||
})
|
||||
|
||||
app.get('/timeout', (req, res) => {
|
||||
return Promise
|
||||
.delay(req.query.ms || 0)
|
||||
@@ -127,6 +133,7 @@ const createApp = (port) => {
|
||||
|
||||
return res
|
||||
.set('WWW-Authenticate', 'Basic')
|
||||
.type('html')
|
||||
.sendStatus(401)
|
||||
})
|
||||
|
||||
@@ -188,6 +195,69 @@ const createApp = (port) => {
|
||||
.send('<html><body>server error</body></html>')
|
||||
})
|
||||
|
||||
const getCookieAdditions = ({ sameSite, secure }) => {
|
||||
let additions = ''
|
||||
|
||||
if (sameSite) additions += `; SameSite=${sameSite}`
|
||||
|
||||
if (secure) additions += '; Secure'
|
||||
|
||||
return additions
|
||||
}
|
||||
|
||||
app.get('/cookie-login', (req, res) => {
|
||||
const { username, redirect } = req.query
|
||||
|
||||
res
|
||||
.header('Set-Cookie', `user=${username}${getCookieAdditions(req.query)}`)
|
||||
.redirect(302, `/verify-cookie-login?username=${username}&redirect=${redirect}`)
|
||||
})
|
||||
|
||||
app.get('/verify-cookie-login', (req, res) => {
|
||||
if (!req.cookies.user) {
|
||||
return res
|
||||
.status(403)
|
||||
.send('<html><body><h1>Not logged in</h1></body></html>')
|
||||
}
|
||||
|
||||
const { username, redirect } = req.query
|
||||
|
||||
res.send(`
|
||||
<html>
|
||||
<body>
|
||||
<h1>Redirecting ${username}...</h1>
|
||||
<script>
|
||||
setTimeout(() => {
|
||||
window.location.href = '${redirect}?username=${username}'
|
||||
}, 1000)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
})
|
||||
|
||||
app.get('/login', (req, res) => {
|
||||
const { username } = req.query
|
||||
|
||||
if (!username) {
|
||||
return res.send('<html><body><h1>Must specify username to log in</h1></body></html>')
|
||||
}
|
||||
|
||||
// can't use res.cookie() because it won't allow setting an invalid
|
||||
// SameSite value, which we want to test
|
||||
res
|
||||
.header('Set-Cookie', `user=${username}${getCookieAdditions(req.query)}`)
|
||||
.redirect(302, '/welcome')
|
||||
})
|
||||
|
||||
app.get('/welcome', (req, res) => {
|
||||
if (!req.cookies.user) {
|
||||
return res.send('<html><body><h1>No user found</h1></body></html>')
|
||||
}
|
||||
|
||||
res.send(`<html><body><h1>Welcome, ${req.cookies.user}!</h1></body></html>`)
|
||||
})
|
||||
|
||||
let _var = ''
|
||||
|
||||
app.get('/set-var', (req, res) => {
|
||||
|
||||
@@ -72,6 +72,20 @@ export const assertLogLength = (logs, expectedLength) => {
|
||||
expect(logs.length).to.eq(expectedLength, `received ${logs.length} logs when we expected ${expectedLength}: [${receivedLogs}]`)
|
||||
}
|
||||
|
||||
export const findCrossOriginLogs = (consolePropCommand, logMap, matchingOrigin) => {
|
||||
const matchedLogs = Array.from(logMap.values()).filter((log) => {
|
||||
const props = log.get()
|
||||
|
||||
let consoleProps = _.isFunction(props?.consoleProps) ? props.consoleProps() : props?.consoleProps
|
||||
|
||||
return consoleProps.Command === consolePropCommand && props.id.includes(matchingOrigin)
|
||||
})
|
||||
|
||||
const logAttrs = matchedLogs.map((log) => log.get())
|
||||
|
||||
return logAttrs.length === 1 ? logAttrs[0] : logAttrs
|
||||
}
|
||||
|
||||
export const attachListeners = (listenerArr) => {
|
||||
return (els) => {
|
||||
_.each(els, (el, elName) => {
|
||||
@@ -94,6 +108,10 @@ const getAllFn = (...aliases) => {
|
||||
)
|
||||
}
|
||||
|
||||
const shouldWithTimeout = (cb, timeout = 250) => {
|
||||
cy.wrap({}, { timeout }).should(cb)
|
||||
}
|
||||
|
||||
export const keyEvents = [
|
||||
'keydown',
|
||||
'keyup',
|
||||
@@ -120,6 +138,8 @@ export const expectCaret = (start, end) => {
|
||||
|
||||
Cypress.Commands.add('getAll', getAllFn)
|
||||
|
||||
Cypress.Commands.add('shouldWithTimeout', shouldWithTimeout)
|
||||
|
||||
const chaiSubset = require('chai-subset')
|
||||
|
||||
chai.use(chaiSubset)
|
||||
|
||||
@@ -5,25 +5,26 @@
|
||||
"scripts": {
|
||||
"clean-deps": "rimraf node_modules",
|
||||
"cypress:open": "node ../../scripts/cypress open",
|
||||
"cypress:run": "node ../../scripts/cypress run",
|
||||
"cypress:run": "node ../../scripts/cypress run --spec \"cypress/integration/*/*\",\"cypress/integration/*/!(multi-domain)/**/*\"",
|
||||
"cypress:open-experimentalSessionAndOrigin": "node ../../scripts/cypress open --config experimentalSessionAndOrigin=true",
|
||||
"cypress:run-experimentalSessionAndOrigin": "node ../../scripts/cypress run --config experimentalSessionAndOrigin=true",
|
||||
"postinstall": "patch-package",
|
||||
"start": "node -e 'console.log(require(`chalk`).red(`\nError:\n\tRunning \\`yarn start\\` is no longer needed for driver/cypress tests.\n\tWe now automatically spawn the server in e2e.setupNodeEvents config.\n\tChanges to the server will be watched and reloaded automatically.`))'"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@babel/code-frame": "7.8.3",
|
||||
"@cypress/bower-kendo-ui": "0.0.2",
|
||||
"@cypress/sinon-chai": "2.9.1",
|
||||
"@cypress/unique-selector": "0.4.4",
|
||||
"@cypress/webpack-preprocessor": "0.0.0-development",
|
||||
"@cypress/what-is-circular": "1.0.1",
|
||||
"@packages/config": "0.0.0-development",
|
||||
"@packages/network": "0.0.0-development",
|
||||
"@packages/resolve-dist": "0.0.0-development",
|
||||
"@packages/runner": "0.0.0-development",
|
||||
"@packages/runner-shared": "0.0.0-development",
|
||||
"@packages/server": "0.0.0-development",
|
||||
"@packages/socket": "0.0.0-development",
|
||||
"@packages/ts": "0.0.0-development",
|
||||
"@rollup/plugin-node-resolve": "^13.0.4",
|
||||
"@sinonjs/fake-timers": "8.1.0",
|
||||
"@types/chalk": "^2.2.0",
|
||||
"@types/common-tags": "^1.8.0",
|
||||
@@ -36,14 +37,13 @@
|
||||
"blob-util": "2.0.2",
|
||||
"bluebird": "3.5.3",
|
||||
"body-parser": "1.19.0",
|
||||
"bootstrap": "4.4.1",
|
||||
"bytes": "3.1.0",
|
||||
"chai": "4.2.0",
|
||||
"chai-as-promised": "7.1.1",
|
||||
"chai-subset": "1.6.0",
|
||||
"chokidar-cli": "2.1.0",
|
||||
"clone": "2.1.2",
|
||||
"compression": "1.7.4",
|
||||
"cookie-parser": "1.4.5",
|
||||
"core-js-pure": "3.21.0",
|
||||
"cors": "2.8.5",
|
||||
"cypress-multi-reporters": "1.4.0",
|
||||
"dayjs": "^1.10.3",
|
||||
@@ -56,7 +56,6 @@
|
||||
"is-valid-hostname": "1.0.1",
|
||||
"jquery": "3.1.1",
|
||||
"js-cookie": "2.2.1",
|
||||
"jsdom": "14.1.0",
|
||||
"json-stable-stringify": "1.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"md5": "2.3.0",
|
||||
@@ -64,9 +63,7 @@
|
||||
"methods": "1.1.2",
|
||||
"mime-types": "2.1.27",
|
||||
"minimatch": "3.0.4",
|
||||
"minimist": "1.2.6",
|
||||
"mocha": "7.0.1",
|
||||
"morgan": "1.9.1",
|
||||
"multer": "1.4.2",
|
||||
"ordinal": "1.0.3",
|
||||
"react-15.6.1": "npm:react@15.6.1",
|
||||
|
||||
@@ -4,16 +4,29 @@ import $dom from '../../../dom'
|
||||
import $utils from '../../../cypress/utils'
|
||||
import $errUtils from '../../../cypress/error_utils'
|
||||
import $elements from '../../../dom/elements'
|
||||
import type { Log } from '../../../cypress/log'
|
||||
|
||||
interface InternalFocusOptions extends Partial<Cypress.Loggable & Cypress.Timeoutable> {
|
||||
_log?: Log
|
||||
$el: JQuery
|
||||
error: boolean
|
||||
verify: boolean
|
||||
}
|
||||
|
||||
interface InternalBlurOptions extends Partial<Cypress.BlurOptions> {
|
||||
_log?: Log
|
||||
$el: JQuery
|
||||
$focused: JQuery
|
||||
error: boolean
|
||||
verify: boolean
|
||||
}
|
||||
|
||||
export default (Commands, Cypress, cy) => {
|
||||
return Commands.addAll({ prevSubject: ['element', 'window'] }, {
|
||||
// TODO: any -> Partial<Cypress.Loggable & Cypress.Timeoutable>
|
||||
focus (subject, options: any = {}) {
|
||||
const userOptions = options
|
||||
|
||||
focus (subject, userOptions: Partial<Cypress.Loggable & Cypress.Timeoutable> = {}) {
|
||||
// we should throw errors by default!
|
||||
// but allow them to be silenced
|
||||
options = _.defaults({}, userOptions, {
|
||||
const options: InternalFocusOptions = _.defaults({}, userOptions, {
|
||||
$el: subject,
|
||||
error: true,
|
||||
log: true,
|
||||
@@ -85,13 +98,10 @@ export default (Commands, Cypress, cy) => {
|
||||
return verifyAssertions()
|
||||
},
|
||||
|
||||
// TODO: any -> Partial<Cypress.BlurOptions>
|
||||
blur (subject, options: any = {}) {
|
||||
const userOptions = options
|
||||
|
||||
blur (subject, userOptions: Partial<Cypress.BlurOptions> = {}) {
|
||||
// we should throw errors by default!
|
||||
// but allow them to be silenced
|
||||
options = _.defaults({}, userOptions, {
|
||||
const options: InternalBlurOptions = _.defaults({}, userOptions, {
|
||||
$el: subject,
|
||||
$focused: cy.getFocused(),
|
||||
error: true,
|
||||
|
||||
@@ -5,6 +5,7 @@ import Promise from 'bluebird'
|
||||
import $dom from '../../../dom'
|
||||
import $utils from '../../../cypress/utils'
|
||||
import $errUtils from '../../../cypress/error_utils'
|
||||
import type { Log } from '../../../cypress/log'
|
||||
|
||||
const findScrollableParent = ($el, win) => {
|
||||
const $parent = $dom.getParent($el)
|
||||
@@ -28,12 +29,26 @@ const isNaNOrInfinity = (item) => {
|
||||
return _.isNaN(num) || !_.isFinite(num)
|
||||
}
|
||||
|
||||
interface InternalScrollIntoViewOptions extends Partial<Cypress.ScrollToOptions> {
|
||||
_log?: Log
|
||||
$el: JQuery
|
||||
$parent: any
|
||||
axis: string
|
||||
offset?: object
|
||||
}
|
||||
|
||||
interface InternalScrollToOptions extends Partial<Cypress.ScrollToOptions> {
|
||||
_log?: Log
|
||||
$el: any
|
||||
x: number
|
||||
y: number
|
||||
error?: any
|
||||
axis: string
|
||||
}
|
||||
|
||||
export default (Commands, Cypress, cy, state) => {
|
||||
Commands.addAll({ prevSubject: 'element' }, {
|
||||
// TODO: any -> Partial<Cypress.ScrollToOptions>
|
||||
scrollIntoView (subject, options: any = {}) {
|
||||
const userOptions = options
|
||||
|
||||
scrollIntoView (subject, userOptions: Partial<Cypress.ScrollToOptions> = {}) {
|
||||
if (!_.isObject(userOptions)) {
|
||||
$errUtils.throwErrByPath('scrollIntoView.invalid_argument', { args: { arg: userOptions } })
|
||||
}
|
||||
@@ -49,7 +64,7 @@ export default (Commands, Cypress, cy, state) => {
|
||||
$errUtils.throwErrByPath('scrollIntoView.multiple_elements', { args: { num: subject.length } })
|
||||
}
|
||||
|
||||
options = _.defaults({}, userOptions, {
|
||||
const options: InternalScrollIntoViewOptions = _.defaults({}, userOptions, {
|
||||
$el: subject,
|
||||
$parent: state('window'),
|
||||
log: true,
|
||||
@@ -115,9 +130,6 @@ export default (Commands, Cypress, cy, state) => {
|
||||
const scrollIntoView = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// scroll our axes
|
||||
// TODO: done() came from jQuery animate(), specifically, EffectsOptions at misc.d.ts
|
||||
// The type definition should be fixed at @types/jquery.scrollto.
|
||||
// @ts-ignore
|
||||
return $(options.$parent).scrollTo(options.$el, {
|
||||
axis: options.axis,
|
||||
easing: options.easing,
|
||||
@@ -157,10 +169,8 @@ export default (Commands, Cypress, cy, state) => {
|
||||
})
|
||||
|
||||
Commands.addAll({ prevSubject: ['optional', 'element', 'window'] }, {
|
||||
// TODO: any -> Partial<Cypress.ScrollToOptions>
|
||||
scrollTo (subject, xOrPosition, yOrOptions, options: any = {}) {
|
||||
scrollTo (subject, xOrPosition, yOrOptions, userOptions: Partial<Cypress.ScrollToOptions> = {}) {
|
||||
let x; let y
|
||||
let userOptions = options
|
||||
|
||||
// check for undefined or null values
|
||||
if (xOrPosition === undefined || xOrPosition === null) {
|
||||
@@ -261,7 +271,7 @@ export default (Commands, Cypress, cy, state) => {
|
||||
$errUtils.throwErrByPath('scrollTo.multiple_containers', { args: { num: $container.length } })
|
||||
}
|
||||
|
||||
options = _.defaults({}, userOptions, {
|
||||
const options: InternalScrollToOptions = _.defaults({}, userOptions, {
|
||||
$el: $container,
|
||||
log: true,
|
||||
duration: 0,
|
||||
@@ -361,10 +371,7 @@ export default (Commands, Cypress, cy, state) => {
|
||||
|
||||
const scrollTo = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// scroll our axis'
|
||||
// TODO: done() came from jQuery animate(), specifically, EffectsOptions at misc.d.ts
|
||||
// The type definition should be fixed at @types/jquery.scrollto.
|
||||
// @ts-ignore
|
||||
// scroll our axis
|
||||
$(options.$el).scrollTo({ left: x, top: y }, {
|
||||
axis: options.axis,
|
||||
easing: options.easing,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user