fix(test): isolate jsdom storage on Node 26

This commit is contained in:
Turtle
2026-07-30 21:49:33 +08:00
parent 5335d61d11
commit f04b35c6a4
15 changed files with 116 additions and 10 deletions

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/testing/2026-07-30-vitest-jsdom-webstorage-ownership.md
2026-07-30-vitest-jsdom-webstorage-ownership.md: 3956a7566fa1c79a767636bce9a19f16588126e2
2026-07-30-vitest-jsdom-webstorage-ownership.zh.md: 9080ee2762b74bf2efdaccd7a5905672001bc0e8

View File

@@ -0,0 +1,26 @@
# Agent Note: Keep browser storage owned by jsdom in Vitest
Status: implemented
English | [中文](2026-07-30-vitest-jsdom-webstorage-ownership.zh.md)
## Problem
The supported Node range includes releases that reserve a process-wide `globalThis.localStorage`. Node 26 exposes that property as `undefined` without `--localstorage-file`; Vitest sees the reserved key and does not project jsdom's isolated `Storage` object over it. Component suites then fail before exercising product behavior, while the primary Node 24 coverage lane remains green because that runtime does not reserve the key by default.
## Decision
Vitest workers disable Node's process-wide Web Storage when the runtime advertises the `--webstorage` flag. The configuration passes `--no-webstorage` through each test project's `execArgv`; runtimes without that flag receive no argument. Node-environment suites therefore stay browser-free, and files selecting jsdom through `@vitest-environment jsdom` receive jsdom's isolated `localStorage`.
The Node compatibility aggregate runs a dedicated jsdom smoke on every advertised compatibility line. It asserts both the conditional worker argument and usable storage, so a future Node or Vitest change cannot leave the primary Node 24 suite as the only signal.
## Alternatives considered
- **Set `NODE_OPTIONS=--no-webstorage` in package scripts or CI.** Rejected because it leaks test-runner policy into subprocesses and misses direct `pnpm exec vitest` invocations.
- **Pass `--localstorage-file` to Node.** Rejected because one process-wide persistent store has different ownership and isolation semantics from browser storage created per jsdom environment.
- **Patch `globalThis.localStorage` in setup code or guard every component test.** Rejected because setup would depend on Vitest's private jsdom projection details, while per-test guards hide a broken browser environment and duplicate policy across suites.
- **Pin tests to Node 24.** Rejected because the package engine advertises newer even Node lines and the compatibility matrix exists to expose their runtime changes.
## Consequences
The same `pnpm test` command works on Node releases with and without built-in Web Storage. Test workers deliberately cannot exercise Node's process-wide Web Storage; a future product need for that API requires a separate explicit test configuration rather than weakening jsdom isolation. The compatibility lane adds one focused Vitest process instead of duplicating the complete unit inventory on every Node version.

View File

@@ -0,0 +1,26 @@
# Agent Note: 在 Vitest 中将浏览器存储交由 jsdom 管理
Status: implemented
[English](2026-07-30-vitest-jsdom-webstorage-ownership.md) | 中文
## 问题
受支持的 Node 版本范围包含会预留进程级 `globalThis.localStorage` 的版本。未设置 `--localstorage-file`Node 26 将该属性暴露为 `undefined`Vitest 检测到这个预留键后,不会用 jsdom 的隔离 `Storage` 对象覆盖该属性。因此,组件测试套件尚未验证产品行为便会失败,而主要的 Node 24 覆盖率分支仍能通过,因为该运行时默认不会预留此键。
## 决策
当运行时声明支持 `--webstorage` 标志时Vitest worker 会禁用 Node 的进程级 Web Storage。配置通过每个测试项目的 `execArgv` 传入 `--no-webstorage`未声明该标志的运行时则不传入此参数。因此Node 环境测试套件不加载浏览器环境,而通过 `@vitest-environment jsdom` 选择 jsdom 的文件会获得 jsdom 隔离的 `localStorage`
Node 兼容性汇总任务会在每条声明支持的兼容版本线上运行专用的 jsdom 冒烟测试。该测试同时断言 worker 参数按条件传入且存储可用,因此未来 Node 或 Vitest 的变化不会让主要的 Node 24 测试套件成为唯一检测信号。
## 曾考虑的替代方案
- **在包脚本或 CI 中设置 `NODE_OPTIONS=--no-webstorage`。** 否决:这会将测试运行器策略传播到子进程,也无法覆盖直接调用 `pnpm exec vitest` 的情况。
- **向 Node 传入 `--localstorage-file`。** 否决:单个进程级持久化存储与每个 jsdom 环境分别创建的浏览器存储具有不同的归属和隔离语义。
- **在初始化代码中修改 `globalThis.localStorage`,或为每个组件测试增加保护逻辑。** 否决:初始化逻辑会依赖 Vitest 私有的 jsdom 映射细节,而逐测试添加的保护逻辑会掩盖浏览器环境损坏,并在多个测试套件中重复该策略。
- **将测试固定在 Node 24。** 否决:包的引擎范围声明支持更新的偶数 Node 版本线,而兼容性矩阵正是为了暴露这些版本的运行时变化。
## 后果
同一条 `pnpm test` 命令在有无内置 Web Storage 的 Node 版本上均可运行。测试 worker 被有意禁止使用 Node 的进程级 Web Storage未来若产品需要该 API必须使用独立且显式的测试配置而不能削弱 jsdom 隔离。兼容性分支只增加一个专项 Vitest 进程,无需在每个 Node 版本上重复整套单元测试。