docs/development-guide/testing.md
:::tip TLDR
run just test-update to run all rust and node.js tests and update snapshots automatically
:::
We have two groups of test suites: one for Rust, and one for Node.js.
:::warning Test principle you should respect
Here are some details about how to choose a test technique details :::
just test for running all tests.just test-update for running all tests and updating snapshots automaticallyjust test-rust for running all Rust tests.just test-node for running all Node.js tests.just test-node-rolldown for running only Rolldown's Node.js tests.just test-node-rollup for running only Rollup's tests.Testing is a crucial part of Rolldown's development process. It helps us ensure the correctness, stability, and performance of the bundler as we add new features and make changes.
Due to the nature of Rolldown being a bundler, we prefer integration tests that cover end-to-end scenarios, rather than unit tests that test individual components in isolation. This allows us to verify that the entire bundling process works as expected, from input files to output bundles.
Generally, there are two types of tests we use:
We use Rust's built-in test framework for writing and running tests. Test cases are stored in the crates/rolldown/tests folder.
A data-driven test case is a folder that contains a _config.json file. The test runner will read the configuration from _config.json, bundle the input files, and execute the output files to verify the behavior.
_config.json contains the configuration for the test suite. If everything works right, you should be able to have auto-completion while editing _config.json due to the config.
For all available options, you could refer to
It generates snapshots of the build artifacts, including:
If _test.mjs doesn't exist, run the output files in Node.js environment to verify the runtime behavior. You might think of it as running node --import ./dist/entry1.mjs --import ./dist/entry2.mjs --import ./dist/entry3.mjs --eval "".
Run _test.mjs if exists to verify more complex behaviors.
_config.json has its limitations, so we also support writing tests with Rust directly. You could refer to
crates/rolldown/tests/rolldown/errors/plugin_error
It basically just replaces the _config.json with Rust code that configures the bundler directly. Everything else works the same way as data-driven testing.
Rolldown also runs tests derived from esbuild's bundler test suite to verify compatibility. These tests are located in crates/rolldown/tests/esbuild.
The scripts directory contains utilities for managing esbuild tests:
gen-esbuild-tests - Generates test cases from esbuild's Go test files.
esbuild-snap-diff - Compares Rolldown's output snapshots against esbuild's expected output. It generates diff reports and compatibility statistics, helping track how closely Rolldown's behavior matches esbuild.
The script generates summary markdown files in scripts/src/esbuild-tests/snap-diff/summary/ and overall statistics in scripts/src/esbuild-tests/snap-diff/stats/stats.md.
Test cases can be skipped by prefixing the folder name with . (e.g., .test_case_name). Skipped tests must have documented reasons in scripts/src/esbuild-tests/reasons.ts.
If a test case folder contains any files named *.hmr-*.js, the test will run in HMR enabled mode.
*.hmr-*.js are called HMR edit files.hmr- indicates the step number of the change. For example, main.hmr-1.js means a change applied in step 1..hmr-1.js are used to overwrite the corresponding files in the temporary directory, and an HMR patch is generated.*.hmr-2.js, *.hmr-3.js, etc., are applied step by step.:::details Example
If the test folder has these files:
main.jssub.jsmain.hmr-1.jssub.hmr-1.jssub2.hmr-2.jsThe test will go through these steps:
main.js, sub.jsmain.js is replaced with main.hmr-1.jssub.js is replaced with sub.hmr-1.jsmain.js and sub.js remain as in Step 1sub2.js is added using the contents of sub2.hmr-2.js:::
For more complex scenarios that cannot be easily expressed with data-driven approaches, we write manual test code that sets up the test environment, runs the bundler with specific options, and verifies the output programmatically.
Not much to tell here, basically just write normal Rust test code that uses Rolldown to perform bundling and verification.
Rolldown integrates the test262 test suite to verify ECMAScript specification compliance. Only the test cases under test/language/module-code are run because other test cases should be covered on Oxc side.
The git submodule should have been initialized after running just setup when setting up the project, but you should also run just update-submodule to update the submodule before running the integration tests.
You can run the test262 integration tests with the following command:
TEST262_FILTER="attribute" cargo test --test integration test262_module_code -- --no-capture
TEST262_FILTER allows you to filter tests by name (e.g., "attribute"). If you omit this environment variable, all test cases will be run. Note that the result snapshot will not be updated if the environment variable is set.--no-capture option displays all test output.The test cases that are expected to fail are listed in crates/rolldown/tests/test262_failures.json.
Rolldown uses Vitest for testing the Node.js side code.
Tests located in packages/rolldown/tests are used to test Rolldown's Node.js API (i.e. the API of the rolldown package published on NPM).
just test-node-rolldown will run rolldown tests.just test-node-rolldown --update will run tests and update snapshots.Data-driven tests are located in packages/rolldown/tests/fixtures.
A data-driven test case is a folder that contains a _config.ts file. The test runner will read the configuration from _config.ts, bundle the input files, and verify the output against expected results.
Not much to tell here either, basically just write normal JavaScript/TypeScript test code that uses Rolldown to perform bundling and verification.
To run tests of the specific file, you could use
just test-node-rolldown test-file-name
For example, to run tests in fixture.test.ts, you could use just test-node-rolldown fixture.
To run specific test, you could use
just test-node-rolldown -t test-name
Names of tests in fixture.test.ts are defined with their folder names. tests/fixtures/resolve/alias will has test name resolve/alias.
To run the tests/fixtures/resolve/alias test, you could use just test-node-rolldown -t resolve/alias.
:::info
just test-node-rolldown -t aaa bbb is different from just test-node-rolldown -t "aaa bbb". The former will run tests that either contains aaa or bbb, while the latter will run tests, whose name contain aaa bbb.
For more advanced usage, please refer to https://vitest.dev/guide/filtering.
:::
We also aim for behavior alignment with Rollup by running Rollup's own tests against Rolldown.
To achieve this, each test case in packages/rollup-tests/test proxies to the corresponding test in the rollup git submodule in project root.
The git submodule should have been initialized after running just setup when setting up the project, but you should also run just update-submodule to update the submodule before running the Rollup tests.
In /packages/rollup-tests:
just test-node-rollup will run rollup tests.just test-node-rollup --update will run and update the tests' status.To run a specific test, use the --grep option with just test-node-rollup:
just test-node-rollup --grep "function"
This will run only tests whose names match "function". For more filtering options, see Mocha's grep documentation.
Our Rust test infra is powerful enough to cover most of the case of JavaScript (plugin, passing function inside config). But since JavaScript side user is still our first class user, try to put tests in JavaScript side if possible. Here are some experience about what test technique you should use. :::tip TLDR Add test in JavaScript side if you don't want to wasting time on deciding which way to use. :::
configVariants you could do that with only one test.Any category not mentioned above should put in JavaScript side.