AgentSkillsCN

e11y-tdd

适用于 Phoenix LiveView 功能的实现——采用 TDD 方法,结合 html_snapshot 进行状态检查,同时利用 Pa11y 确保无障碍性。适时插入快照,查看渲染结果,用毕及时删除。

SKILL.md
--- frontmatter
name: e11y-tdd
description: Use when implementing Phoenix LiveView features - TDD with html_snapshot for state inspection and Pa11y for accessibility. Sprinkle snapshots to see what's rendered, delete when done.

Excessibility TDD - Build with Inspection

Build Phoenix LiveView features with full visibility into rendered HTML and state.

Core Powers

  • html_snapshot(view) - Capture HTML at any point (sprinkle liberally while building)
  • Pa11y checks - Ensure accessibility (WCAG compliance)
  • Timeline analysis - See state evolution across events

The e11y-TDD Cycle

code
1. EXPLORE   - Add html_snapshot(view) calls to see what's rendered
2. RED       - Write test with snapshot at key moment
3. GREEN     - Implement feature, use snapshots to debug
4. CHECK     - Run `mix excessibility` for Pa11y/a11y validation
5. CLEAN     - Remove temporary snapshots, keep essential ones

Snapshot Strategies

Temporary Snapshots (for building/debugging)

Sprinkle these while building. Delete when feature works.

elixir
test "building new feature" do
  {:ok, view, _html} = live(conn, "/page")

  # Sprinkle these to see what's happening
  html_snapshot(view)  # <- see initial state

  view |> element("button") |> render_click()
  html_snapshot(view)  # <- see after click

  view |> form("#my-form") |> render_submit(%{name: "test"})
  html_snapshot(view)  # <- see after submit

  # Delete these when feature works
end

Permanent Snapshots (for regression testing)

Keep these - Pa11y will check them on every run.

elixir
test "feature works and is accessible" do
  {:ok, view, _html} = live(conn, "/page")
  view |> element("button") |> render_click()

  # Keep this - Pa11y will check it on every run
  html_snapshot(view)
end

When to Use

  • Building any LiveView feature - snapshots show you what's rendered
  • Debugging state issues - sprinkle snapshots, inspect, delete
  • Accessibility compliance - Pa11y catches WCAG violations
  • Form implementations - see validation errors, field states
  • Modals/dialogs - verify focus management, aria attributes
  • Dynamic content - check aria-live regions render correctly

Commands

bash
# Run tests (generates snapshots)
mix test test/my_live_view_test.exs

# Check accessibility on all snapshots
mix excessibility

# Run specific test then check its snapshots
mix excessibility test/my_live_view_test.exs

# Debug with timeline analysis
mix excessibility.debug test/my_live_view_test.exs

Reading Snapshots

After running tests, check:

code
test/excessibility/
  html_snapshots/           # HTML files from html_snapshot() calls
    MyApp_PageTest_42.html  # Module_Line.html naming
  timeline.json             # State evolution (if using debug)

Open HTML files in browser to see exactly what was rendered.

Common Patterns

Form with Validation

elixir
test "form shows validation errors accessibly" do
  {:ok, view, _html} = live(conn, "/register")

  # Submit empty form
  view |> form("#register-form") |> render_submit(%{})

  # Snapshot captures error state - Pa11y will check:
  # - Error messages are associated with inputs (aria-describedby)
  # - Required fields are marked (aria-required)
  # - Invalid fields have aria-invalid
  html_snapshot(view)
end

Modal/Dialog

elixir
test "modal is accessible" do
  {:ok, view, _html} = live(conn, "/page")

  # Open modal
  view |> element("#open-modal") |> render_click()

  # Snapshot captures modal state - Pa11y will check:
  # - role="dialog" or aria-modal
  # - aria-labelledby for title
  # - Focus trapped inside modal
  html_snapshot(view)
end

Loading States

elixir
test "loading state is accessible" do
  {:ok, view, _html} = live(conn, "/dashboard")

  # Trigger async load
  view |> element("#refresh") |> render_click()

  # Snapshot during loading - Pa11y will check:
  # - aria-busy on loading container
  # - Loading indicator has appropriate role
  html_snapshot(view)
end

Debugging Tips

  1. Too much output? Use named snapshots:

    elixir
    html_snapshot(view, name: "after_click")
    html_snapshot(view, name: "with_errors")
    
  2. Need to see assigns/state? Use debug mode:

    bash
    mix excessibility.debug test/my_test.exs
    
  3. Pa11y error unclear? Check the snapshot HTML directly:

    bash
    open test/excessibility/html_snapshots/MyModule_42.html
    
  4. Multiple snapshots per test? They're numbered:

    code
    MyModule_42_1.html
    MyModule_42_2.html
    

Integration with superpowers

This skill works well with:

  • test-driven-development - TDD discipline for implementation
  • systematic-debugging - When Pa11y errors are unclear
  • verification-before-completion - Verify Pa11y passes before claiming done