Skip to content

Commit 7ed59f2

Browse files
wan9chicodex
andcommitted
test(e2e): cover Vitest browser caching
Co-authored-by: GPT-5.6 <codex@openai.com>
1 parent bee8aa0 commit 7ed59f2

14 files changed

Lines changed: 375 additions & 45 deletions

File tree

.github/workflows/ci.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ jobs:
6969
needs: detect-changes
7070
if: needs.detect-changes.outputs.code-changed == 'true'
7171
name: Test (${{ matrix.shard }})
72+
env:
73+
# Playwright detects the physical Apple Silicon CPU under Rosetta. Force
74+
# its x64 browser for the x86_64 shard so fspy can inject its x64 preload.
75+
PLAYWRIGHT_HOST_PLATFORM_OVERRIDE: ${{ matrix.target == 'x86_64-apple-darwin' && 'mac15' || '' }}
7276
strategy:
7377
fail-fast: false
7478
matrix:
@@ -128,6 +132,13 @@ jobs:
128132
with:
129133
architecture: ${{ matrix.target == 'x86_64-apple-darwin' && 'x64' || '' }}
130134

135+
- name: Install Chromium
136+
run: pnpm --filter vite-task-tools rebuild @playwright/browser-chromium
137+
138+
- name: Install Chromium system dependencies
139+
if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }}
140+
run: pnpm --filter vite-task-tools exec playwright install-deps chromium
141+
131142
- name: Run ignored tests
132143
run: ${{ matrix.cargo_cmd }} test --target ${{ matrix.build_target }} -- --ignored
133144

@@ -256,6 +267,10 @@ jobs:
256267
- uses: oxc-project/setup-node@4c588e9266bd930b6ddc34307df0659ed511d187 # v1.3.1
257268
if: matrix.mode == 'ignored'
258269

270+
- name: Install Chromium
271+
if: matrix.mode == 'ignored'
272+
run: pnpm --filter vite-task-tools rebuild @playwright/browser-chromium
273+
259274
- name: Run ignored tests
260275
if: matrix.mode == 'ignored'
261276
run: cargo-nextest nextest run --archive-file windows-tests.tar.zst --workspace-remap . --run-ignored ignored-only
@@ -279,6 +294,9 @@ jobs:
279294
# On musl, concurrent PTY operations can trigger SIGSEGV in musl internals.
280295
# Run test threads sequentially to avoid the race.
281296
RUST_TEST_THREADS: 1
297+
# Playwright's bundled Chromium does not support musl. The browser fixture
298+
# is filtered out there, so avoid downloading an unusable glibc binary.
299+
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
282300
steps:
283301
- name: Install Alpine dependencies
284302
shell: sh {0}

CONTRIBUTING.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
- [Rust](https://rustup.rs/) (see [rust-toolchain.toml](rust-toolchain.toml) for the required version)
66
- [Node.js](https://nodejs.org/) (^20.19.0 || >=22.12.0)
7-
- [pnpm](https://pnpm.io/) (10.x)
7+
- [pnpm](https://pnpm.io/) (11.x)
88
- [just](https://just.systems/) — task runner for build commands
99
- [cargo-binstall](https://github.com/cargo-bins/cargo-binstall) — for installing Rust tools
1010

@@ -51,12 +51,13 @@ UPDATE_SNAPSHOTS=1 cargo test # Update snapshots
5151
Integration tests that need Node.js or the `packages/tools` binaries (e.g. `oxlint`) are marked `#[ignore]` — and `[[e2e]]` cases in e2e snapshots.toml can opt in with `ignore = true`. Default `cargo test` runs only the tests that need nothing beyond the Rust toolchain; to run the rest:
5252

5353
```bash
54-
pnpm install # at the workspace root (installs packages/tools too)
54+
pnpm install # at the workspace root (installs packages/tools and Chromium too)
5555
cargo test -- --include-ignored # run everything
5656
cargo test -- --ignored # run only the previously-ignored tests
5757
```
5858

5959
You don't need `pnpm install` in test fixture directories.
60+
Playwright's bundled Chromium is unavailable on musl, so the Vitest browser fixture is skipped there.
6061

6162
### Test Fixtures
6263

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "vitest-browser-cache-fixture",
3+
"private": true,
4+
"type": "module"
5+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
[[e2e]]
2+
name = "vitest_browser_caches_inputs_and_restores_outputs"
3+
comment = """
4+
Vitest browser mode runs in headless Chromium with explicit automatic input and output tracking. A source change must invalidate the cache, while an unchanged run must restore the browser command's generated output without rerunning Vitest. Playwright's bundled Chromium is unavailable on musl, so this browser-specific case is skipped only there.
5+
"""
6+
platform = "non-musl"
7+
ignore = true
8+
steps = [
9+
{ argv = [
10+
"vt",
11+
"run",
12+
"--cache",
13+
"test",
14+
], comment = "first browser run: cache miss writes dist/result.json" },
15+
{ argv = [
16+
"vtt",
17+
"grep-file",
18+
"dist/result.json",
19+
"hello browser alpha",
20+
], comment = "Vitest's JSON report contains the initial imported value" },
21+
{ argv = [
22+
"vtt",
23+
"rm",
24+
"dist/result.json",
25+
], comment = "remove the generated output so restoration is observable" },
26+
{ argv = [
27+
"vt",
28+
"run",
29+
"--cache",
30+
"test",
31+
], comment = "unchanged inputs: cache hit restores dist/result.json" },
32+
{ argv = [
33+
"vtt",
34+
"grep-file",
35+
"dist/result.json",
36+
"hello browser alpha",
37+
], comment = "the automatic output archive restored Vitest's report" },
38+
{ argv = [
39+
"vtt",
40+
"replace-file-content",
41+
"src/greeting.js",
42+
"hello browser alpha",
43+
"hello browser bravo",
44+
], comment = "modify a module loaded by the browser" },
45+
{ argv = [
46+
"vt",
47+
"run",
48+
"--cache",
49+
"test",
50+
], comment = "automatic input changed: cache miss reruns the browser test" },
51+
{ argv = [
52+
"vtt",
53+
"grep-file",
54+
"dist/result.json",
55+
"hello browser bravo",
56+
], comment = "the rerun's JSON report contains the modified imported value" },
57+
]
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# vitest_browser_caches_inputs_and_restores_outputs
2+
3+
Vitest browser mode runs in headless Chromium with explicit automatic input and output tracking. A source change must invalidate the cache, while an unchanged run must restore the browser command's generated output without rerunning Vitest. Playwright's bundled Chromium is unavailable on musl, so this browser-specific case is skipped only there.
4+
5+
## `vt run --cache test`
6+
7+
first browser run: cache miss writes dist/result.json
8+
9+
```
10+
$ vitest run
11+
12+
RUN v4.1.10 <workspace>
13+
14+
✓ chromium src/greeting.test.js (1 test) <duration>
15+
JSON report written to <workspace>/dist/result.json
16+
```
17+
18+
## `vtt grep-file dist/result.json 'hello browser alpha'`
19+
20+
Vitest's JSON report contains the initial imported value
21+
22+
```
23+
dist/result.json: found "hello browser alpha"
24+
```
25+
26+
## `vtt rm dist/result.json`
27+
28+
remove the generated output so restoration is observable
29+
30+
```
31+
```
32+
33+
## `vt run --cache test`
34+
35+
unchanged inputs: cache hit restores dist/result.json
36+
37+
```
38+
$ vitest run ◉ cache hit, replaying
39+
40+
RUN v4.1.10 <workspace>
41+
42+
✓ chromium src/greeting.test.js (1 test) <duration>
43+
JSON report written to <workspace>/dist/result.json
44+
45+
---
46+
vt run: cache hit.
47+
```
48+
49+
## `vtt grep-file dist/result.json 'hello browser alpha'`
50+
51+
the automatic output archive restored Vitest's report
52+
53+
```
54+
dist/result.json: found "hello browser alpha"
55+
```
56+
57+
## `vtt replace-file-content src/greeting.js 'hello browser alpha' 'hello browser bravo'`
58+
59+
modify a module loaded by the browser
60+
61+
```
62+
```
63+
64+
## `vt run --cache test`
65+
66+
automatic input changed: cache miss reruns the browser test
67+
68+
```
69+
$ vitest run ○ cache miss: 'src/greeting.js' modified, executing
70+
71+
RUN v4.1.10 <workspace>
72+
73+
✓ chromium src/greeting.test.js (1 test) <duration>
74+
JSON report written to <workspace>/dist/result.json
75+
```
76+
77+
## `vtt grep-file dist/result.json 'hello browser bravo'`
78+
79+
the rerun's JSON report contains the modified imported value
80+
81+
```
82+
dist/result.json: found "hello browser bravo"
83+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const greeting = 'hello browser alpha';
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { expect, test } from 'vitest';
2+
import { page, server } from 'vitest/browser';
3+
import { greeting } from './greeting.js';
4+
5+
test(greeting, async () => {
6+
document.body.innerHTML = `<h1>${greeting}</h1>`;
7+
8+
await expect.element(page.getByRole('heading')).toHaveTextContent(greeting);
9+
expect(server.browser).toBe('chromium');
10+
expect(server.provider).toBe('playwright');
11+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"tasks": {
3+
"test": {
4+
"command": "vitest run",
5+
"cache": true,
6+
"input": [{ "auto": true }],
7+
// TODO: Add @voidzero-dev/vite-task-client and have Vitest report this path as ignored.
8+
"output": [{ "auto": true }, "!node_modules/.vite/vitest/**"],
9+
"untrackedEnv": ["PLAYWRIGHT_HOST_PLATFORM_OVERRIDE"]
10+
}
11+
}
12+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { playwright } from '@vitest/browser-playwright';
2+
import { defineConfig } from 'vitest/config';
3+
import { DefaultReporter } from 'vitest/node';
4+
5+
class NoTestSummaryReporter extends DefaultReporter {
6+
reportTestSummary() {}
7+
}
8+
9+
export default defineConfig({
10+
test: {
11+
reporters: [new NoTestSummaryReporter({ summary: false }), 'json'],
12+
outputFile: { json: 'dist/result.json' },
13+
browser: {
14+
enabled: true,
15+
headless: true,
16+
provider: playwright(),
17+
instances: [{ browser: 'chromium' }],
18+
},
19+
},
20+
});

crates/vite_task_bin/tests/e2e_snapshots/main.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@ use vite_str::Str;
1818
use vite_workspace::find_workspace_root;
1919

2020
/// Timeout for each step in e2e tests.
21-
/// Windows CI needs a longer timeout due to Git Bash startup overhead and slower I/O.
21+
/// Windows CI and `x86_64` macOS CI need longer for process and browser startup.
2222
const STEP_TIMEOUT: Duration =
23-
if cfg!(windows) { Duration::from_secs(60) } else { Duration::from_secs(20) };
23+
if cfg!(any(windows, all(target_os = "macos", target_arch = "x86_64"))) {
24+
Duration::from_secs(60)
25+
} else {
26+
Duration::from_secs(20)
27+
};
2428

2529
/// Screen size for the PTY terminal. Large enough to avoid line wrapping.
2630
const SCREEN_SIZE: ScreenSize = ScreenSize { rows: 500, cols: 500 };
@@ -226,8 +230,8 @@ struct E2e {
226230
#[serde(default)]
227231
pub cwd: RelativePathBuf,
228232
pub steps: Vec<Step>,
229-
/// Optional platform filter: "unix", "linux", "linux-gnu", "macos", or
230-
/// "windows". If set, test only runs on that platform.
233+
/// Optional platform filter: "unix", "linux", "linux-gnu", "non-musl",
234+
/// "macos", or "windows". If set, test only runs on that platform.
231235
#[serde(default)]
232236
pub platform: Option<Str>,
233237
/// When true, the generated libtest-mimic trial is marked `#[ignore]`
@@ -444,6 +448,11 @@ fn run_case(
444448
// by vt100's `Screen::contents()` when the snapshot is rendered,
445449
// so this does not introduce colour-noise into existing snapshots.
446450
cmd.env("TERM", "xterm-256color");
451+
// The macOS x64 CI shard needs Playwright to select an x64 browser
452+
// while running under Rosetta on Apple Silicon.
453+
if let Some(value) = env::var_os("PLAYWRIGHT_HOST_PLATFORM_OVERRIDE") {
454+
cmd.env("PLAYWRIGHT_HOST_PLATFORM_OVERRIDE", value);
455+
}
447456
// On Windows, ensure common executable extensions are included in PATHEXT for command resolution in subprocesses.
448457
if cfg!(windows) {
449458
cmd.env("PATHEXT", ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC");
@@ -662,6 +671,9 @@ fn main() {
662671
// spawned children, which breaks fixtures that
663672
// depend on interposer ordering.
664673
"linux-gnu" => cfg!(target_os = "linux") && !cfg!(target_env = "musl"),
674+
// Playwright's bundled browser binaries do not
675+
// support musl targets.
676+
"non-musl" => !cfg!(target_env = "musl"),
665677
other => panic!("Unknown platform '{}' in test '{}'", other, e2e.name),
666678
};
667679
if !should_run {

0 commit comments

Comments
 (0)