一键导入
testing
Vitest mock patterns (vi.mock, vi.hoisted, vi.fn reset), assertion strategy, and TDD planning rules. Load when writing or debugging tests.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Vitest mock patterns (vi.mock, vi.hoisted, vi.fn reset), assertion strategy, and TDD planning rules. Load when writing or debugging tests.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | testing |
| description | Vitest mock patterns (vi.mock, vi.hoisted, vi.fn reset), assertion strategy, and TDD planning rules. Load when writing or debugging tests. |
Load this skill when writing, debugging, or planning tests.
*.test.ts and live under test/, not next to source.node:assert/strict for assertions and vitest's test runner (onTestFinished for per-test cleanup).globalThis.fetch for OAuth flows; build payload fixtures inline rather than depending on Pi internals.#src/ and #test/ aliases (e.g., #src/constants)./^You are an expert coding assistant\./, /# Project Context/) over deep-equal on full prompt strings, so tests survive harmless reformatting upstream.pnpm test (vitest run).pnpm run test:watch.pnpm exec vitest run <test-path>.pnpm run check (tsc --noEmit).These apply when a test does reach for vi.mock() / vi.fn() rather than the inline-fixture style above.
vi.mock(), extract each vi.fn() stub to a module-scope variable and reset it in beforeEach — vi.restoreAllMocks() only operates on vi.spyOn() spies, not on vi.fn() instances.
Use .mockReset() when the stub has no default implementation.
Use .mockClear() when the vi.mock() factory provides a default implementation that tests must preserve.vi.mock() factory references a module-scope vi.fn() stub, wrap the stub declaration in vi.hoisted() — Vitest hoists vi.mock() above normal declarations, so unhoisted variables are undefined when the factory runs.vi.fn() factory returns an empty array or narrow literal, annotate its return type explicitly — vi.fn((): string[] => []), not vi.fn(() => []).
Without the annotation TypeScript infers never[], and subsequent mockReturnValueOnce([...]) calls fail with “not assignable to never”.Mock<specific-signature> — e.g., Mock<() => void>, Mock<(arg: string) => Promise<void>>.
Do not use ReturnType<typeof vi.fn> — in Vitest v4 it expands to Mock<Procedure | Constructable>, a union that TypeScript cannot call.vi.mock(), use vi.fn() with no implementation — not vi.fn(() => ({})).
Arrow-function implementations are not constructable; new MockClass() throws "is not a constructor".node:* built-in modules with vi.mock(), include a default key mirroring the named exports — omitting it causes "No default export defined on the mock" errors.setInterval, never use vi.runAllTimersAsync() — it loops infinitely.
Use vi.advanceTimersByTimeAsync(ms) with a specific duration instead.Mock<...> methods (mockResolvedValue, mock.calls, etc.) from the inferred type.
Leave the return type unannotated so callers retain full mock access.?? to supply defaults from an overrides object, explicit undefined values are swallowed.
Use "key" in overrides presence checks or Object.hasOwn(overrides, "key") for fields where undefined is a meaningful test value.process.env inside functions rather than capturing it as a module-level constant — vi.stubEnv() alone cannot change a constant already evaluated at import time.
If a module-level constant is unavoidable, test it with vi.resetModules() + await import(...) inside the test body, and call vi.unstubAllEnvs() + vi.resetModules() in cleanup.test.todo.
A real assertion documents the limitation and lets a future fix flip the expectation.test.fails to document the expected behavior and file a GitHub issue.void 0;, unused locals) in tests just to make an Edit tool's oldText unique — widen oldText with surrounding context instead.async function declared Promise<T> must signal a precondition failure, return Promise.reject(new Error(...)), not throw — a synchronous throw escapes expect(...).rejects.toThrow(...), and switching to async trips @typescript-eslint/require-await when the body has no await.prefer-nullish-coalescing flags ||, check whether the left side could be a falsy non-null value ("", 0, false) that the code intentionally converts to the fallback.
If so, keep || and add // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- || intentional: converts falsy values to fallback.
Do not mechanically replace || with ?? without verifying test expectations.pnpm run check immediately after that step's commit.src/ files that reference the removed field — every file that reads or passes the field must update in the same step.session_start), grep all test files for consumers that resolve it.
A timing change breaks them at runtime (the full suite), not at typecheck, so pnpm run check will not flag them.Use when searching or replacing code patterns - use ast-grep instead of text search for semantic accuracy
Navigate code with IDE features and run proactive LSP diagnostics on files/folders/batches. Use as PRIMARY for code intelligence and type/error checks.
Use when writing a new pi-lens ast-grep rule YAML file — covers schema, drop path, gotchas, and NAPI runner constraints
Use when writing a new pi-lens tree-sitter query rule YAML file — covers schema, S-expression syntax, capture names, predicates, and gotchas
Anthropic Claude Pro/Max OAuth compatibility workflow for this repo. Use when debugging Anthropic OAuth failures, misleading extra-usage errors, Pi request shaping, prompt fingerprinting, or deciding between hook-based fixes and deeper provider overrides.
Aggressive design-polish skill for making websites feel authored, editorial, image-led, and typographically intentional. Use when a page looks too generic, safe, same-font, SaaS-like, cluttered, or visually under-authored.