Skip to content

Commit

Permalink
Add basic framework and simple UI tests of extension within VS Code v…
Browse files Browse the repository at this point in the history
…ia @vscode/test-electron.
  • Loading branch information
DavidAnson committed Sep 25, 2023
1 parent cbcee87 commit 64e95be
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.vscode-test
.vscode-test-web
bundle.js
bundle.web.js
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@
"compile-debug": "webpack --mode none",
"docker-npm-install": "docker run --rm --tty --name npm-install --volume $PWD:/home/workdir --workdir /home/workdir --user node node:latest npm install",
"docker-npm-run-upgrade": "docker run --rm --tty --name npm-run-upgrade --volume $PWD:/home/workdir --workdir /home/workdir --user node node:latest npm run upgrade",
"lint": "eslint --ignore-pattern bundle.js --ignore-pattern bundle.web.js *.js && markdownlint-cli2 *.md",
"lint": "eslint --ignore-pattern bundle.js --ignore-pattern bundle.web.js *.js test/*.cjs && markdownlint-cli2 *.md",
"schema": "cpy ./node_modules/markdownlint/schema/markdownlint-config-schema.json . --flat && node expand-config-schema.js && node generate-config-schema.js && cpy ./node_modules/markdownlint-cli2/schema/markdownlint-cli2-config-schema.json . --flat",
"test": "npm run lint && npm run compile && npm run schema && git diff --exit-code",
"test-ui": "node ./test/run-tests.mjs",
"upgrade": "npx --yes npm-check-updates --upgrade"
},
"categories": [
Expand All @@ -47,6 +48,7 @@
},
"devDependencies": {
"@types/vscode": "1.75.0",
"@vscode/test-electron": "2.3.4",
"cpy-cli": "5.0.0",
"eslint": "8.48.0",
"eslint-plugin-node": "11.1.0",
Expand Down
16 changes: 16 additions & 0 deletions test/index.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"use strict";

// This must be CommonJS or the VS Code host fails with:
// Error [ERR_REQUIRE_ESM]: require() of ES Module .../index.mjs not supported.

const {tests} = require("./tests.cjs");

function run () {
return tests.reduce((previous, current) => previous.then(() => {
// eslint-disable-next-line no-console
console.log(`${current.name}...`);
return current();
}), Promise.resolve());
}

module.exports = {run};
22 changes: 22 additions & 0 deletions test/run-tests.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { runTests } from "@vscode/test-electron";

// @ts-ignore
const extensionDevelopmentPath = new URL("..", import.meta.url).pathname;
// @ts-ignore
const extensionTestsPath = new URL("./index.cjs", import.meta.url).pathname;

try {
// @ts-ignore
await runTests({
extensionDevelopmentPath,
extensionTestsPath,
"launchArgs": [
"--disable-extensions"
// Including "--extensionDevelopmentKind=web" causes the VS Code host to fail with:
// TypeError: Failed to fetch
]
});
} catch (error) {
console.error(`TEST FAILURE: ${error}`);
process.exit(1);
}
149 changes: 149 additions & 0 deletions test/tests.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
"use strict";

// This must be CommonJS or the VS Code host fails with:
// Error: Cannot find package 'vscode' imported from .../tests.mjs

/* eslint-disable func-style */

const assert = require("node:assert");
const path = require("node:path");
const vscode = require("vscode");

function testWrapper (test) {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error("TEST TIMEOUT")), 10_000);
const disposables = [];
const cleanup = () => {
clearTimeout(timeout);
for (const disposable of disposables) {
disposable.dispose();
}
vscode.commands.executeCommand("workbench.action.closeAllEditors");
};
const resolveWrapper = (value) => {
resolve(value);
cleanup();
};
const rejectWrapper = (reason) => {
reject(reason);
cleanup();
};
try {
test(resolveWrapper, rejectWrapper, disposables);
} catch (error) {
rejectWrapper(error);
}
});
}

function callbackWrapper (reject, callback) {
try {
return callback();
} catch (error) {
return reject(error);
}
}

const tests = [

function openLintEditVerifyFixAll () {
return testWrapper((resolve, reject, disposables) => {
let fixedAll = false;
disposables.push(
vscode.window.onDidChangeActiveTextEditor((textEditor) => {
callbackWrapper(reject, () => {
assert.ok(textEditor.document.uri.path.endsWith("/README.md"));
textEditor.edit((editBuilder) => {
// MD019
editBuilder.insert(new vscode.Position(0, 1), " ");
// MD012
editBuilder.insert(new vscode.Position(1, 0), "\n");
});
});
}),
vscode.languages.onDidChangeDiagnostics((diagnosticChangeEvent) => {
callbackWrapper(reject, () => {
const {uris} = diagnosticChangeEvent;
assert.equal(uris.length, 1);
const [ uri ] = uris;
const diagnostics = vscode.languages.getDiagnostics(uri);
if ((diagnostics.length > 0) && !fixedAll) {
// eslint-disable-next-line array-element-newline
const [ md019, md012 ] = diagnostics;
// @ts-ignore
assert.equal(md019.code.value, "MD019");
assert.equal(
// @ts-ignore
md019.code.target.toString(),
"https://github.com/DavidAnson/markdownlint/blob/v0.31.1/doc/md019.md"
);
assert.equal(
md019.message,
"MD019/no-multiple-space-atx: Multiple spaces after hash on atx style heading"
);
assert.ok(md019.range.isEqual(new vscode.Range(0, 0, 0, 4)));
assert.equal(md019.severity, vscode.DiagnosticSeverity.Warning);
assert.equal(md019.source, "markdownlint");
// @ts-ignore
assert.equal(md012.code.value, "MD012");
assert.equal(
// @ts-ignore
md012.code.target.toString(),
"https://github.com/DavidAnson/markdownlint/blob/v0.31.1/doc/md012.md"
);
assert.equal(
md012.message,
"MD012/no-multiple-blanks: Multiple consecutive blank lines [Expected: 1; Actual: 2]"
);
assert.ok(md012.range.isEqual(new vscode.Range(2, 0, 2, 0)));
assert.equal(md012.severity, vscode.DiagnosticSeverity.Warning);
assert.equal(md012.source, "markdownlint");
vscode.commands.executeCommand("markdownlint.fixAll");
fixedAll = true;
} else if ((diagnostics.length === 0) && fixedAll) {
resolve();
}
});
})
);
vscode.window.showTextDocument(vscode.Uri.file(path.join(__dirname, "..", "README.md")));
});
},

function openLintEditCloseClean () {
return testWrapper((resolve, reject, disposables) => {
let closedActiveEditor = false;
disposables.push(
vscode.window.onDidChangeActiveTextEditor((textEditor) => {
callbackWrapper(reject, () => {
if (textEditor) {
assert.ok(textEditor.document.uri.path.endsWith("/README.md"));
textEditor.edit((editBuilder) => {
editBuilder.insert(new vscode.Position(0, 1), " ");
editBuilder.insert(new vscode.Position(1, 0), "\n");
});
}
});
}),
vscode.languages.onDidChangeDiagnostics((diagnosticChangeEvent) => {
callbackWrapper(reject, () => {
const {uris} = diagnosticChangeEvent;
assert.equal(uris.length, 1);
const [ uri ] = uris;
const diagnostics = vscode.languages.getDiagnostics(uri);
if ((diagnostics.length > 0) && !closedActiveEditor) {
vscode.commands.executeCommand("workbench.action.closeActiveEditor");
closedActiveEditor = true;
} else if ((diagnostics.length === 0) && closedActiveEditor) {
resolve();
}
});
})
);
vscode.window.showTextDocument(vscode.Uri.file(path.join(__dirname, "..", "README.md")));
});
}

];

module.exports = {tests};

0 comments on commit 64e95be

Please sign in to comment.