diff --git a/package.json b/package.json index f6865036c..4f9656f28 100644 --- a/package.json +++ b/package.json @@ -308,7 +308,7 @@ ], "editor/context": [ { - "when": "jest:run.interactive && editorLangId =~ /(javascript|javascriptreact|typescript|typescriptreact)/ ", + "when": "jest:run.interactive && editorLangId =~ /(javascript|javascriptreact|typescript|typescriptreact|vue)/ ", "command": "io.orta.jest.editor.run-all-tests", "group": "Jest" } @@ -319,7 +319,7 @@ "command": "io.orta.jest.editor.run-all-tests", "key": "ctrl+alt+t", "mac": "ctrl+alt+t", - "when": "jest:run.interactive && editorLangId =~ /(javascript|javascriptreact|typescript|typescriptreact)/ " + "when": "jest:run.interactive && editorLangId =~ /(javascript|javascriptreact|typescript|typescriptreact|vue)/ " } ], "debuggers": [ diff --git a/src/JestExt/core.ts b/src/JestExt/core.ts index 112a2008f..4b0ea5040 100644 --- a/src/JestExt/core.ts +++ b/src/JestExt/core.ts @@ -488,6 +488,7 @@ export class JestExt { this.processSession.scheduleProcess({ type: 'by-file', testFileName: name, + notTestFile: this.testResultProvider.isTestFile(name) !== 'yes', }) ) { this.dirtyFiles.delete(name); @@ -524,14 +525,15 @@ export class JestExt { if (!this.isSupportedDocument(document) || this.extContext.autoRun.isWatch) { return; } + const isTestFile = this.testResultProvider.isTestFile(document.fileName); if ( this.extContext.autoRun.onSave && - (this.extContext.autoRun.onSave === 'test-src-file' || - this.testResultProvider.isTestFile(document.fileName) !== 'no') + (this.extContext.autoRun.onSave === 'test-src-file' || isTestFile !== 'no') ) { this.processSession.scheduleProcess({ type: 'by-file', testFileName: document.fileName, + notTestFile: isTestFile !== 'yes', }); } else { this.dirtyFiles.add(document.fileName); diff --git a/src/JestProcessManagement/JestProcess.ts b/src/JestProcessManagement/JestProcess.ts index b3eff3bf4..ac9235c37 100644 --- a/src/JestProcessManagement/JestProcess.ts +++ b/src/JestProcessManagement/JestProcess.ts @@ -119,7 +119,10 @@ export class JestProcess implements JestProcessInfo { break; case 'by-file': { options.testFileNamePattern = this.quoteFileName(this.request.testFileName); - args.push('--findRelatedTests', '--watchAll=false'); + args.push('--watchAll=false'); + if (this.request.notTestFile) { + args.push('--findRelatedTests'); + } if (this.request.updateSnapshot) { args.push('--updateSnapshot'); } diff --git a/src/JestProcessManagement/types.ts b/src/JestProcessManagement/types.ts index 9b6e7c492..1c395590b 100644 --- a/src/JestProcessManagement/types.ts +++ b/src/JestProcessManagement/types.ts @@ -55,6 +55,7 @@ export type JestProcessRequestBase = type: Extract; testFileName: string; updateSnapshot?: boolean; + notTestFile?: boolean; } | { type: Extract; diff --git a/src/appGlobals.ts b/src/appGlobals.ts index d82175e90..35ee7b5ac 100644 --- a/src/appGlobals.ts +++ b/src/appGlobals.ts @@ -5,4 +5,5 @@ export const SupportedLanguageIds = [ 'javascriptreact', 'typescript', 'typescriptreact', + 'vue', ]; diff --git a/src/extension.ts b/src/extension.ts index d27d8b05f..12e21136f 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -15,6 +15,7 @@ const addSubscriptions = (context: vscode.ExtensionContext): void => { { language: 'javascriptreact' }, { language: 'typescript' }, { language: 'typescriptreact' }, + { language: 'vue' }, ]; // command function diff --git a/src/test-provider/test-item-data.ts b/src/test-provider/test-item-data.ts index 90bf25e15..970010ffe 100644 --- a/src/test-provider/test-item-data.ts +++ b/src/test-provider/test-item-data.ts @@ -253,7 +253,8 @@ export class WorkspaceRoot extends TestItemDataBase { } }; - private getItemFromProcess = (process: JestProcessInfo): vscode.TestItem => { + /** get test item from jest process. If running tests from source file, will return undefined */ + private getItemFromProcess = (process: JestProcessInfo): vscode.TestItem | undefined => { let fileName; switch (process.request.type) { case 'watch-tests': @@ -270,15 +271,11 @@ export class WorkspaceRoot extends TestItemDataBase { throw new Error(`unsupported external process type ${process.request.type}`); } - const item = this.testDocuments.get(fileName)?.item; - if (item) { - return item; - } - throw new Error(`No test file found for ${fileName}`); + return this.testDocuments.get(fileName)?.item; }; private createTestItemRun = (event: JestRunEvent): TestItemRun => { - const item = this.getItemFromProcess(event.process); + const item = this.getItemFromProcess(event.process) ?? this.item; const run = this.createRun(`${event.type}:${event.process.id}`, item); const end = () => { this.cachedRun.delete(event.process.id); diff --git a/tests/JestExt/core.test.ts b/tests/JestExt/core.test.ts index 26bd09be0..4b500dac8 100644 --- a/tests/JestExt/core.test.ts +++ b/tests/JestExt/core.test.ts @@ -506,6 +506,7 @@ describe('JestExt', () => { ${{ watch: true }} | ${'javascript'} | ${'yes'} | ${false} | ${false} ${{ watch: false }} | ${'javascript'} | ${'yes'} | ${false} | ${true} ${{ watch: false, onSave: 'test-src-file' }} | ${'javascript'} | ${'no'} | ${true} | ${false} + ${{ watch: false, onSave: 'test-src-file' }} | ${'javascript'} | ${'unknown'} | ${true} | ${false} ${{ watch: false, onSave: 'test-src-file' }} | ${'javascript'} | ${'yes'} | ${true} | ${false} ${{ watch: false, onSave: 'test-src-file' }} | ${'json'} | ${'no'} | ${false} | ${false} ${{ watch: false, onSave: 'test-file' }} | ${'javascript'} | ${'no'} | ${false} | ${true} @@ -533,7 +534,11 @@ describe('JestExt', () => { if (shouldSchedule) { expect(mockProcessSession.scheduleProcess).toBeCalledWith( - expect.objectContaining({ type: 'by-file', testFileName: fileName }) + expect.objectContaining({ + type: 'by-file', + testFileName: fileName, + notTestFile: isTestFile !== 'yes', + }) ); } else { expect(mockProcessSession.scheduleProcess).not.toBeCalled(); @@ -651,6 +656,7 @@ describe('JestExt', () => { ${'javascriptreact'} | ${false} ${'typescript'} | ${false} ${'typescriptreact'} | ${false} + ${'vue'} | ${false} `('if languageId=languageId => skip? $shouldSkip', ({ languageId, shouldSkip }) => { const editor = mockEditor('file', languageId); sut.triggerUpdateActiveEditor(editor); @@ -887,20 +893,69 @@ describe('JestExt', () => { }); }); describe('runAllTests', () => { - it('can run all test for the workspace', () => { - const sut = newJestExt(); - sut.runAllTests(); - expect(mockProcessSession.scheduleProcess).toBeCalledWith({ type: 'all-tests' }); - }); - it('can run all test for the given editor', () => { - const sut = newJestExt(); - const editor: any = { document: { fileName: 'whatever' } }; - sut.runAllTests(editor); - expect(mockProcessSession.scheduleProcess).toBeCalledWith({ - type: 'by-file', - testFileName: 'whatever', + describe.each` + scheduleProcess + ${{}} + ${undefined} + `('scheduleProcess returns $scheduleProcess', ({ scheduleProcess }) => { + beforeEach(() => { + mockProcessSession.scheduleProcess.mockReturnValueOnce(scheduleProcess); + }); + it('can run all test for the workspace', () => { + const sut = newJestExt(); + const dirtyFiles: any = sut['dirtyFiles']; + dirtyFiles.clear = jest.fn(); + + sut.runAllTests(); + expect(mockProcessSession.scheduleProcess).toBeCalledWith({ type: 'all-tests' }); + if (scheduleProcess) { + expect(dirtyFiles.clear).toBeCalled(); + } else { + expect(dirtyFiles.clear).not.toBeCalled(); + } + }); + it('can run all test for the given editor', () => { + const sut = newJestExt(); + + const dirtyFiles: any = sut['dirtyFiles']; + dirtyFiles.delete = jest.fn(); + + const editor: any = { document: { fileName: 'whatever' } }; + + sut.runAllTests(editor); + expect(mockProcessSession.scheduleProcess).toBeCalledWith({ + type: 'by-file', + testFileName: editor.document.fileName, + notTestFile: true, + }); + if (scheduleProcess) { + expect(dirtyFiles.delete).toBeCalledWith(editor.document.fileName); + } else { + expect(dirtyFiles.delete).not.toBeCalled(); + } }); }); + it.each` + isTestFile | notTestFile + ${'yes'} | ${false} + ${'no'} | ${true} + ${'unknown'} | ${true} + `( + 'treat unknown as notTestFile: isTestFile=$isTestFile => notTestFile=$notTestFile', + ({ isTestFile, notTestFile }) => { + const sut = newJestExt(); + const editor: any = { document: { fileName: 'whatever' } }; + + (sut.testResultProvider.isTestFile as jest.Mocked).mockReturnValueOnce(isTestFile); + + sut.runAllTests(editor); + expect(mockProcessSession.scheduleProcess).toBeCalledWith({ + type: 'by-file', + testFileName: editor.document.fileName, + notTestFile: notTestFile, + }); + } + ); }); describe('refresh test file list upon file system change', () => { const getProcessType = () => { diff --git a/tests/JestProcessManagement/JestProcess.test.ts b/tests/JestProcessManagement/JestProcess.test.ts index 1a196e48e..818b8fb68 100644 --- a/tests/JestProcessManagement/JestProcess.test.ts +++ b/tests/JestProcessManagement/JestProcess.test.ts @@ -140,7 +140,8 @@ describe('JestProcess', () => { ${'all-tests'} | ${undefined} | ${[false, false]} | ${true} | ${undefined} ${'watch-tests'} | ${undefined} | ${[true, false]} | ${true} | ${undefined} ${'watch-all-tests'} | ${undefined} | ${[true, true]} | ${true} | ${undefined} - ${'by-file'} | ${{ testFileName: '"c:\\a\\b.ts"' }} | ${[false, false]} | ${true} | ${{ args: { args: ['--findRelatedTests'] }, testFileNamePattern: '"C:\\a\\b.ts"' }} + ${'by-file'} | ${{ testFileName: '"c:\\a\\b.ts"' }} | ${[false, false]} | ${true} | ${{ args: { args: [] }, testFileNamePattern: '"C:\\a\\b.ts"' }} + ${'by-file'} | ${{ testFileName: '"c:\\a\\b.ts"', notTestFile: true }} | ${[false, false]} | ${true} | ${{ args: { args: ['--findRelatedTests'] }, testFileNamePattern: '"C:\\a\\b.ts"' }} ${'by-file-test'} | ${{ testFileName: '"/a/b.js"', testNamePattern: 'a test' }} | ${[false, false]} | ${true} | ${{ args: { args: ['--runTestsByPath'] }, testFileNamePattern: '"/a/b.js"', testNamePattern: '"a test"' }} ${'by-file-pattern'} | ${{ testFileNamePattern: '"c:\\a\\b.ts"' }} | ${[false, false]} | ${true} | ${{ args: { args: ['--testPathPattern', '"c:\\\\a\\\\b\\.ts"'] } }} ${'by-file-test-pattern'} | ${{ testFileNamePattern: '/a/b.js', testNamePattern: 'a test' }} | ${[false, false]} | ${true} | ${{ args: { args: ['--testPathPattern', '"/a/b\\.js"'] }, testNamePattern: '"a test"' }} diff --git a/tests/test-provider/test-item-data.test.ts b/tests/test-provider/test-item-data.test.ts index 93b3a9922..2fb56178c 100644 --- a/tests/test-provider/test-item-data.test.ts +++ b/tests/test-provider/test-item-data.test.ts @@ -1092,12 +1092,13 @@ describe('test-item-data', () => { controllerMock.createTestRun.mockClear(); }); describe.each` - request | withFile - ${{ type: 'watch-tests' }} | ${false} - ${{ type: 'watch-all-tests' }} | ${false} - ${{ type: 'all-tests' }} | ${false} - ${{ type: 'by-file', testFileName: file }} | ${true} - ${{ type: 'by-file-pattern', testFileNamePattern: file }} | ${true} + request | withFile + ${{ type: 'watch-tests' }} | ${false} + ${{ type: 'watch-all-tests' }} | ${false} + ${{ type: 'all-tests' }} | ${false} + ${{ type: 'by-file', testFileName: file }} | ${true} + ${{ type: 'by-file', testFileName: 'source.ts', notTestFile: true }} | ${false} + ${{ type: 'by-file-pattern', testFileNamePattern: file }} | ${true} `('will create a new run and use it throughout: $request', ({ request, withFile }) => { it('if run starts before schedule returns: no enqueue', () => { const process = { id: 'whatever', request };