Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ Users can use the following settings to tailor the extension for their environme
|[coverageFormatter](#coverageFormatter)|Determine the coverage overlay style|"DefaultFormatter"|`"jest.coverageFormatter": "GutterFormatter"`|
|[coverageColors](#coverageColors)|Coverage indicator color override|undefined|`"jest.coverageColors": { "uncovered": "rgba(255,99,71, 0.2)", "partially-covered": "rgba(255,215,0, 0.2)"}`|
|**Misc**|
|enable|Enable/disable jest extension for the given workspace folder|true|`"jest.enable": false`|
|debugMode|Enable debug mode to diagnose plugin issues. (see developer console)|false|`"jest.debugMode": true`|
|disabledWorkspaceFolders 💼|Disabled workspace folders names in multiroot environment|[]|`"jest.disabledWorkspaceFolders": ["package-a", "package-b"]`|
|[autoRevealOutput](#autoRevealOutput)|Determine when to show test output|"on-run"|`"jest.autoRevealOutput": "on-exec-error"`|
Expand Down Expand Up @@ -323,6 +324,8 @@ for example:

##### autoRun

AutoRun controls when **tests** should be executed automatically.

Performance and automation/completeness are often a trade-off. autoRun is the tool to fine-tune the balance, which is unique for every project and user.

<img src="images/autoRun-tradeoff.jpg" alt="autoRun-tradeoff" width="600"/>
Expand Down Expand Up @@ -398,6 +401,8 @@ There are 2 ways to change autoRun:

</details>

**Please note**, _even when the `autoRun` is "off", the extension will still perform the usual setup upon start-up, such as checking jest env and parsing test blocks, so users can run test blocks manually. To turn off the extension completely for the given workspace, you can use `jest.enable` setting instead._

##### testExplorer
```ts
testExplorer = {showInlineError?: boolean}
Expand Down
1 change: 1 addition & 0 deletions __mocks__/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const window = {
createTextEditorDecorationType: jest.fn(),
createOutputChannel: jest.fn(),
showWorkspaceFolderPick: jest.fn(),
showQuickPick: jest.fn(),
onDidChangeActiveTextEditor: jest.fn(),
showInformationMessage: jest.fn(),
createWebviewPanel: jest.fn(),
Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@
"type": "object",
"title": "Jest",
"properties": {
"jest.enable": {
"markdownDescription": "enable/disable jest extension for the workspace folder. Default is true",
"type": "boolean",
"scope": "resource",
"default": true
},
"jest.jestCommandLine": {
"description": "The command line to start jest tests. It should be the same command line users run jest tests from a terminal/shell, with ability to append extra arguments (by the extension at runtime)",
"type": "string",
Expand Down
86 changes: 36 additions & 50 deletions src/extensionManager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as vscode from 'vscode';
import { JestExt } from './JestExt';
import { DebugConfigurationProvider } from './DebugConfigurationProvider';
import { PluginWindowSettings } from './Settings';
import { statusBar } from './StatusBar';
import { CoverageCodeLensProvider } from './Coverage';
import { extensionId, extensionName } from './appGlobals';
Expand All @@ -11,20 +10,13 @@ import {
startWizard,
StartWizardOptions,
WizardTaskId,
IgnoreWorkspaceChanges,
} from './setup-wizard';
import { ItemCommand } from './test-provider/types';
import { enabledWorkspaceFolders } from './workspace-manager';

export type GetJestExtByURI = (uri: vscode.Uri) => JestExt | undefined;

export function getExtensionWindowSettings(): PluginWindowSettings {
const config = vscode.workspace.getConfiguration('jest');

return {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
disabledWorkspaceFolders: config.get<string[]>('disabledWorkspaceFolders')!,
};
}

export function addFolderToDisabledWorkspaceFolders(folder: string): void {
const config = vscode.workspace.getConfiguration('jest');
const disabledWorkspaceFolders = new Set(config.get<string[]>('disabledWorkspaceFolders') ?? []);
Expand Down Expand Up @@ -73,30 +65,24 @@ export class ExtensionManager {

private extByWorkspace: Map<string, JestExt> = new Map();
private context: vscode.ExtensionContext;
private commonPluginSettings: PluginWindowSettings;

constructor(context: vscode.ExtensionContext) {
this.context = context;

this.commonPluginSettings = getExtensionWindowSettings();

this.debugConfigurationProvider = new DebugConfigurationProvider();
this.coverageCodeLensProvider = new CoverageCodeLensProvider(this.getByDocUri);
this.startWizard = (options?: StartWizardOptions) =>
startWizard(this.debugConfigurationProvider, context, options);
this.applySettings(this.commonPluginSettings);
this.applySettings();
}
private getPendingSetupTask(): WizardTaskId | undefined {
const root = vscode.workspace.workspaceFolders?.[0];
const task = this.context.globalState.get<PendingSetupTask>(PendingSetupTaskKey);
if (
task &&
vscode.workspace.workspaceFolders &&
vscode.workspace.workspaceFolders[0].name === task.workspace
) {
if (task && root?.name === task.workspace) {
return task.taskId;
}
}
applySettings(settings: PluginWindowSettings): void {
applySettings(): void {
const setupTask = this.getPendingSetupTask();
if (setupTask) {
console.warn(
Expand All @@ -105,18 +91,18 @@ export class ExtensionManager {
this.startWizard({ taskId: setupTask });
return;
}
this.commonPluginSettings = settings;
settings.disabledWorkspaceFolders.forEach(this.unregisterWorkspaceByName, this);

//register workspace folder not in the disable list
const enabled = enabledWorkspaceFolders();
vscode.workspace.workspaceFolders?.forEach((ws) => {
if (!this.extByWorkspace.get(ws.name)) {
if (enabled.includes(ws)) {
this.registerWorkspace(ws);
} else {
this.unregisterWorkspace(ws);
}
});
}
registerWorkspace(workspaceFolder: vscode.WorkspaceFolder): void {
if (!this.shouldStart(workspaceFolder.name)) {
const enabled = enabledWorkspaceFolders();
if (!enabled.includes(workspaceFolder) || this.extByWorkspace.has(workspaceFolder.name)) {
return;
}

Expand Down Expand Up @@ -146,18 +132,7 @@ export class ExtensionManager {
this.unregisterWorkspaceByName(key);
}
}
shouldStart(workspaceFolderName: string): boolean {
const {
commonPluginSettings: { disabledWorkspaceFolders },
} = this;
if (this.extByWorkspace.has(workspaceFolderName)) {
return false;
}
if (disabledWorkspaceFolders.includes(workspaceFolderName)) {
return false;
}
return true;
}

public getByName = (workspaceFolderName: string): JestExt | undefined => {
return this.extByWorkspace.get(workspaceFolderName);
};
Expand All @@ -168,12 +143,19 @@ export class ExtensionManager {
}
};

private async showWorkspaceFolderPick(): Promise<vscode.WorkspaceFolder | undefined> {
const folders = enabledWorkspaceFolders();
if (folders.length <= 0) {
return Promise.resolve(undefined);
}
if (folders.length === 1) {
return Promise.resolve(folders[0]);
}
const folderName = await vscode.window.showQuickPick(folders.map((f) => f.name));
return folders.find((f) => f.name === folderName);
}
async selectExtension(): Promise<JestExt | undefined> {
const workspace =
vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length <= 1
? vscode.workspace.workspaceFolders[0]
: await vscode.window.showWorkspaceFolderPick();

const workspace = await this.showWorkspaceFolderPick();
const instance = workspace && this.getByName(workspace.name);
if (instance) {
return instance;
Expand All @@ -193,7 +175,7 @@ export class ExtensionManager {
switch (command.type) {
case 'all-workspaces': {
return vscode.commands.registerCommand(commandName, async (...args) => {
vscode.workspace.workspaceFolders?.forEach((ws) => {
enabledWorkspaceFolders().forEach((ws) => {
const extension = this.getByName(ws.name);
if (extension) {
command.callback.call(thisArg, extension, ...args);
Expand Down Expand Up @@ -240,16 +222,23 @@ export class ExtensionManager {
}

onDidChangeConfiguration(e: vscode.ConfigurationChangeEvent): void {
let applied = false;
vscode.workspace.workspaceFolders?.forEach((workspaceFolder, idx) => {
if (e.affectsConfiguration('jest', workspaceFolder.uri)) {
if (idx === 0) {
this.applySettings(getExtensionWindowSettings());
if (!applied && (idx === 0 || e.affectsConfiguration('jest.enable', workspaceFolder.uri))) {
this.applySettings();
applied = true;
}

this.getByName(workspaceFolder.name)?.triggerUpdateSettings();
}
});
}
onDidChangeWorkspaceFolders(e: vscode.WorkspaceFoldersChangeEvent): void {
if (this.context.workspaceState.get<boolean>(IgnoreWorkspaceChanges)) {
return;
}

e.added.forEach(this.registerWorkspace, this);
e.removed.forEach(this.unregisterWorkspace, this);
}
Expand Down Expand Up @@ -453,10 +442,7 @@ export class ExtensionManager {
activate(): void {
this.showReleaseMessage();
if (vscode.window.activeTextEditor?.document.uri) {
const ext = this.getByDocUri(vscode.window.activeTextEditor.document.uri);
if (ext) {
ext.activate();
}
this.getByDocUri(vscode.window.activeTextEditor.document.uri)?.activate();
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/setup-wizard/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export {
PendingSetupTask,
PendingSetupTaskKey,
} from './start-wizard';

export { IgnoreWorkspaceChanges } from './tasks';
24 changes: 12 additions & 12 deletions src/setup-wizard/tasks/setup-monorepo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { toErrorString } from '../../helpers';
import { PendingSetupTaskKey } from '../start-wizard';
import { setupJestCmdLine } from './setup-jest-cmdline';

export const IgnoreWorkspaceChanges = 'jest.IgnoreWorkspaceChanges';

export const MonorepoSetupActionId = {
setupJestCmdLine: 0,
autoConvert: 1,
Expand Down Expand Up @@ -121,27 +123,20 @@ export const setupMonorepo: SetupTask = async (context: WizardContext): Promise<
return 'exit';
};

const addWorkspaces = async (rootFolder: vscode.WorkspaceFolder): Promise<WizardStatus> => {
const addWorkspaces = async (): Promise<WizardStatus> => {
message(`Adding monorepo packages to multi-root workspace...`);

const workspaceName = (uri: vscode.Uri): string => {
const parts = uri.path.split('/');
return parts[parts.length - 1];
};

try {
const uris = await wsManager.getFoldersFromFilesystem();
// disable all the folders first so extension manager won't trying to register everything during the process
await disableWorkspaceFolders(
[rootFolder.name].concat(uris.map((uri) => workspaceName(uri)))
);

return new Promise<WizardStatus>((resolve, reject) => {
const subscription = vscode.workspace.onDidChangeWorkspaceFolders(() => {
validateWorkspaces()
.then((status) => resolve(status))
.catch((e) => reject(e))
.finally(() => subscription.dispose());
.finally(() => {
subscription.dispose();
});
});

message(`adding ${uris.length} folders:`);
Expand All @@ -150,14 +145,19 @@ export const setupMonorepo: SetupTask = async (context: WizardContext): Promise<
return { uri };
});

context.vscodeContext.workspaceState.update(IgnoreWorkspaceChanges, true);

const success = vscode.workspace.updateWorkspaceFolders(1, null, ...folders);
if (!success) {
reject(new Error(`failed to add workspace folders`));
}
});
} catch (e) {
message(`Failed to add/validate workspace folders:\r\n${toErrorString(e)}`, 'error');
} finally {
context.vscodeContext.workspaceState.update(IgnoreWorkspaceChanges, undefined);
}

return 'abort';
};

Expand Down Expand Up @@ -226,7 +226,7 @@ export const setupMonorepo: SetupTask = async (context: WizardContext): Promise<
return validateWorkspaces();
}
if (vscode.workspace.workspaceFile) {
return addWorkspaces(workspaces[0]);
return addWorkspaces();
}
return handleSingleRoot();
};
Expand Down
19 changes: 18 additions & 1 deletion src/workspace-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,23 @@ interface ValidatePatternType {
binary: string[];
}

export const enabledWorkspaceFolders = (): vscode.WorkspaceFolder[] => {
if (!vscode.workspace.workspaceFolders) {
return [];
}

const windowConfig = vscode.workspace.getConfiguration('jest');
const disabledWorkspaceFolders = windowConfig.get<string[]>('disabledWorkspaceFolders') ?? [];

return vscode.workspace.workspaceFolders.filter((ws) => {
if (disabledWorkspaceFolders.includes(ws.name)) {
return false;
}
const config = vscode.workspace.getConfiguration('jest', ws);
return config.get<boolean>('enable') ?? true;
});
};

export const isSameWorkspace = (
ws1: vscode.WorkspaceFolder,
ws2: vscode.WorkspaceFolder
Expand All @@ -42,7 +59,7 @@ export class WorkspaceManager {
}

const validWorkspaces: Map<string, WorkspaceInfo> = new Map();
for (const ws of vscode.workspace.workspaceFolders) {
for (const ws of enabledWorkspaceFolders()) {
if (validWorkspaces.has(ws.uri.path)) {
continue;
}
Expand Down
Loading