Skip to content

Commit

Permalink
feat: support running extension test CLI from launch.json
Browse files Browse the repository at this point in the history
  • Loading branch information
connor4312 committed Feb 8, 2024
1 parent f2a7724 commit 66231b0
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This changelog records changes to stable releases since 1.50.2. "TBA" changes he
## Nightly (only)

- feat: lazily announce evaluated scripts ([#1939](https://github.com/microsoft/vscode-js-debug/issues/1939))
- feat: support running extension test CLI from launch.json ([vscode#199211](https://github.com/microsoft/vscode/issues/199211))
- fix: support object property shorthand in logpoints ([#1788](https://github.com/microsoft/vscode-js-debug/issues/1788))
- fix: pages not loading in browser after attach browser disconnect ([#1795](https://github.com/microsoft/vscode-js-debug/issues/1795))
- fix: skipFiles not matching/negating with special chars ([vscode#203408](https://github.com/microsoft/vscode/issues/203408))
Expand Down
4 changes: 3 additions & 1 deletion OPTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,9 @@
"meteor://💻app/*": "${workspaceFolder}/*"
}</pre></code><h4>sourceMapRenames</h4><p>Whether to use the &quot;names&quot; mapping in sourcemaps. This requires requesting source content, which can be slow with certain debuggers.</p>
<h5>Default value:</h4><pre><code>true</pre></code><h4>sourceMaps</h4><p>Use JavaScript source maps (if they exist).</p>
<h5>Default value:</h4><pre><code>true</pre></code><h4>timeout</h4><p>Retry for this number of milliseconds to connect to Node.js. Default is 10000 ms.</p>
<h5>Default value:</h4><pre><code>true</pre></code><h4>testConfiguration</h4><p>Path to a test configuration file for the <a href="https://code.visualstudio.com/api/working-with-extensions/testing-extension#quick-setup-the-test-cli">test CLI</a>.</p>
<h5>Default value:</h4><pre><code>undefined</pre></code><h4>testConfigurationLabel</h4><p>A single configuration to run from the file. If not specified, you may be asked to pick.</p>
<h5>Default value:</h4><pre><code>undefined</pre></code><h4>timeout</h4><p>Retry for this number of milliseconds to connect to Node.js. Default is 10000 ms.</p>
<h5>Default value:</h4><pre><code>10000</pre></code><h4>timeouts</h4><p>Timeouts for several debugger operations.</p>
<h5>Default value:</h4><pre><code>{}</pre></code><h4>trace</h4><p>Configures what diagnostic output is produced.</p>
<h5>Default value:</h4><pre><code>false</pre></code></details>
Expand Down
2 changes: 2 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@
"extensionHost.launch.debugWebWorkerHost": "Configures whether we should try to attach to the web worker extension host.",
"extensionHost.launch.env.description": "Environment variables passed to the extension host.",
"extensionHost.launch.rendererDebugOptions": "Chrome launch options used when attaching to the renderer process, with `debugWebviews` or `debugWebWorkerHost`.",
"extensionHost.launch.testConfiguration": "Path to a test configuration file for the [test CLI](https://code.visualstudio.com/api/working-with-extensions/testing-extension#quick-setup-the-test-cli).",
"extensionHost.launch.testConfigurationLabel": "A single configuration to run from the file. If not specified, you may be asked to pick.",
"extensionHost.launch.runtimeExecutable.description": "Absolute path to VS Code.",
"extensionHost.launch.stopOnEntry.description": "Automatically stop the extension host after launch.",
"extensionHost.snippet.launch.description": "Launch a VS Code extension in debug mode",
Expand Down
12 changes: 11 additions & 1 deletion src/build/generate-contributions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -916,7 +916,7 @@ const extensionHostConfig: IDebugger<IExtensionHostLaunchConfiguration> = {
request: 'launch',
label: refString('extensionHost.label'),
languages: commonLanguages,
required: ['args'],
required: [],
configurationSnippets: [
{
label: refString('extensionHost.snippet.launch.label'),
Expand Down Expand Up @@ -964,6 +964,16 @@ const extensionHostConfig: IDebugger<IExtensionHostLaunchConfiguration> = {
},
properties: chromiumAttachConfigurationAttributes as { [key: string]: JSONSchema6 },
},
testConfiguration: {
markdownDescription: refString('extensionHost.launch.testConfiguration'),
type: 'string',
default: '${workspaceaceFolder}/.vscode-test.js',
},
testConfigurationLabel: {
markdownDescription: refString('extensionHost.launch.testConfigurationLabel'),
type: 'string',
default: '',
},
},
defaults: extensionHostConfigDefaults,
};
Expand Down
12 changes: 12 additions & 0 deletions src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,18 @@ export interface IExtensionHostLaunchConfiguration extends IExtensionHostBaseCon
*/
rendererDebugOptions: Partial<IChromeAttachConfiguration>;

/**
* Path to a test configuration file for the test CLI.
* @see https://code.visualstudio.com/api/working-with-extensions/testing-extension#quick-setup-the-test-cli
*/
testConfiguration?: string;

/**
* A single configuration to run from the file. If not specified, the user
* may be asked to pick.
*/
testConfigurationLabel?: string;

/**
* Port the extension host is listening on.
*/
Expand Down
105 changes: 102 additions & 3 deletions src/ui/configuration/extensionHostConfigurationResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/

import * as l10n from '@vscode/l10n';
import execa from 'execa';
import { promises as fs } from 'fs';
import { injectable } from 'inversify';
import { join } from 'path';
import { dirname, join } from 'path';
import * as vscode from 'vscode';
import { TaskCancelledError } from '../../common/cancellation';
import { DebugType } from '../../common/contributionUtils';
import { canAccess } from '../../common/fsUtils';
import { nearestDirectoryWhere } from '../../common/urlUtils';
import {
IExtensionHostLaunchConfiguration,
ResolvingExtensionHostConfiguration,
applyNodeishDefaults,
extensionHostConfigDefaults,
IExtensionHostLaunchConfiguration,
resolveVariableInConfig,
ResolvingExtensionHostConfiguration,
} from '../../configuration';
import { BaseConfigurationResolver } from './baseConfigurationResolver';

Expand Down Expand Up @@ -48,6 +53,34 @@ export class ExtensionHostConfigurationResolver
});
}

/**
* @inheritdoc
*/
public async resolveDebugConfigurationWithSubstitutedVariables(
_folder: vscode.WorkspaceFolder | undefined,
debugConfiguration: vscode.DebugConfiguration,
): Promise<vscode.DebugConfiguration | undefined> {
const config = debugConfiguration as ResolvingExtensionHostConfiguration;
try {
const testCfg = await resolveTestConfiguration(config);
if (testCfg) {
config.env = { ...config.env, ...testCfg.env };
config.args = [
...(config.args || []),
`--extensionDevelopmentPath=${testCfg.extensionDevelopmentPath}`,
`--extensionTestsPath=${testCfg.extensionTestsPath}`,
];
}
} catch (e) {
if (e instanceof TaskCancelledError) {
return undefined;
}
throw e;
}

return config;
}

protected getType() {
return DebugType.ExtensionHost as const;
}
Expand Down Expand Up @@ -79,3 +112,69 @@ const getExtensionKind = async (

return extensionKind instanceof Array ? extensionKind : [extensionKind];
};

const resolveTestConfiguration = async (config: ResolvingExtensionHostConfiguration) => {
const { testConfiguration } = config;
let { testConfigurationLabel } = config;
if (!testConfiguration) {
return;
}

const suffix = join('node_modules', '@vscode', 'test-cli', 'out', 'bin.mjs');
const dirWithModules = await nearestDirectoryWhere(testConfiguration, dir => {
const binary = join(dir, suffix);
return canAccess(fs, binary);
});

if (!dirWithModules) {
throw new Error(
l10n.t('Cannot find `{0}` installed in {1}', '@vscode/test-cli', dirname(testConfiguration)),
);
}

const result = await execa(
process.execPath,
[join(dirWithModules, suffix), '--config', testConfiguration, '--list-configuration'],
{
env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' },
},
);

const configs: {
config: { label?: string };
extensionTestsPath: string;
extensionDevelopmentPath: string;
env: Record<string, string>;
}[] = JSON.parse(result.stdout);

if (configs.length === 1) {
return configs[0];
}

if (configs.length && !testConfigurationLabel) {
testConfigurationLabel = await vscode.window.showQuickPick(
configs.map((c, i) => c.config.label || String(i)),
{
title: l10n.t('Select test configuration to run'),
},
);
if (!testConfigurationLabel) {
throw new TaskCancelledError('cancelled');
}
}

const found = configs.find(
(c, i) => c.config.label === testConfigurationLabel || String(i) === testConfigurationLabel,
);
if (!found) {
throw new Error(
l10n.t(
'Cannot find test configuration with label `{0}`, got: {1}',
String(testConfigurationLabel),
configs.map((c, i) => c.config.label || i).join(', '),
),
);
}

return found;
};

0 comments on commit 66231b0

Please sign in to comment.