diff --git a/packages/config/__snapshots__/index_spec.js b/packages/config/__snapshots__/index_spec.js index 574dc54d5e11..2e39e551f5bd 100644 --- a/packages/config/__snapshots__/index_spec.js +++ b/packages/config/__snapshots__/index_spec.js @@ -23,7 +23,6 @@ exports['src/index .getDefaultValues returns list of public config keys 1'] = { "e2e": {}, "env": {}, "execTimeout": 60000, - "exit": true, "experimentalFetchPolyfill": false, "experimentalInteractiveRunEvents": false, "experimentalSessionSupport": false, @@ -100,7 +99,6 @@ exports['src/index .getPublicConfigKeys returns list of public config keys 1'] = "e2e", "env", "execTimeout", - "exit", "experimentalFetchPolyfill", "experimentalInteractiveRunEvents", "experimentalSessionSupport", diff --git a/packages/config/lib/options.ts b/packages/config/lib/options.ts index a548b345659c..4aeecc6ab2af 100644 --- a/packages/config/lib/options.ts +++ b/packages/config/lib/options.ts @@ -140,11 +140,6 @@ const resolvedOptions: Array = [ defaultValue: 60000, validation: validate.isNumber, canUpdateDuringTestTime: true, - }, { - name: 'exit', - defaultValue: true, - validation: validate.isBoolean, - canUpdateDuringTestTime: false, }, { name: 'experimentalFetchPolyfill', defaultValue: false, diff --git a/packages/server/lib/modes/run.js b/packages/server/lib/modes/run.js index f2f79dd9b44b..73bd7177050a 100644 --- a/packages/server/lib/modes/run.js +++ b/packages/server/lib/modes/run.js @@ -1459,7 +1459,7 @@ module.exports = { compressedVideoName: videoRecordProps.compressedVideoName, endVideoCapture: videoRecordProps.endVideoCapture, startedVideoCapture: videoRecordProps.startedVideoCapture, - exit: config.exit, + exit: options.exit, videoCompression: options.videoCompression, videoUploadOnPasses: options.videoUploadOnPasses, quiet: options.quiet, @@ -1618,6 +1618,7 @@ module.exports = { quiet: options.quiet, outputPath: options.outputPath, testingType: options.testingType, + exit: options.exit, }) .tap((runSpecs) => { if (!options.quiet) { diff --git a/packages/server/test/unit/config_spec.js b/packages/server/test/unit/config_spec.js index 85a61ecb6709..4edc2073e076 100644 --- a/packages/server/test/unit/config_spec.js +++ b/packages/server/test/unit/config_spec.js @@ -1482,7 +1482,6 @@ describe('lib/config', () => { e2e: { from: 'default', value: {} }, env: {}, execTimeout: { value: 60000, from: 'default' }, - exit: { value: true, from: 'default' }, experimentalFetchPolyfill: { value: false, from: 'default' }, experimentalInteractiveRunEvents: { value: false, from: 'default' }, experimentalSourceRewriting: { value: false, from: 'default' }, @@ -1572,7 +1571,6 @@ describe('lib/config', () => { downloadsFolder: { value: 'cypress/downloads', from: 'default' }, e2e: { from: 'default', value: {} }, execTimeout: { value: 60000, from: 'default' }, - exit: { value: true, from: 'default' }, experimentalFetchPolyfill: { value: false, from: 'default' }, experimentalInteractiveRunEvents: { value: false, from: 'default' }, experimentalSourceRewriting: { value: false, from: 'default' }, diff --git a/packages/server/test/unit/modes/run_spec.js b/packages/server/test/unit/modes/run_spec.js index d6180dc590e0..ba7decd82a53 100644 --- a/packages/server/test/unit/modes/run_spec.js +++ b/packages/server/test/unit/modes/run_spec.js @@ -810,21 +810,6 @@ describe('lib/modes/run', () => { }) }) - it('passes exit from config to waitForTestsToFinishRunning', function () { - this.projectInstance.getConfig.restore() - sinon.stub(this.projectInstance, 'getConfig').resolves({ - proxyUrl: 'http://localhost:12345', - exit: false, - }) - - return runMode.run() - .then(() => { - expect(runMode.waitForTestsToFinishRunning).to.be.calledWithMatch({ - exit: false, - }) - }) - }) - it('passes headed to openProject.launch', () => { const browser = { name: 'electron', family: 'chromium' } diff --git a/system-tests/__snapshots__/pause_headed_exit_spec.ts.js b/system-tests/__snapshots__/pause_headed_exit_spec.ts.js new file mode 100644 index 000000000000..7544efabb671 --- /dev/null +++ b/system-tests/__snapshots__/pause_headed_exit_spec.ts.js @@ -0,0 +1,179 @@ +exports['cy.pause() in run mode / pauses with --headed and --no-exit'] = ` + +==================================================================================================== + + (Run Starting) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Cypress: 1.2.3 │ + │ Browser: FooBrowser 88 │ + │ Specs: 1 found (pause_spec.js) │ + │ Searched: cypress/integration/pause_spec.js │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +──────────────────────────────────────────────────────────────────────────────────────────────────── + + Running: pause_spec.js (1 of 1) + + + cy.pause() + ✓ pauses + + + 1 passing + +not exiting due to options.exit being false + +` + +exports['cy.pause() in run mode / does not pause if headless'] = ` + +==================================================================================================== + + (Run Starting) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Cypress: 1.2.3 │ + │ Browser: FooBrowser 88 │ + │ Specs: 1 found (pause_spec.js) │ + │ Searched: cypress/integration/pause_spec.js │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +──────────────────────────────────────────────────────────────────────────────────────────────────── + + Running: pause_spec.js (1 of 1) + + + cy.pause() + ✓ pauses + + + 1 passing + +not exiting due to options.exit being false + +` + +exports['cy.pause() in run mode / does not pause without --no-exit'] = ` + +==================================================================================================== + + (Run Starting) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Cypress: 1.2.3 │ + │ Browser: FooBrowser 88 │ + │ Specs: 1 found (pause_spec.js) │ + │ Searched: cypress/integration/pause_spec.js │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +──────────────────────────────────────────────────────────────────────────────────────────────────── + + Running: pause_spec.js (1 of 1) + + + cy.pause() + ✓ pauses + + + 1 passing + + + (Results) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Tests: 1 │ + │ Passing: 1 │ + │ Failing: 0 │ + │ Pending: 0 │ + │ Skipped: 0 │ + │ Screenshots: 0 │ + │ Video: true │ + │ Duration: X seconds │ + │ Spec Ran: pause_spec.js │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + + (Video) + + - Started processing: Compressing to 32 CRF + - Finished processing: /XXX/XXX/XXX/cypress/videos/pause_spec.js.mp4 (X second) + + +==================================================================================================== + + (Run Finished) + + + Spec Tests Passing Failing Pending Skipped + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ ✔ pause_spec.js XX:XX 1 1 - - - │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + ✔ All specs passed! XX:XX 1 1 - - - + + +` + +exports['cy.pause() in run mode / does not pause without --headed and --no-exit'] = ` + +==================================================================================================== + + (Run Starting) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Cypress: 1.2.3 │ + │ Browser: FooBrowser 88 │ + │ Specs: 1 found (pause_spec.js) │ + │ Searched: cypress/integration/pause_spec.js │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +──────────────────────────────────────────────────────────────────────────────────────────────────── + + Running: pause_spec.js (1 of 1) + + + cy.pause() + ✓ pauses + + + 1 passing + + + (Results) + + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Tests: 1 │ + │ Passing: 1 │ + │ Failing: 0 │ + │ Pending: 0 │ + │ Skipped: 0 │ + │ Screenshots: 0 │ + │ Video: true │ + │ Duration: X seconds │ + │ Spec Ran: pause_spec.js │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + + + (Video) + + - Started processing: Compressing to 32 CRF + - Finished processing: /XXX/XXX/XXX/cypress/videos/pause_spec.js.mp4 (X second) + + +==================================================================================================== + + (Run Finished) + + + Spec Tests Passing Failing Pending Skipped + ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ ✔ pause_spec.js XX:XX 1 1 - - - │ + └────────────────────────────────────────────────────────────────────────────────────────────────┘ + ✔ All specs passed! XX:XX 1 1 - - - + + +` diff --git a/system-tests/lib/docker.ts b/system-tests/lib/docker.ts index def2684e0a49..e40e0abd1592 100644 --- a/system-tests/lib/docker.ts +++ b/system-tests/lib/docker.ts @@ -104,6 +104,10 @@ class DockerProcess extends EventEmitter implements SpawnerResult { }, ) } + + kill (): boolean { + throw new Error('.kill not implemented for DockerProcess.') + } } const checkBuiltBinary = async () => { diff --git a/system-tests/lib/system-tests.ts b/system-tests/lib/system-tests.ts index 4cecba065d9e..6d21c44bf0dd 100644 --- a/system-tests/lib/system-tests.ts +++ b/system-tests/lib/system-tests.ts @@ -1,6 +1,6 @@ const snapshot = require('snap-shot-it') -import type { SpawnOptions } from 'child_process' +import type { SpawnOptions, ChildProcess } from 'child_process' import stream from 'stream' import { expect } from './spec_helper' import { dockerSpawner } from './docker' @@ -135,6 +135,10 @@ type ExecOptions = { * Pass a function to assert on and/or modify the stdout before snapshotting. */ onStdout?: (stdout: string) => string | void + /** + * Pass a function to receive the spawned process as an argument. + */ + onSpawn?: (sp: SpawnerResult) => void /** * User-supplied snapshot title. If unset, one will be autogenerated from the suite name. */ @@ -265,6 +269,7 @@ export type SpawnerResult = { stderr: stream.Readable on(event: 'error', cb: (err: Error) => void): void on(event: 'exit', cb: (exitCode: number) => void): void + kill: ChildProcess['kill'] } const cpSpawner: Spawner = (cmd, args, env, options) => { @@ -1055,6 +1060,8 @@ const systemTests = { const spawnerFn: Spawner = options.dockerImage ? dockerSpawner : cpSpawner const sp: SpawnerResult = await spawnerFn(cmd, args, env, options) + options.onSpawn && options.onSpawn(sp) + const ColorOutput = function () { const colorOutput = new stream.Transform() diff --git a/system-tests/projects/e2e/cypress/integration/pause_spec.js b/system-tests/projects/e2e/cypress/integration/pause_spec.js new file mode 100644 index 000000000000..5e2b51a62718 --- /dev/null +++ b/system-tests/projects/e2e/cypress/integration/pause_spec.js @@ -0,0 +1,30 @@ +describe('cy.pause()', () => { + it('pauses', () => { + let didPause = false + + cy.visit('https://example.cypress.io') + + cy.once('paused', (name) => { + cy.once('paused', (name) => { + didPause = true + + // resume the rest of the commands so this + // test ends + Cypress.emit('resume:all') + }) + + Cypress.emit('resume:next') + }) + + cy.pause().wrap({}).should('deep.eq', {}).then(function () { + if (Cypress.env('SHOULD_PAUSE')) { + expect(didPause).to.be.true + + // should no longer have onPaused + expect(cy.state('onPaused')).to.be.null + } else { + expect(didPause).to.be.false + } + }) + }) +}) diff --git a/system-tests/test/pause_headed_exit_spec.ts b/system-tests/test/pause_headed_exit_spec.ts new file mode 100644 index 000000000000..3e90285616c1 --- /dev/null +++ b/system-tests/test/pause_headed_exit_spec.ts @@ -0,0 +1,71 @@ +import systemTests from '../lib/system-tests' + +describe('cy.pause() in run mode', () => { + systemTests.setup() + + systemTests.it('pauses with --headed and --no-exit', { + spec: 'pause_spec.js', + config: { + env: { + 'SHOULD_PAUSE': true, + }, + }, + snapshot: true, + headed: true, + noExit: true, + expectedExitCode: null, + onSpawn: (cp) => { + cp.stdout.on('data', (buf) => { + if (buf.toString().includes('not exiting due to options.exit being false')) { + cp.kill() + } + }) + }, + }) + + systemTests.it('does not pause if headless', { + spec: 'pause_spec.js', + config: { + env: { + 'SHOULD_PAUSE': false, + }, + }, + snapshot: true, + headed: false, + noExit: true, + expectedExitCode: null, + onSpawn: (cp) => { + cp.stdout.on('data', (buf) => { + if (buf.toString().includes('not exiting due to options.exit being false')) { + cp.kill() + } + }) + }, + }) + + systemTests.it('does not pause without --no-exit', { + spec: 'pause_spec.js', + config: { + env: { + 'SHOULD_PAUSE': false, + }, + }, + snapshot: true, + headed: true, + noExit: false, + expectedExitCode: 0, + }) + + systemTests.it('does not pause without --headed and --no-exit', { + spec: 'pause_spec.js', + config: { + env: { + 'SHOULD_PAUSE': false, + }, + }, + snapshot: true, + headed: false, + noExit: false, + expectedExitCode: 0, + }) +})