--- name: testing-codegen description: Guide for testing workflows and code generation commands in Biome. Use when running tests, managing snapshots, creating changesets, or generating code. Examples:User needs to run snapshot tests for a lint ruleUser wants to create a changeset for a PRUser needs to regenerate analyzer code after changes --- ## 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 ```shell # 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: ```rust // 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: ```shell 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: ```rust // In crates/biome_html_parser/tests/quick_test.rs // Modify the quick_test function: #[test] pub fn quick_test() { let code = r#""#; 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: ```shell 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: ```shell cargo test ``` Review generated/changed snapshots: ```shell # 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 ```shell # 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: ```jsonc // 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`: ```json { "linter": { "rules": { "nursery": { "noVar": { "level": "error", "options": { "someOption": "value" } } } } } } ``` ### Code Generation Commands **After modifying analyzers/lint rules:** ```shell just gen-analyzer ``` This updates: - Rule registrations - Configuration schemas - Documentation exports - TypeScript bindings **After modifying grammar (.ungram files):** ```shell # Specific language just gen-grammar html # Multiple languages just gen-grammar html css # All languages just gen-grammar ``` **After modifying formatters:** ```shell just gen-formatter html ``` **After modifying configuration:** ```shell just gen-bindings ``` Generates TypeScript types and JSON schema. **Full codegen (rarely needed):** ```shell just gen-all ``` **Before committing:** ```shell just ready ``` Runs full codegen + format + lint (takes time). Or run individually: ```shell just f # Format Rust and TOML just l # Lint code ``` ### Create Changeset For user-visible changes (bug fixes, new features): ```shell 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: ```markdown --- "@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: ```shell just test-doc ``` ### Debugging Tests Use `dbg!()` macro in Rust code: ```rust fn some_function() -> &'static str { let some_variable = "debug_value"; dbg!(&some_variable); // Prints during test some_variable } ``` Run with output: ```shell 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 ` 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 ```rust // 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 ` | | Lint rules in `*_analyze` | `just gen-analyzer` | | Formatter in `*_formatter` | `just gen-formatter ` | | 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