testing-codegen
npx skills add https://github.com/biomejs/biome --skill testing-codegen
Agent 安装分布
Skill 文档
Purpose
Use this skill for testing, code generation, and preparing contributions. Covers snapshot testing with insta, code generation commands, and changeset creation.
Prerequisites
- Install required tools:
just install-tools(installscargo-insta) - Install pnpm:
corepack enableandpnpm installin repo root - 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.,
HtmlAttributevsSvelteBindDirective) - Whether values use
HtmlString(quotes) orHtmlTextExpression(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 snapshotr– reject snapshots– skip snapshotq– 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:
- Package selection: Usually
@biomejs/biome - Change type:
patch– Bug fixesminor– New featuresmajor– Breaking changes (requires targetingnextbranch)
- 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.jsandinvalid.jsfiles - Options per folder:
options.jsonapplies to all tests in that folder .jsoncarrays: Use for multiple quick test cases in script context (no imports/exports)- Code generation order: Grammar â Analyzer â Formatter â Bindings
- CI compatibility: Use
justcommands 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 withcargo 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, nottext_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