diff --git a/CHANGELOG.md b/CHANGELOG.md index d4f98e28e..2c9c4043a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ Bug-fixes within the same version aren't needed * Adds a setting to control when the debug CodeLens appears - seanpoulter * Support the "Jest: Start/Stop" and "Show output" commands without an active text editor - seanpoulter +* Restart Jest with --watchAll when --watch is not supported without git/hg + - seanpoulter --> diff --git a/src/Jest/index.ts b/src/Jest/index.ts new file mode 100644 index 000000000..6576ae0a1 --- /dev/null +++ b/src/Jest/index.ts @@ -0,0 +1,8 @@ +export enum WatchMode { + None = 'none', + Watch = 'watch', + WatchAll = 'watchAll', +} + +export const isWatchNotSupported = (str = '') => + new RegExp('^s*--watch is not supported without git/hg, please use --watchAlls*', 'im').test(str) diff --git a/src/JestExt.ts b/src/JestExt.ts index 386f9be9e..39ff8a69f 100644 --- a/src/JestExt.ts +++ b/src/JestExt.ts @@ -22,6 +22,7 @@ import { DecorationOptions } from './types' import { hasDocument, isOpenInMultipleEditors } from './editor' import { CoverageOverlay } from './Coverage/CoverageOverlay' import { JestProcess, JestProcessManager } from './JestProcessManagement' +import { isWatchNotSupported, WatchMode } from './Jest' export class JestExt { private workspace: ProjectWorkspace @@ -103,6 +104,10 @@ export class JestExt { return } + if (isWatchNotSupported(message)) { + this.jestProcess.watchMode = WatchMode.WatchAll + } + // The "tests are done" message comes through stdErr // We want to use this as a marker that the console should // be cleared, as the next input will be from a new test run. @@ -152,7 +157,7 @@ export class JestExt { } this.jestProcess = this.jestProcessManager.startJestProcess({ - watch: true, + watchMode: WatchMode.Watch, keepAlive: true, exitCallback: (jestProcess, jestProcessInWatchMode) => { if (jestProcessInWatchMode) { diff --git a/src/JestProcessManagement/JestProcess.ts b/src/JestProcessManagement/JestProcess.ts index 4fb6a7a39..24e2af1c6 100644 --- a/src/JestProcessManagement/JestProcess.ts +++ b/src/JestProcessManagement/JestProcess.ts @@ -1,5 +1,6 @@ import { platform } from 'os' import { Runner, ProjectWorkspace } from 'jest-editor-support' +import { WatchMode } from '../Jest' export class JestProcess { static readonly keepAliveLimit = 5 @@ -10,21 +11,19 @@ export class JestProcess { private resolve: Function private keepAliveCounter: number public keepAlive: boolean - public watchMode: boolean public stopRequested: boolean + watchMode: WatchMode private startRunner() { this.stopRequested = false let exited = false - // Use a shell to run Jest command on Windows in order to correctly spawn `.cmd` files - // For details see https://github.com/jest-community/vscode-jest/issues/98 - const useShell = platform() === 'win32' - this.runner = new Runner(this.projectWorkspace, { shell: useShell }) + + const options = { shell: platform() === 'win32' } + this.runner = new Runner(this.projectWorkspace, options) this.restoreJestEvents() - // pass watchMode into watchAll parameter also - this.runner.start(this.watchMode, this.watchMode) + this.runner.start(this.watchMode !== WatchMode.None, this.watchMode === WatchMode.WatchAll) this.runner.on('debuggerProcessExit', () => { if (!exited) { @@ -50,11 +49,11 @@ export class JestProcess { constructor({ projectWorkspace, - watchMode = false, + watchMode = WatchMode.None, keepAlive = false, }: { projectWorkspace: ProjectWorkspace - watchMode?: boolean + watchMode?: WatchMode keepAlive?: boolean }) { this.keepAlive = keepAlive diff --git a/src/JestProcessManagement/JestProcessManager.ts b/src/JestProcessManagement/JestProcessManager.ts index ae4fdfbc5..024d8eb90 100644 --- a/src/JestProcessManagement/JestProcessManager.ts +++ b/src/JestProcessManagement/JestProcessManager.ts @@ -1,5 +1,6 @@ import { ProjectWorkspace } from 'jest-editor-support' import { JestProcess } from './JestProcess' +import { WatchMode } from '../Jest' export class JestProcessManager { private projectWorkspace: ProjectWorkspace @@ -24,11 +25,11 @@ export class JestProcessManager { } } - private runJest({ watch, keepAlive, exitCallback }) { + private runJest({ watchMode, keepAlive, exitCallback }) { const jestProcess = new JestProcess({ projectWorkspace: this.projectWorkspace, - watchMode: watch, - keepAlive: keepAlive, + watchMode, + keepAlive, }) this.jestProcesses.unshift(jestProcess) @@ -37,9 +38,9 @@ export class JestProcessManager { return jestProcess } - private run({ watch, keepAlive, exitCallback }) { + private run({ watchMode, keepAlive, exitCallback }) { return this.runJest({ - watch, + watchMode, keepAlive, exitCallback: exitedJestProcess => { exitCallback(exitedJestProcess) @@ -52,7 +53,7 @@ export class JestProcessManager { private runAllTestsFirst(onExit) { return this.runJest({ - watch: false, + watchMode: WatchMode.None, keepAlive: false, exitCallback: onExit, }) @@ -61,33 +62,29 @@ export class JestProcessManager { public startJestProcess( { exitCallback = () => {}, - watch = false, + watchMode = WatchMode.None, keepAlive = false, }: { exitCallback?: Function - watch?: boolean + watchMode?: WatchMode keepAlive?: boolean - } = { - exitCallback: () => {}, - watch: false, - keepAlive: false, - } + } = {} ): JestProcess { - if (watch && this.runAllTestsFirstInWatchMode) { + if (watchMode !== WatchMode.None && this.runAllTestsFirstInWatchMode) { return this.runAllTestsFirst(exitedJestProcess => { this.removeJestProcessReference(exitedJestProcess) const jestProcessInWatchMode = this.run({ - watch: true, - keepAlive: keepAlive, - exitCallback: exitCallback, + watchMode: WatchMode.Watch, + keepAlive, + exitCallback, }) exitCallback(exitedJestProcess, jestProcessInWatchMode) }) } else { return this.run({ - watch: watch, - keepAlive: keepAlive, - exitCallback: exitCallback, + watchMode, + keepAlive, + exitCallback, }) } } diff --git a/tests/Jest/index.test.ts b/tests/Jest/index.test.ts new file mode 100644 index 000000000..9f60efb43 --- /dev/null +++ b/tests/Jest/index.test.ts @@ -0,0 +1,13 @@ +jest.unmock('../../src/Jest') +import { isWatchNotSupported } from '../../src/Jest' + +describe('isWatchNotSupported', () => { + it('returns true when matching the expected message', () => { + const str = '\n--watch is not supported without git/hg, please use --watchAll \n' + expect(isWatchNotSupported(str)).toBe(true) + }) + + it('returns false otherwise', () => { + expect(isWatchNotSupported()).toBe(false) + }) +}) diff --git a/tests/JestProcessManagement/JestProcess.test.ts b/tests/JestProcessManagement/JestProcess.test.ts index 6f60a8132..5eec4e34e 100644 --- a/tests/JestProcessManagement/JestProcess.test.ts +++ b/tests/JestProcessManagement/JestProcess.test.ts @@ -3,6 +3,7 @@ jest.unmock('../../src/JestProcessManagement/JestProcess') import { Runner, ProjectWorkspace } from 'jest-editor-support' import { JestProcess } from '../../src/JestProcessManagement/JestProcess' import { EventEmitter } from 'events' +import { WatchMode } from '../../src/Jest' describe('JestProcess', () => { let projectWorkspaceMock @@ -39,20 +40,15 @@ describe('JestProcess', () => { expect(jestProcess.stopRequested).toBeFalsy() }) - it('accepts watchMode boolean argument', () => { - jestProcess = new JestProcess({ - projectWorkspace: projectWorkspaceMock, - watchMode: true, - }) - expect(jestProcess).not.toBe(null) - }) - it('records watchMode in the watchMode property', () => { + const expected = WatchMode.Watch + jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, - watchMode: true, + watchMode: expected, }) - expect(jestProcess.watchMode).toBe(true) + + expect(jestProcess.watchMode).toBe(expected) }) it('creates an instance of jest-editor-support runner', () => { @@ -69,34 +65,28 @@ describe('JestProcess', () => { expect(runnerMock.mock.calls[0][0]).toBe(projectWorkspaceMock) }) - it('starts the jest-editor-support runner', () => { + it('starts the jest-editor-support Runner without watch mode by default', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, }) expect(runnerMockImplementation.start).toHaveBeenCalledTimes(1) + expect(runnerMockImplementation.start.mock.calls[0]).toEqual([false, false]) }) - it('passes the watchMode argument == false to the start command when it is not provided', () => { + it('starts the jest-editor-support Runner in --watch mode', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, + watchMode: WatchMode.Watch, }) - expect(runnerMockImplementation.start.mock.calls[0][0]).toBe(false) + expect(runnerMockImplementation.start.mock.calls[0]).toEqual([true, false]) }) - it('passes the watchMode argument == false to the start command when it is false', () => { + it('starts the jest-editor-support Runner in --watchAll mode', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, - watchMode: false, + watchMode: WatchMode.WatchAll, }) - expect(runnerMockImplementation.start.mock.calls[0][0]).toBe(false) - }) - - it('passes the watchMode argument == true to the start command when it is true', () => { - jestProcess = new JestProcess({ - projectWorkspace: projectWorkspaceMock, - watchMode: true, - }) - expect(runnerMockImplementation.start.mock.calls[0][0]).toBe(true) + expect(runnerMockImplementation.start.mock.calls[0]).toEqual([true, true]) }) }) @@ -241,7 +231,7 @@ describe('JestProcess', () => { expect(jestProcess.keepAlive).toBeFalsy() }) - it('creates new instance of jest-editor-support runner', () => { + it('creates new instance of jest-editor-support Runner', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, keepAlive: true, @@ -297,26 +287,16 @@ describe('JestProcess', () => { expect(runnerMockImplementation.start).toHaveBeenCalledTimes(2) }) - it('passes the watchMode argument to the new runner instance when it is false', () => { - jestProcess = new JestProcess({ - projectWorkspace: projectWorkspaceMock, - watchMode: false, - keepAlive: true, - }) - jestProcess.onExit(onExit) - eventEmitter.emit('debuggerProcessExit') - expect(runnerMockImplementation.start.mock.calls[1][0]).toBe(false) - }) - it('passes the watchMode argument to the new runner instance when it is true', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, - watchMode: true, + watchMode: WatchMode.WatchAll, keepAlive: true, }) jestProcess.onExit(onExit) eventEmitter.emit('debuggerProcessExit') - expect(runnerMockImplementation.start.mock.calls[1][0]).toBe(true) + + expect(runnerMockImplementation.start.mock.calls[1]).toEqual([true, true]) }) it('removes all event listeners from the previous instance of the runner', () => { diff --git a/tests/JestProcessManagement/JestProcessManager.test.ts b/tests/JestProcessManagement/JestProcessManager.test.ts index 2b79e5c57..d9efe20dc 100644 --- a/tests/JestProcessManagement/JestProcessManager.test.ts +++ b/tests/JestProcessManagement/JestProcessManager.test.ts @@ -4,6 +4,7 @@ import { ProjectWorkspace } from 'jest-editor-support' import { JestProcessManager } from '../../src/JestProcessManagement/JestProcessManager' import { JestProcess } from '../../src/JestProcessManagement/JestProcess' import { EventEmitter } from 'events' +import { WatchMode } from '../../src/Jest' describe('JestProcessManager', () => { let jestProcessManager @@ -76,7 +77,7 @@ describe('JestProcessManager', () => { it('passes the watchMode flag set to false', () => { jestProcessManager.startJestProcess() - expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('watchMode', false) + expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('watchMode', WatchMode.None) }) }) @@ -103,33 +104,33 @@ describe('JestProcessManager', () => { }) describe('when starting jest process in watch mode', () => { - it('creates two jest processes: one to run all the tests and one for the watch mode', () => { + it('will run all tests without watch mode then restart with --watch', () => { jestProcessMock.mockImplementation(() => ({ onExit: callback => { eventEmitter.on('debuggerProcessExit', callback) }, })) - jestProcessManager.startJestProcess({ watch: true }) + jestProcessManager.startJestProcess({ watchMode: WatchMode.Watch }) eventEmitter.emit('debuggerProcessExit') - expect(jestProcessMock.mock.instances.length).toBe(2) - }) - - it('first runs all the tests in non-watch mode and then spins jest process in watch mode', () => { - jestProcessMock.mockImplementation(() => ({ - onExit: callback => { - eventEmitter.on('debuggerProcessExit', callback) - }, - })) - - jestProcessManager.startJestProcess({ watch: true }) - - eventEmitter.emit('debuggerProcessExit') - - expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('watchMode', false) - expect(jestProcessMock.mock.calls[1][0]).toHaveProperty('watchMode', true) + expect(jestProcessMock.mock.calls).toEqual([ + [ + { + keepAlive: false, + projectWorkspace: projectWorkspaceMock, + watchMode: WatchMode.None, + }, + ], + [ + { + keepAlive: false, + projectWorkspace: projectWorkspaceMock, + watchMode: WatchMode.Watch, + }, + ], + ]) }) it('starts the process for non-watch mode with keep-alive flag set to false', () => { @@ -139,7 +140,10 @@ describe('JestProcessManager', () => { }, })) - jestProcessManager.startJestProcess({ watch: true, keepAlive: true }) + jestProcessManager.startJestProcess({ + keepAlive: true, + watchMode: WatchMode.Watch, + }) // we need this to trigger the watch-mode process that only starts // after the non-watch-mode process exits @@ -156,7 +160,7 @@ describe('JestProcessManager', () => { }, })) - jestProcessManager.startJestProcess({ watch: true }) + jestProcessManager.startJestProcess({ watchMode: WatchMode.Watch }) eventEmitter.emit('debuggerProcessExit') @@ -179,7 +183,10 @@ describe('JestProcessManager', () => { onExit: onExitMock, })) - jestProcessManager.startJestProcess({ watch: true, exitCallback: exitHandler }) + jestProcessManager.startJestProcess({ + exitCallback: exitHandler, + watchMode: WatchMode.Watch, + }) eventEmitter.emit('debuggerProcessExit', { watchMode: false }) eventEmitterForWatchMode.emit('debuggerProcessExit', { watchMode: true }) @@ -207,7 +214,10 @@ describe('JestProcessManager', () => { jestProcessMock.mockImplementation(() => mockImplementation) - jestProcessManager.startJestProcess({ watch: true, exitCallback: exitHandler }) + jestProcessManager.startJestProcess({ + exitCallback: exitHandler, + watchMode: WatchMode.Watch, + }) eventEmitter.emit('debuggerProcessExit', mockImplementation) eventEmitterForWatchMode.emit('debuggerProcessExit', mockImplementation) @@ -317,7 +327,10 @@ describe('JestProcessManager', () => { }, })) - const jestProcess = jestProcessManager.startJestProcess({ watch: true, keepAlive: true }) + const jestProcess = jestProcessManager.startJestProcess({ + keepAlive: true, + watchMode: WatchMode.Watch, + }) eventEmitter.emit('debuggerProcessExit', jestProcess) @@ -412,27 +425,32 @@ describe('JestProcessManager', () => { }) describe('when runAllTestsFirstInWatchMode is false', () => { - beforeEach(() => { + it('does not run all tests first', () => { jestProcessManager = new JestProcessManager({ projectWorkspace: projectWorkspaceMock, runAllTestsFirstInWatchMode: false, }) - }) - it('does not run all tests first', () => { jestProcessMock.mockImplementation(() => ({ onExit: callback => { eventEmitter.on('debuggerProcessExit', callback) }, })) - const jestProcess = jestProcessManager.startJestProcess({ watch: true }) + const watchMode = WatchMode.Watch + const jestProcess = jestProcessManager.startJestProcess({ watchMode }) eventEmitter.emit('debuggerProcessExit', jestProcess) expect(jestProcessMock.mock.instances.length).toBe(1) - expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('watchMode', true) + expect(jestProcessMock.mock.calls[0]).toEqual([ + { + keepAlive: false, + projectWorkspace: projectWorkspaceMock, + watchMode, + }, + ]) }) }) })