From 000a6d14d0bec888be6bed30fbbe3b3717c7793e Mon Sep 17 00:00:00 2001 From: ConnectDotz Date: Mon, 12 Aug 2024 22:06:31 -0400 Subject: [PATCH] adding existing jest.nodeEnv into debug config (#1167) --- src/DebugConfigurationProvider.ts | 85 +++++---- src/JestExt/core.ts | 16 +- src/setup-wizard/tasks/setup-jest-debug.ts | 6 +- src/setup-wizard/types.ts | 6 +- src/setup-wizard/wizard-helper.ts | 3 +- tests/DebugConfigurationProvider.test.ts | 164 ++++++++++++------ tests/JestExt/core.test.ts | 36 ++-- tests/mock-platform.ts | 93 ++++++++++ .../tasks/setup-jest-debug.test.ts | 56 +++--- 9 files changed, 326 insertions(+), 139 deletions(-) create mode 100644 tests/mock-platform.ts diff --git a/src/DebugConfigurationProvider.ts b/src/DebugConfigurationProvider.ts index 27ed45970..c0d7ffc2d 100755 --- a/src/DebugConfigurationProvider.ts +++ b/src/DebugConfigurationProvider.ts @@ -11,11 +11,17 @@ import { toAbsoluteRootPath, } from './helpers'; import { platform } from 'os'; +import { PluginResourceSettings } from './Settings'; export const DEBUG_CONFIG_PLATFORMS = ['windows', 'linux', 'osx']; const testNamePatternRegex = /\$\{jest.testNamePattern\}/g; const testFileRegex = /\$\{jest.testFile\}/g; const testFilePatternRegex = /\$\{jest.testFilePattern\}/g; + +export type DebugConfigOptions = Partial< + Pick +>; +type PartialDebugConfig = Partial; export class DebugConfigurationProvider implements vscode.DebugConfigurationProvider { private fileNameToRun = ''; private testToRun = ''; @@ -176,7 +182,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv } /** return a config if cmd is a package-manager */ - private usePM(cmd: string, args: string[]): Partial { + private usePM(cmd: string, args: string[]): PartialDebugConfig | undefined { const commonConfig = { program: undefined, }; @@ -191,49 +197,58 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv } /** - * generate a debug config incorporating commandLine and rootPath. Throw exception if error. - * @param cmdLine - * @param rootPath - * @returns a debug config. + * Creates a debug configuration for a given workspace. + * + * @param {vscode.WorkspaceFolder} workspace - The workspace folder for which the debug configuration is created. + * @param {DebugConfigOptions} [options] - Optional parameters to override the default debug configuration. + * @returns {vscode.DebugConfiguration} The final debug configuration. + * + * @throws {Error} If the provided jestCommandLine is invalid. + * + * This function customizes the default debug configuration with the settings from the options parameter, + * such as `rootPath`, `jestCommandLine`, and `nodeEnv`. + * Please note, the platform-specific settings that were not converted are removed. */ - withCommandLine( + createDebugConfig( workspace: vscode.WorkspaceFolder, - cmdLine: string, - rootPath?: string + options?: DebugConfigOptions ): vscode.DebugConfiguration { const config = this.provideDebugConfigurations(workspace)[0]; - const [cmd, ...cmdArgs] = parseCmdLine(cmdLine); - if (!cmd) { - throw new Error(`invalid cmdLine: ${cmdLine}`); - } - - const absoluteRootPath = rootPath && toAbsoluteRootPath(workspace, rootPath); - - let finalConfig: vscode.DebugConfiguration = { ...config }; + let args: string[] = []; + let override: PartialDebugConfig = {}; + const absoluteRootPath = options?.rootPath && toAbsoluteRootPath(workspace, options.rootPath); const cwd = absoluteRootPath ?? config.cwd; - const pmConfig = this.usePM(cmd, cmdArgs); - if (pmConfig) { - const args = [...cmdArgs, ...pmConfig.args, ...config.args]; - finalConfig = { - ...finalConfig, - ...pmConfig, - cwd, - args, - }; - } else { - // convert the cmd to absolute path - let program = path.isAbsolute(cmd) - ? cmd - : absoluteRootPath - ? path.resolve(absoluteRootPath, cmd) - : ['${workspaceFolder}', cmd].join(path.sep); - program = this.adjustProgram(program); - const args = [...cmdArgs, ...config.args]; - finalConfig = { ...finalConfig, cwd, program, args }; + // handle jestCommandLine related overrides + if (options?.jestCommandLine) { + const [cmd, ...cmdArgs] = parseCmdLine(options.jestCommandLine); + if (!cmd) { + throw new Error(`invalid cmdLine: ${options.jestCommandLine}`); + } + const pmConfig = this.usePM(cmd, cmdArgs); + if (pmConfig) { + args = [...cmdArgs, ...pmConfig.args, ...config.args]; + override = { ...pmConfig, args }; + } else { + let program = path.isAbsolute(cmd) + ? cmd + : absoluteRootPath + ? path.resolve(absoluteRootPath, cmd) + : ['${workspaceFolder}', cmd].join(path.sep); + program = this.adjustProgram(program); + args = [...cmdArgs, ...config.args]; + override = { program, args }; + } } + //handle nodeEnv + if (options?.nodeEnv) { + override = { env: options.nodeEnv, ...override }; + } + + const finalConfig: vscode.DebugConfiguration = { ...config, cwd, ...override }; + // delete platform specific settings since we did not convert them DEBUG_CONFIG_PLATFORMS.forEach((p) => delete finalConfig[p]); diff --git a/src/JestExt/core.ts b/src/JestExt/core.ts index fdb24fa34..0e04601d6 100644 --- a/src/JestExt/core.ts +++ b/src/JestExt/core.ts @@ -605,17 +605,11 @@ export class JestExt { 'debug', 'No debug config named "vscode-jest-tests.v2" or "vscode-jest-tests" found in launch.json, will use a default config.' ); - if (this.extContext.settings.jestCommandLine) { - debugConfig = this.debugConfigurationProvider.withCommandLine( - this.extContext.workspace, - this.extContext.settings.jestCommandLine, - this.extContext.settings.rootPath - ); - } else { - debugConfig = this.debugConfigurationProvider.provideDebugConfigurations( - this.extContext.workspace - )[0]; - } + debugConfig = this.debugConfigurationProvider.createDebugConfig(this.extContext.workspace, { + jestCommandLine: this.extContext.settings.jestCommandLine, + rootPath: this.extContext.settings.rootPath, + nodeEnv: this.extContext.settings.nodeEnv, + }); this.debugConfig = debugConfig; this.extContext.output.write('auto config debug config:', 'info'); diff --git a/src/setup-wizard/tasks/setup-jest-debug.ts b/src/setup-wizard/tasks/setup-jest-debug.ts index ff082741c..2f49357fc 100644 --- a/src/setup-wizard/tasks/setup-jest-debug.ts +++ b/src/setup-wizard/tasks/setup-jest-debug.ts @@ -110,7 +110,11 @@ export const setupJestDebug: SetupTask = async (context: WizardContext): Promise } } - const debugConfig = debugConfigProvider.withCommandLine(workspace, jestCommandLine, rootPath); + const debugConfig = debugConfigProvider.createDebugConfig(workspace, { + jestCommandLine, + rootPath, + nodeEnv: settings.nodeEnv, + }); message('generated a debug config with jestCommandLine and rootPath:', 'info'); message(`${JSON.stringify(debugConfig, undefined, ' ')}`, 'new-line'); diff --git a/src/setup-wizard/types.ts b/src/setup-wizard/types.ts index 5d3f44d73..fae171ac0 100644 --- a/src/setup-wizard/types.ts +++ b/src/setup-wizard/types.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { DebugConfigurationProvider } from '../DebugConfigurationProvider'; +import { DebugConfigurationProvider, DebugConfigOptions } from '../DebugConfigurationProvider'; import { JestExtOutput } from '../JestExt/output-terminal'; import { WorkspaceManager } from '../workspace-manager'; @@ -57,9 +57,7 @@ export interface ActionInputBoxOptions extends AllowBackButton, Verbose { export type SetupTask = (context: WizardContext) => Promise; // settings -export interface WizardSettings { - jestCommandLine?: string; - rootPath?: string; +export interface WizardSettings extends DebugConfigOptions { absoluteRootPath?: string; configurations?: vscode.DebugConfiguration[]; } diff --git a/src/setup-wizard/wizard-helper.ts b/src/setup-wizard/wizard-helper.ts index 64965fd42..cc4475034 100644 --- a/src/setup-wizard/wizard-helper.ts +++ b/src/setup-wizard/wizard-helper.ts @@ -20,7 +20,7 @@ import { isActionableButton, WizardContext, } from './types'; -import { VirtualFolderSettings, createJestSettingGetter } from '../Settings'; +import { NodeEnv, VirtualFolderSettings, createJestSettingGetter } from '../Settings'; import { existsSync } from 'fs'; import { parseCmdLine, removeSurroundingQuote, toAbsoluteRootPath } from '../helpers'; import { VirtualWorkspaceFolder, isVirtualWorkspaceFolder } from '../virtual-workspace-folder'; @@ -269,6 +269,7 @@ export const getWizardSettings = (workspaceFolder: vscode.WorkspaceFolder): Wiza const wsSettings: WizardSettings = { jestCommandLine: getSetting('jestCommandLine')?.trim() || undefined, rootPath: getSetting('rootPath')?.trim() || undefined, + nodeEnv: getSetting('nodeEnv') || undefined, }; // populate jest settings diff --git a/tests/DebugConfigurationProvider.test.ts b/tests/DebugConfigurationProvider.test.ts index 7984fa18d..f28082a2d 100755 --- a/tests/DebugConfigurationProvider.test.ts +++ b/tests/DebugConfigurationProvider.test.ts @@ -1,5 +1,8 @@ +jest.unmock('./mock-platform'); +jest.unmock('./test-helper'); jest.unmock('../src/DebugConfigurationProvider'); +import { restorePlatform, setPlatform } from './mock-platform'; import { DebugConfigurationProvider } from '../src/DebugConfigurationProvider'; import { getTestCommand, @@ -9,7 +12,6 @@ import { parseCmdLine, toAbsoluteRootPath, } from '../src/helpers'; -import * as os from 'os'; import * as fs from 'fs'; import { makeWorkspaceFolder } from './test-helper'; @@ -149,9 +151,14 @@ describe('DebugConfigurationProvider', () => { ]); }); }); - describe('can generate debug config with jestCommandLine and rootPath', () => { - const canRunTest = (isWin32: boolean) => - (isWin32 && os.platform() === 'win32') || (!isWin32 && os.platform() !== 'win32'); + describe('createDebugConfig', () => { + const switchToWin32 = (useWin32: boolean) => { + if (useWin32) { + setPlatform('win32'); + } else { + setPlatform('linux'); + } + }; const config1 = { type: 'node', @@ -189,6 +196,9 @@ describe('DebugConfigurationProvider', () => { }, }; const workspace = makeWorkspaceFolder('project-root'); + afterEach(() => { + restorePlatform(); + }); beforeEach(() => { (parseCmdLine as jest.Mocked).mockImplementation( jest.requireActual('../src/helpers').parseCmdLine @@ -205,7 +215,7 @@ describe('DebugConfigurationProvider', () => { `('with config $name', ({ config }) => { describe('when merge should succeed', () => { describe.each` - case | isWin32 | cmdLine | expected + case | useWin32 | jestCommandLine | expected ${1} | ${false} | ${'jest'} | ${{ cmd: 'jest', args: [], program: '${workspaceFolder}/jest' }} ${2} | ${false} | ${'./node_modules/.bin/jest'} | ${{ cmd: 'node_modules/.bin/jest', args: [], program: '${workspaceFolder}/node_modules/.bin/jest' }} ${3} | ${false} | ${'./node_modules/.bin/..//jest'} | ${{ cmd: 'node_modules/jest', args: [], program: '${workspaceFolder}/node_modules/jest' }} @@ -220,17 +230,16 @@ describe('DebugConfigurationProvider', () => { ${12} | ${false} | ${'"/dir with space/jest" --arg1=1 --arg2 2 "some string"'} | ${{ cmd: '/dir with space/jest', args: ['--arg1=1', '--arg2', '2', '"some string"'], program: '/dir with space/jest' }} ${13} | ${false} | ${"'/dir with space/jest' --arg1=1 --arg2 2 'some string'"} | ${{ cmd: '/dir with space/jest', args: ['--arg1=1', '--arg2', '2', "'some string'"], program: '/dir with space/jest' }} ${14} | ${false} | ${'jest --arg1 "escaped \\"this\\" string" --arg2 2'} | ${{ cmd: 'jest', args: ['--arg1', '"escaped \\"this\\" string"', '--arg2', '2'], program: '${workspaceFolder}/jest' }} - ${15} | ${true} | ${'.\\node_modules\\.bin\\jest.cmd'} | ${{ cmd: 'node_modules\\jest\\bin\\jest.js', args: [], program: '${workspaceFolder}\\node_modules\\jest\\bin\\jest.js' }} - ${16} | ${true} | ${'..\\jest --config="..\\jest-config.json"'} | ${{ cmd: '..\\jest', args: ['--config=', '"..\\jest-config.json"'], program: '${workspaceFolder}\\..\\jest' }} - ${17} | ${true} | ${'jest --config "..\\dir with space\\jest-config.json"'} | ${{ cmd: 'jest', args: ['--config', '"..\\dir with space\\jest-config.json"'], program: '${workspaceFolder}\\jest' }} - ${18} | ${true} | ${'\\absolute\\jest --runInBand'} | ${{ cmd: '\\absolute\\jest', args: ['--runInBand'], program: '\\absolute\\jest' }} - ${19} | ${true} | ${'"\\dir with space\\jest" --arg1=1 --arg2 2 "some string"'} | ${{ cmd: '\\dir with space\\jest', args: ['--arg1=1', '--arg2', '2', '"some string"'], program: '\\dir with space\\jest' }} - ${20} | ${true} | ${'c:\\jest --arg1 "escaped \\"this\\" string" --arg2 2'} | ${{ cmd: 'c:\\jest', args: ['--arg1', '"escaped \\"this\\" string"', '--arg2', '2'], program: 'c:\\jest' }} - `('case $case', ({ cmdLine, expected, isWin32 }) => { - it('can incorporate jestCommandLine (for win32 only? $isWin32)', () => { - if (!canRunTest(isWin32)) { - return; - } + ${15} | ${false} | ${undefined} | ${{ cmd: 'jest', args: [], program: '${workspaceFolder}/node_modules/.bin/jest' }} + ${16} | ${true} | ${'.\\node_modules\\.bin\\jest.cmd'} | ${{ cmd: 'node_modules\\jest\\bin\\jest.js', args: [], program: '${workspaceFolder}\\node_modules\\jest\\bin\\jest.js' }} + ${17} | ${true} | ${'..\\jest --config="..\\jest-config.json"'} | ${{ cmd: '..\\jest', args: ['--config=', '"..\\jest-config.json"'], program: '${workspaceFolder}\\..\\jest' }} + ${18} | ${true} | ${'jest --config "..\\dir with space\\jest-config.json"'} | ${{ cmd: 'jest', args: ['--config', '"..\\dir with space\\jest-config.json"'], program: '${workspaceFolder}\\jest' }} + ${19} | ${true} | ${'\\absolute\\jest --runInBand'} | ${{ cmd: '\\absolute\\jest', args: ['--runInBand'], program: '\\absolute\\jest' }} + ${20} | ${true} | ${'"\\dir with space\\jest" --arg1=1 --arg2 2 "some string"'} | ${{ cmd: '\\dir with space\\jest', args: ['--arg1=1', '--arg2', '2', '"some string"'], program: '\\dir with space\\jest' }} + ${21} | ${true} | ${'c:\\jest --arg1 "escaped \\"this\\" string" --arg2 2'} | ${{ cmd: 'c:\\jest', args: ['--arg1', '"escaped \\"this\\" string"', '--arg2', '2'], program: 'c:\\jest' }} + `('case $case', ({ jestCommandLine, expected, useWin32 }) => { + it('can incorporate jestCommandLine (for win32 only? $useWin32)', () => { + switchToWin32(useWin32); (fs.existsSync as jest.Mocked) = jest.fn().mockReturnValue(true); // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -243,43 +252,86 @@ describe('DebugConfigurationProvider', () => { args: newArgs, program: newProgram, ...restNewConfig - } = sut.withCommandLine(workspace, cmdLine); + } = sut.createDebugConfig(workspace, { jestCommandLine }); expect(newArgs).toContain('--runInBand'); expect(newArgs).toEqual([...expected.args, ...args]); expect(newProgram).toEqual(expected.program); expect(restNewConfig).toEqual(restConfig); }); }); + describe('can incorporate nodeEnv', () => { + it.each` + case | nodeEnv + ${1} | ${undefined} + ${2} | ${{ NODE_OPTIONS: '--experimental-vm-modules' }} + ${3} | ${{ a: 'whatever', b: 3 }} + `('case $case', ({ nodeEnv }) => { + const sut = new DebugConfigurationProvider(); + const spy = jest.spyOn(sut, 'provideDebugConfigurations'); + spy.mockImplementation(() => [config]); + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { args, program, windows, ...restConfig } = config; + const { + args: newArgs, + program: newProgram, + env, + ...restNewConfig + } = sut.createDebugConfig(workspace, { nodeEnv }); + expect(newArgs).toContain('--runInBand'); + expect(newArgs).toEqual([...args]); + expect(newProgram).toEqual(program); + expect(restNewConfig).toEqual(restConfig); + if (nodeEnv) { + expect(env).toEqual(nodeEnv); + } else { + expect(env).toBeUndefined(); + } + }); + }); }); it.each` - cmdLine - ${''} - `('withCommandLine should throw error for invalid cmdLine: $cmdLine', ({ cmdLine }) => { + jestCommandLine + ${' '} + `( + 'createDebugConfig should throw error for invalid cmdLine: $cmdLine', + ({ jestCommandLine }) => { + const sut = new DebugConfigurationProvider(); + expect(() => sut.createDebugConfig(workspace, { jestCommandLine })).toThrow( + 'invalid cmdLine' + ); + } + ); + it('if not passing any options, we will get the default config', () => { const sut = new DebugConfigurationProvider(); - expect(() => sut.withCommandLine(workspace, cmdLine)).toThrow('invalid cmdLine'); + const spy = jest.spyOn(sut, 'provideDebugConfigurations'); + spy.mockImplementation(() => [config]); + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { windows, ...noWindowsConfig } = config; + const newConfig = sut.createDebugConfig(workspace); + expect(noWindowsConfig).toEqual(newConfig); }); describe('on win32, should throw error if the raw jest binary can not be found', () => { - let platformSpy; - beforeAll(() => { - platformSpy = jest.spyOn(os, 'platform').mockImplementation(() => 'win32'); - }); - afterAll(() => { - platformSpy.mockRestore(); - }); it.each` exists ${true} ${false} `('file exists = $exists', ({ exists }) => { + setPlatform('win32'); (fs.existsSync as jest.Mocked) = jest.fn().mockReturnValue(exists); const sut = new DebugConfigurationProvider(); if (!exists) { expect(() => - sut.withCommandLine(workspace, 'whatever\\node_modules\\.bin\\jest.cmd') + sut.createDebugConfig(workspace, { + jestCommandLine: 'whatever\\node_modules\\.bin\\jest.cmd', + }) ).toThrow(); } else { expect(() => - sut.withCommandLine(workspace, 'whatever\\node_modules\\.bin\\jest.cmd') + sut.createDebugConfig(workspace, { + jestCommandLine: 'whatever\\node_modules\\.bin\\jest.cmd', + }) ).not.toThrow(); } }); @@ -303,7 +355,7 @@ describe('DebugConfigurationProvider', () => { program: newProgram, runtimeExecutable, ...restNewConfig - } = sut.withCommandLine(workspace, cmdLine); + } = sut.createDebugConfig(workspace, { jestCommandLine: cmdLine }); expect(newArgs).toContain('--runInBand'); expect(runtimeExecutable).toEqual(cmd); expect(newProgram).toBeUndefined(); @@ -320,43 +372,47 @@ describe('DebugConfigurationProvider', () => { it('platform specific sections are removed.', () => { const sut = new DebugConfigurationProvider(); - const newConfig = sut.withCommandLine(workspace, 'whatever'); + const newConfig = sut.createDebugConfig(workspace, { jestCommandLine: 'whatever' }); expect(newConfig.windows).toBeUndefined(); }); describe.each` - isWin32 | absoluteRootPath | cmdLine | expected - ${false} | ${undefined} | ${'jest'} | ${{ program: '${workspaceFolder}/jest', cwd: '${workspaceFolder}' }} - ${false} | ${'/absolute/root/path'} | ${'jest'} | ${{ program: '/absolute/root/path/jest' }} - ${false} | ${'/absolute/root/path'} | ${'./jest'} | ${{ program: '/absolute/root/path/jest' }} - ${false} | ${'/absolute/root/path'} | ${'../jest'} | ${{ program: '/absolute/root/jest' }} - ${false} | ${'/absolute/root/path'} | ${'yarn test'} | ${{ runtimeExecutable: 'yarn' }} - ${true} | ${undefined} | ${'jest'} | ${{ program: '${workspaceFolder}\\jest', cwd: '${workspaceFolder}' }} - ${true} | ${'c:\\absolute\\root\\path'} | ${'..\\jest'} | ${{ program: 'c:\\absolute\\root\\jest' }} - ${true} | ${'\\absolute\\root\\path'} | ${'yarn test'} | ${{ runtimeExecutable: 'yarn' }} - `('with rootPath: $absoluteRootPath', ({ isWin32, absoluteRootPath, cmdLine, expected }) => { + case | useWin32 | absoluteRootPath | jestCommandLine | expected + ${1} | ${false} | ${undefined} | ${'jest'} | ${{ program: '${workspaceFolder}/jest', cwd: '${workspaceFolder}' }} + ${2} | ${false} | ${'/absolute/root/path'} | ${'jest'} | ${{ program: '/absolute/root/path/jest' }} + ${3} | ${false} | ${'/absolute/root/path'} | ${'./jest'} | ${{ program: '/absolute/root/path/jest' }} + ${4} | ${false} | ${'/absolute/root/path'} | ${'../jest'} | ${{ program: '/absolute/root/jest' }} + ${5} | ${false} | ${'/absolute/root/path'} | ${'yarn test'} | ${{ runtimeExecutable: 'yarn' }} + ${6} | ${true} | ${undefined} | ${'jest'} | ${{ program: '${workspaceFolder}\\jest', cwd: '${workspaceFolder}' }} + ${7} | ${true} | ${'c:\\absolute\\root\\path'} | ${'..\\jest'} | ${{ program: 'c:\\absolute\\root\\jest' }} + ${8} | ${true} | ${'\\absolute\\root\\path'} | ${'yarn test'} | ${{ runtimeExecutable: 'yarn' }} + `('case $case', ({ useWin32, absoluteRootPath, jestCommandLine, expected }) => { it('debugConfig.cwd will be based on absolute rootPath', () => { - if (!canRunTest(isWin32)) { - return; - } + switchToWin32(useWin32); + const sut = new DebugConfigurationProvider(); - const { cwd } = sut.withCommandLine(workspace, cmdLine, absoluteRootPath); + const { cwd } = sut.createDebugConfig(workspace, { + jestCommandLine, + rootPath: absoluteRootPath, + }); expect(cwd).toEqual(expected.cwd ?? absoluteRootPath); }); it('program will be adjust by rootPath', () => { - if (!canRunTest(isWin32)) { - return; - } + switchToWin32(useWin32); const sut = new DebugConfigurationProvider(); - const { program } = sut.withCommandLine(workspace, cmdLine, absoluteRootPath); + const { program } = sut.createDebugConfig(workspace, { + jestCommandLine, + rootPath: absoluteRootPath, + }); expect(program).toEqual(expected.program); }); it('runtimeExecutable will NOT be adjusted by rootPath', () => { - if (!canRunTest(isWin32)) { - return; - } + switchToWin32(useWin32); const sut = new DebugConfigurationProvider(); - const { runtimeExecutable } = sut.withCommandLine(workspace, cmdLine, absoluteRootPath); + const { runtimeExecutable } = sut.createDebugConfig(workspace, { + jestCommandLine, + rootPath: absoluteRootPath, + }); expect(runtimeExecutable).toEqual(expected.runtimeExecutable); }); }); diff --git a/tests/JestExt/core.test.ts b/tests/JestExt/core.test.ts index 335c41139..a4bf3c698 100644 --- a/tests/JestExt/core.test.ts +++ b/tests/JestExt/core.test.ts @@ -90,7 +90,7 @@ describe('JestExt', () => { const debugConfigurationProvider = { provideDebugConfigurations: jest.fn(), prepareTestRun: jest.fn(), - withCommandLine: jest.fn(), + createDebugConfig: jest.fn(), getDebugConfigNames: jest.fn(), } as any; @@ -168,11 +168,14 @@ describe('JestExt', () => { mockListTestFiles(); }); + const debugConfiguration = { type: 'default-config' }; + const debugConfiguration2 = { type: 'with-setting-override' }; + describe('debugTests()', () => { const fileName = 'fileName'; const document: any = { fileName }; let sut: JestExt; - let startDebugging, debugConfiguration, debugConfiguration2; + let startDebugging; const mockShowQuickPick = jest.fn(); let mockConfigurations = []; beforeEach(() => { @@ -185,10 +188,8 @@ describe('JestExt', () => { } } ); - debugConfiguration = { type: 'default-config' }; - debugConfiguration2 = { type: 'with-command-line' }; debugConfigurationProvider.provideDebugConfigurations.mockReturnValue([debugConfiguration]); - debugConfigurationProvider.withCommandLine.mockReturnValue(debugConfiguration2); + debugConfigurationProvider.createDebugConfig.mockReturnValue(debugConfiguration2); debugConfigurationProvider.getDebugConfigNames.mockImplementation((ws) => { const v1 = [`vscode-jest-tests.${ws.name}`, 'vscode-jest-tests']; const v2 = [`vscode-jest-tests.v2.${ws.name}`, 'vscode-jest-tests.v2']; @@ -252,7 +253,7 @@ describe('JestExt', () => { const notJestConfig = { name: 'not-for-jest' }; it.each` case | folderConfigs | workspaceConfigs | expectedConfig - ${1} | ${undefined} | ${undefined} | ${defaultConfig} + ${1} | ${undefined} | ${undefined} | ${debugConfiguration2} ${2} | ${[notJestConfig]} | ${[v1Config, v2Config]} | ${v2Config} ${3} | ${[v1Config]} | ${[v2Config]} | ${v1Config} ${4} | ${undefined} | ${[v2Config]} | ${v2Config} @@ -265,7 +266,7 @@ describe('JestExt', () => { if (section !== 'launch') { return; } - if (scope === workspaceFolder.ui) { + if (scope === workspaceFolder) { return folderConfigs; } if (!scope) { @@ -285,15 +286,24 @@ describe('JestExt', () => { }); describe('generate debug config if nothing found in launch.json', () => { it.each` - jestCommandLine | debugConfig - ${'yarn test'} | ${() => debugConfiguration2} - ${undefined} | ${() => debugConfiguration} - `('with jestCommandLine: $jestCommandLine', ({ jestCommandLine, debugConfig }) => { - sut = newJestExt({ settings: { jestCommandLine } }); + case | settings | createDebugConfigOptions + ${1} | ${{ jestCommandLine: 'yarn test' }} | ${undefined} + ${2} | ${{}} | ${{ jestCommandLine: 'jest' }} + ${3} | ${{ rootPath: 'packages/abc' }} | ${{ jestCommandLine: 'jest', rootPath: 'packages/abc' }} + ${3} | ${{ jestCommandLine: 'npx jest', nodeEnv: { key: 'value' }, rootPath: 'packages/abc' }} | ${undefined} + `('with settings case $case', ({ settings, createDebugConfigOptions }) => { + sut = newJestExt({ settings }); const mockConfig: any = { get: jest.fn() }; vscode.workspace.getConfiguration = jest.fn(() => mockConfig); sut.debugTests(document, 'whatever'); - expect(vscode.debug.startDebugging).toHaveBeenCalledWith(workspaceFolder, debugConfig()); + expect(sut.debugConfigurationProvider.createDebugConfig).toHaveBeenCalledWith( + workspaceFolder, + createDebugConfigOptions ?? settings + ); + expect(vscode.debug.startDebugging).toHaveBeenCalledWith( + workspaceFolder, + debugConfiguration2 + ); }); }); }); diff --git a/tests/mock-platform.ts b/tests/mock-platform.ts new file mode 100644 index 000000000..6b9202f1c --- /dev/null +++ b/tests/mock-platform.ts @@ -0,0 +1,93 @@ +/** + * help tests to mock the platform related node modules, such as path, os.platform(). + * + * Important: you need to import this module before the module you want to test. + */ + +import * as os from 'os'; + +// Determine the current platform +export const actualPlatform = os.platform(); + +let mockPlatform = actualPlatform; +const platformSpy = jest.spyOn(os, 'platform'); + +jest.mock('path', () => { + const actualPath = jest.requireActual('path'); + + // Return a mock object that dynamically adjusts based on `mockPlatform` + // Create a new object to hold the mock implementation + const pathMock = { + get sep() { + return mockPlatform === 'win32' ? '\\' : '/'; + }, + }; + + // Dynamically add all other methods from the correct platform version + for (const key of Object.keys(actualPath.posix)) { + if (typeof actualPath.posix[key] === 'function') { + pathMock[key] = (...args: any[]) => { + const platformPath = mockPlatform === 'win32' ? actualPath.win32 : actualPath.posix; + return platformPath[key](...args); + }; + } + } + + return pathMock; +}); + +// Utility function to switch the platform in tests +export const setPlatform = (platform: NodeJS.Platform) => { + platformSpy.mockReturnValue(platform); + mockPlatform = platform; +}; + +/* restore the the native platform's path module */ +export const restorePlatform = () => { + setPlatform(actualPlatform); +}; + +//=== original === + +// let mockPlatform = actualPlatform; +// const getMockPlatform = jest.fn().mockReturnValue(actualPlatform); +// const mockSep = jest.fn().mockReturnValue(actualPlatform === 'win32' ? '\\' : '/'); + +// jest.mock('path', () => { +// const actualPath = jest.requireActual('path'); + +// // Return a mock object that dynamically adjusts based on `mockPlatform` +// // Create a new object to hold the mock implementation +// const pathMock = { +// get sep() { +// return mockSep(); +// }, +// }; + +// // Dynamically add all other methods from the correct platform version +// for (const key of Object.keys(actualPath.posix)) { +// if (typeof actualPath.posix[key] === 'function') { +// pathMock[key] = (...args: any[]) => { +// const platformPath = getMockPlatform() === 'win32' ? actualPath.win32 : actualPath.posix; +// return platformPath[key](...args); +// }; +// } +// } + +// return pathMock; +// }); + +// // Utility function to switch the platform in tests +// export const setPlatform = (platform: NodeJS.Platform) => { +// jest.spyOn(os, 'platform').mockReturnValue(platform); + +// getMockPlatform.mockReturnValue(platform); +// mockSep.mockReturnValue(platform === 'win32' ? '\\' : '/'); +// }; + +// /* restore the the native platform's path module */ +// export const restorePlatformPath = () => { +// mockSep.mockReset(); +// setPlatform(actualPlatform); +// mockSep.mockReturnValue(actualPlatform === 'win32' ? '\\' : '/'); +// }; diff --git a/tests/setup-wizard/tasks/setup-jest-debug.test.ts b/tests/setup-wizard/tasks/setup-jest-debug.test.ts index 7d5636231..64eaca746 100644 --- a/tests/setup-wizard/tasks/setup-jest-debug.test.ts +++ b/tests/setup-wizard/tasks/setup-jest-debug.test.ts @@ -23,7 +23,7 @@ describe('wizard-tasks', () => { const mockShowTextDocument = jest.fn(); const mockOpenTextDocument = jest.fn(); const debugConfigProvider = { - withCommandLine: jest.fn(), + createDebugConfig: jest.fn(), getDebugConfigNames: jest.fn(), }; let mockTextDocument; @@ -131,7 +131,7 @@ describe('wizard-tasks', () => { expect(mockSetupJestDebug).toHaveBeenCalledTimes(0); }); }); - describe('generate config with jestCommandLine and rootPath', () => { + describe('generate config with setting overrides', () => { const configName = `${DEBUG_CONFIG_NAME}.v2`; const existingConfig: any = { name: configName }; const otherConfig: any = { name: 'other-config' }; @@ -144,32 +144,48 @@ describe('wizard-tasks', () => { mockGetValidJestCommand.mockResolvedValue({ validSettings: [{ jestCommandLine: 'jest' }], }); - context.debugConfigProvider.withCommandLine.mockReturnValue(newDebugConfig); + context.debugConfigProvider.createDebugConfig.mockReturnValue(newDebugConfig); debugConfigProvider.getDebugConfigNames.mockReturnValue({ sorted: ['whatever'] }); mockShowActionMenu(DebugSetupActionId.generate, undefined); }); - it('invoke debugConfigProvider to generate', async () => { - expect.hasAssertions(); - context.debugConfigProvider.withCommandLine.mockReturnValue(newDebugConfig); + describe('with settings', () => { + it.each` + case | settings | exception + ${1} | ${{}} | ${{ jestCommandLine: 'jest' }} + ${2} | ${{ jestCommandLine: 'yarn test' }} | ${undefined} + ${3} | ${{ rootPath: '/a/b/c' }} | ${{ jestCommandLine: 'jest', rootPath: '/a/b/c' }} + ${4} | ${{ nodeEnv: { NODE_ENV: '--experimental-vm-modules' } }} | ${{ jestCommandLine: 'jest', nodeEnv: { NODE_ENV: '--experimental-vm-modules' } }} + ${5} | ${{ jestCommandLine: 'yarn test', rootPath: '/a/b/c', nodeEnv: { NODE_ENV: '--experimental-vm-modules' } }} | ${undefined} + `('case $case', async ({ settings, exception }) => { + expect.hasAssertions(); + context.debugConfigProvider.createDebugConfig.mockReturnValue(newDebugConfig); - mockShowActionMenu(DebugSetupActionId.generate, undefined); - await expect(setupJestDebug(context)).resolves.toEqual(undefined); + wizardSettings = { ...wizardSettings, ...settings }; - expect(context.debugConfigProvider.withCommandLine).toHaveBeenCalledWith( - expect.anything(), - 'jest', - undefined - ); + if (settings.rootPath) { + mockGetValidJestCommand.mockResolvedValue({ + validSettings: [{ jestCommandLine: 'jest', rootPath: settings.rootPath }], + }); + } - // config should be saved - expect(mockSaveConfig).toHaveBeenCalledWith({ - name: 'launch.configurations', - value: expect.arrayContaining([newDebugConfig]), - }); + mockShowActionMenu(DebugSetupActionId.generate, undefined); + await expect(setupJestDebug(context)).resolves.toEqual(undefined); + + expect(context.debugConfigProvider.createDebugConfig).toHaveBeenCalledWith( + expect.anything(), + exception ?? settings + ); + + // config should be saved + expect(mockSaveConfig).toHaveBeenCalledWith({ + name: 'launch.configurations', + value: expect.arrayContaining([newDebugConfig]), + }); - // config should be displayed - expect(vscode.workspace.openTextDocument).toHaveBeenCalled(); + // config should be displayed + expect(vscode.workspace.openTextDocument).toHaveBeenCalled(); + }); }); describe('can save debugConfig in launch.json', () => { it('will rename existing debugConfig in launch.json', async () => {