diff --git a/setup-wizard.md b/setup-wizard.md
index ab203703d..fc0b5e6a8 100644
--- a/setup-wizard.md
+++ b/setup-wizard.md
@@ -3,7 +3,7 @@
This is an interactive tool to help users set up vscode-jest extension if the default configuration is not sufficient.
-_(As in v4, wizard is released as a beta product. We appreciate you trying out and help improving it so it can be more useful for the community 👍)_
+_(The setup wizard is released as a beta product in v4. Thank you for trying it out! 👍 Please don't hesitate to make suggestion or file issues so we can quickly improve and make it more useful for the community.)_
---
@@ -20,11 +20,11 @@ _(As in v4, wizard is released as a beta product. We appreciate you trying out a
It helps users to set up the essential configurations of the extension via a simple UI. While the extension provides default configurations that work for the common standard environments, such as CRA and plain jest, the more sophisticated projects are most likely needed to customize the extension for their environments. And this is where the setup wizard comes in.
-The wizard asks questions and collects answers to update user's workspace `settings.json` and `launch.json` accordingly ( [How does it work ?](#how-does-it-work)). It works for single and multi-root workspaces. It creates an OUTPUT channel `vscode-jest Setup` with progress, instructions and status, which also will be handy for reporting and diagnosis issues.
+The wizard asks questions and collects answers to update user's workspace `settings.json` and `launch.json` accordingly ( [How does it work ?](#how-does-it-work)). It works for single and multi-root workspaces. It creates an OUTPUT channel `"vscode-jest Setup"` showing progress, and tips to make wizard easier to use and diagnose should there be an issue.
Users can run the wizard any time they want ([How to run it ?](how-to-run-it)) and safely abort if desired.
-The wizard tries its best to create accurate configurations but it will not be able to cover all the use cases out there. However, it always strikes for transparency, and shows where and what the configuration will be updated so users can easily modify it if needed.
+The wizard tries its best to create accurate configurations but it will not be able to cover all the use cases out there. However, it always strikes for transparency, and shows where and what the configuration will be updated so users can easily modify it later manually if needed.
_(Note: the wizard is not to set up [jest](https://jestjs.io) itself. Actually, a working jest environment (such as you can run jest tests in terminal) is a prerequisite of running `vscode-jest` extension.)_
## How to run it
@@ -45,7 +45,14 @@ The extension start jest tests process on behave of the users by issuing the sam
Please be aware that all relative paths in the settings are resolved against the `rootPath`, which by default is the current workspace folder unless you customize it with `"jest.rootPath"`.
-This command line is required to configure the debug config below.
+
+While users can pass any jest CLI options in the `"jest.jestCommandLine"`, it is recommended NOT to pass the following:
+- the watch options (--watch, --watchAll): the extension will append watch flag when needed.
+- the coverage option (--coverage): user can easily toggle on/off coverage via command so no need to add it on the commandLine.
+
+Because the extension appends additional options to this commandLine at run time, please make sure these additional options can be passed through, for example, if your command-line is `"npm test"`, make sure you add `"--"` at the end to pass through the additional options: `"npm test --"`
+
+**Please note, `"jest.jestCommandLine"` setting is required to configure the debug config below.**
### Debug Config
When clicked on the debug codeLens, the extension will look for a debug config named `"vscode-jest-tests"`, and append the additional arguments (such as `--testNamePattern`) when launching the debugger.
@@ -54,9 +61,9 @@ If there is no existing `"jest.jestCommandLine"`, it will suggest to set one up
The wizard will examine the `launch.json` for existing config. If found, users can choose to use it as it is, replace (rename the old one and generate a new one) or manually editing it; if not found, wizard will try to [generate](#note-2) a new one.
-The debug config is saved in `launch.json` in workspace folder and shown at the end of the setup for review/adjustment. If you choose "replace" the existing config, the old configure will be renamed to `vscode-jest-tests-xxxxx` for reference purpose only, which can be safely deleted if you don't need it.
+The debug config is saved in `launch.json` in workspace folder and shown at the end of the setup for review or adjustment. If the user chooses to "replace" the existing config, the old configure will be renamed to `vscode-jest-tests-xxxxx` for reference purpose only, which can be safely deleted if not needed.
-The generated config probably work fine for most projects, but could require further adjustment for some other projects including but not limited to
+The generated config probably work fine for most projects, but could require further adjustment for projects including but not limited to the following:
- projects use different jest config between debug and regular test run
- projects with [platform specific properties](#note-3).
@@ -87,7 +94,7 @@ Check out [here](#note-4) if you are having problem running vscode-jest debug co
- obtain a debug template from `DebugConfigurationProvider`
- merge the jest command and arguments with the template.
- if the command is `npm` or `yarn` it updates `runtimeExecutable` property; otherwise updates `program`
- - the arguments will be added to the `args` property, plus jest debug specific flag such as `--runInBand`.
+ - the arguments will be added to the `args` property, plus jest debug specific flags such as `--runInBand`.
- update `cwd` property with either `jest.rootPath` if defined, otherwise the vscode variable `"${workspaceFolder}"`
@@ -113,19 +120,15 @@ Check out [here](#note-4) if you are having problem running vscode-jest debug co
]
```
See more details and examples in [vscode: platform specific properties](https://code.visualstudio.com/docs/editor/debugging#_platformspecific-properties).
-
- The wizard can preserve the existing platform specific properties while merging the new `jest.jestCommandLine` but does not guarantee they are consistent. Therefore, it is advise to always double check the debug config upon `jest.jestCommandLine` changes.
+
- **vscode-jest debug codeLens failed, now what?**
- If your regular jest run was fine but you can't debug the test with debug codeLens after running wizard. There should be some error message in your terminal that might help you pinpoint the culprit. There are many information online about how to setup vscode debug config for specific environments/frameworks, you might also find the following helpful:
+ If your regular jest run was fine but you can't debug the test with debug codeLens after running wizard. There should be some error message in your terminal that might help you pinpoint the culprit.
+
+ There are many information online about how to setup vscode debug config for specific environments/frameworks, you might find the following helpful:
- [vscode debug config properties](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_launch-configuration-properties)
- [Launch configurations for common scenarios](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_launch-configurations-for-common-scenarios)
- [vscode-recipes for debug jest tests](https://github.com/microsoft/vscode-recipes/tree/master/debugging-jest-tests)
- If you think your use case is quite common, feel free to create a discussion/issue for enhancing the wizard or contribute your example debug config.
-
-
-
-
-
\ No newline at end of file
+ While you can manually correct the debug config and move on, if you think your use case is actually quite common, feel free to create a discussion/issue so we might be able to enhance the wizard or include your example config to help others.
diff --git a/src/JestExt.ts b/src/JestExt.ts
index b8e853ffa..8c3cf1be1 100644
--- a/src/JestExt.ts
+++ b/src/JestExt.ts
@@ -147,6 +147,7 @@ export class JestExt {
vscode.commands.executeCommand(`${extensionName}.setup-extension`, {
workspace: this.workspaceFolder,
taskId,
+ verbose: this.jestWorkspace.debug,
}),
};
}
diff --git a/src/extension.ts b/src/extension.ts
index 746457b75..7e42cb7da 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -4,9 +4,8 @@ import { extensionName } from './appGlobals';
import { statusBar } from './StatusBar';
import { ExtensionManager, getExtensionWindowSettings } from './extensionManager';
import { registerSnapshotCodeLens, registerSnapshotPreview } from './SnapshotCodeLens';
-import { startWizard } from './setup-wizard';
+import { startWizard, StartWizardOptions } from './setup-wizard';
import { DebugTestIdentifier } from './DebugCodeLens';
-import { WizardTaskId } from './setup-wizard/start-wizard';
let extensionManager: ExtensionManager;
@@ -42,12 +41,8 @@ export function activate(context: vscode.ExtensionContext): void {
),
vscode.commands.registerCommand(
`${extensionName}.setup-extension`,
- (options?: { workspace: vscode.WorkspaceFolder; taskId: WizardTaskId }) =>
- startWizard(
- extensionManager.debugConfigurationProvider,
- options?.workspace,
- options?.taskId
- )
+ (options: StartWizardOptions = { verbose: true }) =>
+ startWizard(extensionManager.debugConfigurationProvider, options)
),
vscode.commands.registerCommand(
`${extensionName}.run-test`,
diff --git a/src/setup-wizard/index.ts b/src/setup-wizard/index.ts
index 7cc347a02..ec6cf65e1 100644
--- a/src/setup-wizard/index.ts
+++ b/src/setup-wizard/index.ts
@@ -1 +1 @@
-export { startWizard, WizardTaskId } from './start-wizard';
+export { startWizard, StartWizardOptions, WizardTaskId } from './start-wizard';
diff --git a/src/setup-wizard/start-wizard.ts b/src/setup-wizard/start-wizard.ts
index a98bc8b6b..8d0fbf46f 100644
--- a/src/setup-wizard/start-wizard.ts
+++ b/src/setup-wizard/start-wizard.ts
@@ -9,39 +9,30 @@ import {
import { jsonOut, actionItem, showActionMenu } from './wizard-helper';
import { setupJestCmdLine, setupJestDebug } from './tasks';
-export const createWizardContext = (
- workspace: vscode.WorkspaceFolder,
- debugConfigProvider: vscode.DebugConfigurationProvider,
- message: (msg: string, section?: string) => void
-): WizardContext => {
- return {
- debugConfigProvider,
- workspace,
- message,
- };
-};
-
export const StartWizardActionId = {
cmdLine: 0,
debugConfig: 1,
exit: 2,
};
-export type WizardTaskId = 'cmdLine' | 'debugConfig';
-const TaskActionMap: { [key in WizardTaskId]: number } = {
- ['cmdLine']: StartWizardActionId.cmdLine,
- ['debugConfig']: StartWizardActionId.debugConfig,
-};
-export const WizardTasks: { [key in WizardTaskId]: SetupTask } = {
- ['cmdLine']: setupJestCmdLine,
- ['debugConfig']: setupJestDebug,
+// wizard tasks - right now only 2, could easily add more
+export type WizardTaskId = 'cmdLine' | 'debugConfig';
+export const WizardTasks: { [key in WizardTaskId]: { task: SetupTask; actionId: number } } = {
+ ['cmdLine']: { task: setupJestCmdLine, actionId: StartWizardActionId.cmdLine },
+ ['debugConfig']: { task: setupJestDebug, actionId: StartWizardActionId.debugConfig },
};
+export interface StartWizardOptions {
+ workspace?: vscode.WorkspaceFolder;
+ taskId?: WizardTaskId;
+ verbose?: boolean;
+}
export const startWizard = (
debugConfigProvider: vscode.DebugConfigurationProvider,
- ws?: vscode.WorkspaceFolder,
- taskId?: WizardTaskId
+ options: StartWizardOptions = {}
): Promise => {
+ const { workspace, taskId, verbose } = options;
+
const _output = vscode.window.createOutputChannel('vscode-jest Setup');
const dispose = (): void => {
@@ -50,14 +41,25 @@ export const startWizard = (
const message = (msg: string, section?: string): void => {
if (section) {
- _output.appendLine(`\n===== ${section} =====\n`);
+ const m = `\n===== ${section} =====\n`;
+ _output.appendLine(m);
+ if (verbose) {
+ console.log(` ${m}`);
+ }
+ }
+ if (verbose) {
+ console.log(` ${msg}`);
}
_output.appendLine(msg);
_output.show(true);
};
- const runTask = async (context: WizardContext, taskId: WizardTaskId): Promise =>
- WizardTasks[taskId](context);
+ const runTask = async (context: WizardContext, taskId: WizardTaskId): Promise => {
+ message(`starting ${taskId} task...`);
+ const result = await WizardTasks[taskId].task(context);
+ message(`${taskId} task returns ${result}`);
+ return result;
+ };
const showMainMenu = async (context: WizardContext): Promise => {
const menuItems: ActionableMenuItem[] = [
@@ -79,12 +81,13 @@ export const startWizard = (
];
let result: WizardStatus;
- let selectItemIdx = menuItems.findIndex((item) => item.id === TaskActionMap[taskId]);
+ let selectItemIdx = menuItems.findIndex((item) => item.id === WizardTasks[taskId]?.actionId);
do {
result = await showActionMenu(menuItems, {
title: 'vscode-jest Setup Wizard',
placeholder: 'select a set up action below',
selectItemIdx,
+ verbose,
});
selectItemIdx = undefined;
} while (result !== 'exit' && result !== 'error');
@@ -108,15 +111,16 @@ export const startWizard = (
message(`Welcome to vscode-jest setup wizard!`);
message(`\t(More info about the setup wizard: ${WIZARD_HELP_URL})`);
- const workspace = ws || (await selectWorkspace());
+ const ws = workspace || (await selectWorkspace());
- if (workspace) {
- message(`=> workspace "${workspace.name}" is selected`);
+ if (ws) {
+ message(`=> workspace "${ws.name}" is selected`);
const context: WizardContext = {
debugConfigProvider,
- workspace,
+ workspace: ws,
message,
+ verbose,
};
try {
diff --git a/src/setup-wizard/tasks/setup-jest-cmdline.ts b/src/setup-wizard/tasks/setup-jest-cmdline.ts
index 194e6d209..ad1b28d30 100644
--- a/src/setup-wizard/tasks/setup-jest-cmdline.ts
+++ b/src/setup-wizard/tasks/setup-jest-cmdline.ts
@@ -1,7 +1,7 @@
import * as path from 'path';
import {
- showInputBox,
+ showActionInputBox,
showActionMessage,
getConfirmation,
showActionMenu,
@@ -65,16 +65,19 @@ export const setupJestCmdLine: SetupTask = async (
};
const edit = async (cmdLine?: string): Promise => {
- let editedValue = await showInputBox({
+ let editedValue = await showActionInputBox({
title: 'Enter Jest Command Line',
value: cmdLine,
prompt: 'Note: the command line should match how you run jest tests in terminal ',
enableBackButton: true,
+ verbose: context.verbose,
});
editedValue = editedValue?.trim();
if (!editedValue) {
message(
- `jest command line did not change: jest.jestCommandLine = "${settings.jestCommandLine}"`
+ `jest command line did not change: jest.jestCommandLine = ${
+ settings.jestCommandLine ? `"${settings.jestCommandLine}"` : settings.jestCommandLine
+ }`
);
return 'abort';
}
@@ -149,6 +152,7 @@ export const setupJestCmdLine: SetupTask = async (
title: 'Set up Jest Command Line',
placeholder,
enableBackButton: true,
+ verbose: context.verbose,
});
};
const withoutExistingSettings = async (): Promise => {
@@ -174,7 +178,7 @@ export const setupJestCmdLine: SetupTask = async (
return edit();
};
- message(`Setup jest command line for workspace "${workspace.name}"`);
+ message(`Setup jest command line for workspace "${workspace.name}"`, 'setupJestCmdLine');
return settings.jestCommandLine || settings.pathToJest
? withExistingSettings()
: withoutExistingSettings();
diff --git a/src/setup-wizard/tasks/setup-jest-debug.ts b/src/setup-wizard/tasks/setup-jest-debug.ts
index 09828959c..9f3651b53 100644
--- a/src/setup-wizard/tasks/setup-jest-debug.ts
+++ b/src/setup-wizard/tasks/setup-jest-debug.ts
@@ -151,6 +151,7 @@ export const setupJestDebug: SetupTask = async (context: WizardContext): Promise
title: 'Set up Debug Config',
enableBackButton: true,
placeholder: 'No existing jest debug config found',
+ verbose: context.verbose,
});
};
const withExistingConfig = async (): Promise => {
@@ -183,10 +184,11 @@ export const setupJestDebug: SetupTask = async (context: WizardContext): Promise
title: 'Set up Debug Config',
enableBackButton: true,
placeholder: `Found existing debug config: "${DEBUG_CONFIG_NAME}"`,
+ verbose: context.verbose,
});
};
- message(`Set up jest debug config for workspace "${workspace.name}"`);
+ message(`Set up jest debug config for workspace "${workspace.name}"`, 'setupJestDebug');
const jestDebug = launchConfigs?.find((c) => c.name === DEBUG_CONFIG_NAME);
diff --git a/src/setup-wizard/types.ts b/src/setup-wizard/types.ts
index 0e84ac3d0..c3a21ec56 100644
--- a/src/setup-wizard/types.ts
+++ b/src/setup-wizard/types.ts
@@ -5,6 +5,7 @@ export interface WizardContext {
workspace: vscode.WorkspaceFolder;
message: (msg: string, section?: string) => void;
+ verbose?: boolean;
}
export type WizardStatus = 'success' | 'error' | 'abort' | 'exit' | undefined;
@@ -17,10 +18,19 @@ export type ActionableMenuItem = vscode.QuickPickItem & Action
export type ActionableButton = vscode.QuickInputButton & ActionableComp;
export type ActionableMessageItem = vscode.MessageItem & ActionableComp;
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export const isActionableButton = (arg: any): arg is ActionableButton =>
+ arg && arg.iconPath && typeof arg.action === 'function';
+
export type ActionMessageType = 'info' | 'warning' | 'error';
export type AllowBackButton = { enableBackButton?: boolean };
-export interface ActionMenuOptions extends AllowBackButton {
+export type Verbose = { verbose?: boolean };
+
+// actionable menu
+export type ActionMenuInput = ActionableMenuItem | ActionableButton | undefined;
+export type ActionableMenuResult = T | undefined;
+export interface ActionMenuOptions extends AllowBackButton, Verbose {
title?: string;
placeholder?: string;
value?: string;
@@ -28,7 +38,15 @@ export interface ActionMenuOptions extends AllowBackButton {
selectItemIdx?: number;
}
-export type ActionableResult = T | undefined;
+// actionable input box
+export type ActionInputResult = T | string | undefined;
+export type ActionInput = ActionInputResult | ActionableButton | undefined;
+export interface ActionInputBoxOptions extends AllowBackButton, Verbose {
+ title?: string;
+ prompt?: string;
+ value?: string;
+ rightButtons?: ActionableButton[];
+}
export type SetupTask = (context: WizardContext) => Promise;
diff --git a/src/setup-wizard/wizard-helper.ts b/src/setup-wizard/wizard-helper.ts
index c83f1f693..cfe356549 100644
--- a/src/setup-wizard/wizard-helper.ts
+++ b/src/setup-wizard/wizard-helper.ts
@@ -8,15 +8,19 @@ import {
WizardAction,
ActionableMenuItem,
ActionMenuOptions,
- ActionableResult,
ActionableButton,
- AllowBackButton,
WizardSettings,
JestSettings,
ConfigEntry,
ActionableMessageItem,
WizardContext,
ActionMessageType,
+ ActionInputBoxOptions,
+ ActionInputResult,
+ ActionableMenuResult,
+ ActionMenuInput,
+ ActionInput,
+ isActionableButton,
} from './types';
export const jsonOut = (json: unknown): string => JSON.stringify(json, undefined, 4);
@@ -39,10 +43,10 @@ export const actionItem = (
* @param options
* @returns the selected item or undefined if no selection (esc or backButton click)
*/
-export const showActionMenu = (
+export const showActionMenu = async (
items: ActionableMenuItem[],
options: ActionMenuOptions = {}
-): Promise> => {
+): Promise> => {
const quickPick = vscode.window.createQuickPick>();
quickPick.items = items;
quickPick.title = options.title;
@@ -58,51 +62,38 @@ export const showActionMenu = (
quickPick.buttons = [vscode.QuickInputButtons.Back, ...(quickPick.buttons || [])];
}
- const show = (): Promise> =>
- new Promise>((resolve, reject) => {
- const exec = async (f: () => Promise): Promise => {
- try {
- await f();
- } catch (e) {
- reject(e);
- } finally {
- quickPick.dispose();
- }
- };
- quickPick.onDidChangeSelection(async (selectedItems) => {
- exec(async () => {
- if (selectedItems.length !== 1) {
- return resolve(undefined);
- }
- const result = selectedItems[0].action();
- resolve(result);
- });
- });
- quickPick.onDidTriggerButton(async (button: ActionableButton) => {
- exec(async () => {
- if (button === vscode.QuickInputButtons.Back) {
- resolve(undefined);
- }
- resolve(await button.action());
- });
- });
+ const logging = options?.verbose
+ ? (msg): void => console.log(` ${msg}`)
+ : undefined;
+ try {
+ const input = await new Promise>((resolve) => {
+ quickPick.onDidChangeSelection((selectedItems) =>
+ selectedItems.length === 1 ? resolve(selectedItems[0]) : resolve(undefined)
+ );
+ quickPick.onDidTriggerButton((button: ActionableButton) => resolve(button));
quickPick.show();
if (options.selectItemIdx >= 0 && options.selectItemIdx < items.length) {
quickPick.selectedItems = [items[options.selectItemIdx]];
}
});
-
- return show();
+ if (!input) {
+ logging?.('no selection is made');
+ return undefined;
+ }
+ if (input === vscode.QuickInputButtons.Back) {
+ logging?.('back button is clicked');
+ return undefined;
+ }
+ logging?.(`"${isActionableButton(input) ? `button ${input.id}` : input.label}" is selected`);
+ return input.action();
+ } catch (e) {
+ return Promise.reject(e);
+ } finally {
+ quickPick.dispose();
+ }
};
-export interface InputBoxOptions extends AllowBackButton {
- title?: string;
- prompt?: string;
- value?: string;
- rightButtons?: ActionableButton[];
-}
-type InputReturnType = T | string | undefined;
/**
*
* @param title
@@ -110,9 +101,9 @@ type InputReturnType = T | string | undefined;
* @param options
* @returns string if "enter", undefined if "ESC" or backButton click
*/
-export const showInputBox = (
- options?: InputBoxOptions
-): Promise> => {
+export const showActionInputBox = async (
+ options?: ActionInputBoxOptions
+): Promise> => {
const inputBox = vscode.window.createInputBox();
inputBox.title = options.title;
inputBox.value = options.value;
@@ -123,59 +114,42 @@ export const showInputBox = (
inputBox.buttons = [vscode.QuickInputButtons.Back, ...inputBox.buttons];
}
- let done = false;
- const show = (): Promise> =>
- new Promise>((resolve, reject) => {
- const exec = async (f: () => unknown): Promise => {
- try {
- await f();
- } catch (e) {
- reject(e);
- } finally {
- inputBox.dispose();
- }
- };
- inputBox.onDidAccept(() => {
- exec(() => {
- if (done) {
- return;
- }
- const value = inputBox.value;
- done = true;
- resolve(value);
- });
- });
- inputBox.onDidHide(() => {
- exec(() => {
- if (done) {
- return;
- }
- done = true;
- resolve(undefined);
- });
- });
- inputBox.onDidTriggerButton(async (button: ActionableButton) => {
- exec(async () => {
- done = true;
-
- if (button === vscode.QuickInputButtons.Back) {
- resolve(undefined);
- }
- resolve(await button.action());
- });
- });
-
+ const logging = options?.verbose
+ ? (msg): void => console.log(` ${msg}`)
+ : undefined;
+ try {
+ const input = await new Promise>((resolve) => {
+ inputBox.onDidAccept(() => resolve(inputBox.value));
+ inputBox.onDidHide(() => resolve(undefined));
+ inputBox.onDidTriggerButton((button: ActionableButton) => resolve(button));
inputBox.show();
});
-
- return show();
+ if (!input) {
+ logging?.(`no input received`);
+ return undefined;
+ }
+ if (input === vscode.QuickInputButtons.Back) {
+ logging?.(`back button is clicked`);
+ return undefined;
+ }
+ if (isActionableButton(input)) {
+ logging?.(`button ${input.id} is clicked: `);
+ return input.action();
+ }
+ logging?.(`input box received "${input}"`);
+ return input;
+ } catch (e) {
+ return Promise.reject(e);
+ } finally {
+ inputBox.dispose();
+ }
};
export const showActionMessage = async (
type: ActionMessageType,
message: string,
...buttons: ActionableMessageItem[]
-): Promise> => {
+): Promise> => {
let button;
switch (type) {
case 'info':
diff --git a/tests/JestExt.test.ts b/tests/JestExt.test.ts
index a57cf39c0..db1cd8132 100644
--- a/tests/JestExt.test.ts
+++ b/tests/JestExt.test.ts
@@ -182,6 +182,7 @@ describe('JestExt', () => {
let sut: JestExt;
let startDebugging, debugConfiguration, mockConfigurations;
const mockShowQuickPick = jest.fn();
+ let mockProjectWorkspace;
beforeEach(() => {
startDebugging = (debug.startDebugging as unknown) as jest.Mock<{}>;
@@ -195,11 +196,12 @@ describe('JestExt', () => {
vscode.workspace.getConfiguration = jest.fn().mockReturnValue({
get: jest.fn(() => mockConfigurations),
});
+ mockProjectWorkspace = { ...projectWorkspace };
sut = new JestExt(
context,
workspaceFolder,
- projectWorkspace,
+ mockProjectWorkspace,
channelStub,
extensionSettings,
debugCodeLensProvider,
@@ -223,17 +225,20 @@ describe('JestExt', () => {
);
});
it.each`
- configNames | shouldShowWarning
- ${undefined} | ${true}
- ${[]} | ${true}
- ${['a', 'b']} | ${true}
- ${['a', 'vscode-jest-tests', 'b']} | ${false}
+ configNames | shouldShowWarning | debugMode
+ ${undefined} | ${true} | ${true}
+ ${[]} | ${true} | ${true}
+ ${['a', 'b']} | ${true} | ${false}
+ ${['a', 'vscode-jest-tests', 'b']} | ${false} | ${false}
`(
'provides setup wizard in warning message if no "vscode-jest-tests" in launch.json: $configNames',
- async ({ configNames, shouldShowWarning }) => {
+ async ({ configNames, shouldShowWarning, debugMode }) => {
expect.hasAssertions();
const testNamePattern = 'testNamePattern';
mockConfigurations = configNames ? configNames.map((name) => ({ name })) : undefined;
+
+ mockProjectWorkspace.debug = debugMode;
+
await sut.runTest(workspaceFolder, fileName, testNamePattern);
expect(startDebugging).toBeCalledTimes(1);
@@ -265,7 +270,7 @@ describe('JestExt', () => {
button.action();
expect(vscode.commands.executeCommand).toBeCalledWith(
`${extensionName}.setup-extension`,
- { workspace: workspaceFolder, taskId: 'debugConfig' }
+ { workspace: workspaceFolder, taskId: 'debugConfig', verbose: debugMode }
);
} else {
expect(messaging.systemWarningMessage).not.toHaveBeenCalled();
diff --git a/tests/setup-wizard/start-wizard.test.ts b/tests/setup-wizard/start-wizard.test.ts
index b29ae86ef..33c179f30 100644
--- a/tests/setup-wizard/start-wizard.test.ts
+++ b/tests/setup-wizard/start-wizard.test.ts
@@ -16,6 +16,7 @@ describe('startWizard', () => {
beforeEach(() => {
jest.resetAllMocks();
+ console.log = jest.fn();
mockHelperSetup();
vscode.window.createOutputChannel = jest.fn().mockReturnValue({
show: jest.fn(),
@@ -55,7 +56,7 @@ describe('startWizard', () => {
console.error = jest.fn();
(vscode.workspace as any).workspaceFolders = [workspaceFolder('single-root')];
mockShowActionMenu(menuId, StartWizardActionId.exit);
- const task = WizardTasks[taskId];
+ const task = WizardTasks[taskId].task;
task.mockImplementation(() => {
if (typeof taskResult === 'function') {
return taskResult();
@@ -78,8 +79,7 @@ describe('startWizard', () => {
expect.hasAssertions();
console.error = jest.fn();
const workspace = workspaceFolder('w-1');
- (vscode.workspace as any).workspaceFolders = [workspaceFolder('single-root')];
- const task = WizardTasks[taskId];
+ const task = WizardTasks[taskId].task;
task.mockImplementation(() => {
if (typeof taskResult === 'function') {
return taskResult();
@@ -89,11 +89,31 @@ describe('startWizard', () => {
// exit the wizard via menu
mockShowActionMenu(menuId, StartWizardActionId.exit);
- await expect(startWizard(mockDebugConfigProvider, workspace, taskId)).resolves.toEqual(
+ await expect(startWizard(mockDebugConfigProvider, { workspace, taskId })).resolves.toEqual(
wizardResult
);
expect(task).toBeCalledTimes(1);
});
});
+ it('has a verbose mode', async () => {
+ expect.hasAssertions();
+ (vscode.workspace as any).workspaceFolders = [workspaceFolder('single-root')];
+ const mockLog = jest.fn();
+ console.log = mockLog;
+
+ // exit the wizard via menu
+ mockShowActionMenu(StartWizardActionId.exit);
+ await expect(startWizard(mockDebugConfigProvider, { verbose: true })).resolves.toEqual(
+ 'success'
+ );
+ expect(console.log).toHaveBeenCalled();
+
+ mockLog.mockClear();
+ mockShowActionMenu(StartWizardActionId.exit);
+ await expect(startWizard(mockDebugConfigProvider, { verbose: false })).resolves.toEqual(
+ 'success'
+ );
+ expect(console.log).not.toHaveBeenCalled();
+ });
});
diff --git a/tests/setup-wizard/tasks/setup-jest-cmdline.test.ts b/tests/setup-wizard/tasks/setup-jest-cmdline.test.ts
index 0985e76f4..cfc252763 100644
--- a/tests/setup-wizard/tasks/setup-jest-cmdline.test.ts
+++ b/tests/setup-wizard/tasks/setup-jest-cmdline.test.ts
@@ -34,7 +34,7 @@ describe('wizard-tasks', () => {
// default helper function
mockHelper.showActionMenu.mockReturnValue('success');
- mockHelper.showInputBox.mockImplementation(({ value }) => value);
+ mockHelper.showActionInputBox.mockImplementation(({ value }) => value);
mockSaveConfig.mockImplementation(() => Promise.resolve());
mockHelper.showActionMessage.mockImplementation((_, options) => {
@@ -65,7 +65,7 @@ describe('wizard-tasks', () => {
await setupJestCmdLine(context);
expect(mockHelper.getConfirmation).toHaveBeenCalled();
- expect(mockHelper.showInputBox).toHaveBeenCalled();
+ expect(mockHelper.showActionInputBox).toHaveBeenCalled();
});
it('abort wizard if user can not run jest tests in terminal yet', async () => {
expect.hasAssertions();
@@ -77,7 +77,7 @@ describe('wizard-tasks', () => {
await expect(setupJestCmdLine(context)).resolves.toEqual('error');
expect(mockHelper.getConfirmation).toHaveBeenCalled();
expect(mockHelper.showActionMessage).toHaveBeenCalled();
- expect(mockHelper.showInputBox).not.toHaveBeenCalled();
+ expect(mockHelper.showActionInputBox).not.toHaveBeenCalled();
});
describe('when use can run jest in terminal', () => {
beforeEach(() => {
@@ -94,12 +94,12 @@ describe('wizard-tasks', () => {
`('with "$input" => $result', async ({ input, jestCmdLine, result }) => {
expect.hasAssertions();
- mockHelper.showInputBox = jest.fn().mockReturnValue(Promise.resolve(input));
+ mockHelper.showActionInputBox = jest.fn().mockReturnValue(Promise.resolve(input));
await expect(setupJestCmdLine(context)).resolves.toEqual(result);
// prompt user input for commandLine
expect(mockHelper.getConfirmation).toHaveBeenCalled();
- expect(mockHelper.showInputBox).toHaveBeenCalled();
+ expect(mockHelper.showActionInputBox).toHaveBeenCalled();
if (result === 'success') {
validateConfigUpdate((cmdLine) => expect(cmdLine).toEqual(jestCmdLine));
@@ -116,7 +116,7 @@ describe('wizard-tasks', () => {
mockShowActionMessage('info', CLSetupActionId.info);
await expect(setupJestCmdLine(context)).resolves.toEqual('abort');
- expect(mockHelper.showInputBox).toHaveBeenCalled();
+ expect(mockHelper.showActionInputBox).toHaveBeenCalled();
});
});
});
@@ -140,13 +140,13 @@ describe('wizard-tasks', () => {
// user will be able to editing it before update
if (menuId != null) {
- expect(mockHelper.showInputBox).toBeCalled();
+ expect(mockHelper.showActionInputBox).toBeCalled();
// both jestCommandLine and debug config should be updated
validateConfigUpdate((cmdLine) => expect(cmdLine).toEqual(expectedCmdLine));
} else {
// user abort the menu
- expect(mockHelper.showInputBox).not.toBeCalled();
+ expect(mockHelper.showActionInputBox).not.toBeCalled();
validateConfigUpdate();
}
});
@@ -172,13 +172,13 @@ describe('wizard-tasks', () => {
// user will be able to editing it before update
if (updatedCmdLine) {
- expect(mockHelper.showInputBox).toBeCalled();
+ expect(mockHelper.showActionInputBox).toBeCalled();
// both jestCommandLine and debug config should be updated
validateConfigUpdate((cmdLine) => expect(cmdLine).toEqual(expectedCmdLine));
} else {
// user abort the menu
- expect(mockHelper.showInputBox).not.toBeCalled();
+ expect(mockHelper.showActionInputBox).not.toBeCalled();
validateConfigUpdate();
}
});
@@ -186,7 +186,7 @@ describe('wizard-tasks', () => {
describe('when user enter an invalid commandLine, a warning message will be shown for correction', () => {
beforeEach(() => {
mockHelper.getConfirmation = jest.fn().mockReturnValue(Promise.resolve(true));
- mockHelper.showInputBox = jest.fn().mockReturnValue('an invalid command line');
+ mockHelper.showActionInputBox = jest.fn().mockReturnValue('an invalid command line');
mockHelper.validateCommandLine = jest.fn().mockReturnValueOnce('invalid command');
});
describe('when no existing settings', () => {
@@ -197,7 +197,7 @@ describe('wizard-tasks', () => {
// showing warniing
expect(mockHelper.showActionMessage).toBeCalledTimes(1);
// show input box for editing and reediting
- expect(mockHelper.showInputBox).toBeCalledTimes(2);
+ expect(mockHelper.showActionInputBox).toBeCalledTimes(2);
});
it('user can still choose to accept the command as it is', async () => {
expect.hasAssertions();
@@ -206,7 +206,7 @@ describe('wizard-tasks', () => {
// showing warniing
expect(mockHelper.showActionMessage).toBeCalledTimes(1);
// show input box for editing
- expect(mockHelper.showInputBox).toBeCalledTimes(1);
+ expect(mockHelper.showActionInputBox).toBeCalledTimes(1);
});
});
describe('when existing jestCommandLine is invalid: shows a warning panel', () => {
@@ -223,7 +223,7 @@ describe('wizard-tasks', () => {
expect(mockHelper.showActionMessage).toBeCalledTimes(1);
// no input box
- expect(mockHelper.showInputBox).not.toBeCalled();
+ expect(mockHelper.showActionInputBox).not.toBeCalled();
// no menu will be shown
expect(mockHelper.showActionMenu).not.toHaveBeenCalled();
@@ -239,7 +239,7 @@ describe('wizard-tasks', () => {
// showing warniing
expect(mockHelper.showActionMessage).toBeCalledTimes(1);
// show input box for editing
- expect(mockHelper.showInputBox).toBeCalledTimes(1);
+ expect(mockHelper.showActionInputBox).toBeCalledTimes(1);
// no menu will be shown
expect(mockHelper.showActionMenu).not.toHaveBeenCalled();
@@ -251,7 +251,7 @@ describe('wizard-tasks', () => {
// showing warniing twice
expect(mockHelper.showActionMessage).toBeCalledTimes(2);
// show input box for editing twice
- expect(mockHelper.showInputBox).toBeCalledTimes(2);
+ expect(mockHelper.showActionInputBox).toBeCalledTimes(2);
// no menu will be shown
expect(mockHelper.showActionMenu).not.toHaveBeenCalled();
diff --git a/tests/setup-wizard/tasks/setup-jest-debug.test.ts b/tests/setup-wizard/tasks/setup-jest-debug.test.ts
index b4089784c..c502a6369 100644
--- a/tests/setup-wizard/tasks/setup-jest-debug.test.ts
+++ b/tests/setup-wizard/tasks/setup-jest-debug.test.ts
@@ -69,7 +69,7 @@ describe('wizard-tasks', () => {
// default helper function
mockHelper.showActionMenu.mockReturnValue('success');
- mockHelper.showInputBox.mockImplementation(({ value }) => value);
+ mockHelper.showActionInputBox.mockImplementation(({ value }) => value);
mockSaveConfig.mockImplementation(() => Promise.resolve());
mockHelper.mergeDebugConfigWithCmdLine.mockImplementation((config) => config);
diff --git a/tests/setup-wizard/wizard-helper.test.ts b/tests/setup-wizard/wizard-helper.test.ts
index 4d688dc6e..ac9263581 100644
--- a/tests/setup-wizard/wizard-helper.test.ts
+++ b/tests/setup-wizard/wizard-helper.test.ts
@@ -8,7 +8,7 @@ import * as path from 'path';
import {
showActionMenu,
- showInputBox,
+ showActionInputBox,
getConfirmation,
mergeDebugConfigWithCmdLine,
DEBUG_CONFIG_PLATFORMS,
@@ -30,6 +30,7 @@ describe('QuickInput Proxy', () => {
};
const mockButton = (action?: () => Promise): any => ({
+ iconPath: {},
action: action || (() => Promise.resolve('success')),
});
@@ -40,14 +41,9 @@ describe('QuickInput Proxy', () => {
let mockQuickPick: any;
- const mockItem = (label: string, action: boolean | (() => Promise)): any => ({
+ const mockItem = (label: string, action: () => Promise): any => ({
label,
- action: jest.fn().mockImplementation(() => {
- if (typeof action === 'boolean') {
- return action ? Promise.resolve('success') : Promise.reject('mock failure');
- }
- return action();
- }),
+ action,
});
const triggerSelection = async (selected: any) => {
await handleSelection(Array.isArray(selected) ? selected : [selected]);
@@ -77,7 +73,7 @@ describe('QuickInput Proxy', () => {
const items = [];
const p = showActionMenu(items, options);
expect(mockShow).toBeCalledTimes(1);
- await triggerSelection(mockItem('selected-item', true));
+ await triggerSelection(mockItem('selected-item', () => Promise.resolve('success')));
await p;
@@ -126,7 +122,7 @@ describe('QuickInput Proxy', () => {
enableBackButton | expected
${true} | ${undefined}
${false} | ${'success'}
- `('with back button: $enableBackButton', async ({ enableBackButton, expected }) => {
+ `('can have a back button: $enableBackButton', async ({ enableBackButton, expected }) => {
expect.hasAssertions();
const p = showActionMenu([], { enableBackButton });
@@ -144,7 +140,7 @@ describe('QuickInput Proxy', () => {
expect(result).toEqual(expected);
expect(mockDispose).toBeCalledTimes(1);
});
- describe('with action buttons', () => {
+ describe('can have action buttons', () => {
it.each`
action | expected
${() => Promise.resolve('success')} | ${'success'}
@@ -211,8 +207,42 @@ describe('QuickInput Proxy', () => {
}
}
);
+ describe('can be verbose', () => {
+ let options;
+ beforeEach(() => {
+ console.log = jest.fn();
+ options = {
+ verbose: true,
+ enableBackButton: true,
+ };
+ });
+ it('logging which menu item is selected', async () => {
+ expect.hasAssertions();
+
+ const item = mockItem('item-1', () => Promise.resolve('success'));
+ const p = showActionMenu([item], options);
+
+ await triggerSelection(item);
+ await expect(p).resolves.toEqual('success');
+
+ expect(mockShow).toBeCalledTimes(1);
+ expect(console.log).toBeCalledWith(expect.stringMatching('item-1'));
+ });
+ it('when back button is triggered', async () => {
+ expect.hasAssertions();
+
+ const item = mockItem('item-1', () => Promise.resolve('success'));
+ const p = showActionMenu([item], options);
+
+ await triggerButton(vscode.QuickInputButtons.Back);
+ await expect(p).resolves.toEqual(undefined);
+
+ expect(mockDispose).toBeCalledTimes(1);
+ expect(console.log).toBeCalledWith(expect.stringMatching('back button'));
+ });
+ });
});
- describe('showInputBox', () => {
+ describe('showActionInputBox', () => {
const mockShow = jest.fn();
const mockOnDidAccept = jest.fn();
const mockOnDidHide = jest.fn();
@@ -247,7 +277,7 @@ describe('QuickInput Proxy', () => {
expect.hasAssertions();
const inputValue = 'something';
- const p = showInputBox(options);
+ const p = showActionInputBox(options);
expect(mockShow).toBeCalledTimes(1);
await triggerInput(inputValue);
@@ -261,7 +291,7 @@ describe('QuickInput Proxy', () => {
it('"escape" input returns undefined', async () => {
expect.hasAssertions();
- const p = showInputBox(options);
+ const p = showActionInputBox(options);
await triggerInput();
const result = await p;
@@ -276,7 +306,7 @@ describe('QuickInput Proxy', () => {
expect.hasAssertions();
const inputValue = 'something2';
- const p = showInputBox(options);
+ const p = showActionInputBox(options);
expect(mockShow).toBeCalledTimes(1);
await triggerInput(inputValue);
@@ -286,7 +316,7 @@ describe('QuickInput Proxy', () => {
expect(result).toEqual(inputValue);
expect(mockInputBox.title).toEqual(options.title);
expect(mockInputBox.prompt).toEqual(options.prompt);
- expect(mockDispose).toBeCalledTimes(2);
+ expect(mockDispose).toBeCalledTimes(1);
});
it.each`
enableBackButton | expected
@@ -295,7 +325,7 @@ describe('QuickInput Proxy', () => {
`('with back button: $enableBackButton', async ({ enableBackButton, expected }) => {
expect.hasAssertions();
- const p = showInputBox({ ...options, rightButtons: undefined, enableBackButton });
+ const p = showActionInputBox({ ...options, rightButtons: undefined, enableBackButton });
if (enableBackButton) {
expect(mockInputBox.buttons).toEqual([vscode.QuickInputButtons.Back]);
@@ -319,7 +349,7 @@ describe('QuickInput Proxy', () => {
expect.hasAssertions();
const button = mockButton(action);
- const p = showInputBox({ ...options, rightButtons: [button] });
+ const p = showActionInputBox({ ...options, rightButtons: [button] });
await triggerButton(button);
const result = await p;
@@ -335,7 +365,7 @@ describe('QuickInput Proxy', () => {
expect.hasAssertions();
const button = mockButton(action);
- const p = showInputBox({ ...options, rightButtons: [button] });
+ const p = showActionInputBox({ ...options, rightButtons: [button] });
await triggerButton(button);
await expect(p).rejects.toEqual(expected);
@@ -343,6 +373,39 @@ describe('QuickInput Proxy', () => {
expect(mockDispose).toBeCalledTimes(1);
});
});
+ describe('can be verbose', () => {
+ let verboseOptions;
+ beforeEach(() => {
+ console.log = jest.fn();
+ verboseOptions = {
+ prompt: 'a title',
+ value: 'initial vlaue',
+ title: 'an inputBox',
+ verbose: true,
+ enableBackButton: true,
+ };
+ });
+ it('logging which user enters data', async () => {
+ expect.hasAssertions();
+
+ const p = showActionInputBox(verboseOptions);
+ await triggerInput('some value');
+
+ await expect(p).resolves.toEqual('some value');
+
+ expect(mockShow).toBeCalledTimes(1);
+ expect(console.log).toBeCalledWith(expect.stringMatching('some value'));
+ });
+ it('when back button is triggered', async () => {
+ const p = showActionInputBox(verboseOptions);
+ await triggerButton(vscode.QuickInputButtons.Back);
+
+ await expect(p).resolves.toEqual(undefined);
+
+ expect(mockShow).toBeCalledTimes(1);
+ expect(console.log).toBeCalledWith(expect.stringMatching('back button'));
+ });
+ });
});
});
describe('showActionMessage', () => {