testing-codegen

📁 biomejs/biome 📅 10 days ago
24
总安装量
23
周安装量
#15193
全站排名
安装命令
npx skills add https://github.com/biomejs/biome --skill testing-codegen

Agent 安装分布

opencode 23
github-copilot 23
codex 23
kimi-cli 23
gemini-cli 23
amp 23

Skill 文档

Purpose

Use this skill for testing, code generation, and preparing contributions. Covers snapshot testing with insta, code generation commands, and changeset creation.

Prerequisites

  1. Install required tools: just install-tools (installs cargo-insta)
  2. Install pnpm: corepack enable and pnpm install in repo root
  3. Understand which changes require code generation

Common Workflows

Run Tests

# Run all tests
cargo test

# Run tests for specific crate
cd crates/biome_js_analyze
cargo test

# Run specific test
cargo test quick_test

# Show test output (for dbg! macros)
cargo test quick_test -- --show-output

# Run tests with just (uses CI test runner)
just test

# Test specific crate with just
just test-crate biome_cli

Quick Test for Rules

Fast iteration during development:

// In crates/biome_js_analyze/tests/quick_test.rs
// Modify the quick_test function:

const SOURCE: &str = r#"
const x = 1;
var y = 2;
"#;

let rule_filter = RuleFilter::Rule("nursery", "noVar");

Run:

just qt biome_js_analyze

Quick Test for Parser Development

IMPORTANT: Use this instead of building full Biome binary for syntax inspection – it’s much faster!

For inspecting AST structure when implementing parsers or working with embedded languages:

// In crates/biome_html_parser/tests/quick_test.rs
// Modify the quick_test function:

#[test]
pub fn quick_test() {
    let code = r#"<button on:click={handleClick}>Click</button>"#;
    
    let source_type = HtmlFileSource::svelte();
    let options = HtmlParseOptions::from(&source_type);
    let root = parse_html(code, options);
    let syntax = root.syntax();
    
    dbg!(&syntax, root.diagnostics(), root.has_errors());
}

Run:

just qt biome_html_parser

The dbg! output shows the full AST tree structure, helping you understand:

  • How directives/attributes are parsed (e.g., HtmlAttribute vs SvelteBindDirective)
  • Whether values use HtmlString (quotes) or HtmlTextExpression (curly braces)
  • Token ranges and offsets needed for proper snippet creation
  • Node hierarchy and parent-child relationships

Snapshot Testing with Insta

Run tests and generate snapshots:

cargo test

Review generated/changed snapshots:

# Interactive review (recommended)
cargo insta review

# Accept all changes
cargo insta accept

# Reject all changes
cargo insta reject

# Review for specific test
cargo insta review --test-runner nextest

Snapshot commands:

  • a – accept snapshot
  • r – reject snapshot
  • s – skip snapshot
  • q – quit

Test Lint Rules

# Test specific rule by name
just test-lintrule noVar

# Run from analyzer crate
cd crates/biome_js_analyze
cargo test

Create Test Files

Single file tests – Place in tests/specs/{group}/{rule}/:

tests/specs/nursery/noVar/
├── invalid.js           # Code that triggers the rule
├── valid.js             # Code that doesn't trigger
└── options.json         # Optional: rule configuration

Multiple test cases – Use .jsonc files with arrays:

// tests/specs/nursery/noVar/invalid.jsonc
[
  "var x = 1;",
  "var y = 2; var z = 3;",
  "for (var i = 0; i < 10; i++) {}"
]

Test-specific options – Create options.json:

{
  "linter": {
    "rules": {
      "nursery": {
        "noVar": {
          "level": "error",
          "options": {
            "someOption": "value"
          }
        }
      }
    }
  }
}

Code Generation Commands

After modifying analyzers/lint rules:

just gen-analyzer

This updates:

  • Rule registrations
  • Configuration schemas
  • Documentation exports
  • TypeScript bindings

After modifying grammar (.ungram files):

# Specific language
just gen-grammar html

# Multiple languages
just gen-grammar html css

# All languages
just gen-grammar

After modifying formatters:

just gen-formatter html

After modifying configuration:

just gen-bindings

Generates TypeScript types and JSON schema.

Full codegen (rarely needed):

just gen-all

Before committing:

just ready

Runs full codegen + format + lint (takes time).

Or run individually:

just f  # Format Rust and TOML
just l  # Lint code

Create Changeset

For user-visible changes (bug fixes, new features):

just new-changeset

This prompts for:

  1. Package selection: Usually @biomejs/biome
  2. Change type:
    • patch – Bug fixes
    • minor – New features
    • major – Breaking changes (requires targeting next branch)
  3. Description: What changed (used in CHANGELOG)

Changeset writing guidelines:

  • Be concise and clear (1-3 sentences)
  • Start bug fixes with: Fixed [#issue](link): ...
  • Use past tense for your actions: “Added”, “Fixed”, “Changed”
  • Use present tense for Biome behavior: “Biome now supports…”
  • Include code examples for new rules/features
  • Link to rules: [useConst](https://biomejs.dev/linter/rules/use-const/)
  • End sentences with periods

Example changeset:

---
"@biomejs/biome": patch
---

Fixed [#1234](https://github.com/biomejs/biome/issues/1234): The rule [`noVar`](https://biomejs.dev/linter/rules/no-var/) now correctly handles variables in for loops.

Biome now analyzes the scope of loop variables properly.

Edit changeset – Files created in .changeset/ directory, edit them directly.

Run Doctests

Test code examples in documentation comments:

just test-doc

Debugging Tests

Use dbg!() macro in Rust code:

fn some_function() -> &'static str {
    let some_variable = "debug_value";
    dbg!(&some_variable);  // Prints during test
    some_variable
}

Run with output:

cargo test test_name -- --show-output

Tips

  • Snapshot organization: Group by feature/rule in separate directories
  • Test both valid and invalid: Create both valid.js and invalid.js files
  • Options per folder: options.json applies to all tests in that folder
  • .jsonc arrays: Use for multiple quick test cases in script context (no imports/exports)
  • Code generation order: Grammar → Analyzer → Formatter → Bindings
  • CI compatibility: Use just commands when possible (matches CI)
  • Changeset timing: Create before opening PR, can edit after
  • Snapshot review: Always review snapshots carefully – don’t blindly accept
  • Test performance: Use #[ignore] for slow tests, run with cargo test -- --ignored
  • Parser inspection: Use just qt <package> to run quick_test and inspect AST, NOT full Biome builds (much faster)
  • String extraction: Use inner_string_text() for quoted strings, not text_trimmed() (which includes quotes)
  • Legacy syntax: Ask users before implementing deprecated/legacy syntax – wait for user demand
  • Borrow checker: Avoid temporary borrows that get dropped – use let binding = value; binding.method() pattern

Common Test Patterns

// Snapshot test in rule file
#[test]
fn test_rule() {
    assert_lint_rule! {
        noVar,
        invalid => [
            "var x = 1;",
            "var y = 2;",
        ],
        valid => [
            "const x = 1;",
            "let y = 2;",
        ]
    }
}

// Quick test pattern
#[test]
#[ignore]  // Uncomment when using
fn quick_test() {
    const SOURCE: &str = r#"
        var x = 1;
    "#;
    
    let rule_filter = RuleFilter::Rule("nursery", "noVar");
    // Test runs with this configuration
}

Code Generation Dependencies

When you modify… Run…
.ungram grammar files just gen-grammar <lang>
Lint rules in *_analyze just gen-analyzer
Formatter in *_formatter just gen-formatter <lang>
Configuration types just gen-bindings
Before committing just f && just l
Full rebuild just gen-all (slow)

References

  • Main testing guide: CONTRIBUTING.md § Testing
  • Insta documentation: https://insta.rs
  • Analyzer testing: crates/biome_analyze/CONTRIBUTING.md § Testing
  • Changeset guide: CONTRIBUTING.md § Changelog