Repository navigation
Open
Description
Describe the bug
With more than one browser.instances entry, a test that fails in every instance saves its failure screenshot to the same file in each one:
.vitest/attachments/failure-screenshots/<test file>/<test name>.png
The path has no browser or project name in it (packages/browser/src/client/tester/runner.ts, onTaskFinished). Then resolveTestAttachment copies that file to <attachmentsDir>/<sha1 of the path>.png, which is the same target for every instance too.
This causes two problems:
- The instances overwrite each other's screenshot. With 12 tests failing in Chromium and WebKit,
.vitest/attachmentsholds 12 failure screenshots and 12 copies, not 24 of each. Each instance's report links the same file, which shows whichever browser wrote it last. - When both instances fail the same test at about the same moment, one instance rewrites the screenshot while the other's
copyFileis reading it. Sometimes the copy never finishes: one node process sits at 100% CPU with the screenshot open for read and the attachment open for write, andvitest runnever exits. On macOS, about 3 in 10 runs of the repro below hung. WithscreenshotFailures: false, or with one instance, 0 in 10 hung.
Putting the instance's name in the path would fix both, for example failure-screenshots/<browser>/<file>/<test>.png.
Reproduction
package.json
{
"type": "module",
"devDependencies": {
"@vitest/browser-playwright": "5.1.0",
"playwright": "1.65.0",
"vitest": "5.1.0"
}
}vitest.config.js
import { playwright } from "@vitest/browser-playwright";
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
browser: {
enabled: true,
headless: true,
provider: playwright(),
instances: [{ browser: "chromium" }, { browser: "webkit" }],
},
},
});race.test.js
import { expect, test } from "vitest";
// Large, noisy PNGs widen the window in which both browsers write the same file.
for (let i = 0; i < 12; i++) {
test(`fails ${i}`, () => {
const canvas = document.createElement("canvas");
canvas.width = 1200;
canvas.height = 900;
const context = canvas.getContext("2d");
const pixels = context.createImageData(canvas.width, canvas.height);
for (let p = 0; p < pixels.data.length; p++) pixels.data[p] = Math.random() * 256;
context.putImageData(pixels, 0, 0);
document.body.replaceChildren(canvas);
expect(i).toBe(-1);
});
}Steps:
npm install && npx playwright install chromium webkit- Run
rm -rf .vitest && npx vitest runseveral times. - Each run should fail in about 3 seconds. Some runs never exit.
- In a run that finishes,
find .vitest -type f | wc -lprints 24 for 24 failures: 12 screenshots and 12 copies.
While a run hangs, ps and lsof show:
PID %CPU ELAPSED COMMAND
12386 99.3 00:20 node (vitest run)
node 12386 45r 1106816 .vitest/attachments/failure-screenshots/race.test.js/fails-10.png
node 12386 46w 1106816 .vitest/attachments/b534c997898759bb19dbabfc6f6bb8d3a08a14b6.png
A sample of the stuck process shows a libuv worker looping in uv__fs_work (pread, sendfile).
System Info
OS: macOS 27.0.1, arm64
Node: 26.13.0
vitest: 5.1.0
@vitest/browser-playwright: 5.1.0
playwright: 1.65.0
Used Package Manager
pnpm
Validations
- Follow our Code of Conduct
- Read the Contributing Guidelines.
- Read the docs.
- Check that there isn't already an issue that reports the same bug, to avoid creating a duplicate.
- The provided reproduction is a minimal reproducible example of the bug.
Metadata
Metadata
Assignees
Labels
No labels
Activity