From 9aad2aafdde2d1b30de80912e34c477ebfeb8a1b Mon Sep 17 00:00:00 2001 From: Zachary Williams Date: Wed, 8 Feb 2023 12:52:48 -0600 Subject: [PATCH 1/4] misc: improve debug loading text wrap responsiveness (#25703) --- cli/CHANGELOG.md | 1 + .../src/debug/empty/DebugEmptyStates.cy.tsx | 4 ++++ .../app/src/debug/empty/DebugEmptyView.vue | 22 +++++++++---------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 86f02681fa6f..c8a7f037bb8b 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -17,6 +17,7 @@ _Released 02/14/2023 (PENDING)_ - Improved the layout of the Debug Page on smaller viewports when there is a pending run. Addresses [#25664](https://github.com/cypress-io/cypress/issues/25664). - Improved the layout of the Debug Page when displaying informational messages. Addresses [#25669](https://github.com/cypress-io/cypress/issues/25669). - Icons in Debug page will no longer shrink at small viewports. Addresses [#25665](https://github.com/cypress-io/cypress/issues/25665). +- Improved the layout of the Debug Page empty states on smaller viewports. Addressed in [#25703](https://github.com/cypress-io/cypress/pull/25703). - Increased the spacing between elements and their associated tooltip and added borders around artifact links on the Debug Page. Addresses [#25666](https://github.com/cypress-io/cypress/issues/25666). **Dependency Updates:** diff --git a/packages/app/src/debug/empty/DebugEmptyStates.cy.tsx b/packages/app/src/debug/empty/DebugEmptyStates.cy.tsx index dad90a051224..15339fe3adfe 100644 --- a/packages/app/src/debug/empty/DebugEmptyStates.cy.tsx +++ b/packages/app/src/debug/empty/DebugEmptyStates.cy.tsx @@ -33,6 +33,10 @@ describe('Debug page empty states', () => { cy.findByRole('link', { name: 'Learn about project setup in Cypress' }).should('have.attr', 'href', 'https://on.cypress.io/adding-new-project?utm_source=Binary%3A+Launchpad&utm_medium=Debug+Tab&utm_campaign=Learn+More') cy.percySnapshot() + + cy.viewport(600, 600) + + cy.percySnapshot('responsive') }) }) diff --git a/packages/app/src/debug/empty/DebugEmptyView.vue b/packages/app/src/debug/empty/DebugEmptyView.vue index d0e77d19a009..e83be1670183 100644 --- a/packages/app/src/debug/empty/DebugEmptyView.vue +++ b/packages/app/src/debug/empty/DebugEmptyView.vue @@ -24,18 +24,16 @@ :rows="loadingRows" > From 199c4e6ba2db98cdf7d92a577c96b4716ecea1e5 Mon Sep 17 00:00:00 2001 From: Mike Plummer Date: Thu, 9 Feb 2023 15:49:19 -0600 Subject: [PATCH 2/4] misc: Increase max failures in IATR badge to 99 (#25737) --- cli/CHANGELOG.md | 1 + .../src/navigation/SidebarNavigation.cy.tsx | 8 ++++++-- .../app/src/navigation/SidebarNavigation.vue | 4 ++-- .../src/navigation/SidebarNavigationRow.vue | 18 +++++++++++++++++- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index c8a7f037bb8b..474727f24138 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -17,6 +17,7 @@ _Released 02/14/2023 (PENDING)_ - Improved the layout of the Debug Page on smaller viewports when there is a pending run. Addresses [#25664](https://github.com/cypress-io/cypress/issues/25664). - Improved the layout of the Debug Page when displaying informational messages. Addresses [#25669](https://github.com/cypress-io/cypress/issues/25669). - Icons in Debug page will no longer shrink at small viewports. Addresses [#25665](https://github.com/cypress-io/cypress/issues/25665). +- Increased maximum number of failing tests to reflect in sidebar badge to 99. Addresses [#25662](https://github.com/cypress-io/cypress/issues/25662). - Improved the layout of the Debug Page empty states on smaller viewports. Addressed in [#25703](https://github.com/cypress-io/cypress/pull/25703). - Increased the spacing between elements and their associated tooltip and added borders around artifact links on the Debug Page. Addresses [#25666](https://github.com/cypress-io/cypress/issues/25666). diff --git a/packages/app/src/navigation/SidebarNavigation.cy.tsx b/packages/app/src/navigation/SidebarNavigation.cy.tsx index 2af4905e297d..10aeb2997698 100644 --- a/packages/app/src/navigation/SidebarNavigation.cy.tsx +++ b/packages/app/src/navigation/SidebarNavigation.cy.tsx @@ -170,10 +170,14 @@ describe('SidebarNavigation', () => { it('renders failure badge', () => { mountComponent({ cloudProject: { status: 'FAILED', numFailedTests: 1 } }) cy.findByLabelText('Relevant run had 1 test failure').should('be.visible').contains('1') - cy.percySnapshot('Debug Badge:failed') + cy.percySnapshot('Debug Badge:failed:single-digit') mountComponent({ cloudProject: { status: 'FAILED', numFailedTests: 10 } }) - cy.findByLabelText('Relevant run had 10 test failures').should('be.visible').contains('9+') + cy.findByLabelText('Relevant run had 10 test failures').should('be.visible').contains('10') + cy.percySnapshot('Debug Badge:failed:double-digit') + + mountComponent({ cloudProject: { status: 'FAILED', numFailedTests: 100 } }) + cy.findByLabelText('Relevant run had 100 test failures').should('be.visible').contains('99+') cy.percySnapshot('Debug Badge:failed:truncated') }) diff --git a/packages/app/src/navigation/SidebarNavigation.vue b/packages/app/src/navigation/SidebarNavigation.vue index e0941277fc60..0cbd79e58a6b 100644 --- a/packages/app/src/navigation/SidebarNavigation.vue +++ b/packages/app/src/navigation/SidebarNavigation.vue @@ -202,9 +202,9 @@ watchEffect(() => { let countToDisplay = '0' if (totalFailed) { - countToDisplay = totalFailed < 9 + countToDisplay = totalFailed < 99 ? String(totalFailed) - : '9+' + : '99+' } if (status === 'FAILED') { diff --git a/packages/app/src/navigation/SidebarNavigationRow.vue b/packages/app/src/navigation/SidebarNavigationRow.vue index 7a3eb1a9b0d3..c9624ce3e325 100644 --- a/packages/app/src/navigation/SidebarNavigationRow.vue +++ b/packages/app/src/navigation/SidebarNavigationRow.vue @@ -82,7 +82,23 @@ const props = withDefaults(defineProps <{ }) const badgeVariant = computed(() => { - return props.isNavBarExpanded ? 'ml-16px h-20px text-sm leading-3' : 'absolute outline-gray-1000 outline-2px outline bottom-0 left-36px text-xs h-16px leading-2' + const classes: string[] = [] + + if (props.isNavBarExpanded) { + classes.push('ml-16px', 'h-20px', 'text-sm', 'leading-3') + } else { + classes.push('absolute', 'outline-gray-1000', 'outline-2px', 'outline', 'bottom-0', 'text-xs', 'h-16px', 'leading-2') + + // Keep failure count from overflowing sidebar (#25662) + if ((props.badge.status === 'failed' || props.badge.status === 'error') && props.badge.value.length >= 3) { + classes.push('right-4px') + } else { + // Anything else should left-align and overflow sidebar if needed + classes.push('left-36px') + } + } + + return classes }) const badgeColorStyles = { From acfe868d6201dd52bd1122ee72f0d8fdc86a5337 Mon Sep 17 00:00:00 2001 From: Matt Schile Date: Fri, 10 Feb 2023 09:00:24 -0700 Subject: [PATCH 3/4] chore: exclude collaborator issues/PRs from triage project (#25769) --- .github/workflows/triage_add_to_project.yml | 22 ++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/.github/workflows/triage_add_to_project.yml b/.github/workflows/triage_add_to_project.yml index 4dcf519a2a17..2a5f775d378c 100644 --- a/.github/workflows/triage_add_to_project.yml +++ b/.github/workflows/triage_add_to_project.yml @@ -12,9 +12,25 @@ jobs: add-to-triage-project: name: Add to triage project runs-on: ubuntu-latest + env: + PROJECT_NUMBER: 9 + GITHUB_TOKEN: ${{ secrets.ADD_TO_PROJECT_TOKEN }} steps: - - uses: actions/add-to-project@v0.3.0 + - name: is-collaborator + run: | + gh api graphql -f query=' + query($org: String!, $repo: String!, $user: String!) { + repository(owner: $org, name: $repo) { + collaborators(query: $user, first: 1) { + totalCount + } + } + } ' -f org=${{ github.repository_owner }} -f repo=${{ github.event.repository.name }} -f user=${{ github.actor }} > collaborators.json + + echo 'IS_COLLABORATOR='$(jq -r '.data.repository.collaborators.totalCount' collaborators.json) >> $GITHUB_ENV + - uses: actions/add-to-project@v0.4.0 + # only add issues/prs from outside contributors to the project + if: ${{ env.IS_COLLABORATOR == 0 }} with: - project-url: https://github.com/orgs/cypress-io/projects/9 + project-url: https://github.com/orgs/${{github.repository_owner}}/projects/${{env.PROJECT_NUMBER}} github-token: ${{ secrets.ADD_TO_PROJECT_TOKEN }} - From 19e091d0bc2d1f4e6a6e62d2f81ea6a2f60d531a Mon Sep 17 00:00:00 2001 From: Alejandro Estrada Date: Fri, 10 Feb 2023 11:32:54 -0500 Subject: [PATCH 4/4] feat: add --auto-cancel-after-failures flag (#25237) Co-authored-by: Emily Rohrbough Co-authored-by: Matt Schile Co-authored-by: Ryan Pei Co-authored-by: Emily Rohrbough --- cli/CHANGELOG.md | 1 + cli/__snapshots__/cli_spec.js | 47 +- cli/lib/cli.js | 2 + cli/lib/exec/run.js | 4 + cli/lib/util.js | 1 + cli/test/lib/cli_spec.js | 10 + cli/test/lib/cypress_spec.js | 19 +- cli/test/lib/exec/run_spec.js | 18 + cli/types/cypress-npm-api.d.ts | 4 + cli/types/cypress.d.ts | 1 + .../CLOUD_AUTO_CANCEL_MISMATCH.html | 48 ++ ...OUD_AUTO_CANCEL_NOT_AVAILABLE_IN_PLAN.html | 42 ++ .../RECORD_PARAMS_WITHOUT_RECORDING.html | 4 +- packages/errors/src/errors.ts | 29 +- .../test/unit/visualSnapshotErrors_spec.ts | 16 + packages/graphql/schemas/schema.graphql | 2 + .../__snapshots__/browsers_spec.ts.js | 192 +++---- .../launcher/__snapshots__/darwin_spec.ts.js | 366 +++++++------- .../launcher/__snapshots__/windows_spec.ts.js | 478 +++++++++--------- packages/server/__snapshots__/args_spec.js | 32 ++ packages/server/__snapshots__/cypress_spec.js | 43 +- packages/server/lib/cloud/api.ts | 1 + packages/server/lib/modes/record.js | 23 +- packages/server/lib/modes/run.ts | 14 +- packages/server/lib/util/args.js | 8 +- packages/server/lib/util/print-run.ts | 10 +- .../server/test/integration/cypress_spec.js | 45 ++ .../server/test/unit/modes/record_spec.js | 3 + packages/server/test/unit/util/args_spec.js | 52 ++ system-tests/__snapshots__/record_spec.js | 30 ++ system-tests/test/record_spec.js | 25 + 31 files changed, 1006 insertions(+), 564 deletions(-) create mode 100644 packages/errors/__snapshot-html__/CLOUD_AUTO_CANCEL_MISMATCH.html create mode 100644 packages/errors/__snapshot-html__/CLOUD_AUTO_CANCEL_NOT_AVAILABLE_IN_PLAN.html diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 474727f24138..8fc5ec96fa00 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -11,6 +11,7 @@ _Released 02/14/2023 (PENDING)_ **Features:** - Added the "Open in IDE" feature for failed tests reported from the Debug page. Addressed in [#25691](https://github.com/cypress-io/cypress/pull/25691). +- Added a new CLI flag, called [`--auto-cancel-after-failures`](https://docs.cypress.io/guides/guides/command-line#Options), that overrides the project-level CI ["Auto Cancellation"](https://docs.cypress.io/guides/cloud/smart-orchestration#Auto-Cancellation) value when recording to the Cloud. This gives Cloud users on Business and Enterprise plans the flexibility to alter the auto-cancellation value per run. Addressed in [#25237](https://github.com/cypress-io/cypress/pull/25237). **Misc:** diff --git a/cli/__snapshots__/cli_spec.js b/cli/__snapshots__/cli_spec.js index 69137521fbac..562cbeb7fdf7 100644 --- a/cli/__snapshots__/cli_spec.js +++ b/cli/__snapshots__/cli_spec.js @@ -67,29 +67,30 @@ exports['shows help for run --foo 1'] = ` Runs Cypress tests from the CLI without the GUI Options: - -b, --browser runs Cypress in the browser with the given name. if a filesystem path is supplied, Cypress will attempt to use the browser at that path. - --ci-build-id the unique identifier for a run on your CI provider. typically a "BUILD_ID" env var. this value is automatically detected for most CI providers - --component runs component tests - -c, --config sets configuration values. separate multiple values with a comma. overrides any value in cypress.config.{js,ts,mjs,cjs}. - -C, --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 sets environment variables. separate multiple values with a comma. overrides any value in cypress.config.{js,ts,mjs,cjs} or cypress.env.json - --group a named group for recorded runs in Cypress Cloud - -k, --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) - --no-exit keep the browser open after tests finish - --parallel enables concurrent runs and automatic load balancing of specs across multiple machines or processes - -p, --port runs Cypress on a specific port. overrides any value in cypress.config.{js,ts,mjs,cjs}. - -P, --project 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 Cypress Cloud. - -r, --reporter runs a specific mocha reporter. pass a path to use a custom reporter. defaults to "spec" - -o, --reporter-options options for the mocha reporter. defaults to "null" - -s, --spec runs specific spec file(s). defaults to "all" - -t, --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 + --auto-cancel-after-failures overrides the project-level Cloud configuration to set the failed test threshold for auto cancellation or to disable auto cancellation when recording to the Cloud + -b, --browser runs Cypress in the browser with the given name. if a filesystem path is supplied, Cypress will attempt to use the browser at that path. + --ci-build-id the unique identifier for a run on your CI provider. typically a "BUILD_ID" env var. this value is automatically detected for most CI providers + --component runs component tests + -c, --config sets configuration values. separate multiple values with a comma. overrides any value in cypress.config.{js,ts,mjs,cjs}. + -C, --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 sets environment variables. separate multiple values with a comma. overrides any value in cypress.config.{js,ts,mjs,cjs} or cypress.env.json + --group a named group for recorded runs in Cypress Cloud + -k, --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) + --no-exit keep the browser open after tests finish + --parallel enables concurrent runs and automatic load balancing of specs across multiple machines or processes + -p, --port runs Cypress on a specific port. overrides any value in cypress.config.{js,ts,mjs,cjs}. + -P, --project 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 Cypress Cloud. + -r, --reporter runs a specific mocha reporter. pass a path to use a custom reporter. defaults to "spec" + -o, --reporter-options options for the mocha reporter. defaults to "null" + -s, --spec runs specific spec file(s). defaults to "all" + -t, --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 ------- stderr: ------- diff --git a/cli/lib/cli.js b/cli/lib/cli.js index ce34a1b696f3..04599c93fc0f 100644 --- a/cli/lib/cli.js +++ b/cli/lib/cli.js @@ -93,6 +93,7 @@ const parseVariableOpts = (fnArgs, args) => { } const descriptions = { + autoCancelAfterFailures: 'overrides the project-level Cloud configuration to set the failed test threshold for auto cancellation or to disable auto cancellation when recording to the Cloud', browser: 'runs Cypress in the browser with the given name. if a filesystem path is supplied, Cypress will attempt to use the browser at that path.', cacheClear: 'delete all cached binaries', cachePrune: 'deletes all cached binaries except for the version currently in use', @@ -242,6 +243,7 @@ const addCypressRunCommand = (program) => { .command('run') .usage('[options]') .description('Runs Cypress tests from the CLI without the GUI') + .option('--auto-cancel-after-failures ', text('autoCancelAfterFailures')) .option('-b, --browser ', text('browser')) .option('--ci-build-id ', text('ciBuildId')) .option('--component', text('component')) diff --git a/cli/lib/exec/run.js b/cli/lib/exec/run.js index e070205901b1..5eedabc27b53 100644 --- a/cli/lib/exec/run.js +++ b/cli/lib/exec/run.js @@ -45,6 +45,10 @@ const processRunOptions = (options = {}) => { const args = ['--run-project', options.project] + if (options.autoCancelAfterFailures || options.autoCancelAfterFailures === 0 || options.autoCancelAfterFailures === false) { + args.push('--auto-cancel-after-failures', options.autoCancelAfterFailures) + } + if (options.browser) { args.push('--browser', options.browser) } diff --git a/cli/lib/util.js b/cli/lib/util.js index 1a5e54899018..30a4f763ef68 100644 --- a/cli/lib/util.js +++ b/cli/lib/util.js @@ -192,6 +192,7 @@ const dequote = (str) => { const parseOpts = (opts) => { opts = _.pick(opts, + 'autoCancelAfterFailures', 'browser', 'cachePath', 'cacheList', diff --git a/cli/test/lib/cli_spec.js b/cli/test/lib/cli_spec.js index e5d8506fe0a2..bf6ebf97cb02 100644 --- a/cli/test/lib/cli_spec.js +++ b/cli/test/lib/cli_spec.js @@ -493,6 +493,16 @@ describe('cli', () => { this.exec('run --ci-build-id "123" --group "staging"') expect(run.start).to.be.calledWith({ ciBuildId: '123', group: 'staging' }) }) + + it('call run with --auto-cancel-after-failures', () => { + this.exec('run --auto-cancel-after-failures 4') + expect(run.start).to.be.calledWith({ autoCancelAfterFailures: '4' }) + }) + + it('call run with --auto-cancel-after-failures with false', () => { + this.exec('run --auto-cancel-after-failures false') + expect(run.start).to.be.calledWith({ autoCancelAfterFailures: 'false' }) + }) }) context('cypress open', () => { diff --git a/cli/test/lib/cypress_spec.js b/cli/test/lib/cypress_spec.js index 44579d7ac926..0ed0dd9ef0ea 100644 --- a/cli/test/lib/cypress_spec.js +++ b/cli/test/lib/cypress_spec.js @@ -100,10 +100,27 @@ describe('cypress', function () { } it('calls run#start, passing in options', () => { - return cypress.run({ spec: 'foo' }) + return cypress.run({ spec: 'foo', autoCancelAfterFailures: 4 }) .then(getStartArgs) .then((args) => { expect(args.spec).to.equal('foo') + expect(args.autoCancelAfterFailures).to.equal(4) + }) + }) + + it('calls run#start, passing in autoCancelAfterFailures false', () => { + return cypress.run({ autoCancelAfterFailures: false }) + .then(getStartArgs) + .then((args) => { + expect(args.autoCancelAfterFailures).to.equal(false) + }) + }) + + it('calls run#start, passing in autoCancelAfterFailures 0', () => { + return cypress.run({ autoCancelAfterFailures: 0 }) + .then(getStartArgs) + .then((args) => { + expect(args.autoCancelAfterFailures).to.equal(0) }) }) diff --git a/cli/test/lib/exec/run_spec.js b/cli/test/lib/exec/run_spec.js index a79d1a6e0067..312d77791f3f 100644 --- a/cli/test/lib/exec/run_spec.js +++ b/cli/test/lib/exec/run_spec.js @@ -216,5 +216,23 @@ describe('exec run', function () { ]) }) }) + + it('spawns with --auto-cancel-after-failures value', function () { + return run.start({ autoCancelAfterFailures: 4 }) + .then(() => { + expect(spawn.start).to.be.calledWith([ + '--run-project', process.cwd(), '--auto-cancel-after-failures', 4, + ]) + }) + }) + + it('spawns with --auto-cancel-after-failures value false', function () { + return run.start({ autoCancelAfterFailures: false }) + .then(() => { + expect(spawn.start).to.be.calledWith([ + '--run-project', process.cwd(), '--auto-cancel-after-failures', false, + ]) + }) + }) }) }) diff --git a/cli/types/cypress-npm-api.d.ts b/cli/types/cypress-npm-api.d.ts index a7051b7f1462..23cb8d4e3f13 100644 --- a/cli/types/cypress-npm-api.d.ts +++ b/cli/types/cypress-npm-api.d.ts @@ -95,6 +95,10 @@ declare namespace CypressCommandLine { * Specify the specs to run */ spec: string + /** + * Specify the number of failures to cancel a run being recorded to the Cloud or false to disable auto-cancellation. + */ + autoCancelAfterFailures: number | false } /** diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 3dfaab987170..f18ffe8af444 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -5776,6 +5776,7 @@ declare namespace Cypress { specPattern?: string[] system: SystemDetails tag?: string + autoCancelAfterFailures?: number | false } interface DevServerConfig { diff --git a/packages/errors/__snapshot-html__/CLOUD_AUTO_CANCEL_MISMATCH.html b/packages/errors/__snapshot-html__/CLOUD_AUTO_CANCEL_MISMATCH.html new file mode 100644 index 000000000000..687c1b550fec --- /dev/null +++ b/packages/errors/__snapshot-html__/CLOUD_AUTO_CANCEL_MISMATCH.html @@ -0,0 +1,48 @@ + + + + + + + + + + + +
You passed the --auto-cancel-after-failures flag, but this run originally started with a different value for the --auto-cancel-after-failures flag.
+
+The existing run is: https://cloud.cypress.io/project/abcd/runs/1
+
+The --group flag you passed was: foo
+The --parallel flag you passed was: true
+The --auto-cancel-after-failures flag you passed was: 3
+
+The first setting of --auto-cancel-after-failures for any given run takes precedent.
+
+https://on.cypress.io/auto-cancellation-mismatch
+
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/CLOUD_AUTO_CANCEL_NOT_AVAILABLE_IN_PLAN.html b/packages/errors/__snapshot-html__/CLOUD_AUTO_CANCEL_NOT_AVAILABLE_IN_PLAN.html new file mode 100644 index 000000000000..8f45a1d468c7 --- /dev/null +++ b/packages/errors/__snapshot-html__/CLOUD_AUTO_CANCEL_NOT_AVAILABLE_IN_PLAN.html @@ -0,0 +1,42 @@ + + + + + + + + + + + +
Auto Cancellation is not included under your current billing plan.
+
+To enable this service, please visit your billing and upgrade to another plan with Auto Cancellation.
+
+https://on.cypress.io/set-up-billing
+
\ No newline at end of file diff --git a/packages/errors/__snapshot-html__/RECORD_PARAMS_WITHOUT_RECORDING.html b/packages/errors/__snapshot-html__/RECORD_PARAMS_WITHOUT_RECORDING.html index 32c1b63b4d49..dfa2b68f978d 100644 --- a/packages/errors/__snapshot-html__/RECORD_PARAMS_WITHOUT_RECORDING.html +++ b/packages/errors/__snapshot-html__/RECORD_PARAMS_WITHOUT_RECORDING.html @@ -34,11 +34,11 @@ -
You passed the --ci-build-id, --group, --tag, or --parallel flag without also passing the --record flag.
+    
You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after-failures flag without also passing the --record flag.
 
 The --parallel flag you passed was: true
 
 These flags can only be used when recording to Cypress Cloud.
 
-https://on.cypress.io/record-params-without-recording
+https://on.cypress.io/record-params-without-recording
 
\ No newline at end of file diff --git a/packages/errors/src/errors.ts b/packages/errors/src/errors.ts index 005f5caa508c..4cc53bfa50d6 100644 --- a/packages/errors/src/errors.ts +++ b/packages/errors/src/errors.ts @@ -351,6 +351,32 @@ export const AllCypressErrors = { https://on.cypress.io/run-group-name-not-unique` }, + CLOUD_AUTO_CANCEL_NOT_AVAILABLE_IN_PLAN: (arg1: {link: string}) => { + return errTemplate`\ + ${fmt.highlightSecondary(`Auto Cancellation`)} is not included under your current billing plan. + + To enable this service, please visit your billing and upgrade to another plan with Auto Cancellation. + + ${fmt.off(arg1.link)}` + }, + CLOUD_AUTO_CANCEL_MISMATCH: (arg1: {runUrl: string}) => { + return errTemplate`\ + You passed the ${fmt.flag(`--auto-cancel-after-failures`)} flag, but this run originally started with a different value for the ${fmt.flag(`--auto-cancel-after-failures`)} flag. + + The existing run is: ${fmt.url(arg1.runUrl)} + + ${fmt.listFlags(arg1, { + tags: '--tag', + group: '--group', + parallel: '--parallel', + ciBuildId: '--ciBuildId', + autoCancelAfterFailures: '--auto-cancel-after-failures', + })} + + The first setting of --auto-cancel-after-failures for any given run takes precedent. + + https://on.cypress.io/auto-cancellation-mismatch` + }, DEPRECATED_BEFORE_BROWSER_LAUNCH_ARGS: () => { return errTemplate`\ Deprecation Warning: The ${fmt.highlight(`before:browser:launch`)} plugin event changed its signature in ${fmt.cypressVersion(`4.0.0`)} @@ -390,13 +416,14 @@ export const AllCypressErrors = { }, RECORD_PARAMS_WITHOUT_RECORDING: (arg1: Record) => { return errTemplate`\ - You passed the ${fmt.flag(`--ci-build-id`)}, ${fmt.flag(`--group`)}, ${fmt.flag(`--tag`)}, or ${fmt.flag(`--parallel`)} flag without also passing the ${fmt.flag(`--record`)} flag. + You passed the ${fmt.flag(`--ci-build-id`)}, ${fmt.flag(`--group`)}, ${fmt.flag(`--tag`)}, ${fmt.flag(`--parallel`)}, or ${fmt.flag(`--auto-cancel-after-failures`)} flag without also passing the ${fmt.flag(`--record`)} flag. ${fmt.listFlags(arg1, { ciBuildId: '--ci-build-id', tags: '--tag', group: '--group', parallel: '--parallel', + autoCancelAfterFailures: '--auto-cancel-after-failures', })} These flags can only be used when recording to Cypress Cloud. diff --git a/packages/errors/test/unit/visualSnapshotErrors_spec.ts b/packages/errors/test/unit/visualSnapshotErrors_spec.ts index 967a5048838e..8dd2e3bfeed7 100644 --- a/packages/errors/test/unit/visualSnapshotErrors_spec.ts +++ b/packages/errors/test/unit/visualSnapshotErrors_spec.ts @@ -535,6 +535,22 @@ describe('visual error templates', () => { }], } }, + CLOUD_AUTO_CANCEL_NOT_AVAILABLE_IN_PLAN: () => { + return { + default: [{ link: 'https://on.cypress.io/set-up-billing' }], + } + }, + CLOUD_AUTO_CANCEL_MISMATCH: () => { + return { + default: [{ + runUrl: 'https://cloud.cypress.io/project/abcd/runs/1', + tag: '123', + group: 'foo', + parallel: true, + autoCancelAfterFailures: 3, + }], + } + }, DEPRECATED_BEFORE_BROWSER_LAUNCH_ARGS: () => { return { default: [], diff --git a/packages/graphql/schemas/schema.graphql b/packages/graphql/schemas/schema.graphql index c9a8cbf62921..f1e6bd0448ee 100644 --- a/packages/graphql/schemas/schema.graphql +++ b/packages/graphql/schemas/schema.graphql @@ -1071,6 +1071,8 @@ enum ErrorTypeEnum { CHROME_WEB_SECURITY_NOT_SUPPORTED CLOUD_ALREADY_COMPLETE CLOUD_API_RESPONSE_FAILED_RETRYING + CLOUD_AUTO_CANCEL_MISMATCH + CLOUD_AUTO_CANCEL_NOT_AVAILABLE_IN_PLAN CLOUD_CANCEL_SKIPPED_SPEC CLOUD_CANNOT_CREATE_RUN_OR_INSTANCE CLOUD_CANNOT_PROCEED_IN_PARALLEL diff --git a/packages/launcher/__snapshots__/browsers_spec.ts.js b/packages/launcher/__snapshots__/browsers_spec.ts.js index d1c481ea4073..41bc8b32e916 100644 --- a/packages/launcher/__snapshots__/browsers_spec.ts.js +++ b/packages/launcher/__snapshots__/browsers_spec.ts.js @@ -1,126 +1,126 @@ exports['browsers returns the expected list of browsers 1'] = [ { - "name": "chrome", - "family": "chromium", - "channel": "stable", - "displayName": "Chrome", - "versionRegex": {}, - "binary": [ - "google-chrome", - "chrome", - "google-chrome-stable" + 'name': 'chrome', + 'family': 'chromium', + 'channel': 'stable', + 'displayName': 'Chrome', + 'versionRegex': {}, + 'binary': [ + 'google-chrome', + 'chrome', + 'google-chrome-stable', ], - "minSupportedVersion": 64 + 'minSupportedVersion': 64, }, { - "name": "chromium", - "family": "chromium", - "channel": "stable", - "displayName": "Chromium", - "versionRegex": {}, - "binary": [ - "chromium-browser", - "chromium" + 'name': 'chromium', + 'family': 'chromium', + 'channel': 'stable', + 'displayName': 'Chromium', + 'versionRegex': {}, + 'binary': [ + 'chromium-browser', + 'chromium', ], - "minSupportedVersion": 64 + 'minSupportedVersion': 64, }, { - "name": "chrome", - "family": "chromium", - "channel": "beta", - "displayName": "Chrome Beta", - "versionRegex": {}, - "binary": "google-chrome-beta", - "minSupportedVersion": 64 + 'name': 'chrome', + 'family': 'chromium', + 'channel': 'beta', + 'displayName': 'Chrome Beta', + 'versionRegex': {}, + 'binary': 'google-chrome-beta', + 'minSupportedVersion': 64, }, { - "name": "chrome", - "family": "chromium", - "channel": "canary", - "displayName": "Canary", - "versionRegex": {}, - "binary": "google-chrome-canary", - "minSupportedVersion": 64 + 'name': 'chrome', + 'family': 'chromium', + 'channel': 'canary', + 'displayName': 'Canary', + 'versionRegex': {}, + 'binary': 'google-chrome-canary', + 'minSupportedVersion': 64, }, { - "name": "firefox", - "family": "firefox", - "channel": "stable", - "displayName": "Firefox", - "versionRegex": {}, - "binary": "firefox", - "minSupportedVersion": 86 + 'name': 'firefox', + 'family': 'firefox', + 'channel': 'stable', + 'displayName': 'Firefox', + 'versionRegex': {}, + 'binary': 'firefox', + 'minSupportedVersion': 86, }, { - "name": "firefox", - "family": "firefox", - "channel": "dev", - "displayName": "Firefox Developer Edition", - "versionRegex": {}, - "binary": [ - "firefox-developer-edition", - "firefox" + 'name': 'firefox', + 'family': 'firefox', + 'channel': 'dev', + 'displayName': 'Firefox Developer Edition', + 'versionRegex': {}, + 'binary': [ + 'firefox-developer-edition', + 'firefox', ], - "minSupportedVersion": 86 + 'minSupportedVersion': 86, }, { - "name": "firefox", - "family": "firefox", - "channel": "nightly", - "displayName": "Firefox Nightly", - "versionRegex": {}, - "binary": [ - "firefox-nightly", - "firefox-trunk" + 'name': 'firefox', + 'family': 'firefox', + 'channel': 'nightly', + 'displayName': 'Firefox Nightly', + 'versionRegex': {}, + 'binary': [ + 'firefox-nightly', + 'firefox-trunk', ], - "minSupportedVersion": 86 + 'minSupportedVersion': 86, }, { - "name": "edge", - "family": "chromium", - "channel": "stable", - "displayName": "Edge", - "versionRegex": {}, - "binary": [ - "edge", - "microsoft-edge" + 'name': 'edge', + 'family': 'chromium', + 'channel': 'stable', + 'displayName': 'Edge', + 'versionRegex': {}, + 'binary': [ + 'edge', + 'microsoft-edge', ], - "minSupportedVersion": 79 + 'minSupportedVersion': 79, }, { - "name": "edge", - "family": "chromium", - "channel": "canary", - "displayName": "Edge Canary", - "versionRegex": {}, - "binary": [ - "edge-canary", - "microsoft-edge-canary" + 'name': 'edge', + 'family': 'chromium', + 'channel': 'canary', + 'displayName': 'Edge Canary', + 'versionRegex': {}, + 'binary': [ + 'edge-canary', + 'microsoft-edge-canary', ], - "minSupportedVersion": 79 + 'minSupportedVersion': 79, }, { - "name": "edge", - "family": "chromium", - "channel": "beta", - "displayName": "Edge Beta", - "versionRegex": {}, - "binary": [ - "edge-beta", - "microsoft-edge-beta" - ], - "minSupportedVersion": 79 + 'name': 'edge', + 'family': 'chromium', + 'channel': 'beta', + 'displayName': 'Edge Beta', + 'versionRegex': {}, + 'binary': [ + 'edge-beta', + 'microsoft-edge-beta', + ], + 'minSupportedVersion': 79, }, { - "name": "edge", - "family": "chromium", - "channel": "dev", - "displayName": "Edge Dev", - "versionRegex": {}, - "binary": [ - "edge-dev", - "microsoft-edge-dev" + 'name': 'edge', + 'family': 'chromium', + 'channel': 'dev', + 'displayName': 'Edge Dev', + 'versionRegex': {}, + 'binary': [ + 'edge-dev', + 'microsoft-edge-dev', ], - "minSupportedVersion": 79 - } + 'minSupportedVersion': 79, + }, ] diff --git a/packages/launcher/__snapshots__/darwin_spec.ts.js b/packages/launcher/__snapshots__/darwin_spec.ts.js index 5d3e6a607909..4cd70c8c8c14 100644 --- a/packages/launcher/__snapshots__/darwin_spec.ts.js +++ b/packages/launcher/__snapshots__/darwin_spec.ts.js @@ -1,214 +1,214 @@ exports['darwin browser detection detects browsers as expected 1'] = [ { - "name": "chrome", - "family": "chromium", - "channel": "stable", - "displayName": "Chrome", - "versionRegex": {}, - "binary": [ - "google-chrome", - "chrome", - "google-chrome-stable" + 'name': 'chrome', + 'family': 'chromium', + 'channel': 'stable', + 'displayName': 'Chrome', + 'versionRegex': {}, + 'binary': [ + 'google-chrome', + 'chrome', + 'google-chrome-stable', ], - "minSupportedVersion": 64, - "path": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", - "version": "someVersion", - "findAppParams": { - "appName": "Google Chrome.app", - "executable": "Contents/MacOS/Google Chrome", - "appId": "com.google.Chrome", - "versionProperty": "KSVersion" - } + 'minSupportedVersion': 64, + 'path': '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', + 'version': 'someVersion', + 'findAppParams': { + 'appName': 'Google Chrome.app', + 'executable': 'Contents/MacOS/Google Chrome', + 'appId': 'com.google.Chrome', + 'versionProperty': 'KSVersion', + }, }, { - "name": "chromium", - "family": "chromium", - "channel": "stable", - "displayName": "Chromium", - "versionRegex": {}, - "binary": [ - "chromium-browser", - "chromium" + 'name': 'chromium', + 'family': 'chromium', + 'channel': 'stable', + 'displayName': 'Chromium', + 'versionRegex': {}, + 'binary': [ + 'chromium-browser', + 'chromium', ], - "minSupportedVersion": 64, - "path": "/Applications/Chromium.app/Contents/MacOS/Chromium", - "version": "someVersion", - "findAppParams": { - "appName": "Chromium.app", - "executable": "Contents/MacOS/Chromium", - "appId": "org.chromium.Chromium", - "versionProperty": "CFBundleShortVersionString" - } + 'minSupportedVersion': 64, + 'path': '/Applications/Chromium.app/Contents/MacOS/Chromium', + 'version': 'someVersion', + 'findAppParams': { + 'appName': 'Chromium.app', + 'executable': 'Contents/MacOS/Chromium', + 'appId': 'org.chromium.Chromium', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "chrome", - "family": "chromium", - "channel": "beta", - "displayName": "Chrome Beta", - "versionRegex": {}, - "binary": "google-chrome-beta", - "minSupportedVersion": 64, - "path": "/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta", - "version": "someVersion", - "findAppParams": { - "appName": "Google Chrome Beta.app", - "executable": "Contents/MacOS/Google Chrome Beta", - "appId": "com.google.Chrome.beta", - "versionProperty": "KSVersion" - } + 'name': 'chrome', + 'family': 'chromium', + 'channel': 'beta', + 'displayName': 'Chrome Beta', + 'versionRegex': {}, + 'binary': 'google-chrome-beta', + 'minSupportedVersion': 64, + 'path': '/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta', + 'version': 'someVersion', + 'findAppParams': { + 'appName': 'Google Chrome Beta.app', + 'executable': 'Contents/MacOS/Google Chrome Beta', + 'appId': 'com.google.Chrome.beta', + 'versionProperty': 'KSVersion', + }, }, { - "name": "chrome", - "family": "chromium", - "channel": "canary", - "displayName": "Canary", - "versionRegex": {}, - "binary": "google-chrome-canary", - "minSupportedVersion": 64, - "path": "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary", - "version": "someVersion", - "findAppParams": { - "appName": "Google Chrome Canary.app", - "executable": "Contents/MacOS/Google Chrome Canary", - "appId": "com.google.Chrome.canary", - "versionProperty": "KSVersion" - } + 'name': 'chrome', + 'family': 'chromium', + 'channel': 'canary', + 'displayName': 'Canary', + 'versionRegex': {}, + 'binary': 'google-chrome-canary', + 'minSupportedVersion': 64, + 'path': '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary', + 'version': 'someVersion', + 'findAppParams': { + 'appName': 'Google Chrome Canary.app', + 'executable': 'Contents/MacOS/Google Chrome Canary', + 'appId': 'com.google.Chrome.canary', + 'versionProperty': 'KSVersion', + }, }, { - "name": "firefox", - "family": "firefox", - "channel": "stable", - "displayName": "Firefox", - "versionRegex": {}, - "binary": "firefox", - "minSupportedVersion": 86, - "path": "/Applications/Firefox.app/Contents/MacOS/firefox", - "version": "someVersion", - "findAppParams": { - "appName": "Firefox.app", - "executable": "Contents/MacOS/firefox", - "appId": "org.mozilla.firefox", - "versionProperty": "CFBundleShortVersionString" - } + 'name': 'firefox', + 'family': 'firefox', + 'channel': 'stable', + 'displayName': 'Firefox', + 'versionRegex': {}, + 'binary': 'firefox', + 'minSupportedVersion': 86, + 'path': '/Applications/Firefox.app/Contents/MacOS/firefox', + 'version': 'someVersion', + 'findAppParams': { + 'appName': 'Firefox.app', + 'executable': 'Contents/MacOS/firefox', + 'appId': 'org.mozilla.firefox', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "firefox", - "family": "firefox", - "channel": "dev", - "displayName": "Firefox Developer Edition", - "versionRegex": {}, - "binary": [ - "firefox-developer-edition", - "firefox" + 'name': 'firefox', + 'family': 'firefox', + 'channel': 'dev', + 'displayName': 'Firefox Developer Edition', + 'versionRegex': {}, + 'binary': [ + 'firefox-developer-edition', + 'firefox', ], - "minSupportedVersion": 86, - "path": "/Applications/Firefox Developer Edition.app/Contents/MacOS/firefox", - "version": "someVersion", - "findAppParams": { - "appName": "Firefox Developer Edition.app", - "executable": "Contents/MacOS/firefox", - "appId": "org.mozilla.firefoxdeveloperedition", - "versionProperty": "CFBundleShortVersionString" - } + 'minSupportedVersion': 86, + 'path': '/Applications/Firefox Developer Edition.app/Contents/MacOS/firefox', + 'version': 'someVersion', + 'findAppParams': { + 'appName': 'Firefox Developer Edition.app', + 'executable': 'Contents/MacOS/firefox', + 'appId': 'org.mozilla.firefoxdeveloperedition', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "firefox", - "family": "firefox", - "channel": "nightly", - "displayName": "Firefox Nightly", - "versionRegex": {}, - "binary": [ - "firefox-nightly", - "firefox-trunk" + 'name': 'firefox', + 'family': 'firefox', + 'channel': 'nightly', + 'displayName': 'Firefox Nightly', + 'versionRegex': {}, + 'binary': [ + 'firefox-nightly', + 'firefox-trunk', ], - "minSupportedVersion": 86, - "path": "/Applications/Firefox Nightly.app/Contents/MacOS/firefox", - "version": "someVersion", - "findAppParams": { - "appName": "Firefox Nightly.app", - "executable": "Contents/MacOS/firefox", - "appId": "org.mozilla.nightly", - "versionProperty": "CFBundleShortVersionString" - } + 'minSupportedVersion': 86, + 'path': '/Applications/Firefox Nightly.app/Contents/MacOS/firefox', + 'version': 'someVersion', + 'findAppParams': { + 'appName': 'Firefox Nightly.app', + 'executable': 'Contents/MacOS/firefox', + 'appId': 'org.mozilla.nightly', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "edge", - "family": "chromium", - "channel": "stable", - "displayName": "Edge", - "versionRegex": {}, - "binary": [ - "edge", - "microsoft-edge" + 'name': 'edge', + 'family': 'chromium', + 'channel': 'stable', + 'displayName': 'Edge', + 'versionRegex': {}, + 'binary': [ + 'edge', + 'microsoft-edge', ], - "minSupportedVersion": 79, - "path": "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge", - "version": "someVersion", - "findAppParams": { - "appName": "Microsoft Edge.app", - "executable": "Contents/MacOS/Microsoft Edge", - "appId": "com.microsoft.Edge", - "versionProperty": "CFBundleShortVersionString" - } + 'minSupportedVersion': 79, + 'path': '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge', + 'version': 'someVersion', + 'findAppParams': { + 'appName': 'Microsoft Edge.app', + 'executable': 'Contents/MacOS/Microsoft Edge', + 'appId': 'com.microsoft.Edge', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "edge", - "family": "chromium", - "channel": "canary", - "displayName": "Edge Canary", - "versionRegex": {}, - "binary": [ - "edge-canary", - "microsoft-edge-canary" + 'name': 'edge', + 'family': 'chromium', + 'channel': 'canary', + 'displayName': 'Edge Canary', + 'versionRegex': {}, + 'binary': [ + 'edge-canary', + 'microsoft-edge-canary', ], - "minSupportedVersion": 79, - "path": "/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary", - "version": "someVersion", - "findAppParams": { - "appName": "Microsoft Edge Canary.app", - "executable": "Contents/MacOS/Microsoft Edge Canary", - "appId": "com.microsoft.Edge.Canary", - "versionProperty": "CFBundleShortVersionString" - } + 'minSupportedVersion': 79, + 'path': '/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary', + 'version': 'someVersion', + 'findAppParams': { + 'appName': 'Microsoft Edge Canary.app', + 'executable': 'Contents/MacOS/Microsoft Edge Canary', + 'appId': 'com.microsoft.Edge.Canary', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "edge", - "family": "chromium", - "channel": "beta", - "displayName": "Edge Beta", - "versionRegex": {}, - "binary": [ - "edge-beta", - "microsoft-edge-beta" + 'name': 'edge', + 'family': 'chromium', + 'channel': 'beta', + 'displayName': 'Edge Beta', + 'versionRegex': {}, + 'binary': [ + 'edge-beta', + 'microsoft-edge-beta', ], - "minSupportedVersion": 79, - "path": "/Applications/Microsoft Edge Beta.app/Contents/MacOS/Microsoft Edge Beta", - "version": "someVersion", - "findAppParams": { - "appName": "Microsoft Edge Beta.app", - "executable": "Contents/MacOS/Microsoft Edge Beta", - "appId": "com.microsoft.Edge.Beta", - "versionProperty": "CFBundleShortVersionString" - } + 'minSupportedVersion': 79, + 'path': '/Applications/Microsoft Edge Beta.app/Contents/MacOS/Microsoft Edge Beta', + 'version': 'someVersion', + 'findAppParams': { + 'appName': 'Microsoft Edge Beta.app', + 'executable': 'Contents/MacOS/Microsoft Edge Beta', + 'appId': 'com.microsoft.Edge.Beta', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "edge", - "family": "chromium", - "channel": "dev", - "displayName": "Edge Dev", - "versionRegex": {}, - "binary": [ - "edge-dev", - "microsoft-edge-dev" + 'name': 'edge', + 'family': 'chromium', + 'channel': 'dev', + 'displayName': 'Edge Dev', + 'versionRegex': {}, + 'binary': [ + 'edge-dev', + 'microsoft-edge-dev', ], - "minSupportedVersion": 79, - "path": "/Applications/Microsoft Edge Dev.app/Contents/MacOS/Microsoft Edge Dev", - "version": "someVersion", - "findAppParams": { - "appName": "Microsoft Edge Dev.app", - "executable": "Contents/MacOS/Microsoft Edge Dev", - "appId": "com.microsoft.Edge.Dev", - "versionProperty": "CFBundleShortVersionString" - } - } + 'minSupportedVersion': 79, + 'path': '/Applications/Microsoft Edge Dev.app/Contents/MacOS/Microsoft Edge Dev', + 'version': 'someVersion', + 'findAppParams': { + 'appName': 'Microsoft Edge Dev.app', + 'executable': 'Contents/MacOS/Microsoft Edge Dev', + 'appId': 'com.microsoft.Edge.Dev', + 'versionProperty': 'CFBundleShortVersionString', + }, + }, ] diff --git a/packages/launcher/__snapshots__/windows_spec.ts.js b/packages/launcher/__snapshots__/windows_spec.ts.js index 61711f4ee725..0ba7dfb13388 100644 --- a/packages/launcher/__snapshots__/windows_spec.ts.js +++ b/packages/launcher/__snapshots__/windows_spec.ts.js @@ -1,286 +1,286 @@ exports['windows browser detection detects browsers as expected 1'] = [ { - "name": "chrome", - "family": "chromium", - "channel": "stable", - "displayName": "Chrome", - "versionRegex": {}, - "binary": [ - "google-chrome", - "chrome", - "google-chrome-stable" + 'name': 'chrome', + 'family': 'chromium', + 'channel': 'stable', + 'displayName': 'Chrome', + 'versionRegex': {}, + 'binary': [ + 'google-chrome', + 'chrome', + 'google-chrome-stable', ], - "minSupportedVersion": 64, - "path": "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe", - "version": "1.2.3", - "findAppParams": { - "appName": "Google Chrome.app", - "executable": "Contents/MacOS/Google Chrome", - "appId": "com.google.Chrome", - "versionProperty": "KSVersion" - } + 'minSupportedVersion': 64, + 'path': 'C:/Program Files (x86)/Google/Chrome/Application/chrome.exe', + 'version': '1.2.3', + 'findAppParams': { + 'appName': 'Google Chrome.app', + 'executable': 'Contents/MacOS/Google Chrome', + 'appId': 'com.google.Chrome', + 'versionProperty': 'KSVersion', + }, }, { - "name": "chromium", - "family": "chromium", - "channel": "stable", - "displayName": "Chromium", - "versionRegex": {}, - "binary": [ - "chromium-browser", - "chromium" + 'name': 'chromium', + 'family': 'chromium', + 'channel': 'stable', + 'displayName': 'Chromium', + 'versionRegex': {}, + 'binary': [ + 'chromium-browser', + 'chromium', ], - "minSupportedVersion": 64, - "path": "C:/Program Files (x86)/Google/chrome-win32/chrome.exe", - "version": "2.3.4", - "findAppParams": { - "appName": "Chromium.app", - "executable": "Contents/MacOS/Chromium", - "appId": "org.chromium.Chromium", - "versionProperty": "CFBundleShortVersionString" - } + 'minSupportedVersion': 64, + 'path': 'C:/Program Files (x86)/Google/chrome-win32/chrome.exe', + 'version': '2.3.4', + 'findAppParams': { + 'appName': 'Chromium.app', + 'executable': 'Contents/MacOS/Chromium', + 'appId': 'org.chromium.Chromium', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "chrome", - "family": "chromium", - "channel": "beta", - "displayName": "Chrome Beta", - "versionRegex": {}, - "binary": "google-chrome-beta", - "minSupportedVersion": 64, - "path": "C:/Program Files (x86)/Google/Chrome Beta/Application/chrome.exe", - "version": "6.7.8", - "findAppParams": { - "appName": "Google Chrome Beta.app", - "executable": "Contents/MacOS/Google Chrome Beta", - "appId": "com.google.Chrome.beta", - "versionProperty": "KSVersion" - } + 'name': 'chrome', + 'family': 'chromium', + 'channel': 'beta', + 'displayName': 'Chrome Beta', + 'versionRegex': {}, + 'binary': 'google-chrome-beta', + 'minSupportedVersion': 64, + 'path': 'C:/Program Files (x86)/Google/Chrome Beta/Application/chrome.exe', + 'version': '6.7.8', + 'findAppParams': { + 'appName': 'Google Chrome Beta.app', + 'executable': 'Contents/MacOS/Google Chrome Beta', + 'appId': 'com.google.Chrome.beta', + 'versionProperty': 'KSVersion', + }, }, { - "name": "chrome", - "family": "chromium", - "channel": "canary", - "displayName": "Canary", - "versionRegex": {}, - "binary": "google-chrome-canary", - "minSupportedVersion": 64, - "path": "C:/Users/flotwig/AppData/Local/Google/Chrome SxS/Application/chrome.exe", - "version": "3.4.5", - "findAppParams": { - "appName": "Google Chrome Canary.app", - "executable": "Contents/MacOS/Google Chrome Canary", - "appId": "com.google.Chrome.canary", - "versionProperty": "KSVersion" - } + 'name': 'chrome', + 'family': 'chromium', + 'channel': 'canary', + 'displayName': 'Canary', + 'versionRegex': {}, + 'binary': 'google-chrome-canary', + 'minSupportedVersion': 64, + 'path': 'C:/Users/flotwig/AppData/Local/Google/Chrome SxS/Application/chrome.exe', + 'version': '3.4.5', + 'findAppParams': { + 'appName': 'Google Chrome Canary.app', + 'executable': 'Contents/MacOS/Google Chrome Canary', + 'appId': 'com.google.Chrome.canary', + 'versionProperty': 'KSVersion', + }, }, { - "name": "firefox", - "family": "firefox", - "channel": "stable", - "displayName": "Firefox", - "versionRegex": {}, - "binary": "firefox", - "minSupportedVersion": 86, - "path": "C:/Program Files/Mozilla Firefox/firefox.exe", - "version": "72", - "findAppParams": { - "appName": "Firefox.app", - "executable": "Contents/MacOS/firefox", - "appId": "org.mozilla.firefox", - "versionProperty": "CFBundleShortVersionString" - } + 'name': 'firefox', + 'family': 'firefox', + 'channel': 'stable', + 'displayName': 'Firefox', + 'versionRegex': {}, + 'binary': 'firefox', + 'minSupportedVersion': 86, + 'path': 'C:/Program Files/Mozilla Firefox/firefox.exe', + 'version': '72', + 'findAppParams': { + 'appName': 'Firefox.app', + 'executable': 'Contents/MacOS/firefox', + 'appId': 'org.mozilla.firefox', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "firefox", - "family": "firefox", - "channel": "dev", - "displayName": "Firefox Developer Edition", - "versionRegex": {}, - "binary": [ - "firefox-developer-edition", - "firefox" + 'name': 'firefox', + 'family': 'firefox', + 'channel': 'dev', + 'displayName': 'Firefox Developer Edition', + 'versionRegex': {}, + 'binary': [ + 'firefox-developer-edition', + 'firefox', ], - "minSupportedVersion": 86, - "path": "C:/Program Files (x86)/Firefox Developer Edition/firefox.exe", - "version": "73", - "findAppParams": { - "appName": "Firefox Developer Edition.app", - "executable": "Contents/MacOS/firefox", - "appId": "org.mozilla.firefoxdeveloperedition", - "versionProperty": "CFBundleShortVersionString" - } + 'minSupportedVersion': 86, + 'path': 'C:/Program Files (x86)/Firefox Developer Edition/firefox.exe', + 'version': '73', + 'findAppParams': { + 'appName': 'Firefox Developer Edition.app', + 'executable': 'Contents/MacOS/firefox', + 'appId': 'org.mozilla.firefoxdeveloperedition', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "firefox", - "family": "firefox", - "channel": "nightly", - "displayName": "Firefox Nightly", - "versionRegex": {}, - "binary": [ - "firefox-nightly", - "firefox-trunk" + 'name': 'firefox', + 'family': 'firefox', + 'channel': 'nightly', + 'displayName': 'Firefox Nightly', + 'versionRegex': {}, + 'binary': [ + 'firefox-nightly', + 'firefox-trunk', ], - "minSupportedVersion": 86, - "path": "C:/Program Files/Firefox Nightly/firefox.exe", - "version": "74", - "findAppParams": { - "appName": "Firefox Nightly.app", - "executable": "Contents/MacOS/firefox", - "appId": "org.mozilla.nightly", - "versionProperty": "CFBundleShortVersionString" - } + 'minSupportedVersion': 86, + 'path': 'C:/Program Files/Firefox Nightly/firefox.exe', + 'version': '74', + 'findAppParams': { + 'appName': 'Firefox Nightly.app', + 'executable': 'Contents/MacOS/firefox', + 'appId': 'org.mozilla.nightly', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "edge", - "family": "chromium", - "channel": "stable", - "displayName": "Edge", - "versionRegex": {}, - "binary": [ - "edge", - "microsoft-edge" + 'name': 'edge', + 'family': 'chromium', + 'channel': 'stable', + 'displayName': 'Edge', + 'versionRegex': {}, + 'binary': [ + 'edge', + 'microsoft-edge', ], - "minSupportedVersion": 79, - "path": "C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe", - "version": "11", - "findAppParams": { - "appName": "Microsoft Edge.app", - "executable": "Contents/MacOS/Microsoft Edge", - "appId": "com.microsoft.Edge", - "versionProperty": "CFBundleShortVersionString" - } + 'minSupportedVersion': 79, + 'path': 'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe', + 'version': '11', + 'findAppParams': { + 'appName': 'Microsoft Edge.app', + 'executable': 'Contents/MacOS/Microsoft Edge', + 'appId': 'com.microsoft.Edge', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "edge", - "family": "chromium", - "channel": "canary", - "displayName": "Edge Canary", - "versionRegex": {}, - "binary": [ - "edge-canary", - "microsoft-edge-canary" + 'name': 'edge', + 'family': 'chromium', + 'channel': 'canary', + 'displayName': 'Edge Canary', + 'versionRegex': {}, + 'binary': [ + 'edge-canary', + 'microsoft-edge-canary', ], - "minSupportedVersion": 79, - "path": "C:/Users/flotwig/AppData/Local/Microsoft/Edge SxS/Application/msedge.exe", - "version": "14", - "findAppParams": { - "appName": "Microsoft Edge Canary.app", - "executable": "Contents/MacOS/Microsoft Edge Canary", - "appId": "com.microsoft.Edge.Canary", - "versionProperty": "CFBundleShortVersionString" - } + 'minSupportedVersion': 79, + 'path': 'C:/Users/flotwig/AppData/Local/Microsoft/Edge SxS/Application/msedge.exe', + 'version': '14', + 'findAppParams': { + 'appName': 'Microsoft Edge Canary.app', + 'executable': 'Contents/MacOS/Microsoft Edge Canary', + 'appId': 'com.microsoft.Edge.Canary', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "edge", - "family": "chromium", - "channel": "beta", - "displayName": "Edge Beta", - "versionRegex": {}, - "binary": [ - "edge-beta", - "microsoft-edge-beta" + 'name': 'edge', + 'family': 'chromium', + 'channel': 'beta', + 'displayName': 'Edge Beta', + 'versionRegex': {}, + 'binary': [ + 'edge-beta', + 'microsoft-edge-beta', ], - "minSupportedVersion": 79, - "path": "C:/Program Files (x86)/Microsoft/Edge Beta/Application/msedge.exe", - "version": "12", - "findAppParams": { - "appName": "Microsoft Edge Beta.app", - "executable": "Contents/MacOS/Microsoft Edge Beta", - "appId": "com.microsoft.Edge.Beta", - "versionProperty": "CFBundleShortVersionString" - } + 'minSupportedVersion': 79, + 'path': 'C:/Program Files (x86)/Microsoft/Edge Beta/Application/msedge.exe', + 'version': '12', + 'findAppParams': { + 'appName': 'Microsoft Edge Beta.app', + 'executable': 'Contents/MacOS/Microsoft Edge Beta', + 'appId': 'com.microsoft.Edge.Beta', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "edge", - "family": "chromium", - "channel": "dev", - "displayName": "Edge Dev", - "versionRegex": {}, - "binary": [ - "edge-dev", - "microsoft-edge-dev" + 'name': 'edge', + 'family': 'chromium', + 'channel': 'dev', + 'displayName': 'Edge Dev', + 'versionRegex': {}, + 'binary': [ + 'edge-dev', + 'microsoft-edge-dev', ], - "minSupportedVersion": 79, - "path": "C:/Program Files (x86)/Microsoft/Edge Dev/Application/msedge.exe", - "version": "13", - "findAppParams": { - "appName": "Microsoft Edge Dev.app", - "executable": "Contents/MacOS/Microsoft Edge Dev", - "appId": "com.microsoft.Edge.Dev", - "versionProperty": "CFBundleShortVersionString" - } - } + 'minSupportedVersion': 79, + 'path': 'C:/Program Files (x86)/Microsoft/Edge Dev/Application/msedge.exe', + 'version': '13', + 'findAppParams': { + 'appName': 'Microsoft Edge Dev.app', + 'executable': 'Contents/MacOS/Microsoft Edge Dev', + 'appId': 'com.microsoft.Edge.Dev', + 'versionProperty': 'CFBundleShortVersionString', + }, + }, ] exports['windows browser detection detects 64-bit Chrome Beta app path 1'] = { - "name": "chrome", - "version": "9.0.1", - "path": "C:/Program Files/Google/Chrome Beta/Application/chrome.exe" + 'name': 'chrome', + 'version': '9.0.1', + 'path': 'C:/Program Files/Google/Chrome Beta/Application/chrome.exe', } exports['windows browser detection detects new Chrome 64-bit app path 1'] = { - "name": "chrome", - "version": "4.4.4", - "path": "C:/Program Files/Google/Chrome/Application/chrome.exe" + 'name': 'chrome', + 'version': '4.4.4', + 'path': 'C:/Program Files/Google/Chrome/Application/chrome.exe', } exports['windows browser detection detects local Firefox installs 1'] = [ { - "name": "firefox", - "family": "firefox", - "channel": "stable", - "displayName": "Firefox", - "versionRegex": {}, - "binary": "firefox", - "minSupportedVersion": 86, - "path": "C:/Users/flotwig/AppData/Local/Mozilla Firefox/firefox.exe", - "version": "100", - "findAppParams": { - "appName": "Firefox.app", - "executable": "Contents/MacOS/firefox", - "appId": "org.mozilla.firefox", - "versionProperty": "CFBundleShortVersionString" - } + 'name': 'firefox', + 'family': 'firefox', + 'channel': 'stable', + 'displayName': 'Firefox', + 'versionRegex': {}, + 'binary': 'firefox', + 'minSupportedVersion': 86, + 'path': 'C:/Users/flotwig/AppData/Local/Mozilla Firefox/firefox.exe', + 'version': '100', + 'findAppParams': { + 'appName': 'Firefox.app', + 'executable': 'Contents/MacOS/firefox', + 'appId': 'org.mozilla.firefox', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "firefox", - "family": "firefox", - "channel": "dev", - "displayName": "Firefox Developer Edition", - "versionRegex": {}, - "binary": [ - "firefox-developer-edition", - "firefox" + 'name': 'firefox', + 'family': 'firefox', + 'channel': 'dev', + 'displayName': 'Firefox Developer Edition', + 'versionRegex': {}, + 'binary': [ + 'firefox-developer-edition', + 'firefox', ], - "minSupportedVersion": 86, - "path": "C:/Users/flotwig/AppData/Local/Firefox Developer Edition/firefox.exe", - "version": "300", - "findAppParams": { - "appName": "Firefox Developer Edition.app", - "executable": "Contents/MacOS/firefox", - "appId": "org.mozilla.firefoxdeveloperedition", - "versionProperty": "CFBundleShortVersionString" - } + 'minSupportedVersion': 86, + 'path': 'C:/Users/flotwig/AppData/Local/Firefox Developer Edition/firefox.exe', + 'version': '300', + 'findAppParams': { + 'appName': 'Firefox Developer Edition.app', + 'executable': 'Contents/MacOS/firefox', + 'appId': 'org.mozilla.firefoxdeveloperedition', + 'versionProperty': 'CFBundleShortVersionString', + }, }, { - "name": "firefox", - "family": "firefox", - "channel": "nightly", - "displayName": "Firefox Nightly", - "versionRegex": {}, - "binary": [ - "firefox-nightly", - "firefox-trunk" + 'name': 'firefox', + 'family': 'firefox', + 'channel': 'nightly', + 'displayName': 'Firefox Nightly', + 'versionRegex': {}, + 'binary': [ + 'firefox-nightly', + 'firefox-trunk', ], - "minSupportedVersion": 86, - "path": "C:/Users/flotwig/AppData/Local/Firefox Nightly/firefox.exe", - "version": "200", - "findAppParams": { - "appName": "Firefox Nightly.app", - "executable": "Contents/MacOS/firefox", - "appId": "org.mozilla.nightly", - "versionProperty": "CFBundleShortVersionString" - } - } + 'minSupportedVersion': 86, + 'path': 'C:/Users/flotwig/AppData/Local/Firefox Nightly/firefox.exe', + 'version': '200', + 'findAppParams': { + 'appName': 'Firefox Nightly.app', + 'executable': 'Contents/MacOS/firefox', + 'appId': 'org.mozilla.nightly', + 'versionProperty': 'CFBundleShortVersionString', + }, + }, ] diff --git a/packages/server/__snapshots__/args_spec.js b/packages/server/__snapshots__/args_spec.js index a75c37a22813..2cfc6e70a312 100644 --- a/packages/server/__snapshots__/args_spec.js +++ b/packages/server/__snapshots__/args_spec.js @@ -29,3 +29,35 @@ You passed: {} The error was: spec must be a string or comma-separated list ` + +exports['invalid --auto-cancel-after-failures error'] = ` +Cypress encountered an error while parsing the argument: --auto-cancel-after-failures + +You passed: foo + +The error was: auto-cancel-after-failures must be an integer or false +` + +exports['invalid --auto-cancel-after-failures (true) error'] = ` +Cypress encountered an error while parsing the argument: --auto-cancel-after-failures + +You passed: true + +The error was: auto-cancel-after-failures must be an integer or false +` + +exports['invalid --auto-cancel-after-failures (negative value) error'] = ` +Cypress encountered an error while parsing the argument: --auto-cancel-after-failures + +You passed: true + +The error was: auto-cancel-after-failures must be an integer or false +` + +exports['invalid --auto-cancel-after-failures (decimal value) error'] = ` +Cypress encountered an error while parsing the argument: --auto-cancel-after-failures + +You passed: 1.5 + +The error was: auto-cancel-after-failures must be an integer or false +` diff --git a/packages/server/__snapshots__/cypress_spec.js b/packages/server/__snapshots__/cypress_spec.js index 609fd1d818a2..8c19681367d7 100644 --- a/packages/server/__snapshots__/cypress_spec.js +++ b/packages/server/__snapshots__/cypress_spec.js @@ -1,5 +1,5 @@ exports['RECORD_PARAMS_WITHOUT_RECORDING-ciBuildId 1'] = ` -You passed the --ci-build-id, --group, --tag, or --parallel flag without also passing the --record flag. +You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after-failures flag without also passing the --record flag. The --ci-build-id flag you passed was: ciBuildId123 @@ -19,7 +19,7 @@ https://on.cypress.io/incorrect-ci-build-id-usage ` exports['RECORD_PARAMS_WITHOUT_RECORDING-group 1'] = ` -You passed the --ci-build-id, --group, --tag, or --parallel flag without also passing the --record flag. +You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after-failures flag without also passing the --record flag. The --group flag you passed was: e2e-tests @@ -29,7 +29,7 @@ https://on.cypress.io/record-params-without-recording ` exports['RECORD_PARAMS_WITHOUT_RECORDING-parallel 1'] = ` -You passed the --ci-build-id, --group, --tag, or --parallel flag without also passing the --record flag. +You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after-failures flag without also passing the --record flag. The --parallel flag you passed was: true @@ -39,7 +39,7 @@ https://on.cypress.io/record-params-without-recording ` exports['RECORD_PARAMS_WITHOUT_RECORDING-group-parallel 1'] = ` -You passed the --ci-build-id, --group, --tag, or --parallel flag without also passing the --record flag. +You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after-failures flag without also passing the --record flag. The --group flag you passed was: electron-smoke-tests The --parallel flag you passed was: true @@ -273,7 +273,7 @@ https://on.cypress.io/stale-run ` exports['RECORD_PARAMS_WITHOUT_RECORDING-tag 1'] = ` -You passed the --ci-build-id, --group, --tag, or --parallel flag without also passing the --record flag. +You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after-failures flag without also passing the --record flag. These flags can only be used when recording to Cypress Cloud. @@ -410,3 +410,36 @@ exports['CLOUD_RECOMMENDATION_MESSAGE'] = ` ---------------------------------------------------------------------------------------------------- ` + +exports['RECORD_PARAMS_WITHOUT_RECORDING-no-auto-cancel-after-failures 1'] = ` +You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after-failures flag without also passing the --record flag. + +These flags can only be used when recording to Cypress Cloud. + +https://on.cypress.io/record-params-without-recording +` + +exports['RECORD_PARAMS_WITHOUT_RECORDING-auto-cancel-after-failures 1'] = ` +You passed the --ci-build-id, --group, --tag, --parallel, or --auto-cancel-after-failures flag without also passing the --record flag. + +The --auto-cancel-after-failures flag you passed was: 4 + +These flags can only be used when recording to Cypress Cloud. + +https://on.cypress.io/record-params-without-recording +` + +exports['CLOUD_AUTO_CANCEL_MISMATCH 1'] = ` +You passed the --auto-cancel-after-failures flag, but this run originally started with a different value for the --auto-cancel-after-failures flag. + +The existing run is: https://cloud.cypress.io/runs/12345 + +The --tag flag you passed was: nightly +The --group flag you passed was: electron-smoke-tests +The --ciBuildId flag you passed was: ciBuildId123 +The --auto-cancel-after-failures flag you passed was: 4 + +The first setting of --auto-cancel-after-failures for any given run takes precedent. + +https://on.cypress.io/auto-cancellation-mismatch +` diff --git a/packages/server/lib/cloud/api.ts b/packages/server/lib/cloud/api.ts index e3181f64d87a..2082484deeab 100644 --- a/packages/server/lib/cloud/api.ts +++ b/packages/server/lib/cloud/api.ts @@ -190,6 +190,7 @@ module.exports = { return retryWithBackoff((attemptIndex) => { const body = { ..._.pick(options, [ + 'autoCancelAfterFailures', 'ci', 'specs', 'commit', diff --git a/packages/server/lib/modes/record.js b/packages/server/lib/modes/record.js index 2caab1693782..0097820aa678 100644 --- a/packages/server/lib/modes/record.js +++ b/packages/server/lib/modes/record.js @@ -77,13 +77,14 @@ const throwIfIndeterminateCiBuildId = (ciBuildId, parallel, group) => { } } -const throwIfRecordParamsWithoutRecording = (record, ciBuildId, parallel, group, tag) => { - if (!record && _.some([ciBuildId, parallel, group, tag])) { +const throwIfRecordParamsWithoutRecording = (record, ciBuildId, parallel, group, tag, autoCancelAfterFailures) => { + if (!record && _.some([ciBuildId, parallel, group, tag, autoCancelAfterFailures !== undefined])) { errors.throwErr('RECORD_PARAMS_WITHOUT_RECORDING', { ciBuildId, tag, group, parallel, + autoCancelAfterFailures, }) } } @@ -266,7 +267,7 @@ const createRun = Promise.method((options = {}) => { ciBuildId: null, }) - let { projectId, recordKey, platform, git, specPattern, specs, parallel, ciBuildId, group, tags, testingType } = options + let { projectId, recordKey, platform, git, specPattern, specs, parallel, ciBuildId, group, tags, testingType, autoCancelAfterFailures } = options if (recordKey == null) { recordKey = env.get('CYPRESS_RECORD_KEY') @@ -321,6 +322,7 @@ const createRun = Promise.method((options = {}) => { testingType, ci, commit, + autoCancelAfterFailures, }) .tap((response) => { if (!(response && response.warnings && response.warnings.length)) { @@ -424,6 +426,10 @@ const createRun = Promise.method((options = {}) => { return errors.throwErr('RUN_GROUPING_FEATURE_NOT_AVAILABLE_IN_PLAN', { link: billingLink(orgId), }) + case 'AUTO_CANCEL_NOT_AVAILABLE_IN_PLAN': + return errors.throwErr('CLOUD_AUTO_CANCEL_NOT_AVAILABLE_IN_PLAN', { + link: billingLink(orgId), + }) default: return errors.throwErr('CLOUD_UNKNOWN_INVALID_REQUEST', { response: err, @@ -499,6 +505,15 @@ const createRun = Promise.method((options = {}) => { parallel, ciBuildId, }) + case 'AUTO_CANCEL_MISMATCH': + return errors.throwErr('CLOUD_AUTO_CANCEL_MISMATCH', { + runUrl, + tags, + group, + parallel, + ciBuildId, + autoCancelAfterFailures, + }) default: return errors.throwErr('CLOUD_UNKNOWN_INVALID_REQUEST', { response: err, @@ -581,6 +596,7 @@ const createRunAndRecordSpecs = (options = {}) => { onError, testingType, quiet, + autoCancelAfterFailures, } = options const recordKey = options.key @@ -614,6 +630,7 @@ const createRunAndRecordSpecs = (options = {}) => { specPattern, testingType, configFile: config ? config.configFile : null, + autoCancelAfterFailures, }) .then((resp) => { if (!resp) { diff --git a/packages/server/lib/modes/run.ts b/packages/server/lib/modes/run.ts index 15ad47769e2e..adc11abac84b 100644 --- a/packages/server/lib/modes/run.ts +++ b/packages/server/lib/modes/run.ts @@ -692,10 +692,10 @@ function screenshotMetadata (data, resp) { } } -async function runSpecs (options: { config: Cfg, browser: Browser, sys: any, headed: boolean, outputPath: string, specs: SpecWithRelativeRoot[], specPattern: string | RegExp | string[], beforeSpecRun?: BeforeSpecRun, afterSpecRun?: AfterSpecRun, runUrl?: string, parallel?: boolean, group?: string, tag?: string, testingType: TestingType, quiet: boolean, project: Project, onError: (err: Error) => void, exit: boolean, socketId: string, webSecurity: boolean, projectRoot: string } & Pick) { +async function runSpecs (options: { config: Cfg, browser: Browser, sys: any, headed: boolean, outputPath: string, specs: SpecWithRelativeRoot[], specPattern: string | RegExp | string[], beforeSpecRun?: BeforeSpecRun, afterSpecRun?: AfterSpecRun, runUrl?: string, parallel?: boolean, group?: string, tag?: string, autoCancelAfterFailures?: number | false, testingType: TestingType, quiet: boolean, project: Project, onError: (err: Error) => void, exit: boolean, socketId: string, webSecurity: boolean, projectRoot: string } & Pick) { if (globalThis.CY_TEST_MOCK?.runSpecs) return globalThis.CY_TEST_MOCK.runSpecs - const { config, browser, sys, headed, outputPath, specs, specPattern, beforeSpecRun, afterSpecRun, runUrl, parallel, group, tag } = options + const { config, browser, sys, headed, outputPath, specs, specPattern, beforeSpecRun, afterSpecRun, runUrl, parallel, group, tag, autoCancelAfterFailures } = options const isHeadless = !headed @@ -712,6 +712,7 @@ async function runSpecs (options: { config: Cfg, browser: Browser, sys: any, hea browser, parallel, specPattern, + autoCancelAfterFailures, }) } @@ -742,6 +743,7 @@ async function runSpecs (options: { config: Cfg, browser: Browser, sys: any, hea specPattern, system: _.pick(sys, 'osName', 'osVersion'), tag, + autoCancelAfterFailures, } await runEvents.execute('before:run', config, beforeRunDetails) @@ -893,7 +895,7 @@ async function runSpec (config, spec: SpecWithRelativeRoot, options: { project: return { results } } -async function ready (options: { projectRoot: string, record: boolean, key: string, ciBuildId: string, parallel: boolean, group: string, browser: string, tag: string, testingType: TestingType, socketId: string, spec: string | RegExp | string[], headed: boolean, outputPath: string, exit: boolean, quiet: boolean, onError?: (err: Error) => void, browsers?: FoundBrowser[], webSecurity: boolean }) { +async function ready (options: { projectRoot: string, record: boolean, key: string, ciBuildId: string, parallel: boolean, group: string, browser: string, tag: string, testingType: TestingType, autoCancelAfterFailures: number | false, socketId: string, spec: string | RegExp | string[], headed: boolean, outputPath: string, exit: boolean, quiet: boolean, onError?: (err: Error) => void, browsers?: FoundBrowser[], webSecurity: boolean }) { debug('run mode ready with options %o', options) if (process.env.ELECTRON_RUN_AS_NODE && !process.env.DISPLAY) { @@ -906,7 +908,7 @@ async function ready (options: { projectRoot: string, record: boolean, key: stri quiet: false, }) - const { projectRoot, record, key, ciBuildId, parallel, group, browser: browserName, tag, testingType, socketId } = options + const { projectRoot, record, key, ciBuildId, parallel, group, browser: browserName, tag, testingType, socketId, autoCancelAfterFailures } = options assert(socketId) @@ -932,7 +934,7 @@ async function ready (options: { projectRoot: string, record: boolean, key: stri // if we have a project id and a key but record hasnt been given recordMode.warnIfProjectIdButNoRecordOption(projectId, options) - recordMode.throwIfRecordParamsWithoutRecording(record, ciBuildId, parallel, group, tag) + recordMode.throwIfRecordParamsWithoutRecording(record, ciBuildId, parallel, group, tag, autoCancelAfterFailures) if (record) { recordMode.throwIfNoProjectId(projectId, configFile) @@ -977,6 +979,7 @@ async function ready (options: { projectRoot: string, record: boolean, key: stri async function runAllSpecs ({ beforeSpecRun, afterSpecRun, runUrl, parallel }: { beforeSpecRun?: BeforeSpecRun, afterSpecRun?: AfterSpecRun, runUrl?: string, parallel?: boolean}) { const results = await runSpecs({ + autoCancelAfterFailures, beforeSpecRun, afterSpecRun, projectRoot, @@ -1018,6 +1021,7 @@ async function ready (options: { projectRoot: string, record: boolean, key: stri const { projectName } = config return recordMode.createRunAndRecordSpecs({ + autoCancelAfterFailures, tag, key, sys, diff --git a/packages/server/lib/util/args.js b/packages/server/lib/util/args.js index 007ff4e7a458..9ec4d38fb5ee 100644 --- a/packages/server/lib/util/args.js +++ b/packages/server/lib/util/args.js @@ -14,6 +14,7 @@ const nestedArraysInSquareBracketsRe = /\[(.+?)\]/g const everythingAfterFirstEqualRe = /=(.*)/ const allowList = [ + 'autoCancelAfterFailures', 'apiKey', 'appPath', 'browser', @@ -348,6 +349,7 @@ module.exports = { const alias = { 'api-key': 'apiKey', 'app-path': 'appPath', + 'auto-cancel-after-failures': 'autoCancelAfterFailures', 'ci-build-id': 'ciBuildId', 'config-file': 'configFile', 'exec-path': 'execPath', @@ -407,7 +409,7 @@ module.exports = { } let { spec } = options - const { env, config, reporterOptions, outputPath, tag, testingType } = options + const { env, config, reporterOptions, outputPath, tag, testingType, autoCancelAfterFailures } = options let project = options.project || options.runProject // only accept project if it is a string @@ -448,6 +450,10 @@ module.exports = { } } + if (autoCancelAfterFailures && !((typeof autoCancelAfterFailures === 'number' && Number.isInteger(autoCancelAfterFailures)) || autoCancelAfterFailures === false)) { + return errors.throwErr('COULD_NOT_PARSE_ARGUMENTS', 'auto-cancel-after-failures', autoCancelAfterFailures, 'auto-cancel-after-failures must be an integer or false') + } + if (tag) { options.tag = typeof tag === 'string' ? strToArray(tag) : tag } diff --git a/packages/server/lib/util/print-run.ts b/packages/server/lib/util/print-run.ts index 9557b5d6e03b..138669000b4c 100644 --- a/packages/server/lib/util/print-run.ts +++ b/packages/server/lib/util/print-run.ts @@ -168,16 +168,16 @@ function formatNodeVersion ({ resolvedNodeVersion, resolvedNodePath }: Pick { }) }) + it('errors and exits when using --auto-cancel-after-failures without recording', function () { + return cypress.start([ + `--run-project=${this.recordPath}`, + '--auto-cancel-after-failures=4', + ]) + .then(() => { + this.expectExitWithErr('RECORD_PARAMS_WITHOUT_RECORDING') + + return snapshotConsoleLogs('RECORD_PARAMS_WITHOUT_RECORDING-auto-cancel-after-failures 1') + }) + }) + beforeEach(() => { browsers.open.restore() @@ -1574,6 +1586,7 @@ describe('lib/cypress', () => { '--tag=nightly', '--group=electron-smoke-tests', '--ciBuildId=ciBuildId123', + '--auto-cancel-after-failures=4', ]) .then(() => { this.expectExitWithErr('CLOUD_PARALLEL_REQUIRED') @@ -1602,6 +1615,7 @@ describe('lib/cypress', () => { '--tag=nightly', '--group=electron-smoke-tests', '--ciBuildId=ciBuildId123', + '--auto-cancel-after-failures=4', ]) .then(() => { this.expectExitWithErr('CLOUD_ALREADY_COMPLETE') @@ -1631,6 +1645,7 @@ describe('lib/cypress', () => { '--tag=nightly', '--group=electron-smoke-tests', '--ciBuildId=ciBuildId123', + '--auto-cancel-after-failures=4', ]) .then(() => { this.expectExitWithErr('CLOUD_STALE_RUN') @@ -1639,6 +1654,35 @@ describe('lib/cypress', () => { }) }) + it('errors and exits when auto cancel mismatch', function () { + const err = new Error() + + err.statusCode = 422 + err.error = { + code: 'AUTO_CANCEL_MISMATCH', + payload: { + runUrl: 'https://cloud.cypress.io/runs/12345', + }, + } + + api.createRun.rejects(err) + + return cypress.start([ + `--run-project=${this.recordPath}`, + '--record', + '--key=token-123', + '--tag=nightly', + '--group=electron-smoke-tests', + '--ciBuildId=ciBuildId123', + '--auto-cancel-after-failures=4', + ]) + .then(() => { + this.expectExitWithErr('CLOUD_AUTO_CANCEL_MISMATCH') + + return snapshotConsoleLogs('CLOUD_AUTO_CANCEL_MISMATCH 1') + }) + }) + describe('cloud recommendation message', () => { it('does not display if --record is passed', function () { sinon.stub(ciProvider, 'getIsCi').returns(true) @@ -1651,6 +1695,7 @@ describe('lib/cypress', () => { '--key=token-123', '--group=electron-smoke-tests', '--ciBuildId=ciBuildId123', + '--auto-cancel-after-failures=4', ]) .then(() => { expect(console.log).not.to.be.calledWith(cloudRecommendationMessage) diff --git a/packages/server/test/unit/modes/record_spec.js b/packages/server/test/unit/modes/record_spec.js index c8b8e35bd880..42eef2bbc60c 100644 --- a/packages/server/test/unit/modes/record_spec.js +++ b/packages/server/test/unit/modes/record_spec.js @@ -274,6 +274,7 @@ describe('lib/modes/record', () => { } const tag = 'nightly,develop' const testingType = 'e2e' + const autoCancelAfterFailures = 4 return recordMode.createRunAndRecordSpecs({ key, @@ -289,6 +290,7 @@ describe('lib/modes/record', () => { runAllSpecs, tag, testingType, + autoCancelAfterFailures, }) .then(() => { expect(commitInfo.commitInfo).to.be.calledWith(projectRoot) @@ -325,6 +327,7 @@ describe('lib/modes/record', () => { sha: 'sha-123', }, tags: ['nightly', 'develop'], + autoCancelAfterFailures: 4, }) }) }) diff --git a/packages/server/test/unit/util/args_spec.js b/packages/server/test/unit/util/args_spec.js index 9083616eeba9..f5b7e34c5624 100644 --- a/packages/server/test/unit/util/args_spec.js +++ b/packages/server/test/unit/util/args_spec.js @@ -229,6 +229,58 @@ describe('lib/util/args', () => { }) }) + context('--auto-cancel-after-failures', () => { + it('converts to integer', function () { + const options = this.setup('--auto-cancel-after-failures', '4') + + expect(options.autoCancelAfterFailures).to.eq(4) + }) + + it('converts to false', function () { + const options = this.setup('--auto-cancel-after-failures', 'false') + + expect(options.autoCancelAfterFailures).to.eq(false) + }) + + it('handles value 0', function () { + const options = this.setup('--auto-cancel-after-failures', '0') + + expect(options.autoCancelAfterFailures).to.eq(0) + }) + + it('throws error when a string is set', function () { + try { + return this.setup('--auto-cancel-after-failures', 'foo') + } catch (err) { + return snapshot('invalid --auto-cancel-after-failures error', stripAnsi(err.message)) + } + }) + + it('throws error when true is set', function () { + try { + return this.setup('--auto-cancel-after-failures', 'true') + } catch (err) { + return snapshot('invalid --auto-cancel-after-failures (true) error', stripAnsi(err.message)) + } + }) + + it('throws error when a negative value is set', function () { + try { + return this.setup('--auto-cancel-after-failures', '-1') + } catch (err) { + return snapshot('invalid --auto-cancel-after-failures (negative value) error', stripAnsi(err.message)) + } + }) + + it('throws error when a decimal value is set', function () { + try { + return this.setup('--auto-cancel-after-failures', '1.5') + } catch (err) { + return snapshot('invalid --auto-cancel-after-failures (decimal value) error', stripAnsi(err.message)) + } + }) + }) + context('--port', () => { it('converts to Number', function () { const options = this.setup('--port', '8080') diff --git a/system-tests/__snapshots__/record_spec.js b/system-tests/__snapshots__/record_spec.js index 03677e11e6b2..d16c60b96bb6 100644 --- a/system-tests/__snapshots__/record_spec.js +++ b/system-tests/__snapshots__/record_spec.js @@ -2541,3 +2541,33 @@ plugin stdout ` + +exports['e2e record api interaction errors create run 402 - auto cancel not available in plan errors and exits when auto cancel not available in plan 1'] = ` +Auto Cancellation is not included under your current billing plan. + +To enable this service, please visit your billing and upgrade to another plan with Auto Cancellation. + +https://on.cypress.io/dashboard/organizations/org-id-1234/billing + +` + +exports['e2e record misconfiguration errors and exits when no browser found 1'] = ` +Can't run because you've entered an invalid browser name. + +Browser: browserDoesNotExist was not found on your system or is not supported by Cypress. + +Cypress supports the following browsers: + - electron + - chrome + - chromium + - chrome:canary + - edge + - firefox + +You can also use a custom browser: https://on.cypress.io/customize-browsers + +Available browsers found on your system are: +- browser1 +- browser2 +- browser3 +` diff --git a/system-tests/test/record_spec.js b/system-tests/test/record_spec.js index 23d5446a44a1..50cd46aa0f8c 100644 --- a/system-tests/test/record_spec.js +++ b/system-tests/test/record_spec.js @@ -1269,6 +1269,31 @@ describe('e2e record', () => { }) }) + describe('create run 402 - auto cancel not available in plan', () => { + setupStubbedServer(createRoutes({ + postRun: { + res (req, res) { + return res.status(402).json({ + code: 'AUTO_CANCEL_NOT_AVAILABLE_IN_PLAN', + payload: { + orgId: 'org-id-1234', + }, + }) + }, + } })) + + it('errors and exits when auto cancel not available in plan', function () { + return systemTests.exec(this, { + key: 'f858a2bc-b469-4e48-be67-0876339ee7e1', + configFile: 'cypress-with-project-id.config.js', + spec: 'record_pass*', + record: true, + snapshot: true, + expectedExitCode: 1, + }) + }) + }) + describe('create instance', () => { setupStubbedServer(createRoutes({ postRunInstance: {