|
| 1 | +--- |
| 2 | +name: migrate-vitest-to-rstest |
| 3 | +description: Migrate or convert Vitest test suites and configs to Rstest. Use when asked to move from Vitest to Rstest, replace `vitest` imports with `@rstest/core`, translate Vitest config to `rstest.config.ts`, or update Vite test config to Rsbuild/Rstest equivalents. |
| 4 | +--- |
| 5 | + |
| 6 | +# Migrate Vitest to Rstest |
| 7 | + |
| 8 | +## Goal |
| 9 | + |
| 10 | +Migrate Vitest-based tests and configuration to Rstest with minimal behavior changes. Use the reference guide as the source of truth for exact API and config mappings. |
| 11 | + |
| 12 | +## Migration principles (must follow) |
| 13 | + |
| 14 | +1. **Smallest-change-first**: prefer the smallest viable change that restores test pass. |
| 15 | +2. **Config before code**: prefer fixing in config/tooling/mocks before touching test logic. |
| 16 | +3. **Do not change user source behavior**: avoid modifying production/business source files unless user explicitly requests it. |
| 17 | +4. **Avoid bulk test rewrites**: do not refactor entire test suites when a local compatibility patch can solve it. |
| 18 | +5. **Preserve test intent**: keep assertions and scenario coverage unchanged unless clearly broken by framework differences. |
| 19 | +6. **Defer Vitest removal**: keep `vitest` dependency and `vitest.config.*` during migration; remove them only after Rstest tests pass. |
| 20 | + |
| 21 | +## Workflow |
| 22 | + |
| 23 | +1. Inventory current Vitest usage |
| 24 | +2. **Open the official migration guide** |
| 25 | + - Use the Official guide as the source of truth: |
| 26 | + - https://rstest.rs/guide/migration/vitest.md |
| 27 | +3. Dependency install gate (blocker check) |
| 28 | +4. Migrate configuration and test files following the guide and reference details (minimal patch scope) |
| 29 | +5. Check type errors |
| 30 | +6. Run tests and fix deltas (apply the `providedExports` checklist below when mocks fail unexpectedly) |
| 31 | +7. Remove `vitest` dependency and legacy `vitest.config.*` only after Rstest is green |
| 32 | +8. Summarize changes |
| 33 | + |
| 34 | +## 1. Inventory current Vitest usage |
| 35 | + |
| 36 | +- Locate Vitest config (`vitest.config.*` or `vite.config.*` with a `test` block) and record the format. |
| 37 | +- Capture current test commands and test file patterns (include/exclude or globs). |
| 38 | +- Run test suite to verify current behavior. |
| 39 | +- List setup files, environment settings, and any globals usage. |
| 40 | +- Note any Vite config dependencies that tests rely on (aliases, plugins, or defines). |
| 41 | + |
| 42 | +## 3. Dependency install gate (blocker check) |
| 43 | + |
| 44 | +Before large-scale edits, verify dependencies can be installed and test runner binaries are available. |
| 45 | + |
| 46 | +### Goal |
| 47 | + |
| 48 | +Avoid partial migrations that cannot be validated due to missing dependencies. |
| 49 | + |
| 50 | +### Recommended checks |
| 51 | + |
| 52 | +- Confirm lockfile/package manager context is healthy. |
| 53 | +- Install dependencies for the target workspace/package. |
| 54 | +- Verify `rstest` executable is resolvable from local dependencies. |
| 55 | + |
| 56 | +### Package manager detection (do this before install) |
| 57 | + |
| 58 | +Always detect package manager from repo context before running install commands. |
| 59 | + |
| 60 | +Preferred signal order: |
| 61 | + |
| 62 | +1. `packageManager` field in nearest `package.json`. |
| 63 | +2. Lockfile presence in workspace/root: |
| 64 | + - `pnpm-lock.yaml` -> `pnpm` |
| 65 | + - `yarn.lock` -> `yarn` |
| 66 | + - `package-lock.json` or `npm-shrinkwrap.json` -> `npm` |
| 67 | +3. Monorepo convention/scripts already used in repo docs/CI. |
| 68 | + |
| 69 | +If signals conflict, prefer `packageManager` field, then lockfile. |
| 70 | + |
| 71 | +### Install command policy |
| 72 | + |
| 73 | +- Use the detected package manager first. |
| 74 | +- In monorepo, run workspace-scoped install/check before root-level install when possible. |
| 75 | +- Do not mix package managers in one migration attempt unless user explicitly asks. |
| 76 | + |
| 77 | +Reference command patterns (choose one by detection): |
| 78 | + |
| 79 | +- `pnpm`: `pnpm install` / workspace-scoped equivalent |
| 80 | +- `yarn`: `yarn install` |
| 81 | +- `npm`: `npm install` |
| 82 | + |
| 83 | +If the chosen manager fails due to environment/tool absence (not dependency conflict), report it and propose the exact next command for the user instead of auto-switching silently. |
| 84 | + |
| 85 | +### If install/check succeeds |
| 86 | + |
| 87 | +- Continue migration normally. |
| 88 | + |
| 89 | +### If install/check fails (blocked mode) |
| 90 | + |
| 91 | +Do **not** continue broad code migration. |
| 92 | +Stop at a minimal safe point and return a blocker report for the user to resolve manually. |
| 93 | + |
| 94 | +Blocked-mode output should include: |
| 95 | + |
| 96 | +1. Exact failed command(s). |
| 97 | +2. Error class (network/auth/registry/peer conflict/lockfile mismatch/permission). |
| 98 | +3. Concrete next command(s) for the user to run. |
| 99 | +4. Whether any files were already changed. |
| 100 | +5. Resume point: "after dependencies are installed, continue from Step 4". |
| 101 | +6. Detected package manager and detection basis (field/lockfile). |
| 102 | + |
| 103 | +### Monorepo guidance |
| 104 | + |
| 105 | +- Prefer workspace-scoped install/check first (target package), then root if needed. |
| 106 | +- Avoid changing unrelated packages while blocked. |
| 107 | +- If root install is required but unavailable, limit edits to docs or planning notes only. |
| 108 | + |
| 109 | +## Patch scope policy (strict) |
| 110 | + |
| 111 | +### Preferred change order |
| 112 | + |
| 113 | +1. CLI/script/config migration (`package.json`, `rstest.config.ts`, include/exclude, test environment). |
| 114 | +2. Mock compatibility adjustments (target module path, `{ mock: true }`, `importActual`). |
| 115 | +3. Narrow per-test setup fixes (single-file, single-suite level). |
| 116 | +4. As a last resort, test body changes. |
| 117 | +5. Never modify runtime source logic by default. |
| 118 | + |
| 119 | +### Red lines |
| 120 | + |
| 121 | +- Do not rewrite many tests in one sweep without first proving config-level fixes are insufficient. |
| 122 | +- Do not alter business/runtime behavior to satisfy tests. |
| 123 | +- Do not change assertion semantics just to make tests pass. |
| 124 | +- Do not broaden migration to unrelated packages in monorepo. |
| 125 | +- Do not delete `vitest` dependency or `vitest.config.*` before confirming Rstest tests pass. |
| 126 | + |
| 127 | +### Escalation rule for large edits |
| 128 | + |
| 129 | +If a fix would require either: |
| 130 | + |
| 131 | +- editing many test files, or |
| 132 | +- changing user source files, |
| 133 | + |
| 134 | +stop and provide: |
| 135 | + |
| 136 | +1. why minimal fixes failed, |
| 137 | +2. proposed large-change options, |
| 138 | +3. expected impact/risk per option, |
| 139 | +4. recommended option. |
| 140 | + |
| 141 | +## 5. Run tests and fix deltas |
| 142 | + |
| 143 | +- Run the test suite and fix failures iteratively. |
| 144 | +- Fix configuration and resolver errors first, then address mocks/timers/snapshots, and touch test logic last. |
| 145 | +- If mocks fail for re-exported modules under Rspack, use the **ProvidedExports Troubleshooting** section below before rewriting lots of tests. |
| 146 | + |
| 147 | +## ProvidedExports Troubleshooting (Rspack + Mock-heavy tests) |
| 148 | + |
| 149 | +This is a high-frequency migration pitfall and should be checked early. |
| 150 | + |
| 151 | +### When to suspect this |
| 152 | + |
| 153 | +- Test imports and mocks a re-export module (for example `foo-dom` re-exporting from `foo-core`). |
| 154 | +- Mock appears to be ignored after migration. |
| 155 | +- Failures are concentrated in module-mock tests, especially with `rs.mock('re-export-module', ...)`. |
| 156 | +- Runtime behavior suggests code is resolved from source module, not the re-export module. |
| 157 | + |
| 158 | +### Why it happens |
| 159 | + |
| 160 | +Rspack may optimize re-exports with `optimization.providedExports`. |
| 161 | +After optimization, runtime can bypass the re-export layer, so your mock target no longer matches what is actually executed. |
| 162 | + |
| 163 | +### Migration-safe handling order (do this in order) |
| 164 | + |
| 165 | +1. Temporarily disable `providedExports` to stabilize migration. |
| 166 | +2. Migrate tests until green. |
| 167 | +3. Re-enable `providedExports` and re-run tests. |
| 168 | +4. If failures come back, either: |
| 169 | + - keep `providedExports: false` temporarily, or |
| 170 | + - change mocks to the actual resolved source module. |
| 171 | + |
| 172 | +### Temporary config snippet |
| 173 | + |
| 174 | +```ts |
| 175 | +export default defineConfig({ |
| 176 | + tools: { |
| 177 | + rspack: { |
| 178 | + optimization: { |
| 179 | + providedExports: false, |
| 180 | + }, |
| 181 | + }, |
| 182 | + }, |
| 183 | +}); |
| 184 | +``` |
| 185 | + |
| 186 | +### Mock target adjustment example |
| 187 | + |
| 188 | +If code imports from `react-router-dom` but runtime resolves to `react-router`, mock `react-router`: |
| 189 | + |
| 190 | +```ts |
| 191 | +rs.mock('react-router', () => ({ |
| 192 | + useParams: () => ({ id: 'mocked-id' }), |
| 193 | +})); |
| 194 | +``` |
| 195 | + |
| 196 | +### Documentation expectation in migration summary |
| 197 | + |
| 198 | +If this workaround is used, explicitly record: |
| 199 | + |
| 200 | +- where `providedExports` was changed, |
| 201 | +- whether the change is temporary or permanent, |
| 202 | +- which tests/modules still rely on it, |
| 203 | +- next cleanup action (re-enable and retest scope). |
| 204 | + |
| 205 | +## 6. Summarize changes |
| 206 | + |
| 207 | +- Provide a concise change summary and list files touched. |
| 208 | +- Call out any remaining manual steps or TODOs. |
0 commit comments