Skip to content

Commit

Permalink
add basic integration test framework
Browse files Browse the repository at this point in the history
  • Loading branch information
WebFreak001 committed Sep 21, 2024
1 parent b07f5d1 commit 9065c6f
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 39 deletions.
54 changes: 47 additions & 7 deletions src/test/runTest.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,64 @@
import * as path from 'path';
import * as path from "path";
import * as fs from "fs";
import * as os from "os";
import * as cp from "child_process";

import { runTests } from 'vscode-test';
const packageJson = require("../../package.json");

import {
downloadAndUnzipVSCode,
resolveCliArgsFromVSCodeExecutablePath,
runTests,
} from "@vscode/test-electron";
import { rimraf } from "rimraf";

async function main() {
try {
// The folder containing the Extension Manifest package.json
// Passed to `--extensionDevelopmentPath`
const extensionDevelopmentPath = path.resolve(__dirname, '../../../');
const extensionDevelopmentPath = path.resolve(__dirname, "../../");

// The path to the extension test runner script
// Passed to --extensionTestsPath
const extensionTestsPath = path.resolve(__dirname, './suite/index');
const extensionTestsPath = path.resolve(__dirname, "./suite/index");

const vscodeExecutablePath = await downloadAndUnzipVSCode();

const [cliPath, ...args] =
resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath);

// for (const extensionId of packageJson.extensionDependencies) {
// cp.spawnSync(cliPath, [...args, "--install-extension", extensionId], {
// encoding: "utf-8",
// stdio: "inherit",
// });
// }

await rimraf(".vscode-test/user-data");

let cwd = fs.mkdtempSync(path.join(os.tmpdir(), "coded_project"));
fs.writeFileSync(path.join(cwd, "dub.sdl"), 'name "codedproject"\n');
fs.mkdirSync(path.join(cwd, "source"));
fs.writeFileSync(
path.join(cwd, "source", "app.d"),
'import std.stdio;\n\nvoid main() {\n\twriteln("hello world");\n}\n'
);

// Download VS Code, unzip it and run the integration test
await runTests({ extensionDevelopmentPath, extensionTestsPath });
await runTests({
vscodeExecutablePath,
extensionDevelopmentPath,
launchArgs: [cwd],
extensionTestsPath,
extensionTestsEnv: {
PROJECT_DIR: cwd,
},
});
} catch (err) {
console.error(err);
console.error('Failed to run tests');
console.error("Failed to run tests");
process.exit(1);
}
}

main();
main();
59 changes: 27 additions & 32 deletions src/test/suite/index.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,33 @@
import { glob } from "glob";
import * as Mocha from "mocha";
import * as path from "path";

import * as path from 'path';
import * as Mocha from 'mocha';
import * as glob from 'glob';
export async function run(): Promise<void> {
// Create the mocha test
const mocha = new Mocha({
ui: "tdd",
timeout: 120000,
});

export function run(): Promise<void> {
// Create the mocha test
const mocha = new Mocha({
ui: 'tdd'
});
const testsRoot = path.resolve(__dirname, "..");

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

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

// Add files to the test suite
files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));

try {
// Run the mocha test
mocha.run(failures => {
if (failures > 0) {
e(new Error(`${failures} tests failed.`));
} else {
c();
}
});
} catch (err) {
console.error(err);
e(err);
}
});
});
return new Promise((resolve, reject) => {
// Run the mocha test
try {
mocha.run((failures) => {
if (failures > 0) {
reject(new Error(`${failures} tests failed.`));
} else {
resolve();
}
});
} catch (e) {
reject(e);
}
});
}
54 changes: 54 additions & 0 deletions src/test/suite/it.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as assert from "assert";

// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from "vscode";
import { sleep, testCompletion } from "../utils";
// import * as myExtension from '../../extension';

suite("Integration Tests", () => {
vscode.window.showInformationMessage("Start all tests.");

// sanity test that we have the correct window open
let workspaces = vscode.workspace.workspaceFolders;
assert.strictEqual(workspaces?.length, 1);
assert.strictEqual(workspaces[0].uri.fsPath, process.env["PROJECT_DIR"]);
let workspace = workspaces[0];

test("check code-d installed", async () => {
let coded = vscode.extensions.getExtension("webfreak.code-d")!;
assert.notStrictEqual(coded, undefined, "code-d not installed?!");
});

function file(relative: string): vscode.Uri {
return vscode.Uri.joinPath(workspace.uri, relative);
}

test("Wait for python and code-d extensions", async () => {
let coded = vscode.extensions.getExtension("webfreak.code-d")!;
await coded.activate();
await sleep(5000); // give sufficient startup time
});

test("Recipe file", async () => {
let recipe = await vscode.window.showTextDocument(
await vscode.workspace.openTextDocument(file("dub.sdl")),
vscode.ViewColumn.One
);

await recipe.edit((edit) => {
edit.insert(new vscode.Position(2, 0), "dep");
});

await testCompletion(
recipe,
new vscode.Position(2, 3),
new vscode.CompletionList([
new vscode.CompletionItem("dependency", vscode.CompletionItemKind.Field),
]),
"contains"
);
});

// test('interactive', () => new Promise((resolve, reject) => {}));
});
64 changes: 64 additions & 0 deletions src/test/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as assert from 'assert';
import * as vscode from 'vscode';

export function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}

export async function testCompletion(
editor: vscode.TextEditor,
position: vscode.Position,
expectedCompletionList: vscode.CompletionList,
type: "exact" | "contains",
testKeys: (keyof vscode.CompletionItem)[] = ["label", "kind"]
) {
editor = await vscode.window.showTextDocument(editor.document, editor.viewColumn);
await sleep(500);
editor.selection = new vscode.Selection(position, position);
await sleep(500);

// Executing the command `vscode.executeCompletionItemProvider` to simulate triggering completion
const actualCompletionList = (await vscode.commands.executeCommand(
'vscode.executeCompletionItemProvider',
editor.document.uri,
position
)) as vscode.CompletionList;

if (type === "exact") {
assert.strictEqual(actualCompletionList.items.length, expectedCompletionList.items.length);
expectedCompletionList.items.forEach((expectedItem, i) => {
const actualItem = actualCompletionList.items[i];
testKeys.forEach(key => {
assert.strictEqual(actualItem[key], expectedItem[key],
"completion "
+ JSON.stringify(expectedItem.label)
+ " mismatch on key " + JSON.stringify(key) + ":\n"
+ "expected = " + JSON.stringify(expectedItem[key]) + "\n"
+ " actual = " + JSON.stringify(actualItem[key]));
});
});
} else if (type === "contains") {
assert.ok(actualCompletionList.items.length >= expectedCompletionList.items.length,
"Expected at least " + expectedCompletionList.items.length
+ " completions, but only got " + actualCompletionList.items.length);
expectedCompletionList.items.forEach((expectedItem, i) => {
const actualItem = actualCompletionList.items.find(i => i.label == expectedItem.label);
if (!actualItem)
assert.fail("can't find completion item "
+ JSON.stringify(expectedItem.label)
+ " in "
+ JSON.stringify(actualCompletionList.items.map(c => c.label)));

testKeys.forEach(key => {
assert.strictEqual(actualItem[key], expectedItem[key],
"completion "
+ JSON.stringify(expectedItem.label)
+ " mismatch on key " + JSON.stringify(key) + ":\n"
+ "expected = " + JSON.stringify(expectedItem[key]) + "\n"
+ " actual = " + JSON.stringify(actualItem[key]));
});
});
} else {
throw new Error("invalid type");
}
}

0 comments on commit 9065c6f

Please sign in to comment.