mirror of
https://github.com/cypress-io/cypress.git
synced 2026-05-02 04:50:06 -05:00
Merge pull request #24408 from cypress-io/release/12.0.0
feat(breaking): 12.0 Release
This commit is contained in:
+12
-97
@@ -27,6 +27,7 @@ mainBuildFilters: &mainBuildFilters
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- /^release\/\d+\.\d+\.\d+$/
|
||||
- 'feature/run-all-specs'
|
||||
|
||||
# usually we don't build Mac app - it takes a long time
|
||||
@@ -38,17 +39,16 @@ macWorkflowFilters: &darwin-workflow-filters
|
||||
- equal: [ develop, << pipeline.git.branch >> ]
|
||||
- equal: [ 'feature/run-all-specs', << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: "-release$"
|
||||
value: << pipeline.git.branch >>
|
||||
|
||||
pattern: /^release\/\d+\.\d+\.\d+$/
|
||||
value: << pipeline.git.branch >>
|
||||
linuxArm64WorkflowFilters: &linux-arm64-workflow-filters
|
||||
when:
|
||||
or:
|
||||
- equal: [ develop, << pipeline.git.branch >> ]
|
||||
- equal: [ 'feature/run-all-specs', << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: "-release$"
|
||||
value: << pipeline.git.branch >>
|
||||
pattern: /^release\/\d+\.\d+\.\d+$/
|
||||
value: << pipeline.git.branch >>
|
||||
|
||||
# uncomment & add to the branch conditions below to disable the main linux
|
||||
# flow if we don't want to test it for a certain branch
|
||||
@@ -63,11 +63,9 @@ windowsWorkflowFilters: &windows-workflow-filters
|
||||
when:
|
||||
or:
|
||||
- equal: [ develop, << pipeline.git.branch >> ]
|
||||
- equal: [ 'astone123/fix-windows-lint', << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: "-release$"
|
||||
value: << pipeline.git.branch >>
|
||||
|
||||
pattern: /^release\/\d+\.\d+\.\d+$/
|
||||
value: << pipeline.git.branch >>
|
||||
executors:
|
||||
# the Docker image with Cypress dependencies and Chrome browser
|
||||
cy-doc:
|
||||
@@ -130,7 +128,7 @@ commands:
|
||||
- run:
|
||||
name: Check current branch to persist artifacts
|
||||
command: |
|
||||
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "feature/run-all-specs" ]]; then
|
||||
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* ]]; then
|
||||
echo "Not uploading artifacts or posting install comment for this branch."
|
||||
circleci-agent step halt
|
||||
fi
|
||||
@@ -461,10 +459,6 @@ commands:
|
||||
description: chrome channel to install
|
||||
type: string
|
||||
default: ''
|
||||
experimentalSessionAndOrigin:
|
||||
description: experimental flag to apply
|
||||
type: boolean
|
||||
default: false
|
||||
steps:
|
||||
- restore_cached_workspace
|
||||
- when:
|
||||
@@ -488,13 +482,8 @@ commands:
|
||||
|
||||
if [[ -v MAIN_RECORD_KEY ]]; then
|
||||
# internal PR
|
||||
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
|
||||
CYPRESS_RECORD_KEY=$MAIN_RECORD_KEY \
|
||||
yarn cypress:run --record --parallel --group 5x-driver-<<parameters.browser>> --browser <<parameters.browser>>
|
||||
else
|
||||
# external PR
|
||||
TESTFILES=$(circleci tests glob "cypress/e2e/**/*.cy.*" | circleci tests split --total=$CIRCLE_NODE_TOTAL)
|
||||
@@ -503,11 +492,7 @@ commands:
|
||||
if [[ -z "$TESTFILES" ]]; then
|
||||
echo "Empty list of test files"
|
||||
fi
|
||||
if <<parameters.experimentalSessionAndOrigin>>; then
|
||||
yarn cypress:run-experimentalSessionAndOrigin --browser <<parameters.browser>> --spec $TESTFILES
|
||||
else
|
||||
yarn cypress:run --browser <<parameters.browser>> --spec $TESTFILES
|
||||
fi
|
||||
yarn cypress:run --browser <<parameters.browser>> --spec $TESTFILES
|
||||
fi
|
||||
working_directory: packages/driver
|
||||
- verify-mocha-results
|
||||
@@ -1657,58 +1642,12 @@ jobs:
|
||||
|
||||
driver-integration-tests-webkit:
|
||||
<<: *defaults
|
||||
resource_class: medium+
|
||||
parallelism: 5
|
||||
steps:
|
||||
- run-driver-integration-tests:
|
||||
browser: webkit
|
||||
|
||||
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
|
||||
|
||||
driver-integration-tests-webkit-experimentalSessionAndOrigin:
|
||||
<<: *defaults
|
||||
resource_class: medium
|
||||
parallelism: 5
|
||||
steps:
|
||||
- run-driver-integration-tests:
|
||||
browser: webkit
|
||||
experimentalSessionAndOrigin: true
|
||||
|
||||
run-reporter-component-tests-chrome:
|
||||
<<: *defaults
|
||||
parameters:
|
||||
@@ -2482,26 +2421,6 @@ linux-x64-workflow: &linux-x64-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
|
||||
- driver-integration-tests-webkit-experimentalSessionAndOrigin:
|
||||
context: test-runner:cypress-record-key
|
||||
requires:
|
||||
- build
|
||||
- run-frontend-shared-component-tests-chrome:
|
||||
context: [test-runner:cypress-record-key, test-runner:launchpad-tests, test-runner:percy]
|
||||
percy: true
|
||||
@@ -2606,10 +2525,6 @@ linux-x64-workflow: &linux-x64-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
|
||||
|
||||
+1
-1
@@ -116,7 +116,7 @@
|
||||
"cypress": "bin/cypress"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
"node": "^14.0.0 || ^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"types": "types",
|
||||
"exports": {
|
||||
|
||||
Vendored
+1
-1
@@ -173,7 +173,7 @@ declare namespace CypressCommandLine {
|
||||
title: string[]
|
||||
state: string
|
||||
body: string
|
||||
/**
|
||||
/**
|
||||
* Error string as it's presented in console if the test fails
|
||||
*/
|
||||
displayError: string | null
|
||||
|
||||
Vendored
+162
-174
@@ -50,6 +50,9 @@ declare namespace Cypress {
|
||||
interface CommandFnWithOriginalFnAndSubject<T extends keyof Chainable, S> {
|
||||
(this: Mocha.Context, originalFn: CommandOriginalFnWithSubject<T, S>, prevSubject: S, ...args: Parameters<ChainableMethods[T]>): ReturnType<ChainableMethods[T]> | void
|
||||
}
|
||||
interface QueryFn<T extends keyof ChainableMethods> {
|
||||
(this: Command, ...args: Parameters<ChainableMethods[T]>): (subject: any) => any
|
||||
}
|
||||
interface ObjectLike {
|
||||
[key: string]: any
|
||||
}
|
||||
@@ -128,6 +131,58 @@ declare namespace Cypress {
|
||||
unsupportedVersion?: boolean
|
||||
}
|
||||
|
||||
interface Ensure {
|
||||
/**
|
||||
* Throws an error if `subject` is not one of the passed in `type`s.
|
||||
*/
|
||||
isType(subject: any, type: PrevSubject[], commandName: string, cy: Chainable): void
|
||||
|
||||
/**
|
||||
* Throws an error if `subject` is not a DOM element.
|
||||
*/
|
||||
isElement(subject: any, commandName: string, cy: Chainable): void
|
||||
|
||||
/**
|
||||
* Throws an error if `subject` is not a `document`.
|
||||
*/
|
||||
isDocument(subject: any, commandName: string, cy: Chainable): void
|
||||
|
||||
/**
|
||||
* Throws an error if `subject` is not a `window`.
|
||||
*/
|
||||
isWindow(subject: any, commandName: string, cy: Chainable): void
|
||||
|
||||
/**
|
||||
* Throws an error if `subject` is not a DOM element attached to the application under test.
|
||||
*/
|
||||
isAttached(subject: any, commandName: string, cy: Chainable, onFail?: Log): void
|
||||
|
||||
/**
|
||||
* Throws an error if `subject` is a disabled DOM element.
|
||||
*/
|
||||
isNotDisabled(subject: any, commandName: string, onFail?: Log): void
|
||||
|
||||
/**
|
||||
* Throws an error if `subject` is a DOM element hidden by any of its parent elements.
|
||||
*/
|
||||
isNotHiddenByAncestors(subject: any, commandName: string, onFail?: Log): void
|
||||
|
||||
/**
|
||||
* Throws an error if `subject` is a read-only form element.
|
||||
*/
|
||||
isNotReadonly(subject: any, commandName: string, onFail?: Log): void
|
||||
|
||||
/**
|
||||
* Throws an error if `subject` is a read-only form element.
|
||||
*/
|
||||
isScrollable(subject: any, commandName: string, onFail?: Log): void
|
||||
|
||||
/**
|
||||
* Throws an error if `subject` is not a DOM element visible in the AUT.
|
||||
*/
|
||||
isVisible(subject: any, commandName: string, onFail?: Log): void
|
||||
}
|
||||
|
||||
interface LocalStorage {
|
||||
/**
|
||||
* Called internally to clear `localStorage` in two situations.
|
||||
@@ -305,6 +360,12 @@ declare namespace Cypress {
|
||||
*/
|
||||
sinon: sinon.SinonStatic
|
||||
|
||||
/**
|
||||
* Utility functions for ensuring various properties about a subject.
|
||||
* @see https://on.cypress.io/custom-queries
|
||||
*/
|
||||
ensure: Ensure
|
||||
|
||||
/**
|
||||
* Cypress version string. i.e. "1.1.2"
|
||||
* @see https://on.cypress.io/version
|
||||
@@ -496,30 +557,92 @@ declare namespace Cypress {
|
||||
*/
|
||||
log(options: Partial<LogConfig>): Log
|
||||
|
||||
/**
|
||||
* @see https://on.cypress.io/api/commands
|
||||
*/
|
||||
Commands: {
|
||||
/**
|
||||
* Add a custom command
|
||||
* @see https://on.cypress.io/api/commands
|
||||
*/
|
||||
add<T extends keyof Chainable>(name: T, fn: CommandFn<T>): void
|
||||
|
||||
/**
|
||||
* Add a custom parent command
|
||||
* @see https://on.cypress.io/api/commands#Parent-Commands
|
||||
*/
|
||||
add<T extends keyof Chainable>(name: T, options: CommandOptions & {prevSubject: false}, fn: CommandFn<T>): void
|
||||
|
||||
/**
|
||||
* Add a custom child command
|
||||
* @see https://on.cypress.io/api/commands#Child-Commands
|
||||
*/
|
||||
add<T extends keyof Chainable, S = any>(name: T, options: CommandOptions & {prevSubject: true}, fn: CommandFnWithSubject<T, S>): void
|
||||
|
||||
/**
|
||||
* Add a custom child or dual command
|
||||
* @see https://on.cypress.io/api/commands#Validations
|
||||
*/
|
||||
add<T extends keyof Chainable, S extends PrevSubject>(
|
||||
name: T, options: CommandOptions & { prevSubject: S | ['optional'] }, fn: CommandFnWithSubject<T, PrevSubjectMap[S]>,
|
||||
): void
|
||||
|
||||
/**
|
||||
* Add a custom command that allows multiple types as the prevSubject
|
||||
* @see https://on.cypress.io/api/commands#Validations#Allow-Multiple-Types
|
||||
*/
|
||||
add<T extends keyof Chainable, S extends PrevSubject>(
|
||||
name: T, options: CommandOptions & { prevSubject: S[] }, fn: CommandFnWithSubject<T, PrevSubjectMap<void>[S]>,
|
||||
): void
|
||||
|
||||
/**
|
||||
* Add one or more custom commands
|
||||
* @see https://on.cypress.io/api/commands
|
||||
*/
|
||||
addAll<T extends keyof Chainable>(fns: CommandFns): void
|
||||
|
||||
/**
|
||||
* Add one or more custom parent commands
|
||||
* @see https://on.cypress.io/api/commands#Parent-Commands
|
||||
*/
|
||||
addAll<T extends keyof Chainable>(options: CommandOptions & {prevSubject: false}, fns: CommandFns): void
|
||||
|
||||
/**
|
||||
* Add one or more custom child commands
|
||||
* @see https://on.cypress.io/api/commands#Child-Commands
|
||||
*/
|
||||
addAll<T extends keyof Chainable, S = any>(options: CommandOptions & { prevSubject: true }, fns: CommandFnsWithSubject<S>): void
|
||||
|
||||
/**
|
||||
* Add one or more custom commands that validate their prevSubject
|
||||
* @see https://on.cypress.io/api/commands#Validations
|
||||
*/
|
||||
addAll<T extends keyof Chainable, S extends PrevSubject>(
|
||||
options: CommandOptions & { prevSubject: S | ['optional'] }, fns: CommandFnsWithSubject<PrevSubjectMap[S]>,
|
||||
): void
|
||||
|
||||
/**
|
||||
* Add one or more custom commands that allow multiple types as their prevSubject
|
||||
* @see https://on.cypress.io/api/commands#Allow-Multiple-Types
|
||||
*/
|
||||
addAll<T extends keyof Chainable, S extends PrevSubject>(
|
||||
options: CommandOptions & { prevSubject: S[] }, fns: CommandFnsWithSubject<PrevSubjectMap<void>[S]>,
|
||||
): void
|
||||
|
||||
/**
|
||||
* Overwrite an existing Cypress command with a new implementation
|
||||
* @see https://on.cypress.io/api/commands#Overwrite-Existing-Commands
|
||||
*/
|
||||
overwrite<T extends keyof Chainable>(name: T, fn: CommandFnWithOriginalFn<T>): void
|
||||
|
||||
/**
|
||||
* Overwrite an existing Cypress command with a new implementation
|
||||
* @see https://on.cypress.io/api/commands#Overwrite-Existing-Commands
|
||||
*/
|
||||
overwrite<T extends keyof Chainable, S extends PrevSubject>(name: T, fn: CommandFnWithOriginalFnAndSubject<T, PrevSubjectMap[S]>): void
|
||||
|
||||
/**
|
||||
* Add a custom query
|
||||
* @see https://on.cypress.io/api/custom-queries
|
||||
*/
|
||||
addQuery<T extends keyof Chainable>(name: T, fn: QueryFn<T>): void
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -527,16 +650,6 @@ declare namespace Cypress {
|
||||
*/
|
||||
Cookies: {
|
||||
debug(enabled: boolean, options?: Partial<DebugOptions>): void
|
||||
/**
|
||||
* @deprecated Use `cy.session()` instead.
|
||||
* @see https://on.cypress.io/session
|
||||
*/
|
||||
preserveOnce(...names: string[]): void
|
||||
/**
|
||||
* @deprecated Use `cy.session()` instead.
|
||||
* @see https://on.cypress.io/session
|
||||
*/
|
||||
defaults(options: Partial<CookieDefaults>): CookieDefaults
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -635,13 +748,6 @@ declare namespace Cypress {
|
||||
defaults(options: Partial<KeyboardDefaultsOptions>): void
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://on.cypress.io/api/api-server
|
||||
*/
|
||||
Server: {
|
||||
defaults(options: Partial<ServerOptions>): void
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://on.cypress.io/screenshot-api
|
||||
*/
|
||||
@@ -809,7 +915,7 @@ declare namespace Cypress {
|
||||
clear(options?: Partial<ClearOptions>): Chainable<Subject>
|
||||
|
||||
/**
|
||||
* Clear a specific browser cookie for the current superdomain or for the domain specified.
|
||||
* Clear a specific browser cookie for the current hostname or for the domain specified.
|
||||
* Cypress automatically clears all cookies before each test to prevent state from being shared across tests. You shouldn't need to use this command unless you're using it to clear a specific cookie inside a single test.
|
||||
*
|
||||
* @see https://on.cypress.io/clearcookie
|
||||
@@ -817,7 +923,7 @@ declare namespace Cypress {
|
||||
clearCookie(name: string, options?: CookieOptions): Chainable<null>
|
||||
|
||||
/**
|
||||
* Clear all browser cookies for the current superdomain or for the domain specified.
|
||||
* Clear all browser cookies for the current hostname or for the domain specified.
|
||||
* Cypress automatically clears all cookies before each test to prevent state from being shared across tests. You shouldn't need to use this command unless you're using it to clear all cookies or specific cookies inside a single test.
|
||||
*
|
||||
* @see https://on.cypress.io/clearcookies
|
||||
@@ -1147,8 +1253,6 @@ declare namespace Cypress {
|
||||
/**
|
||||
* Save/Restore browser Cookies, LocalStorage, and SessionStorage data resulting from the supplied `setup` function.
|
||||
*
|
||||
* Only available if the `experimentalSessionAndOrigin` config option is enabled.
|
||||
*
|
||||
* @see https://on.cypress.io/session
|
||||
*/
|
||||
session(id: string | object, setup: () => void, options?: SessionOptions): Chainable<null>
|
||||
@@ -1310,14 +1414,14 @@ declare namespace Cypress {
|
||||
get<S = any>(alias: string, options?: Partial<Loggable & Timeoutable & Withinable & Shadow>): Chainable<S>
|
||||
|
||||
/**
|
||||
* Get a browser cookie by its name for the current superdomain or for the domain specified.
|
||||
* Get a browser cookie by its name for the current hostname or for the domain specified.
|
||||
*
|
||||
* @see https://on.cypress.io/getcookie
|
||||
*/
|
||||
getCookie(name: string, options?: CookieOptions): Chainable<Cookie | null>
|
||||
|
||||
/**
|
||||
* Get all of the browser cookies for the current superdomain or for the domain specified.
|
||||
* Get all of the browser cookies for the current hostname or for the domain specified.
|
||||
*
|
||||
* @see https://on.cypress.io/getcookies
|
||||
*/
|
||||
@@ -1491,6 +1595,13 @@ declare namespace Cypress {
|
||||
*/
|
||||
not(selector: string, options?: Partial<Loggable & Timeoutable>): Chainable<JQuery>
|
||||
|
||||
/**
|
||||
* Invoke a command synchronously, without using the command queue.
|
||||
*
|
||||
* @see https://on.cypress.io/custom-queries
|
||||
*/
|
||||
now(name: string, ...args: any[]): Promise<any> | ((subject: any) => any)
|
||||
|
||||
/**
|
||||
* These events come from Cypress as it issues commands and reacts to their state. These are all useful to listen to for debugging purposes.
|
||||
* @see https://on.cypress.io/catalog-of-events#App-Events
|
||||
@@ -1739,66 +1850,6 @@ declare namespace Cypress {
|
||||
*/
|
||||
root<E extends Node = HTMLHtmlElement>(options?: Partial<Loggable>): Chainable<JQuery<E>> // can't do better typing unless we ignore the `.within()` case
|
||||
|
||||
/**
|
||||
* @deprecated Use `cy.intercept()` instead.
|
||||
*
|
||||
* Use `cy.route()` to manage the behavior of network requests.
|
||||
* @see https://on.cypress.io/route
|
||||
* @example
|
||||
* cy.server()
|
||||
* cy.route('https://localhost:7777/users', [{id: 1, name: 'Pat'}])
|
||||
*/
|
||||
route(url: string | RegExp, response?: string | object): Chainable<null>
|
||||
/**
|
||||
* @deprecated Use `cy.intercept()` instead.
|
||||
*
|
||||
* Spy or stub request with specific method and url.
|
||||
*
|
||||
* @see https://on.cypress.io/route
|
||||
* @example
|
||||
* cy.server()
|
||||
* // spy on POST /todos requests
|
||||
* cy.route('POST', '/todos').as('add-todo')
|
||||
*/
|
||||
route(method: string, url: string | RegExp, response?: string | object): Chainable<null>
|
||||
/**
|
||||
* @deprecated Use `cy.intercept()` instead.
|
||||
*
|
||||
* Set a route by returning an object literal from a callback function.
|
||||
* Functions that return a Promise will automatically be awaited.
|
||||
*
|
||||
* @see https://on.cypress.io/route
|
||||
* @example
|
||||
* cy.server()
|
||||
* cy.route(() => {
|
||||
* // your logic here
|
||||
* // return an appropriate routing object here
|
||||
* return {
|
||||
* method: 'POST',
|
||||
* url: '/comments',
|
||||
* response: this.commentsFixture
|
||||
* }
|
||||
* })
|
||||
*/
|
||||
route(fn: () => RouteOptions): Chainable<null>
|
||||
/**
|
||||
* @deprecated Use `cy.intercept()` instead.
|
||||
*
|
||||
* Spy or stub a given route.
|
||||
*
|
||||
* @see https://on.cypress.io/route
|
||||
* @example
|
||||
* cy.server()
|
||||
* cy.route({
|
||||
* method: 'DELETE',
|
||||
* url: '/users',
|
||||
* status: 412,
|
||||
* delay: 1000
|
||||
* // and other options, see documentation
|
||||
* })
|
||||
*/
|
||||
route(options: Partial<RouteOptions>): Chainable<null>
|
||||
|
||||
/**
|
||||
* Take a screenshot of the application under test and the Cypress Command Log.
|
||||
*
|
||||
@@ -1844,26 +1895,6 @@ declare namespace Cypress {
|
||||
*/
|
||||
select(valueOrTextOrIndex: string | number | Array<string | number>, options?: Partial<SelectOptions>): Chainable<Subject>
|
||||
|
||||
/**
|
||||
* @deprecated Use `cy.intercept()` instead.
|
||||
*
|
||||
* Start a server to begin routing responses to `cy.route()` and `cy.request()`.
|
||||
*
|
||||
* @example
|
||||
* // start server
|
||||
* cy.server()
|
||||
* // get default server options
|
||||
* cy.server().should((server) => {
|
||||
* expect(server.delay).to.eq(0)
|
||||
* expect(server.method).to.eq('GET')
|
||||
* expect(server.status).to.eq(200)
|
||||
* // and many others options
|
||||
* })
|
||||
*
|
||||
* @see https://on.cypress.io/server
|
||||
*/
|
||||
server(options?: Partial<ServerOptions>): Chainable<ServerOptions>
|
||||
|
||||
/**
|
||||
* Set a browser cookie.
|
||||
*
|
||||
@@ -2465,10 +2496,6 @@ declare namespace Cypress {
|
||||
|
||||
type Agent<T extends sinon.SinonSpy> = SinonSpyAgent<T> & T
|
||||
|
||||
interface CookieDefaults {
|
||||
preserve: string | string[] | RegExp | ((cookie: Cookie) => boolean)
|
||||
}
|
||||
|
||||
interface Failable {
|
||||
/**
|
||||
* Whether to fail on response codes other than 2xx and 3xx
|
||||
@@ -2698,7 +2725,7 @@ declare namespace Cypress {
|
||||
interface CookieOptions extends Partial<Loggable & Timeoutable> {
|
||||
/**
|
||||
* Domain to set cookies on or get cookies from
|
||||
* @default superdomain of the current app under test
|
||||
* @default hostname of the current app under test
|
||||
*/
|
||||
domain?: string
|
||||
}
|
||||
@@ -2878,8 +2905,7 @@ declare namespace Cypress {
|
||||
*/
|
||||
supportFile: string | false
|
||||
/**
|
||||
* The test isolation ensures a clean browser context between tests. This option is only available when
|
||||
* `experimentalSessionAndOrigin=true`.
|
||||
* The test isolation ensures a clean browser context between tests.
|
||||
*
|
||||
* Cypress will always reset/clear aliases, intercepts, clock, and viewport before each test
|
||||
* to ensure a clean test slate; i.e. this configuration only impacts the browser context.
|
||||
@@ -2887,23 +2913,23 @@ declare namespace Cypress {
|
||||
* Note: the [`cy.session()`](https://on.cypress.io/session) command will inherent this value to determine whether
|
||||
* or not the page is cleared when the command executes. This command is only available in end-to-end testing.
|
||||
*
|
||||
* - on - The page is cleared before each test. Cookies, local storage and session storage in all domains are cleared
|
||||
* - true - The page is cleared before each test. Cookies, local storage and session storage in all domains are cleared
|
||||
* before each test. The `cy.session()` command will also clear the page and current browser context when creating
|
||||
* or restoring the browser session.
|
||||
* - off - The current browser state will persist between tests. The page does not clear before the test and cookies, local
|
||||
* - false - The current browser state will persist between tests. The page does not clear before the test and cookies, local
|
||||
* storage and session storage will be available in the next test. The `cy.session()` command will only clear the
|
||||
* current browser context when creating or restoring the browser session - the current page will not clear.
|
||||
*
|
||||
* Tradeoffs:
|
||||
* Turning test isolation off may improve performance of end-to-end tests, however, previous tests could impact the
|
||||
* browser state of the next test and cause inconsistency when using .only(). Be mindful to write isolated tests when
|
||||
* test isolation is off. If a test in the suite impacts the state of other tests and it were to fail, you could see
|
||||
* test isolation is false. If a test in the suite impacts the state of other tests and it were to fail, you could see
|
||||
* misleading errors in later tests which makes debugging clunky. See the [documentation](https://on.cypress.io/test-isolation)
|
||||
* for more information.
|
||||
*
|
||||
* @default null, when experimentalSessionAndOrigin=false. The default is 'on' when experimentalSessionAndOrigin=true.
|
||||
* @default true
|
||||
*/
|
||||
testIsolation: null | 'on' | 'off'
|
||||
testIsolation: boolean
|
||||
/**
|
||||
* Path to folder where videos will be saved after a headless or CI run
|
||||
* @default "cypress/videos"
|
||||
@@ -2964,11 +2990,6 @@ declare namespace Cypress {
|
||||
* @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
|
||||
/**
|
||||
* Whether Cypress will search for and replace obstructive code in third party .js or .html files.
|
||||
* NOTE: Setting this flag to true removes Subresource Integrity (SRI).
|
||||
@@ -3065,6 +3086,11 @@ declare namespace Cypress {
|
||||
* @default false
|
||||
*/
|
||||
experimentalRunAllSpecs?: boolean
|
||||
/**
|
||||
* Enables support for require/import within cy.origin.
|
||||
* @default false
|
||||
*/
|
||||
experimentalOriginDependencies?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3140,19 +3166,17 @@ declare namespace Cypress {
|
||||
socketIoRoute: string
|
||||
spec: Cypress['spec'] | null
|
||||
specs: Array<Cypress['spec']>
|
||||
xhrRoute: string
|
||||
xhrUrl: string
|
||||
}
|
||||
|
||||
interface SuiteConfigOverrides extends Partial<
|
||||
Pick<ConfigOptions, 'animationDistanceThreshold' | 'blockHosts' | 'defaultCommandTimeout' | 'env' | 'execTimeout' | 'includeShadowDom' | 'numTestsKeptInMemory' | 'pageLoadTimeout' | 'redirectionLimit' | 'requestTimeout' | 'responseTimeout' | 'retries' | 'screenshotOnRunFailure' | 'slowTestThreshold' | 'scrollBehavior' | 'taskTimeout' | 'viewportHeight' | 'viewportWidth' | 'waitForAnimations' | 'experimentalSessionAndOrigin'>
|
||||
Pick<ConfigOptions, 'animationDistanceThreshold' | 'blockHosts' | 'defaultCommandTimeout' | 'env' | 'execTimeout' | 'includeShadowDom' | 'numTestsKeptInMemory' | 'pageLoadTimeout' | 'redirectionLimit' | 'requestTimeout' | 'responseTimeout' | 'retries' | 'screenshotOnRunFailure' | 'slowTestThreshold' | 'scrollBehavior' | 'taskTimeout' | 'viewportHeight' | 'viewportWidth' | 'waitForAnimations'>
|
||||
>, Partial<Pick<ResolvedConfigOptions, 'baseUrl' | 'testIsolation'>> {
|
||||
browser?: IsBrowserMatcher | IsBrowserMatcher[]
|
||||
keystrokeDelay?: number
|
||||
}
|
||||
|
||||
interface TestConfigOverrides extends Partial<
|
||||
Pick<ConfigOptions, 'animationDistanceThreshold' | 'blockHosts' | 'defaultCommandTimeout' | 'env' | 'execTimeout' | 'includeShadowDom' | 'numTestsKeptInMemory' | 'pageLoadTimeout' | 'redirectionLimit' | 'requestTimeout' | 'responseTimeout' | 'retries' | 'screenshotOnRunFailure' | 'slowTestThreshold' | 'scrollBehavior' | 'taskTimeout' | 'viewportHeight' | 'viewportWidth' | 'waitForAnimations' | 'experimentalSessionAndOrigin'>
|
||||
Pick<ConfigOptions, 'animationDistanceThreshold' | 'blockHosts' | 'defaultCommandTimeout' | 'env' | 'execTimeout' | 'includeShadowDom' | 'numTestsKeptInMemory' | 'pageLoadTimeout' | 'redirectionLimit' | 'requestTimeout' | 'responseTimeout' | 'retries' | 'screenshotOnRunFailure' | 'slowTestThreshold' | 'scrollBehavior' | 'taskTimeout' | 'viewportHeight' | 'viewportWidth' | 'waitForAnimations'>
|
||||
>, Partial<Pick<ResolvedConfigOptions, 'baseUrl'>> {
|
||||
browser?: IsBrowserMatcher | IsBrowserMatcher[]
|
||||
keystrokeDelay?: number
|
||||
@@ -3209,7 +3233,7 @@ declare namespace Cypress {
|
||||
}
|
||||
}
|
||||
|
||||
interface ComponentConfigOptions<ComponentDevServerOpts = any> extends Omit<CoreConfigOptions, 'baseUrl' | 'experimentalSessionAndOrigin' | 'experimentalStudio'> {
|
||||
interface ComponentConfigOptions<ComponentDevServerOpts = any> extends Omit<CoreConfigOptions, 'baseUrl' | 'experimentalStudio'> {
|
||||
devServer: DevServerFn<ComponentDevServerOpts> | DevServerConfigOptions
|
||||
devServerConfig?: ComponentDevServerOpts
|
||||
/**
|
||||
@@ -3398,28 +3422,6 @@ declare namespace Cypress {
|
||||
interval: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting default options for cy.server()
|
||||
* @see https://on.cypress.io/server
|
||||
*/
|
||||
interface ServerOptions {
|
||||
delay: number
|
||||
method: HttpMethod
|
||||
status: number
|
||||
headers: object
|
||||
response: any
|
||||
onRequest(...args: any[]): void
|
||||
onResponse(...args: any[]): void
|
||||
onAbort(...args: any[]): void
|
||||
enable: boolean
|
||||
force404: boolean
|
||||
urlMatchingOptions: object
|
||||
ignore(xhr: Request): void
|
||||
onAnyRequest(route: RouteOptions, proxy: any): void
|
||||
onAnyResponse(route: RouteOptions, proxy: any): void
|
||||
onAnyAbort(route: RouteOptions, proxy: any): void
|
||||
}
|
||||
|
||||
interface Session {
|
||||
// Clear all saved sessions and re-run the current spec file.
|
||||
clearAllSavedSessions: () => Promise<void>
|
||||
@@ -5839,7 +5841,7 @@ declare namespace Cypress {
|
||||
* Useful for debugging purposes if you're confused about the order in which commands will execute.
|
||||
* @see https://on.cypress.io/catalog-of-events#App-Events
|
||||
*/
|
||||
(action: 'command:enqueued', fn: (command: EnqueuedCommand) => void): Cypress
|
||||
(action: 'command:enqueued', fn: (command: EnqueuedCommandAttributes) => void): Cypress
|
||||
/**
|
||||
* Fires when cy begins actually running and executing your command.
|
||||
* Useful for debugging and understanding how the command queue is async.
|
||||
@@ -5967,7 +5969,7 @@ declare namespace Cypress {
|
||||
sameSite?: SameSiteStatus
|
||||
}
|
||||
|
||||
interface EnqueuedCommand {
|
||||
interface EnqueuedCommandAttributes {
|
||||
id: string
|
||||
name: string
|
||||
args: any[]
|
||||
@@ -5975,9 +5977,17 @@ declare namespace Cypress {
|
||||
chainerId: string
|
||||
injected: boolean
|
||||
userInvocationStack?: string
|
||||
query?: boolean
|
||||
fn(...args: any[]): any
|
||||
}
|
||||
|
||||
interface Command {
|
||||
get<K extends keyof EnqueuedCommandAttributes>(attr: K): EnqueuedCommandAttributes[K]
|
||||
get(): EnqueuedCommandAttributes
|
||||
set<K extends keyof EnqueuedCommandAttributes>(key: K, value: EnqueuedCommandAttributes[K]): Log
|
||||
set(options: Partial<EnqueuedCommandAttributes>): Log
|
||||
}
|
||||
|
||||
interface Exec {
|
||||
code: number
|
||||
stdout: string
|
||||
@@ -6056,28 +6066,6 @@ declare namespace Cypress {
|
||||
viewportHeight: number
|
||||
}
|
||||
|
||||
interface WaitXHR {
|
||||
duration: number
|
||||
id: string
|
||||
method: HttpMethod
|
||||
request: {
|
||||
body: string | ObjectLike
|
||||
headers: ObjectLike
|
||||
}
|
||||
requestBody: WaitXHR['request']['body']
|
||||
requestHeaders: WaitXHR['request']['headers']
|
||||
response: {
|
||||
body: string | ObjectLike
|
||||
headers: ObjectLike
|
||||
}
|
||||
responseBody: WaitXHR['response']['body']
|
||||
responseHeaders: WaitXHR['response']['headers']
|
||||
status: number
|
||||
statusMessage: string
|
||||
url: string
|
||||
xhr: XMLHttpRequest
|
||||
}
|
||||
|
||||
type Encodings = 'ascii' | 'base64' | 'binary' | 'hex' | 'latin1' | 'utf8' | 'utf-8' | 'ucs2' | 'ucs-2' | 'utf16le' | 'utf-16le' | null
|
||||
type PositionType = 'topLeft' | 'top' | 'topRight' | 'left' | 'center' | 'right' | 'bottomLeft' | 'bottom' | 'bottomRight'
|
||||
type ViewportPreset = 'macbook-16' | 'macbook-15' | 'macbook-13' | 'macbook-11' | 'ipad-2' | 'ipad-mini' | 'iphone-xr' | 'iphone-x' | 'iphone-6+' | 'iphone-se2' | 'iphone-8' | 'iphone-7' | 'iphone-6' | 'iphone-5' | 'iphone-4' | 'iphone-3' | 'samsung-s10' | 'samsung-note9'
|
||||
|
||||
@@ -46,7 +46,7 @@ Cypress.on('scrolled', ($el) => {
|
||||
})
|
||||
|
||||
Cypress.on('command:enqueued', (command) => {
|
||||
command // $ExpectType EnqueuedCommand
|
||||
command // $ExpectType EnqueuedCommandAttributes
|
||||
})
|
||||
|
||||
Cypress.on('command:start', (command) => {
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace CypressConfigTests {
|
||||
Cypress.config({ e2e: { baseUrl: '.', }}) // $ExpectError
|
||||
Cypress.config({ component: { baseUrl: '.', devServer: () => ({} as any) } }) // $ExpectError
|
||||
Cypress.config({ e2e: { indexHtmlFile: 'index.html' } }) // $ExpectError
|
||||
Cypress.config({ testIsolation: 'off' }) // $ExpectError
|
||||
Cypress.config({ testIsolation: false }) // $ExpectError
|
||||
|
||||
Cypress.config('taskTimeout') // $ExpectType number
|
||||
Cypress.config('includeShadowDom') // $ExpectType boolean
|
||||
@@ -69,6 +69,7 @@ namespace CypressIsCyTests {
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
newCommand: (arg: string) => Chainable<number>
|
||||
newQuery: (arg: string) => Chainable<number>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,6 +292,27 @@ namespace CypressCommandsTests {
|
||||
|
||||
return originalFn(element, text, options)
|
||||
})
|
||||
|
||||
Cypress.Commands.addQuery('newQuery', function(arg) {
|
||||
this // $ExpectType Command
|
||||
arg // $ExpectType string
|
||||
return () => 3
|
||||
})
|
||||
}
|
||||
|
||||
namespace CypressNowTest {
|
||||
cy.now('get') // $ExpectType Promise<any> | ((subject: any) => any)
|
||||
}
|
||||
|
||||
namespace CypressEnsuresTest {
|
||||
Cypress.ensure.isType('', ['optional', 'element'], 'newQuery', cy) // $ExpectType void
|
||||
Cypress.ensure.isElement('', 'newQuery', cy) // $ExpectType void
|
||||
Cypress.ensure.isWindow('', 'newQuery', cy) // $ExpectType void
|
||||
Cypress.ensure.isDocument('', 'newQuery', cy) // $ExpectType void
|
||||
|
||||
Cypress.ensure.isAttached('', 'newQuery', cy) // $ExpectType void
|
||||
Cypress.ensure.isNotDisabled('', 'newQuery') // $ExpectType void
|
||||
Cypress.ensure.isVisible('', 'newQuery') // $ExpectType void
|
||||
}
|
||||
|
||||
namespace CypressLogsTest {
|
||||
@@ -868,7 +890,7 @@ namespace CypressTestConfigOverridesTests {
|
||||
retries: { run: 3 } // $ExpectError
|
||||
}, () => { })
|
||||
it('test', {
|
||||
testIsolation: 'off', // $ExpectError
|
||||
testIsolation: false, // $ExpectError
|
||||
}, () => { })
|
||||
|
||||
it.skip('test', {}, () => {})
|
||||
@@ -887,7 +909,7 @@ namespace CypressTestConfigOverridesTests {
|
||||
}, () => {})
|
||||
|
||||
describe('suite', {
|
||||
testIsolation: 'off',
|
||||
testIsolation: false,
|
||||
}, () => {})
|
||||
|
||||
context('suite', {}, () => {})
|
||||
|
||||
@@ -18,14 +18,6 @@ result // $ExpectType boolean
|
||||
|
||||
Cypress.minimatch('/users/1/comments', '/users/*/comments') // $ExpectType boolean
|
||||
|
||||
// check if cy.server() yields default server options
|
||||
cy.server().should((server) => {
|
||||
server // $ExpectType ServerOptions
|
||||
expect(server.delay).to.eq(0)
|
||||
expect(server.method).to.eq('GET')
|
||||
expect(server.status).to.eq(200)
|
||||
})
|
||||
|
||||
cy.visit('https://www.acme.com/', {
|
||||
auth: {
|
||||
username: 'wile',
|
||||
@@ -33,13 +25,6 @@ cy.visit('https://www.acme.com/', {
|
||||
}
|
||||
})
|
||||
|
||||
const serverOptions: Partial<Cypress.ServerOptions> = {
|
||||
delay: 100,
|
||||
ignore: () => true
|
||||
}
|
||||
|
||||
cy.server(serverOptions)
|
||||
|
||||
Cypress.spec.name // $ExpectType string
|
||||
Cypress.spec.relative // $ExpectType string
|
||||
Cypress.spec.absolute // $ExpectType string
|
||||
|
||||
@@ -27,7 +27,7 @@ const cypressSchematicPackagePath = path.join(__dirname, '..')
|
||||
const ANGULAR_PROJECTS: ProjectFixtureDir[] = ['angular-13', 'angular-14']
|
||||
|
||||
describe('ng add @cypress/schematic / only e2e', function () {
|
||||
this.timeout(1000 * 60 * 4)
|
||||
this.timeout(1000 * 60 * 5)
|
||||
|
||||
for (const project of ANGULAR_PROJECTS) {
|
||||
it('should install e2e files by default', async () => {
|
||||
|
||||
@@ -44,6 +44,18 @@ export function setupHooks (optionalCallback?: Function) {
|
||||
)
|
||||
})
|
||||
|
||||
Cypress.Commands.overwrite('session', () => {
|
||||
throw new Error(
|
||||
'cy.session from a component spec is not allowed',
|
||||
)
|
||||
})
|
||||
|
||||
Cypress.Commands.overwrite('origin', () => {
|
||||
throw new Error(
|
||||
'cy.origin from a component spec is not allowed',
|
||||
)
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
Cypress.on('test:before:run', () => {
|
||||
optionalCallback?.()
|
||||
|
||||
@@ -7,8 +7,7 @@ You can install and bring [Testing library/Cypress](https://testing-library.com/
|
||||
|
||||
```js
|
||||
it('loads and displays greeting (testing-lib)', () => {
|
||||
cy.server()
|
||||
cy.route('/greeting', { greeting: 'Hello there' }).as('greet')
|
||||
cy.intercept('/greeting', { greeting: 'Hello there' }).as('greet')
|
||||
|
||||
const url = '/greeting'
|
||||
mount(<Fetch url={url} />)
|
||||
@@ -19,7 +18,7 @@ it('loads and displays greeting (testing-lib)', () => {
|
||||
cy.findByRole('heading').should('have.text', 'Hello there')
|
||||
cy.findByRole('button').should('be.disabled')
|
||||
cy.get('@greet')
|
||||
.its('url')
|
||||
.its('response.url')
|
||||
.should('match', /\/greeting$/)
|
||||
})
|
||||
```
|
||||
|
||||
@@ -3,8 +3,7 @@ import { mount } from '@cypress/react'
|
||||
import Fetcher from './fetcher'
|
||||
|
||||
it('loads and displays greeting', () => {
|
||||
cy.server()
|
||||
cy.route('/greeting', { greeting: 'Hello there' }).as('greet')
|
||||
cy.intercept('/greeting', { greeting: 'Hello there' }).as('greet')
|
||||
|
||||
const url = '/greeting'
|
||||
|
||||
@@ -14,6 +13,6 @@ it('loads and displays greeting', () => {
|
||||
cy.get('[role=heading]').should('have.text', 'Hello there')
|
||||
cy.get('[role=button]').should('be.disabled')
|
||||
cy.get('@greet')
|
||||
.its('url')
|
||||
.its('response.url')
|
||||
.should('match', /\/greeting$/)
|
||||
})
|
||||
|
||||
@@ -7,8 +7,7 @@ import Fetcher from './fetcher'
|
||||
|
||||
// NOTE: this doesn't work because of update to the @testing/library v7. Looks like build issue with current webpack config
|
||||
it.skip('loads and displays greeting (testing-lib)', () => {
|
||||
cy.server()
|
||||
cy.route('/greeting', { greeting: 'Hello there' }).as('greet')
|
||||
cy.intercept('/greeting', { greeting: 'Hello there' }).as('greet')
|
||||
|
||||
const url = '/greeting'
|
||||
|
||||
@@ -21,6 +20,6 @@ it.skip('loads and displays greeting (testing-lib)', () => {
|
||||
cy.findByRole('heading').should('have.text', 'Hello there')
|
||||
cy.findByRole('button').should('be.disabled')
|
||||
cy.get('@greet')
|
||||
.its('url')
|
||||
.its('response.url')
|
||||
.should('match', /\/greeting$/)
|
||||
})
|
||||
|
||||
@@ -15,14 +15,8 @@ context('Users', () => {
|
||||
})
|
||||
|
||||
describe('Network State', () => {
|
||||
beforeEach(() => {
|
||||
cy.server()
|
||||
// mount the component after defining routes in tests
|
||||
// preventing race conditions where you wait on untouched routes
|
||||
})
|
||||
|
||||
it('can inspect real data in XHR', () => {
|
||||
cy.route('/users?_limit=3').as('users')
|
||||
cy.intercept('/users?_limit=3').as('users')
|
||||
mount(<Users />)
|
||||
cy.wait('@users')
|
||||
.its('response.body')
|
||||
@@ -34,7 +28,7 @@ context('Users', () => {
|
||||
it('can display mock XHR response', () => {
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
|
||||
cy.route('GET', '/users?_limit=3', users).as('users')
|
||||
cy.intercept('GET', '/users?_limit=3', users).as('users')
|
||||
mount(<Users />)
|
||||
cy.get('li')
|
||||
.should('have.length', 1)
|
||||
@@ -45,7 +39,7 @@ context('Users', () => {
|
||||
it('can inspect mocked XHR', () => {
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
|
||||
cy.route('GET', '/users?_limit=3', users).as('users')
|
||||
cy.intercept('GET', '/users?_limit=3', users).as('users')
|
||||
mount(<Users />)
|
||||
cy.wait('@users')
|
||||
.its('response.body')
|
||||
@@ -55,10 +49,8 @@ context('Users', () => {
|
||||
it('can delay and wait on XHR', () => {
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
|
||||
cy.route({
|
||||
method: 'GET',
|
||||
url: '/users?_limit=3',
|
||||
response: users,
|
||||
cy.intercept('GET', '/users?_limit=3', {
|
||||
body: users,
|
||||
delay: 1000,
|
||||
}).as('users')
|
||||
|
||||
|
||||
@@ -13,15 +13,9 @@ describe('Users with Fetch', () => {
|
||||
|
||||
// https://github.com/bahmutov/@cypress/react/issues/347
|
||||
context('mocking', () => {
|
||||
beforeEach(() => {
|
||||
cy.server()
|
||||
// mount the component after defining routes in tests
|
||||
// preventing race conditions where you wait on untouched routes
|
||||
})
|
||||
|
||||
it('can inspect real data from the server', () => {
|
||||
// spy on the request
|
||||
cy.route('/users?_limit=3').as('users')
|
||||
cy.intercept('/users?_limit=3').as('users')
|
||||
mount(<Users />)
|
||||
cy.wait('@users')
|
||||
.its('response.body')
|
||||
@@ -34,7 +28,7 @@ describe('Users with Fetch', () => {
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
|
||||
// stub the request
|
||||
cy.route('GET', '/users?_limit=3', users).as('users')
|
||||
cy.intercept('GET', '/users?_limit=3', users).as('users')
|
||||
mount(<Users />)
|
||||
cy.get('li')
|
||||
.should('have.length', 1)
|
||||
@@ -45,7 +39,7 @@ describe('Users with Fetch', () => {
|
||||
it('can inspect mocked network reaponse', () => {
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
|
||||
cy.route('GET', '/users?_limit=3', users).as('users')
|
||||
cy.intercept('GET', '/users?_limit=3', users).as('users')
|
||||
mount(<Users />)
|
||||
cy.wait('@users')
|
||||
.its('response.body')
|
||||
@@ -55,10 +49,8 @@ describe('Users with Fetch', () => {
|
||||
it('can delay and wait on Ajax call', () => {
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
|
||||
cy.route({
|
||||
method: 'GET',
|
||||
url: '/users?_limit=3',
|
||||
response: users,
|
||||
cy.intercept('GET', '/users?_limit=3', {
|
||||
body: users,
|
||||
delay: 1000,
|
||||
}).as('users')
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import { defineConfig } from 'cypress'
|
||||
export default defineConfig({
|
||||
projectId: 'ypt4pf',
|
||||
e2e: {
|
||||
experimentalSessionAndOrigin: true,
|
||||
defaultCommandTimeout: 10000, // these take a bit longer b/c they're e2e open mode test
|
||||
async setupNodeEvents (on, config) {
|
||||
if (!process.env.HTTP_PROXY_TARGET_FOR_ORIGIN_REQUESTS) {
|
||||
|
||||
@@ -10,21 +10,18 @@ describe('Fetching users with polyfill', () => {
|
||||
})
|
||||
|
||||
it('can spy on the fetch requests', () => {
|
||||
cy.server()
|
||||
cy.route('/users?_limit=3').as('users')
|
||||
cy.intercept('/users?_limit=3').as('users')
|
||||
mount(Users)
|
||||
cy.wait('@users')
|
||||
.its('responseBody.length')
|
||||
.its('response.body.length')
|
||||
.then((length) => {
|
||||
cy.get('.user').should('have.length', length)
|
||||
})
|
||||
})
|
||||
|
||||
it('shows loading UI while fetch is happening', () => {
|
||||
cy.server()
|
||||
cy.route({
|
||||
url: '/users?_limit=3',
|
||||
response: 'fixture:users',
|
||||
cy.intercept('/users?_limit=3', {
|
||||
fixture: 'users',
|
||||
delay: 1000,
|
||||
})
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ describe('AjaxList', () => {
|
||||
context('using cy.intercept()', () => {
|
||||
// because this component loads data right away
|
||||
// we need to setup XHR intercepts BEFORE mounting it
|
||||
// thus each test will first do its "cy.route"
|
||||
// thus each test will first do its "cy.intercept"
|
||||
// then will mount the component
|
||||
|
||||
it('loads list of posts', () => {
|
||||
@@ -60,62 +60,4 @@ describe('AjaxList', () => {
|
||||
cy.get('li').should('have.length', 1)
|
||||
})
|
||||
})
|
||||
|
||||
context('using cy.route()', () => {
|
||||
// because this component loads data right away
|
||||
// we need to setup XHR intercepts BEFORE mounting it
|
||||
// thus each test will first do its "cy.route"
|
||||
// then will mount the component
|
||||
|
||||
it('loads list of posts', () => {
|
||||
mount(AjaxList)
|
||||
cy.get('li').should('have.length', 3)
|
||||
})
|
||||
|
||||
it('can inspect real data in XHR', () => {
|
||||
cy.server()
|
||||
cy.route('/users?_limit=3').as('users')
|
||||
mount(AjaxList)
|
||||
|
||||
cy.wait('@users').its('response.body').should('have.length', 3)
|
||||
})
|
||||
|
||||
it('can display mock XHR response', () => {
|
||||
cy.server()
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
|
||||
cy.route('GET', '/users?_limit=3', users).as('users')
|
||||
mount(AjaxList)
|
||||
|
||||
cy.get('li').should('have.length', 1).first().contains('foo')
|
||||
})
|
||||
|
||||
it('can inspect mocked XHR', () => {
|
||||
cy.server()
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
|
||||
cy.route('GET', '/users?_limit=3', users).as('users')
|
||||
mount(AjaxList)
|
||||
|
||||
cy.wait('@users').its('response.body').should('deep.equal', users)
|
||||
})
|
||||
|
||||
it('can delay and wait on XHR', () => {
|
||||
cy.server()
|
||||
const users = [{ id: 1, name: 'foo' }]
|
||||
|
||||
cy.route({
|
||||
method: 'GET',
|
||||
url: '/users?_limit=3',
|
||||
response: users,
|
||||
delay: 1000,
|
||||
}).as('users')
|
||||
|
||||
mount(AjaxList)
|
||||
|
||||
cy.get('li').should('have.length', 0)
|
||||
cy.wait('@users')
|
||||
cy.get('li').should('have.length', 1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1 +1 @@
|
||||
**/tsconfig.json
|
||||
**/tsconfig.json
|
||||
|
||||
@@ -222,7 +222,7 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F
|
||||
})
|
||||
.tap((opts) => {
|
||||
if (opts.devtool === false) {
|
||||
// disable any overrides if we've explictly turned off sourcemaps
|
||||
// disable any overrides if we've explicitly turned off sourcemaps
|
||||
overrideSourceMaps(false, options.typescript)
|
||||
|
||||
return
|
||||
@@ -248,6 +248,7 @@ const preprocessor: WebpackPreprocessor = (options: PreprocessorOptions = {}): F
|
||||
// so that it's working with plain javascript
|
||||
webpackOptions.module.rules.unshift({
|
||||
test: /\.(js|ts|jsx|tsx)$/,
|
||||
exclude: /node_modules/,
|
||||
use: [{
|
||||
loader: require.resolve('@cypress/webpack-preprocessor/dist/lib/cross-origin-callback-loader.js'),
|
||||
options: {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Unreleased Cypress App Changes
|
||||
|
||||
## Breaking
|
||||
|
||||
- Removed the `rawJson` configuration data from `Cypress.state()`. Addressed [#23945](https://github.com/cypress-io/cypress/issues/23945).
|
||||
@@ -16,7 +16,8 @@ describe('Reporter Header', () => {
|
||||
cy.get('[data-selected-spec="false"]').should('have.length', '27')
|
||||
})
|
||||
|
||||
it('filters the list of specs when searching for specs', () => {
|
||||
// TODO: Reenable as part of https://github.com/cypress-io/cypress/issues/23902
|
||||
it.skip('filters the list of specs when searching for specs', () => {
|
||||
cy.get('body').type('f')
|
||||
|
||||
cy.findByTestId('specs-list-panel').within(() => {
|
||||
@@ -28,7 +29,7 @@ describe('Reporter Header', () => {
|
||||
|
||||
cy.get('@searchInput').clear()
|
||||
|
||||
cy.get('[data-cy="spec-file-item"]').should('have.length', 3)
|
||||
cy.get('[data-cy="spec-file-item"]').should('have.length', 23)
|
||||
|
||||
cy.get('@searchInput').type('asdf', { force: true })
|
||||
|
||||
|
||||
@@ -321,107 +321,6 @@ describe('errors ui', {
|
||||
})
|
||||
})
|
||||
|
||||
it('cy.route', () => {
|
||||
const verify = loadErrorSpec({
|
||||
filePath: 'errors/route.cy.js',
|
||||
failCount: 9,
|
||||
})
|
||||
|
||||
verify('callback assertion failure', {
|
||||
column: 27,
|
||||
message: `expected 'actual' to equal 'expected'`,
|
||||
})
|
||||
|
||||
verify('callback exception', {
|
||||
column: 12,
|
||||
message: 'bar is not a function',
|
||||
})
|
||||
|
||||
verify('command failure', {
|
||||
column: 10,
|
||||
message: 'Expected to find element: #does-not-exist, but never found it',
|
||||
})
|
||||
|
||||
verify('onAbort assertion failure', {
|
||||
column: 29,
|
||||
codeFrameText: 'onAbort',
|
||||
message: `expected 'actual' to equal 'expected'`,
|
||||
})
|
||||
|
||||
verify('onAbort exception', {
|
||||
column: 14,
|
||||
codeFrameText: 'onAbort',
|
||||
message: 'bar is not a function',
|
||||
})
|
||||
|
||||
verify('onRequest assertion failure', {
|
||||
column: 29,
|
||||
codeFrameText: 'onRequest',
|
||||
message: `expected 'actual' to equal 'expected'`,
|
||||
})
|
||||
|
||||
verify('onRequest exception', {
|
||||
column: 14,
|
||||
codeFrameText: 'onRequest',
|
||||
message: 'bar is not a function',
|
||||
})
|
||||
|
||||
verify('onResponse assertion failure', {
|
||||
column: 29,
|
||||
codeFrameText: 'onResponse',
|
||||
message: `expected 'actual' to equal 'expected'`,
|
||||
})
|
||||
|
||||
verify('onResponse exception', {
|
||||
column: 14,
|
||||
codeFrameText: 'onResponse',
|
||||
message: 'bar is not a function',
|
||||
})
|
||||
})
|
||||
|
||||
it('cy.server', () => {
|
||||
const verify = loadErrorSpec({
|
||||
filePath: 'errors/server.cy.js',
|
||||
failCount: 6,
|
||||
})
|
||||
|
||||
verify('onAbort assertion failure', {
|
||||
column: 29,
|
||||
codeFrameText: 'onAbort',
|
||||
message: `expected 'actual' to equal 'expected'`,
|
||||
})
|
||||
|
||||
verify('onAbort exception', {
|
||||
column: 14,
|
||||
codeFrameText: 'onAbort',
|
||||
message: 'bar is not a function',
|
||||
})
|
||||
|
||||
verify('onRequest assertion failure', {
|
||||
column: 29,
|
||||
codeFrameText: 'onRequest',
|
||||
message: `expected 'actual' to equal 'expected'`,
|
||||
})
|
||||
|
||||
verify('onRequest exception', {
|
||||
column: 14,
|
||||
codeFrameText: 'onRequest',
|
||||
message: 'bar is not a function',
|
||||
})
|
||||
|
||||
verify('onResponse assertion failure', {
|
||||
column: 29,
|
||||
codeFrameText: 'onResponse',
|
||||
message: `expected 'actual' to equal 'expected'`,
|
||||
})
|
||||
|
||||
verify('onResponse exception', {
|
||||
column: 14,
|
||||
codeFrameText: 'onResponse',
|
||||
message: 'bar is not a function',
|
||||
})
|
||||
})
|
||||
|
||||
it('cy.readFile', () => {
|
||||
const verify = loadErrorSpec({
|
||||
filePath: 'errors/readfile.cy.js',
|
||||
|
||||
@@ -347,13 +347,19 @@ describe('runner/cypress sessions.ui.spec', {
|
||||
// cy.percySnapshot() // TODO: restore when Percy CSS is fixed. See https://github.com/cypress-io/cypress/issues/23435
|
||||
})
|
||||
|
||||
describe('errors', () => {
|
||||
describe('errors', { testIsolation: false }, () => {
|
||||
describe('created session', () => {
|
||||
before(() => {
|
||||
loadSpec({
|
||||
projectName: 'session-and-origin-e2e-specs',
|
||||
filePath: 'session/errors.cy.js',
|
||||
failCount: 7,
|
||||
cy.then(async () => {
|
||||
await Cypress.action('cy:url:changed', '')
|
||||
await Cypress.action('cy:visit:blank', { type: 'on' })
|
||||
})
|
||||
.then(() => {
|
||||
loadSpec({
|
||||
projectName: 'session-and-origin-e2e-specs',
|
||||
filePath: 'session/errors.cy.js',
|
||||
failCount: 7,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -423,11 +429,7 @@ describe('runner/cypress sessions.ui.spec', {
|
||||
systemTestTitle: 'validate - throws an error',
|
||||
errMessage: 'Something went wrong!',
|
||||
},
|
||||
].forEach((opts, index) => {
|
||||
if (index !== 5) {
|
||||
return
|
||||
}
|
||||
|
||||
].forEach((opts) => {
|
||||
const { testCase, systemTestTitle, errMessage } = opts
|
||||
|
||||
it(`has test error when validate ${testCase}`, () => {
|
||||
@@ -544,20 +546,26 @@ describe('runner/cypress sessions.ui.spec', {
|
||||
|
||||
describe('successfully recreated session', () => {
|
||||
before(() => {
|
||||
loadSpec({
|
||||
projectName: 'session-and-origin-e2e-specs',
|
||||
filePath: 'session/errors.cy.js',
|
||||
passCount: 7,
|
||||
failCount: 0,
|
||||
setup () {
|
||||
cy.window().then((win) => {
|
||||
cy.then(async () => {
|
||||
await Cypress.action('cy:url:changed', '')
|
||||
await Cypress.action('cy:visit:blank', { type: 'on' })
|
||||
})
|
||||
.then(() => {
|
||||
loadSpec({
|
||||
projectName: 'session-and-origin-e2e-specs',
|
||||
filePath: 'session/errors.cy.js',
|
||||
passCount: 7,
|
||||
failCount: 0,
|
||||
setup () {
|
||||
cy.window().then((win) => {
|
||||
// @ts-ignore
|
||||
return win.CYPRESS_TEST_DATA = {
|
||||
restoreSessionWithValidationFailure: true,
|
||||
successfullyRecreatedSession: true,
|
||||
}
|
||||
})
|
||||
},
|
||||
return win.CYPRESS_TEST_DATA = {
|
||||
restoreSessionWithValidationFailure: true,
|
||||
successfullyRecreatedSession: true,
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -593,6 +601,10 @@ describe('runner/cypress sessions.ui.spec', {
|
||||
errMessage: 'Something went wrong!',
|
||||
},
|
||||
].forEach(({ testCase, systemTestTitle, errMessage }, index) => {
|
||||
if (index !== 0) {
|
||||
return
|
||||
}
|
||||
|
||||
it(`has test error when validate ${testCase}`, () => {
|
||||
cy.contains('.test', systemTestTitle).as('example_test')
|
||||
|
||||
@@ -614,20 +626,26 @@ describe('runner/cypress sessions.ui.spec', {
|
||||
|
||||
describe('failed to recreated session', () => {
|
||||
before(() => {
|
||||
loadSpec({
|
||||
projectName: 'session-and-origin-e2e-specs',
|
||||
filePath: 'session/errors.cy.js',
|
||||
passCount: 0,
|
||||
failCount: 7,
|
||||
setup () {
|
||||
cy.window().then((win) => {
|
||||
cy.then(async () => {
|
||||
await Cypress.action('cy:url:changed', '')
|
||||
await Cypress.action('cy:visit:blank', { type: 'on' })
|
||||
})
|
||||
.then(() => {
|
||||
loadSpec({
|
||||
projectName: 'session-and-origin-e2e-specs',
|
||||
filePath: 'session/errors.cy.js',
|
||||
passCount: 0,
|
||||
failCount: 7,
|
||||
setup () {
|
||||
cy.window().then((win) => {
|
||||
// @ts-ignore
|
||||
return win.CYPRESS_TEST_DATA = {
|
||||
restoreSessionWithValidationFailure: true,
|
||||
successfullyRecreatedSession: false,
|
||||
}
|
||||
})
|
||||
},
|
||||
return win.CYPRESS_TEST_DATA = {
|
||||
restoreSessionWithValidationFailure: true,
|
||||
successfullyRecreatedSession: false,
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -202,18 +202,6 @@ describe('App: Settings', () => {
|
||||
})
|
||||
})
|
||||
|
||||
cy.get('[data-cy="experiment-experimentalSessionAndOrigin"]').within(() => {
|
||||
cy.validateExternalLink({
|
||||
name: 'cy.session()',
|
||||
href: 'https://on.cypress.io/session',
|
||||
})
|
||||
|
||||
cy.validateExternalLink({
|
||||
name: 'cy.origin()',
|
||||
href: 'https://on.cypress.io/origin',
|
||||
})
|
||||
})
|
||||
|
||||
cy.get('[data-cy="experiment-experimentalSourceRewriting"]').within(() => {
|
||||
cy.validateExternalLink({
|
||||
name: '#5273',
|
||||
|
||||
@@ -245,7 +245,7 @@ describe('App: Spec List (E2E)', () => {
|
||||
cy.findByText('No specs matched your search:').should('not.be.visible')
|
||||
})
|
||||
|
||||
// TODO: fix flaky test https://github.com/cypress-io/cypress/issues/23305
|
||||
// TODO: FIGURE OUT WHY THIS IS NOW FAILING CONSTANTLY
|
||||
it.skip('saves the filter when navigating to a spec and back', function () {
|
||||
const targetSpecFile = 'accounts_list.spec.js'
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ export const waitForSpecToFinish = (expectedResults, timeout?: number) => {
|
||||
cy.get('.failed > .num').should('exist')
|
||||
|
||||
// Then ensure the tests are running
|
||||
cy.contains('Your tests are loading...', { timeout: timeout || 20000 }).should('not.exist')
|
||||
cy.contains('Your tests are loading...', { timeout: timeout || 30000 }).should('not.exist')
|
||||
|
||||
// Then ensure the tests have finished
|
||||
cy.get('[aria-label="Rerun all tests"]', { timeout: timeout || 30000 })
|
||||
|
||||
@@ -195,7 +195,7 @@ describe('App Top Nav Workflows', () => {
|
||||
})
|
||||
|
||||
it('hides dropdown when version in header is clicked', () => {
|
||||
cy.findByTestId('cypress-update-popover').findByRole('button', { expanded: false }).as('topNavVersionButton').click()
|
||||
cy.findByTestId('cypress-update-popover').findAllByRole('button').first().as('topNavVersionButton').click()
|
||||
|
||||
cy.get('@topNavVersionButton').should('have.attr', 'aria-expanded', 'true')
|
||||
|
||||
@@ -541,7 +541,7 @@ describe('App Top Nav Workflows', () => {
|
||||
cy.findByRole('button', { name: 'Log in' }).click()
|
||||
})
|
||||
|
||||
cy.findByRole('dialog', { name: 'Log in to Cypress' }).within(() => {
|
||||
cy.findByRole('dialog').within(() => {
|
||||
cy.findByRole('button', { name: 'Log in' }).click()
|
||||
|
||||
cy.contains('http://127.0.0.1:0000/redirect-to-auth').should('be.visible')
|
||||
@@ -573,7 +573,7 @@ describe('App Top Nav Workflows', () => {
|
||||
cy.findByRole('button', { name: 'Log in' }).click()
|
||||
})
|
||||
|
||||
cy.findByRole('dialog', { name: 'Log in to Cypress' }).within(() => {
|
||||
cy.findByRole('dialog').within(() => {
|
||||
cy.findByRole('button', { name: 'Log in' }).click()
|
||||
|
||||
cy.contains(loginText.titleFailed).should('be.visible')
|
||||
@@ -623,7 +623,7 @@ describe('App Top Nav Workflows', () => {
|
||||
cy.findByRole('button', { name: 'Log in' }).as('loginButton').click()
|
||||
})
|
||||
|
||||
cy.findByRole('dialog', { name: 'Log in to Cypress' }).within(() => {
|
||||
cy.findByRole('dialog').within(() => {
|
||||
cy.findByRole('button', { name: 'Log in' }).click()
|
||||
|
||||
cy.contains(loginText.titleFailed).should('be.visible')
|
||||
@@ -660,7 +660,7 @@ describe('App Top Nav Workflows', () => {
|
||||
cy.findByRole('button', { name: 'Log in' }).as('loginButton').click()
|
||||
})
|
||||
|
||||
cy.findByRole('dialog', { name: 'Log in to Cypress' }).within(() => {
|
||||
cy.findByRole('dialog').within(() => {
|
||||
cy.findByRole('button', { name: 'Log in' }).click()
|
||||
cy.contains(loginText.titleFailed).should('be.visible')
|
||||
cy.contains(loginText.bodyError).should('be.visible')
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"@intlify/vite-plugin-vue-i18n": "2.4.0",
|
||||
"@packages/frontend-shared": "0.0.0-development",
|
||||
"@percy/cypress": "^3.1.0",
|
||||
"@testing-library/cypress": "8.0.0",
|
||||
"@testing-library/cypress": "BlueWinds/cypress-testing-library#119054b5963b0d2e064b13c5cc6fc9db32c8b7b5",
|
||||
"@types/faker": "5.5.8",
|
||||
"@urql/core": "2.4.4",
|
||||
"@urql/vue": "0.6.2",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { initial, session, sessionLifecycle, visitFailure } from './Blank'
|
||||
import { initial, testIsolationBlankPage, visitFailure } from './Blank'
|
||||
import { getContainerEl } from '@cypress/mount-utils'
|
||||
|
||||
describe('initial', () => {
|
||||
@@ -13,28 +13,9 @@ describe('initial', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('session', () => {
|
||||
describe('testIsolationBlankPage', () => {
|
||||
it('works', () => {
|
||||
getContainerEl()!.innerHTML = session()
|
||||
|
||||
cy.get('[data-cy="cypress-logo"]')
|
||||
cy.get('[data-cy="text"]').should('have.text', 'Default blank page')
|
||||
cy.get('[data-cy="subtext"]').should('have.text', 'This page was cleared by navigating to about:blank.')
|
||||
|
||||
cy.percySnapshot()
|
||||
})
|
||||
|
||||
it('works with small viewport', () => {
|
||||
cy.viewport(200, 500)
|
||||
getContainerEl()!.innerHTML = session()
|
||||
|
||||
cy.percySnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
describe('sessionLifecycle', () => {
|
||||
it('works', () => {
|
||||
getContainerEl()!.innerHTML = sessionLifecycle()
|
||||
getContainerEl()!.innerHTML = testIsolationBlankPage()
|
||||
|
||||
cy.get('[data-cy="cypress-logo"]')
|
||||
cy.get('[data-cy="text"]').should('have.text', 'Default blank page')
|
||||
@@ -45,7 +26,7 @@ describe('sessionLifecycle', () => {
|
||||
|
||||
it('works with small viewport', () => {
|
||||
cy.viewport(200, 500)
|
||||
getContainerEl()!.innerHTML = sessionLifecycle()
|
||||
getContainerEl()!.innerHTML = testIsolationBlankPage()
|
||||
|
||||
cy.percySnapshot()
|
||||
})
|
||||
|
||||
@@ -127,15 +127,11 @@ export const initial = () => {
|
||||
return blankContentsHtml()
|
||||
}
|
||||
|
||||
export const sessionLifecycle = () => {
|
||||
export const testIsolationBlankPage = () => {
|
||||
return blankContentsHtml(blankPageHeader,
|
||||
`${blankPageSubtext}<br>All active session data (cookies, localStorage and sessionStorage) across all domains are cleared.`)
|
||||
}
|
||||
|
||||
export const session = () => {
|
||||
return blankContentsHtml(blankPageHeader, blankPageSubtext)
|
||||
}
|
||||
|
||||
export const visitFailure = (props) => {
|
||||
const { status, statusText, contentType } = props
|
||||
|
||||
@@ -196,7 +192,6 @@ export const visitFailure = (props) => {
|
||||
|
||||
export const blankContents = {
|
||||
initial,
|
||||
session,
|
||||
sessionLifecycle,
|
||||
testIsolationBlankPage,
|
||||
visitFailure,
|
||||
}
|
||||
|
||||
@@ -48,16 +48,12 @@ export class AutIframe {
|
||||
this.$iframe.remove()
|
||||
}
|
||||
|
||||
showInitialBlankContents () {
|
||||
_showInitialBlankPage () {
|
||||
this._showContents(blankContents.initial())
|
||||
}
|
||||
|
||||
showSessionBlankContents () {
|
||||
this._showContents(blankContents.session())
|
||||
}
|
||||
|
||||
showSessionLifecycleBlankContents () {
|
||||
this._showContents(blankContents.sessionLifecycle())
|
||||
_showTestIsolationBlankPage () {
|
||||
this._showContents(blankContents.testIsolationBlankPage())
|
||||
}
|
||||
|
||||
showVisitFailure = (props) => {
|
||||
@@ -126,7 +122,7 @@ export class AutIframe {
|
||||
this.$iframe?.removeAttr('src')
|
||||
}
|
||||
|
||||
visitBlank = ({ type }: { type?: 'session' | 'session-lifecycle' }) => {
|
||||
visitBlankPage = (testIsolation?: boolean) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
if (!this.$iframe) {
|
||||
return
|
||||
@@ -135,15 +131,10 @@ export class AutIframe {
|
||||
this.$iframe[0].src = 'about:blank'
|
||||
|
||||
this.$iframe.one('load', () => {
|
||||
switch (type) {
|
||||
case 'session':
|
||||
this.showSessionBlankContents()
|
||||
break
|
||||
case 'session-lifecycle':
|
||||
this.showSessionLifecycleBlankContents()
|
||||
break
|
||||
default:
|
||||
this.showInitialBlankContents()
|
||||
if (testIsolation) {
|
||||
this._showTestIsolationBlankPage()
|
||||
} else {
|
||||
this._showInitialBlankPage()
|
||||
}
|
||||
|
||||
resolve()
|
||||
|
||||
@@ -53,7 +53,7 @@ export type SocketToDriverMap = {
|
||||
}
|
||||
|
||||
export type DriverToLocalBus = {
|
||||
'visit:blank': { type?: 'session' | 'session-lifecycle' }
|
||||
'visit:blank': { testIsolation?: boolean }
|
||||
'visit:failed': { status?: string, statusText: string, contentType?: () => string }
|
||||
'page:loading': boolean
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ function runSpecCT (config, spec: SpecFile) {
|
||||
|
||||
const specSrc = getSpecUrl(config.namespace, spec.absolute)
|
||||
|
||||
autIframe.showInitialBlankContents()
|
||||
autIframe._showInitialBlankPage()
|
||||
$autIframe.prop('src', specSrc)
|
||||
|
||||
// initialize Cypress (driver) with the AUT!
|
||||
@@ -290,7 +290,7 @@ function runSpecE2E (config, spec: SpecFile) {
|
||||
el.remove()
|
||||
})
|
||||
|
||||
autIframe.showInitialBlankContents()
|
||||
autIframe.visitBlankPage()
|
||||
|
||||
// create Spec IFrame
|
||||
const specSrc = getSpecUrl(config.namespace, encodeURIComponent(spec.relative))
|
||||
|
||||
@@ -47,8 +47,8 @@ export function useEventManager () {
|
||||
getAutIframeModel().reattachStudio()
|
||||
})
|
||||
|
||||
eventManager.on('visit:blank', ({ type }) => {
|
||||
getAutIframeModel().visitBlank({ type })
|
||||
eventManager.on('visit:blank', ({ testIsolation }) => {
|
||||
getAutIframeModel().visitBlankPage(testIsolation)
|
||||
})
|
||||
|
||||
eventManager.on('run:end', () => {
|
||||
|
||||
@@ -22,7 +22,7 @@ describe('SpecItem', () => {
|
||||
const parentColor = getComputedStyle($el.parent()[0]).color
|
||||
const highlightedElementColor = getComputedStyle($el[0]).color
|
||||
|
||||
cy.wrap(highlightedElementColor).should('not.equal', parentColor)
|
||||
expect(highlightedElementColor).not.to.equal(parentColor)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -35,7 +35,7 @@ describe('SpecItem', () => {
|
||||
const parentColor = getComputedStyle($el.parent()[0]).color
|
||||
const highlightedElementColor = getComputedStyle($el[0]).color
|
||||
|
||||
cy.wrap(highlightedElementColor).should('equal', parentColor)
|
||||
expect(highlightedElementColor).to.equal(parentColor)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ exports['config/src/index .getBreakingKeys returns list of breaking config keys
|
||||
'experimentalNetworkStubbing',
|
||||
'experimentalRunEvents',
|
||||
'experimentalSessionSupport',
|
||||
'experimentalSessionAndOrigin',
|
||||
'experimentalShadowDomSupport',
|
||||
'firefoxGcInterval',
|
||||
'ignoreTestFiles',
|
||||
@@ -36,8 +37,8 @@ exports['config/src/index .getDefaultValues returns list of public config keys 1
|
||||
'experimentalFetchPolyfill': false,
|
||||
'experimentalInteractiveRunEvents': false,
|
||||
'experimentalRunAllSpecs': false,
|
||||
'experimentalSessionAndOrigin': false,
|
||||
'experimentalModifyObstructiveThirdPartyCode': false,
|
||||
'experimentalOriginDependencies': false,
|
||||
'experimentalSourceRewriting': false,
|
||||
'experimentalSingleTabRunMode': false,
|
||||
'experimentalStudio': false,
|
||||
@@ -70,7 +71,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys 1
|
||||
'supportFile': 'cypress/support/e2e.{js,jsx,ts,tsx}',
|
||||
'supportFolder': false,
|
||||
'taskTimeout': 60000,
|
||||
'testIsolation': null,
|
||||
'testIsolation': true,
|
||||
'trashAssetsBeforeRuns': true,
|
||||
'userAgent': null,
|
||||
'video': true,
|
||||
@@ -99,7 +100,6 @@ exports['config/src/index .getDefaultValues returns list of public config keys 1
|
||||
'socketId': null,
|
||||
'socketIoCookie': '__socket',
|
||||
'socketIoRoute': '/__socket',
|
||||
'xhrRoute': '/xhrs/',
|
||||
}
|
||||
|
||||
exports['config/src/index .getDefaultValues returns list of public config keys for selected testing type 1'] = {
|
||||
@@ -122,8 +122,8 @@ exports['config/src/index .getDefaultValues returns list of public config keys f
|
||||
'experimentalFetchPolyfill': false,
|
||||
'experimentalInteractiveRunEvents': false,
|
||||
'experimentalRunAllSpecs': false,
|
||||
'experimentalSessionAndOrigin': false,
|
||||
'experimentalModifyObstructiveThirdPartyCode': false,
|
||||
'experimentalOriginDependencies': false,
|
||||
'experimentalSourceRewriting': false,
|
||||
'experimentalSingleTabRunMode': false,
|
||||
'experimentalStudio': false,
|
||||
@@ -156,7 +156,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys f
|
||||
'supportFile': 'cypress/support/e2e.{js,jsx,ts,tsx}',
|
||||
'supportFolder': false,
|
||||
'taskTimeout': 60000,
|
||||
'testIsolation': null,
|
||||
'testIsolation': true,
|
||||
'trashAssetsBeforeRuns': true,
|
||||
'userAgent': null,
|
||||
'video': true,
|
||||
@@ -185,7 +185,6 @@ exports['config/src/index .getDefaultValues returns list of public config keys f
|
||||
'socketId': null,
|
||||
'socketIoCookie': '__socket',
|
||||
'socketIoRoute': '/__socket',
|
||||
'xhrRoute': '/xhrs/',
|
||||
}
|
||||
|
||||
exports['config/src/index .getPublicConfigKeys returns list of public config keys 1'] = [
|
||||
@@ -204,8 +203,8 @@ exports['config/src/index .getPublicConfigKeys returns list of public config key
|
||||
'experimentalFetchPolyfill',
|
||||
'experimentalInteractiveRunEvents',
|
||||
'experimentalRunAllSpecs',
|
||||
'experimentalSessionAndOrigin',
|
||||
'experimentalModifyObstructiveThirdPartyCode',
|
||||
'experimentalOriginDependencies',
|
||||
'experimentalSourceRewriting',
|
||||
'experimentalSingleTabRunMode',
|
||||
'experimentalStudio',
|
||||
|
||||
@@ -165,8 +165,6 @@ export const validate = (cfg: any, onErr: (property: ErrResult | string) => void
|
||||
if (validationFn && value !== defaultValues[key]) {
|
||||
const result = validationFn(key, value, {
|
||||
testingType,
|
||||
// TODO: remove with experimentalSessionAndOrigin. Fixed with: https://github.com/cypress-io/cypress/issues/21471
|
||||
experimentalSessionAndOrigin: cfg.e2e?.experimentalSessionAndOrigin || cfg.experimentalSessionAndOrigin,
|
||||
})
|
||||
|
||||
if (result !== true) {
|
||||
|
||||
@@ -21,6 +21,7 @@ const BREAKING_OPTION_ERROR_KEY: Readonly<AllCypressErrorNames[]> = [
|
||||
'EXPERIMENTAL_NETWORK_STUBBING_REMOVED',
|
||||
'EXPERIMENTAL_RUN_EVENTS_REMOVED',
|
||||
'EXPERIMENTAL_SESSION_SUPPORT_REMOVED',
|
||||
'EXPERIMENTAL_SESSION_AND_ORIGIN_REMOVED',
|
||||
'EXPERIMENTAL_SINGLE_TAB_RUN_MODE',
|
||||
'EXPERIMENTAL_SHADOW_DOM_REMOVED',
|
||||
'FIREFOX_GC_INTERVAL_REMOVED',
|
||||
@@ -33,7 +34,6 @@ const BREAKING_OPTION_ERROR_KEY: Readonly<AllCypressErrorNames[]> = [
|
||||
|
||||
type ValidationOptions = {
|
||||
testingType: TestingType | null
|
||||
experimentalSessionAndOrigin: boolean
|
||||
}
|
||||
|
||||
export type BreakingOptionErrorKey = typeof BREAKING_OPTION_ERROR_KEY[number]
|
||||
@@ -209,18 +209,17 @@ const driverConfigOptions: Array<DriverConfigOption> = [
|
||||
defaultValue: false,
|
||||
validation: validate.isBoolean,
|
||||
isExperimental: true,
|
||||
}, {
|
||||
// TODO: remove with experimentalSessionAndOrigin. Fixed with: https://github.com/cypress-io/cypress/issues/21471
|
||||
name: 'experimentalSessionAndOrigin',
|
||||
defaultValue: false,
|
||||
validation: validate.isBoolean,
|
||||
isExperimental: true,
|
||||
}, {
|
||||
name: 'experimentalModifyObstructiveThirdPartyCode',
|
||||
defaultValue: false,
|
||||
validation: validate.isBoolean,
|
||||
isExperimental: true,
|
||||
requireRestartOnChange: 'server',
|
||||
}, {
|
||||
name: 'experimentalOriginDependencies',
|
||||
defaultValue: false,
|
||||
validation: validate.isBoolean,
|
||||
isExperimental: true,
|
||||
}, {
|
||||
name: 'experimentalSourceRewriting',
|
||||
defaultValue: false,
|
||||
@@ -382,38 +381,17 @@ const driverConfigOptions: Array<DriverConfigOption> = [
|
||||
overrideLevel: 'any',
|
||||
}, {
|
||||
name: 'testIsolation',
|
||||
// TODO: https://github.com/cypress-io/cypress/issues/23093
|
||||
// When experimentalSessionAndOrigin is removed and released as GA,
|
||||
// update the defaultValue from undefined to 'on' and
|
||||
// update this code to remove the check/override specific to enable
|
||||
// 'on' by default when experimentalSessionAndOrigin=true
|
||||
defaultValue: (options: Record<string, any> = {}) => {
|
||||
if (options.testingType === 'component') {
|
||||
return null
|
||||
}
|
||||
|
||||
return options?.experimentalSessionAndOrigin || options?.config?.e2e?.experimentalSessionAndOrigin ? 'on' : null
|
||||
},
|
||||
defaultValue: true,
|
||||
validation: (key: string, value: any, opts: ValidationOptions) => {
|
||||
const { testingType, experimentalSessionAndOrigin } = opts
|
||||
const { testingType } = opts
|
||||
|
||||
if (testingType == null || testingType === 'component') {
|
||||
return true
|
||||
let configOpts = [true, false]
|
||||
|
||||
if (testingType === 'component') {
|
||||
configOpts.pop()
|
||||
}
|
||||
|
||||
if (experimentalSessionAndOrigin && testingType === 'e2e') {
|
||||
return validate.isOneOf('on', 'off')(key, value)
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
return true
|
||||
}
|
||||
|
||||
return {
|
||||
key,
|
||||
value,
|
||||
type: 'not set unless the experimentalSessionAndOrigin flag is turned on',
|
||||
}
|
||||
return validate.isOneOf(...configOpts)(key, value)
|
||||
},
|
||||
overrideLevel: 'suite',
|
||||
}, {
|
||||
@@ -569,11 +547,6 @@ const runtimeOptions: Array<RuntimeConfigOption> = [
|
||||
defaultValue: pkg.version,
|
||||
validation: validate.isString,
|
||||
isInternal: true,
|
||||
}, {
|
||||
name: 'xhrRoute',
|
||||
defaultValue: '/xhrs/',
|
||||
validation: validate.isString,
|
||||
isInternal: true,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -615,6 +588,10 @@ export const breakingOptions: Readonly<BreakingOption[]> = [
|
||||
name: 'experimentalSessionSupport',
|
||||
errorKey: 'EXPERIMENTAL_SESSION_SUPPORT_REMOVED',
|
||||
isWarning: true,
|
||||
}, {
|
||||
name: 'experimentalSessionAndOrigin',
|
||||
errorKey: 'EXPERIMENTAL_SESSION_AND_ORIGIN_REMOVED',
|
||||
isWarning: true,
|
||||
}, {
|
||||
name: 'experimentalShadowDomSupport',
|
||||
errorKey: 'EXPERIMENTAL_SHADOW_DOM_REMOVED',
|
||||
@@ -660,11 +637,6 @@ export const breakingRootOptions: Array<BreakingOption> = [
|
||||
errorKey: 'CONFIG_FILE_INVALID_ROOT_CONFIG_E2E',
|
||||
isWarning: false,
|
||||
testingTypes: ['e2e'],
|
||||
}, {
|
||||
name: 'experimentalSessionAndOrigin',
|
||||
errorKey: 'CONFIG_FILE_INVALID_ROOT_CONFIG_E2E',
|
||||
isWarning: false,
|
||||
testingTypes: ['e2e'],
|
||||
}, {
|
||||
name: 'excludeSpecPattern',
|
||||
errorKey: 'CONFIG_FILE_INVALID_ROOT_CONFIG',
|
||||
@@ -701,6 +673,12 @@ export const breakingRootOptions: Array<BreakingOption> = [
|
||||
isWarning: false,
|
||||
testingTypes: ['e2e'],
|
||||
},
|
||||
{
|
||||
name: 'experimentalOriginDependencies',
|
||||
errorKey: 'EXPERIMENTAL_ORIGIN_DEPENDENCIES_E2E_ONLY',
|
||||
isWarning: false,
|
||||
testingTypes: ['e2e'],
|
||||
},
|
||||
]
|
||||
|
||||
export const testingTypeBreakingOptions: { e2e: Array<BreakingOption>, component: Array<BreakingOption> } = {
|
||||
@@ -709,7 +687,6 @@ export const testingTypeBreakingOptions: { e2e: Array<BreakingOption>, component
|
||||
name: 'experimentalSingleTabRunMode',
|
||||
errorKey: 'EXPERIMENTAL_SINGLE_TAB_RUN_MODE',
|
||||
isWarning: false,
|
||||
testingTypes: ['e2e'],
|
||||
},
|
||||
{
|
||||
name: 'indexHtmlFile',
|
||||
@@ -723,16 +700,10 @@ export const testingTypeBreakingOptions: { e2e: Array<BreakingOption>, component
|
||||
errorKey: 'CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_COMPONENT',
|
||||
isWarning: false,
|
||||
},
|
||||
{
|
||||
name: 'experimentalSessionAndOrigin',
|
||||
errorKey: 'CONFIG_FILE_INVALID_TESTING_TYPE_CONFIG_COMPONENT',
|
||||
isWarning: false,
|
||||
},
|
||||
{
|
||||
name: 'experimentalStudio',
|
||||
errorKey: 'EXPERIMENTAL_STUDIO_E2E_ONLY',
|
||||
isWarning: false,
|
||||
testingTypes: ['component'],
|
||||
},
|
||||
{
|
||||
name: 'testIsolation',
|
||||
@@ -743,7 +714,11 @@ export const testingTypeBreakingOptions: { e2e: Array<BreakingOption>, component
|
||||
name: 'experimentalRunAllSpecs',
|
||||
errorKey: 'EXPERIMENTAL_RUN_ALL_SPECS_E2E_ONLY',
|
||||
isWarning: false,
|
||||
testingTypes: ['component'],
|
||||
},
|
||||
{
|
||||
name: 'experimentalOriginDependencies',
|
||||
errorKey: 'EXPERIMENTAL_ORIGIN_DEPENDENCIES_E2E_ONLY',
|
||||
isWarning: false,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -403,8 +403,6 @@ export function mergeDefaults (
|
||||
|
||||
const defaultsForRuntime = getDefaultValues({
|
||||
...options,
|
||||
// TODO: clean this up. Fixed with: https://github.com/cypress-io/cypress/issues/21471
|
||||
experimentalSessionAndOrigin: config.experimentalSessionAndOrigin,
|
||||
})
|
||||
|
||||
_.defaultsDeep(config, defaultsForRuntime)
|
||||
@@ -480,20 +478,6 @@ export function mergeDefaults (
|
||||
throw makeConfigError(errors.get(err, ...args))
|
||||
}, testingType)
|
||||
|
||||
// TODO: https://github.com/cypress-io/cypress/issues/23093
|
||||
// testIsolation should equal 'on' by default when experimentalSessionAndOrigin=true
|
||||
// Once experimentalSessionAndOrigin is made GA, remove this logic and update the defaultValue
|
||||
// to be be 'on'
|
||||
if (testingType === 'e2e' && config.experimentalSessionAndOrigin) {
|
||||
if (config.rawJson.testIsolation) {
|
||||
config.resolved.testIsolation.from = 'config'
|
||||
} else {
|
||||
config.testIsolation = 'on'
|
||||
config.resolved.testIsolation.value = 'on'
|
||||
config.resolved.testIsolation.from === 'default'
|
||||
}
|
||||
}
|
||||
|
||||
// We need to remove the nested propertied by testing type because it has been
|
||||
// flattened/compacted based on the current testing type that is selected
|
||||
// making the config only available with the properties that are valid,
|
||||
|
||||
@@ -34,7 +34,6 @@ export function setUrls (obj: any) {
|
||||
proxyUrl,
|
||||
browserUrl: rootUrl + obj.clientRoute,
|
||||
reporterUrl: rootUrl + obj.reporterRoute,
|
||||
xhrUrl: `${obj.namespace}${obj.xhrRoute}`,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ describe('config/src/index', () => {
|
||||
|
||||
const isSuiteOverride = true
|
||||
|
||||
configUtil.validateOverridableAtRunTime({ testIsolation: 'on' }, isSuiteOverride, errorFn)
|
||||
configUtil.validateOverridableAtRunTime({ testIsolation: true }, isSuiteOverride, errorFn)
|
||||
|
||||
expect(errorFn).to.have.callCount(0)
|
||||
})
|
||||
@@ -205,7 +205,7 @@ describe('config/src/index', () => {
|
||||
|
||||
const isSuiteOverride = false
|
||||
|
||||
configUtil.validateOverridableAtRunTime({ testIsolation: false }, isSuiteOverride, errorFn)
|
||||
configUtil.validateOverridableAtRunTime({ testIsolation: 'off' }, isSuiteOverride, errorFn)
|
||||
|
||||
expect(errorFn).to.have.callCount(1)
|
||||
expect(errorFn).to.have.been.calledWithMatch({
|
||||
|
||||
@@ -972,6 +972,16 @@ describe('config/src/project/utils', () => {
|
||||
expect(warning).to.be.calledWith('EXPERIMENTAL_SESSION_SUPPORT_REMOVED')
|
||||
})
|
||||
|
||||
it('warns if experimentalSessionAndOrigin is passed', async function () {
|
||||
const warning = sinon.spy(errors, 'warning')
|
||||
|
||||
await this.defaults('experimentalSessionAndOrigin', true, {
|
||||
experimentalSessionAndOrigin: true,
|
||||
})
|
||||
|
||||
expect(warning).to.be.calledWith('EXPERIMENTAL_SESSION_AND_ORIGIN_REMOVED')
|
||||
})
|
||||
|
||||
it('warns if experimentalShadowDomSupport is passed', async function () {
|
||||
const warning = sinon.spy(errors, 'warning')
|
||||
|
||||
@@ -1044,8 +1054,8 @@ describe('config/src/project/utils', () => {
|
||||
experimentalModifyObstructiveThirdPartyCode: { value: false, from: 'default' },
|
||||
experimentalFetchPolyfill: { value: false, from: 'default' },
|
||||
experimentalInteractiveRunEvents: { value: false, from: 'default' },
|
||||
experimentalOriginDependencies: { value: false, from: 'default' },
|
||||
experimentalRunAllSpecs: { value: false, from: 'default' },
|
||||
experimentalSessionAndOrigin: { value: false, from: 'default' },
|
||||
experimentalSingleTabRunMode: { value: false, from: 'default' },
|
||||
experimentalStudio: { value: false, from: 'default' },
|
||||
experimentalSourceRewriting: { value: false, from: 'default' },
|
||||
@@ -1079,7 +1089,7 @@ describe('config/src/project/utils', () => {
|
||||
supportFile: { value: false, from: 'config' },
|
||||
supportFolder: { value: false, from: 'default' },
|
||||
taskTimeout: { value: 60000, from: 'default' },
|
||||
testIsolation: { value: null, from: 'default' },
|
||||
testIsolation: { value: true, from: 'default' },
|
||||
trashAssetsBeforeRuns: { value: true, from: 'default' },
|
||||
userAgent: { value: null, from: 'default' },
|
||||
video: { value: true, from: 'default' },
|
||||
@@ -1139,8 +1149,8 @@ describe('config/src/project/utils', () => {
|
||||
experimentalModifyObstructiveThirdPartyCode: { value: false, from: 'default' },
|
||||
experimentalFetchPolyfill: { value: false, from: 'default' },
|
||||
experimentalInteractiveRunEvents: { value: false, from: 'default' },
|
||||
experimentalOriginDependencies: { value: false, from: 'default' },
|
||||
experimentalRunAllSpecs: { value: false, from: 'default' },
|
||||
experimentalSessionAndOrigin: { value: false, from: 'default' },
|
||||
experimentalSingleTabRunMode: { value: false, from: 'default' },
|
||||
experimentalStudio: { value: false, from: 'default' },
|
||||
experimentalSourceRewriting: { value: false, from: 'default' },
|
||||
@@ -1196,7 +1206,7 @@ describe('config/src/project/utils', () => {
|
||||
supportFile: { value: false, from: 'config' },
|
||||
supportFolder: { value: false, from: 'default' },
|
||||
taskTimeout: { value: 60000, from: 'default' },
|
||||
testIsolation: { value: null, from: 'default' },
|
||||
testIsolation: { value: true, from: 'default' },
|
||||
trashAssetsBeforeRuns: { value: true, from: 'default' },
|
||||
userAgent: { value: null, from: 'default' },
|
||||
video: { value: true, from: 'default' },
|
||||
@@ -1212,14 +1222,14 @@ describe('config/src/project/utils', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('sets testIsolation=on by default when experimentalSessionAndOrigin=true and e2e testing', () => {
|
||||
it('honors user config for testIsolation', () => {
|
||||
sinon.stub(utils, 'getProcessEnvVars').returns({})
|
||||
|
||||
const obj = {
|
||||
projectRoot: '/foo/bar',
|
||||
supportFile: false,
|
||||
baseUrl: 'http://localhost:8080',
|
||||
experimentalSessionAndOrigin: true,
|
||||
testIsolation: false,
|
||||
}
|
||||
|
||||
const options = {
|
||||
@@ -1230,36 +1240,8 @@ describe('config/src/project/utils', () => {
|
||||
|
||||
return mergeDefaults(obj, options, {}, getFilesByGlob)
|
||||
.then((cfg) => {
|
||||
expect(cfg.resolved).to.have.property('experimentalSessionAndOrigin')
|
||||
expect(cfg.resolved.experimentalSessionAndOrigin).to.deep.eq({ value: true, from: 'config' })
|
||||
expect(cfg.resolved).to.have.property('testIsolation')
|
||||
expect(cfg.resolved.testIsolation).to.deep.eq({ value: 'on', from: 'default' })
|
||||
})
|
||||
})
|
||||
|
||||
it('honors user config for testIsolation when experimentalSessionAndOrigin=true and e2e testing', () => {
|
||||
sinon.stub(utils, 'getProcessEnvVars').returns({})
|
||||
|
||||
const obj = {
|
||||
projectRoot: '/foo/bar',
|
||||
supportFile: false,
|
||||
baseUrl: 'http://localhost:8080',
|
||||
experimentalSessionAndOrigin: true,
|
||||
testIsolation: 'on',
|
||||
}
|
||||
|
||||
const options = {
|
||||
testingType: 'e2e',
|
||||
}
|
||||
|
||||
const getFilesByGlob = sinon.stub().returns(['path/to/file.ts'])
|
||||
|
||||
return mergeDefaults(obj, options, {}, getFilesByGlob)
|
||||
.then((cfg) => {
|
||||
expect(cfg.resolved).to.have.property('experimentalSessionAndOrigin')
|
||||
expect(cfg.resolved.experimentalSessionAndOrigin).to.deep.eq({ value: true, from: 'config' })
|
||||
expect(cfg.resolved).to.have.property('testIsolation')
|
||||
expect(cfg.resolved.testIsolation).to.deep.eq({ value: 'on', from: 'config' })
|
||||
expect(cfg.resolved.testIsolation).to.deep.eq({ value: false, from: 'config' })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -28,7 +28,6 @@ module.exports = defineConfig({
|
||||
return require('./cypress/plugins/index.js')(on, config)
|
||||
},
|
||||
baseUrl: 'localhost:3000',
|
||||
experimentalSessionAndOrigin: true,
|
||||
},
|
||||
component: {
|
||||
setupNodeEvents(on, config) {},
|
||||
@@ -69,7 +68,6 @@ module.exports = defineConfig({
|
||||
},
|
||||
retries: 2,
|
||||
baseUrl: 'localhost:300',
|
||||
experimentalSessionAndOrigin: true,
|
||||
slowTestThreshold: 500,
|
||||
},
|
||||
component: {
|
||||
|
||||
@@ -53,7 +53,6 @@ export class HtmlDataSource {
|
||||
'testingType',
|
||||
'componentTesting',
|
||||
'reporterUrl',
|
||||
'xhrUrl',
|
||||
'namespace',
|
||||
'socketIoRoute',
|
||||
]
|
||||
|
||||
@@ -495,7 +495,6 @@ export function reduceConfig (cfg: LegacyCypressConfigJson, options: CreateConfi
|
||||
e2e: { ...acc.e2e, supportFile: val },
|
||||
}
|
||||
case 'baseUrl':
|
||||
case 'experimentalSessionAndOrigin':
|
||||
return {
|
||||
...acc,
|
||||
e2e: { ...acc.e2e, [key]: val },
|
||||
|
||||
@@ -96,11 +96,6 @@ const resolvedOptions: Array<ResolvedConfigOption> = [
|
||||
defaultValue: false,
|
||||
isExperimental: true,
|
||||
canUpdateDuringTestTime: false,
|
||||
}, {
|
||||
name: 'experimentalSessionAndOrigin',
|
||||
defaultValue: false,
|
||||
isExperimental: true,
|
||||
canUpdateDuringTestTime: true,
|
||||
}, {
|
||||
name: 'experimentalSourceRewriting',
|
||||
defaultValue: false,
|
||||
|
||||
@@ -60,7 +60,6 @@ describe('cypress.config.js generation', () => {
|
||||
const config: Partial<Cypress.Config> = {
|
||||
e2e: {
|
||||
baseUrl: 'localhost:3000',
|
||||
experimentalSessionAndOrigin: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -127,7 +126,6 @@ describe('cypress.config.js generation', () => {
|
||||
const config = {
|
||||
viewportWidth: 300,
|
||||
baseUrl: 'localhost:300',
|
||||
experimentalSessionAndOrigin: true,
|
||||
slowTestThreshold: 500,
|
||||
e2e: {
|
||||
retries: 2,
|
||||
|
||||
@@ -6,8 +6,9 @@ The goal of this document is to give a technical overview of the architecture be
|
||||
|
||||
See [Node.js’s URL doc](https://nodejs.org/api/url.html#url-strings-and-url-objects) for a handy breakdown of URL parts
|
||||
|
||||
- **domain**: A hostname without the subdomain (for the purposes of this doc). May also be referred to as **superdomain** (e.g. `example.com`, `example.co.uk`, `localhost`)
|
||||
- **origin**: The combination of the protocol, hostname, and port of a URL. For the purposes of Cypress, the subdomain is irrelevant. (e.g. `http://example.com:3500`)
|
||||
- **domain**: The hostname portion of a URL. (e.g. `example.com`, `www.example.com`, `www.example.co.uk`, `localhost`)
|
||||
- **superdomain**: A domain without the subdomain. (e.g. `example.com`, `example.co.uk`, `localhost`)
|
||||
- **origin**: The combination of the protocol, hostname, and port of a URL (e.g. `http://www.example.com:3500`)
|
||||
- **top**: The main window/frame of the browser
|
||||
- **primary origin**: The origin that top is on
|
||||
- **secondary origin**: Any origin that is not the primary origin
|
||||
@@ -209,13 +210,4 @@ Nesting **cy.origin()** inside the callback is not currently supported, but supp
|
||||
|
||||
### cy.intercept()
|
||||
|
||||
Most use-cases for **cy.intercept()** can be accomplished by using it outside of the **cy.origin()** callback. Since there may be use-cases where setting up a response, for example, requires the scope within the **cy.origin()** callback, we will likely [add support for **cy.intercept()** in the future](https://github.com/cypress-io/cypress/issues/20720).
|
||||
|
||||
### Deprecated commands / methods
|
||||
|
||||
All deprecated APIs are not supported in the **cy.origin()** callback and we do not plan to ever add support for them. If a user attempts to use one, we throw an error that points them to the preferred API that superseded it. The following are deprecated APIs that are not supported:
|
||||
|
||||
- **cy.route()**: Superseded by **cy.intercept()**
|
||||
- **cy.server()**: Superseded by **cy.intercept()**
|
||||
- **Cypress.Server.defaults()**: Superseded by **cy.intercept()**
|
||||
- **Cypress.Cookies.preserveOnce()**: Superseded by sessions
|
||||
Most use-cases for **cy.intercept()** can be accomplished by using it outside of the **cy.origin()** callback. Since there may be use-cases where setting up a response, for example, requires the scope within the **cy.origin()** callback, we will likely [add support for **cy.intercept()** in the future](https://github.com/cypress-io/cypress/issues/20720).
|
||||
|
||||
@@ -18,6 +18,8 @@ export default defineConfig({
|
||||
configFile: '../../mocha-reporter-config.json',
|
||||
},
|
||||
e2e: {
|
||||
experimentalOriginDependencies: true,
|
||||
experimentalModifyObstructiveThirdPartyCode: true,
|
||||
setupNodeEvents: (on, config) => {
|
||||
return require('./cypress/plugins')(on, config)
|
||||
},
|
||||
|
||||
@@ -98,6 +98,27 @@ describe('src/cy/commands/actions/check', () => {
|
||||
cy.get(checkbox).check()
|
||||
})
|
||||
|
||||
it('requeries if the DOM rerenders during actionability', () => {
|
||||
cy.$$('[name=colors]').first().prop('disabled', true)
|
||||
|
||||
const listener = _.after(3, () => {
|
||||
cy.$$('[name=colors]').first().prop('disabled', false)
|
||||
|
||||
const parent = cy.$$('[name=colors]').parent()
|
||||
|
||||
parent.replaceWith(parent[0].outerHTML)
|
||||
cy.off('command:retry', listener)
|
||||
})
|
||||
|
||||
cy.on('command:retry', listener)
|
||||
|
||||
cy.get('[name=colors]').check().then(($inputs) => {
|
||||
$inputs.each((i, el) => {
|
||||
expect($(el)).to.be.checked
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// readonly should only be limited to inputs, not checkboxes
|
||||
it('can check readonly checkboxes', () => {
|
||||
cy.get('#readonly-checkbox').check().then(($checkbox) => {
|
||||
@@ -302,24 +323,27 @@ describe('src/cy/commands/actions/check', () => {
|
||||
})
|
||||
|
||||
it('can set options.waitForAnimations', () => {
|
||||
cy.stub(cy, 'ensureElementIsNotAnimating').throws(new Error('animating!'))
|
||||
let retries = 0
|
||||
|
||||
cy.get(':checkbox:first').check({ waitForAnimations: false }).then(() => {
|
||||
expect(cy.ensureElementIsNotAnimating).not.to.be.called
|
||||
cy.on('command:retry', () => {
|
||||
retries += 1
|
||||
})
|
||||
|
||||
cy.get(':checkbox:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).check({ waitForAnimations: false }).then(() => {
|
||||
expect(retries).to.eq(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('can set options.animationDistanceThreshold', () => {
|
||||
const $btn = cy.$$(':checkbox:first')
|
||||
let retries = 0
|
||||
|
||||
cy.spy(cy, 'ensureElementIsNotAnimating')
|
||||
cy.get(':checkbox:first').check({ animationDistanceThreshold: 1000 }).then(() => {
|
||||
const { fromElWindow } = Cypress.dom.getElementCoordinatesByPosition($btn)
|
||||
const { args } = cy.ensureElementIsNotAnimating.firstCall
|
||||
cy.on('command:retry', () => {
|
||||
retries += 1
|
||||
})
|
||||
|
||||
expect(args[1]).to.deep.eq([fromElWindow, fromElWindow])
|
||||
|
||||
expect(args[2]).to.eq(1000)
|
||||
cy.get(':checkbox:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).check({ animationDistanceThreshold: 1000 }).then(() => {
|
||||
// One retry, because $actionability always waits for two sets of points to determine if an element is animating.
|
||||
expect(retries).to.eq(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -437,7 +461,7 @@ describe('src/cy/commands/actions/check', () => {
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(checked).to.eq(1)
|
||||
expect(err.message).to.include('`cy.check()` failed because this element')
|
||||
expect(err.message).to.include('`cy.check()` failed because the page updated')
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -1079,7 +1103,7 @@ describe('src/cy/commands/actions/check', () => {
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(unchecked).to.eq(1)
|
||||
expect(err.message).to.include('`cy.uncheck()` failed because this element')
|
||||
expect(err.message).to.include('`cy.uncheck()` failed because the page updated')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -52,6 +52,26 @@ describe('src/cy/commands/actions/type - #clear', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('requeries if the DOM rerenders during actionability', () => {
|
||||
const clicked = cy.stub()
|
||||
const retried = cy.stub()
|
||||
|
||||
const textarea = cy.$$('#comments').val('foo bar').prop('disabled', true)
|
||||
|
||||
cy.on('command:retry', _.after(3, () => {
|
||||
if (!retried.callCount) {
|
||||
textarea.replaceWith(textarea[0].outerHTML)
|
||||
cy.$$('#comments').prop('disabled', false).on('click', clicked)
|
||||
retried()
|
||||
}
|
||||
}))
|
||||
|
||||
cy.get('#comments').clear().then(() => {
|
||||
expect(clicked).to.be.calledOnce
|
||||
expect(retried).to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
it('can force clear even when being covered by another element', () => {
|
||||
const $input = $('<input />')
|
||||
.attr('id', 'input-covered-in-span')
|
||||
@@ -275,7 +295,7 @@ describe('src/cy/commands/actions/type - #clear', () => {
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(cleared).to.be.calledOnce
|
||||
expect(err.message).to.include('`cy.clear()` failed because this element')
|
||||
expect(err.message).to.include('`cy.clear()` failed because the page updated')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -738,6 +738,27 @@ describe('src/cy/commands/actions/click', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('requeries if the DOM rerenders during actionability', () => {
|
||||
cy.$$('[name=colors]').first().prop('disabled', true)
|
||||
|
||||
const listener = _.after(3, () => {
|
||||
cy.$$('[name=colors]').first().prop('disabled', false)
|
||||
|
||||
const parent = cy.$$('[name=colors]').parent()
|
||||
|
||||
parent.replaceWith(parent[0].outerHTML)
|
||||
cy.off('command:retry', listener)
|
||||
})
|
||||
|
||||
cy.on('command:retry', listener)
|
||||
|
||||
cy.get('[name=colors]').first().click().then(($inputs) => {
|
||||
$inputs.each((i, el) => {
|
||||
expect($(el)).to.be.checked
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('increases the timeout delta after each click', () => {
|
||||
const count = cy.$$('#three-buttons button').length
|
||||
|
||||
@@ -813,22 +834,20 @@ describe('src/cy/commands/actions/click', () => {
|
||||
})
|
||||
|
||||
it('places cursor at the end of [contenteditable]', () => {
|
||||
cy.get('[contenteditable]:first')
|
||||
.invoke('html', '<div><br></div>').click()
|
||||
.then(expectCaret(0))
|
||||
cy.get('[contenteditable]:first').as('edit')
|
||||
|
||||
cy.get('[contenteditable]:first')
|
||||
.invoke('html', 'foo').click()
|
||||
.then(expectCaret(3))
|
||||
cy.get('@edit').invoke('html', '<div><br></div>')
|
||||
cy.get('@edit').click().then(expectCaret(0))
|
||||
|
||||
cy.get('[contenteditable]:first')
|
||||
.invoke('html', '<div>foo</div>').click()
|
||||
.then(expectCaret(3))
|
||||
cy.get('@edit').invoke('html', 'foo')
|
||||
cy.get('@edit').click().then(expectCaret(3))
|
||||
|
||||
cy.get('@edit').invoke('html', '<div>foo</div>')
|
||||
cy.get('@edit').click().then(expectCaret(3))
|
||||
|
||||
cy.get('[contenteditable]:first')
|
||||
// firefox headless: prevent contenteditable from disappearing (dont set to empty)
|
||||
.invoke('html', '<br>').click()
|
||||
.then(expectCaret(0))
|
||||
cy.get('@edit').invoke('html', '<br>')
|
||||
cy.get('@edit').click().then(expectCaret(0))
|
||||
})
|
||||
|
||||
it('can click SVG elements', () => {
|
||||
@@ -1034,6 +1053,15 @@ describe('src/cy/commands/actions/click', () => {
|
||||
})
|
||||
|
||||
describe('actionability', () => {
|
||||
let retries = 0
|
||||
|
||||
beforeEach(() => {
|
||||
retries = 0
|
||||
cy.on('command:retry', () => {
|
||||
retries += 1
|
||||
})
|
||||
})
|
||||
|
||||
it('can click on inline elements that wrap lines', () => {
|
||||
cy.get('#overflow-link').find('.wrapped').click()
|
||||
})
|
||||
@@ -1320,24 +1348,19 @@ describe('src/cy/commands/actions/click', () => {
|
||||
$('<span>span on button</span>').css({ position: 'absolute', left: $btn.offset().left, top: $btn.offset().top, padding: 5, display: 'inline-block', backgroundColor: 'yellow' }).prependTo(cy.$$('body'))
|
||||
|
||||
const scrolled = []
|
||||
let retried = false
|
||||
let clicked = false
|
||||
|
||||
cy.on('scrolled', ($el, type) => {
|
||||
scrolled.push(type)
|
||||
})
|
||||
|
||||
cy.on('command:retry', () => {
|
||||
retried = true
|
||||
})
|
||||
|
||||
$btn.on('click', () => {
|
||||
clicked = true
|
||||
})
|
||||
|
||||
cy.get('#button-covered-in-span').click({ force: true }).then(() => {
|
||||
expect(scrolled).to.be.empty
|
||||
expect(retried).to.be.false
|
||||
expect(retries).to.eq(0)
|
||||
|
||||
expect(clicked).to.be.true
|
||||
})
|
||||
@@ -1348,19 +1371,14 @@ describe('src/cy/commands/actions/click', () => {
|
||||
|
||||
$('<span>span on button</span>').css({ opacity: 0, position: 'absolute', left: $btn.offset().left, top: $btn.offset().top, padding: 5, display: 'inline-block' }).prependTo(cy.$$('body'))
|
||||
|
||||
let retried = false
|
||||
let clicked = false
|
||||
|
||||
cy.on('command:retry', () => {
|
||||
retried = true
|
||||
})
|
||||
|
||||
$btn.on('click', () => {
|
||||
clicked = true
|
||||
})
|
||||
|
||||
cy.get('#button-covered-in-span').click({ force: true }).then(() => {
|
||||
expect(retried).to.be.false
|
||||
expect(retries).to.be.eq(0)
|
||||
expect(clicked).to.be.true
|
||||
})
|
||||
})
|
||||
@@ -1380,7 +1398,6 @@ describe('src/cy/commands/actions/click', () => {
|
||||
}).prependTo(cy.$$('body'))
|
||||
|
||||
const scrolled = []
|
||||
let retried = false
|
||||
|
||||
cy.on('scrolled', ($el, type) => {
|
||||
scrolled.push(type)
|
||||
@@ -1388,11 +1405,10 @@ describe('src/cy/commands/actions/click', () => {
|
||||
|
||||
cy.on('command:retry', _.after(3, () => {
|
||||
$span.hide()
|
||||
retried = true
|
||||
}))
|
||||
|
||||
cy.get('#button-covered-in-span').click().then(() => {
|
||||
expect(retried).to.be.true
|
||||
expect(retries).to.be.gt(0)
|
||||
|
||||
// - element scrollIntoView
|
||||
// - element scrollIntoView (retry animation coords)
|
||||
@@ -1553,22 +1569,18 @@ describe('src/cy/commands/actions/click', () => {
|
||||
it('waits until element becomes visible', () => {
|
||||
const $btn = cy.$$('#button').hide()
|
||||
|
||||
let retried = false
|
||||
|
||||
cy.on('command:retry', _.after(3, () => {
|
||||
$btn.show()
|
||||
retried = true
|
||||
}))
|
||||
|
||||
cy.get('#button').click().then(() => {
|
||||
expect(retried).to.be.true
|
||||
expect(retries).to.be.gt(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('waits until element is no longer disabled', () => {
|
||||
const $btn = cy.$$('#button').prop('disabled', true)
|
||||
|
||||
let retried = false
|
||||
let clicks = 0
|
||||
|
||||
$btn.on('click', () => {
|
||||
@@ -1577,83 +1589,64 @@ describe('src/cy/commands/actions/click', () => {
|
||||
|
||||
cy.on('command:retry', _.after(3, () => {
|
||||
$btn.prop('disabled', false)
|
||||
retried = true
|
||||
}))
|
||||
|
||||
cy.get('#button').click().then(() => {
|
||||
expect(clicks).to.eq(1)
|
||||
|
||||
expect(retried).to.be.true
|
||||
expect(retries).to.be.gt(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('succeeds when DOM rerenders and returns new subject', () => {
|
||||
const $btn = cy.$$('#button').prop('disabled', true)
|
||||
|
||||
cy.on('command:retry', _.after(3, () => {
|
||||
$btn.replaceWith('<button id="button">New Button</button>')
|
||||
}))
|
||||
|
||||
cy.get('#button').click().should('contain', 'New Button')
|
||||
})
|
||||
|
||||
it('waits until element stops animating', () => {
|
||||
let retries = 0
|
||||
|
||||
cy.on('command:retry', () => {
|
||||
retries += 1
|
||||
})
|
||||
|
||||
cy.stub(cy, 'ensureElementIsNotAnimating')
|
||||
.throws(new Error('animating!'))
|
||||
.onThirdCall().returns()
|
||||
|
||||
cy.get('button:first').click().then(() => {
|
||||
// - retry animation coords
|
||||
// - retry animation
|
||||
// - retry animation
|
||||
expect(retries).to.eq(3)
|
||||
|
||||
expect(cy.ensureElementIsNotAnimating).to.be.calledThrice
|
||||
cy.get('button:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).click().then(() => {
|
||||
expect(retries).to.gt(1)
|
||||
})
|
||||
})
|
||||
|
||||
it('does not throw when waiting for animations is disabled', {
|
||||
waitForAnimations: false,
|
||||
}, () => {
|
||||
cy.stub(cy, 'ensureElementIsNotAnimating').throws(new Error('animating!'))
|
||||
|
||||
cy.get('button:first').click().then(() => {
|
||||
expect(cy.ensureElementIsNotAnimating).not.to.be.called
|
||||
cy.get('button:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).click().then(() => {
|
||||
expect(retries).to.eq(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('does not throw when turning off waitForAnimations in options', () => {
|
||||
cy.stub(cy, 'ensureElementIsNotAnimating').throws(new Error('animating!'))
|
||||
|
||||
cy.get('button:first').click({ waitForAnimations: false }).then(() => {
|
||||
expect(cy.ensureElementIsNotAnimating).not.to.be.called
|
||||
cy.get('button:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).click({ waitForAnimations: false }).then(() => {
|
||||
expect(retries).to.eql(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('passes options.animationDistanceThreshold to cy.ensureElementIsNotAnimating', () => {
|
||||
const $btn = cy.$$('button:first')
|
||||
|
||||
cy.spy(cy, 'ensureElementIsNotAnimating')
|
||||
cy.get('button:first').click({ animationDistanceThreshold: 1000 }).then(() => {
|
||||
const { fromElWindow } = Cypress.dom.getElementCoordinatesByPosition($btn)
|
||||
const { args } = cy.ensureElementIsNotAnimating.firstCall
|
||||
|
||||
expect(args[1]).to.deep.eq([fromElWindow, fromElWindow])
|
||||
|
||||
expect(args[2]).to.eq(1000)
|
||||
it('passes options.animationDistanceThreshold to $actionability.ensureElIsNotAnimating', () => {
|
||||
cy.get('button:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).click({ animationDistanceThreshold: 1000 }).then(() => {
|
||||
// One retry, because $actionability waits for two sets of position coordinates.
|
||||
expect(retries).to.eq(1)
|
||||
})
|
||||
})
|
||||
|
||||
it('passes config.animationDistanceThreshold to cy.ensureElementIsNotAnimating', () => {
|
||||
const animationDistanceThreshold = Cypress.config('animationDistanceThreshold')
|
||||
it('passes config.animationDistanceThreshold to $actionability.ensureElIsNotAnimating', () => {
|
||||
let old = Cypress.config('animationDistanceThreshold')
|
||||
|
||||
const $btn = cy.$$('button:first')
|
||||
Cypress.config('animationDistanceThreshold', 1000)
|
||||
|
||||
cy.spy(cy, 'ensureElementIsNotAnimating')
|
||||
|
||||
cy.get('button:first').click().then(() => {
|
||||
const { fromElWindow } = Cypress.dom.getElementCoordinatesByPosition($btn)
|
||||
const { args } = cy.ensureElementIsNotAnimating.firstCall
|
||||
|
||||
expect(args[1]).to.deep.eq([fromElWindow, fromElWindow])
|
||||
|
||||
expect(args[2]).to.eq(animationDistanceThreshold)
|
||||
cy.get('button:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).click().then(() => {
|
||||
try {
|
||||
// One retry, because $actionability waits for two sets of position coordinates.
|
||||
expect(retries).to.eq(1)
|
||||
} finally {
|
||||
Cypress.config('animationDistanceThreshold', old)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2110,41 +2103,21 @@ describe('src/cy/commands/actions/click', () => {
|
||||
cy.get('.badge-multi').click()
|
||||
})
|
||||
|
||||
it('throws when subject is not in the document', (done) => {
|
||||
let clicked = 0
|
||||
|
||||
const $checkbox = cy.$$(':checkbox:first').click(() => {
|
||||
clicked += 1
|
||||
$checkbox.remove()
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(clicked).to.eq(1)
|
||||
expect(err.message).to.include('`cy.click()` failed because this element is detached from the DOM')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.get(':checkbox:first').click().click()
|
||||
})
|
||||
// This is an instance of an unfixable detached DOM error: .then() is a command, so it sets the subject to a
|
||||
// *specific element*, which then gets detached.
|
||||
|
||||
// The error message tells the user exactly how to fix this case.
|
||||
it('throws when subject is detached during actionability', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.click()` failed because this element is detached from the DOM')
|
||||
expect(err.message).to.include('`cy.click()` failed because the page updated while this command was executing.')
|
||||
expect(err.message).to.include('You can typically solve this by breaking up a chain.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.get('input:first')
|
||||
.then(($el) => {
|
||||
// This represents an asynchronous re-render
|
||||
// since we fire the 'scrolled' event during actionability
|
||||
// if we use el.on('scroll'), headless electron is flaky
|
||||
cy.on('scrolled', () => {
|
||||
$el.remove()
|
||||
})
|
||||
cy.on('scrolled', () => $el.remove())
|
||||
})
|
||||
.click()
|
||||
})
|
||||
@@ -3259,26 +3232,6 @@ describe('src/cy/commands/actions/click', () => {
|
||||
cy.dblclick()
|
||||
})
|
||||
|
||||
it('throws when subject is not in the document', (done) => {
|
||||
let dblclicked = 0
|
||||
|
||||
const $button = cy.$$('button:first').dblclick(() => {
|
||||
dblclicked += 1
|
||||
$button.remove()
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(dblclicked).to.eq(1)
|
||||
expect(err.message).to.include('`cy.dblclick()` failed because this element')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.get('button:first').dblclick().dblclick()
|
||||
})
|
||||
|
||||
it('logs once when not dom subject', function (done) {
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
@@ -3350,21 +3303,6 @@ describe('src/cy/commands/actions/click', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: remove this after 4.0 when {multiple:true} is no longer default
|
||||
// https://github.com/cypress-io/cypress/issues/5406
|
||||
it('does not log default option {multiple:true}', () => {
|
||||
const logs = []
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
logs.push(log)
|
||||
})
|
||||
|
||||
cy.get('button:first').dblclick().then(() => {
|
||||
expect(logs[1].get('message')).to.eq('')
|
||||
expect(logs[1].invoke('consoleProps').Options).not.ok
|
||||
})
|
||||
})
|
||||
|
||||
it('returns only the $el for the element of the subject that was dblclicked', () => {
|
||||
const dblclicks = []
|
||||
|
||||
@@ -3698,26 +3636,6 @@ describe('src/cy/commands/actions/click', () => {
|
||||
cy.rightclick()
|
||||
})
|
||||
|
||||
it('throws when subject is not in the document', (done) => {
|
||||
let rightclicked = 0
|
||||
|
||||
const $button = cy.$$('button:first').on('contextmenu', () => {
|
||||
rightclicked += 1
|
||||
$button.remove()
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(rightclicked).to.eq(1)
|
||||
expect(err.message).to.include('`cy.rightclick()` failed because this element')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.get('button:first').rightclick().rightclick()
|
||||
})
|
||||
|
||||
it('logs once when not dom subject', function (done) {
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
|
||||
@@ -336,7 +336,7 @@ describe('src/cy/commands/actions/focus', () => {
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(focused).to.eq(1)
|
||||
expect(err.message).to.include('`cy.focus()` failed because this element')
|
||||
expect(err.message).to.include('`cy.focus()` failed because the page updated')
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -791,7 +791,7 @@ describe('src/cy/commands/actions/focus', () => {
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(blurred).to.eq(1)
|
||||
expect(err.message).to.include('`cy.blur()` failed because this element')
|
||||
expect(err.message).to.include('`cy.blur()` failed because the page')
|
||||
expect(err.docsUrl).to.include('https://on.cypress.io/element-has-detached-from-dom')
|
||||
|
||||
done()
|
||||
|
||||
@@ -323,7 +323,7 @@ describe('src/cy/commands/actions/scroll', () => {
|
||||
})
|
||||
|
||||
it('retries until element is scrollable', () => {
|
||||
const $container = cy.$$('#nonscroll-becomes-scrollable')
|
||||
let $container = cy.$$('#nonscroll-becomes-scrollable')
|
||||
|
||||
expect($container.get(0).scrollTop).to.eq(0)
|
||||
expect($container.get(0).scrollLeft).to.eq(0)
|
||||
@@ -331,6 +331,11 @@ describe('src/cy/commands/actions/scroll', () => {
|
||||
let retried = false
|
||||
|
||||
cy.on('command:retry', _.after(2, () => {
|
||||
// Replacing the element with itself to ensure that .scrollTo() is requerying the DOM
|
||||
// as necessary
|
||||
$container.replaceWith($container[0].outerHTML)
|
||||
$container = cy.$$('#nonscroll-becomes-scrollable')
|
||||
|
||||
$container.css('overflow', 'scroll')
|
||||
retried = true
|
||||
}))
|
||||
@@ -347,10 +352,10 @@ describe('src/cy/commands/actions/scroll', () => {
|
||||
const scrollTo = cy.spy($.fn, 'scrollTo')
|
||||
|
||||
cy.get('button:first').scrollTo('bottom', { ensureScrollable: false }).then(() => {
|
||||
cy.stub(cy, 'ensureScrollability')
|
||||
cy.stub(Cypress.ensure, 'isScrollable')
|
||||
|
||||
expect(scrollTo).to.be.calledWithMatch({}, { ensureScrollable: false })
|
||||
expect(cy.ensureScrollability).not.to.be.called
|
||||
expect(Cypress.ensure.isScrollable).not.to.be.called
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -385,17 +390,17 @@ describe('src/cy/commands/actions/scroll', () => {
|
||||
})
|
||||
|
||||
it('waits until the subject is scrollable', () => {
|
||||
cy.stub(cy, 'ensureScrollability')
|
||||
cy.stub(Cypress.ensure, 'isScrollable')
|
||||
.onFirstCall().throws(new Error())
|
||||
|
||||
cy.on('command:retry', () => {
|
||||
return cy.ensureScrollability.returns()
|
||||
return Cypress.ensure.isScrollable.returns()
|
||||
})
|
||||
|
||||
cy
|
||||
.get('#scroll-into-view-horizontal')
|
||||
.scrollTo('right').then(() => {
|
||||
expect(cy.ensureScrollability).to.be.calledTwice
|
||||
expect(Cypress.ensure.isScrollable).to.be.calledTwice
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -430,7 +435,7 @@ describe('src/cy/commands/actions/scroll', () => {
|
||||
context('subject errors', () => {
|
||||
it('throws when not passed DOM element as subject', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.scrollTo()` failed because it requires a DOM element.')
|
||||
expect(err.message).to.include('`cy.scrollTo()` failed because it requires a DOM element or window.')
|
||||
expect(err.message).to.include('{foo: bar}')
|
||||
expect(err.message).to.include('> `cy.noop()`')
|
||||
|
||||
@@ -450,6 +455,17 @@ describe('src/cy/commands/actions/scroll', () => {
|
||||
|
||||
cy.get('button').scrollTo('500px')
|
||||
})
|
||||
|
||||
it('throws if subject disappears while waiting for scrollability', (done) => {
|
||||
cy.on('command:retry', () => cy.$$('#nonscroll-becomes-scrollable').remove())
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.scrollTo()` failed because the page updated')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.get('#nonscroll-becomes-scrollable').scrollTo(500, 300)
|
||||
})
|
||||
})
|
||||
|
||||
context('argument errors', () => {
|
||||
|
||||
@@ -214,11 +214,13 @@ describe('src/cy/commands/actions/select', () => {
|
||||
const select = cy.$$('select[name=disabled]')
|
||||
|
||||
cy.on('command:retry', _.once(() => {
|
||||
select.prop('disabled', false)
|
||||
// Replace the element with a copy of itself, to ensure .select() is requerying the DOM
|
||||
select.replaceWith(select[0].outerHTML)
|
||||
cy.$$('select[name=disabled]').prop('disabled', false)
|
||||
}))
|
||||
|
||||
cy.get('select[name=disabled]').select('foo')
|
||||
.invoke('val').should('eq', 'foo')
|
||||
cy.get('select[name=disabled]').invoke('val').should('eq', 'foo')
|
||||
})
|
||||
|
||||
it('retries until <optgroup> is no longer disabled', () => {
|
||||
@@ -376,7 +378,7 @@ describe('src/cy/commands/actions/select', () => {
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(selected).to.eq(1)
|
||||
expect(err.message).to.include('`cy.select()` failed because this element')
|
||||
expect(err.message).to.include('`cy.select()` failed because the page updated')
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -543,8 +545,7 @@ describe('src/cy/commands/actions/select', () => {
|
||||
|
||||
it('throws when the <select> itself is disabled', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.select()` failed because this element is currently disabled:')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/select')
|
||||
expect(err.message).to.include('`cy.select()` failed because this element is `disabled`:')
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -554,8 +555,7 @@ describe('src/cy/commands/actions/select', () => {
|
||||
|
||||
it('throws when the <select> is disabled by a disabled <fieldset>', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.select()` failed because this element is currently disabled:')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/select')
|
||||
expect(err.message).to.include('`cy.select()` failed because this element is `disabled`:')
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -648,7 +648,7 @@ describe('src/cy/commands/actions/select', () => {
|
||||
cy.get('#select-maps').select('de_dust2').then(function ($select) {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('$el')).to.eq($select)
|
||||
expect(lastLog.get('$el')).to.eql($select)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -336,7 +336,7 @@ describe('src/cy/commands/actions/selectFile', () => {
|
||||
})
|
||||
|
||||
describe('errors', {
|
||||
defaultCommandTimeout: 500,
|
||||
defaultCommandTimeout: 100,
|
||||
}, () => {
|
||||
it('is a child command', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
@@ -358,17 +358,17 @@ describe('src/cy/commands/actions/selectFile', () => {
|
||||
|
||||
it('throws when non-input subject', function (done) {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.selectFile()` can only be called on an `<input type="file">` or a `<label for="fileInput">` pointing to or containing one. Your subject is: `<body>...</body>`')
|
||||
expect(err.message).to.include('`cy.selectFile()` can only be called on an `<input type="file">` or a `<label for="fileInput">` pointing to or containing a file input, but received the element:')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.get('body').selectFile({ contents: '@foo' })
|
||||
cy.get('body').selectFile({ contents: '@foo' }, { timeout: 0 })
|
||||
})
|
||||
|
||||
it('throws when non-file input', function (done) {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.selectFile()` can only be called on an `<input type="file">` or a `<label for="fileInput">` pointing to or containing one. Your subject is: `<input type="text" id="text-input">`')
|
||||
expect(err.message).to.include('`cy.selectFile()` can only be called on an `<input type="file">` or a `<label for="fileInput">` pointing to or containing a file input, but received the element:')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
|
||||
done()
|
||||
})
|
||||
@@ -378,7 +378,7 @@ describe('src/cy/commands/actions/selectFile', () => {
|
||||
|
||||
it('throws when label for non-file input', function (done) {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.selectFile()` can only be called on an `<input type="file">` or a `<label for="fileInput">` pointing to or containing one. Your subject is: `<label for="text-input" id="text-label">Text label</label>`')
|
||||
expect(err.message).to.include('`cy.selectFile()` can only be called on an `<input type="file">` or a `<label for="fileInput">` pointing to or containing a file input, but received the element:')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
|
||||
done()
|
||||
})
|
||||
@@ -390,7 +390,7 @@ describe('src/cy/commands/actions/selectFile', () => {
|
||||
// Even though this label contains a file input, testing on real browsers confirms that clicking it
|
||||
// does *not* activate the contained input.
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.selectFile()` can only be called on an `<input type="file">` or a `<label for="fileInput">` pointing to or containing one. Your subject is: `<label for="nonexistent" id="nonexistent-label">...</label>`')
|
||||
expect(err.message).to.include('`cy.selectFile()` can only be called on an `<input type="file">` or a `<label for="fileInput">` pointing to or containing a file input, but received the element:')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/selectfile')
|
||||
done()
|
||||
})
|
||||
@@ -511,6 +511,32 @@ describe('src/cy/commands/actions/selectFile', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('retries until label is not disabled', () => {
|
||||
cy.on('command:retry', () => {
|
||||
// Replace the label with a copy of itself, to ensure selectFile is requerying the DOM
|
||||
const hidden = cy.$$('#hidden-basic-label')
|
||||
|
||||
hidden.replaceWith(hidden[0].outerHTML)
|
||||
|
||||
cy.$$('#hidden-basic-label').show()
|
||||
})
|
||||
|
||||
cy.get('#hidden-basic-label').selectFile({ contents: '@foo' })
|
||||
})
|
||||
|
||||
it('retries until input is not disabled', () => {
|
||||
cy.on('command:retry', () => {
|
||||
// Replace the input with a copy of itself, to ensure selectFile is requerying the DOM
|
||||
const disabled = cy.$$('#disabled')
|
||||
|
||||
disabled.replaceWith(disabled[0].outerHTML)
|
||||
|
||||
cy.$$('#disabled').attr('disabled', false)
|
||||
})
|
||||
|
||||
cy.get('#disabled-label').selectFile({ contents: '@foo' })
|
||||
})
|
||||
|
||||
/*
|
||||
* The tests around actionability are somewhat limited, since the functionality is thoroughly tested in the
|
||||
* `cy.trigger()` unit tests. We include a few tests directly on `cy.selectFile()` in order to ensure we're
|
||||
@@ -628,25 +654,6 @@ is being covered by another element:
|
||||
})
|
||||
})
|
||||
|
||||
it('waits until input stops animating', {
|
||||
defaultCommandTimeout: 1000,
|
||||
}, () => {
|
||||
let retries = 0
|
||||
|
||||
cy.on('command:retry', (obj) => {
|
||||
retries += 1
|
||||
})
|
||||
|
||||
cy.stub(cy, 'ensureElementIsNotAnimating')
|
||||
.throws(new Error('animating!'))
|
||||
.onThirdCall().returns()
|
||||
|
||||
cy.get('#basic').selectFile({ contents: '@foo' }).then(() => {
|
||||
expect(retries).to.eq(3)
|
||||
expect(cy.ensureElementIsNotAnimating).to.be.calledThrice
|
||||
})
|
||||
})
|
||||
|
||||
// TODO(webkit): fix+unskip for experimental webkit
|
||||
it('can specify scrollBehavior in options', { browser: '!webkit' }, () => {
|
||||
cy.get('#scroll').then((el) => {
|
||||
|
||||
@@ -255,7 +255,7 @@ describe('src/cy/commands/actions/submit', () => {
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(submitted).to.eq(1)
|
||||
expect(err.message).to.include('`cy.submit()` failed because this element')
|
||||
expect(err.message).to.include('`cy.submit()` failed because the page')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -149,6 +149,18 @@ describe('src/cy/commands/actions/trigger', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('requeries the dom while waiting for actionability', () => {
|
||||
const $input = cy.$$('input:first').attr('disabled', true)
|
||||
|
||||
cy.on('command:retry', () => {
|
||||
// Replace the input with a copy of itself, to ensure trigger is requerying the DOM
|
||||
$input.replaceWith($input[0].outerHTML)
|
||||
cy.$$('input:first').attr('disabled', false)
|
||||
})
|
||||
|
||||
cy.get('input:first').trigger('keydown')
|
||||
})
|
||||
|
||||
it('can trigger events on the window', () => {
|
||||
let expected = false
|
||||
|
||||
@@ -224,6 +236,15 @@ describe('src/cy/commands/actions/trigger', () => {
|
||||
})
|
||||
|
||||
describe('actionability', () => {
|
||||
let retries = 0
|
||||
|
||||
beforeEach(() => {
|
||||
retries = 0
|
||||
cy.on('command:retry', () => {
|
||||
retries += 1
|
||||
})
|
||||
})
|
||||
|
||||
it('can trigger on elements which are hidden until scrolled within parent container', () => {
|
||||
cy.get('#overflow-auto-container').contains('quux').trigger('mousedown')
|
||||
})
|
||||
@@ -268,24 +289,19 @@ describe('src/cy/commands/actions/trigger', () => {
|
||||
$('<span>span on button</span>').css({ position: 'absolute', left: $btn.offset().left, top: $btn.offset().top, padding: 5, display: 'inline-block', backgroundColor: 'yellow' }).prependTo(cy.$$('body'))
|
||||
|
||||
const scrolled = []
|
||||
let retried = false
|
||||
let tapped = false
|
||||
|
||||
cy.on('scrolled', ($el, type) => {
|
||||
scrolled.push(type)
|
||||
})
|
||||
|
||||
cy.on('command:retry', ($el, type) => {
|
||||
retried = true
|
||||
})
|
||||
|
||||
$btn.on('tap', () => {
|
||||
tapped = true
|
||||
})
|
||||
|
||||
cy.get('#button-covered-in-span').trigger('tap', { force: true }).then(() => {
|
||||
expect(scrolled).to.be.empty
|
||||
expect(retried).to.be.false
|
||||
expect(retries).to.eq(0)
|
||||
expect(tapped).to.be.true
|
||||
})
|
||||
})
|
||||
@@ -306,7 +322,6 @@ describe('src/cy/commands/actions/trigger', () => {
|
||||
.prependTo(cy.$$('body'))
|
||||
|
||||
const scrolled = []
|
||||
let retried = false
|
||||
|
||||
cy.on('scrolled', ($el, type) => {
|
||||
return scrolled.push(type)
|
||||
@@ -314,11 +329,10 @@ describe('src/cy/commands/actions/trigger', () => {
|
||||
|
||||
cy.on('command:retry', _.after(3, () => {
|
||||
$span.hide()
|
||||
retried = true
|
||||
}))
|
||||
|
||||
cy.get('#button-covered-in-span').trigger('mousedown').then(() => {
|
||||
expect(retried).to.be.true
|
||||
expect(retries).to.be.gt(1)
|
||||
|
||||
// - element scrollIntoView
|
||||
// - element scrollIntoView (retry animation coords)
|
||||
@@ -517,15 +531,12 @@ describe('src/cy/commands/actions/trigger', () => {
|
||||
it('waits until element becomes visible', () => {
|
||||
const $btn = cy.$$('#button').hide()
|
||||
|
||||
let retried = false
|
||||
|
||||
cy.on('command:retry', _.after(3, () => {
|
||||
$btn.show()
|
||||
retried = true
|
||||
}))
|
||||
|
||||
cy.get('#button').trigger('mouseover').then(() => {
|
||||
expect(retried).to.be.true
|
||||
expect(retries).to.be.gt(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -551,66 +562,44 @@ describe('src/cy/commands/actions/trigger', () => {
|
||||
})
|
||||
|
||||
it('waits until element stops animating', () => {
|
||||
let retries = 0
|
||||
|
||||
cy.on('command:retry', (obj) => {
|
||||
retries += 1
|
||||
})
|
||||
|
||||
cy.stub(cy, 'ensureElementIsNotAnimating')
|
||||
.throws(new Error('animating!'))
|
||||
.onThirdCall().returns()
|
||||
|
||||
cy.get('button:first').trigger('mouseover').then(() => {
|
||||
// - retry animation coords
|
||||
// - retry animation
|
||||
// - retry animation
|
||||
expect(retries).to.eq(3)
|
||||
expect(cy.ensureElementIsNotAnimating).to.be.calledThrice
|
||||
cy.get('button:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).trigger('mouseover').then(() => {
|
||||
expect(retries).to.be.gt(1)
|
||||
})
|
||||
})
|
||||
|
||||
it('does not throw when waiting for animations is disabled', {
|
||||
it('does not wait when waiting for animations is disabled', {
|
||||
waitForAnimations: false,
|
||||
}, () => {
|
||||
cy.stub(cy, 'ensureElementIsNotAnimating').throws(new Error('animating!'))
|
||||
|
||||
cy.get('button:first').trigger('mouseover').then(() => {
|
||||
expect(cy.ensureElementIsNotAnimating).not.to.be.called
|
||||
cy.get('button:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).trigger('mouseover').then(() => {
|
||||
expect(retries).to.eq(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('does not throw when turning off waitForAnimations in options', () => {
|
||||
cy.stub(cy, 'ensureElementIsNotAnimating').throws(new Error('animating!'))
|
||||
|
||||
cy.get('button:first').trigger('tap', { waitForAnimations: false }).then(() => {
|
||||
expect(cy.ensureElementIsNotAnimating).not.to.be.called
|
||||
it('does not wait when turning off waitForAnimations in options', () => {
|
||||
cy.get('button:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).trigger('tap', { waitForAnimations: false }).then(() => {
|
||||
expect(retries).to.eq(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('passes options.animationDistanceThreshold to cy.ensureElementIsNotAnimating', () => {
|
||||
cy.spy(cy, 'ensureElementIsNotAnimating')
|
||||
|
||||
cy.get('button:first').trigger('tap', { animationDistanceThreshold: 1000 }).then(($btn) => {
|
||||
const { fromElWindow } = Cypress.dom.getElementCoordinatesByPosition($btn)
|
||||
const { args } = cy.ensureElementIsNotAnimating.firstCall
|
||||
|
||||
expect(args[1]).to.deep.eq([fromElWindow, fromElWindow])
|
||||
expect(args[2]).to.eq(1000)
|
||||
it('passes options.animationDistanceThreshold to ensureElementIsNotAnimating', () => {
|
||||
cy.get('button:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).trigger('tap', { animationDistanceThreshold: 1000 }).then(($btn) => {
|
||||
// One retry, because $actionability always waits for two sets of points to determine if an element is animating.
|
||||
expect(retries).to.eq(1)
|
||||
})
|
||||
})
|
||||
|
||||
it('passes config.animationDistanceThreshold to cy.ensureElementIsNotAnimating', () => {
|
||||
const animationDistanceThreshold = Cypress.config('animationDistanceThreshold')
|
||||
it('passes config.animationDistanceThreshold to ensureElementIsNotAnimating', () => {
|
||||
let old = Cypress.config('animationDistanceThreshold')
|
||||
|
||||
cy.spy(cy, 'ensureElementIsNotAnimating')
|
||||
Cypress.config('animationDistanceThreshold', 1000)
|
||||
|
||||
cy.get('button:first').trigger('mouseover').then(($btn) => {
|
||||
const { fromElWindow } = Cypress.dom.getElementCoordinatesByPosition($btn)
|
||||
const { args } = cy.ensureElementIsNotAnimating.firstCall
|
||||
|
||||
expect(args[1]).to.deep.eq([fromElWindow, fromElWindow])
|
||||
expect(args[2]).to.eq(animationDistanceThreshold)
|
||||
cy.get('button:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).trigger('mouseover').then(($btn) => {
|
||||
// One retry, because $actionability always waits for two sets of points to determine if an element is animating.
|
||||
try {
|
||||
expect(retries).to.eq(1)
|
||||
} finally {
|
||||
Cypress.config('animationDistanceThreshold', old)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1057,7 +1046,7 @@ describe('src/cy/commands/actions/trigger', () => {
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(mouseover).to.eq(1)
|
||||
expect(err.message).to.include('`cy.trigger()` failed because this element')
|
||||
expect(err.message).to.include('`cy.trigger()` failed because the page')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -93,6 +93,10 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
cy.get('input:text:first').type('bar')
|
||||
})
|
||||
|
||||
it('can type into an input when given a wrapper element', () => {
|
||||
cy.get('#focus div span').type('foo')
|
||||
})
|
||||
|
||||
it('lists the input as the focused element', () => {
|
||||
const $input = cy.$$('input:text:first')
|
||||
|
||||
@@ -200,6 +204,15 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
})
|
||||
|
||||
describe('actionability', () => {
|
||||
let retries = 0
|
||||
|
||||
beforeEach(() => {
|
||||
retries = 0
|
||||
cy.on('command:retry', () => {
|
||||
retries += 1
|
||||
})
|
||||
})
|
||||
|
||||
it('can forcibly type + click even when element is invisible', () => {
|
||||
const $txt = cy.$$(':text:first').hide()
|
||||
|
||||
@@ -261,35 +274,32 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
it('waits until element becomes visible', () => {
|
||||
const $txt = cy.$$(':text:first').hide()
|
||||
|
||||
const retried = cy.stub()
|
||||
|
||||
cy.on('command:retry', _.after(3, () => {
|
||||
$txt.show()
|
||||
retried()
|
||||
// Replace the element with a copy of itself, to ensure that .type() requeries the DOM
|
||||
// while retrying actionability
|
||||
$txt.replaceWith($txt[0].innerHTML)
|
||||
cy.$$(':text:first').show()
|
||||
}))
|
||||
|
||||
cy.get(':text:first').type('foo').then(() => {
|
||||
expect(retried).to.be.called
|
||||
expect(retries).to.be.gt(1)
|
||||
})
|
||||
})
|
||||
|
||||
it('waits until element is no longer disabled', () => {
|
||||
const $txt = cy.$$(':text:first').prop('disabled', true)
|
||||
|
||||
const retried = cy.stub()
|
||||
const clicked = cy.stub()
|
||||
|
||||
$txt.on('click', clicked)
|
||||
|
||||
cy.on('command:retry', _.after(3, () => {
|
||||
$txt.prop('disabled', false)
|
||||
retried()
|
||||
}))
|
||||
|
||||
cy.get(':text:first').type('foo').then(() => {
|
||||
expect(clicked).to.be.calledOnce
|
||||
|
||||
expect(retried).to.be.called
|
||||
expect(retries).to.be.gt(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -314,67 +324,44 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
})
|
||||
|
||||
it('waits until element stops animating', () => {
|
||||
const retried = cy.stub()
|
||||
|
||||
cy.on('command:retry', retried)
|
||||
|
||||
cy.stub(cy, 'ensureElementIsNotAnimating')
|
||||
.throws(new Error('animating!'))
|
||||
.onThirdCall().returns()
|
||||
|
||||
cy.get(':text:first').type('foo').then(() => {
|
||||
// - retry animation coords
|
||||
// - retry animation
|
||||
// - retry animation
|
||||
expect(retried).to.be.calledThrice
|
||||
|
||||
expect(cy.ensureElementIsNotAnimating).to.be.calledThrice
|
||||
cy.get('button:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).type('foo').then(() => {
|
||||
expect(retries).to.be.gt(1)
|
||||
})
|
||||
})
|
||||
|
||||
it('does not throw when waiting for animations is disabled', {
|
||||
it('does not wait when waiting for animations is disabled', {
|
||||
waitForAnimations: false,
|
||||
}, () => {
|
||||
cy.stub(cy, 'ensureElementIsNotAnimating').throws(new Error('animating!'))
|
||||
|
||||
cy.get(':text:first').type('foo').then(() => {
|
||||
expect(cy.ensureElementIsNotAnimating).not.to.be.called
|
||||
cy.get(':text:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).type('foo').then(() => {
|
||||
expect(retries).to.eq(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('does not throw when turning off waitForAnimations in options', () => {
|
||||
cy.stub(cy, 'ensureElementIsNotAnimating').throws(new Error('animating!'))
|
||||
|
||||
cy.get(':text:first').type('foo', { waitForAnimations: false }).then(() => {
|
||||
expect(cy.ensureElementIsNotAnimating).not.to.be.called
|
||||
it('does not wait when turning off waitForAnimations in options', () => {
|
||||
cy.get(':text:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).type('foo', { waitForAnimations: false }).then(() => {
|
||||
expect(retries).to.eq(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('passes options.animationDistanceThreshold to cy.ensureElementIsNotAnimating', () => {
|
||||
cy.spy(cy, 'ensureElementIsNotAnimating')
|
||||
|
||||
cy.get(':text:first').type('foo', { animationDistanceThreshold: 1000 }).then(($txt) => {
|
||||
const { fromElWindow } = Cypress.dom.getElementCoordinatesByPosition($txt)
|
||||
const { args } = cy.ensureElementIsNotAnimating.firstCall
|
||||
|
||||
expect(args[1]).to.deep.eq([fromElWindow, fromElWindow])
|
||||
|
||||
expect(args[2]).to.eq(1000)
|
||||
it('passes options.animationDistanceThreshold to ensureElementIsNotAnimating', () => {
|
||||
cy.get(':text:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).type('foo', { animationDistanceThreshold: 1000 }).then(($txt) => {
|
||||
// One retry, because $actionability always waits for two sets of points to determine if an element is animating.
|
||||
expect(retries).to.eq(1)
|
||||
})
|
||||
})
|
||||
|
||||
it('passes config.animationDistanceThreshold to cy.ensureElementIsNotAnimating', () => {
|
||||
const animationDistanceThreshold = Cypress.config('animationDistanceThreshold')
|
||||
it('passes config.animationDistanceThreshold to ensureElementIsNotAnimating', () => {
|
||||
let old = Cypress.config('animationDistanceThreshold')
|
||||
|
||||
cy.spy(cy, 'ensureElementIsNotAnimating')
|
||||
Cypress.config('animationDistanceThreshold', 1000)
|
||||
|
||||
cy.get(':text:first').type('foo').then(($txt) => {
|
||||
const { fromElWindow } = Cypress.dom.getElementCoordinatesByPosition($txt)
|
||||
const { args } = cy.ensureElementIsNotAnimating.firstCall
|
||||
|
||||
expect(args[1]).to.deep.eq([fromElWindow, fromElWindow])
|
||||
|
||||
expect(args[2]).to.eq(animationDistanceThreshold)
|
||||
cy.get(':text:first').then(($btn) => $btn.animate({ width: '30em' }, 100)).type('foo').then(($txt) => {
|
||||
// One retry, because $actionability always waits for two sets of points to determine if an element is animating.
|
||||
try {
|
||||
expect(retries).to.eq(1)
|
||||
} finally {
|
||||
Cypress.config('animationDistanceThreshold', old)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -924,7 +911,8 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
})
|
||||
|
||||
it('inserts text after existing text input by invoking val', () => {
|
||||
cy.get('#input-without-value').invoke('val', 'foo').type(' bar').then(($text) => {
|
||||
cy.get('#input-without-value').invoke('val', 'foo')
|
||||
cy.get('#input-without-value').type(' bar').then(($text) => {
|
||||
expect($text).to.have.value('foo bar')
|
||||
})
|
||||
})
|
||||
@@ -1224,7 +1212,8 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
})
|
||||
|
||||
it('inserts text after existing text input by invoking val', () => {
|
||||
cy.get('#number-without-value').invoke('val', '12').type('34').then(($text) => {
|
||||
cy.get('#number-without-value').invoke('val', '12')
|
||||
cy.get('#number-without-value').type('34').then(($text) => {
|
||||
expect($text).to.have.value('1234')
|
||||
})
|
||||
})
|
||||
@@ -1344,7 +1333,8 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
})
|
||||
|
||||
it('inserts text after existing text input by invoking val', () => {
|
||||
cy.get('#email-without-value').invoke('val', 'brian@foo.c').type('om').then(($text) => {
|
||||
cy.get('#email-without-value').invoke('val', 'brian@foo.c')
|
||||
cy.get('#email-without-value').type('om').then(($text) => {
|
||||
expect($text).to.have.value('brian@foo.com')
|
||||
})
|
||||
})
|
||||
@@ -1384,7 +1374,8 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
})
|
||||
|
||||
it('inserts text after existing text input by invoking val', () => {
|
||||
cy.get('#password-without-value').invoke('val', 'secr').type('et').then(($text) => {
|
||||
cy.get('#password-without-value').invoke('val', 'secr')
|
||||
cy.get('#password-without-value').type('et').then(($text) => {
|
||||
expect($text).to.have.value('secret')
|
||||
})
|
||||
})
|
||||
@@ -1441,7 +1432,8 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
})
|
||||
|
||||
it('overwrites existing value input by invoking val', () => {
|
||||
cy.get('#date-without-value').invoke('val', '2016-01-01').type('1959-09-13').then(($text) => {
|
||||
cy.get('#date-without-value').invoke('val', '2016-01-01')
|
||||
cy.get('#date-without-value').type('1959-09-13').then(($text) => {
|
||||
expect($text).to.have.value('1959-09-13')
|
||||
})
|
||||
})
|
||||
@@ -1531,7 +1523,8 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
})
|
||||
|
||||
it('overwrites existing value input by invoking val', () => {
|
||||
cy.get('[type="datetime-local"]').invoke('val', '2016-01-01T05:05').type('1959-09-13T10:10').should('have.value', '1959-09-13T10:10')
|
||||
cy.get('[type="datetime-local"]').invoke('val', '2016-01-01T05:05')
|
||||
cy.get('[type="datetime-local"]').type('1959-09-13T10:10').should('have.value', '1959-09-13T10:10')
|
||||
})
|
||||
|
||||
it('errors when invalid datetime', (done) => {
|
||||
@@ -1559,7 +1552,8 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
})
|
||||
|
||||
it('overwrites existing value input by invoking val', () => {
|
||||
cy.get('#month-without-value').invoke('val', '2016-01').type('1959-09').then(($text) => {
|
||||
cy.get('#month-without-value').invoke('val', '2016-01')
|
||||
cy.get('#month-without-value').type('1959-09').then(($text) => {
|
||||
expect($text).to.have.value('1959-09')
|
||||
})
|
||||
})
|
||||
@@ -1579,7 +1573,8 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
})
|
||||
|
||||
it('overwrites existing value input by invoking val', () => {
|
||||
cy.get('#week-without-value').invoke('val', '2016-W01').type('1959-W09').then(($text) => {
|
||||
cy.get('#week-without-value').invoke('val', '2016-W01')
|
||||
cy.get('#week-without-value').type('1959-W09').then(($text) => {
|
||||
expect($text).to.have.value('1959-W09')
|
||||
})
|
||||
})
|
||||
@@ -1599,7 +1594,8 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
})
|
||||
|
||||
it('overwrites existing value input by invoking val', () => {
|
||||
cy.get('#time-without-value').invoke('val', '01:23:45').type('12:34:56').then(($text) => {
|
||||
cy.get('#time-without-value').invoke('val', '01:23:45')
|
||||
cy.get('#time-without-value').type('12:34:56').then(($text) => {
|
||||
expect($text).to.have.value('12:34:56')
|
||||
})
|
||||
})
|
||||
@@ -1643,7 +1639,8 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
})
|
||||
|
||||
it('inserts text after existing text', () => {
|
||||
cy.get('#input-types [contenteditable]').invoke('text', 'foo').type(' bar').then(($text) => {
|
||||
cy.get('#input-types [contenteditable]').invoke('text', 'foo')
|
||||
cy.get('#input-types [contenteditable]').type(' bar').then(($text) => {
|
||||
expect($text).to.have.text('foo bar')
|
||||
})
|
||||
})
|
||||
@@ -1668,9 +1665,8 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
|
||||
attachKeyListeners({ ce })
|
||||
|
||||
cy.get('#input-types [contenteditable]')
|
||||
.invoke('text', 'foo')
|
||||
.type('{enter}')
|
||||
cy.get('#input-types [contenteditable]').invoke('text', 'foo')
|
||||
cy.get('#input-types [contenteditable]').type('{enter}')
|
||||
.should(($text) => {
|
||||
expect(trimInnerText($text)).eq('foo')
|
||||
})
|
||||
@@ -2488,7 +2484,8 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
|
||||
describe('case-insensitivity', () => {
|
||||
it('special chars are case-insensitive', () => {
|
||||
cy.get(':text:first').invoke('val', 'bar').type('{leftarrow}{DeL}').then(($input) => {
|
||||
cy.get(':text:first').invoke('val', 'bar')
|
||||
cy.get(':text:first').type('{leftarrow}{DeL}').then(($input) => {
|
||||
expect($input).to.have.value('ba')
|
||||
})
|
||||
})
|
||||
@@ -2787,9 +2784,8 @@ describe('src/cy/commands/actions/type - #type', () => {
|
||||
// even if actual and expected appear the same.
|
||||
const expected = '{\n foo: 1\n bar: 2\n baz: 3\n}'
|
||||
|
||||
cy.get('[contenteditable]:first')
|
||||
.invoke('html', '<div><br></div>')
|
||||
.type('{{}{enter} foo: 1{enter} bar: 2{enter} baz: 3{enter}}')
|
||||
cy.get('[contenteditable]:first').invoke('html', '<div><br></div>')
|
||||
cy.get('[contenteditable]:first').type('{{}{enter} foo: 1{enter} bar: 2{enter} baz: 3{enter}}')
|
||||
.should(($el) => {
|
||||
expectMatchInnerText($el, expected)
|
||||
})
|
||||
|
||||
@@ -40,7 +40,7 @@ describe('src/cy/commands/actions/type - #type errors', () => {
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(typed).to.be.calledOnce
|
||||
expect(err.message).to.include('`cy.type()` failed because this element')
|
||||
expect(err.message).to.include('`cy.type()` failed because the page')
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -249,6 +249,8 @@ If you want to skip parsing special character sequences and type the text exactl
|
||||
it('can type into input with invalid type attribute', () => {
|
||||
cy.get(':text:first')
|
||||
.invoke('attr', 'type', 'asdf')
|
||||
|
||||
cy.get(':text:first')
|
||||
.type('foobar')
|
||||
.should('have.value', 'foobar')
|
||||
})
|
||||
|
||||
@@ -323,10 +323,6 @@ describe('src/cy/commands/agents', () => {
|
||||
expect(cy.state('aliases').myStub).to.exist
|
||||
})
|
||||
|
||||
it('stores the agent as the subject', function () {
|
||||
expect(cy.state('aliases').myStub.subject).to.eq(this.stub)
|
||||
})
|
||||
|
||||
it('assigns subject to runnable ctx', function () {
|
||||
expect(this.myStub).to.eq(this.stub)
|
||||
})
|
||||
@@ -404,10 +400,6 @@ describe('src/cy/commands/agents', () => {
|
||||
expect(cy.state('aliases')['my.stub']).to.exist
|
||||
})
|
||||
|
||||
it('stores the agent as the subject', function () {
|
||||
expect(cy.state('aliases')['my.stub'].subject).to.eq(this.stub)
|
||||
})
|
||||
|
||||
it('assigns subject to runnable ctx', function () {
|
||||
expect(this['my.stub']).to.eq(this.stub)
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const { assertLogLength } = require('../../support/utils')
|
||||
const { _, $ } = Cypress
|
||||
const { _ } = Cypress
|
||||
|
||||
describe('src/cy/commands/aliasing', () => {
|
||||
beforeEach(() => {
|
||||
@@ -7,14 +7,6 @@ describe('src/cy/commands/aliasing', () => {
|
||||
})
|
||||
|
||||
context('#as', () => {
|
||||
it('is special utility command', () => {
|
||||
cy.wrap('foo').as('f').then(() => {
|
||||
const cmd = cy.queue.find({ name: 'as' })
|
||||
|
||||
expect(cmd.get('type')).to.eq('utility')
|
||||
})
|
||||
})
|
||||
|
||||
it('does not change the subject', () => {
|
||||
const body = cy.$$('body')
|
||||
|
||||
@@ -34,11 +26,13 @@ describe('src/cy/commands/aliasing', () => {
|
||||
cy.get('@body')
|
||||
})
|
||||
|
||||
it('stores the resulting subject as the alias', () => {
|
||||
const $body = cy.$$('body')
|
||||
|
||||
it('stores the resulting subject chain as the alias', () => {
|
||||
cy.get('body').as('b').then(() => {
|
||||
expect(cy.state('aliases').b.subject.get(0)).to.eq($body.get(0))
|
||||
const { subjectChain } = cy.state('aliases').b
|
||||
|
||||
expect(subjectChain.length).to.eql(2)
|
||||
expect(subjectChain[0]).to.be.undefined
|
||||
expect(subjectChain[1].commandName).to.eq('get')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -50,6 +44,15 @@ describe('src/cy/commands/aliasing', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('retries previous commands invoked inside custom commands', () => {
|
||||
Cypress.Commands.add('get2', (selector) => cy.get(selector))
|
||||
|
||||
cy.get2('body').children('div').as('divs')
|
||||
cy.visit('/fixtures/dom.html')
|
||||
|
||||
cy.get('@divs')
|
||||
})
|
||||
|
||||
it('retries primitives and assertions', () => {
|
||||
const obj = {}
|
||||
|
||||
@@ -87,29 +90,13 @@ describe('src/cy/commands/aliasing', () => {
|
||||
})
|
||||
})
|
||||
|
||||
context('DOM subjects', () => {
|
||||
it('assigns the remote jquery instance', () => {
|
||||
const obj = {}
|
||||
it('retries previous commands invoked inside custom commands', () => {
|
||||
Cypress.Commands.add('get2', (selector) => cy.get(selector))
|
||||
|
||||
const jquery = () => {
|
||||
return obj
|
||||
}
|
||||
cy.get2('body').children('div').as('divs')
|
||||
cy.visit('/fixtures/dom.html')
|
||||
|
||||
cy.state('jQuery', jquery)
|
||||
|
||||
cy.get('input:first').as('input').then(function () {
|
||||
expect(this.input).to.eq(obj)
|
||||
})
|
||||
})
|
||||
|
||||
it('retries previous commands invoked inside custom commands', () => {
|
||||
Cypress.Commands.add('get2', (selector) => cy.get(selector))
|
||||
|
||||
cy.get2('body').children('div').as('divs')
|
||||
cy.visit('/fixtures/dom.html')
|
||||
|
||||
cy.get('@divs')
|
||||
})
|
||||
cy.get('@divs')
|
||||
})
|
||||
|
||||
context('#assign', () => {
|
||||
@@ -305,8 +292,7 @@ describe('src/cy/commands/aliasing', () => {
|
||||
it('does not match alias when the alias has already been applied', () => {
|
||||
cy
|
||||
.visit('/fixtures/commands.html')
|
||||
.server()
|
||||
.route(/foo/, {}).as('getFoo')
|
||||
.intercept(/foo/, {}).as('getFoo')
|
||||
.then(function () {
|
||||
// 1 log from visit
|
||||
// 1 log from route
|
||||
@@ -328,9 +314,8 @@ describe('src/cy/commands/aliasing', () => {
|
||||
// sanity check without command overwrite
|
||||
cy.wrap('alias value').as('myAlias')
|
||||
.then(() => {
|
||||
expect(cy.getAlias('@myAlias'), 'alias exists').to.exist
|
||||
expect(cy.getAlias('@myAlias'), 'alias value')
|
||||
.to.have.property('subject', 'alias value')
|
||||
expect(cy.getAlias('@myAlias')).to.exist
|
||||
expect(cy.getAlias('@myAlias').subjectChain).to.eql(['alias value'])
|
||||
})
|
||||
.then(() => {
|
||||
// cy.get returns the alias
|
||||
@@ -349,10 +334,9 @@ describe('src/cy/commands/aliasing', () => {
|
||||
|
||||
cy.wrap('alias value').as('myAlias')
|
||||
.then(() => {
|
||||
expect(wrapCalled, 'overwrite was called').to.be.true
|
||||
expect(cy.getAlias('@myAlias'), 'alias exists').to.exist
|
||||
expect(cy.getAlias('@myAlias'), 'alias value')
|
||||
.to.have.property('subject', 'alias value')
|
||||
expect(wrapCalled).to.be.true
|
||||
expect(cy.getAlias('@myAlias')).to.exist
|
||||
expect(cy.getAlias('@myAlias').subjectChain).to.eql(['alias value'])
|
||||
})
|
||||
.then(() => {
|
||||
// verify cy.get works in arrow function
|
||||
@@ -382,9 +366,8 @@ describe('src/cy/commands/aliasing', () => {
|
||||
.then(() => {
|
||||
expect(wrapCalled, 'overwrite was called').to.be.true
|
||||
expect(thenCalled, 'then was called').to.be.true
|
||||
expect(cy.getAlias('@myAlias'), 'alias exists').to.exist
|
||||
expect(cy.getAlias('@myAlias'), 'alias value')
|
||||
.to.have.property('subject', 'alias value')
|
||||
expect(cy.getAlias('@myAlias')).to.exist
|
||||
expect(cy.getAlias('@myAlias').subjectChain).to.eql(['alias value'])
|
||||
})
|
||||
.then(() => {
|
||||
// verify cy.get works in arrow function
|
||||
@@ -400,9 +383,9 @@ describe('src/cy/commands/aliasing', () => {
|
||||
// sanity test before the next one
|
||||
cy.wrap(1).as('myAlias')
|
||||
cy.wrap(2).then(function (subj) {
|
||||
expect(subj, 'subject').to.equal(2)
|
||||
expect(this, 'this is defined').to.not.be.undefined
|
||||
expect(this.myAlias, 'this has the alias as a property').to.eq(1)
|
||||
expect(subj).to.equal(2)
|
||||
expect(this).to.not.be.undefined
|
||||
expect(this.myAlias).to.eq(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -414,8 +397,8 @@ describe('src/cy/commands/aliasing', () => {
|
||||
|
||||
cy.wrap(1).as('myAlias')
|
||||
cy.wrap(2).then(function (subj) {
|
||||
expect(subj, 'subject').to.equal(2)
|
||||
expect(this, 'this is defined').to.not.be.undefined
|
||||
expect(subj).to.equal(2)
|
||||
expect(this).to.not.be.undefined
|
||||
expect(this.myAlias).to.eq(1)
|
||||
})
|
||||
})
|
||||
@@ -428,166 +411,61 @@ describe('src/cy/commands/aliasing', () => {
|
||||
|
||||
cy.wrap(1).as('myAlias')
|
||||
cy.wrap(2).then(function (subj) {
|
||||
expect(subj, 'subject').to.equal(2)
|
||||
expect(this, 'this is defined').to.not.be.undefined
|
||||
expect(subj).to.equal(2)
|
||||
expect(this).to.not.be.undefined
|
||||
expect(this.myAlias).to.eq(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#replayCommandsFrom', () => {
|
||||
describe('subject in document', () => {
|
||||
it('returns if subject is still in the document', () => {
|
||||
cy.get('#list').as('list').then(() => {
|
||||
const currentLength = cy.queue.length
|
||||
|
||||
cy.get('@list').then(() => {
|
||||
// should only add the .get() and the .then()
|
||||
expect(cy.queue.length).to.eq(currentLength + 2)
|
||||
})
|
||||
context('#replaying subjects', () => {
|
||||
it('returns if subject is still in the document', () => {
|
||||
cy.get('#list').as('list').then((firstList) => {
|
||||
cy.get('@list').then((secondList) => {
|
||||
expect(firstList).to.eql(secondList)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('subject not in document', () => {
|
||||
it('inserts into the queue', () => {
|
||||
const existingNames = cy.queue.names()
|
||||
it('requeries when reading alias', () => {
|
||||
cy
|
||||
.get('#list li')
|
||||
.as('items').then((firstItems) => {
|
||||
cy.$$('#list').append('<li class="foobar">123456789</li>')
|
||||
|
||||
cy
|
||||
.get('#list li').eq(0).as('firstLi').then(($li) => {
|
||||
return $li.remove()
|
||||
})
|
||||
.get('@firstLi').then(() => {
|
||||
expect(cy.queue.names()).to.deep.eq(
|
||||
existingNames.concat(
|
||||
['get', 'eq', 'as', 'then', 'get', 'get', 'eq', 'then'],
|
||||
),
|
||||
)
|
||||
cy.get('@items').then((secondItems) => {
|
||||
expect(firstItems).to.have.length(3)
|
||||
expect(secondItems).to.have.length(4)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('replays from last root to current', () => {
|
||||
const first = cy.$$('#list li').eq(0)
|
||||
const second = cy.$$('#list li').eq(1)
|
||||
it('requeries when subject is not in the DOM', () => {
|
||||
cy
|
||||
.get('#list li')
|
||||
.as('items').then((firstItems) => {
|
||||
firstItems.remove()
|
||||
setTimeout(() => {
|
||||
cy.$$('#list').append('<li class="foobar">123456789</li>')
|
||||
}, 50)
|
||||
|
||||
cy
|
||||
.get('#list li').eq(0).as('firstLi').then(($li) => {
|
||||
expect($li.get(0)).to.eq(first.get(0))
|
||||
|
||||
return $li.remove()
|
||||
})
|
||||
.get('@firstLi').then(($li) => {
|
||||
expect($li.get(0)).to.eq(second.get(0))
|
||||
cy.get('@items').then((secondItems) => {
|
||||
expect(secondItems).to.have.length(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('replays up until first root command', () => {
|
||||
const existingNames = cy.queue.names()
|
||||
it('only retries up to last command', () => {
|
||||
cy
|
||||
.get('#list li')
|
||||
.then((items) => items.length)
|
||||
.as('itemCount')
|
||||
.then(() => cy.$$('#list li').remove())
|
||||
|
||||
cy
|
||||
.get('body').noop({})
|
||||
.get('#list li').eq(0).as('firstLi').then(($li) => {
|
||||
return $li.remove()
|
||||
})
|
||||
.get('@firstLi').then(() => {
|
||||
expect(cy.queue.names()).to.deep.eq(
|
||||
existingNames.concat(
|
||||
['get', 'noop', 'get', 'eq', 'as', 'then', 'get', 'get', 'eq', 'then'],
|
||||
),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('resets the chainerId allow subjects to be carried on', () => {
|
||||
cy.get('#dom').find('#button').as('button').then(($button) => {
|
||||
$button.remove()
|
||||
|
||||
cy.$$('#dom').append($('<button />', { id: 'button' }))
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
// when cy is a separate chainer there *was* a bug
|
||||
// that cause the subject to null because of different
|
||||
// chainer id's
|
||||
cy.get('@button').then(($button) => {
|
||||
expect($button).to.have.id('button')
|
||||
})
|
||||
})
|
||||
|
||||
it('skips commands which did not change, and starts at the first valid subject or parent command', () => {
|
||||
const existingNames = cy.queue.names()
|
||||
|
||||
cy.$$('#list li').click(function () {
|
||||
const ul = $(this).parent()
|
||||
const lis = ul.children().clone()
|
||||
|
||||
// this simulates a re-render
|
||||
ul.children().remove()
|
||||
ul.append(lis)
|
||||
|
||||
return lis.first().remove()
|
||||
})
|
||||
|
||||
cy
|
||||
.get('#list li')
|
||||
.then(($lis) => {
|
||||
return $lis
|
||||
})
|
||||
.as('items')
|
||||
.first()
|
||||
.click()
|
||||
.as('firstItem')
|
||||
.then(() => {
|
||||
expect(cy.queue.names()).to.deep.eq(
|
||||
existingNames.concat(
|
||||
['get', 'then', 'as', 'first', 'click', 'as', 'then', 'get', 'should', 'then', 'get', 'should', 'then'],
|
||||
),
|
||||
)
|
||||
})
|
||||
.get('@items')
|
||||
.should('have.length', 2)
|
||||
.then(() => {
|
||||
expect(cy.queue.names()).to.deep.eq(
|
||||
existingNames.concat(
|
||||
['get', 'then', 'as', 'first', 'click', 'as', 'then', 'get', 'get', 'should', 'then', 'get', 'should', 'then'],
|
||||
),
|
||||
)
|
||||
})
|
||||
.get('@firstItem')
|
||||
.should('contain', 'li 1')
|
||||
.then(() => {
|
||||
expect(cy.queue.names()).to.deep.eq(
|
||||
existingNames.concat(
|
||||
['get', 'then', 'as', 'first', 'click', 'as', 'then', 'get', 'get', 'should', 'then', 'get', 'get', 'first', 'should', 'then'],
|
||||
),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('inserts assertions', (done) => {
|
||||
const existingNames = cy.queue.names()
|
||||
|
||||
cy
|
||||
.get('#checkboxes input')
|
||||
.eq(0)
|
||||
.should('be.checked', 'cockatoo')
|
||||
.as('firstItem')
|
||||
.then(($input) => {
|
||||
return $input.remove()
|
||||
})
|
||||
.get('@firstItem')
|
||||
.then(() => {
|
||||
expect(cy.queue.names()).to.deep.eq(
|
||||
existingNames.concat(
|
||||
['get', 'eq', 'should', 'as', 'then', 'get', 'get', 'eq', 'should', 'then'],
|
||||
),
|
||||
)
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
// Even though the list items have been removed from the DOM, 'then' can't be retried
|
||||
// so we just have the primitive value "3" as our subject.
|
||||
cy.get('@itemCount').should('eq', 3)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -616,5 +494,14 @@ describe('src/cy/commands/aliasing', () => {
|
||||
.get('@lastDiv')
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: Re-enable as part of https://github.com/cypress-io/cypress/issues/23902
|
||||
it.skip('maintains .within() context while reading aliases', () => {
|
||||
cy.get('#specific-contains').within(() => {
|
||||
cy.get('span').as('spanWithin').should('have.length', 1)
|
||||
})
|
||||
|
||||
cy.get('@spanWithin').should('have.length', 1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,332 +0,0 @@
|
||||
const { assertLogLength } = require('../../support/utils')
|
||||
const { _, $ } = Cypress
|
||||
|
||||
describe('src/cy/commands/angular', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/angular.html')
|
||||
})
|
||||
|
||||
describe('#ng', () => {
|
||||
context('find by binding', () => {
|
||||
it('finds color.name binding elements', () => {
|
||||
const spans = cy.$$('.colors span.name')
|
||||
|
||||
cy.ng('binding', 'color.name').then(($spans) => {
|
||||
$spans.each((i, span) => {
|
||||
expect(span).to.eq(spans[i])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('errors', {
|
||||
defaultCommandTimeout: 100,
|
||||
}, () => {
|
||||
beforeEach(function () {
|
||||
this.angular = cy.state('window').angular
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
cy.state('window').angular = this.angular
|
||||
})
|
||||
|
||||
it('throws when cannot find angular', { retries: 2 }, (done) => {
|
||||
delete cy.state('window').angular
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('Angular global (`window.angular`) was not found in your window. You cannot use `cy.ng()` methods without angular.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.ng('binding', 'phone')
|
||||
})
|
||||
|
||||
it('throws when binding cannot be found', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('Could not find element for binding: \'not-found\'.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.ng('binding', 'not-found')
|
||||
})
|
||||
|
||||
it('cancels additional finds when aborted', (done) => {
|
||||
cy.timeout(1000)
|
||||
cy.stub(Cypress.runner, 'stop')
|
||||
|
||||
let retry = _.after(2, () => {
|
||||
Cypress.stop()
|
||||
})
|
||||
|
||||
cy.on('command:retry', retry)
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
done(err)
|
||||
})
|
||||
|
||||
cy.on('stop', () => {
|
||||
retry = cy.spy(cy, 'retry')
|
||||
|
||||
_.delay(() => {
|
||||
expect(retry.callCount).to.eq(0)
|
||||
|
||||
done()
|
||||
}, 100)
|
||||
})
|
||||
|
||||
cy.ng('binding', 'not-found')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('find by repeater', () => {
|
||||
const ngPrefixes = { 'phone in phones': 'ng-', 'phone2 in phones': 'ng_', 'phone3 in phones': 'data-ng-', 'phone4 in phones': 'x-ng-' }
|
||||
|
||||
_.each(ngPrefixes, (prefix, attr) => {
|
||||
it(`finds by ${prefix}repeat`, () => {
|
||||
// make sure we find this element
|
||||
const li = cy.$$(`[${prefix}repeat*='${attr}']`)
|
||||
|
||||
expect(li).to.exist
|
||||
|
||||
// and make sure they are the same DOM element
|
||||
cy.ng('repeater', attr).then(($li) => {
|
||||
expect($li.get(0)).to.eq(li.get(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('favors earlier items in the array when duplicates are found', () => {
|
||||
const li = cy.$$('[ng-repeat*=\'foo in foos\']')
|
||||
|
||||
cy.ng('repeater', 'foo in foos').then(($li) => {
|
||||
expect($li.get(0)).to.eq(li.get(0))
|
||||
})
|
||||
})
|
||||
|
||||
it('waits to find a missing input', () => {
|
||||
const missingLi = $('<li />', { 'data-ng-repeat': 'li in lis' })
|
||||
|
||||
// wait until we're ALMOST about to time out before
|
||||
// appending the missingInput
|
||||
cy.on('command:retry', _.after(2, () => {
|
||||
cy.$$('body').append(missingLi)
|
||||
}))
|
||||
|
||||
cy.ng('repeater', 'li in lis').then(($li) => {
|
||||
expect($li).to.match(missingLi)
|
||||
})
|
||||
})
|
||||
|
||||
describe('errors', {
|
||||
defaultCommandTimeout: 100,
|
||||
}, () => {
|
||||
beforeEach(function () {
|
||||
this.angular = cy.state('window').angular
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
cy.state('window').angular = this.angular
|
||||
})
|
||||
|
||||
it('throws when repeater cannot be found', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('Could not find element for repeater: \'not-found\'. Searched [ng-repeat*=\'not-found\'], [ng_repeat*=\'not-found\'], [data-ng-repeat*=\'not-found\'], [x-ng-repeat*=\'not-found\'].')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.ng('repeater', 'not-found')
|
||||
})
|
||||
|
||||
it('cancels additional finds when aborted', (done) => {
|
||||
cy.timeout(1000)
|
||||
cy.stub(Cypress.runner, 'stop')
|
||||
|
||||
let retry = _.after(2, () => {
|
||||
Cypress.stop()
|
||||
})
|
||||
|
||||
cy.on('command:retry', retry)
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
done(err)
|
||||
})
|
||||
|
||||
cy.on('stop', () => {
|
||||
retry = cy.spy(cy, 'retry')
|
||||
|
||||
_.delay(() => {
|
||||
expect(retry.callCount).to.eq(0)
|
||||
|
||||
done()
|
||||
}, 100)
|
||||
})
|
||||
|
||||
cy.ng('repeater', 'not-found')
|
||||
})
|
||||
|
||||
it('throws when cannot find angular', (done) => {
|
||||
delete cy.state('window').angular
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('Angular global (`window.angular`) was not found in your window. You cannot use `cy.ng()` methods without angular.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.ng('repeater', 'phone in phones')
|
||||
})
|
||||
})
|
||||
|
||||
describe('log', () => {
|
||||
beforeEach(function () {
|
||||
this.logs = []
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
if (attrs.name === 'assert') {
|
||||
this.lastLog = log
|
||||
this.logs.push(log)
|
||||
}
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
it('does not incorrectly merge 2nd assertion into 1st', function () {
|
||||
cy
|
||||
.ng('repeater', 'foo in foos').should('have.length', 2)
|
||||
.url().should('include', ':')
|
||||
.then(() => {
|
||||
assertLogLength(this.logs, 2)
|
||||
expect(this.logs[0].get('state')).to.eq('passed')
|
||||
expect(this.logs[1].get('state')).to.eq('passed')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('find by model', () => {
|
||||
const ngPrefixes = { query: 'ng-', query2: 'ng_', query3: 'data-ng-', query4: 'x-ng-' }
|
||||
|
||||
_.each(ngPrefixes, (prefix, attr) => {
|
||||
it(`finds element by ${prefix}model`, () => {
|
||||
// make sure we find this element
|
||||
const input = cy.$$(`[${prefix}model=${attr}]`)
|
||||
|
||||
expect(input).to.exist
|
||||
|
||||
// and make sure they are the same DOM element
|
||||
cy.ng('model', attr).then(($input) => {
|
||||
expect($input.get(0)).to.eq(input.get(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('favors earlier items in the array when duplicates are found', () => {
|
||||
const input = cy.$$('[ng-model=foo]')
|
||||
|
||||
cy.ng('model', 'foo').then(($input) => {
|
||||
expect($input.get(0)).to.eq(input.get(0))
|
||||
})
|
||||
})
|
||||
|
||||
it('waits to find a missing input', () => {
|
||||
const missingInput = $('<input />', { 'data-ng-model': 'missing-input' })
|
||||
|
||||
// wait until we're ALMOST about to time out before
|
||||
// appending the missingInput
|
||||
cy.on('command:retry', _.after(2, () => {
|
||||
cy.$$('body').append(missingInput)
|
||||
}))
|
||||
|
||||
cy.ng('model', 'missing-input').then(($input) => {
|
||||
expect($input).to.match(missingInput)
|
||||
})
|
||||
})
|
||||
|
||||
it('cancels other retries when one resolves', () => {
|
||||
const retry = cy.spy(cy, 'retry')
|
||||
|
||||
const missingInput = $('<input />', { 'data-ng-model': 'missing-input' })
|
||||
|
||||
cy.on('command:retry', _.after(6, _.once(() => {
|
||||
cy.$$('body').append(missingInput)
|
||||
})))
|
||||
|
||||
// we want to make sure that the ng promises do not continue
|
||||
// to retry after the first one resolves
|
||||
cy.ng('model', 'missing-input')
|
||||
.then(() => {
|
||||
return retry.resetHistory()
|
||||
})
|
||||
.wait(100)
|
||||
.then(() => {
|
||||
expect(retry.callCount).to.eq(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('errors', {
|
||||
defaultCommandTimeout: 100,
|
||||
}, () => {
|
||||
beforeEach(function () {
|
||||
this.angular = cy.state('window').angular
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
cy.state('window').angular = this.angular
|
||||
})
|
||||
|
||||
it('throws when model cannot be found', (done) => {
|
||||
cy.ng('model', 'not-found')
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('Could not find element for model: \'not-found\'. Searched [ng-model=\'not-found\'], [ng_model=\'not-found\'], [data-ng-model=\'not-found\'], [x-ng-model=\'not-found\'].')
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('cancels additional finds when aborted', (done) => {
|
||||
cy.timeout(1000)
|
||||
cy.stub(Cypress.runner, 'stop')
|
||||
|
||||
let retry = _.after(2, () => {
|
||||
Cypress.stop()
|
||||
})
|
||||
|
||||
cy.on('command:retry', retry)
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
done(err)
|
||||
})
|
||||
|
||||
cy.on('stop', () => {
|
||||
retry = cy.spy(cy, 'retry')
|
||||
|
||||
_.delay(() => {
|
||||
expect(retry.callCount).to.eq(0)
|
||||
|
||||
done()
|
||||
}, 100)
|
||||
})
|
||||
|
||||
cy.ng('model', 'not-found')
|
||||
})
|
||||
|
||||
it('throws when cannot find angular', (done) => {
|
||||
delete cy.state('window').angular
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('Angular global (`window.angular`) was not found in your window. You cannot use `cy.ng()` methods without angular.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.ng('model', 'query')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -134,21 +134,6 @@ describe('src/cy/commands/assertions', () => {
|
||||
cy.noop(obj).its('requestJSON').should('have.property', 'teamIds').should('deep.eq', [2])
|
||||
})
|
||||
|
||||
// TODO: make cy.then retry
|
||||
// https://github.com/cypress-io/cypress/issues/627
|
||||
it.skip('outer assertions retry on cy.then', () => {
|
||||
const obj = { foo: 'bar' }
|
||||
|
||||
cy.wrap(obj).then(() => {
|
||||
setTimeout(() => {
|
||||
obj.foo = 'baz'
|
||||
}
|
||||
, 1000)
|
||||
|
||||
return obj
|
||||
}).should('deep.eq', { foo: 'baz' })
|
||||
})
|
||||
|
||||
it('does it retry when wrapped', () => {
|
||||
const obj = { foo: 'bar' }
|
||||
|
||||
@@ -325,6 +310,19 @@ describe('src/cy/commands/assertions', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/22587
|
||||
it('does not allow cypress commands inside the callback', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.eq('`cy.should()` failed because you invoked a command inside the callback. `cy.should()` retries the inner function, which would result in commands being added to the queue multiple times. Use `cy.then()` instead of `cy.should()`, or move any commands outside the callback function.\n\nThe command invoked was:\n\n > `cy.log()`')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.window().should((win) => {
|
||||
cy.log(win)
|
||||
})
|
||||
})
|
||||
|
||||
context('remote jQuery instances', () => {
|
||||
beforeEach(function () {
|
||||
this.remoteWindow = cy.state('window')
|
||||
@@ -448,13 +446,13 @@ describe('src/cy/commands/assertions', () => {
|
||||
|
||||
expect(this.logs[1].get('name')).to.eq('assert')
|
||||
expect(this.logs[1].get('state')).to.eq('failed')
|
||||
expect(this.logs[1].get('error').name).to.eq('CypressError')
|
||||
expect(this.logs[1].get('error').name).to.eq('AssertionError')
|
||||
expect(this.logs[1].get('error')).to.eq(err)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.contains('Nested Find').should('have.length', 2)
|
||||
cy.contains('Nested Find', { timeout: 50 }).should('have.length', 2)
|
||||
})
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/6384
|
||||
@@ -651,7 +649,7 @@ describe('src/cy/commands/assertions', () => {
|
||||
done()
|
||||
})
|
||||
|
||||
cy.get('button:first', { timeout: 100 }).should('have.class', 'does-not-have-class')
|
||||
cy.get('button:first', { timeout: 500 }).should('have.class', 'does-not-have-class')
|
||||
})
|
||||
|
||||
it('has a pending state while retrying for commands with onFail', (done) => {
|
||||
@@ -666,29 +664,7 @@ describe('src/cy/commands/assertions', () => {
|
||||
|
||||
cy.on('fail', () => {})
|
||||
|
||||
cy.readFile('does-not-exist.json').should('exist')
|
||||
})
|
||||
|
||||
it('throws when the subject isnt in the DOM', function (done) {
|
||||
cy.$$('button:first').click(function () {
|
||||
$(this).addClass('foo').remove()
|
||||
})
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
const names = _.invokeMap(this.logs, 'get', 'name')
|
||||
|
||||
// the 'should' is not here because based on
|
||||
// when we check for the element to be detached
|
||||
// it never actually runs the assertion
|
||||
expect(names).to.deep.eq(['get', 'click'])
|
||||
expect(err.message).to.include('`cy.should()` failed because this element is detached')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.get('button:first').click().should('have.class', 'foo').then(() => {
|
||||
done('cy.should was supposed to fail')
|
||||
})
|
||||
cy.readFile('does-not-exist.json', { timeout: 500 }).should('exist')
|
||||
})
|
||||
|
||||
it('throws when the subject eventually isnt in the DOM', function (done) {
|
||||
@@ -705,14 +681,12 @@ describe('src/cy/commands/assertions', () => {
|
||||
|
||||
// should is present here due to the retry
|
||||
expect(names).to.deep.eq(['get', 'click', 'assert'])
|
||||
expect(err.message).to.include('`cy.should()` failed because this element is detached')
|
||||
expect(err.message).to.include('`cy.should()` failed because the page updated')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.get('button:first').click().should('have.class', 'foo').then(() => {
|
||||
done('cy.should was supposed to fail')
|
||||
})
|
||||
cy.get('button:first').click().should('have.class', 'foo')
|
||||
})
|
||||
|
||||
it('throws when should(\'have.length\') isnt a number', function (done) {
|
||||
@@ -838,7 +812,7 @@ describe('src/cy/commands/assertions', () => {
|
||||
return null
|
||||
})
|
||||
|
||||
it('does not output should logs on failures', function (done) {
|
||||
it('does not output should logs on failures', { defaultCommandTimeout: 50 }, function (done) {
|
||||
cy.on('fail', () => {
|
||||
const { length } = this.logs
|
||||
|
||||
@@ -864,23 +838,8 @@ describe('src/cy/commands/assertions', () => {
|
||||
done()
|
||||
})
|
||||
|
||||
cy.get('body').then(() => {
|
||||
expect(cy.currentSubject()).to.match('body')
|
||||
})
|
||||
})
|
||||
|
||||
it('sets type to child when subject matches', (done) => {
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
if (attrs.name === 'assert') {
|
||||
cy.removeAllListeners('log:added')
|
||||
expect(log.get('type')).to.eq('child')
|
||||
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
cy.wrap('foo').then(() => {
|
||||
expect('foo').to.eq('foo')
|
||||
cy.get('body').then((subject) => {
|
||||
expect(subject).to.match('body')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1539,7 +1498,7 @@ describe('src/cy/commands/assertions', () => {
|
||||
|
||||
it('fails not.visible for detached DOM', function (done) {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).include('detached')
|
||||
expect(err.message).include('`cy.should()` failed because the page updated')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -2117,6 +2076,20 @@ describe('src/cy/commands/assertions', () => {
|
||||
|
||||
cy.wrap(undefined).should('have.value', 'somevalue')
|
||||
})
|
||||
|
||||
it('shows subject instead of undefined when a previous traversal errors', (done) => {
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
if (attrs.name === 'assert') {
|
||||
cy.removeAllListeners('log:added')
|
||||
expect(log.get('message')).to.eq('expected **subject** to have class **updated**')
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
cy.get('body')
|
||||
.contains('Does not exist')
|
||||
.should('have.class', 'updated')
|
||||
})
|
||||
})
|
||||
|
||||
context('descendants', () => {
|
||||
|
||||
@@ -213,8 +213,6 @@ describe('command log', () => {
|
||||
cy.root(options)
|
||||
})
|
||||
|
||||
// Ignore cy.route() because it doesn't log to reporter.
|
||||
|
||||
testOptions('screenshot', { capture: 'viewport' }, 0, (options) => {
|
||||
cy.screenshot(options)
|
||||
})
|
||||
@@ -231,8 +229,6 @@ describe('command log', () => {
|
||||
cy.get('#fruits').select('apples', options)
|
||||
})
|
||||
|
||||
// Ignore cy.server() because it doesn't log to reporter.
|
||||
|
||||
// TODO: fix flaky test https://github.com/cypress-io/cypress/issues/23444
|
||||
// testOptions('setCookie', { httpOnly: true }, 0, (options) => {
|
||||
// cy.setCookie('auth_key', '123key', options)
|
||||
|
||||
@@ -88,6 +88,21 @@ describe('src/cy/commands/commands', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('throws when attempting to add an existing query', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.eq('`Cypress.Commands.addQuery()` is used to create new queries, but `get` is an existing Cypress command or query, or is reserved internally by Cypress.\n\n If you want to override an existing command or query, use `Cypress.Commands.overrideQuery()` instead.')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/custom-queries')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
Cypress.Commands.addQuery('get', () => {
|
||||
cy
|
||||
.get('[contenteditable]')
|
||||
.first()
|
||||
})
|
||||
})
|
||||
|
||||
it('allows calling .add with hover / mount', () => {
|
||||
let calls = 0
|
||||
|
||||
@@ -120,6 +135,21 @@ describe('src/cy/commands/commands', () => {
|
||||
.first()
|
||||
})
|
||||
})
|
||||
|
||||
it('throws when attempting to add a query with the same name as an internal function', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.eq('`Cypress.Commands.addQuery()` cannot create a new query named `addCommand` because that name is reserved internally by Cypress.')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/custom-queries')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
Cypress.Commands.addQuery('addCommand', () => {
|
||||
cy
|
||||
.get('[contenteditable]')
|
||||
.first()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('errors', () => {
|
||||
|
||||
@@ -215,6 +215,19 @@ describe('src/cy/commands/connectors', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('completely resets the subject chain when queries are used', () => {
|
||||
cy.wrap('foo')
|
||||
.then(() => cy.get('body'))
|
||||
.then(() => {
|
||||
// We expect the current subject chain to look like [undefined, get()],
|
||||
// inherited from .get() inside the first .then().
|
||||
|
||||
// There was a regression where it would instead look like ['foo', get()],
|
||||
// mixing the subjects from .wrap() and .get().
|
||||
expect(cy.subjectChain()[0]).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
describe('errors', {
|
||||
defaultCommandTimeout: 100,
|
||||
}, () => {
|
||||
@@ -310,7 +323,7 @@ describe('src/cy/commands/connectors', () => {
|
||||
return $div
|
||||
})
|
||||
.then(function () {
|
||||
expect(cy.currentSubject()).not.to.be.instanceof(this.remoteWindow.$)
|
||||
expect(cy.subject()).not.to.be.instanceof(this.remoteWindow.$)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -628,8 +641,8 @@ describe('src/cy/commands/connectors', () => {
|
||||
}
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('Timed out retrying after 100ms: `cy.invoke()` errored because the property: `bar` returned a `string` value instead of a function. `cy.invoke()` can only be used on properties that return callable functions.')
|
||||
expect(err.message).to.include('`cy.invoke()` waited for the specified property `bar` to return a function, but it never did.')
|
||||
expect(err.message).to.include('Timed out retrying after 100ms: `cy.invoke()` errored because the property: `foo.bar` returned a `string` value instead of a function. `cy.invoke()` can only be used on properties that return callable functions.')
|
||||
expect(err.message).to.include('`cy.invoke()` waited for the specified property `foo.bar` to return a function, but it never did.')
|
||||
expect(err.message).to.include('If you want to assert on the property\'s value, then switch to use `cy.its()` and add an assertion such as:')
|
||||
expect(err.message).to.include('`cy.wrap({ foo: \'bar\' }).its(\'foo\').should(\'eq\', \'bar\')`')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/invoke')
|
||||
@@ -642,7 +655,7 @@ describe('src/cy/commands/connectors', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('accepts a options argument', () => {
|
||||
describe('accepts an options argument', () => {
|
||||
it('changes subject to function invocation', () => {
|
||||
cy.noop({ foo () {
|
||||
return 'foo'
|
||||
@@ -729,16 +742,15 @@ describe('src/cy/commands/connectors', () => {
|
||||
|
||||
cy.wrap({ foo () {
|
||||
return 'foo'
|
||||
} }).invoke(() => {
|
||||
return {}
|
||||
})
|
||||
} })
|
||||
.invoke(() => {})
|
||||
})
|
||||
|
||||
it('throws when first parameter is neither of type object nor of type string nor of type number', function (done) {
|
||||
it('throws when we can\'t determine both a valid options and path', function (done) {
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(err.message).to.include('`cy.invoke()` only accepts a string or a number as the functionName argument.')
|
||||
expect(err.message).to.include('`cy.invoke()` only accepts an object as the options argument.')
|
||||
expect(lastLog.get('error').message).to.include(err.message)
|
||||
|
||||
done()
|
||||
@@ -903,30 +915,19 @@ describe('src/cy/commands/connectors', () => {
|
||||
Command: 'invoke',
|
||||
Function: '.bar()',
|
||||
Subject: this.obj,
|
||||
'With Arguments': [],
|
||||
Yielded: 'bar',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('#consoleProps as a function property with args', function () {
|
||||
cy.noop(this.obj).invoke('sum', 1, 2, 3).then(function () {
|
||||
expect(this.lastLog.invoke('consoleProps')).to.deep.eq({
|
||||
Command: 'invoke',
|
||||
Function: '.sum(1, 2, 3)',
|
||||
'With Arguments': [1, 2, 3],
|
||||
Subject: this.obj,
|
||||
Yielded: 6,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('#consoleProps as a function reduced property with args', function () {
|
||||
it('#consoleProps as a deep function property with args', function () {
|
||||
cy.noop(this.obj).invoke('math.sum', 1, 2, 3).then(function () {
|
||||
expect(this.lastLog.invoke('consoleProps')).to.deep.eq({
|
||||
Command: 'invoke',
|
||||
Function: '.math.sum(1, 2, 3)',
|
||||
'With Arguments': [1, 2, 3],
|
||||
Subject: this.obj['math'],
|
||||
Subject: this.obj,
|
||||
Yielded: 6,
|
||||
})
|
||||
})
|
||||
@@ -939,8 +940,9 @@ describe('src/cy/commands/connectors', () => {
|
||||
expect(consoleProps).to.deep.eq({
|
||||
Command: 'invoke',
|
||||
Function: '.hide()',
|
||||
Subject: $btn.get(0),
|
||||
Yielded: $btn.get(0),
|
||||
Subject: $btn,
|
||||
'With Arguments': [],
|
||||
Yielded: $btn,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1058,7 +1060,7 @@ describe('src/cy/commands/connectors', () => {
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(err.message).to.include('Timed out retrying after 100ms: `cy.invoke()` errored because the property: `baz` does not exist on your subject.')
|
||||
expect(err.message).to.include('Timed out retrying after 100ms: `cy.invoke()` errored because the property: `foo.bar.baz.fizz` does not exist on your subject.')
|
||||
expect(lastLog.get('error').message).to.include(err.message)
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/invoke')
|
||||
|
||||
@@ -1081,10 +1083,8 @@ describe('src/cy/commands/connectors', () => {
|
||||
this.remoteWindow = cy.state('window')
|
||||
})
|
||||
|
||||
it('proxies to #invokeFn', () => {
|
||||
const fn = () => {
|
||||
return 'bar'
|
||||
}
|
||||
it('returns function properties', () => {
|
||||
const fn = () => 'bar'
|
||||
|
||||
cy.wrap({ foo: fn }).its('foo').should('eq', fn)
|
||||
})
|
||||
@@ -1244,7 +1244,8 @@ describe('src/cy/commands/connectors', () => {
|
||||
cy.stub()
|
||||
.onCall(0).returns(undefined)
|
||||
.onCall(1).returns(undefined)
|
||||
.onCall(2).returns(true),
|
||||
.onCall(2).returns(undefined)
|
||||
.onCall(3).returns(true),
|
||||
)
|
||||
|
||||
cy.wrap(obj).its('foo').should('eq', true)
|
||||
@@ -1268,15 +1269,6 @@ describe('src/cy/commands/connectors', () => {
|
||||
cy.wrap({}).its('foo').should('not.exist')
|
||||
cy.wrap({}).its('foo').should('be.undefined')
|
||||
cy.wrap({}).its('foo').should('not.be.ok')
|
||||
|
||||
// TODO: should these really pass here?
|
||||
// isn't this the same situation as: cy.should('not.have.class', '...')
|
||||
//
|
||||
// when we use the 'eq' and 'not.eq' chainer aren't we effectively
|
||||
// saying that it must *have* a value as opposed to the property not
|
||||
// existing at all?
|
||||
//
|
||||
// does a tree falling in the forest really make a sound?
|
||||
cy.wrap({}).its('foo').should('eq', undefined)
|
||||
cy.wrap({}).its('foo').should('not.eq', 'bar')
|
||||
})
|
||||
@@ -1309,71 +1301,6 @@ describe('src/cy/commands/connectors', () => {
|
||||
cy.wrap(obj).its('foo').should('eq', undefined)
|
||||
})
|
||||
|
||||
describe('accepts a options argument and works as without options argument', () => {
|
||||
it('proxies to #invokeFn', () => {
|
||||
const fn = () => {
|
||||
return 'bar'
|
||||
}
|
||||
|
||||
cy.wrap({ foo: fn }).its('foo', { log: false }).should('eq', fn)
|
||||
})
|
||||
|
||||
it('does not invoke a function and uses as a property', () => {
|
||||
const fn = () => {
|
||||
return 'fn'
|
||||
}
|
||||
|
||||
fn.bar = 'bar'
|
||||
|
||||
cy.wrap(fn).its('bar', { log: false }).should('eq', 'bar')
|
||||
})
|
||||
|
||||
it('works with numerical indexes', () => {
|
||||
cy.wrap(['foo', 'bar']).its(1, {}).should('eq', 'bar')
|
||||
})
|
||||
|
||||
describe('.log', () => {
|
||||
beforeEach(function () {
|
||||
this.obj = {
|
||||
foo: 'foo bar baz',
|
||||
num: 123,
|
||||
}
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
this.lastLog = log
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
it('logs obj as a property', function () {
|
||||
cy.noop(this.obj).its('foo', { log: true }).then(function () {
|
||||
const obj = {
|
||||
name: 'its',
|
||||
message: '.foo',
|
||||
}
|
||||
|
||||
const { lastLog } = this
|
||||
|
||||
_.each(obj, (value, key) => {
|
||||
expect(lastLog.get(key)).to.deep.eq(value)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('#consoleProps as a regular property', function () {
|
||||
cy.noop(this.obj).its('num', { log: true }).then(function () {
|
||||
expect(this.lastLog.invoke('consoleProps')).to.deep.eq({
|
||||
Command: 'its',
|
||||
Property: '.num',
|
||||
Subject: this.obj,
|
||||
Yielded: 123,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('.log', () => {
|
||||
beforeEach(function () {
|
||||
this.obj = {
|
||||
@@ -1570,7 +1497,7 @@ describe('src/cy/commands/connectors', () => {
|
||||
|
||||
it('throws the traversalErr as precedence when property does not exist even if the additional assertions fail', function (done) {
|
||||
cy.once('fail', (err) => {
|
||||
const { itsLog, lastLog } = this
|
||||
const { itsLog } = this
|
||||
|
||||
expect(err.message).to.include('Timed out retrying after 100ms: `cy.its()` errored because the property: `b` does not exist on your subject.')
|
||||
expect(err.message).to.include('`cy.its()` waited for the specified property `b` to exist, but it never did.')
|
||||
@@ -1578,13 +1505,8 @@ describe('src/cy/commands/connectors', () => {
|
||||
expect(err.message).to.include('`cy.wrap({ foo: \'bar\' }).its(\'quux\').should(\'not.exist\')`')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/its')
|
||||
|
||||
expect(itsLog.get('state')).to.eq('passed')
|
||||
expect(itsLog.get('error')).to.be.undefined
|
||||
|
||||
expect(lastLog.get('name')).to.eq('assert')
|
||||
expect(lastLog.get('state')).to.eq('failed')
|
||||
expect(lastLog.get('message')).to.contain('to be true')
|
||||
expect(lastLog.get('error').message).to.include(err.message)
|
||||
expect(itsLog.get('state')).to.eq('failed')
|
||||
expect(itsLog.get('error').message).to.include(err.message)
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -1594,7 +1516,7 @@ describe('src/cy/commands/connectors', () => {
|
||||
|
||||
it('throws the traversalErr as precedence when property value is undefined even if the additional assertions fail', function (done) {
|
||||
cy.once('fail', (err) => {
|
||||
const { itsLog, lastLog } = this
|
||||
const { itsLog } = this
|
||||
|
||||
expect(err.message).to.include('Timed out retrying after 100ms: `cy.its()` errored because the property: `a` returned a `undefined` value.')
|
||||
expect(err.message).to.include('`cy.its()` waited for the specified property `a` to become accessible, but it never did.')
|
||||
@@ -1602,13 +1524,8 @@ describe('src/cy/commands/connectors', () => {
|
||||
expect(err.message).to.include('`cy.wrap({ foo: undefined }).its(\'foo\').should(\'be.undefined\')`')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/its')
|
||||
|
||||
expect(itsLog.get('state')).to.eq('passed')
|
||||
expect(itsLog.get('error')).to.be.undefined
|
||||
|
||||
expect(lastLog.get('name')).to.eq('assert')
|
||||
expect(lastLog.get('state')).to.eq('failed')
|
||||
expect(lastLog.get('message')).to.contain('to be true')
|
||||
expect(lastLog.get('error').message).to.include(err.message)
|
||||
expect(itsLog.get('state')).to.eq('failed')
|
||||
expect(itsLog.get('error').message).to.include(err.message)
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -1623,9 +1540,7 @@ describe('src/cy/commands/connectors', () => {
|
||||
},
|
||||
}
|
||||
|
||||
obj.foo.bar.baz = () => {
|
||||
return 'baz'
|
||||
}
|
||||
obj.foo.bar.baz = () => 'baz'
|
||||
|
||||
cy.once('fail', (err) => {
|
||||
const { itsLog, lastLog } = this
|
||||
@@ -1670,7 +1585,7 @@ describe('src/cy/commands/connectors', () => {
|
||||
cy.on('fail', (err) => {
|
||||
const { itsLog } = this
|
||||
|
||||
expect(err.message).to.include('Timed out retrying after 100ms: `cy.its()` errored because the property: `baz` does not exist on your subject.')
|
||||
expect(err.message).to.include('Timed out retrying after 100ms: `cy.its()` errored because the property: `foo.bar.baz.fizz` does not exist on your subject.')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/its')
|
||||
expect(itsLog.get('error').message).to.include(err.message)
|
||||
|
||||
@@ -1686,19 +1601,19 @@ describe('src/cy/commands/connectors', () => {
|
||||
cy.wrap(obj).its('foo.bar.baz.fizz')
|
||||
});
|
||||
|
||||
[null, undefined].forEach((val) => {
|
||||
[null/*, undefined*/].forEach((val) => {
|
||||
it(`throws on traversed '${val}' subject`, (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out retrying after 100ms: \`cy.its()\` errored because the property: \`a\` returned a \`${val}\` value. The property: \`b\` does not exist on a \`${val}\` value.`)
|
||||
expect(err.message).to.include('`cy.its()` waited for the specified property `b` to become accessible, but it never did.')
|
||||
expect(err.message).to.include('If you do not expect the property `b` to exist, then add an assertion such as:')
|
||||
expect(err.message).to.include(`\`cy.wrap({ foo: ${val} }).its('foo.baz').should('not.exist')\``)
|
||||
expect(err.message).to.include(`Timed out retrying after 100ms: \`cy.its()\` errored because the property: \`a.b\` returned a \`${val}\` value.`)
|
||||
expect(err.message).to.include('`cy.its()` waited for the specified property `a.b` to become accessible, but it never did.')
|
||||
expect(err.message).to.include(`If you expect the property \`a.b\` to be \`${val}\`, then add an assertion such as:`)
|
||||
expect(err.message).to.include(`\`cy.wrap({ foo: ${val} }).its('foo').should('be.null')\``)
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/its')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.wrap({ a: val }).its('a.b.c')
|
||||
cy.wrap({ a: { b: val } }).its('a.b')
|
||||
})
|
||||
|
||||
it(`throws on initial '${val}' subject`, (done) => {
|
||||
|
||||
@@ -5,134 +5,227 @@ const { Promise } = Cypress
|
||||
const isWebkit = Cypress.isBrowser('webkit')
|
||||
|
||||
describe('src/cy/commands/cookies - no stub', () => {
|
||||
context('#getCookies', () => {
|
||||
// this can be removed along with the experimental flag since once the flag
|
||||
// removed, clearing cookies for all domains will be done by default
|
||||
beforeEach(() => {
|
||||
if (!Cypress.config('experimentalSessionAndOrigin')) {
|
||||
cy.clearCookies({ domain: null })
|
||||
}
|
||||
})
|
||||
const setCookies = () => {
|
||||
cy.log('set cookies')
|
||||
cy.setCookie('key1', 'value1', { domain: 'www.foobar.com', log: false })
|
||||
cy.setCookie('key2', 'value2', { domain: 'foobar.com', log: false })
|
||||
cy.setCookie('key3', 'value3', { domain: 'www.barbaz.com', log: false })
|
||||
cy.setCookie('key4', 'value4', { domain: '.www.barbaz.com', log: false })
|
||||
cy.setCookie('key5', 'value5', { domain: 'barbaz.com', log: false })
|
||||
cy.setCookie('key6', 'value6', { domain: '.barbaz.com', log: false })
|
||||
cy.setCookie('key7', 'value7', { domain: 'www2.barbaz.com', log: false })
|
||||
cy.setCookie('key8', 'value8', { domain: 'www2.foobar.com', log: false })
|
||||
}
|
||||
|
||||
it('returns cookies from the domain matching the AUT by default', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar', { domain: 'www.foobar.com' })
|
||||
cy.setCookie('baz', 'qux', { domain: 'foobar.com' })
|
||||
cy.setCookie('foo', 'bar') // defaults to (super)domain: barbaz.com
|
||||
cy.setCookie('qux', 'quuz', { domain: 'www.barbaz.com' })
|
||||
context('#getCookies', () => {
|
||||
it('returns cookies from only the bare domain matching the AUT by default when AUT is an apex domain', () => {
|
||||
cy.visit('http://barbaz.com:3500/fixtures/generic.html')
|
||||
setCookies()
|
||||
|
||||
cy.getCookies().then((cookies) => {
|
||||
expect(cookies).to.have.length(2)
|
||||
// both the barbaz.com and www.barbaz.com cookies are yielded
|
||||
expect(cookies[0].domain).to.match(/\.?barbaz\.com/)
|
||||
expect(cookies[1].domain).to.match(/\.?www\.barbaz\.com/)
|
||||
|
||||
const sortedCookies = Cypress._.sortBy(cookies, 'name')
|
||||
|
||||
expect(sortedCookies[0].name).to.equal('key5')
|
||||
expect(sortedCookies[0].domain).to.match(/\.?barbaz\.com/)
|
||||
expect(sortedCookies[1].name).to.equal('key6')
|
||||
expect(sortedCookies[1].domain).to.match(/\.?barbaz\.com/)
|
||||
})
|
||||
|
||||
if (isWebkit || !Cypress.config('experimentalSessionAndOrigin')) return
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://foobar.com:3500/fixtures/generic.html')
|
||||
|
||||
cy.getCookies().then((cookies) => {
|
||||
expect(cookies).to.have.length(1)
|
||||
expect(cookies[0].name).to.equal('key2')
|
||||
expect(cookies[0].domain).to.match(/\.?foobar\.com/)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('returns cookies from the subdomain and bare domain matching the AUT by default when AUT is a subdomain', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
setCookies()
|
||||
cy.getCookies().then((cookies) => {
|
||||
expect(cookies).to.have.length(4)
|
||||
|
||||
const sortedCookies = Cypress._.sortBy(cookies, 'name')
|
||||
|
||||
expect(sortedCookies[0].name).to.equal('key3')
|
||||
expect(sortedCookies[0].domain).to.match(/\.?www\.barbaz\.com/)
|
||||
expect(sortedCookies[1].name).to.equal('key4')
|
||||
expect(sortedCookies[1].domain).to.match(/\.?www\.barbaz\.com/)
|
||||
expect(sortedCookies[2].name).to.equal('key5')
|
||||
expect(sortedCookies[2].domain).to.match(/\.?barbaz\.com/)
|
||||
expect(sortedCookies[3].name).to.equal('key6')
|
||||
expect(sortedCookies[3].domain).to.match(/\.?barbaz\.com/)
|
||||
})
|
||||
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
|
||||
cy.getCookies().then((cookies) => {
|
||||
expect(cookies[0].domain).to.match(/\.?www\.foobar\.com/)
|
||||
expect(cookies[1].domain).to.match(/\.?foobar\.com/)
|
||||
const sortedCookies = Cypress._.sortBy(cookies, 'name')
|
||||
|
||||
expect(sortedCookies).to.have.length(2)
|
||||
expect(sortedCookies[0].name).to.equal('key1')
|
||||
expect(sortedCookies[0].domain).to.match(/\.?www\.foobar\.com/)
|
||||
expect(sortedCookies[1].name).to.equal('key2')
|
||||
expect(sortedCookies[1].domain).to.match(/\.?foobar\.com/)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('returns cookies for the specified domain', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar', { domain: 'www.foobar.com' })
|
||||
cy.setCookie('foo', 'bar') // defaults to (super)domain: barbaz.com
|
||||
cy.setCookie('qux', 'quuz', { domain: 'www.barbaz.com' })
|
||||
cy.visit('http://barbaz.com:3500/fixtures/generic.html')
|
||||
setCookies()
|
||||
|
||||
cy.getCookies({ domain: 'www.foobar.com' }).then((cookies) => {
|
||||
expect(cookies).to.have.length(2)
|
||||
expect(cookies[0].name).to.equal('key1')
|
||||
expect(cookies[0].domain).to.match(/\.?www\.foobar\.com/)
|
||||
expect(cookies[1].name).to.equal('key2')
|
||||
expect(cookies[1].domain).to.match(/\.?foobar\.com/)
|
||||
})
|
||||
|
||||
if (isWebkit || !Cypress.config('experimentalSessionAndOrigin')) return
|
||||
cy.getCookies({ domain: 'barbaz.com' }).then((cookies) => {
|
||||
expect(cookies).to.have.length(2)
|
||||
|
||||
const sortedCookies = Cypress._.sortBy(cookies, 'name')
|
||||
|
||||
expect(sortedCookies[0].name).to.equal('key5')
|
||||
expect(sortedCookies[0].domain).to.match(/\.?barbaz\.com/)
|
||||
expect(sortedCookies[1].name).to.equal('key6')
|
||||
expect(sortedCookies[1].domain).to.match(/\.?barbaz\.com/)
|
||||
})
|
||||
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
|
||||
cy.getCookies({ domain: 'barbaz.com' }).then((cookies) => {
|
||||
expect(cookies).to.have.length(2)
|
||||
// both the barbaz.com and www.barbaz.com cookies are yielded
|
||||
expect(cookies[0].domain).to.match(/\.?barbaz\.com/)
|
||||
expect(cookies[1].domain).to.match(/\.?www\.barbaz\.com/)
|
||||
cy.getCookies({ domain: 'www.barbaz.com' }).then((cookies) => {
|
||||
expect(cookies).to.have.length(4)
|
||||
|
||||
const sortedCookies = Cypress._.sortBy(cookies, 'name')
|
||||
|
||||
expect(sortedCookies[0].name).to.equal('key3')
|
||||
expect(sortedCookies[0].domain).to.match(/\.?www\.barbaz\.com/)
|
||||
expect(sortedCookies[1].name).to.equal('key4')
|
||||
expect(sortedCookies[1].domain).to.match(/\.?www\.barbaz\.com/)
|
||||
expect(sortedCookies[2].name).to.equal('key5')
|
||||
expect(sortedCookies[2].domain).to.match(/\.?barbaz\.com/)
|
||||
expect(sortedCookies[3].name).to.equal('key6')
|
||||
expect(sortedCookies[3].domain).to.match(/\.?barbaz\.com/)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('returns cookies for all domains when domain is null', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar', { domain: 'www.foobar.com' })
|
||||
cy.setCookie('foo', 'bar')
|
||||
|
||||
cy.getCookies({ domain: null }).should('have.length', 2)
|
||||
|
||||
if (isWebkit || !Cypress.config('experimentalSessionAndOrigin')) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
|
||||
cy.getCookies({ domain: null }).should('have.length', 2)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#getCookie', () => {
|
||||
it('returns the cookie from the domain matching the AUT by default', () => {
|
||||
const setCookies = () => {
|
||||
cy.log('set cookies')
|
||||
cy.setCookie('key', 'www.barbaz.com value', { domain: 'www.barbaz.com', log: false })
|
||||
cy.setCookie('key', 'barbaz.com value', { domain: 'barbaz.com', log: false })
|
||||
cy.setCookie('key', 'www.foobar.com value', { domain: 'www.foobar.com', log: false })
|
||||
cy.setCookie('key', 'foobar.com value', { domain: 'foobar.com', log: false })
|
||||
}
|
||||
|
||||
it('returns the cookie from the domain matching the AUT by default when AUT is an apex domain', () => {
|
||||
cy.visit('http://barbaz.com:3500/fixtures/generic.html')
|
||||
setCookies()
|
||||
|
||||
cy.getCookie('key').then((cookie) => {
|
||||
expect(cookie.value).to.equal('barbaz.com value')
|
||||
expect(cookie.domain).to.match(/\.?barbaz\.com/)
|
||||
})
|
||||
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://foobar.com:3500/fixtures/generic.html')
|
||||
|
||||
cy.getCookie('key').then((cookie) => {
|
||||
expect(cookie.value).to.equal('foobar.com value')
|
||||
expect(cookie.domain).to.match(/\.?foobar\.com/)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('can return the cookie from the subdomain matching the AUT by default when AUT is a subdomain', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar', { domain: 'www.foobar.com' })
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.setCookie('key', 'www.barbaz.com value', { domain: 'www.barbaz.com', log: false })
|
||||
|
||||
cy.getCookie('foo').its('domain').should('match', /\.?barbaz\.com/)
|
||||
cy.getCookie('key').then((cookie) => {
|
||||
expect(cookie.value).to.equal('www.barbaz.com value')
|
||||
expect(cookie.domain).to.match(/\.?www\.barbaz\.com/)
|
||||
})
|
||||
|
||||
if (isWebkit || !Cypress.config('experimentalSessionAndOrigin')) return
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('key', 'www.foobar.com value', { domain: 'www.foobar.com', log: false })
|
||||
|
||||
cy.getCookie('foo').its('domain').should('match', /\.?www\.foobar\.com/)
|
||||
cy.getCookie('key').then((cookie) => {
|
||||
expect(cookie.value).to.equal('www.foobar.com value')
|
||||
expect(cookie.domain).to.match(/\.?www\.foobar\.com/)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('can return the cookie from the bare domain matching the AUT by default when AUT is a subdomain', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('key', 'barbaz.com value', { domain: 'barbaz.com', log: false })
|
||||
|
||||
cy.getCookie('key').then((cookie) => {
|
||||
expect(cookie.value).to.equal('barbaz.com value')
|
||||
expect(cookie.domain).to.match(/\.?barbaz\.com/)
|
||||
})
|
||||
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('key', 'foobar.com value', { domain: 'foobar.com', log: false })
|
||||
|
||||
cy.getCookie('key').then((cookie) => {
|
||||
expect(cookie.value).to.equal('foobar.com value')
|
||||
expect(cookie.domain).to.match(/\.?foobar\.com/)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('returns the cookie from the specified domain', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar', { domain: 'www.foobar.com' })
|
||||
cy.setCookie('foo', 'bar')
|
||||
setCookies()
|
||||
|
||||
cy.getCookie('foo', { domain: 'www.foobar.com' })
|
||||
.its('domain').should('match', /\.?www\.foobar\.com/)
|
||||
|
||||
if (isWebkit || !Cypress.config('experimentalSessionAndOrigin')) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
|
||||
cy.getCookie('foo', { domain: 'barbaz.com' })
|
||||
.its('domain').should('match', /\.?barbaz\.com/)
|
||||
cy.getCookie('key', { domain: 'www.foobar.com' }).then((cookie) => {
|
||||
expect(cookie.value).to.equal('www.foobar.com value')
|
||||
expect(cookie.domain).to.match(/\.?www\.foobar\.com/)
|
||||
})
|
||||
})
|
||||
|
||||
it('returns cookie for any domain when domain is null', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar', { domain: 'www.foobar.com' })
|
||||
|
||||
cy.getCookie('foo', { domain: null })
|
||||
.its('domain').should('match', /\.?www\.foobar\.com/)
|
||||
|
||||
if (isWebkit || !Cypress.config('experimentalSessionAndOrigin')) return
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
|
||||
cy.clearCookie('foo')
|
||||
cy.setCookie('foo', 'bar', { domain: 'barbaz.com' })
|
||||
|
||||
cy.getCookie('foo', { domain: null })
|
||||
.its('domain').should('match', /\.?barbaz\.com/)
|
||||
cy.getCookie('key', { domain: 'www.barbaz.com' }).then((cookie) => {
|
||||
expect(cookie.value).to.equal('www.barbaz.com value')
|
||||
expect(cookie.domain).to.match(/\.?www\.barbaz\.com/)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -140,28 +233,37 @@ describe('src/cy/commands/cookies - no stub', () => {
|
||||
context('#setCookie', () => {
|
||||
it('sets the cookie on the domain matching the AUT by default', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.setCookie('key', 'value')
|
||||
|
||||
cy.getCookie('foo').its('domain').should('match', /\.?barbaz\.com/)
|
||||
cy.getCookie('key').its('domain').should('match', /\.?www\.barbaz\.com/)
|
||||
// domain is exact
|
||||
cy.getCookie('key', { domain: 'barbaz.com' }).should('be.null')
|
||||
|
||||
if (isWebkit || !Cypress.config('experimentalSessionAndOrigin')) return
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.setCookie('key', 'value')
|
||||
|
||||
cy.getCookie('foo').its('domain').should('equal', '.foobar.com')
|
||||
cy.getCookie('key').its('domain').should('match', /\.?www\.foobar\.com/)
|
||||
// domain is exact
|
||||
cy.getCookie('key', { domain: 'foobar.com' }).should('be.null')
|
||||
})
|
||||
})
|
||||
|
||||
it('set the cookie on the specified domain', () => {
|
||||
it('sets the cookie on the specified domain', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar', { domain: 'www.foobar.com' })
|
||||
|
||||
cy.getCookie('foo', { domain: 'www.foobar.com' })
|
||||
.its('domain').should('match', /\.?www\.foobar\.com/)
|
||||
|
||||
if (isWebkit || !Cypress.config('experimentalSessionAndOrigin')) return
|
||||
// domain is exact
|
||||
cy.getCookie('key', { domain: 'foobar.com' }).should('be.null')
|
||||
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
@@ -169,170 +271,219 @@ describe('src/cy/commands/cookies - no stub', () => {
|
||||
|
||||
cy.getCookie('foo', { domain: 'barbaz.com' })
|
||||
.its('domain').should('match', /\.?barbaz\.com/)
|
||||
|
||||
// domain is exact
|
||||
cy.getCookie('key', { domain: 'barbaz.com' }).should('be.null')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#clearCookies', () => {
|
||||
it('clears all cookies', () => {
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.getCookies().should('have.length', 1)
|
||||
cy.clearCookies()
|
||||
cy.getCookies().should('have.length', 0)
|
||||
})
|
||||
|
||||
it('clears the cookies on the domain matching the AUT by default', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.setCookie('baz', 'qux')
|
||||
cy.setCookie('foo', 'bar', { domain: 'www.foobar.com' })
|
||||
|
||||
it('clears cookies from only the bare domain matching the AUT by default when AUT is an apex domain', () => {
|
||||
cy.visit('http://barbaz.com:3500/fixtures/generic.html')
|
||||
setCookies()
|
||||
cy.clearCookies()
|
||||
|
||||
cy.getCookie('foo').should('be.null')
|
||||
cy.getCookie('baz').should('be.null')
|
||||
cy.getCookie('foo', { domain: 'www.foobar.com' }).should('exist')
|
||||
cy.getCookie('key1', { domain: 'www.foobar.com' }).should('exist')
|
||||
cy.getCookie('key2', { domain: 'foobar.com' }).should('exist')
|
||||
cy.getCookie('key3', { domain: 'www.barbaz.com' }).should('exist')
|
||||
cy.getCookie('key4', { domain: 'www.barbaz.com' }).should('exist')
|
||||
cy.getCookie('key5').should('not.exist')
|
||||
cy.getCookie('key6').should('not.exist')
|
||||
|
||||
if (isWebkit || !Cypress.config('experimentalSessionAndOrigin')) return
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('baz', 'qux')
|
||||
cy.setCookie('foo', 'bar', { domain: 'barbaz.com' })
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://foobar.com:3500/fixtures/generic.html')
|
||||
// put back cookies removed above
|
||||
cy.setCookie('key5', 'value5', { domain: 'barbaz.com' })
|
||||
cy.setCookie('key6', 'value6', { domain: 'barbaz.com' })
|
||||
|
||||
cy.clearCookies()
|
||||
|
||||
cy.getCookie('foo').should('be.null')
|
||||
cy.getCookie('baz').should('be.null')
|
||||
cy.getCookie('foo', { domain: 'barbaz.com' }).should('exist')
|
||||
cy.getCookie('key1', { domain: 'www.foobar.com' }).should('exist')
|
||||
cy.getCookie('key2').should('be.null')
|
||||
cy.getCookie('key3', { domain: 'www.barbaz.com' }).should('exist')
|
||||
cy.getCookie('key4', { domain: 'www.barbaz.com' }).should('exist')
|
||||
cy.getCookie('key5', { domain: 'barbaz.com' }).should('exist')
|
||||
cy.getCookie('key6', { domain: 'barbaz.com' }).should('exist')
|
||||
})
|
||||
})
|
||||
|
||||
it('clears cookies from the subdomain and bare domain matching the AUT by default when AUT is a subdomain', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
setCookies()
|
||||
cy.clearCookies()
|
||||
|
||||
cy.getCookie('key1', { domain: 'www.foobar.com' }).should('exist')
|
||||
cy.getCookie('key2', { domain: 'foobar.com' }).should('exist')
|
||||
cy.getCookie('key3').should('not.exist')
|
||||
cy.getCookie('key4').should('not.exist')
|
||||
cy.getCookie('key5').should('not.exist')
|
||||
cy.getCookie('key6').should('not.exist')
|
||||
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
// put back cookies removed above
|
||||
cy.setCookie('key3', 'value3', { domain: 'www.barbaz.com' })
|
||||
cy.setCookie('key4', 'value4', { domain: 'www.barbaz.com' })
|
||||
cy.setCookie('key5', 'value5', { domain: 'barbaz.com' })
|
||||
cy.setCookie('key6', 'value6', { domain: 'barbaz.com' })
|
||||
|
||||
cy.clearCookies()
|
||||
|
||||
cy.getCookie('key1').should('be.null')
|
||||
cy.getCookie('key2').should('be.null')
|
||||
cy.getCookie('key3', { domain: 'www.barbaz.com' }).should('exist')
|
||||
cy.getCookie('key4', { domain: 'www.barbaz.com' }).should('exist')
|
||||
cy.getCookie('key5', { domain: 'barbaz.com' }).should('exist')
|
||||
cy.getCookie('key6', { domain: 'barbaz.com' }).should('exist')
|
||||
})
|
||||
})
|
||||
|
||||
it('clears the cookies on the specified domain', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.setCookie('foo', 'bar', { domain: 'www.foobar.com' })
|
||||
cy.setCookie('baz', 'qux', { domain: 'www.foobar.com' })
|
||||
|
||||
setCookies()
|
||||
cy.clearCookies({ domain: 'www.foobar.com' })
|
||||
|
||||
cy.getCookie('foo', { domain: 'www.foobar.com' }).should('be.null')
|
||||
cy.getCookie('baz', { domain: 'www.foobar.com' }).should('be.null')
|
||||
cy.getCookie('foo').should('exist')
|
||||
cy.getCookie('key1').should('be.null')
|
||||
cy.getCookie('key2').should('be.null')
|
||||
cy.getCookie('key3', { domain: 'www.barbaz.com' }).should('exist')
|
||||
cy.getCookie('key4', { domain: 'www.barbaz.com' }).should('exist')
|
||||
cy.getCookie('key5', { domain: 'barbaz.com' }).should('exist')
|
||||
cy.getCookie('key6', { domain: 'barbaz.com' }).should('exist')
|
||||
|
||||
if (isWebkit || !Cypress.config('experimentalSessionAndOrigin')) return
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.setCookie('baz', 'qux', { domain: 'barbaz.com' })
|
||||
// put back cookies removed above
|
||||
cy.setCookie('key1', 'value1')
|
||||
cy.setCookie('key2', 'value2', { domain: 'foobar.com' })
|
||||
|
||||
cy.clearCookies({ domain: 'barbaz.com' })
|
||||
cy.clearCookies({ domain: 'www.barbaz.com' })
|
||||
|
||||
cy.getCookie('foo', { domain: 'barbaz.com' }).should('be.null')
|
||||
cy.getCookie('baz', { domain: 'barbaz.com' }).should('be.null')
|
||||
cy.getCookie('foo').should('exist')
|
||||
})
|
||||
})
|
||||
|
||||
it('clears cookies for all domains when domain is null', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.setCookie('foo', 'bar', { domain: 'www.foobar.com' })
|
||||
|
||||
cy.clearCookies({ domain: null })
|
||||
cy.getCookies().should('have.length', 0)
|
||||
|
||||
if (isWebkit || !Cypress.config('experimentalSessionAndOrigin')) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.setCookie('foo', 'bar', { domain: 'barbaz.com' })
|
||||
|
||||
cy.clearCookies({ domain: null })
|
||||
cy.getCookies().should('have.length', 0)
|
||||
cy.getCookie('key1', { domain: 'www.foobar.com' }).should('exist')
|
||||
cy.getCookie('key2', { domain: 'foobar.com' }).should('exist')
|
||||
cy.getCookie('key3').should('not.exist')
|
||||
cy.getCookie('key4').should('not.exist')
|
||||
cy.getCookie('key5').should('not.exist')
|
||||
cy.getCookie('key6').should('not.exist')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('#clearCookie', () => {
|
||||
it('clears a single cookie', () => {
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.setCookie('key', 'val')
|
||||
cy.getCookies().should('have.length', 2)
|
||||
cy.clearCookie('foo')
|
||||
cy.getCookies().should('have.length', 1).then((cookies) => {
|
||||
expect(cookies[0].name).to.eq('key')
|
||||
const setCookies = () => {
|
||||
cy.log('set cookies')
|
||||
cy.setCookie('key', 'www.barbaz.com value', { domain: 'www.barbaz.com', log: false })
|
||||
cy.setCookie('key', 'barbaz.com value', { domain: 'barbaz.com', log: false })
|
||||
cy.setCookie('key', 'www.foobar.com value', { domain: 'www.foobar.com', log: false })
|
||||
cy.setCookie('key', 'foobar.com value', { domain: 'foobar.com', log: false })
|
||||
}
|
||||
|
||||
it('clears the cookie from the domain matching the AUT by default when AUT is an apex domain', () => {
|
||||
cy.visit('http://barbaz.com:3500/fixtures/generic.html')
|
||||
setCookies()
|
||||
|
||||
cy.clearCookie('key')
|
||||
|
||||
cy.getCookie('key').should('be.null')
|
||||
cy.getCookie('key', { domain: 'www.barbaz.com' }).should('exist')
|
||||
cy.getCookie('key', { domain: 'www.foobar.com' }).should('exist')
|
||||
cy.getCookie('key', { domain: 'foobar.com' }).should('exist')
|
||||
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://foobar.com:3500/fixtures/generic.html')
|
||||
// put back cookie removed above
|
||||
cy.setCookie('key', 'value1', { domain: 'barbaz.com' })
|
||||
|
||||
cy.clearCookie('key')
|
||||
|
||||
cy.getCookie('key').should('be.null')
|
||||
cy.getCookie('key', { domain: 'www.barbaz.com' }).should('exist')
|
||||
cy.getCookie('key', { domain: 'barbaz.com' }).should('exist')
|
||||
cy.getCookie('key', { domain: 'www.foobar.com' }).should('exist')
|
||||
})
|
||||
})
|
||||
|
||||
it('clears the cookie on the domain matching the AUT by default', () => {
|
||||
it('can clear the cookie from the subdomain matching the AUT by default when AUT is a subdomain', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.setCookie('foo', 'bar', { domain: 'www.foobar.com' })
|
||||
cy.setCookie('key', 'www.barbaz.com value', { domain: 'www.barbaz.com', log: false })
|
||||
|
||||
cy.clearCookie('foo')
|
||||
cy.clearCookie('key')
|
||||
|
||||
cy.getCookie('foo').should('be.null')
|
||||
cy.getCookie('foo', { domain: 'www.foobar.com' }).should('exist')
|
||||
cy.getCookie('key').should('not.exist')
|
||||
|
||||
if (isWebkit || !Cypress.config('experimentalSessionAndOrigin')) return
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar', { domain: 'barbaz.com' })
|
||||
cy.setCookie('key', 'www.foobar.com value', { domain: 'www.foobar.com', log: false })
|
||||
|
||||
cy.clearCookie('foo')
|
||||
cy.clearCookie('key')
|
||||
|
||||
cy.getCookie('foo').should('be.null')
|
||||
cy.getCookie('foo', { domain: 'barbaz.com' }).should('exist')
|
||||
cy.getCookie('key').should('not.exist')
|
||||
})
|
||||
})
|
||||
|
||||
it('can clear the cookie from the bare domain matching the AUT by default when AUT is a subdomain', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('key', 'barbaz.com value', { domain: 'barbaz.com', log: false })
|
||||
|
||||
cy.clearCookie('key')
|
||||
|
||||
cy.getCookie('key').should('not.exist')
|
||||
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('key', 'foobar.com value', { domain: 'foobar.com', log: false })
|
||||
|
||||
cy.clearCookie('key')
|
||||
|
||||
cy.getCookie('key').should('not.exist')
|
||||
})
|
||||
})
|
||||
|
||||
it('clears the cookie on the specified domain', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.setCookie('foo', 'bar', { domain: 'www.foobar.com' })
|
||||
cy.visit('http://barbaz.com:3500/fixtures/generic.html')
|
||||
setCookies()
|
||||
|
||||
cy.clearCookie('foo', { domain: 'www.foobar.com' })
|
||||
cy.clearCookie('key', { domain: 'foobar.com' })
|
||||
|
||||
cy.getCookie('foo', { domain: 'www.foobar.com' }).should('be.null')
|
||||
cy.getCookie('foo').should('exist')
|
||||
cy.getCookie('key', { domain: 'foobar.com' }).should('be.null')
|
||||
cy.getCookie('key', { domain: 'www.foobar.com' }).should('exist')
|
||||
cy.getCookie('key', { domain: 'www.barbaz.com' }).should('exist')
|
||||
cy.getCookie('key', { domain: 'barbaz.com' }).should('exist')
|
||||
|
||||
if (isWebkit || !Cypress.config('experimentalSessionAndOrigin')) return
|
||||
// webkit does not support cy.origin()
|
||||
if (isWebkit) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar')
|
||||
cy.origin('http://foobar.com:3500', () => {
|
||||
cy.visit('http://foobar.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('key', 'value1')
|
||||
|
||||
cy.clearCookie('foo', { domain: 'barbaz.com' })
|
||||
cy.clearCookie('key', { domain: 'barbaz.com' })
|
||||
|
||||
cy.getCookie('foo', { domain: 'barbaz.com' }).should('be.null')
|
||||
cy.getCookie('foo').should('exist')
|
||||
cy.getCookie('key', { domain: 'barbaz.com' }).should('be.null')
|
||||
cy.getCookie('key', { domain: 'www.barbaz.com' }).should('exist')
|
||||
cy.getCookie('key', { domain: 'www.foobar.com' }).should('exist')
|
||||
cy.getCookie('key', { domain: 'foobar.com' }).should('exist')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('clears cookie for any domain when domain is null', () => {
|
||||
cy.visit('http://www.barbaz.com:3500/fixtures/generic.html')
|
||||
cy.setCookie('foo', 'bar', { domain: 'www.foobar.com' })
|
||||
|
||||
cy.clearCookie('foo', { domain: null })
|
||||
cy.getCookies().should('have.length', 0)
|
||||
|
||||
if (isWebkit || !Cypress.config('experimentalSessionAndOrigin')) return
|
||||
|
||||
cy.origin('http://www.foobar.com:3500', () => {
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
|
||||
cy.setCookie('foo', 'bar', { domain: 'barbaz.com' })
|
||||
|
||||
cy.clearCookie('foo', { domain: null })
|
||||
cy.getCookies().should('have.length', 0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('src/cy/commands/cookies', () => {
|
||||
@@ -1340,35 +1491,7 @@ describe('src/cy/commands/cookies', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('calls \'clear:cookies\' only with clearableCookies', () => {
|
||||
Cypress.automation
|
||||
.withArgs('get:cookies')
|
||||
.resolves([
|
||||
{ name: 'foo' },
|
||||
{ name: 'bar' },
|
||||
])
|
||||
.withArgs('clear:cookies', [
|
||||
{ name: 'foo', domain: 'localhost' },
|
||||
])
|
||||
.resolves({
|
||||
name: 'foo',
|
||||
})
|
||||
|
||||
cy.stub(Cypress.Cookies, 'getClearableCookies')
|
||||
.withArgs([{ name: 'foo' }, { name: 'bar' }])
|
||||
.returns([{ name: 'foo' }])
|
||||
|
||||
cy.clearCookies().should('be.null').then(() => {
|
||||
expect(Cypress.automation).to.be.calledWith(
|
||||
'clear:cookies',
|
||||
[{ name: 'foo', domain: 'localhost' }],
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('calls \'clear:cookies\' with all cookies', () => {
|
||||
Cypress.Cookies.preserveOnce('bar', 'baz')
|
||||
|
||||
Cypress.automation
|
||||
.withArgs('get:cookies')
|
||||
.resolves([
|
||||
@@ -1376,12 +1499,6 @@ describe('src/cy/commands/cookies', () => {
|
||||
{ name: 'bar' },
|
||||
{ name: 'baz' },
|
||||
])
|
||||
.withArgs('clear:cookies', [
|
||||
{ name: 'foo', domain: 'localhost' },
|
||||
])
|
||||
.resolves({
|
||||
name: 'foo',
|
||||
})
|
||||
.withArgs('clear:cookies', [
|
||||
{ name: 'foo', domain: 'localhost' },
|
||||
{ name: 'bar', domain: 'localhost' },
|
||||
@@ -1393,11 +1510,6 @@ describe('src/cy/commands/cookies', () => {
|
||||
|
||||
cy
|
||||
.clearCookies().should('be.null').then(() => {
|
||||
expect(Cypress.automation).to.be.calledWith(
|
||||
'clear:cookies',
|
||||
[{ name: 'foo', domain: 'localhost' }],
|
||||
)
|
||||
}).clearCookies().should('be.null').then(() => {
|
||||
expect(Cypress.automation).to.be.calledWith(
|
||||
'clear:cookies', [
|
||||
{ name: 'foo', domain: 'localhost' },
|
||||
@@ -1659,34 +1771,4 @@ describe('src/cy/commands/cookies', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('Cypress.Cookies.defaults', () => {
|
||||
it('throws error on use of renamed whitelist option', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`Cypress.Cookies.defaults` `whitelist` option has been renamed to `preserve`. Please rename `whitelist` to `preserve`.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
Cypress.Cookies.defaults({
|
||||
whitelist: 'session_id',
|
||||
})
|
||||
})
|
||||
|
||||
it('logs deprecation warning', () => {
|
||||
cy.stub(Cypress.utils, 'warning')
|
||||
|
||||
Cypress.Cookies.defaults({})
|
||||
expect(Cypress.utils.warning).to.be.calledWith('`Cypress.Cookies.defaults()` has been deprecated and will be removed in a future release. Consider using `cy.session()` instead.\n\nhttps://on.cypress.io/session')
|
||||
})
|
||||
})
|
||||
|
||||
context('Cypress.Cookies.preserveOnce', () => {
|
||||
it('logs deprecation warning', () => {
|
||||
cy.stub(Cypress.utils, 'warning')
|
||||
|
||||
Cypress.Cookies.preserveOnce({})
|
||||
expect(Cypress.utils.warning).to.be.calledWith('`Cypress.Cookies.preserveOnce()` has been deprecated and will be removed in a future release. Consider using `cy.session()` instead.\n\nhttps://on.cypress.io/session')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -517,9 +517,7 @@ describe('src/cy/commands/location', () => {
|
||||
|
||||
const { lastLog } = this
|
||||
|
||||
_.each(obj, (value, key) => {
|
||||
expect(lastLog.get(key)).to.deep.eq(value)
|
||||
})
|
||||
expect(_.pick(lastLog.attributes, ['name', 'message'])).to.eql(obj)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -10,6 +10,11 @@ describe('src/cy/commands/misc', () => {
|
||||
it('nulls out the subject', () => {
|
||||
cy.noop({}).end().then((subject) => {
|
||||
expect(subject).to.be.null
|
||||
|
||||
// We want cy.end() to break the subject chain - any previous entries
|
||||
// (in this case `{}`) should be discarded. No re-running any previous
|
||||
// query functions once you've used `.end()` on a chain.
|
||||
expect(cy.subjectChain()).to.eql([null])
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -18,6 +23,11 @@ describe('src/cy/commands/misc', () => {
|
||||
it('nulls out the subject', () => {
|
||||
cy.wrap({}).log('foo').then((subject) => {
|
||||
expect(subject).to.be.null
|
||||
|
||||
// We want cy.end() to break the subject chain - any previous entries
|
||||
// (in this case `{}`) should be discarded. No re-running any previous
|
||||
// query functions once you've used `.end()` on a chain.
|
||||
expect(cy.subjectChain()).to.eql([null])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -35,16 +45,19 @@ describe('src/cy/commands/misc', () => {
|
||||
|
||||
it('logs immediately', function (done) {
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
cy.removeAllListeners('log:added')
|
||||
if (log.get('name') === 'log') {
|
||||
cy.removeAllListeners('log:added')
|
||||
|
||||
expect(log.get('message')).to.eq('foo, {foo: bar}')
|
||||
expect(log.get('name')).to.eq('log')
|
||||
expect(log.get('end')).to.be.true
|
||||
expect(log.get('message')).to.eq('foo, {foo: bar}')
|
||||
expect(log.get('end')).to.be.true
|
||||
|
||||
done()
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
cy.log('foo', { foo: 'bar' }).then(() => {
|
||||
// Query the 'body' here to verify that .log()
|
||||
// ignores the previous subject and logs only its arguments.
|
||||
cy.get('body').log('foo', { foo: 'bar' }).then(() => {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('ended')).to.be.true
|
||||
@@ -229,9 +242,8 @@ describe('src/cy/commands/misc', () => {
|
||||
|
||||
it('throws when wrapping an array of windows', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.scrollTo()` failed because it requires a DOM element.')
|
||||
expect(err.message).to.include('`cy.scrollTo()` failed because it requires a DOM element or window.')
|
||||
expect(err.message).to.include('[<window>]')
|
||||
expect(err.message).to.include('All 2 subject validations failed on this subject.')
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -243,9 +255,8 @@ describe('src/cy/commands/misc', () => {
|
||||
|
||||
it('throws when wrapping an array of documents', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.screenshot()` failed because it requires a DOM element.')
|
||||
expect(err.message).to.include('`cy.screenshot()` failed because it requires a DOM element, window or document.')
|
||||
expect(err.message).to.include('[<document>]')
|
||||
expect(err.message).to.include('All 3 subject validations failed on this subject.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -581,34 +581,18 @@ describe('src/cy/commands/navigation', () => {
|
||||
})
|
||||
})
|
||||
|
||||
if (!Cypress.config('experimentalSessionAndOrigin')) {
|
||||
it('removes window:load listeners 2x for about:blank and first url visit when experimentalSessionAndOrigin=false', () => {
|
||||
describe('removes window:load listeners when testIsolation=true', () => {
|
||||
it('removes for first url visit', () => {
|
||||
const listeners = cy.listeners('window:load')
|
||||
|
||||
const winLoad = cy.spy(cy, 'once').withArgs('window:load')
|
||||
|
||||
cy.visit('/fixtures/generic.html').then(() => {
|
||||
// once for about:blank, once for $iframe src
|
||||
expect(winLoad).to.be.calledTwice
|
||||
expect(winLoad).to.be.calledOnce
|
||||
expect(cy.listeners('window:load')).to.deep.eq(listeners)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (Cypress.config('experimentalSessionAndOrigin')) {
|
||||
describe('removes window:load listeners for first url visit when experimentalSessionAndOrigin=true', () => {
|
||||
it('removes for first url visit', () => {
|
||||
const listeners = cy.listeners('window:load')
|
||||
|
||||
const winLoad = cy.spy(cy, 'once').withArgs('window:load')
|
||||
|
||||
cy.visit('/fixtures/generic.html').then(() => {
|
||||
expect(winLoad).to.be.calledOnce // once for $iframe src
|
||||
expect(cy.listeners('window:load')).to.deep.eq(listeners)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
it('can visit pages on the same origin', () => {
|
||||
cy
|
||||
@@ -617,6 +601,32 @@ describe('src/cy/commands/navigation', () => {
|
||||
.visit('http://localhost:3500/fixtures/dimensions.html')
|
||||
})
|
||||
|
||||
it('can visit a 2nd domain on different port', function () {
|
||||
cy.visit('http://localhost:3500/fixtures/generic.html')
|
||||
cy.visit('http://localhost:3501/fixtures/generic.html')
|
||||
})
|
||||
|
||||
it('can visit a 2nd domain on different protocol', function () {
|
||||
cy.visit('http://localhost:3500/fixtures/generic.html')
|
||||
cy.visit('https://localhost:3502/fixtures/generic.html')
|
||||
})
|
||||
|
||||
it('can visit a 2nd domain on different superdomain', function () {
|
||||
cy.visit('http://localhost:3500/fixtures/generic.html')
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
})
|
||||
|
||||
it('can visit 2 unique ip addresses', function () {
|
||||
cy
|
||||
.visit('http://127.0.0.1:3500/fixtures/generic.html')
|
||||
.visit('http://0.0.0.0:3500/fixtures/generic.html')
|
||||
})
|
||||
|
||||
it('can navigate to a cross origin', { pageLoadTimeout: 3000 }, function () {
|
||||
cy.visit('/fixtures/primary-origin.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
})
|
||||
|
||||
it('resolves the subject to the remote iframe window', () => {
|
||||
cy.visit('/fixtures/jquery.html').then((win) => {
|
||||
expect(win).to.eq(cy.state('$autIframe').prop('contentWindow'))
|
||||
@@ -768,83 +778,41 @@ describe('src/cy/commands/navigation', () => {
|
||||
cy.visit('fixtures/redirection-loop-a.html')
|
||||
})
|
||||
|
||||
if (!Cypress.config('experimentalSessionAndOrigin')) {
|
||||
describe('when only hashes are changing when experimentalSessionAndOrigin=false', () => {
|
||||
it('short circuits the visit if the page will not refresh', () => {
|
||||
let count = 0
|
||||
const urls = []
|
||||
describe('when only hashes are changing when testIsolation=true', () => {
|
||||
it('short circuits the visit if the page will not refresh', () => {
|
||||
let count = 0
|
||||
const urls = []
|
||||
|
||||
cy.on('window:load', () => {
|
||||
urls.push(cy.state('window').location.href)
|
||||
cy.on('window:load', () => {
|
||||
urls.push(cy.state('window').location.href)
|
||||
|
||||
count += 1
|
||||
})
|
||||
count += 1
|
||||
})
|
||||
|
||||
cy
|
||||
// about:blank yes (1)
|
||||
.visit('/fixtures/generic.html?foo#bar') // yes (2)
|
||||
.visit('/fixtures/generic.html?foo#foo') // no (2)
|
||||
.visit('/fixtures/generic.html?bar#bar') // yes (3)
|
||||
.visit('/fixtures/dimensions.html?bar#bar') // yes (4)
|
||||
.visit('/fixtures/dimensions.html?baz#bar') // yes (5)
|
||||
.visit('/fixtures/dimensions.html#bar') // yes (6)
|
||||
.visit('/fixtures/dimensions.html') // yes (7)
|
||||
.visit('/fixtures/dimensions.html#baz') // no (7)
|
||||
.visit('/fixtures/dimensions.html#') // no (7)
|
||||
.then(() => {
|
||||
expect(count).to.eq(7)
|
||||
cy
|
||||
.visit('/fixtures/generic.html?foo#bar') // yes (1)
|
||||
.visit('/fixtures/generic.html?foo#foo') // no (1)
|
||||
.visit('/fixtures/generic.html?bar#bar') // yes (2)
|
||||
.visit('/fixtures/dimensions.html?bar#bar') // yes (3)
|
||||
.visit('/fixtures/dimensions.html?baz#bar') // yes (4)
|
||||
.visit('/fixtures/dimensions.html#bar') // yes (5)
|
||||
.visit('/fixtures/dimensions.html') // yes (6)
|
||||
.visit('/fixtures/dimensions.html#baz') // no (6)
|
||||
.visit('/fixtures/dimensions.html#') // no (6)
|
||||
.then(() => {
|
||||
expect(count).to.eq(6)
|
||||
|
||||
expect(urls).to.deep.eq([
|
||||
'about:blank',
|
||||
'http://localhost:3500/fixtures/generic.html?foo#bar',
|
||||
'http://localhost:3500/fixtures/generic.html?bar#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html?bar#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html?baz#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html',
|
||||
])
|
||||
})
|
||||
expect(urls).to.deep.eq([
|
||||
'http://localhost:3500/fixtures/generic.html?foo#bar',
|
||||
'http://localhost:3500/fixtures/generic.html?bar#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html?bar#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html?baz#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html',
|
||||
])
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (Cypress.config('experimentalSessionAndOrigin')) {
|
||||
describe('when only hashes are changing when experimentalSessionAndOrigin=true', () => {
|
||||
it('short circuits the visit if the page will not refresh', () => {
|
||||
let count = 0
|
||||
const urls = []
|
||||
|
||||
cy.on('window:load', () => {
|
||||
urls.push(cy.state('window').location.href)
|
||||
|
||||
count += 1
|
||||
})
|
||||
|
||||
cy
|
||||
.visit('/fixtures/generic.html?foo#bar') // yes (1)
|
||||
.visit('/fixtures/generic.html?foo#foo') // no (1)
|
||||
.visit('/fixtures/generic.html?bar#bar') // yes (2)
|
||||
.visit('/fixtures/dimensions.html?bar#bar') // yes (3)
|
||||
.visit('/fixtures/dimensions.html?baz#bar') // yes (4)
|
||||
.visit('/fixtures/dimensions.html#bar') // yes (5)
|
||||
.visit('/fixtures/dimensions.html') // yes (6)
|
||||
.visit('/fixtures/dimensions.html#baz') // no (6)
|
||||
.visit('/fixtures/dimensions.html#') // no (6)
|
||||
.then(() => {
|
||||
expect(count).to.eq(6)
|
||||
|
||||
expect(urls).to.deep.eq([
|
||||
'http://localhost:3500/fixtures/generic.html?foo#bar',
|
||||
'http://localhost:3500/fixtures/generic.html?bar#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html?bar#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html?baz#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html',
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/1311
|
||||
// TODO: fix flaky test https://github.com/cypress-io/cypress/issues/23201
|
||||
@@ -1015,10 +983,10 @@ describe('src/cy/commands/navigation', () => {
|
||||
expect(win.location.href).to.include('/fixtures/jquery.html?foo=bar#dashboard?baz=quux')
|
||||
})
|
||||
|
||||
this.win = cy.state('window')
|
||||
this.cyWin = cy.state('window')
|
||||
|
||||
this.eq = (attr, str) => {
|
||||
expect(this.win.location[attr]).to.eq(str)
|
||||
expect(this.cyWin.location[attr]).to.eq(str)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1469,159 +1437,6 @@ 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.docsUrl).to.eq('https://on.cypress.io/cannot-visit-different-origin-domain')
|
||||
assertLogLength(this.logs, 2)
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.visit('http://localhost:3500/fixtures/generic.html')
|
||||
cy.visit('http://localhost:3501/fixtures/generic.html')
|
||||
|
||||
// If experimentalSessionAndOrigin is enabled this is no longer an error
|
||||
if (Cypress.config('experimentalSessionAndOrigin')) {
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
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.docsUrl).to.eq('https://on.cypress.io/cannot-visit-different-origin-domain')
|
||||
assertLogLength(this.logs, 2)
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.visit('http://localhost:3500/fixtures/generic.html')
|
||||
cy.visit('https://localhost:3502/fixtures/generic.html')
|
||||
|
||||
// If experimentalSessionAndOrigin is enabled this is no longer an error
|
||||
if (Cypress.config('experimentalSessionAndOrigin')) {
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
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://www.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.docsUrl).to.eq('https://on.cypress.io/cannot-visit-different-origin-domain')
|
||||
assertLogLength(this.logs, 2)
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.visit('http://localhost:3500/fixtures/generic.html')
|
||||
cy.visit('http://www.foobar.com:3500/fixtures/generic.html')
|
||||
|
||||
// If experimentalSessionAndOrigin is enabled this is no longer an error
|
||||
if (Cypress.config('experimentalSessionAndOrigin')) {
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
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.docsUrl).to.eq('https://on.cypress.io/cannot-visit-different-origin-domain')
|
||||
assertLogLength(this.logs, 2)
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy
|
||||
.visit('http://127.0.0.1:3500/fixtures/generic.html')
|
||||
.visit('http://0.0.0.0:3500/fixtures/generic.html')
|
||||
|
||||
// If experimentalSessionAndOrigin is enabled this is no longer an error
|
||||
if (Cypress.config('experimentalSessionAndOrigin')) {
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
it('displays loading_network_failed when _resolveUrl throws', function (done) {
|
||||
const err1 = new Error('connect ECONNREFUSED 127.0.0.1:64646')
|
||||
|
||||
@@ -2233,42 +2048,6 @@ describe('src/cy/commands/navigation', () => {
|
||||
.get('#does-not-exist', { timeout: 200 }).should('have.class', 'foo')
|
||||
})
|
||||
|
||||
it('displays cross origin failures when navigating to a cross origin', { pageLoadTimeout: 3000 }, function (done) {
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
|
||||
const error = Cypress.isBrowser('firefox') ? 'Permission denied to get property "href" 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.contain(stripIndent`\
|
||||
Cypress detected a cross origin error happened on page load:\n
|
||||
> ${error}\n
|
||||
Before the page load, you were bound to the origin:\n
|
||||
> http://localhost:3500\n
|
||||
A cross origin error happens when your application navigates to a new URL which does not match the origin above.\n
|
||||
A new URL does not match the origin if the 'protocol', 'port' (if specified), and/or 'host' 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 }`)
|
||||
|
||||
expect(err.message).to.contain(`packages/driver/cypress.config.ts`)
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/cross-origin-violation')
|
||||
assertLogLength(this.logs, 7)
|
||||
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.visit('/fixtures/primary-origin.html')
|
||||
cy.get('a[data-cy="cross-origin-secondary-link"]').click()
|
||||
|
||||
// If experimentalSessionAndOrigin is enabled this is no longer an error
|
||||
if (Cypress.config('experimentalSessionAndOrigin')) {
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
})
|
||||
@@ -2345,7 +2124,7 @@ describe('src/cy/commands/navigation', () => {
|
||||
expect(this.lastLog).to.exist
|
||||
expect(this.lastLog.get('state')).to.eq('pending')
|
||||
expect(this.lastLog.get('message')).to.eq('--waiting for new page to load--')
|
||||
expect(this.lastLog.get('snapshots')).to.not.exist
|
||||
expect(this.lastLog.get('snapshots')).to.have.length(0)
|
||||
})
|
||||
}).get('#dimensions').click()
|
||||
.then(function () {
|
||||
@@ -2373,7 +2152,7 @@ describe('src/cy/commands/navigation', () => {
|
||||
expect(this.lastLog).to.exist
|
||||
expect(this.lastLog.get('state')).to.eq('pending')
|
||||
expect(this.lastLog.get('message')).to.eq('--waiting for new page to load--')
|
||||
expect(this.lastLog.get('snapshots')).to.not.exist
|
||||
expect(this.lastLog.get('snapshots')).to.have.length(0)
|
||||
})
|
||||
|
||||
cy
|
||||
@@ -2594,206 +2373,101 @@ describe('src/cy/commands/navigation', () => {
|
||||
})
|
||||
})
|
||||
|
||||
if (!Cypress.config('experimentalSessionAndOrigin')) {
|
||||
describe('filters page load events when going back with window navigation when experimentalSessionAndOrigin=false', () => {
|
||||
describe('filters page load events when going back with window navigation when testIsolation=true', () => {
|
||||
// https://github.com/cypress-io/cypress/issues/19230
|
||||
it('when going back with window navigation', () => {
|
||||
const emit = cy.spy(Cypress, 'emit').log(false).withArgs('navigation:changed')
|
||||
it('when going back with window navigation', () => {
|
||||
const emit = cy.spy(Cypress, 'emit').log(false).withArgs('navigation:changed')
|
||||
|
||||
cy
|
||||
.visit('/fixtures/generic.html')
|
||||
.get('#hashchange').click()
|
||||
.window().then((win) => {
|
||||
cy
|
||||
.visit('/fixtures/generic.html')
|
||||
.get('#hashchange').click()
|
||||
.window().then((win) => {
|
||||
return new Promise((resolve) => {
|
||||
cy.once('navigation:changed', resolve)
|
||||
|
||||
win.history.back()
|
||||
}).then(() => {
|
||||
return new Promise((resolve) => {
|
||||
cy.once('navigation:changed', resolve)
|
||||
|
||||
win.history.back()
|
||||
}).then(() => {
|
||||
return new Promise((resolve) => {
|
||||
cy.once('navigation:changed', resolve)
|
||||
|
||||
win.history.forward()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
cy.get('#dimensions').click()
|
||||
.window().then((win) => {
|
||||
return new Promise((resolve) => {
|
||||
cy.on('navigation:changed', (event) => {
|
||||
if (event.includes('(load)')) {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
|
||||
win.history.back()
|
||||
})
|
||||
.then(() => {
|
||||
return new Promise((resolve) => {
|
||||
cy.on('navigation:changed', resolve)
|
||||
win.history.back()
|
||||
})
|
||||
})
|
||||
.then(() => {
|
||||
expect(emit.getCall(0)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(1)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (before:load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(2)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(3)).to.be.calledWithMatch(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
|
||||
expect(emit.getCall(4)).to.be.calledWithMatch(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
|
||||
expect(emit.getCall(5)).to.be.calledWithMatch(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
|
||||
expect(emit.getCall(6)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (before:load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(7)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(8)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (before:load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(9)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(10)).to.be.calledWithMatch(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
|
||||
expect(emit.callCount).to.eq(11)
|
||||
win.history.forward()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (Cypress.config('experimentalSessionAndOrigin')) {
|
||||
describe('filters page load events when going back with window navigation when experimentalSessionAndOrigin=true', () => {
|
||||
// https://github.com/cypress-io/cypress/issues/19230
|
||||
it('when going back with window navigation', () => {
|
||||
const emit = cy.spy(Cypress, 'emit').log(false).withArgs('navigation:changed')
|
||||
cy.get('#dimensions').click()
|
||||
.window().then((win) => {
|
||||
return new Promise((resolve) => {
|
||||
cy.on('navigation:changed', (event) => {
|
||||
if (event.includes('(load)')) {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
|
||||
cy
|
||||
.visit('/fixtures/generic.html')
|
||||
.get('#hashchange').click()
|
||||
.window().then((win) => {
|
||||
win.history.back()
|
||||
})
|
||||
.then(() => {
|
||||
return new Promise((resolve) => {
|
||||
cy.once('navigation:changed', resolve)
|
||||
|
||||
cy.on('navigation:changed', resolve)
|
||||
win.history.back()
|
||||
}).then(() => {
|
||||
return new Promise((resolve) => {
|
||||
cy.once('navigation:changed', resolve)
|
||||
|
||||
win.history.forward()
|
||||
})
|
||||
})
|
||||
})
|
||||
.then(() => {
|
||||
expect(emit.getCall(0)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (before:load)',
|
||||
)
|
||||
|
||||
cy.get('#dimensions').click()
|
||||
.window().then((win) => {
|
||||
return new Promise((resolve) => {
|
||||
cy.on('navigation:changed', (event) => {
|
||||
if (event.includes('(load)')) {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
expect(emit.getCall(1)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (load)',
|
||||
)
|
||||
|
||||
win.history.back()
|
||||
})
|
||||
.then(() => {
|
||||
return new Promise((resolve) => {
|
||||
cy.on('navigation:changed', resolve)
|
||||
win.history.back()
|
||||
})
|
||||
})
|
||||
.then(() => {
|
||||
expect(emit.getCall(0)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (before:load)',
|
||||
)
|
||||
expect(emit.getCall(2)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
|
||||
expect(emit.getCall(1)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (load)',
|
||||
)
|
||||
expect(emit.getCall(3)).to.be.calledWithMatch(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
|
||||
expect(emit.getCall(2)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
expect(emit.getCall(4)).to.be.calledWithMatch(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
|
||||
expect(emit.getCall(3)).to.be.calledWithMatch(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
expect(emit.getCall(5)).to.be.calledWithMatch(
|
||||
'navigation:changed',
|
||||
'page navigation event (before:load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(4)).to.be.calledWithMatch(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
expect(emit.getCall(6)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(5)).to.be.calledWithMatch(
|
||||
'navigation:changed',
|
||||
'page navigation event (before:load)',
|
||||
)
|
||||
expect(emit.getCall(7)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (before:load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(6)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (load)',
|
||||
)
|
||||
expect(emit.getCall(8)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(7)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (before:load)',
|
||||
)
|
||||
expect(emit.getCall(9)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
|
||||
expect(emit.getCall(8)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(9)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
|
||||
expect(emit.callCount).to.eq(10)
|
||||
})
|
||||
expect(emit.callCount).to.eq(10)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
it('logs url changed event', () => {
|
||||
cy
|
||||
|
||||
@@ -95,7 +95,7 @@ describe('src/cy/commands/querying', () => {
|
||||
cy.get('body').focused().then(function () {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('type')).to.eq('parent')
|
||||
expect(lastLog.get('type')).not.to.eq('child')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -123,7 +123,7 @@ describe('src/cy/commands/querying', () => {
|
||||
cy.get('input:first').focused().then(function ($input) {
|
||||
const { lastLog } = this
|
||||
|
||||
expect(lastLog.get('$el')).to.eq($input)
|
||||
expect(lastLog.get('$el')).to.eql($input)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -18,23 +18,6 @@ describe('src/cy/commands/querying', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// NOTE: FLAKY in CI, need to investigate further
|
||||
it.skip('retries finding elements until something is found', () => {
|
||||
const missingEl = $('<div />', { id: 'missing-el' })
|
||||
|
||||
// wait until we're ALMOST about to time out before
|
||||
// appending the missingEl
|
||||
cy.on('command:retry', (options) => {
|
||||
if ((options.total + (options._interval * 4)) > options._runnableTimeout) {
|
||||
cy.$$('body').append(missingEl)
|
||||
}
|
||||
})
|
||||
|
||||
cy.get('#missing-el').then(($div) => {
|
||||
expect($div).to.match(missingEl)
|
||||
})
|
||||
})
|
||||
|
||||
it('can increase the timeout', () => {
|
||||
const missingEl = $('<div />', { id: 'missing-el' })
|
||||
|
||||
@@ -287,7 +270,7 @@ describe('src/cy/commands/querying', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('retries an alias when too many elements found without replaying commands', () => {
|
||||
it('retries an alias when too many elements found', () => {
|
||||
// add 500ms to the delta
|
||||
cy.timeout(500, true)
|
||||
|
||||
@@ -295,24 +278,15 @@ describe('src/cy/commands/querying', () => {
|
||||
|
||||
const length = buttons.length - 2
|
||||
|
||||
const replayCommandsFrom = cy.spy(cy, 'replayCommandsFrom')
|
||||
|
||||
cy.on('command:retry', () => {
|
||||
buttons.last().remove()
|
||||
buttons = cy.$$('button')
|
||||
})
|
||||
|
||||
const existingLen = cy.queue.length
|
||||
|
||||
// should eventually resolve after adding 1 button
|
||||
cy
|
||||
.get('button').as('btns')
|
||||
.get('@btns').should('have.length', length).then(($buttons) => {
|
||||
expect(replayCommandsFrom).not.to.be.called
|
||||
|
||||
// get, as, get, should, then == 5
|
||||
expect(cy.queue.length - existingLen).to.eq(5) // we should not have replayed any commands
|
||||
|
||||
expect($buttons.length).to.eq(length)
|
||||
})
|
||||
})
|
||||
@@ -380,10 +354,9 @@ describe('src/cy/commands/querying', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('logs route aliases', () => {
|
||||
it('logs intercept aliases', () => {
|
||||
cy.visit('http://localhost:3500/fixtures/jquery.html')
|
||||
cy.server()
|
||||
cy.route(/users/, {}).as('get.users')
|
||||
cy.intercept(/users/, {}).as('get.users')
|
||||
cy.window().then({ timeout: 2000 }, (win) => {
|
||||
win.$.get('/users')
|
||||
})
|
||||
@@ -392,35 +365,27 @@ describe('src/cy/commands/querying', () => {
|
||||
expect(this.lastLog.pick('message', 'referencesAlias', 'aliasType')).to.deep.eq({
|
||||
message: '@get.users',
|
||||
referencesAlias: { name: 'get.users' },
|
||||
aliasType: 'route',
|
||||
aliasType: 'intercept',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('logs primitive aliases', (done) => {
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
if (attrs.name === 'get') {
|
||||
expect(log.pick('$el', 'numRetries', 'referencesAlias', 'aliasType')).to.deep.eq({
|
||||
referencesAlias: { name: 'f' },
|
||||
aliasType: 'primitive',
|
||||
})
|
||||
|
||||
done()
|
||||
}
|
||||
it('logs primitive aliases', () => {
|
||||
cy.noop('foo').as('f')
|
||||
.get('@f').then(function () {
|
||||
expect(this.lastLog.pick('$el', 'numRetries', 'referencesAlias', 'aliasType')).to.deep.eq({
|
||||
referencesAlias: { name: 'f' },
|
||||
aliasType: 'primitive',
|
||||
})
|
||||
})
|
||||
|
||||
cy
|
||||
.noop('foo').as('f')
|
||||
.get('@f')
|
||||
})
|
||||
|
||||
it('logs immediately before resolving', (done) => {
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
if (attrs.name === 'get') {
|
||||
expect(log.pick('state', 'referencesAlias', 'aliasType')).to.deep.eq({
|
||||
expect(log.pick('state', 'referencesAlias')).to.deep.eq({
|
||||
state: 'pending',
|
||||
referencesAlias: undefined,
|
||||
aliasType: 'dom',
|
||||
})
|
||||
|
||||
done()
|
||||
@@ -453,10 +418,10 @@ describe('src/cy/commands/querying', () => {
|
||||
referencesAlias: undefined,
|
||||
}
|
||||
|
||||
expect(this.lastLog.get('$el').get(0)).to.eq($body.get(0))
|
||||
expect(this.lastLog.get('$el')).to.eql($body)
|
||||
|
||||
_.each(obj, (value, key) => {
|
||||
expect(this.lastLog.get(key)).deep.eq(value, `expected key: ${key} to eq value: ${value}`)
|
||||
expect(this.lastLog.get(key)).to.eq(value, `expected key: ${key} to eq value: ${value}`)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -493,14 +458,13 @@ describe('src/cy/commands/querying', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('#consoleProps with a route alias', () => {
|
||||
it('#consoleProps with an intercept alias', () => {
|
||||
cy
|
||||
.server()
|
||||
.route(/users/, {}).as('getUsers')
|
||||
.intercept(/users/, {}).as('getUsers')
|
||||
.visit('http://localhost:3500/fixtures/jquery.html')
|
||||
.window().then({ timeout: 2000 }, (win) => {
|
||||
return win.$.get('/users')
|
||||
}).get('@getUsers').then(function (obj) {
|
||||
}).wait('@getUsers').get('@getUsers').then(function (obj) {
|
||||
expect(this.lastLog.invoke('consoleProps')).to.deep.eq({
|
||||
Command: 'get',
|
||||
Alias: '@getUsers',
|
||||
@@ -556,36 +520,33 @@ describe('src/cy/commands/querying', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('route aliases', () => {
|
||||
describe('intercept aliases', () => {
|
||||
it('returns the xhr', () => {
|
||||
cy
|
||||
.server()
|
||||
.route(/users/, {}).as('getUsers')
|
||||
.intercept(/users/, {}).as('getUsers')
|
||||
.visit('http://localhost:3500/fixtures/jquery.html')
|
||||
.window().then({ timeout: 2000 }, (win) => {
|
||||
return win.$.get('/users')
|
||||
}).get('@getUsers').then((xhr) => {
|
||||
expect(xhr.url).to.include('/users')
|
||||
}).wait('@getUsers').get('@getUsers').then((xhr) => {
|
||||
expect(xhr.response.url).to.include('/users')
|
||||
})
|
||||
})
|
||||
|
||||
it('handles dots in alias name', () => {
|
||||
cy.server()
|
||||
cy.route(/users/, {}).as('get.users')
|
||||
cy.intercept(/users/, {}).as('get.users')
|
||||
cy.visit('http://localhost:3500/fixtures/jquery.html')
|
||||
cy.window().then({ timeout: 2000 }, (win) => {
|
||||
return win.$.get('/users')
|
||||
})
|
||||
|
||||
cy.get('@get.users').then((xhr) => {
|
||||
expect(xhr.url).to.include('/users')
|
||||
cy.wait('@get.users').get('@get.users').then((xhr) => {
|
||||
expect(xhr.response.url).to.include('/users')
|
||||
})
|
||||
})
|
||||
|
||||
it('returns null if no xhr is found', () => {
|
||||
cy
|
||||
.server()
|
||||
.route(/users/, {}).as('getUsers')
|
||||
.intercept(/users/, {}).as('getUsers')
|
||||
.visit('http://localhost:3500/fixtures/jquery.html')
|
||||
.get('@getUsers').then((xhr) => {
|
||||
expect(xhr).to.be.null
|
||||
@@ -595,25 +556,23 @@ describe('src/cy/commands/querying', () => {
|
||||
it('returns an array of xhrs', () => {
|
||||
cy
|
||||
.visit('http://localhost:3500/fixtures/jquery.html')
|
||||
.server()
|
||||
.route(/users/, {}).as('getUsers')
|
||||
.intercept(/users/, {}).as('getUsers')
|
||||
.window().then({ timeout: 2000 }, (win) => {
|
||||
return Promise.all([
|
||||
win.$.get('/users', { num: 1 }),
|
||||
win.$.get('/users', { num: 2 }),
|
||||
])
|
||||
}).get('@getUsers.all').then((xhrs) => {
|
||||
}).wait('@getUsers').wait('@getUsers').get('@getUsers.all').then((xhrs) => {
|
||||
expect(xhrs).to.be.an('array')
|
||||
expect(xhrs[0].url).to.include('/users?num=1')
|
||||
expect(xhrs[0].response.url).to.include('/users?num=1')
|
||||
|
||||
expect(xhrs[1].url).to.include('/users?num=2')
|
||||
expect(xhrs[1].response.url).to.include('/users?num=2')
|
||||
})
|
||||
})
|
||||
|
||||
it('returns an array of xhrs when dots in alias name', () => {
|
||||
cy.visit('http://localhost:3500/fixtures/jquery.html')
|
||||
cy.server()
|
||||
cy.route(/users/, {}).as('get.users')
|
||||
cy.intercept(/users/, {}).as('get.users')
|
||||
cy.window().then({ timeout: 2000 }, (win) => {
|
||||
return Promise.all([
|
||||
win.$.get('/users', { num: 1 }),
|
||||
@@ -621,48 +580,45 @@ describe('src/cy/commands/querying', () => {
|
||||
])
|
||||
})
|
||||
|
||||
cy.get('@get.users.all').then((xhrs) => {
|
||||
cy.wait('@get.users').wait('@get.users').get('@get.users.all').then((xhrs) => {
|
||||
expect(xhrs).to.be.an('array')
|
||||
expect(xhrs[0].url).to.include('/users?num=1')
|
||||
expect(xhrs[0].response.url).to.include('/users?num=1')
|
||||
|
||||
expect(xhrs[1].url).to.include('/users?num=2')
|
||||
expect(xhrs[1].response.url).to.include('/users?num=2')
|
||||
})
|
||||
})
|
||||
|
||||
it('returns the 1st xhr', () => {
|
||||
cy
|
||||
.visit('http://localhost:3500/fixtures/jquery.html')
|
||||
.server()
|
||||
.route(/users/, {}).as('getUsers')
|
||||
.intercept(/users/, {}).as('getUsers')
|
||||
.window().then({ timeout: 2000 }, (win) => {
|
||||
return Promise.all([
|
||||
win.$.get('/users', { num: 1 }),
|
||||
win.$.get('/users', { num: 2 }),
|
||||
])
|
||||
}).get('@getUsers.1').then((xhr1) => {
|
||||
expect(xhr1.url).to.include('/users?num=1')
|
||||
}).wait('@getUsers').wait('@getUsers').get('@getUsers.1').then((xhr1) => {
|
||||
expect(xhr1.response.url).to.include('/users?num=1')
|
||||
})
|
||||
})
|
||||
|
||||
it('returns the 2nd xhr', () => {
|
||||
cy
|
||||
.visit('http://localhost:3500/fixtures/jquery.html')
|
||||
.server()
|
||||
.route(/users/, {}).as('getUsers')
|
||||
.intercept(/users/, {}).as('getUsers')
|
||||
.window().then({ timeout: 2000 }, (win) => {
|
||||
return Promise.all([
|
||||
win.$.get('/users', { num: 1 }),
|
||||
win.$.get('/users', { num: 2 }),
|
||||
])
|
||||
}).get('@getUsers.2').then((xhr2) => {
|
||||
expect(xhr2.url).to.include('/users?num=2')
|
||||
}).wait('@getUsers').wait('@getUsers').get('@getUsers.2').then((xhr2) => {
|
||||
expect(xhr2.response.url).to.include('/users?num=2')
|
||||
})
|
||||
})
|
||||
|
||||
it('returns the 2nd xhr when dots in alias', () => {
|
||||
cy.visit('http://localhost:3500/fixtures/jquery.html')
|
||||
cy.server()
|
||||
cy.route(/users/, {}).as('get.users')
|
||||
cy.intercept(/users/, {}).as('get.users')
|
||||
cy.window().then({ timeout: 2000 }, (win) => {
|
||||
return Promise.all([
|
||||
win.$.get('/users', { num: 1 }),
|
||||
@@ -670,68 +626,27 @@ describe('src/cy/commands/querying', () => {
|
||||
])
|
||||
})
|
||||
|
||||
cy.get('@get.users.2').then((xhr2) => {
|
||||
expect(xhr2.url).to.include('/users?num=2')
|
||||
cy.wait('@get.users').wait('@get.users').get('@get.users.2').then((xhr2) => {
|
||||
expect(xhr2.response.url).to.include('/users?num=2')
|
||||
})
|
||||
})
|
||||
|
||||
it('returns the 3rd xhr as null', () => {
|
||||
cy
|
||||
.server()
|
||||
.route(/users/, {}).as('getUsers')
|
||||
.intercept(/users/, {}).as('getUsers')
|
||||
.visit('http://localhost:3500/fixtures/jquery.html')
|
||||
.window().then({ timeout: 2000 }, (win) => {
|
||||
return Promise.all([
|
||||
win.$.get('/users', { num: 1 }),
|
||||
win.$.get('/users', { num: 2 }),
|
||||
])
|
||||
}).get('@getUsers.3').then((xhr3) => {
|
||||
}).wait('@getUsers').wait('@getUsers').get('@getUsers.3').then((xhr3) => {
|
||||
expect(xhr3).to.be.null
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// it "re-queries the dom if any element in an alias isnt visible", ->
|
||||
// inputs = cy.$$("input")
|
||||
// inputs.hide()
|
||||
|
||||
// cy
|
||||
// .get("input", {visible: false}).as("inputs").then ($inputs) ->
|
||||
// @length = $inputs.length
|
||||
|
||||
// ## show the inputs
|
||||
// $inputs.show()
|
||||
|
||||
// return $inputs
|
||||
// .get("@inputs").then ($inputs) ->
|
||||
// ## we should have re-queried for these inputs
|
||||
// ## which should have increased their length by 1
|
||||
// expect($inputs).to.have.length(@length)
|
||||
|
||||
// these other tests are for .save
|
||||
// it "will resolve deferred arguments", ->
|
||||
// df = $.Deferred()
|
||||
|
||||
// _.delay ->
|
||||
// df.resolve("iphone")
|
||||
// , 100
|
||||
|
||||
// cy.get("input:text:first").type(df).then ($input) ->
|
||||
// expect($input).to.have.value("iphone")
|
||||
|
||||
// it "handles saving subjects", ->
|
||||
// cy.noop({foo: "foo"}).assign("foo").noop(cy.get("foo")).then (subject) ->
|
||||
// expect(subject).to.deep.eq {foo: "foo"}
|
||||
|
||||
// it "resolves falsy arguments", ->
|
||||
// cy.noop(0).assign("zero").then ->
|
||||
// expect(cy.get("zero")).to.eq 0
|
||||
|
||||
// it "returns a function when no alias was found", ->
|
||||
// cy.noop().then ->
|
||||
// expect(cy.get("something")).to.be.a("function")
|
||||
|
||||
describe('errors', {
|
||||
defaultCommandTimeout: 50,
|
||||
}, () => {
|
||||
@@ -840,8 +755,7 @@ describe('src/cy/commands/querying', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/json/, { foo: 'foo' }).as('getJSON')
|
||||
.intercept(/json/, { foo: 'foo' }).as('getJSON')
|
||||
.visit('http://localhost:3500/fixtures/xhr.html').then(() => {
|
||||
cy.$$('#get-json').click(() => {
|
||||
cy.timeout(1000)
|
||||
@@ -974,34 +888,31 @@ describe('src/cy/commands/querying', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/users/, {}).as('getUsers')
|
||||
.intercept(/users/, {}).as('getUsers')
|
||||
.get('@getUsers.0')
|
||||
})
|
||||
|
||||
it('throws when alias property isnt just a digit', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`1b` is not a valid alias property. Only `numbers` or `all` is permitted.')
|
||||
expect(err.message).to.include('could not find a registered alias for: `@getUsers.1b`')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/users/, {}).as('getUsers')
|
||||
.intercept(/users/, {}).as('getUsers')
|
||||
.get('@getUsers.1b')
|
||||
})
|
||||
|
||||
it('throws when alias property isnt a digit or `all`', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`all ` is not a valid alias property. Only `numbers` or `all` is permitted.')
|
||||
expect(err.message).to.include('could not find a registered alias for: `@getUsers.all `')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/users/, {}).as('getUsers')
|
||||
.intercept(/users/, {}).as('getUsers')
|
||||
.get('@getUsers.all ')
|
||||
})
|
||||
|
||||
@@ -1089,7 +1000,7 @@ describe('src/cy/commands/querying', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('GET is scoped to the current subject', () => {
|
||||
it('is scoped to the current subject', () => {
|
||||
const span = cy.$$('#click-me a span')
|
||||
|
||||
cy.get('#click-me a').contains('click').then(($span) => {
|
||||
@@ -1232,23 +1143,9 @@ describe('src/cy/commands/querying', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('finds text by regexp and restores contains', () => {
|
||||
const { contains } = Cypress.$Cypress.$.expr[':']
|
||||
|
||||
cy.contains(/^asdf \d+/).then(($li) => {
|
||||
expect($li).to.have.text('asdf 1')
|
||||
|
||||
expect(Cypress.$Cypress.$.expr[':'].contains).to.eq(contains)
|
||||
})
|
||||
})
|
||||
|
||||
it('finds text by regexp when second parameter is a regexp and restores contains', () => {
|
||||
const { contains } = Cypress.$Cypress.$.expr[':']
|
||||
|
||||
it('finds text by regexp when second parameter is a regexp', () => {
|
||||
cy.contains('#asdf>li:first', /asdf 1/).then(($li) => {
|
||||
expect($li).to.have.text('asdf 1')
|
||||
|
||||
expect(Cypress.$Cypress.$.expr[':'].contains).to.eq(contains)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1316,6 +1213,20 @@ describe('src/cy/commands/querying', () => {
|
||||
cy.contains(/=[0-6]/, { timeout: 100 }).should('have.text', 'a=2')
|
||||
})
|
||||
|
||||
it('does not interfere with other aliased .contains()', () => {
|
||||
/*
|
||||
* There was a regression (no github issue logged) while refactoring .contains() where if a test aliased
|
||||
* a query using .contains(), future .contains() calls could overwrite its internal state, causing the first one
|
||||
* to look for the second one's arguments rather than its own.
|
||||
*
|
||||
* This test guards against that regression; if the `contains('New York')` inside @newYork alias were
|
||||
* overwritten by contains(`Nested Find`), then the existence assertion would fail.
|
||||
*/
|
||||
cy.contains('New York').as('newYork')
|
||||
cy.contains('Nested Find').invoke('remove')
|
||||
cy.get('@newYork').should('exist')
|
||||
})
|
||||
|
||||
describe('should(\'not.exist\')', () => {
|
||||
it('returns null when no content exists', () => {
|
||||
cy.contains('alksjdflkasjdflkajsdf').should('not.exist').then(($el) => {
|
||||
@@ -1422,6 +1333,7 @@ space
|
||||
|
||||
it('is case sensitive when matchCase is undefined', () => {
|
||||
cy.get('#test-button').contains('Test')
|
||||
cy.contains('test').should('not.exist')
|
||||
})
|
||||
|
||||
it('is case sensitive when matchCase is true', () => {
|
||||
@@ -1599,16 +1511,20 @@ space
|
||||
})
|
||||
|
||||
describe('special characters', () => {
|
||||
_.each('\' " [ ] { } . @ # $ % ^ & * ( ) , ; :'.split(' '), (char) => {
|
||||
it(`finds content by string with character: ${char}`, () => {
|
||||
const specialCharacters = '\' " [ ] { } . @ # $ % ^ & * ( ) , ; :'.split(' ')
|
||||
|
||||
it(`finds content by string with characters`, () => {
|
||||
_.each(specialCharacters, (char) => {
|
||||
const span = $(`<span>special char ${char} content</span>`).appendTo(cy.$$('body'))
|
||||
|
||||
cy.contains('span', char).then(($span) => {
|
||||
expect($span.get(0)).to.eq(span.get(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it(`finds content by regex with character: ${char}`, () => {
|
||||
it(`finds content by regex with characters`, () => {
|
||||
_.each(specialCharacters, (char) => {
|
||||
const span = $(`<span>special char ${char} content</span>`).appendTo(cy.$$('body'))
|
||||
|
||||
cy.contains('span', new RegExp(_.escapeRegExp(char))).then(($span) => {
|
||||
@@ -1676,22 +1592,16 @@ space
|
||||
})
|
||||
})
|
||||
|
||||
it('sets type to parent when subject isnt element', () => {
|
||||
cy.window().contains('foo').then(function () {
|
||||
expect(this.lastLog.get('type')).to.eq('parent')
|
||||
it('sets type to child when used as a child command', () => {
|
||||
cy.get('#specific-contains').contains('foo').then(function () {
|
||||
expect(this.lastLog.get('type')).to.eq('child')
|
||||
|
||||
cy.document().contains('foo').then(function () {
|
||||
expect(this.lastLog.get('type')).to.eq('parent')
|
||||
expect(this.lastLog.get('type')).to.eq('child')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('sets type to child when used as a child command', () => {
|
||||
cy.get('body').contains('foo').then(function () {
|
||||
expect(this.lastLog.get('type')).to.eq('child')
|
||||
})
|
||||
})
|
||||
|
||||
it('logs when not exists', () => {
|
||||
cy.contains('does-not-exist').should('not.exist').then(function () {
|
||||
expect(this.lastLog.get('message')).to.eq('does-not-exist')
|
||||
@@ -1883,7 +1793,7 @@ space
|
||||
const containsLog = this.logs[0]
|
||||
const assertionLog = this.logs[1]
|
||||
|
||||
expect(err.message).to.eq('`cy.contains()` cannot be passed a `length` option because it will only ever return 1 element.')
|
||||
expect(err.message).to.eq('`cy.contains()` only ever returns one element, so you cannot assert on a `length` greater than one.')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/contains')
|
||||
|
||||
expect(containsLog.get('state')).to.eq('passed')
|
||||
@@ -1897,46 +1807,6 @@ space
|
||||
|
||||
cy.contains('Nested Find').should('have.length', 2)
|
||||
})
|
||||
|
||||
it('restores contains even when cy.get fails', (done) => {
|
||||
const { contains } = Cypress.$Cypress.$.expr[':']
|
||||
|
||||
const cyNow = cy.now
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('Syntax error, unrecognized expression')
|
||||
expect(Cypress.$Cypress.$.expr[':'].contains).to.eq(contains)
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.stub(cy, 'now').callsFake(() => cyNow('get', 'aBad:jQuery^Selector', {}))
|
||||
|
||||
cy.contains(/^asdf \d+/)
|
||||
})
|
||||
|
||||
it('restores contains on abort', (done) => {
|
||||
cy.timeout(1000)
|
||||
|
||||
const { contains } = Cypress.$Cypress.$.expr[':']
|
||||
|
||||
cy.stub(Cypress.runner, 'stop')
|
||||
|
||||
cy.on('stop', () => {
|
||||
_.delay(() => {
|
||||
expect(Cypress.$Cypress.$.expr[':'].contains).to.eq(contains)
|
||||
|
||||
done()
|
||||
}
|
||||
, 50)
|
||||
})
|
||||
|
||||
cy.on('command:retry', _.after(2, () => {
|
||||
Cypress.stop()
|
||||
}))
|
||||
|
||||
cy.contains(/^does not contain asdfasdf at all$/)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
const helpers = require('../../../support/helpers')
|
||||
|
||||
const { _ } = Cypress
|
||||
|
||||
describe('src/cy/commands/querying - shadow dom', () => {
|
||||
@@ -218,7 +216,7 @@ describe('src/cy/commands/querying - shadow dom', () => {
|
||||
cy.get('#shadow-element-1').shadow()
|
||||
.then(function ($el) {
|
||||
expect(this.lastLog.invoke('consoleProps')).to.deep.eq({
|
||||
'Applied To': helpers.getFirstSubjectByName('get').get(0),
|
||||
'Applied To': cy.$$('#shadow-element-1')[0],
|
||||
Yielded: Cypress.dom.getElements($el),
|
||||
Elements: $el.length,
|
||||
Command: 'shadow',
|
||||
|
||||
@@ -104,7 +104,7 @@ describe('src/cy/commands/querying/within', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('clears withinSubject after within is over', () => {
|
||||
it('clears withinSubjectChain after within is over', () => {
|
||||
const input = cy.$$('input:first')
|
||||
const span = cy.$$('#button-text button span')
|
||||
|
||||
@@ -133,17 +133,17 @@ describe('src/cy/commands/querying/within', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('clears withinSubject even if next is null', (done) => {
|
||||
it('clears withinSubjectChain even if next is null', (done) => {
|
||||
const span = cy.$$('#button-text button span')
|
||||
|
||||
// should be defined here because next would have been
|
||||
// null and withinSubject would not have been cleared
|
||||
// null and withinSubjectChain would not have been cleared
|
||||
cy.once('command:queue:before:end', () => {
|
||||
expect(cy.state('withinSubject')).not.to.be.undefined
|
||||
expect(cy.state('withinSubjectChain')).not.to.be.undefined
|
||||
})
|
||||
|
||||
cy.once('command:queue:end', () => {
|
||||
expect(cy.state('withinSubject')).to.be.null
|
||||
expect(cy.state('withinSubjectChain')).to.be.null
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -169,6 +169,16 @@ describe('src/cy/commands/querying/within', () => {
|
||||
cy.contains(`button`, `button`).should(`exist`)
|
||||
})
|
||||
|
||||
it('re-queries if withinSubject is detached from dom', () => {
|
||||
cy.on('command:retry', _.after(2, (options) => {
|
||||
cy.$$('#wrapper').replaceWith('<div id="wrapper"><div id="upper">Newer York</div></div>')
|
||||
}))
|
||||
|
||||
cy.get('#wrapper').within(() => {
|
||||
cy.get(`#upper`).should(`contain.text`, `Newer York`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('.log', () => {
|
||||
beforeEach(function () {
|
||||
this.logs = []
|
||||
@@ -219,7 +229,7 @@ describe('src/cy/commands/querying/within', () => {
|
||||
|
||||
it('provides additional information in console prop', () => {
|
||||
cy.get('div').within(() => {})
|
||||
.then(function () {
|
||||
cy.then(function () {
|
||||
const { lastLog } = this
|
||||
|
||||
const consoleProps = lastLog.get('consoleProps')()
|
||||
@@ -287,7 +297,7 @@ describe('src/cy/commands/querying/within', () => {
|
||||
})
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.within()` failed because this element')
|
||||
expect(err.message).to.include('`cy.within()` failed because it requires a DOM element')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
context('cy.server', () => {
|
||||
it('throws error on use of cy.server', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`cy.server()` was removed in Cypress version 12.0.0. Please update to use `cy.intercept()` instead.')
|
||||
expect(err.docsUrl).to.equal('https://on.cypress.io/intercept')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.server()
|
||||
})
|
||||
})
|
||||
|
||||
context('cy.route', () => {
|
||||
it('throws error on use of cy.route', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`cy.route()` was removed in Cypress version 12.0.0. Please update to use `cy.intercept()` instead.')
|
||||
expect(err.docsUrl).to.equal('https://on.cypress.io/intercept')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.route('/foo')
|
||||
})
|
||||
})
|
||||
|
||||
context('Cypress.server.defaults', () => {
|
||||
it('throws error on use of Cypress.Server.defaults', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`Cypress.Server.defaults()` was removed in Cypress version 12.0.0. Please update to use `cy.intercept()` instead.')
|
||||
expect(err.docsUrl).to.equal('https://on.cypress.io/intercept')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
Cypress.Server.defaults({})
|
||||
})
|
||||
})
|
||||
|
||||
context('Cypress.Cookies.defaults', () => {
|
||||
it('throws error on use of Cookies.defaults()', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`Cypress.Cookies.defaults()` was removed in Cypress version 12.0.0. Please update to use `cy.session()` instead.')
|
||||
expect(err.docsUrl).to.equal('https://on.cypress.io/session')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
Cypress.Cookies.defaults({})
|
||||
})
|
||||
})
|
||||
|
||||
context('Cypress.Cookies.preserveOnce', () => {
|
||||
it('throws error on use of Cookies.preserveOnce', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`Cypress.Cookies.preserveOnce()` was removed in Cypress version 12.0.0. Please update to use `cy.session()` instead.')
|
||||
expect(err.docsUrl).to.equal('https://on.cypress.io/session')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
Cypress.Cookies.preserveOnce({})
|
||||
})
|
||||
})
|
||||
@@ -366,9 +366,9 @@ describe('src/cy/commands/request', () => {
|
||||
},
|
||||
})
|
||||
.then(function () {
|
||||
// qs is encoded and merged into the url to make url consistent with cy.visit() and URLSearchParams
|
||||
this.expectOptionsToBe({
|
||||
url: 'http://localhost:8888/',
|
||||
qs: { foo: 'bar' },
|
||||
url: 'http://localhost:8888/?foo=bar',
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -432,6 +432,53 @@ describe('src/cy/commands/request', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('querystring', () => {
|
||||
// https://github.com/cypress-io/cypress/issues/19407
|
||||
it('generated querystring should be consistent with cy.visit() and URLSearchParams', () => {
|
||||
const url = '/status-code'
|
||||
const qs = {
|
||||
postId: [1, 2],
|
||||
id: 3,
|
||||
}
|
||||
|
||||
let visitURL
|
||||
let requestURL
|
||||
|
||||
cy.intercept(`${url}*`, {
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
}).as('request')
|
||||
|
||||
cy.visit({
|
||||
url,
|
||||
qs,
|
||||
})
|
||||
|
||||
cy.wait('@request').then(({ request }) => {
|
||||
visitURL = request.url
|
||||
}).then(() => {
|
||||
cy.request({
|
||||
url,
|
||||
qs,
|
||||
}).then((resp) => {
|
||||
requestURL = resp.allRequestResponses[0]['Request URL']
|
||||
})
|
||||
}).then(() => {
|
||||
expect(requestURL).to.eq(visitURL)
|
||||
|
||||
// Check if the querystring matches the querystring generated by the default URLSearchParams.
|
||||
const params = new URLSearchParams({
|
||||
postId: [1, 2],
|
||||
id: 3,
|
||||
})
|
||||
|
||||
expect(requestURL).to.eq(`http://localhost:3500${url}?${params.toString()}`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('failOnStatusCode', () => {
|
||||
it('does not fail on status 401', () => {
|
||||
Cypress.backend
|
||||
|
||||
@@ -52,7 +52,7 @@ describe('cy.session', { retries: 0 }, () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('testIsolation=on', { testIsolation: 'on' }, () => {
|
||||
describe('testIsolation=true', { testIsolation: true }, () => {
|
||||
describe('test:before:run:async', () => {
|
||||
it('clears page before each run', () => {
|
||||
cy.visit('/fixtures/form.html')
|
||||
@@ -62,9 +62,10 @@ describe('cy.session', { retries: 0 }, () => {
|
||||
await Cypress.action('runner:test:before:run:async', {})
|
||||
|
||||
expect(Cypress.action).to.be.calledWith('cy:url:changed', '')
|
||||
expect(Cypress.action).to.be.calledWith('cy:visit:blank', { type: 'session-lifecycle' })
|
||||
expect(Cypress.action).to.be.calledWith('cy:visit:blank', { testIsolation: true })
|
||||
})
|
||||
.url('about:blank')
|
||||
.url()
|
||||
.should('eq', 'about:blank')
|
||||
})
|
||||
|
||||
it('clears session data before each run', async () => {
|
||||
@@ -96,7 +97,7 @@ describe('cy.session', { retries: 0 }, () => {
|
||||
await Cypress.action('runner:test:before:run:async', {})
|
||||
|
||||
expect(Cypress.action).to.be.calledWith('cy:url:changed', '')
|
||||
expect(Cypress.action).to.be.calledWith('cy:visit:blank', { type: 'session-lifecycle' })
|
||||
expect(Cypress.action).to.be.calledWith('cy:visit:blank', { testIsolation: true })
|
||||
})
|
||||
|
||||
cy.window().its('cookie').should('be.undefined')
|
||||
@@ -775,7 +776,7 @@ describe('cy.session', { retries: 0 }, () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('testIsolation=off', { testIsolation: 'off' }, () => {
|
||||
describe('testIsolation=false', { testIsolation: false }, () => {
|
||||
before(async () => {
|
||||
// manually ensure clear browser state! since we turned testIsolation off
|
||||
await Cypress.session.clearCurrentSessionData()
|
||||
@@ -1459,50 +1460,6 @@ describe('cy.session', { retries: 0 }, () => {
|
||||
return null
|
||||
})
|
||||
|
||||
it('throws error when experimentalSessionAndOrigin not enabled', { experimentalSessionAndOrigin: false, experimentalSessionSupport: false }, (done) => {
|
||||
cy.once('fail', (err) => {
|
||||
expect(lastSessionLog).to.eq(lastLog)
|
||||
expect(lastSessionLog.get('error')).to.eq(err)
|
||||
expect(lastSessionLog.get('state')).to.eq('failed')
|
||||
expect(err.message).to.eq('`cy.session()` requires enabling the `experimentalSessionAndOrigin` flag.')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/session')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.session('sessions-not-enabled')
|
||||
})
|
||||
|
||||
it('throws error when experimentalSessionSupport is enabled through test config', { experimentalSessionAndOrigin: false, experimentalSessionSupport: true }, (done) => {
|
||||
cy.once('fail', (err) => {
|
||||
expect(lastSessionLog).to.eq(lastLog)
|
||||
expect(lastSessionLog.get('error')).to.eq(err)
|
||||
expect(lastSessionLog.get('state')).to.eq('failed')
|
||||
expect(err.message).to.eq('\`cy.session()\` requires enabling the \`experimentalSessionAndOrigin\` flag. The \`experimentalSessionSupport\` flag was enabled but was removed in Cypress version 9.6.0.')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/session')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.session('sessions-not-enabled')
|
||||
})
|
||||
|
||||
it('throws error when experimentalSessionSupport is enabled through Cypress.config', { experimentalSessionAndOrigin: false }, (done) => {
|
||||
Cypress.config('experimentalSessionSupport', true)
|
||||
|
||||
cy.once('fail', (err) => {
|
||||
Cypress.config('experimentalSessionSupport', false)
|
||||
expect(lastSessionLog).to.eq(lastLog)
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
expect(lastLog.get('state')).to.eq('failed')
|
||||
expect(err.message).to.eq('\`cy.session()\` requires enabling the \`experimentalSessionAndOrigin\` flag. The \`experimentalSessionSupport\` flag was enabled but was removed in Cypress version 9.6.0.')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/session')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.session('sessions-not-enabled')
|
||||
})
|
||||
|
||||
it('throws when sessionId argument was not provided', function (done) {
|
||||
cy.once('fail', (err) => {
|
||||
expect(lastSessionLog).to.eq(lastLog)
|
||||
|
||||
@@ -176,7 +176,7 @@ describe('src/cy/commands/sessions/utils.ts', () => {
|
||||
})
|
||||
|
||||
describe('.navigateAboutBlank', () => {
|
||||
it('triggers session blank page visit', () => {
|
||||
it('triggers test isolation blank page visit', () => {
|
||||
const stub = cy.stub(Cypress, 'action').log(false)
|
||||
.callThrough()
|
||||
.withArgs('cy:visit:blank')
|
||||
@@ -185,19 +185,8 @@ describe('src/cy/commands/sessions/utils.ts', () => {
|
||||
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' })
|
||||
expect(stub.args[0]).to.deep.eq(['cy:visit:blank', { testIsolation: true }])
|
||||
expect(stub.args[1]).to.deep.eq(['cy:visit:blank', { testIsolation: true }])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -246,11 +246,7 @@ describe('src/cy/commands/storage', () => {
|
||||
const clear = cy.spy(Cypress.LocalStorage, 'clear')
|
||||
|
||||
Cypress.emit('test:before:run', {})
|
||||
if (Cypress.config('experimentalSessionAndOrigin')) {
|
||||
expect(clear).not.to.be.called
|
||||
} else {
|
||||
expect(clear).to.be.calledWith([])
|
||||
}
|
||||
expect(clear).not.to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
const { assertLogLength } = require('../../support/utils')
|
||||
const { _, $, dom } = Cypress
|
||||
|
||||
const helpers = require('../../support/helpers')
|
||||
|
||||
describe('src/cy/commands/traversals', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/dom.html')
|
||||
@@ -95,7 +93,7 @@ describe('src/cy/commands/traversals', () => {
|
||||
})
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`\`cy.${name}()\` failed because this element`)
|
||||
expect(err.message).to.include(`\`cy.${name}()\` failed because it requires a DOM element`)
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -108,7 +106,7 @@ describe('src/cy/commands/traversals', () => {
|
||||
node = dom.stringify(cy.$$(node), 'short')
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Expected to find element: \`${el}\`, but never found it. Queried from element: ${node}`)
|
||||
expect(err.message).to.include(`Expected to find element: \`${el}\`, but never found it. Queried from:`)
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -208,7 +206,7 @@ describe('src/cy/commands/traversals', () => {
|
||||
const yielded = Cypress.dom.getElements($el)
|
||||
|
||||
_.extend(obj, {
|
||||
'Applied To': helpers.getFirstSubjectByName('get').get(0),
|
||||
'Applied To': cy.$$('#list')[0],
|
||||
Yielded: yielded,
|
||||
Elements: $el.length,
|
||||
})
|
||||
@@ -324,7 +322,7 @@ describe('src/cy/commands/traversals', () => {
|
||||
|
||||
it('errors after timing out not finding element', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('Expected to find element: `span`, but never found it. Queried from element: <li.item>')
|
||||
expect(err.message).to.include('Expected to find element: `span`, but never found it. Queried from:')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -61,15 +61,14 @@ describe('src/cy/commands/waiting', () => {
|
||||
const response = { foo: 'foo' }
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route('GET', /.*/, response).as('fetch')
|
||||
.intercept('GET', /.*/, response).as('fetch')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/foo')
|
||||
|
||||
return null
|
||||
})
|
||||
.wait('@fetch.response').then((xhr) => {
|
||||
expect(xhr.responseBody).to.deep.eq(response)
|
||||
expect(xhr.response.body).to.deep.eq(response)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -83,11 +82,13 @@ describe('src/cy/commands/waiting', () => {
|
||||
}))
|
||||
|
||||
cy
|
||||
.server({ delay: 1000 })
|
||||
.route(/users/, {}).as('getUsers')
|
||||
.intercept(/users/, {
|
||||
body: {},
|
||||
delay: 1000,
|
||||
}).as('getUsers')
|
||||
.wait('@getUsers.request').then((xhr) => {
|
||||
expect(xhr.url).to.include('/users')
|
||||
expect(xhr.response).to.be.null
|
||||
expect(xhr.request.url).to.include('/users')
|
||||
expect(xhr.response).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
@@ -99,8 +100,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route('GET', /.*/, {}).as('fetch')
|
||||
.intercept('GET', /.*/, {}).as('fetch')
|
||||
.wait('@fetch', { timeout: 900 })
|
||||
})
|
||||
|
||||
@@ -114,8 +114,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
cy.on('command:retry', retry)
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route('GET', /.*/, {}).as('fetch')
|
||||
.intercept('GET', /.*/, {}).as('fetch')
|
||||
.wait('@fetch').then(() => {
|
||||
expect(cy.timeout()).to.eq(prevTimeout)
|
||||
})
|
||||
@@ -131,8 +130,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route('GET', '*', {}).as('fetch')
|
||||
.intercept('GET', '*', {}).as('fetch')
|
||||
.wait('@fetch').then(() => {
|
||||
expect(cy.timeout()).to.eq(199)
|
||||
})
|
||||
@@ -147,8 +145,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route('GET', '*', {}).as('fetch')
|
||||
.intercept('GET', '*', {}).as('fetch')
|
||||
.wait('@fetch', { requestTimeout: 199 })
|
||||
})
|
||||
|
||||
@@ -156,14 +153,18 @@ describe('src/cy/commands/waiting', () => {
|
||||
responseTimeout: 299,
|
||||
}, (done) => {
|
||||
cy.on('command:retry', (options) => {
|
||||
expect(options.timeout).to.eq(299)
|
||||
if (options.type === 'response') {
|
||||
expect(options.timeout).to.eq(299)
|
||||
|
||||
done()
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
cy
|
||||
.server({ delay: 100 })
|
||||
.route('GET', '*', {}).as('fetch')
|
||||
.intercept('GET', '*', {
|
||||
body: {},
|
||||
delay: 100,
|
||||
}).as('fetch')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/foo')
|
||||
|
||||
@@ -174,15 +175,19 @@ describe('src/cy/commands/waiting', () => {
|
||||
|
||||
it('waits for responseTimeout override', (done) => {
|
||||
cy.on('command:retry', (options) => {
|
||||
expect(options.type).to.eq('response')
|
||||
expect(options.timeout).to.eq(299)
|
||||
if (options.type === 'response') {
|
||||
expect(options.type).to.eq('response')
|
||||
expect(options.timeout).to.eq(299)
|
||||
|
||||
done()
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
cy
|
||||
.server({ delay: 100 })
|
||||
.route('GET', '*', {}).as('fetch')
|
||||
.intercept('GET', '*', {
|
||||
body: {},
|
||||
delay: 100,
|
||||
}).as('fetch')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/foo')
|
||||
|
||||
@@ -198,7 +203,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
retryCount++
|
||||
if (retryCount === 1) {
|
||||
expect(options.type).to.eq('request')
|
||||
expect(options.timeout).to.eq(100)
|
||||
expect(options.timeout).to.eq(1000)
|
||||
|
||||
// trigger request to move onto response timeout verification
|
||||
const win = cy.state('window')
|
||||
@@ -206,26 +211,27 @@ describe('src/cy/commands/waiting', () => {
|
||||
xhrGet(win, '/foo')
|
||||
}
|
||||
|
||||
if (retryCount === 2) {
|
||||
if (options.type === 'response') {
|
||||
expect(options.type).to.eq('response')
|
||||
expect(options.timeout).to.eq(299)
|
||||
expect(options.timeout).to.eq(1001)
|
||||
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
cy
|
||||
.server({ delay: 100 })
|
||||
.route('GET', '*', {}).as('fetch')
|
||||
.wait('@fetch', { requestTimeout: 100, responseTimeout: 299 })
|
||||
.intercept('GET', '*', {
|
||||
body: {},
|
||||
delay: 100,
|
||||
}).as('fetch')
|
||||
.wait('@fetch', { requestTimeout: 1000, responseTimeout: 1001 })
|
||||
})
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/369
|
||||
it('does not mutate 2nd route methods when using shorthand route', () => {
|
||||
cy
|
||||
.server()
|
||||
.route('POST', /foo/, {}).as('getFoo')
|
||||
.route(/bar/, {}).as('getBar')
|
||||
.intercept('POST', /foo/, {}).as('getFoo')
|
||||
.intercept(/bar/, {}).as('getBar')
|
||||
.window().then((win) => {
|
||||
win.$.post('/foo')
|
||||
xhrGet(win, '/bar')
|
||||
@@ -269,8 +275,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
done()
|
||||
})
|
||||
|
||||
cy.server()
|
||||
cy.route('GET', /.*/, {}).as('fetch')
|
||||
cy.intercept('GET', /.*/, {}).as('fetch')
|
||||
cy.wait('@fetch')
|
||||
})
|
||||
|
||||
@@ -284,8 +289,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, {}).as('foo')
|
||||
.intercept(/foo/, {}).as('foo')
|
||||
.wait('@foo.request')
|
||||
})
|
||||
|
||||
@@ -297,8 +301,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route('*', {}).as('getAny')
|
||||
.intercept('*', {}).as('getAny')
|
||||
.wait('getAny').then(() => {})
|
||||
})
|
||||
|
||||
@@ -310,8 +313,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, {}).as('foo')
|
||||
.intercept(/foo/, {}).as('foo')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/foo')
|
||||
|
||||
@@ -328,9 +330,8 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, {}).as('foo')
|
||||
.route(/bar/, {}).as('bar')
|
||||
.intercept(/foo/, {}).as('foo')
|
||||
.intercept(/bar/, {}).as('bar')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/foo')
|
||||
|
||||
@@ -348,8 +349,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, {}).as('foo')
|
||||
.intercept(/foo/, {}).as('foo')
|
||||
.get('body').as('bar')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/foo')
|
||||
@@ -376,9 +376,8 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, {}).as('foo')
|
||||
.route(/bar/, {}).as('bar')
|
||||
.intercept(/foo/, {}).as('foo')
|
||||
.intercept(/bar/, {}).as('bar')
|
||||
.wait(['@foo', '@bar'])
|
||||
})
|
||||
|
||||
@@ -400,9 +399,8 @@ describe('src/cy/commands/waiting', () => {
|
||||
}))
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, { foo: 'foo' }).as('foo')
|
||||
.route(/bar/, { bar: 'bar' }).as('bar')
|
||||
.intercept(/foo/, { foo: 'foo' }).as('foo')
|
||||
.intercept(/bar/, { bar: 'bar' }).as('bar')
|
||||
.wait(['@foo', '@bar'])
|
||||
})
|
||||
|
||||
@@ -424,9 +422,8 @@ describe('src/cy/commands/waiting', () => {
|
||||
}))
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, { foo: 'foo' }).as('foo')
|
||||
.route(/bar/, { bar: 'bar' }).as('bar')
|
||||
.intercept(/foo/, { foo: 'foo' }).as('foo')
|
||||
.intercept(/bar/, { bar: 'bar' }).as('bar')
|
||||
.wait(['@foo', '@bar'])
|
||||
})
|
||||
|
||||
@@ -442,9 +439,8 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, {}).as('foo')
|
||||
.route(/bar/, {}).as('bar')
|
||||
.intercept(/foo/, {}).as('foo')
|
||||
.intercept(/bar/, {}).as('bar')
|
||||
.wait(['@foo', 'bar'])
|
||||
})
|
||||
|
||||
@@ -461,8 +457,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, {}).as('foo')
|
||||
.intercept(/foo/, {}).as('foo')
|
||||
.get('body').as('bar')
|
||||
.wait(['@foo', '@bar'])
|
||||
})
|
||||
@@ -484,9 +479,8 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, { foo: 'foo' }).as('foo')
|
||||
.route(/bar/, { bar: 'bar' }).as('bar')
|
||||
.intercept(/foo/, { foo: 'foo' }).as('foo')
|
||||
.intercept(/bar/, { bar: 'bar' }).as('bar')
|
||||
.wait(['@foo', '@bar'])
|
||||
})
|
||||
|
||||
@@ -515,8 +509,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
return xhrGet(win, `/users?num=${response}`)
|
||||
})
|
||||
|
||||
cy.server()
|
||||
cy.route(/users/, resp).as('get.users')
|
||||
cy.intercept(/users/, resp).as('get.users')
|
||||
cy.wait(['@get.users', '@get.users', '@get.users'])
|
||||
})
|
||||
|
||||
@@ -541,8 +534,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
}))
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/users/, resp).as('getUsers')
|
||||
.intercept(/users/, resp).as('getUsers')
|
||||
.wait('@getUsers')
|
||||
.wait('@getUsers')
|
||||
})
|
||||
@@ -568,8 +560,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
}))
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/users/, resp).as('getUsers')
|
||||
.intercept(/users/, resp).as('getUsers')
|
||||
.wait('@getUsers.request')
|
||||
.wait('@getUsers.request')
|
||||
})
|
||||
@@ -584,8 +575,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route('*').as('response')
|
||||
.intercept('*').as('response')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/timeout?ms=500')
|
||||
|
||||
@@ -604,8 +594,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route('*').as('response')
|
||||
.intercept('*').as('response')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/timeout?ms=0')
|
||||
xhrGet(win, '/timeout?ms=5000')
|
||||
@@ -625,9 +614,8 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route('/timeout?ms=0').as('foo')
|
||||
.route('/timeout?ms=5000').as('bar')
|
||||
.intercept('/timeout?ms=0').as('foo')
|
||||
.intercept('/timeout?ms=5000').as('bar')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/timeout?ms=0')
|
||||
xhrGet(win, '/timeout?ms=5000')
|
||||
@@ -655,11 +643,13 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server({ delay: 200 })
|
||||
.route(/users/, {}).as('getUsers')
|
||||
.intercept(/users/, {
|
||||
body: {},
|
||||
delay: 200,
|
||||
}).as('getUsers')
|
||||
.wait('@getUsers.request').then((xhr) => {
|
||||
expect(xhr.url).to.include('/users')
|
||||
expect(xhr.response).to.be.null
|
||||
expect(xhr.request.url).to.include('/users')
|
||||
expect(xhr.response).to.be.undefined
|
||||
})
|
||||
.wait('@getUsers')
|
||||
})
|
||||
@@ -686,10 +676,9 @@ describe('src/cy/commands/waiting', () => {
|
||||
done()
|
||||
})
|
||||
|
||||
cy.server()
|
||||
cy.route('/timeout?ms=2001').as('getOne')
|
||||
cy.route('/timeout?ms=2002').as('getTwo')
|
||||
cy.route(/three/, {}).as('get.three')
|
||||
cy.intercept('/timeout?ms=2001').as('getOne')
|
||||
cy.intercept('/timeout?ms=2002').as('getTwo')
|
||||
cy.intercept(/three/, {}).as('get.three')
|
||||
cy.wait(['@getOne', '@getTwo', '@get.three'])
|
||||
})
|
||||
|
||||
@@ -700,7 +689,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
const win = cy.state('window')
|
||||
|
||||
cy.on('command:retry', (options) => {
|
||||
if (/getThree/.test(options.error)) {
|
||||
if (/getThree/.test(options.error) && options.type === 'response') {
|
||||
options._runnableTimeout = 0
|
||||
}
|
||||
})
|
||||
@@ -712,10 +701,9 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route('/timeout?ms=1').as('getOne')
|
||||
.route('/timeout?ms=2').as('getTwo')
|
||||
.route('/timeout?ms=3000').as('getThree')
|
||||
.intercept('/timeout?ms=1').as('getOne')
|
||||
.intercept('/timeout?ms=2').as('getTwo')
|
||||
.intercept('/timeout?ms=3000').as('getThree')
|
||||
.then(() => {
|
||||
xhrGet(win, '/timeout?ms=1')
|
||||
xhrGet(win, '/timeout?ms=2')
|
||||
@@ -758,9 +746,8 @@ describe('src/cy/commands/waiting', () => {
|
||||
const resp1 = { foo: 'foo' }
|
||||
const resp2 = { bar: 'bar' }
|
||||
|
||||
cy.server()
|
||||
cy.route(/users/, resp1).as('getUsers')
|
||||
cy.route(/posts/, resp2).as('get.posts')
|
||||
cy.intercept(/users/, resp1).as('getUsers')
|
||||
cy.intercept(/posts/, resp2).as('get.posts')
|
||||
cy.window().then((win) => {
|
||||
xhrGet(win, '/users')
|
||||
xhrGet(win, '/posts')
|
||||
@@ -769,8 +756,8 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy.wait(['@getUsers', '@get.posts']).spread((xhr1, xhr2) => {
|
||||
expect(xhr1.responseBody).to.deep.eq(resp1)
|
||||
expect(xhr2.responseBody).to.deep.eq(resp2)
|
||||
expect(xhr1.response.body).to.deep.eq(resp1)
|
||||
expect(xhr2.response.body).to.deep.eq(resp2)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -795,19 +782,18 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/users/, resp).as('getUsers')
|
||||
.intercept(/users/, resp).as('getUsers')
|
||||
.wait('@getUsers').then((xhr) => {
|
||||
expect(xhr.url).to.include('/users?num=1')
|
||||
expect(xhr.responseBody).to.deep.eq(resp)
|
||||
expect(xhr.request.url).to.include('/users?num=1')
|
||||
expect(xhr.response.body).to.deep.eq(resp)
|
||||
})
|
||||
.wait('@getUsers').then((xhr) => {
|
||||
expect(xhr.url).to.include('/users?num=2')
|
||||
expect(xhr.responseBody).to.deep.eq(resp)
|
||||
expect(xhr.request.url).to.include('/users?num=2')
|
||||
expect(xhr.response.body).to.deep.eq(resp)
|
||||
})
|
||||
.wait('@getUsers').then((xhr) => {
|
||||
expect(xhr.url).to.include('/users?num=3')
|
||||
expect(xhr.responseBody).to.deep.eq(resp)
|
||||
expect(xhr.request.url).to.include('/users?num=3')
|
||||
expect(xhr.response.body).to.deep.eq(resp)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -829,17 +815,16 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/users/, resp).as('getUsers')
|
||||
.intercept(/users/, resp).as('getUsers')
|
||||
.wait(['@getUsers', '@getUsers', '@getUsers']).spread((xhr1, xhr2, xhr3) => {
|
||||
expect(xhr1.url).to.include('/users?num=1')
|
||||
expect(xhr1.request.url).to.include('/users?num=1')
|
||||
expect(xhr2.url).to.include('/users?num=2')
|
||||
|
||||
expect(xhr3.url).to.include('/users?num=3')
|
||||
expect(xhr3.request.url).to.include('/users?num=3')
|
||||
}).wait('@getUsers').then((xhr) => {
|
||||
expect(xhr.url).to.include('/users?num=4')
|
||||
expect(xhr.request.url).to.include('/users?num=4')
|
||||
|
||||
expect(xhr.responseBody).to.deep.eq(resp)
|
||||
expect(xhr.response.body).to.deep.eq(resp)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -881,6 +866,10 @@ describe('src/cy/commands/waiting', () => {
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
this.lastLog = log
|
||||
if (log.get('name') === 'wait') {
|
||||
this.lastWaitLog = log
|
||||
}
|
||||
|
||||
this.logs.push(log)
|
||||
})
|
||||
|
||||
@@ -994,8 +983,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
done()
|
||||
})
|
||||
|
||||
cy.server()
|
||||
cy.route(/foo/, {}).as('getFoo')
|
||||
cy.intercept(/foo/, {}).as('getFoo')
|
||||
cy.noop({}).wait('@getFoo')
|
||||
})
|
||||
|
||||
@@ -1014,9 +1002,9 @@ describe('src/cy/commands/waiting', () => {
|
||||
requestTimeout: 100,
|
||||
}, function (done) {
|
||||
cy.on('fail', (err) => {
|
||||
const { lastLog } = this
|
||||
const { lastWaitLog } = this
|
||||
|
||||
expect(lastLog.get('error')).to.eq(err)
|
||||
expect(lastWaitLog.get('error')).to.eq(err)
|
||||
expect(err.message).to.include('`cy.wait()` timed out waiting `100ms` for the 1st request to the route: `getBar`. No request ever occurred.')
|
||||
|
||||
done()
|
||||
@@ -1025,9 +1013,8 @@ describe('src/cy/commands/waiting', () => {
|
||||
cy.visit('/fixtures/empty.html')
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, {}).as('getFoo')
|
||||
.route(/bar/, {}).as('getBar')
|
||||
.intercept(/foo/, {}).as('getFoo')
|
||||
.intercept(/bar/, {}).as('getBar')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/foo')
|
||||
|
||||
@@ -1050,8 +1037,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
|
||||
it('is a parent command', () => {
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, {}).as('getFoo')
|
||||
.intercept(/foo/, {}).as('getFoo')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/foo')
|
||||
|
||||
@@ -1066,9 +1052,8 @@ describe('src/cy/commands/waiting', () => {
|
||||
|
||||
it('passes as array of referencesAlias', () => {
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, {}).as('getFoo')
|
||||
.route(/bar/, {}).as('getBar')
|
||||
.intercept(/foo/, {}).as('getFoo')
|
||||
.intercept(/bar/, {}).as('getBar')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/foo')
|
||||
xhrGet(win, '/bar')
|
||||
@@ -1077,9 +1062,9 @@ describe('src/cy/commands/waiting', () => {
|
||||
return null
|
||||
})
|
||||
.wait(['@getFoo', '@getBar', '@getFoo']).then(function (xhrs) {
|
||||
const { lastLog } = this
|
||||
const { lastWaitLog } = this
|
||||
|
||||
expect(lastLog.get('referencesAlias')).to.deep.eq([
|
||||
expect(lastWaitLog.get('referencesAlias')).to.deep.eq([
|
||||
{
|
||||
name: 'getFoo',
|
||||
cardinal: 1,
|
||||
@@ -1101,15 +1086,14 @@ describe('src/cy/commands/waiting', () => {
|
||||
|
||||
it('#consoleProps waiting on 1 alias', () => {
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, {}).as('getFoo')
|
||||
.intercept(/foo/, {}).as('getFoo')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/foo')
|
||||
|
||||
return null
|
||||
})
|
||||
.wait('@getFoo').then(function (xhr) {
|
||||
expect(this.lastLog.invoke('consoleProps')).to.deep.eq({
|
||||
expect(this.lastWaitLog.invoke('consoleProps')).to.deep.eq({
|
||||
Command: 'wait',
|
||||
'Waited For': 'getFoo',
|
||||
Yielded: xhr,
|
||||
@@ -1119,9 +1103,8 @@ describe('src/cy/commands/waiting', () => {
|
||||
|
||||
it('#consoleProps waiting on multiple aliases', () => {
|
||||
cy
|
||||
.server()
|
||||
.route(/foo/, {}).as('getFoo')
|
||||
.route(/bar/, {}).as('getBar')
|
||||
.intercept(/foo/, {}).as('getFoo')
|
||||
.intercept(/bar/, {}).as('getBar')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/foo')
|
||||
xhrGet(win, '/bar')
|
||||
@@ -1129,7 +1112,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
return null
|
||||
})
|
||||
.wait(['@getFoo', '@getBar']).then(function (xhrs) {
|
||||
expect(this.lastLog.invoke('consoleProps')).to.deep.eq({
|
||||
expect(this.lastWaitLog.invoke('consoleProps')).to.deep.eq({
|
||||
Command: 'wait',
|
||||
'Waited For': 'getFoo, getBar',
|
||||
Yielded: [xhrs[0], xhrs[1]], // explicitly create the array here
|
||||
@@ -1153,8 +1136,7 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route('GET', '*', {}).as('fetch')
|
||||
.intercept('GET', '*', {}).as('fetch')
|
||||
.wait('@fetch')
|
||||
})
|
||||
|
||||
@@ -1166,23 +1148,26 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
cy
|
||||
.server()
|
||||
.route('GET', '*', {}).as('fetch')
|
||||
.intercept('GET', '*', {}).as('fetch')
|
||||
.wait('@fetch', { requestTimeout: 199 })
|
||||
})
|
||||
|
||||
it('sets default responseTimeout', {
|
||||
responseTimeout: 299,
|
||||
}, function (done) {
|
||||
cy.on('command:retry', () => {
|
||||
expect(this.lastLog.get('timeout')).to.eq(299)
|
||||
cy.on('command:retry', (command) => {
|
||||
if (command.type === 'response') {
|
||||
expect(this.lastWaitLog.get('timeout')).to.eq(299)
|
||||
|
||||
done()
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
cy
|
||||
.server({ delay: 100 })
|
||||
.route('GET', '*', {}).as('fetch')
|
||||
.intercept('GET', '*', {
|
||||
body: {},
|
||||
delay: 100,
|
||||
}).as('fetch')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/foo')
|
||||
|
||||
@@ -1192,15 +1177,19 @@ describe('src/cy/commands/waiting', () => {
|
||||
})
|
||||
|
||||
it('sets custom responseTimeout', function (done) {
|
||||
cy.on('command:retry', () => {
|
||||
expect(this.lastLog.get('timeout')).to.eq(299)
|
||||
cy.on('command:retry', (command) => {
|
||||
if (command.type === 'response') {
|
||||
expect(this.lastWaitLog.get('timeout')).to.eq(299)
|
||||
|
||||
done()
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
cy
|
||||
.server({ delay: 100 })
|
||||
.route('GET', '*', {}).as('fetch')
|
||||
.intercept('GET', '*', {
|
||||
body: {},
|
||||
delay: 100,
|
||||
}).as('fetch')
|
||||
.window().then((win) => {
|
||||
xhrGet(win, '/foo')
|
||||
|
||||
@@ -1213,11 +1202,11 @@ describe('src/cy/commands/waiting', () => {
|
||||
let log
|
||||
let retryCount = 0
|
||||
|
||||
cy.on('command:retry', () => {
|
||||
log = log || this.lastLog
|
||||
cy.on('command:retry', (command) => {
|
||||
log = log || this.lastWaitLog
|
||||
retryCount++
|
||||
if (retryCount === 1) {
|
||||
expect(log.get('timeout')).to.eq(100)
|
||||
expect(log.get('timeout')).to.eq(1000)
|
||||
|
||||
// trigger request to move onto response timeout verification
|
||||
const win = cy.state('window')
|
||||
@@ -1225,17 +1214,19 @@ describe('src/cy/commands/waiting', () => {
|
||||
xhrGet(win, '/foo')
|
||||
}
|
||||
|
||||
if (retryCount === 2) {
|
||||
expect(log.get('timeout')).to.eq(299)
|
||||
if (command.type === 'response') {
|
||||
expect(log.get('timeout')).to.eq(1001)
|
||||
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
cy
|
||||
.server({ delay: 100 })
|
||||
.route('GET', '*', {}).as('fetch')
|
||||
.wait('@fetch', { requestTimeout: 100, responseTimeout: 299 })
|
||||
.intercept('GET', '*', {
|
||||
body: {},
|
||||
delay: 100,
|
||||
}).as('fetch')
|
||||
.wait('@fetch', { requestTimeout: 1000, responseTimeout: 1001 })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,6 @@ const createCommand = (props = {}) => {
|
||||
type: 'parent',
|
||||
chainerId: _.uniqueId('ch'),
|
||||
userInvocationStack: '',
|
||||
injected: false,
|
||||
fn () {},
|
||||
}, props))
|
||||
}
|
||||
@@ -25,15 +24,17 @@ const log = (props = {}) => {
|
||||
describe('src/cypress/command_queue', () => {
|
||||
let queue
|
||||
const state = (() => {}) as StateFunc
|
||||
const timeout = () => {}
|
||||
const whenStable = {} as IStability
|
||||
const fail = () => {}
|
||||
const isCy = () => true
|
||||
const clearTimeout = () => {}
|
||||
const setSubjectForChainer = () => {}
|
||||
const stubCy = {
|
||||
timeout: () => {},
|
||||
fail: () => {},
|
||||
isCy: () => true,
|
||||
clearTimeout: () => {},
|
||||
setSubjectForChainer: () => {},
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
queue = new CommandQueue(state, timeout, whenStable, fail, isCy, clearTimeout, setSubjectForChainer)
|
||||
queue = new CommandQueue(state, whenStable, stubCy as any)
|
||||
|
||||
queue.add(createCommand({
|
||||
name: 'get',
|
||||
|
||||
@@ -142,7 +142,7 @@ describe('driver/src/cypress/cy', () => {
|
||||
|
||||
cy.wrap(subject).then(() => {
|
||||
expect(cy.state('subject')).to.equal(subject)
|
||||
expect(Cypress.utils.warning).to.be.calledWith('`cy.state(\'subject\')` has been deprecated and will be removed in a future release. Consider migrating to `cy.currentSubject()` instead.')
|
||||
expect(Cypress.utils.warning).to.be.calledWith('`cy.state(\'subject\')` has been deprecated and will be removed in a future release. Consider migrating to `cy.subject()` instead.')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -300,23 +300,6 @@ describe('driver/src/cypress/cy', () => {
|
||||
cy.c('bar')
|
||||
})
|
||||
|
||||
it('fails when previous subject becomes detached', (done) => {
|
||||
cy.$$('#button').click(function () {
|
||||
return $(this).remove()
|
||||
})
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.parent()` failed because this element is detached from the DOM.')
|
||||
expect(err.message).to.include('<button id="button">button</button>')
|
||||
expect(err.message).to.include('> `cy.click()`')
|
||||
expect(err.docsUrl).to.eq('https://on.cypress.io/element-has-detached-from-dom')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
cy.get('#button').click().parent()
|
||||
})
|
||||
|
||||
it('fails when previous subject isnt window', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.winOnly()` failed because it requires the subject be a global `window` object.')
|
||||
@@ -346,10 +329,9 @@ describe('driver/src/cypress/cy', () => {
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
expect(firstPassed).to.be.true
|
||||
expect(err.message).to.include('`cy.elWinOnly()` failed because it requires a DOM element.')
|
||||
expect(err.message).to.include('`cy.elWinOnly()` failed because it requires a DOM element or window.')
|
||||
expect(err.message).to.include('string')
|
||||
expect(err.message).to.include('> `cy.wrap()`')
|
||||
expect(err.message).to.include('All 2 subject validations failed')
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -416,10 +398,10 @@ describe('driver/src/cypress/cy', () => {
|
||||
return orig(`foo${arg1}`)
|
||||
})
|
||||
|
||||
Cypress.Commands.overwrite('first', (orig, subject) => {
|
||||
subject = $([1, 2])
|
||||
Cypress.Commands.overwrite('each', (orig, subject, cb) => {
|
||||
subject = $([1])
|
||||
|
||||
return orig(subject)
|
||||
return orig(subject, cb)
|
||||
})
|
||||
|
||||
Cypress.Commands.overwrite('noop', function (orig, fn) {
|
||||
@@ -439,8 +421,8 @@ describe('driver/src/cypress/cy', () => {
|
||||
})
|
||||
|
||||
it('can modify child commands', () => {
|
||||
cy.get('li').first().then((el) => {
|
||||
expect(el[0]).to.eq(1)
|
||||
cy.get('li').each((i) => {
|
||||
expect(i).to.eq(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -493,4 +475,95 @@ describe('driver/src/cypress/cy', () => {
|
||||
cy.bar()
|
||||
})
|
||||
})
|
||||
|
||||
context('queries', {
|
||||
defaultCommandTimeout: 30,
|
||||
}, () => {
|
||||
it('throws when queries return a promise', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.promiseQuery()` failed because you returned a promise from a query.\n\nQueries must be synchronous functions that return a function. You cannot invoke commands or return promises inside of them.')
|
||||
done()
|
||||
})
|
||||
|
||||
Cypress.Commands.addQuery('promiseQuery', () => Promise.resolve())
|
||||
cy.promiseQuery()
|
||||
})
|
||||
|
||||
it('throws when a query returns a non-function value', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.badReturnQuery()` failed because you returned a value other than a function from a query.\n\nQueries must be synchronous functions that return a function.\n\nThe returned value was:\n\n > `1`')
|
||||
done()
|
||||
})
|
||||
|
||||
Cypress.Commands.addQuery('badReturnQuery', () => 1)
|
||||
cy.badReturnQuery()
|
||||
})
|
||||
|
||||
it('throws when a command is invoked inside a query', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include('`cy.commandQuery()` failed because you invoked a command inside a query.\n\nQueries must be synchronous functions that return a function. You cannot invoke commands or return promises inside of them.\n\nThe command invoked was:\n\n > `cy.visit()`')
|
||||
done()
|
||||
})
|
||||
|
||||
Cypress.Commands.addQuery('commandQuery', () => cy.visit('/'))
|
||||
cy.commandQuery()
|
||||
})
|
||||
|
||||
it('custom commands that return query chainers retry', () => {
|
||||
Cypress.Commands.add('getButton', () => cy.get('button'))
|
||||
cy.on('command:retry', () => cy.$$('button').first().remove())
|
||||
|
||||
cy.getButton().should('have.length', 23)
|
||||
})
|
||||
|
||||
it('allows queries to use other queries', () => {
|
||||
const logs = []
|
||||
|
||||
cy.on('log:added', (attrs, log) => logs.push(log))
|
||||
|
||||
Cypress.Commands.addQuery('getButtonQuery', () => {
|
||||
cy.now('get', 'body')
|
||||
|
||||
return cy.now('get', 'button')
|
||||
})
|
||||
|
||||
Cypress.Commands.addQuery('queryQuery', () => cy.now('getButtonQuery'))
|
||||
|
||||
cy.queryQuery().should('have.length', 24)
|
||||
cy.then(() => {
|
||||
// Length of 3: getButtonQuery.body (from get), getButtonQuery.button (from get), `should.have.length.24`
|
||||
expect(logs.length).to.eq(3)
|
||||
})
|
||||
})
|
||||
|
||||
it('closes each log as the query completes', (done) => {
|
||||
let getLog
|
||||
|
||||
cy.on('log:added', (attrs, log) => {
|
||||
if (attrs.name === 'get') {
|
||||
getLog = log
|
||||
} else if (attrs.name === 'find') {
|
||||
expect(getLog.get('state')).to.eq('passed')
|
||||
done()
|
||||
}
|
||||
})
|
||||
|
||||
cy.get('body').find('#wrapper')
|
||||
})
|
||||
|
||||
it('ends all messages when query chain fails', (done) => {
|
||||
const logs = []
|
||||
|
||||
cy.on('log:added', (attrs, log) => logs.push(log))
|
||||
|
||||
cy.on('fail', (err) => {
|
||||
const state = logs.map((l) => l.get('state'))
|
||||
|
||||
expect(state).to.eql(['passed', 'passed', 'passed', 'failed'])
|
||||
done()
|
||||
})
|
||||
|
||||
cy.get('body').find('#specific-contains').children().should('have.class', 'active')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -460,61 +460,6 @@ describe('Proxy Logging', () => {
|
||||
))
|
||||
})
|
||||
})
|
||||
|
||||
context('with cy.route()', () => {
|
||||
context('flags', () => {
|
||||
let $XMLHttpRequest
|
||||
|
||||
const testFlagXhr = (expectStatus, expectInterceptions, setupFn) => {
|
||||
return testFlag(expectStatus, expectInterceptions, setupFn, (url) => {
|
||||
const xhr = new $XMLHttpRequest()
|
||||
|
||||
xhr.open('GET', url)
|
||||
xhr.send()
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit('/fixtures/empty.html')
|
||||
cy.window()
|
||||
.then(({ XMLHttpRequest }) => {
|
||||
$XMLHttpRequest = XMLHttpRequest
|
||||
})
|
||||
})
|
||||
|
||||
it('is unflagged when not routed', testFlagXhr(
|
||||
undefined,
|
||||
[],
|
||||
() => {},
|
||||
))
|
||||
|
||||
it('spied flagged as expected', testFlagXhr(
|
||||
undefined,
|
||||
[{
|
||||
command: 'route',
|
||||
alias,
|
||||
type: 'spy',
|
||||
}],
|
||||
() => {
|
||||
cy.server()
|
||||
cy.route(`${url}`).as(alias)
|
||||
},
|
||||
))
|
||||
|
||||
it('stubbed flagged as expected', testFlagXhr(
|
||||
undefined,
|
||||
[{
|
||||
command: 'route',
|
||||
alias,
|
||||
type: 'stub',
|
||||
}],
|
||||
() => {
|
||||
cy.server()
|
||||
cy.route(url, 'stubbed response').as(alias)
|
||||
},
|
||||
))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('Cypress.ProxyLogging', () => {
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import $XHR from '@packages/driver/src/cypress/xml_http_request'
|
||||
|
||||
describe('src/cypress/xml_http_request', () => {
|
||||
let xhr
|
||||
let $xhr
|
||||
|
||||
beforeEach(() => {
|
||||
xhr = {
|
||||
id: '1',
|
||||
url: 'http://example.com',
|
||||
method: 'GET',
|
||||
}
|
||||
|
||||
$xhr = $XHR.create(xhr)
|
||||
})
|
||||
|
||||
context('._getFixtureError', () => {
|
||||
it('returns __error property on response body', () => {
|
||||
$xhr.response = {
|
||||
body: {
|
||||
__error: 'Something went wrong',
|
||||
},
|
||||
}
|
||||
|
||||
const err = $xhr._getFixtureError()
|
||||
|
||||
expect(err).to.equal('Something went wrong')
|
||||
})
|
||||
|
||||
it('returns undefined if response does not exist', () => {
|
||||
const err = $xhr._getFixtureError()
|
||||
|
||||
expect(err).to.be.undefined
|
||||
})
|
||||
|
||||
it('returns undefined if response body does not exist', () => {
|
||||
$xhr.response = {}
|
||||
|
||||
const err = $xhr._getFixtureError()
|
||||
|
||||
expect(err).to.be.undefined
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -104,9 +104,7 @@ describe('basic login', { browser: '!webkit' }, () => {
|
||||
}, {
|
||||
validate: () => {
|
||||
cy.window().then((win) => {
|
||||
const cypressAuthToken = win.sessionStorage.getItem('cypressAuthToken')
|
||||
|
||||
return !!cypressAuthToken
|
||||
expect(win.sessionStorage.getItem('cypressAuthToken')).to.exist
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
@@ -195,6 +195,9 @@ context('cy.origin actions', { browser: '!webkit' }, () => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://localhost:3500\` but the application is at origin \`http://www.foobar.com:3500\`.`)
|
||||
expect(err.message).to.include(`This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.foobar.com:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://www.foobar.com:3500', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})`)
|
||||
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
|
||||
@@ -111,8 +111,8 @@ context('cy.origin connectors', { browser: '!webkit' }, () => {
|
||||
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')
|
||||
expect(consoleProps.Subject[0]).to.have.property('tagName').that.equals('BUTTON')
|
||||
expect(consoleProps.Subject[0]).to.have.property('id').that.equals('button')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -58,7 +58,7 @@ describe('cy.origin cookies', { browser: '!webkit' }, () => {
|
||||
expect(logs[0].get('state')).to.eq('failed')
|
||||
expect(logs[0].get('name')).to.eq('getCookie')
|
||||
expect(logs[0].get('message')).to.eq('foo')
|
||||
expect(err.message).to.eq('Timed out retrying after 100ms: The command was expected to run against origin `http://localhost:3500` but the application is at origin `http://www.foobar.com:3500`.\n\nThis commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.')
|
||||
expect(err.message).to.include('Timed out retrying after 100ms: The command was expected to run against origin `http://localhost:3500` but the application is at origin `http://www.foobar.com:3500`.\n\nThis commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.')
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -73,7 +73,7 @@ describe('cy.origin cookies', { browser: '!webkit' }, () => {
|
||||
expect(logs[0].get('state')).to.eq('failed')
|
||||
expect(logs[0].get('name')).to.eq('getCookies')
|
||||
expect(logs[0].get('message')).to.eq('')
|
||||
expect(err.message).to.eq('Timed out retrying after 100ms: The command was expected to run against origin `http://localhost:3500` but the application is at origin `http://www.foobar.com:3500`.\n\nThis commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.')
|
||||
expect(err.message).to.include('Timed out retrying after 100ms: The command was expected to run against origin `http://localhost:3500` but the application is at origin `http://www.foobar.com:3500`.\n\nThis commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.')
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -88,7 +88,7 @@ describe('cy.origin cookies', { browser: '!webkit' }, () => {
|
||||
expect(logs[0].get('state')).to.eq('failed')
|
||||
expect(logs[0].get('name')).to.eq('setCookie')
|
||||
expect(logs[0].get('message')).to.eq('foo, bar')
|
||||
expect(err.message).to.eq('Timed out retrying after 100ms: The command was expected to run against origin `http://localhost:3500` but the application is at origin `http://www.foobar.com:3500`.\n\nThis commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.')
|
||||
expect(err.message).to.include('Timed out retrying after 100ms: The command was expected to run against origin `http://localhost:3500` but the application is at origin `http://www.foobar.com:3500`.\n\nThis commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.')
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -103,7 +103,7 @@ describe('cy.origin cookies', { browser: '!webkit' }, () => {
|
||||
expect(logs[0].get('state')).to.eq('failed')
|
||||
expect(logs[0].get('name')).to.eq('clearCookie')
|
||||
expect(logs[0].get('message')).to.eq('foo')
|
||||
expect(err.message).to.eq('Timed out retrying after 100ms: The command was expected to run against origin `http://localhost:3500` but the application is at origin `http://www.foobar.com:3500`.\n\nThis commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.')
|
||||
expect(err.message).to.include('Timed out retrying after 100ms: The command was expected to run against origin `http://localhost:3500` but the application is at origin `http://www.foobar.com:3500`.\n\nThis commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.')
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -118,7 +118,7 @@ describe('cy.origin cookies', { browser: '!webkit' }, () => {
|
||||
expect(logs[0].get('state')).to.eq('failed')
|
||||
expect(logs[0].get('name')).to.eq('clearCookies')
|
||||
expect(logs[0].get('message')).to.eq('')
|
||||
expect(err.message).to.eq('Timed out retrying after 100ms: The command was expected to run against origin `http://localhost:3500` but the application is at origin `http://www.foobar.com:3500`.\n\nThis commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.')
|
||||
expect(err.message).to.include('Timed out retrying after 100ms: The command was expected to run against origin `http://localhost:3500` but the application is at origin `http://www.foobar.com:3500`.\n\nThis commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.')
|
||||
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -210,16 +210,16 @@ it('verifies number of cy commands', () => {
|
||||
// remove custom commands we added for our own testing
|
||||
const customCommands = ['getAll', 'shouldWithTimeout', 'originLoadUtils']
|
||||
// @ts-ignore
|
||||
const actualCommands = Cypress._.reject(Object.keys(cy.commandFns), (command) => customCommands.includes(command))
|
||||
const actualCommands = Cypress._.pullAll([...Object.keys(cy.commandFns), ...Object.keys(cy.queryFns)], customCommands)
|
||||
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',
|
||||
'selectFile', 'submit', 'type', 'clear', 'trigger', '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',
|
||||
'focused', 'get', 'contains', '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',
|
||||
'mount', 'getAllLocalStorage', 'clearAllLocalStorage', 'getAllSessionStorage', 'clearAllSessionStorage',
|
||||
'mount', 'as', 'root', 'getAllLocalStorage', 'clearAllLocalStorage', 'getAllSessionStorage', 'clearAllSessionStorage',
|
||||
]
|
||||
const addedCommands = Cypress._.difference(actualCommands, expectedCommands)
|
||||
const removedCommands = Cypress._.difference(expectedCommands, actualCommands)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
const { stripIndent } = require('common-tags')
|
||||
import { findCrossOriginLogs } from '../../../../support/utils'
|
||||
|
||||
context('cy.origin navigation', { browser: '!webkit' }, () => {
|
||||
@@ -195,35 +194,6 @@ context('cy.origin navigation', { browser: '!webkit' }, () => {
|
||||
})
|
||||
})
|
||||
|
||||
// @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/primary-origin.html')\`
|
||||
\`<commands targeting http://localhost:3500 go here>\`\n
|
||||
\`cy.origin('http://www.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/primary-origin.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/primary-origin.html')
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ context('cy.origin querying', { browser: '!webkit' }, () => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://localhost:3500\` but the application is at origin \`http://www.foobar.com:3500\`.`)
|
||||
expect(err.message).to.include(`This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.foobar.com:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://www.foobar.com:3500', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})`)
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
@@ -52,6 +54,8 @@ context('cy.origin querying', { browser: '!webkit' }, () => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://localhost:3500\` but the application is at origin \`http://www.foobar.com:3500\`.`)
|
||||
expect(err.message).to.include(`This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.foobar.com:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://www.foobar.com:3500', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})`)
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
@@ -66,6 +70,8 @@ context('cy.origin querying', { browser: '!webkit' }, () => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://localhost:3500\` but the application is at origin \`http://www.foobar.com:3500\`.`)
|
||||
expect(err.message).to.include(`This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.foobar.com:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://www.foobar.com:3500', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})`)
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
@@ -82,6 +88,8 @@ context('cy.origin querying', { browser: '!webkit' }, () => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://localhost:3500\` but the application is at origin \`http://www.foobar.com:3500\`.`)
|
||||
expect(err.message).to.include(`This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.foobar.com:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://www.foobar.com:3500', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})`)
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
@@ -96,6 +104,8 @@ context('cy.origin querying', { browser: '!webkit' }, () => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://localhost:3500\` but the application is at origin \`http://www.foobar.com:3500\`.`)
|
||||
expect(err.message).to.include(`This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.foobar.com:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://www.foobar.com:3500', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})`)
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
@@ -110,6 +120,8 @@ context('cy.origin querying', { browser: '!webkit' }, () => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://localhost:3500\` but the application is at origin \`http://www.foobar.com:3500\`.`)
|
||||
expect(err.message).to.include(`This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.foobar.com:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://www.foobar.com:3500', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})`)
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
@@ -146,7 +158,7 @@ context('cy.origin querying', { browser: '!webkit' }, () => {
|
||||
const { consoleProps } = findCrossOriginLogs('contains', logs, 'foobar.com')
|
||||
|
||||
expect(consoleProps.Command).to.equal('contains')
|
||||
expect(consoleProps['Applied To']).to.be.undefined
|
||||
expect(consoleProps['Applied To']).to.have.property('tagName').that.equals('BODY')
|
||||
expect(consoleProps.Elements).to.equal(1)
|
||||
expect(consoleProps.Content).to.equal('Nested Find')
|
||||
expect(consoleProps.Yielded).to.have.property('tagName').that.equals('DIV')
|
||||
|
||||
@@ -4,30 +4,6 @@ context('cy.origin unsupported commands', { browser: '!webkit' }, () => {
|
||||
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://www.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://www.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:')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { makeRequestForCookieBehaviorTests as makeRequest } from '../../../support/utils'
|
||||
|
||||
describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!webkit' }, () => {
|
||||
describe('Cookie Behavior', { browser: '!webkit' }, () => {
|
||||
const serverConfig = {
|
||||
http: {
|
||||
sameOriginPort: 3500,
|
||||
@@ -249,7 +249,7 @@ describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!
|
||||
// firefox actually sets the cookie correctly
|
||||
cy.getCookie('foo1').its('value').should('equal', 'bar1')
|
||||
} else {
|
||||
cy.getCookie('foo1').its('value').should('equal', null)
|
||||
cy.getCookie('foo1').should('equal', null)
|
||||
}
|
||||
|
||||
// FIXME: Ideally, browser should have access to this cookie. Should be fixed in https://github.com/cypress-io/cypress/pull/23643.
|
||||
@@ -296,7 +296,7 @@ describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!
|
||||
// firefox actually sets the cookie correctly
|
||||
cy.getCookie('foo1').its('value').should('equal', 'bar1')
|
||||
} else {
|
||||
cy.getCookie('foo1').its('value').should('equal', null)
|
||||
cy.getCookie('foo1').should('equal', null)
|
||||
}
|
||||
|
||||
// FIXME: Ideally, browser should have access to this cookie. Should be fixed in https://github.com/cypress-io/cypress/pull/23643.
|
||||
@@ -374,7 +374,7 @@ describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!
|
||||
// firefox actually sets the cookie correctly
|
||||
cy.getCookie('foo1').its('value').should('equal', 'bar1')
|
||||
} else {
|
||||
cy.getCookie('foo1').its('value').should('equal', null)
|
||||
cy.getCookie('foo1').should('equal', null)
|
||||
}
|
||||
|
||||
// FIXME: Ideally, browser should have access to this cookie. Should be fixed in https://github.com/cypress-io/cypress/pull/23643.
|
||||
@@ -416,7 +416,7 @@ describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!
|
||||
// firefox actually sets the cookie correctly
|
||||
cy.getCookie('foo1').its('value').should('equal', 'bar1')
|
||||
} else {
|
||||
cy.getCookie('foo1').its('value').should('equal', null)
|
||||
cy.getCookie('foo1').should('equal', null)
|
||||
}
|
||||
|
||||
// FIXME: Ideally, browser should have access to this cookie. Should be fixed in https://github.com/cypress-io/cypress/pull/23643.
|
||||
@@ -525,13 +525,13 @@ describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!
|
||||
// assert cookie value is actually set in the browser
|
||||
if (scheme === 'https') {
|
||||
// FIXME: cy.getCookie does not believe this cookie exists. Should be fixed in https://github.com/cypress-io/cypress/pull/23643.
|
||||
cy.getCookie('bar1').its('value').should('equal', null)
|
||||
cy.getCookie('bar1').should('equal', null)
|
||||
// can only set third-party SameSite=None with Secure attribute, which is only possibly over https
|
||||
|
||||
//expected future assertion
|
||||
// cy.getCookie('bar1').its('value').should('equal', 'baz1')
|
||||
} else {
|
||||
cy.getCookie('bar1').its('value').should('equal', null)
|
||||
cy.getCookie('bar1').should('equal', null)
|
||||
}
|
||||
|
||||
cy.window().then((win) => {
|
||||
@@ -566,7 +566,7 @@ describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!
|
||||
})
|
||||
|
||||
// FIXME: cy.getCookie does not believe this cookie exists. Should be fixed in https://github.com/cypress-io/cypress/pull/23643.
|
||||
cy.getCookie('bar1').its('value').should('equal', null)
|
||||
cy.getCookie('bar1').should('equal', null)
|
||||
// can only set third-party SameSite=None with Secure attribute, which is only possibly over https
|
||||
|
||||
//expected future assertion
|
||||
@@ -608,7 +608,7 @@ describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!
|
||||
return cy.wrap(makeRequest(win, `${scheme}://www.barbaz.com:${sameOriginPort}/set-cookie?cookie=bar1=baz1; Domain=barbaz.com`, 'fetch', credentialOption as 'same-origin' | 'omit'))
|
||||
})
|
||||
|
||||
cy.getCookie('bar1').its('value').should('equal', null)
|
||||
cy.getCookie('bar1').should('equal', null)
|
||||
|
||||
cy.window().then((win) => {
|
||||
return cy.wrap(makeRequest(win, `${scheme}://www.barbaz.com:${sameOriginPort}/test-request`, 'fetch', credentialOption as 'same-origin' | 'omit'))
|
||||
@@ -645,13 +645,13 @@ describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!
|
||||
// assert cookie value is actually set in the browser
|
||||
if (scheme === 'https') {
|
||||
// FIXME: cy.getCookie does not believe this cookie exists. Should be fixed in https://github.com/cypress-io/cypress/pull/23643.
|
||||
cy.getCookie('bar1').its('value').should('equal', null)
|
||||
cy.getCookie('bar1').should('equal', null)
|
||||
// can only set third-party SameSite=None with Secure attribute, which is only possibly over https
|
||||
|
||||
//expected future assertion
|
||||
// cy.getCookie('bar1').its('value').should('equal', 'baz1')
|
||||
} else {
|
||||
cy.getCookie('bar1').its('value').should('equal', null)
|
||||
cy.getCookie('bar1').should('equal', null)
|
||||
}
|
||||
|
||||
cy.window().then((win) => {
|
||||
@@ -691,7 +691,7 @@ describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!
|
||||
// assert cookie value is actually set in the browser
|
||||
|
||||
// FIXME: cy.getCookie does not believe this cookie exists, though it is set in the browser. Should be fixed in https://github.com/cypress-io/cypress/pull/23643.
|
||||
cy.getCookie('bar1').its('value').should('equal', null)
|
||||
cy.getCookie('bar1').should('equal', null)
|
||||
// can only set third-party SameSite=None with Secure attribute, which is only possibly over https
|
||||
|
||||
//expected future assertion
|
||||
@@ -861,7 +861,6 @@ describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!
|
||||
})
|
||||
|
||||
// without cy.origin means the AUT has the same origin as top
|
||||
// TODO: In the future, this test should be run with the experimentalSessionAndOrigin=true and experimentalSessionAndOrigin=false
|
||||
describe('w/o cy.origin', () => {
|
||||
describe('same site / same origin', () => {
|
||||
describe('XMLHttpRequest', () => {
|
||||
@@ -1189,13 +1188,13 @@ describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!
|
||||
// assert cookie value is actually set in the browser
|
||||
if (scheme === 'https') {
|
||||
// FIXME: cy.getCookie does not believe this cookie exists, though it is set in the browser. Should be fixed in https://github.com/cypress-io/cypress/pull/23643.
|
||||
cy.getCookie('bar1').its('value').should('equal', null)
|
||||
cy.getCookie('bar1').should('equal', null)
|
||||
// can only set third-party SameSite=None with Secure attribute, which is only possibly over https
|
||||
|
||||
//expected future assertion
|
||||
// cy.getCookie('bar1').its('value').should('equal', 'baz1')
|
||||
} else {
|
||||
cy.getCookie('bar1').its('value').should('equal', null)
|
||||
cy.getCookie('bar1').should('equal', null)
|
||||
}
|
||||
|
||||
cy.window().then((win) => {
|
||||
@@ -1220,7 +1219,7 @@ describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!
|
||||
})
|
||||
|
||||
// FIXME: cy.getCookie does not believe this cookie exists, though it is set in the browser. Should be fixed in https://github.com/cypress-io/cypress/pull/23643
|
||||
cy.getCookie('bar1').its('value').should('equal', null)
|
||||
cy.getCookie('bar1').should('equal', null)
|
||||
// can only set third-party SameSite=None with Secure attribute, which is only possibly over https
|
||||
|
||||
//expected future assertion
|
||||
@@ -1251,7 +1250,7 @@ describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!
|
||||
return cy.wrap(makeRequest(win, `${scheme}://www.barbaz.com:${sameOriginPort}/set-cookie?cookie=bar1=baz1; Domain=barbaz.com`, 'fetch', credentialOption as 'same-origin' | 'omit'))
|
||||
})
|
||||
|
||||
cy.getCookie('bar1').its('value').should('equal', null)
|
||||
cy.getCookie('bar1').should('equal', null)
|
||||
|
||||
cy.window().then((win) => {
|
||||
return cy.wrap(makeRequest(win, `${scheme}://www.barbaz.com:${sameOriginPort}/test-request`, 'fetch', credentialOption as 'same-origin' | 'omit'))
|
||||
@@ -1277,13 +1276,13 @@ describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!
|
||||
// assert cookie value is actually set in the browser
|
||||
if (scheme === 'https') {
|
||||
// FIXME: cy.getCookie does not believe this cookie exists, though it is set in the browser. Should be fixed in https://github.com/cypress-io/cypress/pull/23643
|
||||
cy.getCookie('bar1').its('value').should('equal', null)
|
||||
cy.getCookie('bar1').should('equal', null)
|
||||
// can only set third-party SameSite=None with Secure attribute, which is only possibly over https
|
||||
|
||||
//expected future assertion
|
||||
// cy.getCookie('bar1').its('value').should('equal', 'baz1')
|
||||
} else {
|
||||
cy.getCookie('bar1').its('value').should('equal', null)
|
||||
cy.getCookie('bar1').should('equal', null)
|
||||
}
|
||||
|
||||
cy.window().then((win) => {
|
||||
@@ -1313,7 +1312,7 @@ describe('Cookie Behavior with experimentalSessionAndOrigin=true', { browser: '!
|
||||
// assert cookie value is actually set in the browser
|
||||
|
||||
// FIXME: cy.getCookie does not believe this cookie exists, though it is set in the browser. Should be fixed in https://github.com/cypress-io/cypress/pull/23643
|
||||
cy.getCookie('bar1').its('value').should('equal', null)
|
||||
cy.getCookie('bar1').should('equal', null)
|
||||
// can only set third-party SameSite=None with Secure attribute, which is only possibly over https
|
||||
|
||||
//expected future assertion
|
||||
|
||||
@@ -204,30 +204,6 @@ describe('cy.origin Cypress API', { browser: '!webkit' }, () => {
|
||||
})
|
||||
|
||||
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://www.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://www.foobar.com:3500', () => {
|
||||
Cypress.Cookies.preserveOnce('')
|
||||
})
|
||||
})
|
||||
|
||||
it('throws an error when a user attempts to call Cypress.session.clearAllSavedSessions() inside of cy.origin', (done) => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.equal('`Cypress.session.*` methods are not supported in the `cy.origin()` callback. Consider using them outside of the callback instead.')
|
||||
|
||||
@@ -2,7 +2,6 @@ import CryptoJS from 'crypto-js'
|
||||
import type { TemplateExecutor } from 'lodash'
|
||||
|
||||
// NOTE: in order to run these tests, the following config flags need to be set
|
||||
// experimentalSessionAndOrigin=true
|
||||
// experimentalModifyObstructiveThirdPartyCode=true
|
||||
describe('Integrity Preservation', { browser: '!webkit' }, () => {
|
||||
// Add common SRI hashes used when setting script/link integrity.
|
||||
|
||||
@@ -30,8 +30,7 @@ describe('cy.origin logging', { browser: '!webkit' }, () => {
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: fix flaky test https://github.com/cypress-io/cypress/issues/21300
|
||||
it.skip('logs cy.origin as group when failing with validation failure', () => {
|
||||
it('logs cy.origin as group when failing with validation failure', () => {
|
||||
const logs: any[] = []
|
||||
|
||||
cy.on('log:added', (attrs) => {
|
||||
@@ -51,8 +50,7 @@ describe('cy.origin logging', { browser: '!webkit' }, () => {
|
||||
cy.origin(false, () => {})
|
||||
})
|
||||
|
||||
// TODO: fix flaky test https://github.com/cypress-io/cypress/issues/21300
|
||||
it.skip('logs cy.origin as group when failing with serialization failure', () => {
|
||||
it('logs cy.origin as group when failing with serialization failure', () => {
|
||||
const logs: any[] = []
|
||||
|
||||
cy.on('log:added', (attrs) => {
|
||||
|
||||
@@ -251,6 +251,8 @@ describe('errors', { browser: '!webkit' }, () => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://localhost:3500\` but the application is at origin \`http://www.foobar.com:3500\`.`)
|
||||
expect(err.message).to.include(`This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.foobar.com:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://www.foobar.com:3500', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})`)
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
@@ -267,6 +269,8 @@ describe('errors', { browser: '!webkit' }, () => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://www.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.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://localhost:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://localhost:3500', () => {\`\n\` <commands targeting http://localhost:3500 go here>\`\n\`})`)
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
@@ -290,6 +294,8 @@ describe('errors', { browser: '!webkit' }, () => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://www.idp.com:3500\` but the application is at origin \`http://www.foobar.com:3500\`.`)
|
||||
expect(err.message).to.include(`This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.foobar.com:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://www.foobar.com:3500', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})`)
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
@@ -313,6 +319,8 @@ describe('errors', { browser: '!webkit' }, () => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://localhost:3500\` but the application is at origin \`http://www.idp.com:3500\`.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.idp.com:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://www.idp.com:3500', () => {\`\n\` <commands targeting http://www.idp.com:3500 go here>\`\n\`})`)
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
@@ -335,6 +343,8 @@ describe('errors', { browser: '!webkit' }, () => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://localhost:3500\` but the application is at origin \`http://www.foobar.com:3500\`.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.foobar.com:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://www.foobar.com:3500', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})`)
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
@@ -360,6 +370,8 @@ describe('errors', { browser: '!webkit' }, () => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://www.idp.com:3500\` but the application is at origin \`http://www.foobar.com:3500\`.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.foobar.com:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://www.foobar.com:3500', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})`)
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
@@ -388,6 +400,8 @@ describe('errors', { browser: '!webkit' }, () => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://www.idp.com:3500\` but the application is at origin \`http://localhost:3500\`.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://localhost:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://localhost:3500', () => {\`\n\` <commands targeting http://localhost:3500 go here>\`\n\`})`)
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
@@ -432,6 +446,8 @@ describe('errors', { browser: '!webkit' }, () => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://localhost:3500\` but the application is at origin \`http://www.foobar.com:3500\`.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.foobar.com:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://www.foobar.com:3500', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})`)
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
@@ -479,6 +495,8 @@ describe('errors', { browser: '!webkit' }, () => {
|
||||
cy.on('fail', (err) => {
|
||||
expect(err.message).to.include(`Timed out retrying after 50ms:`)
|
||||
expect(err.message).to.include(`The command was expected to run against origin \`http://localhost:3500\` but the application is at origin \`http://www.foobar.com:3500\`.`)
|
||||
expect(err.message).to.include(`Using \`cy.origin()\` to wrap the commands run on \`http://www.foobar.com:3500\` will likely fix this issue.`)
|
||||
expect(err.message).to.include(`cy.origin('http://www.foobar.com:3500', () => {\`\n\` <commands targeting http://www.foobar.com:3500 go here>\`\n\`})`)
|
||||
// make sure that the secondary origin failures do NOT show up as spec failures or AUT failures
|
||||
expect(err.message).not.to.include(`The following error originated from your test code, not from Cypress`)
|
||||
expect(err.message).not.to.include(`The following error originated from your application code, not from Cypress`)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user