Skip to content

Commit 70ae551

Browse files
authored
Merge pull request #68 from VSC-NeuroPilot/unit-tests
Unit tests
2 parents a72b292 + 5d275b0 commit 70ae551

58 files changed

Lines changed: 4046 additions & 311 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/tests.yml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
name: Tests (Desktop & Web)
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
- dev
8+
# Tests should always run on Pull Requests in order to catch regressions early
9+
pull_request:
10+
workflow_dispatch: # in case we need to regenerate test coverage
11+
12+
jobs:
13+
desktop-tests:
14+
name: Desktop (Electron) tests
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
include:
19+
- os: macos-latest
20+
name: macOS
21+
- os: windows-latest
22+
name: Windows
23+
- os: ubuntu-latest
24+
name: Linux
25+
runs-on: ${{ matrix.os }}
26+
steps:
27+
- name: Checkout
28+
uses: actions/checkout@v4
29+
- name: Cache VS Code test downloads
30+
uses: actions/cache@v4
31+
with:
32+
key: neuropilot-tests-vscode-${{ github.repository }}-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
33+
restore-keys: |
34+
neuropilot-tests-vscode-${{ github.repository }}-${{ runner.os }}-
35+
path: .vscode-test/
36+
- name: Setup pnpm
37+
uses: pnpm/action-setup@v4
38+
with:
39+
version: 10.14.0
40+
- name: Install Node.js
41+
uses: actions/setup-node@v4
42+
with:
43+
node-version: 22.x
44+
cache: 'pnpm'
45+
cache-dependency-path: './pnpm-lock.yaml'
46+
- run: pnpm install --frozen-lockfile
47+
- name: Run desktop tests (Linux)
48+
run: xvfb-run -a pnpm run test:desktop
49+
if: runner.os == 'Linux'
50+
- name: Run desktop tests (macOS/Windows)
51+
run: pnpm run test:desktop
52+
if: runner.os != 'Linux'
53+
54+
web-tests:
55+
name: Web tests (browser matrix)
56+
runs-on: ubuntu-latest
57+
strategy:
58+
matrix:
59+
browser: [chromium, firefox, webkit]
60+
steps:
61+
- name: Checkout
62+
uses: actions/checkout@v4
63+
- name: Cache VS Code web test downloads
64+
uses: actions/cache@v4
65+
with:
66+
key: neuropilot-tests-vscode-web-v2-${{ github.repository }}-${{ runner.os }}
67+
path: .vscode-test-web/
68+
- name: Cache Playwright browsers
69+
uses: actions/cache@v4
70+
with:
71+
key: neuropilot-playwright-v2-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
72+
path: ~/.cache/ms-playwright
73+
- name: Setup pnpm
74+
uses: pnpm/action-setup@v4
75+
with:
76+
version: 10.14.0
77+
- name: Install Node.js
78+
uses: actions/setup-node@v4
79+
with:
80+
node-version: 22.x
81+
cache: 'pnpm'
82+
cache-dependency-path: './pnpm-lock.yaml'
83+
- run: pnpm install --frozen-lockfile
84+
- name: Ensure Playwright browsers are installed (Chromium, Firefox, WebKit)
85+
run: pnpm exec playwright install --with-deps
86+
- name: Build web extension and test bundle (browser)
87+
run: pnpm run build:web && pnpm run test:web:browser:esbuild
88+
- name: Run web tests (headless)
89+
run: pnpm exec vscode-test-web --headless --browser=${{ matrix.browser }} --extensionDevelopmentPath=. --extensionTestsPath=out/web/test/browser.js test-playground

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,11 @@ pnpm-debug.log*
2828

2929
# macOS-specific files
3030
.DS_Store
31+
32+
# Coverage
33+
/*coverage
34+
/coverage-web/*
35+
/coverage-desktop/*
36+
37+
# Local Playwright browser cache if needed for local web tests, in non-default location
38+
.playwright-browsers

.vscode-test.mjs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// @ts-check
2+
import { defineConfig } from '@vscode/test-cli';
3+
4+
export default defineConfig({
5+
tests: [
6+
{
7+
label: 'desktopUnitTest',
8+
platform: 'desktop',
9+
files: 'out/desktop/test.js',
10+
workspaceFolder: './test-playground',
11+
mocha: {
12+
timeout: 5000
13+
},
14+
env: {
15+
NEUROPILOT_TEST: 'true',
16+
},
17+
},
18+
{ // i hate how underdeveloped the web test side of testing is
19+
label: 'webUnitTest',
20+
files: 'out/web/test/index.js',
21+
workspaceFolder: './test-playground',
22+
mocha: {
23+
timeout: 5000
24+
},
25+
env: {
26+
NEUROPILOT_TEST: 'true',
27+
},
28+
},
29+
{
30+
label: 'browserUnitTest',
31+
files: 'out/web/test/browser.js',
32+
workspaceFolder: './test-playground',
33+
mocha: {
34+
timeout: 5000
35+
},
36+
env: {
37+
NEUROPILOT_TEST: 'true',
38+
},
39+
},
40+
],
41+
coverage: { // only as a backup, should be overridden by CLI options (thank you microsoft, why can't this be simpler)
42+
reporter: ['html', 'json'],
43+
include: ['src/**/*.ts'],
44+
exclude: ['src/test/**/*.ts'],
45+
output: './coverage',
46+
}
47+
});

.vscode/extensions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"recommendations": ["astro-build.astro-vscode", "connor4312.esbuild-problem-matchers"],
2+
"recommendations": ["astro-build.astro-vscode", "connor4312.esbuild-problem-matchers", "ms-vscode.extension-test-runner"],
33
"unwantedRecommendations": []
44
}

.vscode/launch.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,19 @@
3636
],
3737
"preLaunchTask": "watch:web"
3838
},
39+
{
40+
"name": "Extension Tests",
41+
"type": "extensionHost",
42+
"request": "launch",
43+
"runtimeExecutable": "${execPath}",
44+
"args": [
45+
"--extensionDevelopmentPath=${workspaceFolder}",
46+
"--extensionTestsPath=${workspaceFolder}/out/desktop/test.js",
47+
"${workspaceFolder}/test-playground"
48+
],
49+
"outFiles": ["${workspaceFolder}/out/desktop/test.js"],
50+
"preLaunchTask": "watch:desktop"
51+
},
3952
{
4053
"name": "Open in Browser",
4154
"type": "node-terminal",

.vscode/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@
77
"neuropilot",
88
"Pasu",
99
"sama"
10-
]
10+
],
11+
"python.testing.pytestEnabled": false,
12+
"python.testing.unittestEnabled": false,
13+
"python.testing.promptToConfigure": false
1114
}

.vscodeignore

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,10 @@ node_modules/
1212
project-files/
1313

1414
# Build files
15-
esbuild.{m,c,}js
1615
esbuild-configs/
1716

1817
# Other meta scripts
19-
sort-enums.ts
18+
/scripts
2019

2120
# Krita
2221
*.kra
@@ -28,3 +27,11 @@ sort-enums.ts
2827
# Tony
2928
.venv
3029
tony/
30+
31+
# Coverage
32+
/coverage-*
33+
34+
# Integration testing
35+
test-playground/
36+
.vscode-test.mjs
37+
out/test/**

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,53 @@ If you have an idea or want to contribute a feature, please first [create an iss
118118
If you make a pull request that contributes code, please run `npm run lint src` and resolve any errors that did not get auto-fixed, preferrably before each commit.
119119

120120
Please also refer to our [contributor docs](https://vsc-neuropilot.github.io/docs/meta/contributors).
121+
122+
### Tests
123+
124+
We have both unit tests and integration tests. Integration tests spin up a VS Code host (desktop Electron or the browser-hosted workbench) and exercise the extension.
125+
126+
Folder layout:
127+
128+
- `src/test/unit-test/` — unit tests that validate prompt text generation logic only
129+
- `src/test/suite/desktop/` and `src/test/suite/web/` — integration test harnesses and suites
130+
131+
Unit tests:
132+
133+
- Purpose: verify prompt text formatting and related pure logic (e.g., line counts, pluralization, escaping)
134+
- Scope: they DO NOT execute action handlers or use VS Code APIs; they only cover prompt-generation logic
135+
- Examples: `rewrite_all.simple.test.ts`, `find_text.simple.test.ts`, etc. under `src/test/unit-test/`
136+
- Execution: unit tests are imported into both the desktop and web test runners, so `pnpm test` (and CI) runs them alongside integration tests; no separate Node/Mocha job is required
137+
138+
Integration tests:
139+
140+
- Purpose: verify actual action functionality and end-to-end extension behavior
141+
- Scope: these use VS Code APIs (open/save files, edits, decorations, terminal/tasks, git, etc.) and assert the real effects
142+
- Environments:
143+
- Desktop integration runs in the Electron host
144+
- Web integration runs either under the Electron host with the web bundle or in a real browser via `@vscode/test-web`
145+
146+
Prerequisites (web tests):
147+
148+
- Install Playwright browsers (required for Firefox/WebKit; Chromium usually works out-of-the-box but we recommend installing all):
149+
- `pnpm dlx playwright install --with-deps`
150+
151+
Commands:
152+
153+
- Desktop (Electron host):
154+
- Run desktop tests: `pnpm run test:desktop`
155+
156+
- Web (true browser via `@vscode/test-web`):
157+
- Quick (Chromium default, build + run): `pnpm run test:web`
158+
- Per-browser shortcuts (build + run):
159+
- Chromium: `pnpm run test:web:browser:chromium`
160+
- Firefox: `pnpm run test:web:browser:firefox`
161+
- WebKit: `pnpm run test:web:browser:webkit`
162+
- Manual steps (if you need them):
163+
- Build browser test bundle: `pnpm run test:web:browser:esbuild`
164+
- Run with explicit browser flag: `pnpm run test:web:browser:vscode -- --browser=chromium`
165+
166+
Notes:
167+
- Web tests run in a real browser using the web extension bundle; they do not use the Electron (desktop) harness.
168+
- The browser test workspace is mounted under a virtual scheme; the workspace name may appear as `mount` instead of `test-playground`.
169+
- File operations in browser mode use the VS Code virtual FS, so ‘trash’ is disabled and deletes are immediate.
170+
- Headless web runs may log warnings like "Output channel not initialized", "[NeuroClient] WebSocket is not open", or 404s for dev assets. These are expected in the test harness; assertions still validate real side effects (file edits/opens/renames/deletes, document text, active editor) and verify `sendContext` via a mocked client.

esbuild-configs/desktop.esbuild.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export async function desktop(prodFlag, watchFlag) {
1313
outfile: 'out/desktop/extension.js',
1414
external: ['vscode'],
1515
logLevel: 'warning',
16+
tsconfig: './tsconfig.app.json',
1617
plugins: [
1718
/* add to the end of plugins array */
1819
esbuildProblemMatcherPlugin,
@@ -26,6 +27,40 @@ export async function desktop(prodFlag, watchFlag) {
2627
}
2728
}
2829

30+
export async function desktopTest(_prodFlag, watchFlag) {
31+
const ctx = await context({
32+
entryPoints: ['src/test/suite/desktop/index.ts'],
33+
bundle: true,
34+
format: 'cjs',
35+
minify: false, // Don't minify tests for better debugging
36+
sourcemap: true, // Always generate sourcemaps for tests
37+
sourcesContent: true, // Include source content for better debugging
38+
platform: 'node',
39+
outfile: 'out/desktop/test.js',
40+
tsconfig: './test-tsconfigs/tsconfig.app.json',
41+
external: [
42+
'vscode',
43+
'mocha',
44+
'@vscode/test-electron',
45+
],
46+
logLevel: 'warning',
47+
define: {
48+
// Define test environment variables
49+
'process.env.NODE_ENV': '"test"',
50+
},
51+
plugins: [
52+
esbuildProblemMatcherPlugin,
53+
],
54+
});
55+
56+
if (watchFlag) {
57+
await ctx.watch();
58+
} else {
59+
await ctx.rebuild();
60+
await ctx.dispose();
61+
}
62+
}
63+
2964
/**
3065
* @type {import('esbuild').Plugin}
3166
*/

0 commit comments

Comments
 (0)