AgentSkillsCN

run-tests

运行Vitest测试套件,诊断常见失败模式并加以修复。在测试失败、新增功能后、修复Bug,或验证无回归现象时使用。涵盖TooltipProvider封装、aria-label查询、localStorage隔离、hook act()封装,以及卡牌查找错误。触发词:测试、测试失败、Vitest、运行测试、测试错误、修复测试、回归。

SKILL.md
--- frontmatter
name: run-tests
description: "Run the Vitest test suite, diagnose common failure patterns, and fix them. Use when tests are failing, after adding a new feature, fixing a bug, or verifying nothing regressed. Covers TooltipProvider wrapping, aria-label queries, localStorage isolation, hook act() wrapping, and card lookup errors. Trigger words: test, tests failing, vitest, run tests, test error, fix test, regression."
argument-hint: "Optional: specific test file or pattern (e.g., useGameState, Meter)"
user-invocable: true

Run Tests

When to Use

  • After adding a new hook action, component, or genre pack
  • After fixing a bug (verify the regression test passes)
  • When a CI check fails but you're not sure why
  • Before committing to confirm nothing regressed

Commands

bash
# Run all tests once
npm test

# Watch mode (re-runs on save)
npm run test:watch

# Single file
npm test -- src/hooks/useGameState.test.ts

# With verbose output
npm test -- --reporter=verbose

# With coverage
npm run test:coverage

Diagnose Failures Automatically

bash
node .github/skills/run-tests/scripts/diagnose-failures.cjs

Outputs ✅/❌ per failure with pattern-matched hints for the most common causes.

Common Failure Patterns and Fixes

1. Missing TooltipProvider wrapper

Symptom:

code
Warning: Missing TooltipProvider

or a component rendering incorrectly without tooltip content.

Fix: Wrap in withTooltip() helper at the top of the test file:

typescript
import { TooltipProvider } from '@/components/ui/tooltip';

function withTooltip(ui: React.ReactElement) {
  return render(<TooltipProvider>{ui}</TooltipProvider>);
}

// Usage in test:
withTooltip(<Meter label="Tension" value={5} max={10} type="tension" onAdjust={vi.fn()} tooltipText="" />);

Apply withTooltip() to: Meter, GameCard (compact mode), and any widget that uses <Tooltip>.

2. Icon-only button not found

Symptom:

code
TestingLibraryElementError: Unable to find an accessible element with the role "button" and name /\+/

Cause: The button renders a Lucide SVG icon — no text content exists.

Fix: Query by aria-label:

typescript
// ❌ Wrong
screen.getByRole("button", { name: /\+/ });
screen.getByRole("button", { name: /−|-/ });

// ✅ Correct
screen.getByRole("button", { name: /increase tension/i });
screen.getByRole("button", { name: /decrease coherence/i });

The Meter component sets aria-label="Increase <Label>" / "Decrease <Label>".

3. State update outside act()

Symptom:

code
Warning: An update to ... inside a test was not wrapped in act(...)

Fix: Always wrap hook action calls:

typescript
// ❌ Wrong
result.current.adjustMeter("tension", 1);

// ✅ Correct
act(() => result.current.adjustMeter("tension", 1));

4. resetGame localStorage assertion failing

Symptom: Test expects phase: 'setup' but gets a string in localStorage.

Fix: Assert on state fields, not localStorage JSON:

typescript
// ❌ Fragile
expect(localStorage.getItem("noir-narrative-game")).toBeNull();

// ✅ Correct
expect(result.current.gameState.phase).toBe("setup");
expect(result.current.gameState.players).toHaveLength(0);

5. Card not found in player's hand

Symptom:

code
Error: Card "Resolve" not found in player 0's hand

Cause: The card might not be in the hand yet, or the name is wrong (names are case-sensitive).

Fix: Use the findCard helper and check that the card name matches the content exactly:

typescript
const card = findCard(result, 0, "Resolve"); // exact name from gameContent.ts
if (!card) throw new Error("Card not found — check gameContent.ts for name");

6. Hook test timing / async

If a hook action triggers async behaviour (future feature), wrap in await act(async () => ...). Currently all actions are synchronous.

Test File Map

What brokeTest file to check
Game rule / meter / fact / scenesrc/hooks/useGameState.test.ts
Card renders / buttons / modalssrc/components/game/components.test.tsx
Genre pack missing contentsrc/data/gameContent.test.ts

Adding a Regression Test

When fixing a bug, always add a test first that fails before the fix:

typescript
it("does not silently skip draw when deck is empty", () => {
  // arrange: exhaust the deck
  // act: trigger a draw
  // assert: player hand maintained (no silent skip)
});

After the fix, the test must pass. Commit both together.