chore(v12): merge develop into release/12.0.0 (#24705)

Co-authored-by: amehta265 <65267668+amehta265@users.noreply.github.com>
Co-authored-by: Bill Glesias <bglesias@gmail.com>
Co-authored-by: mattvCypress <mattv@cypress.io>
Co-authored-by: Ryan Manuel <ryanm@cypress.io>
Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com>
Co-authored-by: Jordan <jordan@jpdesigning.com>
Co-authored-by: Adam Stone <adams@cypress.io>
This commit is contained in:
Emily Rohrbough
2022-11-18 16:02:31 -06:00
committed by GitHub
parent 4db85d690e
commit 79ea453764
201 changed files with 1561 additions and 1135 deletions

View File

@@ -1,119 +0,0 @@
# unignore hidden files
!.*
**/__snapshots__
**/_test-output
**/build
**/cypress/fixtures
**/dist
**/dist-*
**/node_modules
**/support/fixtures/*
!**/support/fixtures/projects
**/support/fixtures/projects/**/_fixtures/*
**/support/fixtures/projects/**/static/*
**/support/fixtures/projects/**/*.jsx
**/support/fixtures/projects/**/fail.js
system-tests/fixtures/*
!system-tests/projects
system-tests/projects/**/_fixtures/*
system-tests/projects/**/static/*
system-tests/projects/**/*.jsx
system-tests/projects/**/fail.js
system-tests/lib/scaffold/plugins/index.js
system-tests/lib/scaffold/support/e2e.js
system-tests/lib/scaffold/support/component.js
system-tests/lib/scaffold/support/commands.js
system-tests/test/support/projects/e2e/cypress/
system-tests/projects/e2e/cypress/e2e/stdout_exit_early_failing.cy.js
system-tests/projects/e2e/cypress/e2e/typescript_syntax_error.cy.ts
system-tests/projects/config-with-ts-syntax-error/**
system-tests/projects/config-with-ts-module-error/**
system-tests/projects/no-specs-vue-2/**
**/test/fixtures
**/vendor
# cli/types is linted by tslint/dtslint
cli/types
# cli/react, cli/vue, and cli/mount-utils are all copied from dist'd builds
cli/react
cli/vue
cli/vue2
cli/mount-utils
# packages/example is not linted (think about changing this)
packages/example
packages/extension/test/helpers/background.js
e2e/stdout_exit_early_failing_spec.js
npm/webpack-preprocessor/cypress/tests/e2e/compile-error.js
npm/webpack-preprocessor/examples/use-babelrc/cypress/e2e/spec.cy.js
npm/cypress-schematic/src/**/*.js
**/.projects
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/.vscode
**/.history
**/.cy
**/.git
/npm/react/bin/*
/npm/react/**/coverage
**/.next/**
/npm/create-cypress-tests/initial-template
/npm/create-cypress-tests/**/*.template.*
# The global eslint configuration is not set up to parse vue@2 files
/npm/vue2/**/*.vue
npm/grep/cypress
packages/data-context/test/unit/codegen/files
packages/config/test/__fixtures__/**/*
packages/config/test/__babel_fixtures__/**/*
# community templates we test against, no need to lint
system-tests/projects/cra-4/**/*
system-tests/projects/cra-5/**/*
system-tests/projects/cra-ejected/**/*
system-tests/projects/create-react-app-configured/**/*
system-tests/projects/create-react-app-unconfigured/**/*
system-tests/projects/create-react-app-custom-index-html
system-tests/projects/vueclivue2-unconfigured/**/*
system-tests/projects/vueclivue2-configured/**/*
system-tests/projects/outdated-deps-vuecli3/**/*
system-tests/projects/vueclivue3-unconfigured/**/*
system-tests/projects/vueclivue3-configured/**/*
system-tests/projects/vueclivue3-custom-index-html
system-tests/projects/vuecli5vue3-unconfigured/**/*
system-tests/projects/vuecli5vue3-configured/**/*
system-tests/projects/vue3-vite-ts-unconfigured/**/*
system-tests/projects/vue3-vite-ts-configured/**/*
system-tests/projects/vue3-vite-ts-custom-index-html
system-tests/projects/nextjs-unconfigured/**/*
system-tests/projects/nextjs-configured/**/*
system-tests/projects/nuxtjs-vue2-unconfigured/**/*
system-tests/projects/nuxtjs-vue2-configured/**/*
system-tests/projects/react-app-webpack-5-unconfigured/**/*
system-tests/project-fixtures/**
system-tests/projects/**/*/expected-cypress*/**/*
# These are generated files that are not linted
tooling/electron-mksnapshot/bin/**
tooling/v8-snapshot/cache/**

View File

@@ -10,8 +10,8 @@ body:
- type: textarea
id: dashboard
attributes:
label: Link to dashboard or CircleCI failure
description: Please include a link to the failure in the Cypress Dashboard or in CircleCI.
label: Link to Cypress Cloud or CircleCI failure
description: Please include a link to the failure in Cypress Cloud or in CircleCI.
validations:
required: true
- type: textarea

View File

@@ -238,7 +238,7 @@ If there are errors building the packages, prefix the commands with `DEBUG=cypre
When running `yarn start` this routes through the CLI and eventually calls `yarn dev` with the proper arguments. This enables Cypress day-to-day development to match the logic of the built binary + CLI integration.
If you want to bypass the CLI entirely, you can use the `yarn dev` task and pass arguments directly. For example, to headlessly run a project in a given folder, while trying to record to the Dashboard
If you want to bypass the CLI entirely, you can use the `yarn dev` task and pass arguments directly. For example, to headlessly run a project in a given folder, while trying to record to Cypress Cloud.
```text
yarn dev --run-project /project/folder --record --key <key>

14
cli/.eslintignore Normal file
View File

@@ -0,0 +1,14 @@
**/__snapshots__
/build
# cli/types is linted by tslint/dtslint
/types
# these are all copied from dist'd builds from the individual libs
/angular
/react
/react18
/vue
/vue2
/svelte
/mount-utils

View File

@@ -1,37 +1,37 @@
exports['package.json build outputs expected properties 1'] = {
"name": "test",
"engines": "test engines",
"version": "x.y.z",
"buildInfo": "replaced by normalizePackageJson",
"description": "Cypress is a next generation front end testing tool built for the modern web",
"homepage": "https://github.com/cypress-io/cypress",
"license": "MIT",
"bugs": {
"url": "https://github.com/cypress-io/cypress/issues"
'name': 'test',
'engines': 'test engines',
'version': 'x.y.z',
'buildInfo': 'replaced by normalizePackageJson',
'description': 'Cypress is a next generation front end testing tool built for the modern web',
'homepage': 'https://github.com/cypress-io/cypress',
'license': 'MIT',
'bugs': {
'url': 'https://github.com/cypress-io/cypress/issues',
},
"repository": {
"type": "git",
"url": "https://github.com/cypress-io/cypress.git"
'repository': {
'type': 'git',
'url': 'https://github.com/cypress-io/cypress.git',
},
"keywords": [
"automation",
"browser",
"cypress",
"cypress.io",
"e2e",
"end-to-end",
"integration",
"component",
"mocks",
"runner",
"spies",
"stubs",
"test",
"testing"
'keywords': [
'automation',
'browser',
'cypress',
'cypress.io',
'e2e',
'end-to-end',
'integration',
'component',
'mocks',
'runner',
'spies',
'stubs',
'test',
'testing',
],
"types": "types",
"scripts": {
"postinstall": "node index.js --exec install",
"size": "t=\"$(npm pack .)\"; wc -c \"${t}\"; tar tvf \"${t}\"; rm \"${t}\";"
}
'types': 'types',
'scripts': {
'postinstall': 'node index.js --exec install',
'size': 't="$(npm pack .)"; wc -c "${t}"; tar tvf "${t}"; rm "${t}";',
},
}

View File

@@ -74,7 +74,7 @@ exports['shows help for run --foo 1'] = `
-C, --config-file <config-file> path to script file where configuration values are set. defaults to "cypress.config.{js,ts,mjs,cjs}".
--e2e runs end to end tests
-e, --env <env> sets environment variables. separate multiple values with a comma. overrides any value in cypress.config.{js,ts,mjs,cjs} or cypress.env.json
--group <name> a named group for recorded runs in the Cypress Dashboard
--group <name> a named group for recorded runs in Cypress Cloud
-k, --key <record-key> your secret Record Key. you can omit this if you set a CYPRESS_RECORD_KEY environment variable.
--headed displays the browser instead of running headlessly
--headless hide the browser instead of running headed (default for cypress run)
@@ -83,11 +83,11 @@ exports['shows help for run --foo 1'] = `
-p, --port <port> runs Cypress on a specific port. overrides any value in cypress.config.{js,ts,mjs,cjs}.
-P, --project <project-path> path to the project
-q, --quiet run quietly, using only the configured reporter
--record [bool] records the run. sends test results, screenshots and videos to your Cypress Dashboard.
--record [bool] records the run. sends test results, screenshots and videos to Cypress Cloud.
-r, --reporter <reporter> runs a specific mocha reporter. pass a path to use a custom reporter. defaults to "spec"
-o, --reporter-options <reporter-options> options for the mocha reporter. defaults to "null"
-s, --spec <spec> runs specific spec file(s). defaults to "all"
-t, --tag <tag> named tag(s) for recorded runs in the Cypress Dashboard
-t, --tag <tag> named tag(s) for recorded runs in Cypress Cloud
--dev runs cypress in development and bypasses binary check
-h, --help display help for command
-------

View File

@@ -1,4 +1,4 @@
exports['cypress .run resolves with contents of tmp file 1'] = {
"code": 0,
"failingTests": []
'code': 0,
'failingTests': [],
}

View File

@@ -50,8 +50,8 @@ Cypress Version: 1.2.3
`
exports['child kill error object'] = {
"description": "The Test Runner unexpectedly exited via a exit event with signal SIGKILL",
"solution": "Please search Cypress documentation for possible solutions:\n\n https://on.cypress.io\n\nCheck if there is a GitHub issue describing this crash:\n\n https://github.com/cypress-io/cypress/issues\n\nConsider opening a new issue."
'description': 'The Test Runner unexpectedly exited via a exit event with signal SIGKILL',
'solution': 'Please search Cypress documentation for possible solutions:\n\n https://on.cypress.io\n\nCheck if there is a GitHub issue describing this crash:\n\n https://github.com/cypress-io/cypress/issues\n\nConsider opening a new issue.',
}
exports['Error message'] = `
@@ -74,29 +74,29 @@ Cypress Version: 1.2.3
`
exports['errors individual has the following errors 1'] = [
"CYPRESS_RUN_BINARY",
"binaryNotExecutable",
"childProcessKilled",
"failedDownload",
"failedUnzip",
"failedUnzipWindowsMaxPathLength",
"incompatibleHeadlessFlags",
"incompatibleTestTypeFlags",
"incompatibleTestingTypeAndFlag",
"invalidCacheDirectory",
"invalidConfigFile",
"invalidCypressEnv",
"invalidOS",
"invalidRunProjectPath",
"invalidSmokeTestDisplayError",
"invalidTestingType",
"missingApp",
"missingDependency",
"missingXvfb",
"nonZeroExitCodeXvfb",
"notInstalledCI",
"smokeTestFailure",
"unexpected",
"unknownError",
"versionMismatch"
'CYPRESS_RUN_BINARY',
'binaryNotExecutable',
'childProcessKilled',
'failedDownload',
'failedUnzip',
'failedUnzipWindowsMaxPathLength',
'incompatibleHeadlessFlags',
'incompatibleTestTypeFlags',
'incompatibleTestingTypeAndFlag',
'invalidCacheDirectory',
'invalidConfigFile',
'invalidCypressEnv',
'invalidOS',
'invalidRunProjectPath',
'invalidSmokeTestDisplayError',
'invalidTestingType',
'missingApp',
'missingDependency',
'missingXvfb',
'nonZeroExitCodeXvfb',
'notInstalledCI',
'smokeTestFailure',
'unexpected',
'unknownError',
'versionMismatch',
]

View File

@@ -1,27 +1,27 @@
exports['exec run .processRunOptions does not remove --record option when using --browser 1'] = [
"--run-project",
'--run-project',
null,
"--browser",
"test browser",
"--record",
"foo"
'--browser',
'test browser',
'--record',
'foo',
]
exports['exec run .processRunOptions passes --browser option 1'] = [
"--run-project",
'--run-project',
null,
"--browser",
"test browser"
'--browser',
'test browser',
]
exports['exec run .processRunOptions passes --record option 1'] = [
"--run-project",
'--run-project',
null,
"--record",
"my record id"
'--record',
'my record id',
]
exports['exec run .processRunOptions defaults to e2e testingType 1'] = [
"--run-project",
null
'--run-project',
null,
]

View File

@@ -1,18 +1,18 @@
exports['lib/exec/spawn .start forces colors and streams when supported 1'] = {
"FORCE_COLOR": "1",
"DEBUG_COLORS": "1",
"MOCHA_COLORS": "1",
"FORCE_STDIN_TTY": "1",
"FORCE_STDOUT_TTY": "1",
"FORCE_STDERR_TTY": "1"
'FORCE_COLOR': '1',
'DEBUG_COLORS': '1',
'MOCHA_COLORS': '1',
'FORCE_STDIN_TTY': '1',
'FORCE_STDOUT_TTY': '1',
'FORCE_STDERR_TTY': '1',
}
exports['lib/exec/spawn .start does not force colors and streams when not supported 1'] = {
"FORCE_COLOR": "0",
"DEBUG_COLORS": "0",
"FORCE_STDIN_TTY": "0",
"FORCE_STDOUT_TTY": "0",
"FORCE_STDERR_TTY": "0"
'FORCE_COLOR': '0',
'DEBUG_COLORS': '0',
'FORCE_STDIN_TTY': '0',
'FORCE_STDOUT_TTY': '0',
'FORCE_STDERR_TTY': '0',
}
exports['lib/exec/spawn .start detects kill signal exits with error on SIGKILL 1'] = `

View File

@@ -1,27 +1,27 @@
exports['config_as_object 1'] = {
"config": "{\"baseUrl\":\"http://localhost:2000\",\"watchForFileChanges\":false}"
'config': '{"baseUrl":"http://localhost:2000","watchForFileChanges":false}',
}
exports['env_as_object 1'] = {
"env": "{\"foo\":\"bar\",\"magicNumber\":1234,\"host\":\"kevin.dev.local\"}"
'env': '{"foo":"bar","magicNumber":1234,"host":"kevin.dev.local"}',
}
exports['env_as_string 1'] = {
"env": "foo=bar"
'env': 'foo=bar',
}
exports['others_unchanged 1'] = {
"foo": "bar"
'foo': 'bar',
}
exports['reporter_options_as_object 1'] = {
"reporterOptions": "{\"mochaFile\":\"results/my-test-output.xml\",\"toConsole\":true}"
'reporterOptions': '{"mochaFile":"results/my-test-output.xml","toConsole":true}',
}
exports['spec_as_array 1'] = {
"spec": "[\"a\",\"b\",\"c\"]"
'spec': '["a","b","c"]',
}
exports['spec_as_string 1'] = {
"spec": "x,y,z"
'spec': 'x,y,z',
}

View File

@@ -110,7 +110,7 @@ const descriptions = {
exit: 'keep the browser open after tests finish',
forceInstall: 'force install the Cypress binary',
global: 'force Cypress into global mode as if its globally installed',
group: 'a named group for recorded runs in the Cypress Dashboard',
group: 'a named group for recorded runs in Cypress Cloud',
headed: 'displays the browser instead of running headlessly',
headless: 'hide the browser instead of running headed (default for cypress run)',
key: 'your secret Record Key. you can omit this if you set a CYPRESS_RECORD_KEY environment variable.',
@@ -118,11 +118,11 @@ const descriptions = {
port: 'runs Cypress on a specific port. overrides any value in cypress.config.{js,ts,mjs,cjs}.',
project: 'path to the project',
quiet: 'run quietly, using only the configured reporter',
record: 'records the run. sends test results, screenshots and videos to your Cypress Dashboard.',
record: 'records the run. sends test results, screenshots and videos to Cypress Cloud.',
reporter: 'runs a specific mocha reporter. pass a path to use a custom reporter. defaults to "spec"',
reporterOptions: 'options for the mocha reporter. defaults to "null"',
spec: 'runs specific spec file(s). defaults to "all"',
tag: 'named tag(s) for recorded runs in the Cypress Dashboard',
tag: 'named tag(s) for recorded runs in Cypress Cloud',
version: 'prints Cypress version',
}

View File

@@ -16,6 +16,7 @@
"test-unit": "yarn unit",
"test-watch": "yarn unit --watch",
"types": "yarn dtslint",
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json,.vue .",
"unit": "cross-env BLUEBIRD_DEBUG=1 NODE_ENV=test mocha --reporter mocha-multi-reporters --reporter-options configFile=../mocha-reporter-config.json"
},
"dependencies": {

View File

@@ -229,6 +229,7 @@ declare namespace CypressCommandLine {
startedAt: dateTimeISO
endedAt: dateTimeISO
duration: ms
wallClockDuration?: number
}
/**
* Reporter name like "spec"
@@ -259,8 +260,10 @@ declare namespace CypressCommandLine {
* resolved filename of the spec
*/
absolute: string
relativeToCommonRoot: string
}
shouldUploadVideo: boolean
skippedSpec: boolean
}
/**

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/// <reference path="./cypress-npm-api.d.ts" />
/// <reference path="./cypress-eventemitter.d.ts" />
/// <reference path="./cypress-type-helpers.d.ts" />
@@ -489,7 +490,7 @@ declare namespace Cypress {
* @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]>,
name: T, options: CommandOptions & { prevSubject: S | ['optional'] }, fn: CommandFnWithSubject<T, PrevSubjectMap[S]>,
): void
/**
@@ -497,7 +498,7 @@ declare namespace Cypress {
* @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]>,
name: T, options: CommandOptions & { prevSubject: S[] }, fn: CommandFnWithSubject<T, PrevSubjectMap<void>[S]>,
): void
/**
@@ -523,7 +524,7 @@ declare namespace Cypress {
* @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]>,
options: CommandOptions & { prevSubject: S | ['optional'] }, fns: CommandFnsWithSubject<PrevSubjectMap[S]>,
): void
/**
@@ -531,7 +532,7 @@ declare namespace Cypress {
* @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]>,
options: CommandOptions & { prevSubject: S[] }, fns: CommandFnsWithSubject<PrevSubjectMap<void>[S]>,
): void
/**
@@ -724,8 +725,8 @@ declare namespace Cypress {
type CanReturnChainable = void | Chainable | Promise<unknown>
type ThenReturn<S, R> =
R extends void ? Chainable<S> :
R extends R | undefined ? Chainable<S | Exclude<R, undefined>> :
Chainable<S>
R extends R | undefined ? Chainable<S | Exclude<R, undefined>> :
Chainable<S>
/**
* Chainable interface for non-array Subjects
@@ -2339,8 +2340,8 @@ declare namespace Cypress {
type ChainableMethods<Subject = any> = {
[P in keyof Chainable<Subject>]: Chainable<Subject>[P] extends ((...args: any[]) => any)
? Chainable<Subject>[P]
: never
? Chainable<Subject>[P]
: never
}
interface SinonSpyAgent<A extends sinon.SinonSpy> {
@@ -2824,7 +2825,7 @@ declare namespace Cypress {
*/
video: boolean
/**
* Whether Cypress will upload the video to the Dashboard even if all tests are passing. This applies only when recording your runs to the Dashboard. Turn this off if you'd like the video uploaded only when there are failing tests.
* Whether Cypress will upload the video to Cypress Cloud even if all tests are passing. This applies only when recording your runs to Cypress Cloud. Turn this off if you'd like the video uploaded only when there are failing tests.
* @default true
*/
videoUploadOnPasses: boolean
@@ -3066,8 +3067,8 @@ declare namespace Cypress {
type PickConfigOpt<T> = T extends keyof DefineDevServerConfig ? DefineDevServerConfig[T] : any
interface AngularDevServerProjectConfig {
root: string,
sourceRoot: string,
root: string
sourceRoot: string
buildOptions: Record<string, any>
}
@@ -3116,7 +3117,7 @@ declare namespace Cypress {
/**
* Hosts mappings to IP addresses.
*/
hosts?: null | {[key: string]: string}
hosts?: null | {[key: string]: string}
}
interface PluginConfigOptions extends ResolvedConfigOptions, RuntimeConfigOptions {

View File

@@ -118,7 +118,7 @@ There are a number of reasons why a ticket may be closed without any change or P
The best place for asking questions is our [Discord server](https://discord.gg/cypress) which has a very active community of folks with a diverse set of knowledge. Other available channels to explore include [Cypress GitHub discussions](https://github.com/cypress-io/cypress/discussions), [community chat](https://on.cypress.io/chat), and [Stack Overflow](https://stackoverflow.com/questions/tagged/cypress).
We also offer support via email with our [paid plans](https://www.cypress.io/pricing/).
- **Feature request for Cypress Dashboard** - Thank you for your support as a Cypress Dashboard user! These issues are routed to our Cypress Dashboard team's ticketing system. Your customer success representative is available for follow-up and will reach out you directly via email if more information is needed.
- **Feature request for Cypress Cloud** - Thank you for your support as a Cypress Cloud user! These issues are routed to our Cypress Cloud team's ticketing system. Your customer success representative is available for follow-up and will reach out you directly via email if more information is needed.
- **The fix or feature is not within our vision for Cypress** - There will inevitably be suggestions that will not fit within the scope of Cypress' vision for our product. We will do our best to explain why we will not be addressing this issue.

View File

@@ -82,7 +82,7 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
- Go into a project, run a quick test, make sure things look right
- Optionally, install the new version into an established project and run the tests there
- [cypress-realworld-app](https://github.com/cypress-io/cypress-realworld-app) uses yarn and represents a typical consumer implementation.
- Optionally, do more thorough tests, for example test the new version of Cypress against the Cypress Dashboard repo.
- Optionally, do more thorough tests, for example test the new version of Cypress against the Cypress Cloud repo.
5. Log into AWS SSO with `aws sso login --profile <name_of_profile>`. If you have setup your credentials under a different profile than `prod`, be sure to set the `AWS_PROFILE` environment variable to that profile name for the remaining steps. For example, if you are using `production` instead of `prod`, do `export AWS_PROFILE=production`.
@@ -119,7 +119,7 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
- Go into a project, run a quick test, make sure things look right
- Install the new version into an established project and run the tests there
- [cypress-realworld-app](https://github.com/cypress-io/cypress-realworld-app) uses yarn and represents a typical consumer implementation.
- Optionally, do more thorough tests, for example test the new version of Cypress against the Cypress Dashboard repo.
- Optionally, do more thorough tests, for example test the new version of Cypress against the Cypress Cloud repo.
11. Create or review the release-specific documentation and changelog in [cypress-documentation](https://github.com/cypress-io/cypress-documentation). If there is not already a release-specific PR open, create one. This PR must be merged, built, and deployed before moving to the next step.
- Use [`release-automations`](https://github.com/cypress-io/release-automations)'s `issues-in-release` tool to generate a starting point for the changelog, based off of ZenHub:

View File

@@ -0,0 +1,5 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures

View File

@@ -8,7 +8,8 @@
"build": "rollup -c rollup.config.mjs",
"postbuild": "node ../../scripts/sync-exported-npm-with-cli.js",
"build-prod": "yarn build",
"check-ts": "tsc --noEmit"
"check-ts": "tsc --noEmit",
"lint": "eslint --ext .js,.ts,.json, ."
},
"dependencies": {},
"devDependencies": {

View File

@@ -0,0 +1,9 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures
**/test/fixtures
**/__snapshots__
/initial-template
/**/*.template.*

View File

@@ -9,7 +9,8 @@
"prepare-example": "node scripts/example copy-to ./initial-template",
"prepare-copy-templates": "node scripts/copy-templates copy-to ./dist/src",
"test": "cross-env TS_NODE_PROJECT=./tsconfig.test.json mocha --config .mocharc.json './src/**/*.test.ts'",
"test:watch": "yarn test -w"
"test:watch": "yarn test -w",
"lint": "eslint --ext .js,.ts,.json, ."
},
"dependencies": {
"@babel/core": "^7.5.4",

View File

@@ -0,0 +1,5 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures

View File

@@ -135,9 +135,9 @@ Before running Cypress in `open` mode, ensure that you have started your applica
Read our docs to learn more about [launching browsers](https://on.cypress.io/launching-browsers) with Cypress.
### Recording test results to the Cypress Dashboard
### Recording test results to Cypress Cloud
We recommend setting your [Cypress Dashboard](https://on.cypress.io/features-dashboard) recording key as an environment variable and NOT as a builder option when running it in CI.
We recommend setting your [Cypress Cloud](https://on.cypress.io/features-dashboard) recording key as an environment variable and NOT as a builder option when running it in CI.
```json
"cypress-run": {
@@ -145,7 +145,7 @@ We recommend setting your [Cypress Dashboard](https://on.cypress.io/features-das
"options": {
"devServerTarget": "{project-name}:serve",
"record": true,
"key": "your-cypress-dashboard-recording-key"
"key": "your-cypress-cloud-recording-key"
},
"configurations": {
"production": {
@@ -155,7 +155,7 @@ We recommend setting your [Cypress Dashboard](https://on.cypress.io/features-das
}
```
Read our docs to learn more about [recording test results](https://on.cypress.io/recording-project-runs) to the [Cypress Dashboard](https://on.cypress.io/features-dashboard).
Read our docs to learn more about [recording test results](https://on.cypress.io/recording-project-runs) to [Cypress Cloud](https://on.cypress.io/features-dashboard).
### Specifying a custom config file
@@ -187,7 +187,7 @@ Read our docs to learn more about all the [configuration options](https://on.cyp
"devServerTarget": "{project-name}:serve",
"parallel": true,
"record": true,
"key": "your-cypress-dashboard-recording-key"
"key": "your-cypress-cloud-recording-key"
},
"configurations": {
"production": {

View File

@@ -6,7 +6,8 @@
"scripts": {
"build": "tsc -p tsconfig.json",
"build:watch": "tsc -p tsconfig.json --watch",
"test": "mocha -r @packages/ts/register --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json src/**/*.spec.ts"
"test": "mocha -r @packages/ts/register --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json src/**/*.spec.ts",
"lint": "eslint --ext .ts,.json, ."
},
"dependencies": {
"@angular-devkit/architect": "^0.1402.1",

View File

@@ -0,0 +1,5 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures

View File

@@ -8,7 +8,8 @@
"postbuild": "node ../../scripts/sync-exported-npm-with-cli.js",
"build-prod": "yarn build",
"check-ts": "tsc --noEmit",
"watch": "tsc -w"
"watch": "tsc -w",
"lint": "eslint --ext .js,.ts,.json, ."
},
"dependencies": {},
"devDependencies": {

5
npm/react/.eslintignore Normal file
View File

@@ -0,0 +1,5 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures

View File

@@ -12,6 +12,7 @@
"cy:run": "node ../../scripts/cypress.js run --component",
"cy:run:debug": "node --inspect-brk ../../scripts/start.js --component-testing --run-project ${PWD}",
"test": "yarn cy:run",
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json, .",
"watch": "yarn build --watch --watch.exclude ./dist/**/*"
},
"devDependencies": {

View File

@@ -0,0 +1,5 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures

View File

@@ -7,7 +7,8 @@
"build": "rimraf dist && rollup -c rollup.config.mjs",
"postbuild": "node ../../scripts/sync-exported-npm-with-cli.js",
"build-prod": "yarn build",
"watch": "yarn build --watch --watch.exclude ./dist/**/*"
"watch": "yarn build --watch --watch.exclude ./dist/**/*",
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json, ."
},
"devDependencies": {
"@cypress/mount-utils": "0.0.0-development",

5
npm/svelte/.eslintignore Normal file
View File

@@ -0,0 +1,5 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures

View File

@@ -8,6 +8,7 @@
"build": "rollup -c rollup.config.mjs",
"postbuild": "node ../../scripts/sync-exported-npm-with-cli.js",
"build-prod": "yarn build",
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json, .",
"check-ts": "tsc --noEmit"
},
"devDependencies": {

View File

@@ -0,0 +1,5 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures

View File

@@ -12,7 +12,8 @@
"cypress:open": "yarn cypress:run-cypress-in-cypress gulp open --project .",
"watch": "tsc -w",
"test": "yarn test-unit",
"test-unit": "mocha -r ts-node/register/transpile-only --config ./test/.mocharc.js"
"test-unit": "mocha -r ts-node/register/transpile-only --config ./test/.mocharc.js",
"lint": "eslint --ext .js,.ts,.json, ."
},
"dependencies": {
"debug": "^4.3.4",

5
npm/vue/.eslintignore Normal file
View File

@@ -0,0 +1,5 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures

View File

@@ -10,6 +10,8 @@
"build": "rimraf dist && rollup -c rollup.config.mjs",
"postbuild": "node ../../scripts/sync-exported-npm-with-cli.js",
"typecheck": "yarn tsd && vue-tsc --noEmit",
"check-ts": "yarn tsd && vue-tsc --noEmit",
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json,.vue .",
"test": "yarn cy:run",
"tsd": "yarn build && yarn tsc -p test-tsd/tsconfig.tsd.json",
"watch": "yarn build --watch --watch.exclude ./dist/**/*"

5
npm/vue2/.eslintignore Normal file
View File

@@ -0,0 +1,5 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures

View File

@@ -9,7 +9,9 @@
"postbuild": "node ../../scripts/sync-exported-npm-with-cli.js",
"build-prod": "yarn build",
"test": "echo \"Tests for @cypress/vue2 are run from system-tests\"",
"watch": "yarn build --watch --watch.exclude ./dist/**/*"
"watch": "yarn build --watch --watch.exclude ./dist/**/*",
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json,.vue .",
"test-ci": "node ../../scripts/run-ct-examples.js --examplesList=./examples.env"
},
"devDependencies": {
"@cypress/mount-utils": "0.0.0-development",

View File

@@ -4,7 +4,8 @@
"description": "Cypress preprocessor for bundling JavaScript via webpack with dependencies included and support for various ES features, TypeScript, and CoffeeScript",
"private": false,
"scripts": {
"test": "mocha test/e2e/*.spec.* --timeout 4000"
"test": "mocha test/e2e/*.spec.* --timeout 4000",
"lint": "eslint --ext .js,.ts,.json, ."
},
"dependencies": {
"@babel/core": "^7.11.1",

View File

@@ -0,0 +1,7 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures
**/test/fixtures
**/__snapshots__

View File

@@ -14,7 +14,8 @@
"cypress:run-cypress-in-cypress": "cross-env HTTP_PROXY_TARGET_FOR_ORIGIN_REQUESTS=http://localhost:4455 CYPRESS_REMOTE_DEBUGGING_PORT=6666 TZ=America/New_York",
"cypress:open": "yarn cypress:run-cypress-in-cypress gulp open --project .",
"test": "yarn test-unit",
"test-unit": "mocha -r ts-node/register/transpile-only --config ./test/.mocharc.js"
"test-unit": "mocha -r ts-node/register/transpile-only --config ./test/.mocharc.js",
"lint": "eslint --ext .js,.ts,.json, ."
},
"dependencies": {
"find-up": "6.3.0",

View File

@@ -0,0 +1,9 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures
**/test/fixtures
**/_test-output
/cypress/tests/e2e/compile-error.js
/examples/use-babelrc/cypress/e2e/spec.cy.js

View File

@@ -17,7 +17,8 @@
"test-unit": "mocha test/unit/*.spec.*",
"test-watch": "yarn test-unit & chokidar '**/*.(js|ts)' 'test/unit/*.(js|ts)' -c 'yarn test-unit'",
"check-ts": "tsc --noEmit",
"watch": "rimraf dist && tsc --watch"
"watch": "rimraf dist && tsc --watch",
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json, ."
},
"dependencies": {
"@babel/parser": "7.13.0",

View File

@@ -1,6 +1,6 @@
{
"name": "cypress",
"version": "11.0.0",
"version": "11.1.0",
"description": "Cypress is a next generation front end testing tool built for the modern web",
"private": true,
"scripts": {
@@ -42,7 +42,7 @@
"get-next-version": "node scripts/get-next-version.js",
"postinstall": "node ./scripts/run-postInstall.js",
"jscodeshift": "jscodeshift -t ./node_modules/js-codemod/transforms/arrow-function-arguments.js",
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json,.vue .",
"lint": "lerna run lint --no-bail",
"lint-changed": "lint-changed",
"prepare-release-artifacts": "node ./scripts/prepare-release-artifacts.js",
"npm-release": "node scripts/npm-release.js",

View File

@@ -0,0 +1,7 @@
**/dist
**/dist-*
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures
/src/store/mobx-runner-store.ts

View File

@@ -16,7 +16,7 @@ This is the front-end for the Cypress App.
Cypress has two modes: `run` and `open`. We want run mode to be as light and fast as possible, since this is the mode used to run on CI machines, etc. Run mode has minimal UI showing only what is necessary. Open mode is the interactive experience.
- **`open`** mode is driven using GraphQL and urql. It shows the full Cypress app, include the top nav, side nav, spec list, etc. You can change between testing types, check your latest runs on the Cypress Dashboard, update settings, etc.
- **`open`** mode is driven using GraphQL and urql. It shows the full Cypress app, include the top nav, side nav, spec list, etc. You can change between testing types, check your latest runs on Cypress Cloud, update settings, etc.
- **`run`** mode is does not rely on GraphQL. This is so we can be as performant as possible. It only renders the "runner" part of the UI, which is comprised of the command log, Spec Runner header, and AUT iframe.
The two modes are composed using the same logic, but have slightly different components. You can see where the differences are in `Runner.vue`(src/pages/Specs/Runner.vue). Notice that `<SpecRunnerOpenMode>` receives a `gql` prop, since it uses GraphQL, and `<SpecRunnerRunMode>` does not.

View File

@@ -19,10 +19,7 @@ const validateSetupSessionGroup = (isNewSession = true) => {
cy.get('@setupSession').find('.command-expander').click()
cy.get('@setupSession').find('.command-alias').contains('runSetup')
return cy.contains(groupText)
.closest('.command')
.find('.command-name-Clear-page')
.should('have.length', 1)
return cy.get('@setupSession')
}
describe('runner/cypress sessions.ui.spec', {
@@ -200,7 +197,7 @@ describe('runner/cypress sessions.ui.spec', {
cy.contains('user1')
cy.contains('restored')
cy.get('.command-name-Clear-page').should('have.length', 1)
cy.get('.command-name-Clear-page').should('have.length', 2)
cy.contains('Restore saved session')
@@ -258,7 +255,7 @@ describe('runner/cypress sessions.ui.spec', {
.find('.command-expander')
.should('have.class', 'command-expander-is-open')
cy.get('.command-name-Clear-page').should('have.length', 2)
cy.get('.command-name-Clear-page').should('have.length', 3)
validateSetupSessionGroup(false)
@@ -321,8 +318,6 @@ describe('runner/cypress sessions.ui.spec', {
cy.get('.command-name-Clear-page').should('have.length', 2)
validateSetupSessionGroup(false)
.parent()
.closest('.command')
.next()
.contains('Validate session')
.closest('.command').as('secondValidateSession')

View File

@@ -279,7 +279,7 @@ describe('App: Runs', { viewportWidth: 1200 }, () => {
name: 'Test User A',
} })
cy.contains('button', 'Log in to the Cypress Dashboard').click()
cy.contains('button', 'Log in to Cypress Cloud').click()
cy.findByRole('dialog', { name: 'Log in to Cypress' }).as('logInModal').within(() => {
cy.findByRole('button', { name: 'Log in' }).click()
@@ -578,7 +578,7 @@ describe('App: Runs', { viewportWidth: 1200 }, () => {
})
context('Runs - No Runs', () => {
it('when no runs and not connected, shows connect to dashboard button', () => {
it('when no runs and not connected, shows connect to Cypress Cloud button', () => {
cy.scaffoldProject('component-tests')
cy.openProject('component-tests', ['--config-file', 'cypressWithoutProjectId.config.js'])
cy.startAppServer('component')
@@ -722,7 +722,7 @@ describe('App: Runs', { viewportWidth: 1200 }, () => {
moveToRunsPage()
cy.contains('h2', 'Cannot connect to the Cypress Dashboard')
cy.contains('h2', 'Cannot connect to Cypress Cloud')
// cy.percySnapshot() // TODO: restore when Percy CSS is fixed. See https://github.com/cypress-io/cypress/issues/23435
cy.remoteGraphQLIntercept((obj) => {
@@ -755,7 +755,7 @@ describe('App: Runs', { viewportWidth: 1200 }, () => {
cy.get('[data-cy=warning-alert]')
.should('contain.text', 'You have no internet connection')
.and('contain.text', 'Check your internet connection to pull the latest data from the dashboard')
.and('contain.text', 'Check your internet connection to pull the latest data from Cypress Cloud')
})
it('should remove the alert warning if the app reconnects to the internet', () => {
@@ -769,7 +769,7 @@ describe('App: Runs', { viewportWidth: 1200 }, () => {
cy.get('[data-cy=warning-alert]')
.should('contain.text', 'You have no internet connection')
.and('contain.text', 'Check your internet connection to pull the latest data from the dashboard')
.and('contain.text', 'Check your internet connection to pull the latest data from Cypress Cloud')
cy.goOnline()
@@ -810,7 +810,7 @@ describe('App: Runs', { viewportWidth: 1200 }, () => {
cy.get('[data-cy=standard-modal]')
.should('contain.text', 'You have no internet connection')
.and('contain.text', 'Check your internet connection to pull the latest data from the dashboard')
.and('contain.text', 'Check your internet connection to pull the latest data from Cypress Cloud')
})
it('shows correct message on connect project modal', () => {
@@ -844,7 +844,7 @@ describe('App: Runs', { viewportWidth: 1200 }, () => {
cy.get('[data-cy=standard-modal]')
.should('contain.text', 'You have no internet connection')
.and('contain.text', 'Check your internet connection to pull the latest data from the dashboard')
.and('contain.text', 'Check your internet connection to pull the latest data from Cypress Cloud')
})
})

View File

@@ -21,7 +21,7 @@ describe('App: Settings', () => {
cy.findByText('Device settings').should('be.visible')
cy.findByText('Project settings').should('be.visible')
cy.findByText('Dashboard settings').should('be.visible')
cy.findByText('Cypress Cloud settings').should('be.visible')
})
describe('Cloud Settings', () => {
@@ -33,7 +33,7 @@ describe('App: Settings', () => {
cy.startAppServer('e2e')
cy.visitApp()
cy.get(SidebarSettingsLinkSelector).click()
cy.findByText('Dashboard settings').click()
cy.findByText('Cypress Cloud settings').click()
cy.findByText('Project ID').should('be.visible')
cy.get('[data-cy="code-box"]').should('contain', 'fromCli')
cy.findByText('Copy').click()
@@ -49,7 +49,7 @@ describe('App: Settings', () => {
cy.visitApp()
cy.get(SidebarSettingsLinkSelector).click()
cy.findByText('Dashboard settings').click()
cy.findByText('Cypress Cloud settings').click()
cy.findByText('Record key').should('be.visible')
})
@@ -59,7 +59,7 @@ describe('App: Settings', () => {
cy.visitApp()
cy.get(SidebarSettingsLinkSelector).click()
cy.findByText('Dashboard settings').click()
cy.findByText('Cypress Cloud settings').click()
cy.get('[data-cy="code-box"]').should('contain', '***')
cy.get('[aria-label="Record Key Visibility Toggle"]').click()
cy.get('[data-cy="code-box"]').should('contain', '2aaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa')
@@ -69,7 +69,7 @@ describe('App: Settings', () => {
cy.startAppServer('e2e')
cy.loginUser()
cy.visitApp('settings')
cy.findByText('Dashboard settings').click()
cy.findByText('Cypress Cloud settings').click()
cy.findByText('Manage keys').click()
cy.withRetryableCtx((ctx) => {
expect((ctx.actions.electron.openExternal as SinonStub).lastCall.lastArg).to.eq('http:/test.cloud/cloud-project/settings')
@@ -97,7 +97,7 @@ describe('App: Settings', () => {
// Wait for the test to pass, so the test is completed
cy.get('.passed > .num').should('contain', 1)
cy.get(SidebarSettingsLinkSelector).click()
cy.contains('Dashboard settings').click()
cy.contains('Cypress Cloud settings').click()
// Assert the data is not there before it arrives
cy.contains('Record key').should('not.exist')
cy.contains('Record key')
@@ -112,7 +112,7 @@ describe('App: Settings', () => {
})
cy.findByTestId('sidebar-link-settings-page').click()
cy.contains('Dashboard settings').click()
cy.contains('Cypress Cloud settings').click()
cy.contains('Record key').should('exist')
cy.findByTestId('sidebar-link-runs-page').click()
cy.findByTestId('user-avatar-title').click()
@@ -123,7 +123,7 @@ describe('App: Settings', () => {
})
cy.findByTestId('sidebar-link-settings-page').click()
cy.contains('Dashboard settings').click()
cy.contains('Cypress Cloud settings').click()
cy.contains('Record key').should('not.exist')
})
})
@@ -396,13 +396,13 @@ describe('App: Settings without cloud', () => {
cy.visitApp()
cy.get(SidebarSettingsLinkSelector).click()
cy.findByText('Dashboard settings').click()
cy.findByText('Cypress Cloud settings').click()
cy.findByText('Project ID').should('not.exist')
cy.withCtx((ctx, o) => {
o.sinon.spy(ctx._apis.authApi, 'logIn')
})
cy.contains('button', 'Log in to the Cypress Dashboard').click()
cy.contains('button', 'Log in to Cypress Cloud').click()
cy.findByRole('dialog', { name: 'Log in to Cypress' }).within(() => {
cy.contains('button', 'Log in').click()
})

View File

@@ -245,7 +245,8 @@ describe('App: Spec List (E2E)', () => {
cy.findByText('No specs matched your search:').should('not.be.visible')
})
it('saves the filter when navigating to a spec and back', function () {
// 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'
clearSearchAndType(targetSpecFile)

View File

@@ -198,9 +198,9 @@ describe('App/Cloud Integration - Latest runs and Average duration', { viewportW
it('shows correct tooltips with log in buttons', () => {
cy.findByTestId('latest-runs-header').trigger('mouseenter')
cy.get('.v-popper__popper--shown')
.should('contain', 'Connect to the Cypress Dashboard to see the status of your latest runs')
.should('contain', 'Connect to Cypress Cloud to see the status of your latest runs')
.find('button')
.should('have.text', 'Log in to the Dashboard')
.should('have.text', 'Log in to Cypress Cloud')
.click()
cy.findByRole('dialog', { name: 'Log in to Cypress' }).within(() => {
@@ -212,9 +212,9 @@ describe('App/Cloud Integration - Latest runs and Average duration', { viewportW
cy.findByTestId('average-duration-header').trigger('mouseenter')
cy.get('.v-popper__popper--shown')
.should('contain', 'Connect to the Cypress Dashboard to see the average spec durations of your latest runs')
.should('contain', 'Connect to Cypress Cloud to see the average spec durations of your latest runs')
.find('button')
.should('have.text', 'Log in to the Dashboard')
.should('have.text', 'Log in to Cypress Cloud')
.click()
cy.findByRole('dialog', { name: 'Log in to Cypress' }).within(() => {
@@ -267,7 +267,7 @@ describe('App/Cloud Integration - Latest runs and Average duration', { viewportW
it('shows correct tooltips with log in buttons', () => {
cy.findByTestId('latest-runs-header').trigger('mouseenter')
cy.get('.v-popper__popper--shown')
.should('contain', 'Connect to the Cypress Dashboard to see the status of your latest runs')
.should('contain', 'Connect to Cypress Cloud to see the status of your latest runs')
.find('button')
.should('have.text', 'Connect your project')
.click()
@@ -280,7 +280,7 @@ describe('App/Cloud Integration - Latest runs and Average duration', { viewportW
cy.findByTestId('average-duration-header').trigger('mouseenter')
cy.get('.v-popper__popper--shown')
.should('contain', 'Connect to the Cypress Dashboard to see the average spec durations of your latest runs')
.should('contain', 'Connect to Cypress Cloud to see the average spec durations of your latest runs')
.find('button')
.should('have.text', 'Connect your project')
.click()
@@ -323,7 +323,7 @@ describe('App/Cloud Integration - Latest runs and Average duration', { viewportW
it('shows correct tooltips', () => {
cy.findByTestId('latest-runs-header').trigger('mouseenter')
cy.get('.v-popper__popper--shown')
.should('contain', 'The status of your latest runs in the Cypress Dashboard')
.should('contain', 'The status of your latest runs in Cypress Cloud')
.find('button')
.should('not.exist')
@@ -331,7 +331,7 @@ describe('App/Cloud Integration - Latest runs and Average duration', { viewportW
cy.findByTestId('average-duration-header').trigger('mouseenter')
cy.get('.v-popper__popper--shown')
.should('contain', 'The average spec durations of your latest runs in the Cypress Dashboard')
.should('contain', 'The average spec durations of your latest runs in Cypress Cloud')
.find('button')
.should('not.exist')

View File

@@ -15,7 +15,8 @@
"cypress:run:e2e": "yarn cypress:run-cypress-in-cypress node ../../scripts/cypress run --project .",
"dev": "yarn cypress:run-cypress-in-cypress gulp dev --project .",
"start": "echo \"run 'yarn dev' from the root\" && exit 1",
"watch": "echo \"run 'yarn dev' from the root\" && exit 1"
"watch": "echo \"run 'yarn dev' from the root\" && exit 1",
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json, ."
},
"dependencies": {},
"devDependencies": {

View File

@@ -25,7 +25,6 @@ export function useSpecFilter (savedFilter?: string) {
function setSpecFilter (specFilter: string) {
if (specStore.specFilter !== specFilter) {
specStore.setSpecFilter(specFilter)
saveSpecFilter.executeMutation({ value: JSON.stringify({ specFilter }) })
}
}

View File

@@ -416,7 +416,7 @@ export class EventManager {
if (config.isTextTerminal && !runState.currentId) {
// we are in run mode and it's the first load
// store runnables in backend and maybe send to dashboard
// store runnables in backend and maybe send to Cypress Cloud
return this.ws.emit('set:runnables:and:maybe:record:tests', runnables, run)
}

View File

@@ -9,12 +9,12 @@ describe('<RunsError />', () => {
return (
<div class="h-screen">
<RunsError
message="Cannot connect to the Cypress Dashboard"
message="Cannot connect to Cypress Cloud"
icon="error"
buttonText="Request Access"
buttonIcon={PaperAirplaneIcon}
>
The request timed out when trying to retrieve the recorded runs from the Cypress Dashboard. <br/>
The request timed out when trying to retrieve the recorded runs from Cypress Cloud. <br/>
Please refresh the page to try again and visit our Status Page if this behavior continues.
</RunsError>
</div>

View File

@@ -40,11 +40,11 @@ describe('<SettingsContainer />', { viewportHeight: 800, viewportWidth: 900 }, (
})
it('expands and collapses cloud settings', () => {
cy.contains('Dashboard settings').click()
cy.contains('Cypress Cloud settings').click()
cy.findByText(defaultMessages.settingsPage.projectId.title).scrollIntoView().should('be.visible')
cy.percySnapshot()
cy.findByText('Dashboard settings').click()
cy.findByText('Cypress Cloud settings').click()
cy.findByText(defaultMessages.settingsPage.projectId.title).should('not.exist')
})

View File

@@ -5,7 +5,7 @@ describe('<SettingsSection />', () => {
cy.viewport(800, 200)
const title = 'Project Id'
const description = 'A Cypress config setting used to uniquely identify your project when recording runs to Cypress Dashboard. Learn more.'
const description = 'A Cypress config setting used to uniquely identify your project when recording runs to Cypress Cloud. Learn more.'
const slots = {
description: () => <p>{description}</p>,
title: () => <h1>{title}</h1>,

View File

@@ -29,7 +29,7 @@ import InlineSpecListHeader from './InlineSpecListHeader.vue'
import InlineSpecListTree from './InlineSpecListTree.vue'
import CreateSpecModal from './CreateSpecModal.vue'
import { fuzzySortSpecs, makeFuzzyFoundSpec, useCachedSpecs } from './spec-utils'
import type { FuzzyFoundSpec } from './spec-utils'
import type { FuzzyFoundSpec } from './tree/useCollapsibleTree'
import { useSpecFilter } from '../composables/useSpecFilter'
gql`

View File

@@ -1,7 +1,7 @@
import type { FuzzyFoundSpec } from './spec-utils'
import { ref } from 'vue'
import { useSpecStore } from '../store'
import InlineSpecListTree from './InlineSpecListTree.vue'
import type { FuzzyFoundSpec } from './tree/useCollapsibleTree'
describe('InlineSpecListTree', () => {
let foundSpecs: FuzzyFoundSpec[]

View File

@@ -60,17 +60,14 @@
</template>
<script setup lang="ts">
import { useCollapsibleTree } from '@packages/frontend-shared/src/composables/useCollapsibleTree'
import type { UseCollapsibleTreeNode } from '@packages/frontend-shared/src/composables/useCollapsibleTree'
import { buildSpecTree } from './spec-utils'
import type { SpecTreeNode, FuzzyFoundSpec } from './spec-utils'
import { UseCollapsibleTreeNode, useCollapsibleTree, SpecTreeNode, FuzzyFoundSpec, buildSpecTree } from './tree/useCollapsibleTree'
import SpecFileItem from './SpecFileItem.vue'
import { computed, watch, onMounted } from 'vue'
import DirectoryItem from './DirectoryItem.vue'
import { RouterLink, useRouter } from 'vue-router'
import { useSpecStore } from '../store'
import { useVirtualList } from '@packages/frontend-shared/src/composables/useVirtualList'
import { useVirtualListNavigation } from '@packages/frontend-shared/src/composables/useVirtualListNavigation'
import { useVirtualList } from './tree/useVirtualList'
import { useVirtualListNavigation } from './tree/useVirtualListNavigation'
import { useStudioStore } from '../store/studio-store'
const props = defineProps<{

View File

@@ -13,7 +13,7 @@
>
<component
:is="latestRun? ExternalLink : 'div'"
:href="dashboardUrl"
:href="cloudUrl"
>
<div
v-if="isRunsLoaded"
@@ -54,7 +54,7 @@
>
<ExternalLink
v-if="latestRun"
:href="dashboardUrl"
:href="cloudUrl"
:use-default-hocus="false"
>
<SpecRunSummary
@@ -213,7 +213,7 @@ const latestDot = computed(() => {
}
})
const dashboardUrl = computed(() => {
const cloudUrl = computed(() => {
if (latestRun.value?.url) {
return getUrlWithParams({
url: latestRun.value.url,

View File

@@ -50,7 +50,7 @@
data-cy="login-button"
@click="emits('showLoginConnect')"
>
{{ t('specPage.dashboardLoginButton') }}
{{ t('specPage.cloudLoginButton') }}
</Button>
<Button
v-else-if="userStatusMatches('needsProjectConnect')"

View File

@@ -183,12 +183,11 @@ import { gql, useSubscription } from '@urql/vue'
import { computed, ref, toRef, watch } from 'vue'
import { Specs_SpecsListFragment, SpecsList_GitInfoUpdatedDocument, SpecsListFragment } from '../generated/graphql'
import { useI18n } from '@cy/i18n'
import { buildSpecTree, fuzzySortSpecs, makeFuzzyFoundSpec, useCachedSpecs } from './spec-utils'
import type { FuzzyFoundSpec } from './spec-utils'
import { useCollapsibleTree } from '@packages/frontend-shared/src/composables/useCollapsibleTree'
import { buildSpecTree, FuzzyFoundSpec, useCollapsibleTree } from './tree/useCollapsibleTree'
import { fuzzySortSpecs, makeFuzzyFoundSpec, useCachedSpecs } from './spec-utils'
import RowDirectory from './RowDirectory.vue'
import SpecItem from './SpecItem.vue'
import { useVirtualList } from '@packages/frontend-shared/src/composables/useVirtualList'
import { useVirtualList } from './tree/useVirtualList'
import NoResults from '@cy/components/NoResults.vue'
import SpecPatternModal from '../components/SpecPatternModal.vue'
import { useOnline, useResizeObserver } from '@vueuse/core'

View File

@@ -9,7 +9,7 @@
style="width: fit-content"
>
<ExternalLink
:href="dashboardUrl"
:href="cloudUrl"
class="hocus:no-underline"
>
<FlakyBadge />
@@ -17,7 +17,7 @@
<template #popper="{ shown }">
<ExternalLink
v-if="shown && props.projectGql?.projectId && props.specGql?.relative"
:href="dashboardUrl"
:href="cloudUrl"
class="hocus:no-underline"
>
<FlakySpecSummaryAdapter
@@ -85,7 +85,7 @@ const props = defineProps<{
}>()
const isFlaky = computed(() => props.cloudSpecGql?.data?.__typename === 'CloudProjectSpec' && !!props.cloudSpecGql?.data?.isConsideredFlaky)
const dashboardUrl = computed(() => {
const cloudUrl = computed(() => {
const cloudSpec = props.cloudSpecGql?.data?.__typename === 'CloudProjectSpec' ? props.cloudSpecGql.data : null
const flakyStatus = cloudSpec?.flakyStatus?.__typename === 'CloudProjectSpecFlakyStatus' ? cloudSpec.flakyStatus : null

View File

@@ -58,8 +58,8 @@
import { gql, useMutation } from '@urql/vue'
import { ScaffoldGeneratorStepOne_ScaffoldIntegrationDocument } from '../../../generated/graphql'
import { computed, onMounted } from 'vue'
import { buildSpecTree } from '../../spec-utils'
import { useCollapsibleTree } from '@packages/frontend-shared/src/composables/useCollapsibleTree'
import { buildSpecTree } from '../../../specs/tree/useCollapsibleTree'
import { useCollapsibleTree } from '../../tree/useCollapsibleTree'
import StandardModalFooter from '@cy/components/StandardModalFooter.vue'
import Button from '@cy/components/Button.vue'
import { useI18n } from '@cy/i18n'

View File

@@ -2,127 +2,7 @@ import fuzzySort from 'fuzzysort'
import type { FoundSpec } from '@packages/types'
import { ComputedRef, Ref, ref, watch } from 'vue'
import _ from 'lodash'
import { getRunnerConfigFromWindow } from '../runner'
// Platform may not be available when this file is loaded (e.g. during component our component tests) so
// we defer loading it until it's used
let platform
const getPlatform = (): string => {
if (!platform) {
platform = getRunnerConfigFromWindow().platform
}
return platform
}
const getRegexSeparator = () => getPlatform() === 'win32' ? /\\/ : /\//
const getSeparator = () => getPlatform() === 'win32' ? '\\' : '/'
export type FuzzyFoundSpec<T = FoundSpec> = T & {
fuzzyIndexes: {
relative: number[]
baseName: number[]
}
}
export type SpecTreeNode<T extends FoundSpec = FoundSpec> = {
id: string
name: string
children: SpecTreeNode<T>[]
isLeaf: boolean
parent?: SpecTreeNode<T>
data?: T
highlightIndexes: number[]
}
export function buildSpecTree<T extends FoundSpec> (specs: FoundSpec[], root: SpecTreeNode<T> = { name: '', isLeaf: false, children: [], id: '', highlightIndexes: [] }): SpecTreeNode<T> {
specs.forEach((spec) => buildSpecTreeRecursive(spec.relative, root, spec))
collapseEmptyChildren(root)
return root
}
export function buildSpecTreeRecursive<T extends FoundSpec> (path: string, tree: SpecTreeNode<T>, data?: T) {
const [firstFile, ...rest] = path.split(getRegexSeparator())
const id = tree.id ? [tree.id, firstFile].join(getSeparator()) : firstFile
const newNode: SpecTreeNode<T> = { name: firstFile, isLeaf: false, children: [], parent: tree, data, id, highlightIndexes: [] }
if (rest.length < 1) {
newNode.isLeaf = true
newNode.highlightIndexes = getHighlightIndexes(newNode)
tree.children.push(newNode)
return tree
}
const foundChild = tree.children.find((child) => child.name === firstFile)
if (foundChild) {
buildSpecTreeRecursive(rest.join(getSeparator()), foundChild, data)
return tree
}
newNode.highlightIndexes = getHighlightIndexes(newNode)
const newTree = buildSpecTreeRecursive(rest.join(getSeparator()), newNode, data)
tree.children.push(newTree)
return tree
}
function collapseEmptyChildren<T extends FoundSpec> (node: SpecTreeNode<T>) {
for (const child of node.children) {
collapseEmptyChildren(child)
}
if (node.isLeaf) {
return
}
// Root name of our tree is '/'. We don't want to collapse into the root node
// so we check node.parent.parent
if (node.parent && node.parent.parent && (node.parent.children.length === 1)) {
node.parent.name = [node.parent.name, node.name].join(getSeparator())
node.parent.id = [node.parent.id, node.name].join(getSeparator())
node.parent.children = node.children
node.parent.highlightIndexes = getHighlightIndexes(node.parent)
}
return
}
// Given a node, return the indexes of the characters that should be highlighted
// The indexes are matched to the `relative` and `baseName` keys of FoundSpec, and
// depending on whether the node is a leaf or not, the indexes are normalized to the position
// of the node's location in the tree.
// If a search of "src/comp" is given with indexes [0,1,2,3,4,5,6,7], the indexes will be split
// into [0,1,2] and [4,5,6,7] corresponding with a
// - src
// - components
// - index.ts
// tree (given that src/comp is not collapsed)
function getHighlightIndexes <T extends FoundSpec> (node: SpecTreeNode<T>) {
if (!(node.data as any)?.fuzzyIndexes) {
return []
}
const { relative: relativeIndexes, baseName: baseNameIndexes } = (node.data as FuzzyFoundSpec<T>).fuzzyIndexes
// When there is a full (or close to) relative match, baseName will no longer match.
// If we have indexes for baseName we show them, otherwise we pull from relative to derive
// baseName indexes
if (node.isLeaf && baseNameIndexes.length > 0) {
return baseNameIndexes
}
const maxIndex = node.id.length - 1
const minIndex = maxIndex - node.name.length + 1
const res = relativeIndexes.filter((index) => index >= minIndex && index <= maxIndex)
return res.map((idx) => idx - minIndex)
}
import { FuzzyFoundSpec, getPlatform } from './tree/useCollapsibleTree'
export function fuzzySortSpecs <T extends FuzzyFoundSpec> (specs: T[], searchValue: string) {
const normalizedSearchValue = getPlatform() === 'win32' ? searchValue.replaceAll('/', '\\') : searchValue

View File

@@ -0,0 +1,266 @@
import type { ComputedRef, Ref } from 'vue'
import { computed } from 'vue'
import { useToggle } from '@vueuse/core'
import type { FoundSpec } from '@packages/types/src'
import { getRunnerConfigFromWindow } from '../../runner'
export type RawNode <T> = {
id: string
name: string
children: RawNode<T>[]
}
export type FuzzyFoundSpec<T = FoundSpec> = T & {
fuzzyIndexes: {
relative: number[]
baseName: number[]
}
}
export type SpecTreeNode<T extends FoundSpec = FoundSpec> = {
id: string
name: string
children: SpecTreeNode<T>[]
isLeaf: boolean
parent?: SpecTreeNode<T>
data?: T
highlightIndexes: number[]
}
// Platform may not be available when this file is loaded (e.g. during component our component tests) so
// we defer loading it until it's used
let platform: string
export const getPlatform = (): string => {
if (!platform) {
platform = getRunnerConfigFromWindow().platform
}
return platform
}
const getRegexSeparator = () => getPlatform() === 'win32' ? /\\/ : /\//
const getSeparator = () => getPlatform() === 'win32' ? '\\' : '/'
export function buildSpecTree<T extends FoundSpec> (specs: FoundSpec[], root: SpecTreeNode<T> = { name: '', isLeaf: false, children: [], id: '', highlightIndexes: [] }): SpecTreeNode<T> {
specs.forEach((spec) => buildSpecTreeRecursive(spec.relative, root, spec))
collapseEmptyChildren(root)
return root
}
// Given a node, return the indexes of the characters that should be highlighted
// The indexes are matched to the `relative` and `baseName` keys of FoundSpec, and
// depending on whether the node is a leaf or not, the indexes are normalized to the position
// of the node's location in the tree.
// If a search of "src/comp" is given with indexes [0,1,2,3,4,5,6,7], the indexes will be split
// into [0,1,2] and [4,5,6,7] corresponding with a
// - src
// - components
// - index.ts
// tree (given that src/comp is not collapsed)
function getHighlightIndexes <T extends FoundSpec> (node: SpecTreeNode<T>) {
if (!(node.data as any)?.fuzzyIndexes) {
return []
}
const { relative: relativeIndexes, baseName: baseNameIndexes } = (node.data as FuzzyFoundSpec<T>).fuzzyIndexes
// When there is a full (or close to) relative match, baseName will no longer match.
// If we have indexes for baseName we show them, otherwise we pull from relative to derive
// baseName indexes
if (node.isLeaf && baseNameIndexes.length > 0) {
return baseNameIndexes
}
const maxIndex = node.id.length - 1
const minIndex = maxIndex - node.name.length + 1
const res = relativeIndexes.filter((index) => index >= minIndex && index <= maxIndex)
return res.map((idx) => idx - minIndex)
}
export function buildSpecTreeRecursive<T extends FoundSpec> (path: string, tree: SpecTreeNode<T>, data?: T) {
const [firstFile, ...rest] = path.split(getRegexSeparator())
const id = tree.id ? [tree.id, firstFile].join(getSeparator()) : firstFile
const newNode: SpecTreeNode<T> = { name: firstFile, isLeaf: false, children: [], parent: tree, data, id, highlightIndexes: [] }
if (rest.length < 1) {
newNode.isLeaf = true
newNode.highlightIndexes = getHighlightIndexes(newNode)
tree.children.push(newNode)
return tree
}
const foundChild = tree.children.find((child) => child.name === firstFile)
if (foundChild) {
buildSpecTreeRecursive(rest.join(getSeparator()), foundChild, data)
return tree
}
newNode.highlightIndexes = getHighlightIndexes(newNode)
const newTree = buildSpecTreeRecursive(rest.join(getSeparator()), newNode, data)
tree.children.push(newTree)
return tree
}
function collapseEmptyChildren<T extends FoundSpec> (node: SpecTreeNode<T>) {
for (const child of node.children) {
collapseEmptyChildren(child)
}
if (node.isLeaf) {
return
}
// Root name of our tree is '/'. We don't want to collapse into the root node
// so we check node.parent.parent
if (node.parent && node.parent.parent && (node.parent.children.length === 1)) {
node.parent.name = [node.parent.name, node.name].join(getSeparator())
node.parent.id = [node.parent.id, node.name].join(getSeparator())
node.parent.children = node.children
node.parent.highlightIndexes = getHighlightIndexes(node.parent)
}
return
}
export type UseCollapsibleTreeNode <T extends RawNode<T>> = {
// control open/close state
hidden: ComputedRef<boolean>
expanded: Ref<boolean>
toggle: () => void
// Depth of a particular node -- 1 indexed
depth: number
parent?: UseCollapsibleTreeNode<T>
children: UseCollapsibleTreeNode<T>[]
} & { [K in keyof T]: T[K]}
export interface UseCollapsibleTreeOptions {
expandInitially?: boolean
dropRoot?: boolean
/**
* Provide a long-lived cache to preserve directory collapse state across tree re-builds.
* This can be useful when row data is updating but doesn't represent a change to the
* structure of the tree.
*/
cache?: Map<string, boolean>
}
function collectRoots<T extends RawNode<T>> (node: UseCollapsibleTreeNode<T> | null, acc: UseCollapsibleTreeNode<T>[] = []) {
if (!node || !node.parent) {
return acc
}
acc.push(node)
collectRoots<T>(node.parent, acc)
return acc
}
export const useCollapsibleTreeNode = <T extends RawNode<T>>(rawNode: T, options: UseCollapsibleTreeOptions, depth: number, parent: UseCollapsibleTreeNode<T> | null): UseCollapsibleTreeNode<T> => {
const { cache, expandInitially } = options
const treeNode = rawNode as UseCollapsibleTreeNode<T>
const roots = parent ? collectRoots<T>(parent) : []
const [expanded, toggle] = useToggle(cache?.get(rawNode.id) ?? !!expandInitially)
const hidden = computed(() => {
return !!roots.find((r) => r.expanded.value === false)
})
const wrappedToggle = (value?: boolean): boolean => {
const originalState = expanded.value
const newValue = toggle(value)
// If this is a non-hidden directory then watch for expansion changes and register them into the cache if one was provided
if (!!cache && !hidden.value && rawNode.children?.length) {
cache.set(rawNode.id, !originalState)
}
return newValue
}
return {
...treeNode,
depth,
parent,
hidden,
expanded,
toggle: wrappedToggle,
}
}
function buildTree<T extends RawNode<T>> (
rawNode: T,
options: UseCollapsibleTreeOptions,
acc: UseCollapsibleTreeNode<T>[] = [],
depth = 1,
parent: UseCollapsibleTreeNode<T> | null = null,
) {
// console.log(arguments)
const node = useCollapsibleTreeNode<T>(rawNode, options, depth, parent)
acc.push(node)
if (node.children?.length) {
for (const child of node.children) {
buildTree(child, options, acc, depth + 1, node)
}
}
return acc
}
function sortTree<T extends RawNode<T>> (tree: T) {
if (tree.children.length > 0) {
tree.children = tree.children.sort((a, b) => {
if (a.children.length === 0 && b.children.length === 0) {
return a.name > b.name ? 1 : -1
}
if (a.children.length === 0) {
return 1
}
if (b.children.length === 0) {
return -1
}
return a.name > b.name ? 1 : -1
})
tree.children.forEach(sortTree)
}
}
export function useCollapsibleTree <T extends RawNode<T>> (tree: T, options: UseCollapsibleTreeOptions = {}) {
options.expandInitially = options.expandInitially ?? true
sortTree(tree)
const collapsibleTree = buildTree<T>(tree, options)
collapsibleTree.sort((a, b) => {
if (a.parent === b.parent) {
if (a.children.length && !b.children.length) {
return -1
}
return 0
}
return 0
})
return {
tree: options.dropRoot ? collapsibleTree.slice(1) : collapsibleTree,
}
}

View File

@@ -12,7 +12,6 @@ export const useSpecStore = defineStore({
state (): SpecState {
return {
activeSpec: undefined,
specFilter: undefined,
}
},
@@ -20,8 +19,5 @@ export const useSpecStore = defineStore({
setActiveSpec (activeSpec: SpecFile | null) {
this.activeSpec = activeSpec
},
setSpecFilter (filter: string) {
this.specFilter = filter
},
},
})

View File

@@ -0,0 +1,8 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures
/test/__fixtures__/**/*
/test/__babel_fixtures__/**/*
**/__snapshots__

View File

@@ -13,7 +13,8 @@
"test": "yarn test-unit",
"test:clean": "find ./test/__fixtures__ -depth -name 'output.*' -type f -exec rm {} \\;",
"test-debug": "yarn test-unit --inspect-brk=5566",
"test-unit": "mocha --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json -r @packages/ts/register 'test/**/*.spec.ts' --exit --timeout 5000"
"test-unit": "mocha --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json -r @packages/ts/register 'test/**/*.spec.ts' --exit --timeout 5000",
"lint": "eslint --ext .js,.ts,.json, ."
},
"dependencies": {
"@babel/core": "^7",

View File

@@ -27,6 +27,10 @@ import path from 'node:path'
const debug = Debug('test')
describe('config/src/project/utils', () => {
beforeEach(function () {
delete process.env.CYPRESS_COMMERCIAL_RECOMMENDATIONS
})
before(function () {
this.env = process.env;

View File

@@ -10,6 +10,10 @@ import {
} from '../src/project/utils'
describe('config/src/utils', () => {
beforeEach(function () {
delete process.env.CYPRESS_COMMERCIAL_RECOMMENDATIONS
})
describe('hideKeys', () => {
it('removes middle part of the string', () => {
const hidden = hideKeys('12345-xxxx-abcde')

View File

@@ -0,0 +1,9 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures
/test/unit/codegen/files
/test/unit/codegen/tmp
/test/fixtures/**
**/__snapshots__

View File

@@ -0,0 +1,16 @@
{
"extends": [
"../../.eslintrc.js"
],
"parser": "@typescript-eslint/parser",
"overrides": [
{
"files": [
"*.ts"
],
"rules": {
"@typescript-eslint/no-unused-vars": "off"
}
}
]
}

View File

@@ -11,7 +11,8 @@
"tslint": "tslint --config ../ts/tslint.json --project .",
"clean": "rimraf --glob \"./{src,test}/**/*.js\"",
"test": "yarn test-unit",
"test-unit": "mocha -r @packages/ts/register --config ./test/.mocharc.js --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json"
"test-unit": "mocha -r @packages/ts/register --config ./test/.mocharc.js --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json",
"lint": "eslint --ext .js,.ts,.json, ."
},
"dependencies": {
"@babel/code-frame": "7.8.3",

View File

@@ -25,10 +25,10 @@ export class EventCollectorActions {
async recordEvent (event: CollectableEvent): Promise<boolean> {
try {
const dashboardUrl = this.ctx.cloud.getDashboardUrl(cloudEnv)
const cloudUrl = this.ctx.cloud.getCloudUrl(cloudEnv)
await this.ctx.util.fetch(
`${dashboardUrl}/anon-collect`,
`${cloudUrl}/anon-collect`,
{
method: 'POST',
headers: {

View File

@@ -119,7 +119,7 @@ export class ProjectConfigIpc extends EventEmitter {
loadConfig (): Promise<LoadConfigReply> {
return new Promise((resolve, reject) => {
if (this._childProcess.stdout && this._childProcess.stderr) {
// manually pipe plugin stdout and stderr for dashboard capture
// manually pipe plugin stdout and stderr for Cypress Cloud capture
// @see https://github.com/cypress-io/cypress/issues/7434
this._childProcess.stdout.on('data', (data) => process.stdout.write(data))
this._childProcess.stderr.on('data', (data) => process.stderr.write(data))

View File

@@ -465,7 +465,7 @@ export class ProjectConfigManager {
/*
Used to detect the correct file path when a test fails.
It is derived and assigned in the packages/driver in stack_utils.
It's needed to show the correct link to files in repo mgmt tools like GitHub in the dashboard.
It's needed to show the correct link to files in repo mgmt tools like GitHub in Cypress Cloud.
Right now we assume the repoRoot is where the `.git` dir is located.
*/
return this.options.ctx.git?.gitBaseDir

View File

@@ -129,7 +129,7 @@ export interface CoreDataShape {
gqlSocketServer?: Maybe<SocketIONamespace>
}
hasInitializedMode: 'run' | 'open' | null
dashboardGraphQLError: ErrorWrapperSource | null
cloudGraphQLError: ErrorWrapperSource | null
dev: DevStateShape
localSettings: LocalSettingsDataShape
app: AppDataShape
@@ -162,7 +162,7 @@ export function makeCoreData (modeOptions: Partial<AllModeOptions> = {}): CoreDa
machineBrowsers: null,
allBrowsers: null,
hasInitializedMode: null,
dashboardGraphQLError: null,
cloudGraphQLError: null,
dev: {
refreshState: null,
},

View File

@@ -31,9 +31,9 @@ const debug = debugLib('cypress:data-context:sources:CloudDataSource')
const cloudEnv = getenv('CYPRESS_INTERNAL_CLOUD_ENV', process.env.CYPRESS_INTERNAL_ENV || 'development') as keyof typeof REMOTE_SCHEMA_URLS
const REMOTE_SCHEMA_URLS = {
staging: 'https://dashboard-staging.cypress.io',
staging: 'https://cloud-staging.cypress.io',
development: 'http://localhost:3000',
production: 'https://dashboard.cypress.io',
production: 'https://cloud.cypress.io',
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -99,7 +99,7 @@ export class CloudDataSource {
reset () {
return this.#cloudUrqlClient = createClient({
url: `${this.getDashboardUrl(cloudEnv)}/test-runner-graphql`,
url: `${this.getCloudUrl(cloudEnv)}/test-runner-graphql`,
exchanges: [
dedupExchange,
cacheExchange({
@@ -182,7 +182,7 @@ export class CloudDataSource {
}
#formatWithErrors = async (data: OperationResult<any, any>) => {
// If we receive a 401 from the dashboard, we need to logout the user
// If we receive a 401 from Cypress Cloud, we need to logout the user
if (data.error?.response?.status === 401) {
await this.params.logout()
}
@@ -333,7 +333,7 @@ export class CloudDataSource {
return JSON.parse(this.#lastCache ?? '')
}
getDashboardUrl (env: keyof typeof REMOTE_SCHEMA_URLS) {
getCloudUrl (env: keyof typeof REMOTE_SCHEMA_URLS) {
return REMOTE_SCHEMA_URLS[env]
}
}

View File

@@ -0,0 +1,5 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures

View File

@@ -24,27 +24,6 @@ const clearAllSavedSessions = () => {
})
}
// In webkit, the clear page and clear cookies, etc log messages may be reversed. This isn't an issue, but we just want to test we have both messages.
const validateClearLogs = (logs, sessionGroupId) => {
let clearPageLogIndex = 0
let clearCookiesIndex = 1
if (logs[1].get('name') === 'Clear page') {
clearPageLogIndex = 1
clearCookiesIndex = 0
}
expect(logs[clearPageLogIndex].get()).to.contain({
name: 'Clear page',
group: sessionGroupId,
})
expect(logs[clearCookiesIndex].get()).to.contain({
displayName: 'Clear cookies, localStorage and sessionStorage',
group: sessionGroupId,
})
}
describe('cy.session', { retries: 0 }, () => {
describe('args', () => {
it('accepts string as id', () => {
@@ -147,9 +126,6 @@ describe('cy.session', { retries: 0 }, () => {
const handleValidate = () => {
// both create & restore session clears page after running
cy.contains('Default blank page')
cy.contains('This page was cleared by navigating to about:blank.')
cy.visit('/fixtures/auth/index.html')
cy.contains('Welcome tester')
}
@@ -205,7 +181,7 @@ describe('cy.session', { retries: 0 }, () => {
})
// test must be first to run before blank page visit between each test
it('clears page after setup runs', () => {
it('clears page after command runs', () => {
cy.url().should('eq', 'about:blank')
})
@@ -217,6 +193,7 @@ describe('cy.session', { retries: 0 }, () => {
it('groups session logs correctly', () => {
expect(logs[0].get()).to.deep.contain({
name: 'session',
state: 'passed',
id: sessionGroupId,
sessionInfo: {
id: 'session-1',
@@ -225,7 +202,15 @@ describe('cy.session', { retries: 0 }, () => {
},
})
validateClearLogs([logs[1], logs[2]], sessionGroupId)
expect(logs[1].get()).to.contain({
name: 'Clear page',
group: sessionGroupId,
})
expect(logs[2].get()).to.contain({
displayName: 'Clear cookies, localStorage and sessionStorage',
group: sessionGroupId,
})
const createNewSessionGroup = logs[3].get()
@@ -242,7 +227,7 @@ describe('cy.session', { retries: 0 }, () => {
expect(logs[5].get()).to.contain({
name: 'Clear page',
group: createNewSessionGroup.id,
group: sessionGroupId,
})
})
@@ -285,8 +270,8 @@ describe('cy.session', { retries: 0 }, () => {
})
// test must be first to run before blank page visit between each test
it('does not clear page visit from validate function', () => {
cy.url().should('contain', '/fixtures/auth/index.html')
it('clears page after command runs', () => {
cy.url().should('eq', 'about:blank')
})
it('successfully creates new session and validates it', () => {
@@ -298,6 +283,7 @@ describe('cy.session', { retries: 0 }, () => {
it('groups session logs correctly', () => {
expect(logs[0].get()).to.deep.contain({
name: 'session',
state: 'passed',
id: sessionGroupId,
sessionInfo: {
id: sessionId,
@@ -306,7 +292,15 @@ describe('cy.session', { retries: 0 }, () => {
},
})
validateClearLogs([logs[1], logs[2]], sessionGroupId)
expect(logs[1].get()).to.contain({
name: 'Clear page',
group: sessionGroupId,
})
expect(logs[2].get()).to.contain({
displayName: 'Clear cookies, localStorage and sessionStorage',
group: sessionGroupId,
})
const createNewSessionGroup = logs[3].get()
@@ -321,22 +315,22 @@ describe('cy.session', { retries: 0 }, () => {
group: createNewSessionGroup.id,
})
expect(logs[5].get()).to.contain({
name: 'Clear page',
group: createNewSessionGroup.id,
})
const validateSessionGroup = logs[6].get()
const validateSessionGroup = logs[5].get()
expect(validateSessionGroup).to.contain({
displayName: 'Validate session',
group: sessionGroupId,
})
expect(logs[7].get()).to.deep.contain({
expect(logs[6].get()).to.deep.contain({
alias: ['validateSession'],
group: validateSessionGroup.id,
})
expect(logs[7].get()).to.contain({
name: 'Clear page',
group: sessionGroupId,
})
})
})
@@ -348,10 +342,11 @@ describe('cy.session', { retries: 0 }, () => {
cy.once('fail', (err) => {
expect(setup).to.be.calledOnce
expect(validate).to.be.calledOnce
expect(clearPageCount, 'total times session cleared the page').to.eq(2)
expect(clearPageCount, 'total times session cleared the page').to.eq(1)
expect(err.message).to.contain('This error occurred while validating the created session')
expect(logs[0].get()).to.deep.contain({
name: 'session',
state: 'failed',
id: sessionGroupId,
sessionInfo: {
id: `session-${Cypress.state('test').id}`,
@@ -360,7 +355,15 @@ describe('cy.session', { retries: 0 }, () => {
},
})
validateClearLogs([logs[1], logs[2]], sessionGroupId)
expect(logs[1].get()).to.contain({
name: 'Clear page',
group: sessionGroupId,
})
expect(logs[2].get()).to.contain({
displayName: 'Clear cookies, localStorage and sessionStorage',
group: sessionGroupId,
})
const createNewSessionGroup = logs[3].get()
@@ -375,19 +378,14 @@ describe('cy.session', { retries: 0 }, () => {
group: createNewSessionGroup.id,
})
expect(logs[5].get()).to.contain({
name: 'Clear page',
group: createNewSessionGroup.id,
})
const validateSessionGroup = logs[6].get()
const validateSessionGroup = logs[5].get()
expect(validateSessionGroup).to.contain({
displayName: 'Validate session',
group: sessionGroupId,
})
expect(logs[7].get()).to.deep.contain({
expect(logs[6].get()).to.deep.contain({
alias: ['validateSession'],
group: validateSessionGroup.id,
})
@@ -426,12 +424,13 @@ describe('cy.session', { retries: 0 }, () => {
it('successfully restores saved session', () => {
expect(setup).to.not.be.called
expect(validate).to.not.be.called
expect(clearPageCount, 'total times session cleared the page').to.eq(1)
expect(clearPageCount, 'total times session cleared the page').to.eq(2)
})
it('groups session logs correctly', () => {
expect(logs[0].get()).to.contain({
name: 'session',
state: 'passed',
id: sessionGroupId,
})
@@ -445,7 +444,15 @@ describe('cy.session', { retries: 0 }, () => {
},
})
validateClearLogs([logs[1], logs[2]], sessionGroupId)
expect(logs[1].get()).to.contain({
name: 'Clear page',
group: sessionGroupId,
})
expect(logs[2].get()).to.contain({
displayName: 'Clear cookies, localStorage and sessionStorage',
group: sessionGroupId,
})
const restoreSavedSessionGroup = logs[3].get()
@@ -474,19 +481,20 @@ describe('cy.session', { retries: 0 }, () => {
})
// test must be first to run before blank page visit between each test
it('does not clear page visit from validate function', () => {
cy.url().should('contain', '/fixtures/auth/index.html')
it('clears page after command runs', () => {
cy.url().should('eq', 'about:blank')
})
it('successfully restores saved session', () => {
expect(setup).to.not.be.called
expect(validate).to.be.calledOnce
expect(clearPageCount, 'total times session cleared the page').to.eq(1)
expect(clearPageCount, 'total times session cleared the page').to.eq(2)
})
it('groups session logs correctly', () => {
expect(logs[0].get()).to.contain({
name: 'session',
state: 'passed',
id: sessionGroupId,
})
@@ -500,7 +508,15 @@ describe('cy.session', { retries: 0 }, () => {
},
})
validateClearLogs([logs[1], logs[2]], sessionGroupId)
expect(logs[1].get()).to.contain({
name: 'Clear page',
group: sessionGroupId,
})
expect(logs[2].get()).to.contain({
displayName: 'Clear cookies, localStorage and sessionStorage',
group: sessionGroupId,
})
const restoreSavedSessionGroup = logs[3].get()
@@ -548,8 +564,8 @@ describe('cy.session', { retries: 0 }, () => {
})
// test must be first to run before blank page visit between each test
it('does not clear page visit from validate function', () => {
cy.url().should('contain', '/fixtures/auth/index.html')
it('clears page after command runs', () => {
cy.url().should('eq', 'about:blank')
})
it('successfully recreates session', () => {
@@ -561,6 +577,7 @@ describe('cy.session', { retries: 0 }, () => {
it('groups session logs correctly', () => {
expect(logs[0].get()).to.contain({
name: 'session',
state: 'warned',
id: sessionGroupId,
})
@@ -574,7 +591,15 @@ describe('cy.session', { retries: 0 }, () => {
},
})
validateClearLogs([logs[1], logs[2]], sessionGroupId)
expect(logs[1].get()).to.contain({
name: 'Clear page',
group: sessionGroupId,
})
expect(logs[2].get()).to.contain({
displayName: 'Clear cookies, localStorage and sessionStorage',
group: sessionGroupId,
})
const restoreSavedSessionGroup = logs[3].get()
@@ -598,7 +623,15 @@ describe('cy.session', { retries: 0 }, () => {
// this error is associated with the group since the validation rejected
expect(logs[4].get('error').message).to.contain('This error occurred while validating the restored session')
validateClearLogs([logs[6], logs[7]], sessionGroupId)
expect(logs[6].get()).to.contain({
name: 'Clear page',
group: sessionGroupId,
})
expect(logs[7].get()).to.contain({
displayName: 'Clear cookies, localStorage and sessionStorage',
group: sessionGroupId,
})
const createNewSessionGroup = logs[8].get()
@@ -613,22 +646,22 @@ describe('cy.session', { retries: 0 }, () => {
group: createNewSessionGroup.id,
})
expect(logs[10].get()).to.contain({
name: 'Clear page',
group: createNewSessionGroup.id,
})
const secondValidateSessionGroup = logs[11].get()
const secondValidateSessionGroup = logs[10].get()
expect(secondValidateSessionGroup).to.contain({
displayName: 'Validate session',
group: sessionGroupId,
})
expect(logs[12].get()).to.deep.contain({
expect(logs[11].get()).to.deep.contain({
alias: ['validateSession'],
group: secondValidateSessionGroup.id,
})
expect(logs[12].get()).to.contain({
name: 'Clear page',
group: sessionGroupId,
})
})
})
@@ -638,7 +671,7 @@ describe('cy.session', { retries: 0 }, () => {
cy.log('Creating new session for test')
cy.session(`session-${Cypress.state('test').id}`, setup, { validate })
.then(() => {
// reset and only test restored session
// reset and only test restored session
resetMocks()
validate.callsFake(() => Promise.reject(false))
})
@@ -647,10 +680,11 @@ describe('cy.session', { retries: 0 }, () => {
expect(err.message).to.contain('Your `cy.session` **validate** promise rejected with false')
expect(setup).to.be.calledOnce
expect(validate).to.be.calledTwice
expect(clearPageCount, 'total times session cleared the page').to.eq(3)
expect(clearPageCount, 'total times session cleared the page').to.eq(2)
expect(logs[0].get()).to.contain({
name: 'session',
state: 'failed',
id: sessionGroupId,
})
@@ -664,7 +698,15 @@ describe('cy.session', { retries: 0 }, () => {
},
})
validateClearLogs([logs[1], logs[2]], sessionGroupId)
expect(logs[1].get()).to.contain({
name: 'Clear page',
group: sessionGroupId,
})
expect(logs[2].get()).to.contain({
displayName: 'Clear cookies, localStorage and sessionStorage',
group: sessionGroupId,
})
const restoreSavedSessionGroup = logs[3].get()
@@ -688,7 +730,15 @@ describe('cy.session', { retries: 0 }, () => {
// this error is associated with the group since the validation rejected
expect(logs[4].get('error').message).to.contain('Your `cy.session` **validate** promise rejected with false.')
validateClearLogs([logs[6], logs[7]], sessionGroupId)
expect(logs[6].get()).to.contain({
name: 'Clear page',
group: sessionGroupId,
})
expect(logs[7].get()).to.contain({
displayName: 'Clear cookies, localStorage and sessionStorage',
group: sessionGroupId,
})
const createNewSessionGroup = logs[8].get()
@@ -703,19 +753,14 @@ describe('cy.session', { retries: 0 }, () => {
group: createNewSessionGroup.id,
})
expect(logs[10].get()).to.contain({
name: 'Clear page',
group: createNewSessionGroup.id,
})
const secondValidateSessionGroup = logs[11].get()
const secondValidateSessionGroup = logs[10].get()
expect(secondValidateSessionGroup).to.contain({
displayName: 'Validate session',
group: sessionGroupId,
})
expect(logs[12].get()).to.deep.contain({
expect(logs[11].get()).to.deep.contain({
alias: ['validateSession'],
group: secondValidateSessionGroup.id,
})
@@ -876,6 +921,7 @@ describe('cy.session', { retries: 0 }, () => {
it('groups session logs correctly', () => {
expect(logs[0].get()).to.deep.contain({
name: 'session',
state: 'passed',
id: sessionGroupId,
sessionInfo: {
id: 'session-1',
@@ -954,6 +1000,7 @@ describe('cy.session', { retries: 0 }, () => {
it('groups session logs correctly', () => {
expect(logs[0].get()).to.deep.contain({
name: 'session',
state: 'passed',
id: sessionGroupId,
sessionInfo: {
id: sessionId,
@@ -1006,6 +1053,7 @@ describe('cy.session', { retries: 0 }, () => {
expect(err.message).to.contain('Your `cy.session` **validate** promise rejected with false')
expect(logs[0].get()).to.deep.contain({
name: 'session',
state: 'failed',
id: sessionGroupId,
sessionInfo: {
id: `session-${Cypress.state('test').id}`,
@@ -1083,6 +1131,7 @@ describe('cy.session', { retries: 0 }, () => {
it('groups session logs correctly', () => {
expect(logs[0].get()).to.contain({
name: 'session',
state: 'passed',
id: sessionGroupId,
})
@@ -1140,6 +1189,7 @@ describe('cy.session', { retries: 0 }, () => {
it('groups session logs correctly', () => {
expect(logs[0].get()).to.contain({
name: 'session',
state: 'passed',
id: sessionGroupId,
})
@@ -1216,6 +1266,7 @@ describe('cy.session', { retries: 0 }, () => {
it('groups session logs correctly', () => {
expect(logs[0].get()).to.contain({
name: 'session',
state: 'warned',
id: sessionGroupId,
})
@@ -1307,6 +1358,7 @@ describe('cy.session', { retries: 0 }, () => {
expect(logs[0].get()).to.contain({
name: 'session',
state: 'failed',
id: sessionGroupId,
})

View File

@@ -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
})
},
})

View File

@@ -8,7 +8,8 @@
"cypress:open": "node ../../scripts/cypress open",
"cypress:run": "node ../../scripts/cypress run --spec \"cypress/e2e/*/*\",\"cypress/e2e/*/!(origin|sessions)/**/*\"",
"postinstall": "patch-package",
"start": "node -e 'console.log(require(`chalk`).red(`\nError:\n\tRunning \\`yarn start\\` is no longer needed for driver/cypress tests.\n\tWe now automatically spawn the server in e2e.setupNodeEvents config.\n\tChanges to the server will be watched and reloaded automatically.`))'"
"start": "node -e 'console.log(require(`chalk`).red(`\nError:\n\tRunning \\`yarn start\\` is no longer needed for driver/cypress tests.\n\tWe now automatically spawn the server in e2e.setupNodeEvents config.\n\tChanges to the server will be watched and reloaded automatically.`))'",
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json, ."
},
"dependencies": {},
"devDependencies": {

View File

@@ -8,6 +8,7 @@ import SessionsManager from './manager'
import {
getConsoleProps,
navigateAboutBlank,
SESSION_STEPS,
statusMap,
} from './utils'
@@ -27,8 +28,6 @@ export default function (Commands, Cypress, cy) {
const sessionsManager = new SessionsManager(Cypress, cy)
const sessions = sessionsManager.sessions
type SESSION_STEPS = 'create' | 'restore' | 'recreate' | 'validate'
Cypress.on('run:start', () => {
// @ts-ignore
Object.values(Cypress.state('activeSessions') || {}).forEach((sessionData: ServerSessionData) => {
@@ -42,10 +41,7 @@ export default function (Commands, Cypress, cy) {
return
}
// Component testing does not support navigation and handles clearing the page via mount utils
const clearPage = Cypress.testingType === 'e2e' ? navigateAboutBlank(false) : new Cypress.Promise.resolve()
return clearPage
return navigateAboutBlank(false)
.then(() => sessions.clearCurrentSessionData())
.then(() => Cypress.backend('reset:rendered:html:origins'))
})
@@ -134,6 +130,7 @@ export default function (Commands, Cypress, cy) {
function setSessionLogStatus (status: string) {
_log.set({
state: statusMap.commandState(status),
sessionInfo: {
id: session.id,
isGlobalSession: session.cacheAcrossSpecs,
@@ -182,7 +179,6 @@ export default function (Commands, Cypress, cy) {
})
.then(async () => {
cy.state('onQueueFailed', null)
await navigateAboutBlank()
const data = await sessions.getCurrentSessionData()
_.extend(existingSession, data)
@@ -222,7 +218,7 @@ export default function (Commands, Cypress, cy) {
return sessions.setSessionData(testSession)
}
function validateSession (existingSession, step: SESSION_STEPS) {
function validateSession (existingSession, step: keyof typeof SESSION_STEPS) {
const isValidSession = true
if (!existingSession.validate) {
@@ -311,11 +307,7 @@ export default function (Commands, Cypress, cy) {
// skip all commands between this command which errored and _commandToRunAfterValidation
for (let i = cy.queue.index; i < index; i++) {
const cmd = commands[i]
if (!cmd.get('restore-within')) {
commands[i].skip()
}
commands[i].skip()
}
// restore within subject back to the original subject used when
@@ -359,7 +351,7 @@ export default function (Commands, Cypress, cy) {
}
const failValidation = (err) => {
if (step === 'restore') {
if (step === SESSION_STEPS.restore) {
enhanceErr(err)
// move to recreate session flow
@@ -427,18 +419,18 @@ export default function (Commands, Cypress, cy) {
await navigateAboutBlank()
await sessions.clearCurrentSessionData()
return createSession(existingSession, step)
return cy.whenStable(() => createSession(existingSession, step))
})
.then(() => validateSession(existingSession, step))
.then(async (isValidSession: boolean) => {
if (!isValidSession) {
throw new Error('not a valid session :(')
return 'failed'
}
sessionsManager.registeredSessions.set(existingSession.id, true)
await sessions.saveSessionData(existingSession)
setSessionLogStatus(statusMap.complete(step))
return statusMap.complete(step)
})
}
@@ -450,19 +442,19 @@ export default function (Commands, Cypress, cy) {
*/
const restoreSessionWorkflow = (existingSession: SessionData) => {
return cy.then(async () => {
setSessionLogStatus('restoring')
setSessionLogStatus(statusMap.inProgress(SESSION_STEPS.restore))
await navigateAboutBlank()
await sessions.clearCurrentSessionData()
return restoreSession(existingSession)
})
.then(() => validateSession(existingSession, 'restore'))
.then(() => validateSession(existingSession, SESSION_STEPS.restore))
.then((isValidSession: boolean) => {
if (!isValidSession) {
return createSessionWorkflow(existingSession, 'recreate')
return createSessionWorkflow(existingSession, SESSION_STEPS.recreate)
}
setSessionLogStatus('restored')
return statusMap.complete(SESSION_STEPS.restore)
})
}
@@ -493,13 +485,16 @@ export default function (Commands, Cypress, cy) {
_.extend(session, _.omit(serverStoredSession, 'setup', 'validate'))
session.hydrated = true
} else {
return createSessionWorkflow(session, 'create')
return createSessionWorkflow(session, SESSION_STEPS.create)
}
}
return restoreSessionWorkflow(session)
}).then(() => {
_log.set({ state: 'passed' })
}).then((status: 'created' | 'restored' | 'recreated' | 'failed') => {
return navigateAboutBlank()
.then(() => {
setSessionLogStatus(status)
})
})
})
},

View File

@@ -194,16 +194,42 @@ const getPostMessageLocalStorage = (specWindow, origins): Promise<any[]> => {
}
function navigateAboutBlank (session: boolean = true) {
if (Cypress.config('testIsolation') === 'off') {
return
// Component testing does not support navigation and handles clearing the page via mount utils
if (Cypress.testingType === 'component' || Cypress.config('testIsolation') === 'off') {
return Promise.resolve()
}
Cypress.action('cy:url:changed', '')
return new Promise((resolve) => {
cy.once('window:load', resolve)
return Cypress.action('cy:visit:blank', { type: session ? 'session' : 'session-lifecycle' }) as unknown as Promise<void>
Cypress.action('cy:url:changed', '')
return Cypress.action('cy:visit:blank', { type: session ? 'session' : 'session-lifecycle' }) as unknown as Promise<void>
})
}
const enum SESSION_STEPS {
create = 'create',
restore = 'restore',
recreate = 'recreate',
validate = 'validate',
}
const statusMap = {
commandState: (status: string) => {
switch (status) {
case 'failed':
return 'failed'
case 'recreating':
case 'recreated':
return 'warned'
case 'created':
case 'restored':
return 'passed'
default:
return 'pending'
}
},
inProgress: (step) => {
switch (step) {
case 'create':
@@ -250,5 +276,6 @@ export {
getConsoleProps,
getPostMessageLocalStorage,
navigateAboutBlank,
SESSION_STEPS,
statusMap,
}

View File

@@ -123,7 +123,7 @@ function mutateConfiguration (testConfig: ResolvedTestConfigOverride, config, en
// this is called during test onRunnable time
// in order to resolve the test config upfront before test runs
// note: must return as an object to meet the dashboard recording API
// note: must return as an object to meet the Cypress Cloud recording API
export function getResolvedTestConfigOverride (test): ResolvedTestConfigOverride {
let curr = test
let testConfigList: TestConfig[] = []
@@ -147,7 +147,7 @@ export function getResolvedTestConfigOverride (test): ResolvedTestConfigOverride
const testConfig = {
testConfigList: testConfigList.filter(({ overrides }) => overrides !== undefined),
// collect test overrides to send to the dashboard api when @packages/server is ran in record mode
// collect test overrides to send to the Cypress Cloud api when @packages/server is ran in record mode
unverifiedTestConfig: _.reduce(testConfigList, (acc, { overrides }) => _.extend(acc, overrides), {}),
}

View File

@@ -155,7 +155,7 @@ const wrap = (runnable): Record<string, any> | null => {
// Reduce runnable down to its props and collections.
// Sent to the Reporter to populate command log
// and send to the Dashboard when in record mode.
// and send to Cypress Cloud when in record mode.
const wrapAll = (runnable): Record<string, any> => {
return _.extend(
{},

View File

@@ -0,0 +1,5 @@
**/dist
**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/cypress/fixtures

View File

@@ -47,5 +47,5 @@ Upgrading `electron` involves more than just bumping this package's `package.jso
- Update the Docker `image`s to the new matching `browsers` image.
- Update the `xcode` version to one with the same major Node.js version bundled. There is usually not an exact match, this is ok as long as the major version number as the same.
- [ ] Do a global search for the old Node.js version to identify any new areas that may need updating/unification, and update those locations (and this document!)
- [ ] **Manually smoke test `cypress open`.** Upgrading Electron can break the `desktop-gui` in unexpected ways. Since testing in this area is weak, double-check that things like launching `cypress open`, signing into the Dashboard, and launching Electron tests still work.
- [ ] **Manually smoke test `cypress open`.** Upgrading Electron can break the `desktop-gui` in unexpected ways. Since testing in this area is weak, double-check that things like launching `cypress open`, signing into Cypress Cloud, and launching Electron tests still work.
- [ ] **Fix failing tests.** Usually, these are due to breaking changes in either Node.js or Electron. Check the changelogs of both to find relevant changes.

View File

@@ -12,7 +12,8 @@
"test": "yarn test-unit",
"test-debug": "yarn test-unit --inspect-brk=5566",
"test-unit": "mocha --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json",
"test-watch": "yarn test-unit --watch"
"test-watch": "yarn test-unit --watch",
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json, ."
},
"dependencies": {
"@packages/icons": "0.0.0-development",

View File

@@ -36,7 +36,7 @@
</head>
<body><pre><span style="color:#e05561">The run you are attempting to access is already complete and will not accept new groups.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">The existing run is: <span style="color:#4ec4ff">https://dashboard.cypress.io/project/abcd/runs/1<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">The existing run is: <span style="color:#4ec4ff">https://cloud.cypress.io/project/abcd/runs/1<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">
<span style="color:#e05561">When a run finishes all of its groups, it waits for a configurable set of time before finally completing. You must add more groups during that time period.<span style="color:#e6e6e6">
<span style="color:#e05561"><span style="color:#e6e6e6">

Some files were not shown because too many files have changed in this diff Show More