Skip to content

Commit f3dfd14

Browse files
authored
Get extension tests running in the browser (#2850)
1 parent 471dca1 commit f3dfd14

15 files changed

+1341
-380
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ dist
336336
./test/
337337
media
338338
.vscode-test
339+
.vscode-test-web
339340
.DS_Store
340341
*.vsix
341342
package-lock.json

.vscode/launch.json

+76-11
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,15 @@
1010
"--extensionDevelopmentPath=${workspaceFolder}",
1111
"--disable-extension=GitHub.vscode-pull-request-github-insiders"
1212
],
13-
"skipFiles": ["<node_internals>/**/*.js", "**/node_modules/**/*.js"],
13+
"skipFiles": [
14+
"<node_internals>/**/*.js",
15+
"**/node_modules/**/*.js"
16+
],
1417
"smartStep": true,
1518
"sourceMaps": true,
16-
"outFiles": ["${workspaceFolder}/dist/*.js"]
19+
"outFiles": [
20+
"${workspaceFolder}/dist/*.js"
21+
]
1722
},
1823
{
1924
"name": "Launch Extension in Webworker",
@@ -25,10 +30,15 @@
2530
"--extensionDevelopmentPath=${workspaceFolder}",
2631
"--disable-extension=GitHub.vscode-pull-request-github-insiders"
2732
],
28-
"skipFiles": ["<node_internals>/**/*.js", "**/node_modules/**/*.js"],
33+
"skipFiles": [
34+
"<node_internals>/**/*.js",
35+
"**/node_modules/**/*.js"
36+
],
2937
"smartStep": true,
3038
"sourceMaps": true,
31-
"outFiles": ["${workspaceFolder}/dist/browser/*.js"]
39+
"outFiles": [
40+
"${workspaceFolder}/dist/browser/*.js"
41+
]
3242
},
3343
{
3444
"name": "Watch & Launch Extension",
@@ -39,11 +49,16 @@
3949
"--extensionDevelopmentPath=${workspaceFolder}",
4050
"--disable-extension=GitHub.vscode-pull-request-github-insiders"
4151
],
42-
"skipFiles": ["<node_internals>/**/*.js", "**/node_modules/**/*.js"],
52+
"skipFiles": [
53+
"<node_internals>/**/*.js",
54+
"**/node_modules/**/*.js"
55+
],
4356
"preLaunchTask": "npm: watch",
4457
"smartStep": true,
4558
"sourceMaps": true,
46-
"outFiles": ["${workspaceFolder}/dist/*.js"]
59+
"outFiles": [
60+
"${workspaceFolder}/dist/*.js"
61+
]
4762
},
4863
{
4964
"name": "Watch & Launch Extension in Webworker",
@@ -55,11 +70,16 @@
5570
"--extensionDevelopmentPath=${workspaceFolder}",
5671
"--disable-extension=GitHub.vscode-pull-request-github-insiders"
5772
],
58-
"skipFiles": ["<node_internals>/**/*.js", "**/node_modules/**/*.js"],
73+
"skipFiles": [
74+
"<node_internals>/**/*.js",
75+
"**/node_modules/**/*.js"
76+
],
5977
"preLaunchTask": "npm: watch",
6078
"smartStep": true,
6179
"sourceMaps": true,
62-
"outFiles": ["${workspaceFolder}/dist/browser/*.js"]
80+
"outFiles": [
81+
"${workspaceFolder}/dist/browser/*.js"
82+
]
6383
},
6484
{
6585
"name": "Extension Tests",
@@ -72,10 +92,55 @@
7292
"--disable-extension=GitHub.vscode-pull-request-github-insiders",
7393
"--disable-extensions"
7494
],
75-
"preLaunchTask": "npm: compile:test",
95+
"preLaunchTask": "npm: test:preprocess",
7696
"smartStep": true,
7797
"sourceMaps": true,
78-
"outFiles": ["${workspaceFolder}/out/src/test/**/*.js"]
98+
"outFiles": [
99+
"${workspaceFolder}/dist/test/*.js"
100+
]
101+
},
102+
{
103+
"type": "pwa-node",
104+
"request": "launch",
105+
"name": "Attach Web Test",
106+
"program": "${workspaceFolder}/node_modules/vscode-test-web/out/index.js",
107+
"args": [
108+
"--extensionTestsPath=dist/browser/test/index.js",
109+
"--extensionDevelopmentPath=.",
110+
"--browserType=chromium",
111+
"--attach=9229"
112+
],
113+
"cascadeTerminateToConfigurations": [
114+
"Launch Web Test"
115+
],
116+
"presentation": {
117+
"hidden": true,
118+
}
119+
},
120+
{
121+
"type": "pwa-chrome",
122+
"request": "launch",
123+
"name": "Launch Web Test",
124+
"skipFiles": [
125+
"<node_internals>/**"
126+
],
127+
"port": 9229,
128+
"resolveSourceMapLocations": [
129+
"!**/vs/**", // exclude core vscode sources
130+
"!**/static/build/extensions/**", // exclude built-in extensions
131+
],
132+
"presentation": {
133+
"hidden": true,
134+
}
135+
},
136+
],
137+
"compounds": [
138+
{
139+
"name": "Debug Web Test",
140+
"configurations": [
141+
"Attach Web Test",
142+
"Launch Web Test"
143+
]
79144
}
80145
]
81-
}
146+
}

package.json

+10-2
Original file line numberDiff line numberDiff line change
@@ -1420,6 +1420,8 @@
14201420
"pretty": "prettier --config .prettierrc --loglevel warn --write .",
14211421
"test": "yarn run test:preprocess && node ./out/src/test/runTests.js",
14221422
"test:preprocess": "yarn run compile && yarn run compile:test && yarn run test:preprocess-gql && yarn run test:preprocess-svg",
1423+
"browsertest:preprocess": "yarn run compile && tsc ./src/test/browser/runTests.ts --outDir ./dist/browser/test --rootDir ./src/test/browser --target es6 --module commonjs",
1424+
"browsertest": "yarn run browsertest:preprocess && node ./dist/browser/test/runTests.js",
14231425
"test:preprocess-gql": "node scripts/preprocess-gql --in src/github/queries.gql --out out/src/github/queries.gql",
14241426
"test:preprocess-svg": "node scripts/preprocess-svg --in ../resources/ --out out/resources",
14251427
"update-dts": "pushd \"src/@types\" && npx vscode-dts main && npx vscode-dts dev && popd",
@@ -1431,16 +1433,20 @@
14311433
"@types/glob": "7.1.3",
14321434
"@types/lru-cache": "^5.1.0",
14331435
"@types/marked": "^0.7.2",
1434-
"@types/mocha": "^5.2.2",
1436+
"@types/mocha": "^8.2.2",
14351437
"@types/node": "12.12.70",
14361438
"@types/react": "^16.8.4",
14371439
"@types/react-dom": "^16.8.2",
14381440
"@types/sinon": "7.0.11",
14391441
"@types/temp": "0.8.34",
1442+
"@types/vscode": "1.58.1",
1443+
"@types/webpack-env": "^1.16.0",
14401444
"@typescript-eslint/eslint-plugin": "4.18.0",
14411445
"@typescript-eslint/parser": "4.18.0",
14421446
"@vscode/test-electron": "^1.6.1",
1447+
"assert": "^2.0.0",
14431448
"buffer": "^6.0.3",
1449+
"constants-browserify": "^1.0.0",
14441450
"crypto-browserify": "3.12.0",
14451451
"css-loader": "5.1.3",
14461452
"esbuild-loader": "2.10.0",
@@ -1458,9 +1464,10 @@
14581464
"merge-options": "3.0.4",
14591465
"minimist": "^1.2.5",
14601466
"mkdirp": "1.0.4",
1461-
"mocha": "^7.0.1",
1467+
"mocha": "^9.0.1",
14621468
"mocha-junit-reporter": "1.23.0",
14631469
"mocha-multi-reporters": "1.1.7",
1470+
"os-browserify": "^0.3.0",
14641471
"path-browserify": "1.0.1",
14651472
"prettier": "2.2.1",
14661473
"process": "^0.11.10",
@@ -1476,6 +1483,7 @@
14761483
"ts-loader": "8.0.18",
14771484
"tty": "1.0.1",
14781485
"typescript": "4.2.3",
1486+
"vscode-test-web": "^0.0.5",
14791487
"webpack": "5.26.3",
14801488
"webpack-cli": "4.2.0"
14811489
},

src/@types/vscode-test-web.d.ts

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env node
2+
export declare type BrowserType = 'chromium' | 'firefox' | 'webkit';
3+
export declare type VSCodeVersion = 'insiders' | 'stable' | 'sources';
4+
export interface Options {
5+
/**
6+
* Browser to run the test against: 'chromium' | 'firefox' | 'webkit'
7+
*/
8+
browserType: BrowserType;
9+
/**
10+
* Absolute path to folder that contains one or more extensions (in subfolders).
11+
* Extension folders include a `package.json` extension manifest.
12+
*/
13+
extensionDevelopmentPath?: string;
14+
/**
15+
* Absolute path to the extension tests runner module.
16+
* Can be either a file path or a directory path that contains an `index.js`.
17+
* The module is expected to have a `run` function of the following signature:
18+
*
19+
* ```ts
20+
* function run(): Promise<void>;
21+
* ```
22+
*
23+
* When running the extension test, the Extension Development Host will call this function
24+
* that runs the test suite. This function should throws an error if any test fails.
25+
*/
26+
extensionTestsPath?: string;
27+
/**
28+
* The VS Code version to use. Valid versions are:
29+
* - `'stable'` : The latest stable build
30+
* - `'insiders'` : The latest insiders build
31+
* - `'sources'`: From sources, served at localhost:8080 by running `yarn web` in the vscode repo
32+
*
33+
* Currently defaults to `insiders`, which is latest stable insiders.
34+
*/
35+
version?: VSCodeVersion;
36+
/**
37+
* Open the dev tools.
38+
*/
39+
devTools?: boolean;
40+
/**
41+
* Do not show the browser. Defaults to `true` if a extensionTestsPath is provided, `false` otherwise.
42+
*/
43+
headless?: boolean;
44+
/**
45+
* Expose browser debugging on this port number, and wait for the debugger to attach before running tests.
46+
*/
47+
waitForDebugger?: number;
48+
/**
49+
* The folder URI to open VSCode on
50+
*/
51+
folderUri?: string;
52+
}
53+
/**
54+
* Runs the tests in a browser.
55+
*
56+
* @param options The options defining browser type, extension and test location.
57+
*/
58+
export declare function runTests(options: Options & {
59+
extensionTestsPath: string;
60+
}): Promise<void>;
61+
export declare function open(options: Options): Promise<void>;

src/env/browser/ssh.ts

+52
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,57 @@
22
* Copyright (c) Microsoft Corporation. All rights reserved.
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
5+
import { parse as parseConfig } from 'ssh-config';
56

67
export const resolve = (_url: string) => undefined;
8+
9+
export function baseResolver(config: Config) {
10+
return {
11+
...config,
12+
Hostname: config.Host,
13+
};
14+
}
15+
16+
/**
17+
* SSH Config interface
18+
*
19+
* Note that this interface atypically capitalizes field names. This is for consistency
20+
* with SSH config files.
21+
*/
22+
export interface Config {
23+
Host: string;
24+
[param: string]: string;
25+
}
26+
27+
/**
28+
* ConfigResolvers take a config, resolve some additional data (perhaps using
29+
* a config file), and return a new Config.
30+
*/
31+
export type ConfigResolver = (config: Config) => Config;
32+
33+
export function chainResolvers(...chain: (ConfigResolver | undefined)[]): ConfigResolver {
34+
const resolvers = chain.filter(x => !!x) as ConfigResolver[];
35+
return (config: Config) =>
36+
resolvers.reduce(
37+
(resolved, next) => ({
38+
...resolved,
39+
...next(resolved),
40+
}),
41+
config,
42+
);
43+
}
44+
45+
export function resolverFromConfig(text: string): ConfigResolver {
46+
const config = parseConfig(text);
47+
return h => config.compute(h.Host);
48+
}
49+
50+
export class Resolvers {
51+
static default = baseResolver;
52+
53+
static fromConfig(conf: string) {
54+
return chainResolvers(baseResolver, resolverFromConfig(conf));
55+
}
56+
57+
static current = Resolvers.default;
58+
}

src/env/node/ssh.ts

+1-42
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,12 @@
11
import { readFileSync } from 'fs';
22
import { homedir } from 'os';
33
import { join } from 'path';
4-
import { parse as parseConfig } from 'ssh-config';
54
import Logger from '../../common/logger';
5+
import { baseResolver, chainResolvers, Config, ConfigResolver, resolverFromConfig } from '../browser/ssh';
66

77
const SSH_URL_RE = /^(?:([^@:]+)@)?([^:/]+):?(.+)$/;
88
const URL_SCHEME_RE = /^([a-z-]+):\/\//;
99

10-
/**
11-
* SSH Config interface
12-
*
13-
* Note that this interface atypically capitalizes field names. This is for consistency
14-
* with SSH config files.
15-
*/
16-
export interface Config {
17-
Host: string;
18-
[param: string]: string;
19-
}
20-
21-
/**
22-
* ConfigResolvers take a config, resolve some additional data (perhaps using
23-
* a config file), and return a new Config.
24-
*/
25-
export type ConfigResolver = (config: Config) => Config;
26-
2710
/**
2811
* Parse and resolve an SSH url. Resolves host aliases using the configuration
2912
* specified by ~/.ssh/config, if present.
@@ -83,13 +66,6 @@ const parse = (url: string): Config | undefined => {
8366
return { User, Host, path };
8467
};
8568

86-
function baseResolver(config: Config) {
87-
return {
88-
...config,
89-
Hostname: config.Host,
90-
};
91-
}
92-
9369
function resolverFromConfigFile(configPath = join(homedir(), '.ssh', 'config')): ConfigResolver | undefined {
9470
try {
9571
const config = readFileSync(configPath).toString();
@@ -98,20 +74,3 @@ function resolverFromConfigFile(configPath = join(homedir(), '.ssh', 'config')):
9874
Logger.appendLine(`${configPath}: ${error.message}`);
9975
}
10076
}
101-
102-
export function resolverFromConfig(text: string): ConfigResolver {
103-
const config = parseConfig(text);
104-
return h => config.compute(h.Host);
105-
}
106-
107-
function chainResolvers(...chain: (ConfigResolver | undefined)[]): ConfigResolver {
108-
const resolvers = chain.filter(x => !!x) as ConfigResolver[];
109-
return (config: Config) =>
110-
resolvers.reduce(
111-
(resolved, next) => ({
112-
...resolved,
113-
...next(resolved),
114-
}),
115-
config,
116-
);
117-
}

0 commit comments

Comments
 (0)