Skip to content

Commit 9065c6f

Browse files
committed
add basic integration test framework
1 parent b07f5d1 commit 9065c6f

File tree

4 files changed

+192
-39
lines changed

4 files changed

+192
-39
lines changed

src/test/runTest.ts

+47-7
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,64 @@
1-
import * as path from 'path';
1+
import * as path from "path";
2+
import * as fs from "fs";
3+
import * as os from "os";
4+
import * as cp from "child_process";
25

3-
import { runTests } from 'vscode-test';
6+
const packageJson = require("../../package.json");
7+
8+
import {
9+
downloadAndUnzipVSCode,
10+
resolveCliArgsFromVSCodeExecutablePath,
11+
runTests,
12+
} from "@vscode/test-electron";
13+
import { rimraf } from "rimraf";
414

515
async function main() {
616
try {
717
// The folder containing the Extension Manifest package.json
818
// Passed to `--extensionDevelopmentPath`
9-
const extensionDevelopmentPath = path.resolve(__dirname, '../../../');
19+
const extensionDevelopmentPath = path.resolve(__dirname, "../../");
1020

1121
// The path to the extension test runner script
1222
// Passed to --extensionTestsPath
13-
const extensionTestsPath = path.resolve(__dirname, './suite/index');
23+
const extensionTestsPath = path.resolve(__dirname, "./suite/index");
24+
25+
const vscodeExecutablePath = await downloadAndUnzipVSCode();
26+
27+
const [cliPath, ...args] =
28+
resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath);
29+
30+
// for (const extensionId of packageJson.extensionDependencies) {
31+
// cp.spawnSync(cliPath, [...args, "--install-extension", extensionId], {
32+
// encoding: "utf-8",
33+
// stdio: "inherit",
34+
// });
35+
// }
36+
37+
await rimraf(".vscode-test/user-data");
38+
39+
let cwd = fs.mkdtempSync(path.join(os.tmpdir(), "coded_project"));
40+
fs.writeFileSync(path.join(cwd, "dub.sdl"), 'name "codedproject"\n');
41+
fs.mkdirSync(path.join(cwd, "source"));
42+
fs.writeFileSync(
43+
path.join(cwd, "source", "app.d"),
44+
'import std.stdio;\n\nvoid main() {\n\twriteln("hello world");\n}\n'
45+
);
1446

1547
// Download VS Code, unzip it and run the integration test
16-
await runTests({ extensionDevelopmentPath, extensionTestsPath });
48+
await runTests({
49+
vscodeExecutablePath,
50+
extensionDevelopmentPath,
51+
launchArgs: [cwd],
52+
extensionTestsPath,
53+
extensionTestsEnv: {
54+
PROJECT_DIR: cwd,
55+
},
56+
});
1757
} catch (err) {
1858
console.error(err);
19-
console.error('Failed to run tests');
59+
console.error("Failed to run tests");
2060
process.exit(1);
2161
}
2262
}
2363

24-
main();
64+
main();

src/test/suite/index.ts

+27-32
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,33 @@
1+
import { glob } from "glob";
2+
import * as Mocha from "mocha";
3+
import * as path from "path";
14

2-
import * as path from 'path';
3-
import * as Mocha from 'mocha';
4-
import * as glob from 'glob';
5+
export async function run(): Promise<void> {
6+
// Create the mocha test
7+
const mocha = new Mocha({
8+
ui: "tdd",
9+
timeout: 120000,
10+
});
511

6-
export function run(): Promise<void> {
7-
// Create the mocha test
8-
const mocha = new Mocha({
9-
ui: 'tdd'
10-
});
12+
const testsRoot = path.resolve(__dirname, "..");
1113

12-
const testsRoot = path.resolve(__dirname, '..');
14+
let files = await glob("**/**.test.js", { cwd: testsRoot });
1315

14-
return new Promise((c, e) => {
15-
glob('suite/**/**.test.js', { cwd: testsRoot }, (err, files) => {
16-
if (err) {
17-
return e(err);
18-
}
16+
// Add files to the test suite
17+
files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f)));
1918

20-
// Add files to the test suite
21-
files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
22-
23-
try {
24-
// Run the mocha test
25-
mocha.run(failures => {
26-
if (failures > 0) {
27-
e(new Error(`${failures} tests failed.`));
28-
} else {
29-
c();
30-
}
31-
});
32-
} catch (err) {
33-
console.error(err);
34-
e(err);
35-
}
36-
});
37-
});
19+
return new Promise((resolve, reject) => {
20+
// Run the mocha test
21+
try {
22+
mocha.run((failures) => {
23+
if (failures > 0) {
24+
reject(new Error(`${failures} tests failed.`));
25+
} else {
26+
resolve();
27+
}
28+
});
29+
} catch (e) {
30+
reject(e);
31+
}
32+
});
3833
}

src/test/suite/it.test.ts

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import * as assert from "assert";
2+
3+
// You can import and use all API from the 'vscode' module
4+
// as well as import your extension to test it
5+
import * as vscode from "vscode";
6+
import { sleep, testCompletion } from "../utils";
7+
// import * as myExtension from '../../extension';
8+
9+
suite("Integration Tests", () => {
10+
vscode.window.showInformationMessage("Start all tests.");
11+
12+
// sanity test that we have the correct window open
13+
let workspaces = vscode.workspace.workspaceFolders;
14+
assert.strictEqual(workspaces?.length, 1);
15+
assert.strictEqual(workspaces[0].uri.fsPath, process.env["PROJECT_DIR"]);
16+
let workspace = workspaces[0];
17+
18+
test("check code-d installed", async () => {
19+
let coded = vscode.extensions.getExtension("webfreak.code-d")!;
20+
assert.notStrictEqual(coded, undefined, "code-d not installed?!");
21+
});
22+
23+
function file(relative: string): vscode.Uri {
24+
return vscode.Uri.joinPath(workspace.uri, relative);
25+
}
26+
27+
test("Wait for python and code-d extensions", async () => {
28+
let coded = vscode.extensions.getExtension("webfreak.code-d")!;
29+
await coded.activate();
30+
await sleep(5000); // give sufficient startup time
31+
});
32+
33+
test("Recipe file", async () => {
34+
let recipe = await vscode.window.showTextDocument(
35+
await vscode.workspace.openTextDocument(file("dub.sdl")),
36+
vscode.ViewColumn.One
37+
);
38+
39+
await recipe.edit((edit) => {
40+
edit.insert(new vscode.Position(2, 0), "dep");
41+
});
42+
43+
await testCompletion(
44+
recipe,
45+
new vscode.Position(2, 3),
46+
new vscode.CompletionList([
47+
new vscode.CompletionItem("dependency", vscode.CompletionItemKind.Field),
48+
]),
49+
"contains"
50+
);
51+
});
52+
53+
// test('interactive', () => new Promise((resolve, reject) => {}));
54+
});

src/test/utils.ts

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import * as assert from 'assert';
2+
import * as vscode from 'vscode';
3+
4+
export function sleep(ms: number): Promise<void> {
5+
return new Promise((resolve) => setTimeout(resolve, ms));
6+
}
7+
8+
export async function testCompletion(
9+
editor: vscode.TextEditor,
10+
position: vscode.Position,
11+
expectedCompletionList: vscode.CompletionList,
12+
type: "exact" | "contains",
13+
testKeys: (keyof vscode.CompletionItem)[] = ["label", "kind"]
14+
) {
15+
editor = await vscode.window.showTextDocument(editor.document, editor.viewColumn);
16+
await sleep(500);
17+
editor.selection = new vscode.Selection(position, position);
18+
await sleep(500);
19+
20+
// Executing the command `vscode.executeCompletionItemProvider` to simulate triggering completion
21+
const actualCompletionList = (await vscode.commands.executeCommand(
22+
'vscode.executeCompletionItemProvider',
23+
editor.document.uri,
24+
position
25+
)) as vscode.CompletionList;
26+
27+
if (type === "exact") {
28+
assert.strictEqual(actualCompletionList.items.length, expectedCompletionList.items.length);
29+
expectedCompletionList.items.forEach((expectedItem, i) => {
30+
const actualItem = actualCompletionList.items[i];
31+
testKeys.forEach(key => {
32+
assert.strictEqual(actualItem[key], expectedItem[key],
33+
"completion "
34+
+ JSON.stringify(expectedItem.label)
35+
+ " mismatch on key " + JSON.stringify(key) + ":\n"
36+
+ "expected = " + JSON.stringify(expectedItem[key]) + "\n"
37+
+ " actual = " + JSON.stringify(actualItem[key]));
38+
});
39+
});
40+
} else if (type === "contains") {
41+
assert.ok(actualCompletionList.items.length >= expectedCompletionList.items.length,
42+
"Expected at least " + expectedCompletionList.items.length
43+
+ " completions, but only got " + actualCompletionList.items.length);
44+
expectedCompletionList.items.forEach((expectedItem, i) => {
45+
const actualItem = actualCompletionList.items.find(i => i.label == expectedItem.label);
46+
if (!actualItem)
47+
assert.fail("can't find completion item "
48+
+ JSON.stringify(expectedItem.label)
49+
+ " in "
50+
+ JSON.stringify(actualCompletionList.items.map(c => c.label)));
51+
52+
testKeys.forEach(key => {
53+
assert.strictEqual(actualItem[key], expectedItem[key],
54+
"completion "
55+
+ JSON.stringify(expectedItem.label)
56+
+ " mismatch on key " + JSON.stringify(key) + ":\n"
57+
+ "expected = " + JSON.stringify(expectedItem[key]) + "\n"
58+
+ " actual = " + JSON.stringify(actualItem[key]));
59+
});
60+
});
61+
} else {
62+
throw new Error("invalid type");
63+
}
64+
}

0 commit comments

Comments
 (0)