Skip to content

Commit 7fc3793

Browse files
authored
feat: update dependencies and improve test coverage
- Introduced unit tests using Vitest for the extension bundle. - Created integration tests to verify extension installation, activation, and command registration. - Added a script to run integration tests with VS Code Test Electron. - Updated package.json to include new test scripts and dependencies for testing. - Configured TypeScript for integration tests with a new tsconfig.json. - Updated @hpcc-js/esbuild-plugins to version 1.8.7 - Updated @hpcc-js/observablehq-compiler to version 3.7.9 - Updated @hpcc-js/util to version 3.5.5 - Updated @observablehq/notebook-kit to version 1.5.2 - Updated eslint-plugin-unused-imports to version 4.2.0 - Updated vitest to version 4.1.3 - Added support for R language in observable2vscode - Refactored extension tests to remove unnecessary setup and improve assertions - Removed commented-out code for bundle environment preparation - Added new tests for bundle size and exports of activate/deactivate functions - Cleaned up integration tests by removing redundant command registration test - Added @types/mocha (10.0.10) and mocha (11.7.5) to devDependencies. - Updated serialize-javascript to version 7.0.5. - Removed @vscode/test-cli from dependencies. - Cleaned up package-lock.json by removing unused packages. Signed-off-by: Gordon Smith <GordonJSmith@gmail.com>
1 parent 4e66640 commit 7fc3793

11 files changed

Lines changed: 1863 additions & 539 deletions

File tree

esbuild.mjs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ const aliasPlugin = {
3737
}
3838
}
3939
});
40+
41+
// For Node builds, use the /node subpath of @hpcc-js/observablehq-compiler
42+
// which has a clean Node-compatible build without Rollup's broken require() stubs.
43+
if (build.initialOptions.platform === "node") {
44+
build.onResolve({ filter: /^@hpcc-js\/observablehq-compiler$/ }, () => {
45+
return { path: path.resolve("node_modules/@hpcc-js/observablehq-compiler/dist/node/index.js") };
46+
});
47+
}
4048
}
4149
};
4250

@@ -77,7 +85,7 @@ async function main(tsconfigRaw, entryPoint, platform, format, plugins = [], out
7785
];
7886

7987
const external = platform === "node"
80-
? ["vscode", "fs", "path", "os"]
88+
? ["vscode"]
8189
: ["vscode", ...npmExternals];
8290

8391
const ctx = await esbuild.context({

package-lock.json

Lines changed: 1700 additions & 529 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@
3838
"watch": "run-p gen-types-watch build-ts-watch",
3939
"lint": "eslint ./src",
4040
"lint-fix": "npm run lint -- --fix",
41-
"test": "run-s lint package",
41+
"unit-test": "vitest run",
42+
"integration-test": "tsc -p tests/integration/tsconfig.json && node out/tests/integration/runTest.js",
43+
"test": "run-s lint build unit-test integration-test package",
4244
"vscode:prepublish": "run-s clean build",
4345
"publish": "vsce publish",
4446
"package": "vsce package -o ./observable-js.vsix",
@@ -47,18 +49,20 @@
4749
"update": "npx -y npm-check-updates -u -t minor"
4850
},
4951
"devDependencies": {
50-
"@hpcc-js/esbuild-plugins": "1.5.0",
52+
"@hpcc-js/esbuild-plugins": "1.8.7",
5153
"@hpcc-js/observable-shim": "3.1.0",
52-
"@hpcc-js/observablehq-compiler": "3.5.1",
53-
"@hpcc-js/util": "3.3.10",
54+
"@hpcc-js/observablehq-compiler": "3.7.9",
55+
"@hpcc-js/util": "3.5.5",
5456
"@observablehq/inspector": "5.0.1",
55-
"@observablehq/notebook-kit": "1.4.1",
57+
"@observablehq/notebook-kit": "1.5.2",
5658
"@observablehq/runtime": "5.9.9",
5759
"@observablehq/stdlib": "5.8.8",
60+
"@types/mocha": "10.0.10",
5861
"@types/node": "24.5.2",
5962
"@types/vscode": "1.102.0",
6063
"@types/vscode-notebook-renderer": "1.72.4",
6164
"@vscode/extension-telemetry": "1.0.0",
65+
"@vscode/test-electron": "2.5.2",
6266
"@vscode/vsce": "3.6.0",
6367
"acorn": "8.15.0",
6468
"acorn-walk": "8.3.4",
@@ -71,20 +75,27 @@
7175
"esbuild-plugin-umd-wrapper": "3.0.0",
7276
"eslint": "9.35.0",
7377
"eslint-plugin-react-hooks": "5.2.0",
74-
"eslint-plugin-unused-imports": "^4.2.0",
78+
"eslint-plugin-unused-imports": "4.2.0",
7579
"globals": "16.4.0",
7680
"jsdom": "26.1.0",
81+
"mocha": "11.7.5",
7782
"node-fetch": "3.3.2",
7883
"npm-run-all": "4.1.5",
7984
"os-browserify": "0.3.0",
8085
"path-browserify": "1.0.1",
8186
"rimraf": "6.0.1",
87+
"serialize-javascript": "7.0.5",
8288
"stream-browserify": "3.0.0",
8389
"tslib": "2.8.1",
8490
"typescript": "5.9.2",
8591
"typescript-eslint": "8.44.0",
8692
"uuid": "13.0.0",
87-
"vitest": "^3.2.4"
93+
"vite": "7.3.2",
94+
"vitest": "4.1.3"
95+
},
96+
"overrides": {
97+
"vite": "$vite",
98+
"serialize-javascript": "$serialize-javascript"
8899
},
89100
"author": {
90101
"name": "Gordon Smith"

src/notebook-kit/common/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ export const observable2vscode: Record<Cell["mode"], string> = {
2525
"dot": "dot",
2626
"node": "node",
2727
"python": "python",
28-
"ts": "typescript"
28+
"ts": "typescript",
29+
"r": "r"
2930
};
3031

3132
export interface NotebookCell {

tests/extension.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { describe, it, expect } from "vitest";
2+
import { existsSync, readFileSync, statSync } from "node:fs";
3+
import { resolve, dirname } from "node:path";
4+
import { fileURLToPath } from "node:url";
5+
6+
const __dirname = dirname(fileURLToPath(import.meta.url));
7+
const rootDir = resolve(__dirname, "..");
8+
const distDir = resolve(rootDir, "dist");
9+
10+
describe("extension bundle", () => {
11+
it("dist/extension.js exists", () => {
12+
expect(existsSync(resolve(distDir, "extension.js"))).toBe(true);
13+
});
14+
15+
it("dist/xhr-sync-worker.js exists", () => {
16+
expect(existsSync(resolve(distDir, "xhr-sync-worker.js"))).toBe(true);
17+
});
18+
19+
it("bundle has reasonable size", () => {
20+
const stats = statSync(resolve(distDir, "extension.js"));
21+
expect(stats.size).toBeGreaterThan(100_000);
22+
});
23+
24+
it("bundle exports activate and deactivate", () => {
25+
const src = readFileSync(resolve(distDir, "extension.js"), "utf-8");
26+
expect(src).toContain("activate");
27+
expect(src).toContain("deactivate");
28+
});
29+
});

tests/integration/runTest.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { resolve } from "node:path";
2+
import { runTests } from "@vscode/test-electron";
3+
4+
async function main(): Promise<void> {
5+
const extensionDevelopmentPath = resolve(__dirname, "..", "..", "..");
6+
const extensionTestsPath = resolve(__dirname, "suite/index");
7+
const workspacePath = resolve(extensionDevelopmentPath, "samples");
8+
9+
await runTests({
10+
extensionDevelopmentPath,
11+
extensionTestsPath,
12+
launchArgs: [
13+
workspacePath,
14+
"--disable-extensions",
15+
"--disable-gpu",
16+
],
17+
});
18+
}
19+
20+
main().catch((err) => {
21+
console.error("Failed to run tests:", err);
22+
process.exit(1);
23+
});
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import * as assert from "node:assert/strict";
2+
import * as vscode from "vscode";
3+
4+
const EXTENSION_ID = "GordonSmith.observable-js";
5+
6+
suite("Extension Integration", () => {
7+
8+
test("extension is installed", () => {
9+
const ext = vscode.extensions.getExtension(EXTENSION_ID);
10+
assert.ok(ext, `Extension ${EXTENSION_ID} should be installed`);
11+
});
12+
13+
test("extension activates successfully", async function () {
14+
const ext = vscode.extensions.getExtension(EXTENSION_ID);
15+
assert.ok(ext, `Extension ${EXTENSION_ID} should be installed`);
16+
17+
if (!ext.isActive) {
18+
await ext.activate();
19+
}
20+
assert.ok(ext.isActive, "Extension should be active after activation");
21+
});
22+
});

tests/integration/suite/index.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { resolve } from "node:path";
2+
import { globSync } from "node:fs";
3+
4+
// Patch globalThis.window BEFORE importing Mocha.
5+
// The VS Code Extension Host (Electron renderer process) defines `document`
6+
// but not `window`. Mocha 11's utils.js uses `typeof document` to detect
7+
// browser vs Node and then accesses `window.location`, which crashes.
8+
if (typeof globalThis.window === "undefined") {
9+
(globalThis as Record<string, unknown>).window = {
10+
location: (globalThis as Record<string, unknown>).location || { href: "" },
11+
};
12+
}
13+
14+
import Mocha from "mocha";
15+
16+
export function run(): Promise<void> {
17+
const mocha = new Mocha({
18+
ui: "tdd",
19+
timeout: 30_000,
20+
color: true,
21+
});
22+
23+
const testFiles = globSync("*.test.js", { cwd: __dirname });
24+
for (const f of testFiles) {
25+
mocha.addFile(resolve(__dirname, f));
26+
}
27+
28+
return new Promise((resolveP, reject) => {
29+
mocha.run((failures) => {
30+
if (failures > 0) {
31+
reject(new Error(`${failures} test(s) failed.`));
32+
} else {
33+
resolveP();
34+
}
35+
});
36+
});
37+
}

tests/integration/tsconfig.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2022",
4+
"module": "NodeNext",
5+
"moduleResolution": "NodeNext",
6+
"outDir": "../../out/tests/integration",
7+
"rootDir": ".",
8+
"strict": true,
9+
"esModuleInterop": true,
10+
"skipLibCheck": true
11+
},
12+
"include": [
13+
"**/*.ts"
14+
]
15+
}

0 commit comments

Comments
 (0)