diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 7712bc0b..b83f4f0b 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -31,6 +31,9 @@ jobs:
- name: Install dependencies
run: pnpm install
+ - name: Install Playwright browsers
+ run: pnpm dlx playwright install chromium
+
- name: Build Rspack
run: |
pnpm build:rspack
@@ -50,3 +53,6 @@ jobs:
- name: Build Rsdoctor
run: |
pnpm build:rsdoctor
+
+ - name: Test Rstest
+ run: pnpm test:rstest
diff --git a/.gitignore b/.gitignore
index 5a4c755c..65f56ca1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@ lib-cov
# Coverage directory used by tools like istanbul
coverage
+!rstest/coverage/
*.lcov
# nyc test coverage
diff --git a/AGENTS.md b/AGENTS.md
index 18efc26f..020a4b96 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -4,9 +4,33 @@ This file provides guidance for AI coding agents working in this repository.
## Repository Overview
-- This is a monorepo of example projects for the Rstack ecosystem: Rspack, Rsbuild, Rspress, Rsdoctor, and Rslib.
+- This is a monorepo of example projects for the Rstack ecosystem: Rspack, Rsbuild, Rspress, Rsdoctor, Rslib, and Rstest.
- Package manager: `pnpm` (see `package.json#packageManager`).
-- Workspace layout: `pnpm-workspace.yaml` includes `rspack/**`, `rsbuild/**`, `rslib/**`, `rspress/**`, `rsdoctor/**`.
+- Workspace layout: `pnpm-workspace.yaml` includes `rspack/**`, `rsbuild/**`, `rslib/**`, `rspress/**`, `rsdoctor/**`, `rstest/**`.
+
+## Core Purpose of Examples
+
+**The primary goal of each example is to demonstrate "how a specific API achieves a specific effect through specific configuration".**
+
+When creating or modifying examples:
+- **Keep it minimal**: Only include code necessary to demonstrate the target feature/API
+- **Avoid over-engineering**: Don't add complex business logic that distracts from the core demonstration
+- **Focus on the tool, not the ecosystem**: For example, in a test runner example, focus on the test runner's APIs (mocking, assertions, configuration), not on complex DOM manipulation or third-party library integrations
+- **One concept per example**: Each example should ideally demonstrate one main feature or configuration pattern
+- **Clarity over completeness**: A simple, clear example is better than a comprehensive but confusing one
+
+Example of good vs bad:
+```
+# Good: Demonstrates rstest's mocking API
+- Simple mock function usage
+- Clear before/after assertions
+- Minimal setup code
+
+# Bad: Demonstrates rstest's mocking API
+- Complex React component with many interactions
+- Detailed DOM testing with @testing-library
+- Business logic mixed with test assertions
+```
## Quick Start (Local)
diff --git a/biome.json b/biome.json
index d3da8e8c..fd0d2cbe 100644
--- a/biome.json
+++ b/biome.json
@@ -13,11 +13,13 @@
"**/rspress/**",
"**/rsdoctor/**",
"**/rslib/**",
+ "**/rstest/**",
"!**/dist",
"!**/dist-*",
"!**/doc_build",
"!**/auto-imports.d.ts",
- "!**/components.d.ts"
+ "!**/components.d.ts",
+ "!**/__snapshots__/**"
],
"ignoreUnknown": true
},
diff --git a/package.json b/package.json
index 89f14efc..4c1d2b09 100644
--- a/package.json
+++ b/package.json
@@ -10,10 +10,11 @@
"build:rsdoctor": "pnpm --filter \"rsdoctor-*\" build",
"build:rspack": "pnpm --filter \"example-*\" build",
"test:rspack": "pnpm --filter \"example-*\" test",
+ "test:rstest": "pnpm --filter \"rstest-*\" test",
"build:rspress": "pnpm --filter \"rspress-*\" build",
"build:rslib": "pnpm --filter \"rslib-*\" build",
"prepare": "husky",
- "sort-package-json": "npx sort-package-json \"rspack/*/package.json\" \"rsbuild/*/package.json\" \"rspress/*/package.json\" \"rsdoctor/*/package.json\" \"rslib/*/package.json\""
+ "sort-package-json": "npx sort-package-json \"rspack/*/package.json\" \"rsbuild/*/package.json\" \"rspress/*/package.json\" \"rsdoctor/*/package.json\" \"rslib/*/package.json\" \"rstest/*/package.json\""
},
"lint-staged": {
"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9d9c2de2..afdd6322 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -111,10 +111,10 @@ importers:
devDependencies:
'@module-federation/enhanced':
specifier: ^0.22.0
- version: 0.22.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
+ version: 0.22.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
'@module-federation/rsbuild-plugin':
specifier: ^0.22.0
- version: 0.22.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
+ version: 0.22.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
'@rsbuild/core':
specifier: ^1.7.1
version: 1.7.1
@@ -142,10 +142,10 @@ importers:
devDependencies:
'@module-federation/enhanced':
specifier: ^0.22.0
- version: 0.22.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
+ version: 0.22.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
'@module-federation/rsbuild-plugin':
specifier: ^0.22.0
- version: 0.22.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
+ version: 0.22.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
'@rsbuild/core':
specifier: ^1.7.1
version: 1.7.1
@@ -428,7 +428,7 @@ importers:
version: 1.4.2(@rsbuild/core@1.7.1)(webpack-hot-middleware@2.26.1)
'@rstest/core':
specifier: 0.7.8
- version: 0.7.8(jsdom@27.4.0(postcss@8.5.6))
+ version: 0.7.8(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))
'@testing-library/jest-dom':
specifier: ^6.9.1
version: 6.9.1
@@ -871,7 +871,7 @@ importers:
version: 1.7.1
'@rsbuild/plugin-vue2':
specifier: ^1.0.5
- version: 1.0.5(@rsbuild/core@1.7.1)(@vue/compiler-sfc@3.5.24)(css-loader@7.1.2(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1))
+ version: 1.0.5(@rsbuild/core@1.7.1)(@vue/compiler-sfc@3.5.24)(css-loader@7.1.2(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1))
typescript:
specifier: ^5.9.3
version: 5.9.3
@@ -1021,7 +1021,7 @@ importers:
version: 1.4.2(@rsbuild/core@1.7.1)(webpack-hot-middleware@2.26.1)
'@rsdoctor/rspack-plugin':
specifier: ^1.4.0
- version: 1.4.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)
+ version: 1.4.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
'@types/react':
specifier: ^19.2.7
version: 19.2.7
@@ -1055,7 +1055,7 @@ importers:
devDependencies:
'@rsdoctor/rspack-plugin':
specifier: ^1.4.0
- version: 1.4.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)
+ version: 1.4.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)
'@rspack/cli':
specifier: 1.7.0
version: 1.7.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack-cli@6.0.1)(webpack@5.104.1)
@@ -1095,7 +1095,7 @@ importers:
devDependencies:
'@rsbuild/plugin-react':
specifier: ^1.4.2
- version: 1.4.2(@rsbuild/core@1.7.1)(webpack-hot-middleware@2.26.1)
+ version: 1.4.2(@rsbuild/core@1.7.2)(webpack-hot-middleware@2.26.1)
'@rslib/core':
specifier: ^0.19.1
version: 0.19.1(typescript@5.9.3)
@@ -1134,7 +1134,7 @@ importers:
devDependencies:
'@module-federation/rsbuild-plugin':
specifier: ^0.22.0
- version: 0.22.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
+ version: 0.22.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
'@rsbuild/core':
specifier: ~1.7.1
version: 1.7.1
@@ -1155,16 +1155,16 @@ importers:
devDependencies:
'@module-federation/enhanced':
specifier: ^0.22.0
- version: 0.22.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
+ version: 0.22.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
'@module-federation/rsbuild-plugin':
specifier: ^0.22.0
- version: 0.22.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
+ version: 0.22.0(@rsbuild/core@1.7.2)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
'@module-federation/storybook-addon':
specifier: ^5.0.2
- version: 5.0.2(@module-federation/sdk@0.22.0)(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack-virtual-modules@0.6.2)(webpack@5.104.1)
+ version: 5.0.2(@module-federation/sdk@0.22.0)(@rsbuild/core@1.7.2)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack-virtual-modules@0.6.2)(webpack@5.104.1)
'@rsbuild/plugin-react':
specifier: ^1.4.2
- version: 1.4.2(@rsbuild/core@1.7.1)(webpack-hot-middleware@2.26.1)
+ version: 1.4.2(@rsbuild/core@1.7.2)(webpack-hot-middleware@2.26.1)
'@rslib/core':
specifier: ^0.19.1
version: 0.19.1(typescript@5.9.3)
@@ -1185,10 +1185,10 @@ importers:
version: 10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
storybook-addon-rslib:
specifier: ^3.2.0
- version: 3.2.0(@rsbuild/core@1.7.1)(@rslib/core@0.19.1(typescript@5.9.3))(storybook-builder-rsbuild@3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1)))(typescript@5.9.3)
+ version: 3.2.0(@rsbuild/core@1.7.2)(@rslib/core@0.19.1(typescript@5.9.3))(storybook-builder-rsbuild@3.2.0(@rsbuild/core@1.7.2)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1)))(typescript@5.9.3)
storybook-react-rsbuild:
specifier: ^3.2.0
- version: 3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.53.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.104.1)
+ version: 3.2.0(@rsbuild/core@1.7.2)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.53.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.104.1)
rslib/module-federation/mf-remote:
dependencies:
@@ -1201,7 +1201,7 @@ importers:
devDependencies:
'@module-federation/rsbuild-plugin':
specifier: ^0.22.0
- version: 0.22.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
+ version: 0.22.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
'@rsbuild/core':
specifier: ~1.7.1
version: 1.7.1
@@ -1237,7 +1237,7 @@ importers:
version: 0.19.1(typescript@5.9.3)
'@rstest/core':
specifier: ^0.7.8
- version: 0.7.8(jsdom@27.4.0(postcss@8.5.6))
+ version: 0.7.8(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))
'@types/node':
specifier: ^24.10.4
version: 24.10.4
@@ -1249,7 +1249,7 @@ importers:
devDependencies:
'@rsbuild/plugin-preact':
specifier: ^1.7.0
- version: 1.7.0(@rsbuild/core@1.7.1)(preact@10.28.2)
+ version: 1.7.0(@rsbuild/core@1.7.2)(preact@10.28.2)
'@rslib/core':
specifier: ^0.19.1
version: 0.19.1(typescript@5.9.3)
@@ -1268,7 +1268,7 @@ importers:
devDependencies:
'@rsbuild/plugin-react':
specifier: ^1.4.2
- version: 1.4.2(@rsbuild/core@1.7.1)(webpack-hot-middleware@2.26.1)
+ version: 1.4.2(@rsbuild/core@1.7.2)(webpack-hot-middleware@2.26.1)
'@rslib/core':
specifier: ^0.19.1
version: 0.19.1(typescript@5.9.3)
@@ -1290,7 +1290,7 @@ importers:
devDependencies:
'@rsbuild/plugin-react':
specifier: ^1.4.2
- version: 1.4.2(@rsbuild/core@1.7.1)(webpack-hot-middleware@2.26.1)
+ version: 1.4.2(@rsbuild/core@1.7.2)(webpack-hot-middleware@2.26.1)
'@rslib/core':
specifier: ^0.19.1
version: 0.19.1(typescript@5.9.3)
@@ -1312,10 +1312,10 @@ importers:
devDependencies:
'@rsbuild/plugin-less':
specifier: ^1.5.0
- version: 1.5.0(@rsbuild/core@1.7.1)
+ version: 1.5.0(@rsbuild/core@1.7.2)
'@rsbuild/plugin-react':
specifier: ^1.4.2
- version: 1.4.2(@rsbuild/core@1.7.1)(webpack-hot-middleware@2.26.1)
+ version: 1.4.2(@rsbuild/core@1.7.2)(webpack-hot-middleware@2.26.1)
'@rslib/core':
specifier: ^0.19.1
version: 0.19.1(typescript@5.9.3)
@@ -1337,13 +1337,13 @@ importers:
devDependencies:
'@rsbuild/plugin-react':
specifier: ^1.4.2
- version: 1.4.2(@rsbuild/core@1.7.1)(webpack-hot-middleware@2.26.1)
+ version: 1.4.2(@rsbuild/core@1.7.2)(webpack-hot-middleware@2.26.1)
'@rslib/core':
specifier: ^0.19.1
version: 0.19.1(typescript@5.9.3)
'@rstest/core':
specifier: ^0.7.8
- version: 0.7.8(jsdom@27.4.0(postcss@8.5.6))
+ version: 0.7.8(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))
'@testing-library/jest-dom':
specifier: ^6.9.1
version: 6.9.1
@@ -1371,10 +1371,10 @@ importers:
devDependencies:
'@rsbuild/plugin-react':
specifier: ^1.4.2
- version: 1.4.2(@rsbuild/core@1.7.1)(webpack-hot-middleware@2.26.1)
+ version: 1.4.2(@rsbuild/core@1.7.2)(webpack-hot-middleware@2.26.1)
'@rsbuild/plugin-sass':
specifier: ^1.4.0
- version: 1.4.0(@rsbuild/core@1.7.1)
+ version: 1.4.0(@rsbuild/core@1.7.2)
'@rslib/core':
specifier: ^0.19.1
version: 0.19.1(typescript@5.9.3)
@@ -1422,10 +1422,10 @@ importers:
version: 10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
storybook-addon-rslib:
specifier: ^3.2.0
- version: 3.2.0(@rsbuild/core@1.7.1)(@rslib/core@0.19.1(typescript@5.9.3))(storybook-builder-rsbuild@3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1)))(typescript@5.9.3)
+ version: 3.2.0(@rsbuild/core@1.7.1)(@rslib/core@0.19.1(typescript@5.9.3))(storybook-builder-rsbuild@3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1)))(typescript@5.9.3)
storybook-react-rsbuild:
specifier: ^3.2.0
- version: 3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.53.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.104.1(esbuild@0.27.2))
+ version: 3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.53.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.104.1(esbuild@0.27.2))
typescript:
specifier: ^5.9.3
version: 5.9.3
@@ -1438,7 +1438,7 @@ importers:
devDependencies:
'@rsbuild/plugin-react':
specifier: ^1.4.2
- version: 1.4.2(@rsbuild/core@1.7.1)(webpack-hot-middleware@2.26.1)
+ version: 1.4.2(@rsbuild/core@1.7.2)(webpack-hot-middleware@2.26.1)
'@rslib/core':
specifier: ^0.19.1
version: 0.19.1(typescript@5.9.3)
@@ -1466,7 +1466,7 @@ importers:
devDependencies:
'@rsbuild/plugin-react':
specifier: ^1.4.2
- version: 1.4.2(@rsbuild/core@1.7.1)(webpack-hot-middleware@2.26.1)
+ version: 1.4.2(@rsbuild/core@1.7.2)(webpack-hot-middleware@2.26.1)
'@rslib/core':
specifier: ^0.19.1
version: 0.19.1(typescript@5.9.3)
@@ -1487,10 +1487,10 @@ importers:
devDependencies:
'@rsbuild/plugin-babel':
specifier: ^1.0.6
- version: 1.0.6(@rsbuild/core@1.7.1)
+ version: 1.0.6(@rsbuild/core@1.7.2)
'@rsbuild/plugin-solid':
specifier: ^1.0.6
- version: 1.0.6(@babel/core@7.28.5)(@rsbuild/core@1.7.1)(solid-js@1.9.10)
+ version: 1.0.6(@babel/core@7.28.5)(@rsbuild/core@1.7.2)(solid-js@1.9.10)
'@rslib/core':
specifier: ^0.19.1
version: 0.19.1(typescript@5.9.3)
@@ -1508,10 +1508,10 @@ importers:
devDependencies:
'@rsbuild/plugin-react':
specifier: ^1.4.2
- version: 1.4.2(@rsbuild/core@1.7.1)(webpack-hot-middleware@2.26.1)
+ version: 1.4.2(@rsbuild/core@1.7.2)(webpack-hot-middleware@2.26.1)
'@rsbuild/plugin-sass':
specifier: ^1.4.0
- version: 1.4.0(@rsbuild/core@1.7.1)
+ version: 1.4.0(@rsbuild/core@1.7.2)
'@rslib/core':
specifier: ^0.19.1
version: 0.19.1(typescript@5.9.3)
@@ -1532,7 +1532,7 @@ importers:
version: 0.19.1(typescript@5.9.3)
rsbuild-plugin-unplugin-vue:
specifier: ^0.1.0
- version: 0.1.0(@rsbuild/core@1.7.1)(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(vue@3.5.21(typescript@5.9.3))(yaml@2.8.1)
+ version: 0.1.0(@rsbuild/core@1.7.2)(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(vue@3.5.21(typescript@5.9.3))(yaml@2.8.1)
typescript:
specifier: ^5.9.3
version: 5.9.3
@@ -1550,7 +1550,7 @@ importers:
version: 0.19.1(typescript@5.9.3)
'@rstest/core':
specifier: ^0.7.8
- version: 0.7.8(jsdom@27.4.0(postcss@8.5.6))
+ version: 0.7.8(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))
'@testing-library/jest-dom':
specifier: ^6.9.1
version: 6.9.1
@@ -1565,7 +1565,7 @@ importers:
version: 27.4.0(postcss@8.5.6)
rsbuild-plugin-unplugin-vue:
specifier: ^0.1.0
- version: 0.1.0(@rsbuild/core@1.7.1)(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(vue@3.5.21(typescript@5.9.3))(yaml@2.8.1)
+ version: 0.1.0(@rsbuild/core@1.7.2)(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(vue@3.5.21(typescript@5.9.3))(yaml@2.8.1)
typescript:
specifier: ^5.9.3
version: 5.9.3
@@ -1601,10 +1601,10 @@ importers:
version: 10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
storybook-addon-rslib:
specifier: ^3.2.0
- version: 3.2.0(@rsbuild/core@1.7.1)(@rslib/core@0.19.1(typescript@5.9.3))(storybook-builder-rsbuild@3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1)))(typescript@5.9.3)
+ version: 3.2.0(@rsbuild/core@1.7.1)(@rslib/core@0.19.1(typescript@5.9.3))(storybook-builder-rsbuild@3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1)))(typescript@5.9.3)
storybook-vue3-rsbuild:
specifier: ^3.2.0
- version: 3.2.0(@babel/preset-env@7.28.5(@babel/core@7.28.5))(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.3))(webpack@5.104.1(esbuild@0.27.2))
+ version: 3.2.0(@babel/preset-env@7.28.5(@babel/core@7.28.5))(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.3))(webpack@5.104.1(esbuild@0.27.2))
typescript:
specifier: ^5.9.3
version: 5.9.3
@@ -2251,7 +2251,7 @@ importers:
version: 10.0.0(@babel/core@7.28.5)(webpack@5.104.1)
html-webpack-plugin:
specifier: ^5.6.5
- version: 5.6.5(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)
+ version: 5.6.5(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
react-refresh:
specifier: ^0.18.0
version: 0.18.0
@@ -3693,6 +3693,141 @@ importers:
specifier: ^19.2.3
version: 19.2.3(react@19.2.3)
+ rstest/browser-rsbuild-react:
+ devDependencies:
+ '@rsbuild/core':
+ specifier: ^1.4.2
+ version: 1.7.2
+ '@rsbuild/plugin-react':
+ specifier: ^1.4.2
+ version: 1.4.2(@rsbuild/core@1.7.2)(webpack-hot-middleware@2.26.1)
+ '@rstest/adapter-rsbuild':
+ specifier: ^0.1.0
+ version: 0.1.0(@rsbuild/core@1.7.2)
+ '@rstest/browser':
+ specifier: ^0.7.9
+ version: 0.7.9(@rstest/core@0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6)))(playwright@1.57.0)
+ '@rstest/browser-react':
+ specifier: ^0.7.9
+ version: 0.7.9(@rstest/core@0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@rstest/core':
+ specifier: ^0.7.9
+ version: 0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))
+ '@testing-library/dom':
+ specifier: ^10.4.0
+ version: 10.4.0
+ '@testing-library/user-event':
+ specifier: ^14.6.1
+ version: 14.6.1(@testing-library/dom@10.4.0)
+ '@types/react':
+ specifier: ^19.1.8
+ version: 19.2.7
+ '@types/react-dom':
+ specifier: ^19.1.6
+ version: 19.2.3(@types/react@19.2.7)
+ playwright:
+ specifier: ^1.57.0
+ version: 1.57.0
+ react:
+ specifier: ^19.2.3
+ version: 19.2.3
+ react-dom:
+ specifier: ^19.2.3
+ version: 19.2.3(react@19.2.3)
+ typescript:
+ specifier: ^5.8.3
+ version: 5.9.3
+
+ rstest/browser-rsbuild-vanilla:
+ devDependencies:
+ '@rsbuild/core':
+ specifier: ^1.7.1
+ version: 1.7.2
+ '@rstest/browser':
+ specifier: ^0.7.9
+ version: 0.7.9(@rstest/core@0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6)))(playwright@1.57.0)
+ '@rstest/core':
+ specifier: ^0.7.9
+ version: 0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))
+ playwright:
+ specifier: ^1.57.0
+ version: 1.57.0
+ typescript:
+ specifier: ^5.8.3
+ version: 5.9.3
+
+ rstest/coverage:
+ devDependencies:
+ '@rstest/core':
+ specifier: ^0.7.9
+ version: 0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))
+ '@rstest/coverage-istanbul':
+ specifier: ^0.1.6
+ version: 0.1.6(@rstest/core@0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6)))
+ typescript:
+ specifier: ^5.8.3
+ version: 5.9.3
+
+ rstest/fake-timers:
+ devDependencies:
+ '@rstest/core':
+ specifier: ^0.7.9
+ version: 0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))
+ typescript:
+ specifier: ^5.8.3
+ version: 5.9.3
+
+ rstest/mocking:
+ devDependencies:
+ '@rstest/core':
+ specifier: ^0.7.9
+ version: 0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))
+ typescript:
+ specifier: ^5.8.3
+ version: 5.9.3
+
+ rstest/rsbuild-adapter:
+ devDependencies:
+ '@rsbuild/core':
+ specifier: ^1.7.2
+ version: 1.7.2
+ '@rstest/adapter-rsbuild':
+ specifier: ^0.1.0
+ version: 0.1.0(@rsbuild/core@1.7.2)
+ '@rstest/core':
+ specifier: ^0.7.9
+ version: 0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))
+ happy-dom:
+ specifier: ^18.0.1
+ version: 18.0.1
+ typescript:
+ specifier: ^5.8.3
+ version: 5.9.3
+
+ rstest/rslib-adapter:
+ devDependencies:
+ '@rslib/core':
+ specifier: ^0.19.1
+ version: 0.19.1(typescript@5.9.3)
+ '@rstest/adapter-rslib':
+ specifier: ^0.1.1
+ version: 0.1.1(@rslib/core@0.19.1(typescript@5.9.3))(typescript@5.9.3)
+ '@rstest/core':
+ specifier: ^0.7.9
+ version: 0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))
+ typescript:
+ specifier: ^5.8.3
+ version: 5.9.3
+
+ rstest/snapshot:
+ devDependencies:
+ '@rstest/core':
+ specifier: ^0.7.9
+ version: 0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))
+ typescript:
+ specifier: ^5.8.3
+ version: 5.9.3
+
packages:
'@aashutoshrathi/word-wrap@1.2.6':
@@ -5712,12 +5847,12 @@ packages:
'@jridgewell/sourcemap-codec@1.5.5':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
- '@jridgewell/trace-mapping@0.3.25':
- resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
-
'@jridgewell/trace-mapping@0.3.29':
resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
'@jridgewell/trace-mapping@0.3.9':
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
@@ -6768,6 +6903,11 @@ packages:
engines: {node: '>=18.12.0'}
hasBin: true
+ '@rsbuild/core@1.7.2':
+ resolution: {integrity: sha512-VAFO6cM+cyg2ntxNW6g3tB2Jc5J5mpLjLluvm7VtW2uceNzyUlVv41o66Yp1t1ikxd3ljtqegViXem62JqzveA==}
+ engines: {node: '>=18.12.0'}
+ hasBin: true
+
'@rsbuild/plugin-babel@1.0.6':
resolution: {integrity: sha512-tWnqG938MedKJx7U4F1lHb156VDtNzj7mSsi2ZoxZVBnECQE01/V6QTN1XKw7nWunGyGoETb+nQBGc+fkVZjvw==}
peerDependencies:
@@ -6947,6 +7087,11 @@ packages:
cpu: [arm64]
os: [darwin]
+ '@rspack/binding-darwin-arm64@1.7.1':
+ resolution: {integrity: sha512-3C0w0kfCHfgOH+AP/Dx1bm/b3AR/or5CmU22Abevek0m95ndU3iT902eLcm9JNiMQnDQLBQbolfj5P591t0oPg==}
+ cpu: [arm64]
+ os: [darwin]
+
'@rspack/binding-darwin-x64@1.3.12':
resolution: {integrity: sha512-Sj4m+mCUxL7oCpdu7OmWT7fpBM7hywk5CM9RDc3D7StaBZbvNtNftafCrTZzTYKuZrKmemTh5SFzT5Tz7tf6GA==}
cpu: [x64]
@@ -6967,6 +7112,11 @@ packages:
cpu: [x64]
os: [darwin]
+ '@rspack/binding-darwin-x64@1.7.1':
+ resolution: {integrity: sha512-HTrBpdw2gWwcpJ3c8h4JF8B1YRNvrFT+K620ycttrlu/HvI4/U770BBJ/ej36R/hdh59JvMCGe+w49FyXv6rzg==}
+ cpu: [x64]
+ os: [darwin]
+
'@rspack/binding-linux-arm64-gnu@1.3.12':
resolution: {integrity: sha512-7MuOxf3/Mhv4mgFdLTvgnt/J+VouNR65DEhorth+RZm3LEWojgoFEphSAMAvpvAOpYSS68Sw4SqsOZi719ia2w==}
cpu: [arm64]
@@ -6987,6 +7137,11 @@ packages:
cpu: [arm64]
os: [linux]
+ '@rspack/binding-linux-arm64-gnu@1.7.1':
+ resolution: {integrity: sha512-BX9yAPCO0WBFyOzKl9bSXT/cH27nnOJp02smIQMxfv7RNfwGkJg5GgakYcuYG+9U1HEFitBSzmwS2+dxDcAxlg==}
+ cpu: [arm64]
+ os: [linux]
+
'@rspack/binding-linux-arm64-musl@1.3.12':
resolution: {integrity: sha512-s6KKj20T9Z1bA8caIjU6EzJbwyDo1URNFgBAlafCT2UC6yX7flstDJJ38CxZacA9A2P24RuQK2/jPSZpWrTUFA==}
cpu: [arm64]
@@ -7007,6 +7162,11 @@ packages:
cpu: [arm64]
os: [linux]
+ '@rspack/binding-linux-arm64-musl@1.7.1':
+ resolution: {integrity: sha512-maBX19XyiVkxzh/NA79ALetCobc4zUyoWkWLeCGyW5xKzhPVFatJp+qCiHqHkqUZcgRo+1i5ihoZ2bXmelIeZg==}
+ cpu: [arm64]
+ os: [linux]
+
'@rspack/binding-linux-x64-gnu@1.3.12':
resolution: {integrity: sha512-0w/sRREYbRgHgWvs2uMEJSLfvzbZkPHUg6CMcYQGNVK6axYRot6jPyKetyFYA9pR5fB5rsXegpnFaZaVrRIK2g==}
cpu: [x64]
@@ -7027,6 +7187,11 @@ packages:
cpu: [x64]
os: [linux]
+ '@rspack/binding-linux-x64-gnu@1.7.1':
+ resolution: {integrity: sha512-8KJAeBLiWcN7zEc9aaS7LRJPZVtZuQU8mCsn+fRhdQDSc+a9FcTN8b6Lw29z8cejwbU6Gxr/8wk5XGexMWFaZA==}
+ cpu: [x64]
+ os: [linux]
+
'@rspack/binding-linux-x64-musl@1.3.12':
resolution: {integrity: sha512-jEdxkPymkRxbijDRsBGdhopcbGXiXDg59lXqIRkVklqbDmZ/O6DHm7gImmlx5q9FoWbz0gqJuOKBz4JqWxjWVA==}
cpu: [x64]
@@ -7047,6 +7212,11 @@ packages:
cpu: [x64]
os: [linux]
+ '@rspack/binding-linux-x64-musl@1.7.1':
+ resolution: {integrity: sha512-Gn9x5vhKRELvSoZ3ZjquY8eWtCXur0OsYnZ2/ump8mofM6IDaL7Qqu3Hf4Kud31PDH0tfz0jWf9piX32HHPmgg==}
+ cpu: [x64]
+ os: [linux]
+
'@rspack/binding-wasm32-wasi@1.6.7':
resolution: {integrity: sha512-yx88EFdE9RP3hh7VhjjW6uc6wGU0KcpOcZp8T8E/a+X8L98fX0aVrtM1IDbndhmdluIMqGbfJNap2+QqOCY9Mw==}
cpu: [wasm32]
@@ -7059,6 +7229,10 @@ packages:
resolution: {integrity: sha512-eaZzkGpxzVESmaX/UALMiQO+eNppe/i1VWQksGRfdoUu0rILqr/YDjsWFTcpbI9Dt3fg2kshHawBHxfwtxHcZQ==}
cpu: [wasm32]
+ '@rspack/binding-wasm32-wasi@1.7.1':
+ resolution: {integrity: sha512-2r9M5iVchmsFkp3sz7A5YnMm2TfpkB71LK3AoaRWKMfvf5oFky0GSGISYd2TCBASO+X2Qskaq+B24Szo8zH5FA==}
+ cpu: [wasm32]
+
'@rspack/binding-win32-arm64-msvc@1.3.12':
resolution: {integrity: sha512-ZRvUCb3TDLClAqcTsl/o9UdJf0B5CgzAxgdbnYJbldyuyMeTUB4jp20OfG55M3C2Nute2SNhu2bOOp9Se5Ongw==}
cpu: [arm64]
@@ -7079,6 +7253,11 @@ packages:
cpu: [arm64]
os: [win32]
+ '@rspack/binding-win32-arm64-msvc@1.7.1':
+ resolution: {integrity: sha512-/WIHp982yqqqAuiz2WLtf1ofo9d1lHDGZJ7flxFllb1iMgnUeSRyX6stxEi11K3Rg6pQa7FdCZGKX/engyj2bw==}
+ cpu: [arm64]
+ os: [win32]
+
'@rspack/binding-win32-ia32-msvc@1.3.12':
resolution: {integrity: sha512-1TKPjuXStPJr14f3ZHuv40Xc/87jUXx10pzVtrPnw+f3hckECHrbYU/fvbVzZyuXbsXtkXpYca6ygCDRJAoNeQ==}
cpu: [ia32]
@@ -7099,6 +7278,11 @@ packages:
cpu: [ia32]
os: [win32]
+ '@rspack/binding-win32-ia32-msvc@1.7.1':
+ resolution: {integrity: sha512-Kpela29n+kDGGsss6q/3qTd6n9VW7TOQaiA7t1YLdCCl8qqcdKlz/vWjFMd2MqgcSGC/16PvChE4sgpUvryfCQ==}
+ cpu: [ia32]
+ os: [win32]
+
'@rspack/binding-win32-x64-msvc@1.3.12':
resolution: {integrity: sha512-lCR0JfnYKpV+a6r2A2FdxyUKUS4tajePgpPJN5uXDgMGwrDtRqvx+d0BHhwjFudQVJq9VVbRaL89s2MQ6u+xYw==}
cpu: [x64]
@@ -7119,6 +7303,11 @@ packages:
cpu: [x64]
os: [win32]
+ '@rspack/binding-win32-x64-msvc@1.7.1':
+ resolution: {integrity: sha512-B/y4MWqP2Xeto1/HV0qtZNOMPSLrEVOqi2b7JSIXG/bhlf+3IAkDzEEoHs+ZikLR4C8hMaS0pVJsDGKFmGzC9A==}
+ cpu: [x64]
+ os: [win32]
+
'@rspack/binding@1.3.12':
resolution: {integrity: sha512-4Ic8lV0+LCBfTlH5aIOujIRWZOtgmG223zC4L3o8WY/+ESAgpdnK6lSSMfcYgRanYLAy3HOmFIp20jwskMpbAg==}
@@ -7131,6 +7320,9 @@ packages:
'@rspack/binding@1.7.0':
resolution: {integrity: sha512-xO+pZKG2dvU9CuRTTi+DcCc4p+CZhBJlvuYikBja/0a62cTntQV2PWV+/xU1a6Vbo89yNz158LR05nvjtKVwTw==}
+ '@rspack/binding@1.7.1':
+ resolution: {integrity: sha512-qVTV1/UWpMSZktvK5A8+HolgR1Qf0nYR3Gg4Vax5x3/BcHDpwGZ0fbdFRUirGVWH/XwxZ81zoI6F2SZq7xbX+w==}
+
'@rspack/cli@1.7.0':
resolution: {integrity: sha512-4fGf1lvP9+JzpkVIljtZ+NrGn8JEsyVwNACDZPL4Heg1U2tStIde2RvzHLVs+0+NBVENu47v6tho5xMLcJPB7w==}
hasBin: true
@@ -7173,6 +7365,15 @@ packages:
'@swc/helpers':
optional: true
+ '@rspack/core@1.7.1':
+ resolution: {integrity: sha512-kRxfY8RRa6nU3/viDvAIP6CRpx+0rfXFRonPL0pHBx8u6HhV7m9rLEyaN6MWsLgNIAWkleFGb7tdo4ux2ljRJQ==}
+ engines: {node: '>=18.12.0'}
+ peerDependencies:
+ '@swc/helpers': '>=0.5.1'
+ peerDependenciesMeta:
+ '@swc/helpers':
+ optional: true
+
'@rspack/dev-server@1.1.5':
resolution: {integrity: sha512-cwz0qc6iqqoJhyWqxP7ZqE2wyYNHkBMQUXxoQ0tNoZ4YNRkDyQ4HVJ/3oPSmMKbvJk/iJ16u7xZmwG6sK47q/A==}
engines: {node: '>= 18.12.0'}
@@ -7334,6 +7535,38 @@ packages:
resolution: {integrity: sha512-sEORXfNT85RffLSjo5+AOCpQBi77drvtW5bFSk9MOmzqpv9tSk8SkIsDyjzccoOveKXCu4DIoJHaVYhf5kJcYQ==}
engines: {node: '>=14.17.6'}
+ '@rstest/adapter-rsbuild@0.1.0':
+ resolution: {integrity: sha512-DhH/EiSOkzQPRZk4sENTeeOZIBmKyZJDCjRQ+SPgtG/JWV9PS9gv4J6l2e5hV1c0PpMiniiOCxX3O4wRCDcF4Q==}
+ peerDependencies:
+ '@rsbuild/core': '*'
+
+ '@rstest/adapter-rslib@0.1.1':
+ resolution: {integrity: sha512-R63E/DrtG4ol9wffAxgeq9ze6/Dm9wms0S6grm8maXAQ7qLooza/CTj4AH6aXeEjbpb64hfRGZy8VmZh9+fCfg==}
+ peerDependencies:
+ '@rslib/core': '>=0.18.6'
+ typescript: ^5
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@rstest/browser-react@0.7.9':
+ resolution: {integrity: sha512-UQ0R+GXon4vCR5eJ7+yN6HpOBAABkd8p1yFw0ZNZYcK/NMCfNwnLfz1h9+dD9Rf5PS/y0AtHA7xRbodTdDHvKA==}
+ engines: {node: '>=18.12.0'}
+ peerDependencies:
+ '@rstest/core': workspace:^
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ '@rstest/browser@0.7.9':
+ resolution: {integrity: sha512-IFbo2YbT+ckQXVI7qu40ez/iraHPkEbHnHyP4D26XKqd9QHYne9jH4nU8N/ero2Oh4WqObv2rujfDUX/p5syRA==}
+ engines: {node: '>=18.12.0'}
+ peerDependencies:
+ '@rstest/core': workspace:^
+ playwright: ^1.49.1
+ peerDependenciesMeta:
+ playwright:
+ optional: true
+
'@rstest/core@0.7.8':
resolution: {integrity: sha512-A7oN5GLMYuVkTGjXTH+69nz3wR0lxHUkWax+hVq+jNQmcf81ZqJosTLqN6jwFZdmCfHbj3iTd9ku6boo8tJlnA==}
engines: {node: '>=18.12.0'}
@@ -7347,6 +7580,24 @@ packages:
jsdom:
optional: true
+ '@rstest/core@0.7.9':
+ resolution: {integrity: sha512-RHNPS1MDUxtf+1Z0YZi+vIQ13SdvCbcbpzDA7XHcPziTRy2mAPg8nfcms+XzbIp95KXH75ucAhgAKNFO0QgARA==}
+ engines: {node: '>=18.12.0'}
+ hasBin: true
+ peerDependencies:
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
+ '@rstest/coverage-istanbul@0.1.6':
+ resolution: {integrity: sha512-a7p3MCuKdbA89A0fL8IkXppvAEkl9+cUrqA5Nixgy/8DemJwJ+i9iDuQetLDaTdlXfNknIT5A6dQjKgoR4cWjw==}
+ peerDependencies:
+ '@rstest/core': ~0.7.9
+
'@selderee/plugin-htmlparser2@0.11.0':
resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
@@ -8159,6 +8410,9 @@ packages:
'@types/node-forge@1.3.11':
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
+ '@types/node@20.19.27':
+ resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==}
+
'@types/node@24.10.0':
resolution: {integrity: sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==}
@@ -8251,6 +8505,9 @@ packages:
'@types/webpack@5.28.5':
resolution: {integrity: sha512-wR87cgvxj3p6D0Crt1r5avwqffqPXUkNlnQ1mjU93G7gCuFjufZR4I6j8cz5g1F1tTYpfOOFvly+cmIQwL9wvw==}
+ '@types/whatwg-mimetype@3.0.2':
+ resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==}
+
'@types/ws@8.5.10':
resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
@@ -10444,6 +10701,10 @@ packages:
resolution: {integrity: sha512-L8vK54CSjKB4pwlwx0YaqeBdUSGufaLHl/pEgD+EqnMrYCVUA8HzMjURALSyvOlC57e953yN7KyXS63qDoc3Rg==}
engines: {node: '>=12.20'}
+ env-editor@1.3.0:
+ resolution: {integrity: sha512-EqiD/j01PooUbeWk+etUo2TWoocjoxMfGNYpS9e47glIJ5r8WepycIki+LCbonFbPdwlqY5ETeSTAJVMih4z4w==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
env-paths@2.2.1:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
engines: {node: '>=6'}
@@ -11004,6 +11265,11 @@ packages:
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+ fsevents@2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -11171,6 +11437,10 @@ packages:
engines: {node: '>=0.4.7'}
hasBin: true
+ happy-dom@18.0.1:
+ resolution: {integrity: sha512-qn+rKOW7KWpVTtgIUi6RVmTBZJSe2k0Db0vh1f7CWrWclkkc7/Q+FrOfkZIb2eiErLyqu5AXEzE7XthO9JVxRA==}
+ engines: {node: '>=20.0.0'}
+
has-ansi@2.0.0:
resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==}
engines: {node: '>=0.10.0'}
@@ -11896,6 +12166,10 @@ packages:
resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==}
engines: {node: '>=8'}
+ istanbul-reports@3.2.0:
+ resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==}
+ engines: {node: '>=8'}
+
iterare@1.2.1:
resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==}
engines: {node: '>=6'}
@@ -12345,6 +12619,10 @@ packages:
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
engines: {node: '>=14'}
+ line-column-path@3.0.0:
+ resolution: {integrity: sha512-Atocnm7Wr9nuvAn97yEPQa3pcQI5eLQGBz+m6iTb+CVw+IOzYB9MrYK7jI7BfC9ISnT4Fu0eiwhAScV//rp4Hw==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
@@ -13321,6 +13599,10 @@ packages:
oniguruma-to-es@4.3.4:
resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==}
+ open-editor@4.1.1:
+ resolution: {integrity: sha512-SYtGeZ9Zkzj/naoZaEF9LzwDYEGwuqQ4Fx5E3xdVRN98LFJjvMhG/ElByFEOVOiXepGra/Wi1fA4i/E1fXSBsw==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
open@10.2.0:
resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
engines: {node: '>=18'}
@@ -13584,6 +13866,16 @@ packages:
pkg-types@2.3.0:
resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
+ playwright-core@1.57.0:
+ resolution: {integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ playwright@1.57.0:
+ resolution: {integrity: sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==}
+ engines: {node: '>=18'}
+ hasBin: true
+
pnpm-workspace-yaml@1.3.0:
resolution: {integrity: sha512-Krb5q8Totd5mVuLx7we+EFHq/AfxA75nbfTm25Q1pIf606+RlaKUG+PXH8SDihfe5b5k4H09gE+sL47L1t5lbw==}
@@ -15284,6 +15576,9 @@ packages:
engines: {node: '>=14.0.0'}
hasBin: true
+ swc-plugin-coverage-instrument@0.0.32:
+ resolution: {integrity: sha512-KsO1xNQdcVb331Rm98Op6uYf36/CYdF1kILQxOddmtCNlRLISpCUqHcnogu+QgELZvG48oie+w0fzS8uktjcEA==}
+
swc-plugin-css-modules@8.0.0:
resolution: {integrity: sha512-h7YgyHwCYlcOLhrt3lKOs8ulYAqDtxc5bVStsjHDw+aUmyfPK7vFejVvfEeYvF2QQ7qXoFoJTUfcgOKBdiHVRg==}
@@ -15703,6 +15998,9 @@ packages:
unconfig@7.4.2:
resolution: {integrity: sha512-nrMlWRQ1xdTjSnSUqvYqJzbTBFugoqHobQj58B2bc8qxHKBBHMNNsWQFP3Cd3/JZK907voM2geYPWqD4VK3MPQ==}
+ undici-types@6.21.0:
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
undici-types@7.16.0:
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
@@ -16337,6 +16635,10 @@ packages:
engines: {node: '>=18'}
deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation
+ whatwg-mimetype@3.0.0:
+ resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
+ engines: {node: '>=12'}
+
whatwg-mimetype@4.0.0:
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
engines: {node: '>=18'}
@@ -16642,7 +16944,7 @@ snapshots:
'@ampproject/remapping@2.3.0':
dependencies:
'@jridgewell/gen-mapping': 0.3.5
- '@jridgewell/trace-mapping': 0.3.29
+ '@jridgewell/trace-mapping': 0.3.31
'@antfu/ni@27.0.1':
dependencies:
@@ -16803,7 +17105,7 @@ snapshots:
'@babel/parser': 7.28.5
'@babel/types': 7.28.4
'@jridgewell/gen-mapping': 0.3.5
- '@jridgewell/trace-mapping': 0.3.25
+ '@jridgewell/trace-mapping': 0.3.31
jsesc: 3.0.2
'@babel/generator@7.26.5':
@@ -16811,7 +17113,7 @@ snapshots:
'@babel/parser': 7.28.5
'@babel/types': 7.28.5
'@jridgewell/gen-mapping': 0.3.12
- '@jridgewell/trace-mapping': 0.3.29
+ '@jridgewell/trace-mapping': 0.3.31
jsesc: 3.0.2
'@babel/generator@7.28.0':
@@ -16819,7 +17121,7 @@ snapshots:
'@babel/parser': 7.28.5
'@babel/types': 7.28.5
'@jridgewell/gen-mapping': 0.3.12
- '@jridgewell/trace-mapping': 0.3.29
+ '@jridgewell/trace-mapping': 0.3.31
jsesc: 3.0.2
'@babel/generator@7.28.3':
@@ -16827,7 +17129,7 @@ snapshots:
'@babel/parser': 7.28.5
'@babel/types': 7.28.5
'@jridgewell/gen-mapping': 0.3.12
- '@jridgewell/trace-mapping': 0.3.29
+ '@jridgewell/trace-mapping': 0.3.31
jsesc: 3.0.2
'@babel/generator@7.28.5':
@@ -16835,7 +17137,7 @@ snapshots:
'@babel/parser': 7.28.5
'@babel/types': 7.28.5
'@jridgewell/gen-mapping': 0.3.12
- '@jridgewell/trace-mapping': 0.3.29
+ '@jridgewell/trace-mapping': 0.3.31
jsesc: 3.0.2
'@babel/helper-annotate-as-pure@7.27.1':
@@ -19287,7 +19589,7 @@ snapshots:
'@jest/test-result': 30.2.0
'@jest/transform': 30.2.0
'@jest/types': 30.2.0
- '@jridgewell/trace-mapping': 0.3.29
+ '@jridgewell/trace-mapping': 0.3.31
'@types/node': 24.10.4
chalk: 4.1.2
collect-v8-coverage: 1.0.2
@@ -19325,7 +19627,7 @@ snapshots:
'@jest/source-map@30.0.1':
dependencies:
- '@jridgewell/trace-mapping': 0.3.29
+ '@jridgewell/trace-mapping': 0.3.31
callsites: 3.1.0
graceful-fs: 4.2.11
@@ -19347,7 +19649,7 @@ snapshots:
dependencies:
'@babel/core': 7.28.5
'@jest/types': 30.2.0
- '@jridgewell/trace-mapping': 0.3.29
+ '@jridgewell/trace-mapping': 0.3.31
babel-plugin-istanbul: 7.0.1
chalk: 4.1.2
convert-source-map: 2.0.0
@@ -19395,13 +19697,13 @@ snapshots:
'@jridgewell/gen-mapping@0.3.12':
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
- '@jridgewell/trace-mapping': 0.3.29
+ '@jridgewell/trace-mapping': 0.3.31
'@jridgewell/gen-mapping@0.3.5':
dependencies:
'@jridgewell/set-array': 1.2.1
'@jridgewell/sourcemap-codec': 1.5.5
- '@jridgewell/trace-mapping': 0.3.29
+ '@jridgewell/trace-mapping': 0.3.31
'@jridgewell/remapping@2.3.5':
dependencies:
@@ -19415,16 +19717,16 @@ snapshots:
'@jridgewell/source-map@0.3.5':
dependencies:
'@jridgewell/gen-mapping': 0.3.12
- '@jridgewell/trace-mapping': 0.3.29
+ '@jridgewell/trace-mapping': 0.3.31
'@jridgewell/sourcemap-codec@1.5.5': {}
- '@jridgewell/trace-mapping@0.3.25':
+ '@jridgewell/trace-mapping@0.3.29':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
- '@jridgewell/trace-mapping@0.3.29':
+ '@jridgewell/trace-mapping@0.3.31':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
@@ -19675,7 +19977,7 @@ snapshots:
- supports-color
- utf-8-validate
- '@module-federation/enhanced@0.22.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)':
+ '@module-federation/enhanced@0.22.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)':
dependencies:
'@module-federation/bridge-react-webpack-plugin': 0.22.0
'@module-federation/cli': 0.22.0(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))
@@ -19685,7 +19987,7 @@ snapshots:
'@module-federation/inject-external-runtime-core-plugin': 0.22.0(@module-federation/runtime-tools@0.22.0)
'@module-federation/managers': 0.22.0
'@module-federation/manifest': 0.22.0(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))
- '@module-federation/rspack': 0.22.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))
+ '@module-federation/rspack': 0.22.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))
'@module-federation/runtime-tools': 0.22.0
'@module-federation/sdk': 0.22.0
btoa: 1.2.1
@@ -19735,9 +20037,9 @@ snapshots:
- utf-8-validate
- vue-tsc
- '@module-federation/node@2.7.26(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)':
+ '@module-federation/node@2.7.26(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)':
dependencies:
- '@module-federation/enhanced': 0.22.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
+ '@module-federation/enhanced': 0.22.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
'@module-federation/runtime': 0.22.0
'@module-federation/sdk': 0.22.0
btoa: 1.2.1
@@ -19756,10 +20058,10 @@ snapshots:
- utf-8-validate
- vue-tsc
- '@module-federation/rsbuild-plugin@0.22.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)':
+ '@module-federation/rsbuild-plugin@0.22.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)':
dependencies:
- '@module-federation/enhanced': 0.22.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
- '@module-federation/node': 2.7.26(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
+ '@module-federation/enhanced': 0.22.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
+ '@module-federation/node': 2.7.26(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
'@module-federation/sdk': 0.22.0
fs-extra: 11.3.0
optionalDependencies:
@@ -19777,7 +20079,28 @@ snapshots:
- vue-tsc
- webpack
- '@module-federation/rspack@0.22.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))':
+ '@module-federation/rsbuild-plugin@0.22.0(@rsbuild/core@1.7.2)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)':
+ dependencies:
+ '@module-federation/enhanced': 0.22.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
+ '@module-federation/node': 2.7.26(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
+ '@module-federation/sdk': 0.22.0
+ fs-extra: 11.3.0
+ optionalDependencies:
+ '@rsbuild/core': 1.7.2
+ transitivePeerDependencies:
+ - '@rspack/core'
+ - bufferutil
+ - debug
+ - next
+ - react
+ - react-dom
+ - supports-color
+ - typescript
+ - utf-8-validate
+ - vue-tsc
+ - webpack
+
+ '@module-federation/rspack@0.22.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))':
dependencies:
'@module-federation/bridge-react-webpack-plugin': 0.22.0
'@module-federation/dts-plugin': 0.22.0(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))
@@ -19786,7 +20109,7 @@ snapshots:
'@module-federation/manifest': 0.22.0(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))
'@module-federation/runtime-tools': 0.22.0
'@module-federation/sdk': 0.22.0
- '@rspack/core': 1.7.0(@swc/helpers@0.5.18)
+ '@rspack/core': 1.7.1(@swc/helpers@0.5.18)
btoa: 1.2.1
optionalDependencies:
typescript: 5.9.3
@@ -19851,12 +20174,12 @@ snapshots:
'@module-federation/sdk@0.22.0': {}
- '@module-federation/storybook-addon@5.0.2(@module-federation/sdk@0.22.0)(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack-virtual-modules@0.6.2)(webpack@5.104.1)':
+ '@module-federation/storybook-addon@5.0.2(@module-federation/sdk@0.22.0)(@rsbuild/core@1.7.2)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack-virtual-modules@0.6.2)(webpack@5.104.1)':
dependencies:
- '@module-federation/enhanced': 0.22.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
+ '@module-federation/enhanced': 0.22.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(vue-tsc@3.2.1(typescript@5.9.3))(webpack@5.104.1)
optionalDependencies:
'@module-federation/sdk': 0.22.0
- '@rsbuild/core': 1.7.1
+ '@rsbuild/core': 1.7.2
webpack: 5.104.1(webpack-cli@6.0.1)
webpack-virtual-modules: 0.6.2
transitivePeerDependencies:
@@ -20528,6 +20851,14 @@ snapshots:
core-js: 3.47.0
jiti: 2.6.1
+ '@rsbuild/core@1.7.2':
+ dependencies:
+ '@rspack/core': 1.7.1(@swc/helpers@0.5.18)
+ '@rspack/lite-tapable': 1.1.0
+ '@swc/helpers': 0.5.18
+ core-js: 3.47.0
+ jiti: 2.6.1
+
'@rsbuild/plugin-babel@1.0.6(@rsbuild/core@1.6.15)':
dependencies:
'@babel/core': 7.28.0
@@ -20556,6 +20887,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@rsbuild/plugin-babel@1.0.6(@rsbuild/core@1.7.2)':
+ dependencies:
+ '@babel/core': 7.28.0
+ '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.0)
+ '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.0)
+ '@babel/preset-typescript': 7.27.1(@babel/core@7.28.0)
+ '@rsbuild/core': 1.7.2
+ '@types/babel__core': 7.20.5
+ deepmerge: 4.3.1
+ reduce-configs: 1.1.0
+ upath: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
'@rsbuild/plugin-check-syntax@1.5.0(@rsbuild/core@1.7.1)':
dependencies:
acorn: 8.15.0
@@ -20585,6 +20930,12 @@ snapshots:
deepmerge: 4.3.1
reduce-configs: 1.1.1
+ '@rsbuild/plugin-less@1.5.0(@rsbuild/core@1.7.2)':
+ dependencies:
+ '@rsbuild/core': 1.7.2
+ deepmerge: 4.3.1
+ reduce-configs: 1.1.1
+
'@rsbuild/plugin-mdx@1.1.0(@rsbuild/core@1.7.1)(webpack@5.104.1)':
dependencies:
'@mdx-js/loader': 3.1.1(webpack@5.104.1)
@@ -20604,6 +20955,16 @@ snapshots:
transitivePeerDependencies:
- preact
+ '@rsbuild/plugin-preact@1.7.0(@rsbuild/core@1.7.2)(preact@10.28.2)':
+ dependencies:
+ '@prefresh/core': 1.5.9(preact@10.28.2)
+ '@prefresh/utils': 1.2.1
+ '@rsbuild/core': 1.7.2
+ '@rspack/plugin-preact-refresh': 1.1.4(@prefresh/core@1.5.9(preact@10.28.2))(@prefresh/utils@1.2.1)
+ '@swc/plugin-prefresh': 12.3.0
+ transitivePeerDependencies:
+ - preact
+
'@rsbuild/plugin-react@1.3.5(@rsbuild/core@1.3.22)(webpack-hot-middleware@2.26.1)':
dependencies:
'@rsbuild/core': 1.3.22
@@ -20628,6 +20989,14 @@ snapshots:
transitivePeerDependencies:
- webpack-hot-middleware
+ '@rsbuild/plugin-react@1.4.2(@rsbuild/core@1.7.2)(webpack-hot-middleware@2.26.1)':
+ dependencies:
+ '@rsbuild/core': 1.7.2
+ '@rspack/plugin-react-refresh': 1.5.2(react-refresh@0.18.0)(webpack-hot-middleware@2.26.1)
+ react-refresh: 0.18.0
+ transitivePeerDependencies:
+ - webpack-hot-middleware
+
'@rsbuild/plugin-sass@1.3.1(@rsbuild/core@1.3.22)':
dependencies:
'@rsbuild/core': 1.3.22
@@ -20637,9 +21006,9 @@ snapshots:
reduce-configs: 1.1.1
sass-embedded: 1.93.2
- '@rsbuild/plugin-sass@1.4.0(@rsbuild/core@1.7.1)':
+ '@rsbuild/plugin-sass@1.4.0(@rsbuild/core@1.7.2)':
dependencies:
- '@rsbuild/core': 1.7.1
+ '@rsbuild/core': 1.7.2
deepmerge: 4.3.1
loader-utils: 2.0.4
postcss: 8.5.6
@@ -20657,6 +21026,17 @@ snapshots:
- solid-js
- supports-color
+ '@rsbuild/plugin-solid@1.0.6(@babel/core@7.28.5)(@rsbuild/core@1.7.2)(solid-js@1.9.10)':
+ dependencies:
+ '@rsbuild/core': 1.7.2
+ '@rsbuild/plugin-babel': 1.0.6(@rsbuild/core@1.7.2)
+ babel-preset-solid: 1.9.8(@babel/core@7.28.5)(solid-js@1.9.10)
+ solid-refresh: 0.6.3(solid-js@1.9.10)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - solid-js
+ - supports-color
+
'@rsbuild/plugin-styled-components@1.6.0(@rsbuild/core@1.7.1)':
dependencies:
'@swc/plugin-styled-components': 12.0.1
@@ -20682,25 +21062,37 @@ snapshots:
- svelte
- typescript
- '@rsbuild/plugin-type-check@1.3.2(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(typescript@5.9.3)':
+ '@rsbuild/plugin-type-check@1.3.2(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(typescript@5.9.3)':
dependencies:
deepmerge: 4.3.1
json5: 2.2.3
reduce-configs: 1.1.1
- ts-checker-rspack-plugin: 1.2.3(@rspack/core@1.7.0(@swc/helpers@0.5.18))(typescript@5.9.3)
+ ts-checker-rspack-plugin: 1.2.3(@rspack/core@1.7.1(@swc/helpers@0.5.18))(typescript@5.9.3)
optionalDependencies:
'@rsbuild/core': 1.7.1
transitivePeerDependencies:
- '@rspack/core'
- typescript
+ '@rsbuild/plugin-type-check@1.3.2(@rsbuild/core@1.7.2)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(typescript@5.9.3)':
+ dependencies:
+ deepmerge: 4.3.1
+ json5: 2.2.3
+ reduce-configs: 1.1.1
+ ts-checker-rspack-plugin: 1.2.3(@rspack/core@1.7.1(@swc/helpers@0.5.18))(typescript@5.9.3)
+ optionalDependencies:
+ '@rsbuild/core': 1.7.2
+ transitivePeerDependencies:
+ - '@rspack/core'
+ - typescript
+
'@rsbuild/plugin-umd@1.0.5(@rsbuild/core@1.7.1)':
optionalDependencies:
'@rsbuild/core': 1.7.1
- '@rsbuild/plugin-vue2@1.0.5(@rsbuild/core@1.7.1)(@vue/compiler-sfc@3.5.24)(css-loader@7.1.2(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1))':
+ '@rsbuild/plugin-vue2@1.0.5(@rsbuild/core@1.7.1)(@vue/compiler-sfc@3.5.24)(css-loader@7.1.2(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1))':
dependencies:
- vue-loader: 15.11.1(@vue/compiler-sfc@3.5.24)(css-loader@7.1.2(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1))(ejs@3.1.10)(handlebars@4.7.8)(lodash@4.17.21)(pug@3.0.2)(webpack@5.104.1)
+ vue-loader: 15.11.1(@vue/compiler-sfc@3.5.24)(css-loader@7.1.2(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1))(webpack@5.104.1)
webpack: 5.104.1(webpack-cli@6.0.1)
optionalDependencies:
'@rsbuild/core': 1.7.1
@@ -20783,7 +21175,29 @@ snapshots:
'@rsdoctor/client@1.4.0': {}
- '@rsdoctor/core@1.4.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)':
+ '@rsdoctor/core@1.4.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)':
+ dependencies:
+ '@rsbuild/plugin-check-syntax': 1.5.0(@rsbuild/core@1.7.1)
+ '@rsdoctor/graph': 1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ '@rsdoctor/sdk': 1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ '@rsdoctor/types': 1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ '@rsdoctor/utils': 1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ browserslist-load-config: 1.0.1
+ enhanced-resolve: 5.12.0
+ es-toolkit: 1.41.0
+ filesize: 10.1.6
+ fs-extra: 11.3.2
+ semver: 7.7.3
+ source-map: 0.7.6
+ transitivePeerDependencies:
+ - '@rsbuild/core'
+ - '@rspack/core'
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+ - webpack
+
+ '@rsdoctor/core@1.4.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)':
dependencies:
'@rsbuild/plugin-check-syntax': 1.5.0(@rsbuild/core@1.7.1)
'@rsdoctor/graph': 1.4.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)
@@ -20816,9 +21230,36 @@ snapshots:
- '@rspack/core'
- webpack
- '@rsdoctor/rspack-plugin@1.4.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)':
+ '@rsdoctor/graph@1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)':
+ dependencies:
+ '@rsdoctor/types': 1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ '@rsdoctor/utils': 1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ es-toolkit: 1.41.0
+ path-browserify: 1.0.1
+ source-map: 0.7.6
+ transitivePeerDependencies:
+ - '@rspack/core'
+ - webpack
+
+ '@rsdoctor/rspack-plugin@1.4.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)':
+ dependencies:
+ '@rsdoctor/core': 1.4.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ '@rsdoctor/graph': 1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ '@rsdoctor/sdk': 1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ '@rsdoctor/types': 1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ '@rsdoctor/utils': 1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ optionalDependencies:
+ '@rspack/core': 1.7.1(@swc/helpers@0.5.18)
+ transitivePeerDependencies:
+ - '@rsbuild/core'
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+ - webpack
+
+ '@rsdoctor/rspack-plugin@1.4.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)':
dependencies:
- '@rsdoctor/core': 1.4.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)
+ '@rsdoctor/core': 1.4.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)
'@rsdoctor/graph': 1.4.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)
'@rsdoctor/sdk': 1.4.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)
'@rsdoctor/types': 1.4.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)
@@ -20848,6 +21289,22 @@ snapshots:
- utf-8-validate
- webpack
+ '@rsdoctor/sdk@1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)':
+ dependencies:
+ '@rsdoctor/client': 1.4.0
+ '@rsdoctor/graph': 1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ '@rsdoctor/types': 1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ '@rsdoctor/utils': 1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ safer-buffer: 2.1.2
+ socket.io: 4.8.1
+ tapable: 2.2.3
+ transitivePeerDependencies:
+ - '@rspack/core'
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+ - webpack
+
'@rsdoctor/types@1.4.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)':
dependencies:
'@types/connect': 3.4.38
@@ -20858,6 +21315,16 @@ snapshots:
'@rspack/core': 1.7.0(@swc/helpers@0.5.18)
webpack: 5.104.1(webpack-cli@6.0.1)
+ '@rsdoctor/types@1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)':
+ dependencies:
+ '@types/connect': 3.4.38
+ '@types/estree': 1.0.5
+ '@types/tapable': 2.2.7
+ source-map: 0.7.6
+ optionalDependencies:
+ '@rspack/core': 1.7.1(@swc/helpers@0.5.18)
+ webpack: 5.104.1(webpack-cli@6.0.1)
+
'@rsdoctor/utils@1.4.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(webpack@5.104.1)':
dependencies:
'@babel/code-frame': 7.26.2
@@ -20879,6 +21346,27 @@ snapshots:
- '@rspack/core'
- webpack
+ '@rsdoctor/utils@1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)':
+ dependencies:
+ '@babel/code-frame': 7.26.2
+ '@rsdoctor/types': 1.4.0(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ '@types/estree': 1.0.5
+ acorn: 8.15.0
+ acorn-import-attributes: 1.9.5(acorn@8.15.0)
+ acorn-walk: 8.3.4
+ deep-eql: 4.1.4
+ envinfo: 7.21.0
+ fs-extra: 11.3.2
+ get-port: 5.1.1
+ json-stream-stringify: 3.0.1
+ lines-and-columns: 2.0.4
+ picocolors: 1.1.1
+ rslog: 1.2.11
+ strip-ansi: 6.0.1
+ transitivePeerDependencies:
+ - '@rspack/core'
+ - webpack
+
'@rslib/core@0.19.1(typescript@5.9.3)':
dependencies:
'@rsbuild/core': 1.7.1
@@ -20900,6 +21388,9 @@ snapshots:
'@rspack/binding-darwin-arm64@1.7.0':
optional: true
+ '@rspack/binding-darwin-arm64@1.7.1':
+ optional: true
+
'@rspack/binding-darwin-x64@1.3.12':
optional: true
@@ -20912,6 +21403,9 @@ snapshots:
'@rspack/binding-darwin-x64@1.7.0':
optional: true
+ '@rspack/binding-darwin-x64@1.7.1':
+ optional: true
+
'@rspack/binding-linux-arm64-gnu@1.3.12':
optional: true
@@ -20924,6 +21418,9 @@ snapshots:
'@rspack/binding-linux-arm64-gnu@1.7.0':
optional: true
+ '@rspack/binding-linux-arm64-gnu@1.7.1':
+ optional: true
+
'@rspack/binding-linux-arm64-musl@1.3.12':
optional: true
@@ -20936,6 +21433,9 @@ snapshots:
'@rspack/binding-linux-arm64-musl@1.7.0':
optional: true
+ '@rspack/binding-linux-arm64-musl@1.7.1':
+ optional: true
+
'@rspack/binding-linux-x64-gnu@1.3.12':
optional: true
@@ -20948,6 +21448,9 @@ snapshots:
'@rspack/binding-linux-x64-gnu@1.7.0':
optional: true
+ '@rspack/binding-linux-x64-gnu@1.7.1':
+ optional: true
+
'@rspack/binding-linux-x64-musl@1.3.12':
optional: true
@@ -20960,6 +21463,9 @@ snapshots:
'@rspack/binding-linux-x64-musl@1.7.0':
optional: true
+ '@rspack/binding-linux-x64-musl@1.7.1':
+ optional: true
+
'@rspack/binding-wasm32-wasi@1.6.7':
dependencies:
'@napi-rs/wasm-runtime': 1.0.7
@@ -20975,6 +21481,11 @@ snapshots:
'@napi-rs/wasm-runtime': 1.0.7
optional: true
+ '@rspack/binding-wasm32-wasi@1.7.1':
+ dependencies:
+ '@napi-rs/wasm-runtime': 1.0.7
+ optional: true
+
'@rspack/binding-win32-arm64-msvc@1.3.12':
optional: true
@@ -20987,6 +21498,9 @@ snapshots:
'@rspack/binding-win32-arm64-msvc@1.7.0':
optional: true
+ '@rspack/binding-win32-arm64-msvc@1.7.1':
+ optional: true
+
'@rspack/binding-win32-ia32-msvc@1.3.12':
optional: true
@@ -20999,6 +21513,9 @@ snapshots:
'@rspack/binding-win32-ia32-msvc@1.7.0':
optional: true
+ '@rspack/binding-win32-ia32-msvc@1.7.1':
+ optional: true
+
'@rspack/binding-win32-x64-msvc@1.3.12':
optional: true
@@ -21011,6 +21528,9 @@ snapshots:
'@rspack/binding-win32-x64-msvc@1.7.0':
optional: true
+ '@rspack/binding-win32-x64-msvc@1.7.1':
+ optional: true
+
'@rspack/binding@1.3.12':
optionalDependencies:
'@rspack/binding-darwin-arm64': 1.3.12
@@ -21062,6 +21582,19 @@ snapshots:
'@rspack/binding-win32-ia32-msvc': 1.7.0
'@rspack/binding-win32-x64-msvc': 1.7.0
+ '@rspack/binding@1.7.1':
+ optionalDependencies:
+ '@rspack/binding-darwin-arm64': 1.7.1
+ '@rspack/binding-darwin-x64': 1.7.1
+ '@rspack/binding-linux-arm64-gnu': 1.7.1
+ '@rspack/binding-linux-arm64-musl': 1.7.1
+ '@rspack/binding-linux-x64-gnu': 1.7.1
+ '@rspack/binding-linux-x64-musl': 1.7.1
+ '@rspack/binding-wasm32-wasi': 1.7.1
+ '@rspack/binding-win32-arm64-msvc': 1.7.1
+ '@rspack/binding-win32-ia32-msvc': 1.7.1
+ '@rspack/binding-win32-x64-msvc': 1.7.1
+
'@rspack/cli@1.7.0(@rspack/core@1.7.0(@swc/helpers@0.5.18))(@types/express@5.0.6)':
dependencies:
'@discoveryjs/json-ext': 0.5.7
@@ -21127,6 +21660,14 @@ snapshots:
optionalDependencies:
'@swc/helpers': 0.5.18
+ '@rspack/core@1.7.1(@swc/helpers@0.5.18)':
+ dependencies:
+ '@module-federation/runtime-tools': 0.22.0
+ '@rspack/binding': 1.7.1
+ '@rspack/lite-tapable': 1.1.0
+ optionalDependencies:
+ '@swc/helpers': 0.5.18
+
'@rspack/dev-server@1.1.5(@rspack/core@1.7.0(@swc/helpers@0.5.18))(@types/express@4.17.21)(webpack@5.104.1)':
dependencies:
'@rspack/core': 1.7.0(@swc/helpers@0.5.18)
@@ -21437,14 +21978,66 @@ snapshots:
react-helmet-async: 1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react-syntax-highlighter: 15.6.1(react@18.3.1)
- '@rstest/core@0.7.8(jsdom@27.4.0(postcss@8.5.6))':
+ '@rstest/adapter-rsbuild@0.1.0(@rsbuild/core@1.7.2)':
+ dependencies:
+ '@rsbuild/core': 1.7.2
+
+ '@rstest/adapter-rslib@0.1.1(@rslib/core@0.19.1(typescript@5.9.3))(typescript@5.9.3)':
+ dependencies:
+ '@rslib/core': 0.19.1(typescript@5.9.3)
+ optionalDependencies:
+ typescript: 5.9.3
+
+ '@rstest/browser-react@0.7.9(@rstest/core@0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6)))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
+ dependencies:
+ '@rstest/core': 0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+
+ '@rstest/browser@0.7.9(@rstest/core@0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6)))(playwright@1.57.0)':
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.31
+ '@rstest/core': 0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))
+ convert-source-map: 2.0.0
+ open-editor: 4.1.1
+ pathe: 2.0.3
+ sirv: 2.0.4
+ ws: 8.18.3
+ optionalDependencies:
+ playwright: 1.57.0
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
+ '@rstest/core@0.7.8(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))':
+ dependencies:
+ '@rsbuild/core': 1.7.1
+ '@types/chai': 5.2.3
+ tinypool: 1.1.1
+ optionalDependencies:
+ happy-dom: 18.0.1
+ jsdom: 27.4.0(postcss@8.5.6)
+
+ '@rstest/core@0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))':
dependencies:
'@rsbuild/core': 1.7.1
'@types/chai': 5.2.3
tinypool: 1.1.1
optionalDependencies:
+ happy-dom: 18.0.1
jsdom: 27.4.0(postcss@8.5.6)
+ '@rstest/coverage-istanbul@0.1.6(@rstest/core@0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6)))':
+ dependencies:
+ '@rstest/core': 0.7.9(happy-dom@18.0.1)(jsdom@27.4.0(postcss@8.5.6))
+ istanbul-lib-coverage: 3.2.2
+ istanbul-lib-report: 3.0.1
+ istanbul-lib-source-maps: 5.0.6
+ istanbul-reports: 3.2.0
+ swc-plugin-coverage-instrument: 0.0.32
+ transitivePeerDependencies:
+ - supports-color
+
'@selderee/plugin-htmlparser2@0.11.0':
dependencies:
domhandler: 5.0.3
@@ -22352,6 +22945,10 @@ snapshots:
dependencies:
'@types/node': 24.10.4
+ '@types/node@20.19.27':
+ dependencies:
+ undici-types: 6.21.0
+
'@types/node@24.10.0':
dependencies:
undici-types: 7.16.0
@@ -22443,7 +23040,7 @@ snapshots:
'@types/webpack@5.28.5(webpack-cli@6.0.1)':
dependencies:
- '@types/node': 25.0.3
+ '@types/node': 24.10.4
tapable: 2.3.0
webpack: 5.104.1(webpack-cli@6.0.1)
transitivePeerDependencies:
@@ -22453,6 +23050,8 @@ snapshots:
- webpack-cli
optional: true
+ '@types/whatwg-mimetype@3.0.2': {}
+
'@types/ws@8.5.10':
dependencies:
'@types/node': 24.10.4
@@ -24392,6 +24991,20 @@ snapshots:
'@rspack/core': 1.7.0(@swc/helpers@0.5.18)
webpack: 5.104.1(webpack-cli@6.0.1)
+ css-loader@7.1.2(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1):
+ dependencies:
+ icss-utils: 5.1.0(postcss@8.4.49)
+ postcss: 8.4.49
+ postcss-modules-extract-imports: 3.1.0(postcss@8.4.49)
+ postcss-modules-local-by-default: 4.0.5(postcss@8.4.49)
+ postcss-modules-scope: 3.2.0(postcss@8.4.49)
+ postcss-modules-values: 4.0.0(postcss@8.4.49)
+ postcss-value-parser: 4.2.0
+ semver: 7.6.3
+ optionalDependencies:
+ '@rspack/core': 1.7.1(@swc/helpers@0.5.18)
+ webpack: 5.104.1(webpack-cli@6.0.1)
+
css-mediaquery@0.1.2: {}
css-select@4.3.0:
@@ -24927,6 +25540,8 @@ snapshots:
fromentries: 1.3.2
java-properties: 1.0.2
+ env-editor@1.3.0: {}
+
env-paths@2.2.1: {}
envinfo@7.14.0: {}
@@ -25785,6 +26400,9 @@ snapshots:
fs.realpath@1.0.0: {}
+ fsevents@2.3.2:
+ optional: true
+
fsevents@2.3.3:
optional: true
@@ -25975,6 +26593,12 @@ snapshots:
uglify-js: 3.19.3
optional: true
+ happy-dom@18.0.1:
+ dependencies:
+ '@types/node': 20.19.27
+ '@types/whatwg-mimetype': 3.0.2
+ whatwg-mimetype: 3.0.0
+
has-ansi@2.0.0:
dependencies:
ansi-regex: 2.1.1
@@ -26363,6 +26987,17 @@ snapshots:
'@rspack/core': 1.7.0(@swc/helpers@0.5.18)
webpack: 5.104.1(webpack-cli@6.0.1)
+ html-webpack-plugin@5.6.5(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1):
+ dependencies:
+ '@types/html-minifier-terser': 6.1.0
+ html-minifier-terser: 6.1.0
+ lodash: 4.17.21
+ pretty-error: 4.0.0
+ tapable: 2.3.0
+ optionalDependencies:
+ '@rspack/core': 1.7.1(@swc/helpers@0.5.18)
+ webpack: 5.104.1(webpack-cli@6.0.1)
+
htmlparser2@10.0.0:
dependencies:
domelementtype: 2.3.0
@@ -26848,7 +27483,7 @@ snapshots:
istanbul-lib-source-maps@5.0.6:
dependencies:
- '@jridgewell/trace-mapping': 0.3.29
+ '@jridgewell/trace-mapping': 0.3.31
debug: 4.4.3
istanbul-lib-coverage: 3.2.2
transitivePeerDependencies:
@@ -26859,6 +27494,11 @@ snapshots:
html-escaper: 2.0.2
istanbul-lib-report: 3.0.1
+ istanbul-reports@3.2.0:
+ dependencies:
+ html-escaper: 2.0.2
+ istanbul-lib-report: 3.0.1
+
iterare@1.2.1: {}
jackspeak@3.4.3:
@@ -27532,6 +28172,10 @@ snapshots:
lilconfig@3.1.3: {}
+ line-column-path@3.0.0:
+ dependencies:
+ type-fest: 2.19.0
+
lines-and-columns@1.2.4: {}
lines-and-columns@2.0.4: {}
@@ -29031,6 +29675,13 @@ snapshots:
regex: 6.0.1
regex-recursion: 6.0.2
+ open-editor@4.1.1:
+ dependencies:
+ env-editor: 1.3.0
+ execa: 5.1.1
+ line-column-path: 3.0.0
+ open: 8.4.2
+
open@10.2.0:
dependencies:
default-browser: 5.4.0
@@ -29314,6 +29965,14 @@ snapshots:
exsolve: 1.0.7
pathe: 2.0.3
+ playwright-core@1.57.0: {}
+
+ playwright@1.57.0:
+ dependencies:
+ playwright-core: 1.57.0
+ optionalDependencies:
+ fsevents: 2.3.2
+
pnpm-workspace-yaml@1.3.0:
dependencies:
yaml: 2.8.1
@@ -30250,6 +30909,13 @@ snapshots:
optionalDependencies:
'@rsbuild/core': 1.7.1
+ rsbuild-plugin-html-minifier-terser@1.1.2(@rsbuild/core@1.7.2):
+ dependencies:
+ '@types/html-minifier-terser': 7.0.2
+ html-minifier-terser: 7.2.0
+ optionalDependencies:
+ '@rsbuild/core': 1.7.2
+
rsbuild-plugin-unplugin-vue@0.1.0(@rsbuild/core@1.7.1)(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(vue@3.5.21(typescript@5.9.3))(yaml@2.8.1):
dependencies:
unplugin-vue: 6.2.0(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(vue@3.5.21(typescript@5.9.3))(yaml@2.8.1)
@@ -30270,6 +30936,26 @@ snapshots:
- vue
- yaml
+ rsbuild-plugin-unplugin-vue@0.1.0(@rsbuild/core@1.7.2)(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(vue@3.5.21(typescript@5.9.3))(yaml@2.8.1):
+ dependencies:
+ unplugin-vue: 6.2.0(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(vue@3.5.21(typescript@5.9.3))(yaml@2.8.1)
+ optionalDependencies:
+ '@rsbuild/core': 1.7.2
+ transitivePeerDependencies:
+ - '@types/node'
+ - jiti
+ - less
+ - lightningcss
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - vue
+ - yaml
+
rslog@1.2.11: {}
rspack-manifest-plugin@5.2.0(@rspack/core@1.7.0(@swc/helpers@0.5.18)):
@@ -30981,18 +31667,26 @@ snapshots:
es-errors: 1.3.0
internal-slot: 1.1.0
- storybook-addon-rslib@3.2.0(@rsbuild/core@1.7.1)(@rslib/core@0.19.1(typescript@5.9.3))(storybook-builder-rsbuild@3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1)))(typescript@5.9.3):
+ storybook-addon-rslib@3.2.0(@rsbuild/core@1.7.1)(@rslib/core@0.19.1(typescript@5.9.3))(storybook-builder-rsbuild@3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1)))(typescript@5.9.3):
dependencies:
'@rsbuild/core': 1.7.1
'@rslib/core': 0.19.1(typescript@5.9.3)
- storybook-builder-rsbuild: 3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))
+ storybook-builder-rsbuild: 3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))
+ optionalDependencies:
+ typescript: 5.9.3
+
+ storybook-addon-rslib@3.2.0(@rsbuild/core@1.7.2)(@rslib/core@0.19.1(typescript@5.9.3))(storybook-builder-rsbuild@3.2.0(@rsbuild/core@1.7.2)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1)))(typescript@5.9.3):
+ dependencies:
+ '@rsbuild/core': 1.7.2
+ '@rslib/core': 0.19.1(typescript@5.9.3)
+ storybook-builder-rsbuild: 3.2.0(@rsbuild/core@1.7.2)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))
optionalDependencies:
typescript: 5.9.3
- storybook-builder-rsbuild@3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1)):
+ storybook-builder-rsbuild@3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1)):
dependencies:
'@rsbuild/core': 1.7.1
- '@rsbuild/plugin-type-check': 1.3.2(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(typescript@5.9.3)
+ '@rsbuild/plugin-type-check': 1.3.2(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(typescript@5.9.3)
'@vitest/mocker': 3.2.4(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))
browser-assert: 1.2.1
case-sensitive-paths-webpack-plugin: 2.4.0
@@ -31020,7 +31714,38 @@ snapshots:
- msw
- vite
- storybook-react-rsbuild@3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.53.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.104.1(esbuild@0.27.2)):
+ storybook-builder-rsbuild@3.2.0(@rsbuild/core@1.7.2)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1)):
+ dependencies:
+ '@rsbuild/core': 1.7.2
+ '@rsbuild/plugin-type-check': 1.3.2(@rsbuild/core@1.7.2)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(typescript@5.9.3)
+ '@vitest/mocker': 3.2.4(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))
+ browser-assert: 1.2.1
+ case-sensitive-paths-webpack-plugin: 2.4.0
+ cjs-module-lexer: 2.1.1
+ constants-browserify: 1.0.0
+ es-module-lexer: 1.7.0
+ fs-extra: 11.3.2
+ magic-string: 0.30.21
+ path-browserify: 1.0.1
+ picocolors: 1.1.1
+ process: 0.11.10
+ rsbuild-plugin-html-minifier-terser: 1.1.2(@rsbuild/core@1.7.2)
+ sirv: 2.0.4
+ storybook: 10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ ts-dedent: 2.2.0
+ url: 0.11.4
+ util: 0.12.5
+ util-deprecate: 1.0.2
+ optionalDependencies:
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - '@rspack/core'
+ - msw
+ - vite
+
+ storybook-react-rsbuild@3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.53.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.104.1(esbuild@0.27.2)):
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.53.3)
'@rsbuild/core': 1.7.1
@@ -31034,7 +31759,7 @@ snapshots:
react-dom: 19.2.3(react@19.2.3)
resolve: 1.22.11
storybook: 10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- storybook-builder-rsbuild: 3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))
+ storybook-builder-rsbuild: 3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))
tsconfig-paths: 4.2.0
optionalDependencies:
typescript: 5.9.3
@@ -31046,10 +31771,10 @@ snapshots:
- vite
- webpack
- storybook-react-rsbuild@3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.53.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.104.1):
+ storybook-react-rsbuild@3.2.0(@rsbuild/core@1.7.2)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@4.53.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))(webpack@5.104.1):
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.53.3)
- '@rsbuild/core': 1.7.1
+ '@rsbuild/core': 1.7.2
'@storybook/react': 10.1.10(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)
'@storybook/react-docgen-typescript-plugin': 1.0.1(typescript@5.9.3)(webpack@5.104.1)
find-up: 5.0.0
@@ -31060,7 +31785,7 @@ snapshots:
react-dom: 19.2.3(react@19.2.3)
resolve: 1.22.11
storybook: 10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- storybook-builder-rsbuild: 3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))
+ storybook-builder-rsbuild: 3.2.0(@rsbuild/core@1.7.2)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))
tsconfig-paths: 4.2.0
optionalDependencies:
typescript: 5.9.3
@@ -31072,12 +31797,12 @@ snapshots:
- vite
- webpack
- storybook-vue3-rsbuild@3.2.0(@babel/preset-env@7.28.5(@babel/core@7.28.5))(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.3))(webpack@5.104.1(esbuild@0.27.2)):
+ storybook-vue3-rsbuild@3.2.0(@babel/preset-env@7.28.5(@babel/core@7.28.5))(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.3))(webpack@5.104.1(esbuild@0.27.2)):
dependencies:
'@rsbuild/core': 1.7.1
'@storybook/vue3': 10.1.11(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vue@3.5.21(typescript@5.9.3))
storybook: 10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
- storybook-builder-rsbuild: 3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.0(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))
+ storybook-builder-rsbuild: 3.2.0(@rsbuild/core@1.7.1)(@rspack/core@1.7.1(@swc/helpers@0.5.18))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.1.11(@testing-library/dom@10.4.0)(prettier@2.8.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@7.2.6(@types/node@25.0.3)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.97.1)(stylus@0.64.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.8.1))
vue: 3.5.21(typescript@5.9.3)
vue-docgen-api: 4.79.2(vue@3.5.21(typescript@5.9.3))
vue-docgen-loader: 2.0.1(@babel/preset-env@7.28.5(@babel/core@7.28.5))(vue-docgen-api@4.79.2(vue@3.5.21(typescript@5.9.3)))(webpack@5.104.1(esbuild@0.27.2))
@@ -31462,6 +32187,8 @@ snapshots:
csso: 5.0.5
picocolors: 1.1.1
+ swc-plugin-coverage-instrument@0.0.32: {}
+
swc-plugin-css-modules@8.0.0: {}
swc-plugin-vue-jsx@0.4.0(@swc/core@1.15.8(@swc/helpers@0.5.18)):
@@ -31756,6 +32483,19 @@ snapshots:
optionalDependencies:
'@rspack/core': 1.7.0(@swc/helpers@0.5.18)
+ ts-checker-rspack-plugin@1.2.3(@rspack/core@1.7.1(@swc/helpers@0.5.18))(typescript@5.9.3):
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@rspack/lite-tapable': 1.1.0
+ chokidar: 3.6.0
+ is-glob: 4.0.3
+ memfs: 4.51.1
+ minimatch: 9.0.5
+ picocolors: 1.1.1
+ typescript: 5.9.3
+ optionalDependencies:
+ '@rspack/core': 1.7.1(@swc/helpers@0.5.18)
+
ts-dedent@2.2.0: {}
ts-interface-checker@0.1.13: {}
@@ -31936,6 +32676,8 @@ snapshots:
quansync: 1.0.0
unconfig-core: 7.4.2
+ undici-types@6.21.0: {}
+
undici-types@7.16.0: {}
unhead@2.0.19:
@@ -32267,7 +33009,7 @@ snapshots:
v8-to-istanbul@9.2.0:
dependencies:
- '@jridgewell/trace-mapping': 0.3.29
+ '@jridgewell/trace-mapping': 0.3.31
'@types/istanbul-lib-coverage': 2.0.6
convert-source-map: 2.0.0
@@ -32512,6 +33254,72 @@ snapshots:
- walrus
- whiskers
+ vue-loader@15.11.1(@vue/compiler-sfc@3.5.24)(css-loader@7.1.2(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1))(webpack@5.104.1):
+ dependencies:
+ '@vue/component-compiler-utils': 3.3.0(ejs@3.1.10)(handlebars@4.7.8)(lodash@4.17.21)(pug@3.0.2)
+ css-loader: 7.1.2(@rspack/core@1.7.1(@swc/helpers@0.5.18))(webpack@5.104.1)
+ hash-sum: 1.0.2
+ loader-utils: 1.4.2
+ vue-hot-reload-api: 2.3.4
+ vue-style-loader: 4.1.3
+ webpack: 5.104.1(webpack-cli@6.0.1)
+ optionalDependencies:
+ '@vue/compiler-sfc': 3.5.24
+ transitivePeerDependencies:
+ - arc-templates
+ - atpl
+ - babel-core
+ - bracket-template
+ - coffee-script
+ - dot
+ - dust
+ - dustjs-helpers
+ - dustjs-linkedin
+ - eco
+ - ect
+ - ejs
+ - haml-coffee
+ - hamlet
+ - hamljs
+ - handlebars
+ - hogan.js
+ - htmling
+ - jade
+ - jazz
+ - jqtpl
+ - just
+ - liquid-node
+ - liquor
+ - lodash
+ - marko
+ - mote
+ - mustache
+ - nunjucks
+ - plates
+ - pug
+ - qejs
+ - ractive
+ - razor-tmpl
+ - react
+ - react-dom
+ - slm
+ - squirrelly
+ - swig
+ - swig-templates
+ - teacup
+ - templayed
+ - then-jade
+ - then-pug
+ - tinyliquid
+ - toffee
+ - twig
+ - twing
+ - underscore
+ - vash
+ - velocityjs
+ - walrus
+ - whiskers
+
vue-loader@17.4.2(vue@3.2.45)(webpack@5.104.1):
dependencies:
chalk: 4.1.2
@@ -32864,6 +33672,8 @@ snapshots:
dependencies:
iconv-lite: 0.6.3
+ whatwg-mimetype@3.0.0: {}
+
whatwg-mimetype@4.0.0: {}
whatwg-url@14.2.0:
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index e54caeb4..e79c588c 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -4,4 +4,5 @@ packages:
- "rslib/**"
- "rspress/**"
- "rsdoctor/**"
+ - "rstest/**"
- "!**/dist"
diff --git a/rstest/browser-rsbuild-react/package.json b/rstest/browser-rsbuild-react/package.json
new file mode 100644
index 00000000..bb677818
--- /dev/null
+++ b/rstest/browser-rsbuild-react/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "rstest-browser-rsbuild-react",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "dev": "rsbuild dev",
+ "build": "rsbuild build",
+ "test": "rstest run"
+ },
+ "devDependencies": {
+ "@rsbuild/core": "^1.4.2",
+ "@rsbuild/plugin-react": "^1.4.2",
+ "@rstest/adapter-rsbuild": "^0.1.0",
+ "@rstest/browser": "^0.7.9",
+ "@rstest/browser-react": "^0.7.9",
+ "@rstest/core": "^0.7.9",
+ "@testing-library/dom": "^10.4.0",
+ "@testing-library/user-event": "^14.6.1",
+ "@types/react": "^19.1.8",
+ "@types/react-dom": "^19.1.6",
+ "playwright": "^1.57.0",
+ "react": "^19.2.3",
+ "react-dom": "^19.2.3",
+ "typescript": "^5.8.3"
+ }
+}
diff --git a/rstest/browser-rsbuild-react/rsbuild.config.ts b/rstest/browser-rsbuild-react/rsbuild.config.ts
new file mode 100644
index 00000000..1e6583b3
--- /dev/null
+++ b/rstest/browser-rsbuild-react/rsbuild.config.ts
@@ -0,0 +1,19 @@
+import path from 'node:path';
+import { defineConfig } from '@rsbuild/core';
+import { pluginReact } from '@rsbuild/plugin-react';
+
+export default defineConfig({
+ plugins: [pluginReact()],
+ source: {
+ entry: {
+ index: './src/index.tsx',
+ },
+ alias: {
+ '@': path.resolve(__dirname, 'src'),
+ '@components': path.resolve(__dirname, 'src/components'),
+ },
+ define: {
+ __APP_VERSION__: JSON.stringify('1.0.0'),
+ },
+ },
+});
diff --git a/rstest/browser-rsbuild-react/rstest.config.ts b/rstest/browser-rsbuild-react/rstest.config.ts
new file mode 100644
index 00000000..39804db1
--- /dev/null
+++ b/rstest/browser-rsbuild-react/rstest.config.ts
@@ -0,0 +1,11 @@
+import { withRsbuildConfig } from '@rstest/adapter-rsbuild';
+import { defineConfig, type ExtendConfigFn } from '@rstest/core';
+
+export default defineConfig({
+ extends: withRsbuildConfig() as ExtendConfigFn,
+ browser: {
+ enabled: true,
+ browser: 'chromium',
+ port: 3012,
+ },
+});
diff --git a/rstest/browser-rsbuild-react/src/components/Counter.tsx b/rstest/browser-rsbuild-react/src/components/Counter.tsx
new file mode 100644
index 00000000..c650ecc4
--- /dev/null
+++ b/rstest/browser-rsbuild-react/src/components/Counter.tsx
@@ -0,0 +1,40 @@
+import { useState } from 'react';
+
+export interface CounterProps {
+ initialValue?: number;
+ step?: number;
+}
+
+export function Counter({ initialValue = 0, step = 1 }: CounterProps) {
+ const [count, setCount] = useState(initialValue);
+
+ return (
+
+ setCount((c) => c - step)}
+ aria-label="Decrement"
+ data-testid="decrement-btn"
+ >
+ -
+
+ {count}
+ setCount((c) => c + step)}
+ aria-label="Increment"
+ data-testid="increment-btn"
+ >
+ +
+
+ setCount(initialValue)}
+ aria-label="Reset"
+ data-testid="reset-btn"
+ >
+ Reset
+
+
+ );
+}
diff --git a/rstest/browser-rsbuild-react/src/hooks.ts b/rstest/browser-rsbuild-react/src/hooks.ts
new file mode 100644
index 00000000..edb6375d
--- /dev/null
+++ b/rstest/browser-rsbuild-react/src/hooks.ts
@@ -0,0 +1,25 @@
+import { useCallback, useEffect, useRef, useState } from 'react';
+
+export function useCounter(initialValue = 0, step = 1) {
+ const [count, setCount] = useState(initialValue);
+
+ const increment = useCallback(() => setCount((c) => c + step), [step]);
+ const decrement = useCallback(() => setCount((c) => c - step), [step]);
+ const reset = useCallback(() => setCount(initialValue), [initialValue]);
+
+ return { count, increment, decrement, reset };
+}
+
+export function useToggle(initialValue = false) {
+ const [value, setValue] = useState(initialValue);
+ const toggle = useCallback(() => setValue((v) => !v), []);
+ return { value, toggle };
+}
+
+export function usePrevious(value: T): T | undefined {
+ const ref = useRef(undefined);
+ useEffect(() => {
+ ref.current = value;
+ }, [value]);
+ return ref.current;
+}
diff --git a/rstest/browser-rsbuild-react/src/index.tsx b/rstest/browser-rsbuild-react/src/index.tsx
new file mode 100644
index 00000000..50e6769b
--- /dev/null
+++ b/rstest/browser-rsbuild-react/src/index.tsx
@@ -0,0 +1,13 @@
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import { Counter } from './components/Counter';
+
+const root = document.getElementById('root');
+if (root) {
+ createRoot(root).render(
+
+ React Counter App
+
+ ,
+ );
+}
diff --git a/rstest/browser-rsbuild-react/tests/adapter.test.tsx b/rstest/browser-rsbuild-react/tests/adapter.test.tsx
new file mode 100644
index 00000000..9f17825a
--- /dev/null
+++ b/rstest/browser-rsbuild-react/tests/adapter.test.tsx
@@ -0,0 +1,30 @@
+// Use @components alias inherited from rsbuild.config.ts
+import { Counter } from '@components/Counter';
+import { cleanup, render } from '@rstest/browser-react';
+import { afterEach, describe, expect, it } from '@rstest/core';
+import { getByTestId } from '@testing-library/dom';
+
+declare const __APP_VERSION__: string;
+
+describe('withRsbuildConfig - alias inheritance', () => {
+ afterEach(() => {
+ cleanup();
+ });
+
+ it('should resolve @components alias from rsbuild.config.ts', async () => {
+ const { container } = await render( );
+ expect(getByTestId(container, 'counter-value').textContent).toBe('5');
+ });
+
+ it('should resolve @/ alias from rsbuild.config.ts', async () => {
+ // Dynamic import using @/ alias
+ const { useCounter } = await import('@/hooks');
+ expect(typeof useCounter).toBe('function');
+ });
+});
+
+describe('withRsbuildConfig - define inheritance', () => {
+ it('should inherit __APP_VERSION__ from rsbuild.config.ts', () => {
+ expect(__APP_VERSION__).toBe('1.0.0');
+ });
+});
diff --git a/rstest/browser-rsbuild-react/tests/counter.test.tsx b/rstest/browser-rsbuild-react/tests/counter.test.tsx
new file mode 100644
index 00000000..e36d997b
--- /dev/null
+++ b/rstest/browser-rsbuild-react/tests/counter.test.tsx
@@ -0,0 +1,33 @@
+import { act, cleanup, render } from '@rstest/browser-react';
+import { afterEach, describe, expect, it } from '@rstest/core';
+import { getByTestId } from '@testing-library/dom';
+import userEvent from '@testing-library/user-event';
+import { Counter } from '../src/components/Counter';
+
+describe('render - Component Testing', () => {
+ afterEach(() => {
+ cleanup();
+ });
+
+ it('should render with initial value', async () => {
+ const { container } = await render( );
+ expect(getByTestId(container, 'counter-value').textContent).toBe('10');
+ });
+
+ it('should increment on button click', async () => {
+ const user = userEvent.setup();
+ const { container } = await render( );
+
+ await act(() => user.click(getByTestId(container, 'increment-btn')));
+
+ expect(getByTestId(container, 'counter-value').textContent).toBe('1');
+ });
+
+ it('should clean up on unmount', async () => {
+ const { container, unmount } = await render( );
+ expect(container.querySelector('[data-testid="counter"]')).not.toBeNull();
+
+ await unmount();
+ expect(container.innerHTML).toBe('');
+ });
+});
diff --git a/rstest/browser-rsbuild-react/tests/hooks.test.ts b/rstest/browser-rsbuild-react/tests/hooks.test.ts
new file mode 100644
index 00000000..8110ecfd
--- /dev/null
+++ b/rstest/browser-rsbuild-react/tests/hooks.test.ts
@@ -0,0 +1,55 @@
+import { act, cleanup, renderHook } from '@rstest/browser-react';
+import { afterEach, describe, expect, it } from '@rstest/core';
+import { useCounter, usePrevious, useToggle } from '../src/hooks';
+
+describe('renderHook - useCounter', () => {
+ afterEach(() => {
+ cleanup();
+ });
+
+ it('should initialize with value', async () => {
+ const { result } = await renderHook(() => useCounter(10));
+ expect(result.current.count).toBe(10);
+ });
+
+ it('should increment with act', async () => {
+ const { result } = await renderHook(() => useCounter(0));
+
+ await act(() => result.current.increment());
+
+ expect(result.current.count).toBe(1);
+ });
+});
+
+describe('renderHook - useToggle', () => {
+ afterEach(() => {
+ cleanup();
+ });
+
+ it('should toggle value', async () => {
+ const { result } = await renderHook(() => useToggle(false));
+
+ await act(() => result.current.toggle());
+ expect(result.current.value).toBe(true);
+
+ await act(() => result.current.toggle());
+ expect(result.current.value).toBe(false);
+ });
+});
+
+describe('renderHook - usePrevious', () => {
+ afterEach(() => {
+ cleanup();
+ });
+
+ it('should return previous value after rerender', async () => {
+ let value = 1;
+ const { result, rerender } = await renderHook(() => usePrevious(value));
+
+ expect(result.current).toBeUndefined();
+
+ value = 2;
+ await rerender();
+ expect(result.current).toBe(1);
+ });
+});
diff --git a/rstest/browser-rsbuild-react/tsconfig.json b/rstest/browser-rsbuild-react/tsconfig.json
new file mode 100644
index 00000000..e039ed87
--- /dev/null
+++ b/rstest/browser-rsbuild-react/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "jsx": "react-jsx",
+ "types": ["@rstest/core"],
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["src/*"],
+ "@components/*": ["src/components/*"]
+ }
+ },
+ "include": ["src", "tests", "rstest.config.ts", "rsbuild.config.ts"]
+}
diff --git a/rstest/browser-rsbuild-vanilla/package.json b/rstest/browser-rsbuild-vanilla/package.json
new file mode 100644
index 00000000..22d7ff1e
--- /dev/null
+++ b/rstest/browser-rsbuild-vanilla/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "rstest-browser-rsbuild-vanilla",
+ "version": "1.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "rsbuild dev",
+ "build": "rsbuild build",
+ "test": "rstest run"
+ },
+ "devDependencies": {
+ "@rsbuild/core": "^1.7.1",
+ "@rstest/browser": "^0.7.9",
+ "@rstest/core": "^0.7.9",
+ "playwright": "^1.57.0",
+ "typescript": "^5.8.3"
+ }
+}
diff --git a/rstest/browser-rsbuild-vanilla/rsbuild.config.ts b/rstest/browser-rsbuild-vanilla/rsbuild.config.ts
new file mode 100644
index 00000000..a85d2ff0
--- /dev/null
+++ b/rstest/browser-rsbuild-vanilla/rsbuild.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig } from '@rsbuild/core';
+
+export default defineConfig({
+ source: {
+ entry: {
+ index: './src/index.ts',
+ },
+ },
+});
diff --git a/rstest/browser-rsbuild-vanilla/rstest.config.ts b/rstest/browser-rsbuild-vanilla/rstest.config.ts
new file mode 100644
index 00000000..165728c4
--- /dev/null
+++ b/rstest/browser-rsbuild-vanilla/rstest.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({
+ browser: {
+ enabled: true,
+ browser: 'chromium',
+ port: 3010,
+ },
+});
diff --git a/rstest/browser-rsbuild-vanilla/src/counter.ts b/rstest/browser-rsbuild-vanilla/src/counter.ts
new file mode 100644
index 00000000..9e2b9278
--- /dev/null
+++ b/rstest/browser-rsbuild-vanilla/src/counter.ts
@@ -0,0 +1,47 @@
+export function createCounter(initialValue = 0) {
+ let value = initialValue;
+
+ const container = document.createElement('div');
+ container.className = 'counter';
+
+ const display = document.createElement('span');
+ display.className = 'counter-display';
+ display.textContent = String(value);
+ display.setAttribute('data-testid', 'counter-value');
+
+ const incrementBtn = document.createElement('button');
+ incrementBtn.textContent = '+';
+ incrementBtn.setAttribute('data-testid', 'increment-btn');
+
+ const decrementBtn = document.createElement('button');
+ decrementBtn.textContent = '-';
+ decrementBtn.setAttribute('data-testid', 'decrement-btn');
+
+ const updateDisplay = () => {
+ display.textContent = String(value);
+ };
+
+ const increment = () => {
+ value++;
+ updateDisplay();
+ };
+
+ const decrement = () => {
+ value--;
+ updateDisplay();
+ };
+
+ incrementBtn.addEventListener('click', increment);
+ decrementBtn.addEventListener('click', decrement);
+
+ container.appendChild(decrementBtn);
+ container.appendChild(display);
+ container.appendChild(incrementBtn);
+
+ return {
+ element: container,
+ getValue: () => value,
+ increment,
+ decrement,
+ };
+}
diff --git a/rstest/browser-rsbuild-vanilla/src/index.ts b/rstest/browser-rsbuild-vanilla/src/index.ts
new file mode 100644
index 00000000..2db7dcbf
--- /dev/null
+++ b/rstest/browser-rsbuild-vanilla/src/index.ts
@@ -0,0 +1,16 @@
+import { createCounter } from './counter';
+
+const app = document.querySelector('#root');
+
+if (app) {
+ app.innerHTML = `
+ Vanilla Counter App
+
+ `;
+
+ const container = document.querySelector('#counter-container');
+ if (container) {
+ const counter = createCounter(0);
+ container.appendChild(counter.element);
+ }
+}
diff --git a/rstest/browser-rsbuild-vanilla/tests/counter.test.ts b/rstest/browser-rsbuild-vanilla/tests/counter.test.ts
new file mode 100644
index 00000000..1f81a222
--- /dev/null
+++ b/rstest/browser-rsbuild-vanilla/tests/counter.test.ts
@@ -0,0 +1,56 @@
+import { afterEach, beforeEach, describe, expect, it } from '@rstest/core';
+import { createCounter } from '../src/counter';
+
+describe('createCounter', () => {
+ let container: HTMLDivElement;
+
+ beforeEach(() => {
+ container = document.createElement('div');
+ document.body.appendChild(container);
+ });
+
+ afterEach(() => {
+ container.remove();
+ });
+
+ it('should render with initial value', () => {
+ const counter = createCounter(10);
+ container.appendChild(counter.element);
+
+ expect(counter.getValue()).toBe(10);
+ expect(counter.element.querySelector('[data-testid="counter-value"]')?.textContent).toBe('10');
+ });
+
+ it('should increment on button click', () => {
+ const counter = createCounter(0);
+ container.appendChild(counter.element);
+
+ const btn = counter.element.querySelector('[data-testid="increment-btn"]') as HTMLButtonElement;
+ btn.click();
+
+ expect(counter.getValue()).toBe(1);
+ });
+
+ it('should decrement on button click', () => {
+ const counter = createCounter(5);
+ container.appendChild(counter.element);
+
+ const btn = counter.element.querySelector('[data-testid="decrement-btn"]') as HTMLButtonElement;
+ btn.click();
+
+ expect(counter.getValue()).toBe(4);
+ });
+});
+
+describe('Browser APIs', () => {
+ it('should have access to DOM', () => {
+ expect(typeof document).toBe('object');
+ expect(typeof window).toBe('object');
+ });
+
+ it('should have access to localStorage', () => {
+ localStorage.setItem('test', 'value');
+ expect(localStorage.getItem('test')).toBe('value');
+ localStorage.removeItem('test');
+ });
+});
diff --git a/rstest/browser-rsbuild-vanilla/tsconfig.json b/rstest/browser-rsbuild-vanilla/tsconfig.json
new file mode 100644
index 00000000..2156e6d3
--- /dev/null
+++ b/rstest/browser-rsbuild-vanilla/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "types": ["@rstest/core"]
+ },
+ "include": ["src", "tests", "rstest.config.ts", "rsbuild.config.ts"]
+}
diff --git a/rstest/coverage/package.json b/rstest/coverage/package.json
new file mode 100644
index 00000000..e5d7ae63
--- /dev/null
+++ b/rstest/coverage/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "rstest-coverage",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "test": "rstest run",
+ "test:coverage": "rstest run --coverage",
+ "test:coverage:html": "rstest run --coverage --coverage.reporter=html",
+ "test:coverage:lcov": "rstest run --coverage --coverage.reporter=lcov"
+ },
+ "devDependencies": {
+ "@rstest/core": "^0.7.9",
+ "@rstest/coverage-istanbul": "^0.1.6",
+ "typescript": "^5.8.3"
+ }
+}
diff --git a/rstest/coverage/rstest.config.ts b/rstest/coverage/rstest.config.ts
new file mode 100644
index 00000000..0c23f474
--- /dev/null
+++ b/rstest/coverage/rstest.config.ts
@@ -0,0 +1,19 @@
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({
+ coverage: {
+ enabled: true,
+ provider: 'istanbul',
+ include: ['src/**/*.ts'],
+ exclude: ['src/**/*.d.ts', 'src/**/*.test.ts', 'src/**/index.ts'],
+ reporters: ['text', 'html', 'lcov'],
+ reportsDirectory: './coverage',
+ thresholds: {
+ lines: 70,
+ branches: 70,
+ functions: 70,
+ statements: 70,
+ },
+ clean: true,
+ },
+});
diff --git a/rstest/coverage/src/math.ts b/rstest/coverage/src/math.ts
new file mode 100644
index 00000000..ccf28650
--- /dev/null
+++ b/rstest/coverage/src/math.ts
@@ -0,0 +1,49 @@
+/**
+ * Basic math utilities demonstrating line and function coverage
+ */
+
+export function add(a: number, b: number): number {
+ return a + b;
+}
+
+export function subtract(a: number, b: number): number {
+ return a - b;
+}
+
+export function multiply(a: number, b: number): number {
+ return a * b;
+}
+
+export function divide(a: number, b: number): number {
+ if (b === 0) {
+ throw new Error('Cannot divide by zero');
+ }
+ return a / b;
+}
+
+/**
+ * Calculate the factorial of a number
+ * Demonstrates branch coverage with recursion
+ */
+export function factorial(n: number): number {
+ if (n < 0) {
+ throw new Error('Cannot calculate factorial of negative number');
+ }
+ if (n === 0 || n === 1) {
+ return 1;
+ }
+ return n * factorial(n - 1);
+}
+
+/**
+ * This function is intentionally NOT tested to show uncovered code
+ */
+export function untested_power(base: number, exponent: number): number {
+ if (exponent === 0) {
+ return 1;
+ }
+ if (exponent < 0) {
+ return 1 / untested_power(base, -exponent);
+ }
+ return base * untested_power(base, exponent - 1);
+}
diff --git a/rstest/coverage/src/validator.ts b/rstest/coverage/src/validator.ts
new file mode 100644
index 00000000..044ed456
--- /dev/null
+++ b/rstest/coverage/src/validator.ts
@@ -0,0 +1,120 @@
+/**
+ * Validation utilities demonstrating branch coverage
+ */
+
+export interface ValidationResult {
+ valid: boolean;
+ errors: string[];
+}
+
+/**
+ * Validate an email address
+ * Multiple branches for different validation rules
+ */
+export function validateEmail(email: string): ValidationResult {
+ const errors: string[] = [];
+
+ if (!email) {
+ errors.push('Email is required');
+ return { valid: false, errors };
+ }
+
+ if (!email.includes('@')) {
+ errors.push('Email must contain @');
+ }
+
+ if (!email.includes('.')) {
+ errors.push('Email must contain a domain');
+ }
+
+ const [local, domain] = email.split('@');
+
+ if (local && local.length > 64) {
+ errors.push('Local part too long');
+ }
+
+ if (domain && domain.length > 255) {
+ errors.push('Domain too long');
+ }
+
+ return {
+ valid: errors.length === 0,
+ errors,
+ };
+}
+
+/**
+ * Validate a password with multiple criteria
+ */
+export function validatePassword(password: string): ValidationResult {
+ const errors: string[] = [];
+
+ if (!password) {
+ errors.push('Password is required');
+ return { valid: false, errors };
+ }
+
+ if (password.length < 8) {
+ errors.push('Password must be at least 8 characters');
+ }
+
+ if (password.length > 128) {
+ errors.push('Password must be at most 128 characters');
+ }
+
+ if (!/[A-Z]/.test(password)) {
+ errors.push('Password must contain an uppercase letter');
+ }
+
+ if (!/[a-z]/.test(password)) {
+ errors.push('Password must contain a lowercase letter');
+ }
+
+ if (!/[0-9]/.test(password)) {
+ errors.push('Password must contain a number');
+ }
+
+ if (!/[!@#$%^&*]/.test(password)) {
+ errors.push('Password must contain a special character');
+ }
+
+ return {
+ valid: errors.length === 0,
+ errors,
+ };
+}
+
+/**
+ * This function has branches that are intentionally not fully tested
+ * to demonstrate partial branch coverage
+ */
+export function validateAge(age: unknown): ValidationResult {
+ const errors: string[] = [];
+
+ if (age === null || age === undefined) {
+ errors.push('Age is required');
+ return { valid: false, errors };
+ }
+
+ if (typeof age !== 'number') {
+ errors.push('Age must be a number');
+ return { valid: false, errors };
+ }
+
+ if (!Number.isInteger(age)) {
+ errors.push('Age must be an integer');
+ }
+
+ if (age < 0) {
+ errors.push('Age cannot be negative');
+ }
+
+ if (age > 150) {
+ errors.push('Age seems unrealistic');
+ }
+
+ return {
+ valid: errors.length === 0,
+ errors,
+ };
+}
diff --git a/rstest/coverage/tests/math.test.ts b/rstest/coverage/tests/math.test.ts
new file mode 100644
index 00000000..cc592afb
--- /dev/null
+++ b/rstest/coverage/tests/math.test.ts
@@ -0,0 +1,74 @@
+import { describe, expect, it } from '@rstest/core';
+import { add, divide, factorial, multiply, subtract } from '../src/math';
+
+describe('math utilities', () => {
+ describe('add', () => {
+ it('should add two positive numbers', () => {
+ expect(add(2, 3)).toBe(5);
+ });
+
+ it('should add negative numbers', () => {
+ expect(add(-2, -3)).toBe(-5);
+ });
+
+ it('should add zero', () => {
+ expect(add(5, 0)).toBe(5);
+ });
+ });
+
+ describe('subtract', () => {
+ it('should subtract two numbers', () => {
+ expect(subtract(5, 3)).toBe(2);
+ });
+
+ it('should handle negative result', () => {
+ expect(subtract(3, 5)).toBe(-2);
+ });
+ });
+
+ describe('multiply', () => {
+ it('should multiply two numbers', () => {
+ expect(multiply(4, 5)).toBe(20);
+ });
+
+ it('should handle zero', () => {
+ expect(multiply(4, 0)).toBe(0);
+ });
+ });
+
+ describe('divide', () => {
+ it('should divide two numbers', () => {
+ expect(divide(10, 2)).toBe(5);
+ });
+
+ it('should throw error when dividing by zero', () => {
+ expect(() => divide(10, 0)).toThrow('Cannot divide by zero');
+ });
+
+ it('should handle decimal results', () => {
+ expect(divide(5, 2)).toBe(2.5);
+ });
+ });
+
+ describe('factorial', () => {
+ it('should return 1 for factorial of 0', () => {
+ expect(factorial(0)).toBe(1);
+ });
+
+ it('should return 1 for factorial of 1', () => {
+ expect(factorial(1)).toBe(1);
+ });
+
+ it('should calculate factorial of positive numbers', () => {
+ expect(factorial(5)).toBe(120);
+ expect(factorial(3)).toBe(6);
+ });
+
+ it('should throw error for negative numbers', () => {
+ expect(() => factorial(-1)).toThrow('Cannot calculate factorial of negative number');
+ });
+ });
+
+ // Note: untested_power is intentionally NOT tested
+ // to demonstrate uncovered code in coverage reports
+});
diff --git a/rstest/coverage/tests/validator.test.ts b/rstest/coverage/tests/validator.test.ts
new file mode 100644
index 00000000..d3cc6683
--- /dev/null
+++ b/rstest/coverage/tests/validator.test.ts
@@ -0,0 +1,117 @@
+import { describe, expect, it } from '@rstest/core';
+import { validateAge, validateEmail, validatePassword } from '../src/validator';
+
+describe('validator utilities', () => {
+ describe('validateEmail', () => {
+ it('should return error for empty email', () => {
+ const result = validateEmail('');
+ expect(result.valid).toBe(false);
+ expect(result.errors).toContain('Email is required');
+ });
+
+ it('should return error for email without @', () => {
+ const result = validateEmail('invalidemail.com');
+ expect(result.valid).toBe(false);
+ expect(result.errors).toContain('Email must contain @');
+ });
+
+ it('should return error for email without domain dot', () => {
+ const result = validateEmail('test@localhost');
+ expect(result.valid).toBe(false);
+ expect(result.errors).toContain('Email must contain a domain');
+ });
+
+ it('should validate correct email', () => {
+ const result = validateEmail('test@example.com');
+ expect(result.valid).toBe(true);
+ expect(result.errors).toHaveLength(0);
+ });
+
+ it('should return error for local part too long', () => {
+ const longLocal = 'a'.repeat(65);
+ const result = validateEmail(`${longLocal}@example.com`);
+ expect(result.valid).toBe(false);
+ expect(result.errors).toContain('Local part too long');
+ });
+
+ // Note: Domain too long branch is intentionally not tested
+ // to show partial branch coverage
+ });
+
+ describe('validatePassword', () => {
+ it('should return error for empty password', () => {
+ const result = validatePassword('');
+ expect(result.valid).toBe(false);
+ expect(result.errors).toContain('Password is required');
+ });
+
+ it('should return error for short password', () => {
+ const result = validatePassword('Ab1!');
+ expect(result.valid).toBe(false);
+ expect(result.errors).toContain('Password must be at least 8 characters');
+ });
+
+ it('should return error for password without uppercase', () => {
+ const result = validatePassword('password1!');
+ expect(result.valid).toBe(false);
+ expect(result.errors).toContain('Password must contain an uppercase letter');
+ });
+
+ it('should return error for password without lowercase', () => {
+ const result = validatePassword('PASSWORD1!');
+ expect(result.valid).toBe(false);
+ expect(result.errors).toContain('Password must contain a lowercase letter');
+ });
+
+ it('should return error for password without number', () => {
+ const result = validatePassword('Password!!');
+ expect(result.valid).toBe(false);
+ expect(result.errors).toContain('Password must contain a number');
+ });
+
+ it('should return error for password without special character', () => {
+ const result = validatePassword('Password1a');
+ expect(result.valid).toBe(false);
+ expect(result.errors).toContain('Password must contain a special character');
+ });
+
+ it('should validate correct password', () => {
+ const result = validatePassword('SecureP@ss1');
+ expect(result.valid).toBe(true);
+ expect(result.errors).toHaveLength(0);
+ });
+
+ // Note: Password too long branch is intentionally not tested
+ });
+
+ describe('validateAge', () => {
+ it('should return error for null age', () => {
+ const result = validateAge(null);
+ expect(result.valid).toBe(false);
+ expect(result.errors).toContain('Age is required');
+ });
+
+ it('should return error for undefined age', () => {
+ const result = validateAge(undefined);
+ expect(result.valid).toBe(false);
+ expect(result.errors).toContain('Age is required');
+ });
+
+ it('should return error for non-number age', () => {
+ const result = validateAge('25');
+ expect(result.valid).toBe(false);
+ expect(result.errors).toContain('Age must be a number');
+ });
+
+ it('should validate correct age', () => {
+ const result = validateAge(25);
+ expect(result.valid).toBe(true);
+ expect(result.errors).toHaveLength(0);
+ });
+
+ // Note: Following branches are intentionally not tested:
+ // - Non-integer age
+ // - Negative age
+ // - Age > 150
+ });
+});
diff --git a/rstest/coverage/tsconfig.json b/rstest/coverage/tsconfig.json
new file mode 100644
index 00000000..5d1c2508
--- /dev/null
+++ b/rstest/coverage/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "declaration": true,
+ "types": ["@rstest/core"]
+ },
+ "include": ["src", "tests", "rstest.config.ts"]
+}
diff --git a/rstest/fake-timers/package.json b/rstest/fake-timers/package.json
new file mode 100644
index 00000000..09d6bb65
--- /dev/null
+++ b/rstest/fake-timers/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "rstest-fake-timers",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "test": "rstest run"
+ },
+ "devDependencies": {
+ "@rstest/core": "^0.7.9",
+ "typescript": "^5.8.3"
+ }
+}
diff --git a/rstest/fake-timers/rstest.config.ts b/rstest/fake-timers/rstest.config.ts
new file mode 100644
index 00000000..9ee3cbab
--- /dev/null
+++ b/rstest/fake-timers/rstest.config.ts
@@ -0,0 +1,3 @@
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({});
diff --git a/rstest/fake-timers/src/index.ts b/rstest/fake-timers/src/index.ts
new file mode 100644
index 00000000..46eba0e5
--- /dev/null
+++ b/rstest/fake-timers/src/index.ts
@@ -0,0 +1,3 @@
+export * from './polling';
+export * from './timer';
+export * from './timing';
diff --git a/rstest/fake-timers/src/polling.ts b/rstest/fake-timers/src/polling.ts
new file mode 100644
index 00000000..1bcbcaf6
--- /dev/null
+++ b/rstest/fake-timers/src/polling.ts
@@ -0,0 +1,127 @@
+/**
+ * Polling utility for checking conditions
+ */
+export class Poller {
+ private intervalId: ReturnType | null = null;
+ private timeoutId: ReturnType | null = null;
+
+ /**
+ * Poll until condition is met or timeout
+ */
+ async poll(
+ checkFn: () => T | Promise,
+ options: {
+ interval?: number;
+ timeout?: number;
+ condition?: (result: T) => boolean;
+ } = {},
+ ): Promise {
+ const { interval = 100, timeout = 5000, condition = Boolean } = options;
+
+ return new Promise((resolve, reject) => {
+ const startTime = Date.now();
+
+ const check = async () => {
+ try {
+ const result = await checkFn();
+
+ if (condition(result)) {
+ this.stop();
+ resolve(result);
+ } else if (Date.now() - startTime >= timeout) {
+ this.stop();
+ reject(new Error(`Polling timeout after ${timeout}ms`));
+ }
+ } catch (error) {
+ this.stop();
+ reject(error);
+ }
+ };
+
+ // Set timeout for overall polling
+ this.timeoutId = setTimeout(() => {
+ this.stop();
+ reject(new Error(`Polling timeout after ${timeout}ms`));
+ }, timeout);
+
+ // Start polling
+ this.intervalId = setInterval(check, interval);
+
+ // Check immediately
+ check();
+ });
+ }
+
+ /**
+ * Stop polling
+ */
+ stop(): void {
+ if (this.intervalId) {
+ clearInterval(this.intervalId);
+ this.intervalId = null;
+ }
+ if (this.timeoutId) {
+ clearTimeout(this.timeoutId);
+ this.timeoutId = null;
+ }
+ }
+}
+
+/**
+ * Rate limiter using token bucket algorithm
+ */
+export class RateLimiter {
+ private tokens: number;
+ private lastRefill: number;
+ private readonly maxTokens: number;
+ private readonly refillRate: number; // tokens per second
+
+ constructor(maxTokens: number, refillRate: number) {
+ this.maxTokens = maxTokens;
+ this.tokens = maxTokens;
+ this.refillRate = refillRate;
+ this.lastRefill = Date.now();
+ }
+
+ /**
+ * Try to acquire a token
+ */
+ tryAcquire(): boolean {
+ this.refill();
+
+ if (this.tokens >= 1) {
+ this.tokens -= 1;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get time until next token is available
+ */
+ getWaitTime(): number {
+ this.refill();
+
+ if (this.tokens >= 1) {
+ return 0;
+ }
+
+ return Math.ceil(((1 - this.tokens) / this.refillRate) * 1000);
+ }
+
+ /**
+ * Get current token count
+ */
+ getTokens(): number {
+ this.refill();
+ return Math.floor(this.tokens);
+ }
+
+ private refill(): void {
+ const now = Date.now();
+ const elapsed = (now - this.lastRefill) / 1000;
+ this.tokens = Math.min(this.maxTokens, this.tokens + elapsed * this.refillRate);
+ this.lastRefill = now;
+ }
+}
diff --git a/rstest/fake-timers/src/timer.ts b/rstest/fake-timers/src/timer.ts
new file mode 100644
index 00000000..02c49111
--- /dev/null
+++ b/rstest/fake-timers/src/timer.ts
@@ -0,0 +1,127 @@
+/**
+ * Countdown timer
+ */
+export class CountdownTimer {
+ private remaining: number;
+ private intervalId: ReturnType | null = null;
+ private onTick?: (remaining: number) => void;
+ private onComplete?: () => void;
+
+ constructor(durationMs: number) {
+ this.remaining = durationMs;
+ }
+
+ /**
+ * Set callback for each tick
+ */
+ setOnTick(callback: (remaining: number) => void): void {
+ this.onTick = callback;
+ }
+
+ /**
+ * Set callback for when timer completes
+ */
+ setOnComplete(callback: () => void): void {
+ this.onComplete = callback;
+ }
+
+ /**
+ * Start the countdown
+ */
+ start(tickInterval = 1000): void {
+ if (this.intervalId) return;
+
+ this.intervalId = setInterval(() => {
+ this.remaining -= tickInterval;
+
+ if (this.remaining <= 0) {
+ this.remaining = 0;
+ this.stop();
+ this.onTick?.(0);
+ this.onComplete?.();
+ } else {
+ this.onTick?.(this.remaining);
+ }
+ }, tickInterval);
+ }
+
+ /**
+ * Stop the countdown
+ */
+ stop(): void {
+ if (this.intervalId) {
+ clearInterval(this.intervalId);
+ this.intervalId = null;
+ }
+ }
+
+ /**
+ * Get remaining time
+ */
+ getRemaining(): number {
+ return this.remaining;
+ }
+
+ /**
+ * Check if timer is running
+ */
+ isRunning(): boolean {
+ return this.intervalId !== null;
+ }
+}
+
+/**
+ * Stopwatch for measuring elapsed time
+ */
+export class Stopwatch {
+ private startTime: number | null = null;
+ private elapsedTime = 0;
+ private running = false;
+
+ /**
+ * Start the stopwatch
+ */
+ start(): void {
+ if (!this.running) {
+ this.startTime = Date.now();
+ this.running = true;
+ }
+ }
+
+ /**
+ * Stop the stopwatch
+ */
+ stop(): void {
+ if (this.running && this.startTime !== null) {
+ this.elapsedTime += Date.now() - this.startTime;
+ this.running = false;
+ this.startTime = null;
+ }
+ }
+
+ /**
+ * Reset the stopwatch
+ */
+ reset(): void {
+ this.startTime = null;
+ this.elapsedTime = 0;
+ this.running = false;
+ }
+
+ /**
+ * Get elapsed time in milliseconds
+ */
+ getElapsed(): number {
+ if (this.running && this.startTime !== null) {
+ return this.elapsedTime + (Date.now() - this.startTime);
+ }
+ return this.elapsedTime;
+ }
+
+ /**
+ * Check if stopwatch is running
+ */
+ isRunning(): boolean {
+ return this.running;
+ }
+}
diff --git a/rstest/fake-timers/src/timing.ts b/rstest/fake-timers/src/timing.ts
new file mode 100644
index 00000000..cb247fec
--- /dev/null
+++ b/rstest/fake-timers/src/timing.ts
@@ -0,0 +1,100 @@
+/**
+ * Debounce function - delays execution until after wait ms since last call
+ */
+export function debounce unknown>(
+ fn: T,
+ wait: number,
+): (...args: Parameters) => void {
+ let timeoutId: ReturnType | null = null;
+
+ return (...args: Parameters) => {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ timeoutId = setTimeout(() => {
+ fn(...args);
+ timeoutId = null;
+ }, wait);
+ };
+}
+
+/**
+ * Throttle function - limits execution to once per wait ms
+ */
+export function throttle unknown>(
+ fn: T,
+ wait: number,
+): (...args: Parameters) => void {
+ let lastCall = 0;
+ let timeoutId: ReturnType | null = null;
+
+ return (...args: Parameters) => {
+ const now = Date.now();
+ const remaining = wait - (now - lastCall);
+
+ if (remaining <= 0) {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ timeoutId = null;
+ }
+ lastCall = now;
+ fn(...args);
+ } else if (!timeoutId) {
+ timeoutId = setTimeout(() => {
+ lastCall = Date.now();
+ timeoutId = null;
+ fn(...args);
+ }, remaining);
+ }
+ };
+}
+
+/**
+ * Retry function with exponential backoff
+ */
+export async function retryWithBackoff(
+ fn: () => Promise,
+ options: {
+ maxRetries?: number;
+ initialDelay?: number;
+ maxDelay?: number;
+ factor?: number;
+ } = {},
+): Promise {
+ const { maxRetries = 3, initialDelay = 100, maxDelay = 5000, factor = 2 } = options;
+
+ let lastError: Error | null = null;
+ let delay = initialDelay;
+
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
+ try {
+ return await fn();
+ } catch (error) {
+ lastError = error as Error;
+
+ if (attempt < maxRetries) {
+ await new Promise((resolve) => setTimeout(resolve, delay));
+ delay = Math.min(delay * factor, maxDelay);
+ }
+ }
+ }
+
+ throw lastError;
+}
+
+/**
+ * Delay helper
+ */
+export function delay(ms: number): Promise {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+}
+
+/**
+ * Timeout helper - rejects if operation takes too long
+ */
+export function withTimeout(promise: Promise, ms: number): Promise {
+ return Promise.race([
+ promise,
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms)),
+ ]);
+}
diff --git a/rstest/fake-timers/tests/advanced.test.ts b/rstest/fake-timers/tests/advanced.test.ts
new file mode 100644
index 00000000..2dfa7f13
--- /dev/null
+++ b/rstest/fake-timers/tests/advanced.test.ts
@@ -0,0 +1,143 @@
+import { afterEach, beforeEach, describe, expect, it, rstest } from '@rstest/core';
+import { RateLimiter } from '../src/polling';
+import { retryWithBackoff } from '../src/timing';
+
+describe('RateLimiter with Fake Timers', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ rstest.setSystemTime(new Date('2024-01-01T00:00:00.000Z'));
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should allow requests up to max tokens', () => {
+ const limiter = new RateLimiter(3, 1);
+
+ expect(limiter.tryAcquire()).toBe(true);
+ expect(limiter.tryAcquire()).toBe(true);
+ expect(limiter.tryAcquire()).toBe(true);
+ expect(limiter.tryAcquire()).toBe(false);
+ });
+
+ it('should refill tokens over time', () => {
+ const limiter = new RateLimiter(3, 1); // 1 token per second
+
+ limiter.tryAcquire();
+ limiter.tryAcquire();
+ limiter.tryAcquire();
+ expect(limiter.getTokens()).toBe(0);
+
+ rstest.advanceTimersByTime(2000);
+
+ expect(limiter.getTokens()).toBe(2);
+ expect(limiter.tryAcquire()).toBe(true);
+ expect(limiter.tryAcquire()).toBe(true);
+ });
+
+ it('should not exceed max tokens', () => {
+ const limiter = new RateLimiter(3, 1);
+
+ rstest.advanceTimersByTime(10000);
+
+ expect(limiter.getTokens()).toBe(3);
+ });
+
+ it('should calculate wait time correctly', () => {
+ const limiter = new RateLimiter(1, 1);
+
+ limiter.tryAcquire();
+ expect(limiter.getWaitTime()).toBeGreaterThan(0);
+
+ rstest.advanceTimersByTime(1000);
+ expect(limiter.getWaitTime()).toBe(0);
+ });
+});
+
+describe('retryWithBackoff with Fake Timers', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should succeed on first attempt', async () => {
+ const fn = rstest.fn().mockResolvedValue('success');
+
+ const resultPromise = retryWithBackoff(fn, { maxRetries: 3 });
+
+ await rstest.runAllTimersAsync();
+
+ const result = await resultPromise;
+ expect(result).toBe('success');
+ expect(fn).toHaveBeenCalledTimes(1);
+ });
+
+ it('should retry on failure and eventually succeed', async () => {
+ const fn = rstest.fn().mockRejectedValueOnce(new Error('fail 1')).mockResolvedValue('success');
+
+ const resultPromise = retryWithBackoff(fn, {
+ maxRetries: 3,
+ initialDelay: 100,
+ });
+
+ // Run all timers to complete the retry cycle
+ await rstest.runAllTimersAsync();
+
+ const result = await resultPromise;
+ expect(result).toBe('success');
+ expect(fn).toHaveBeenCalledTimes(2); // Initial + 1 retry
+ });
+});
+
+describe('Timeout Patterns with Fake Timers', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should handle promise race with timers', async () => {
+ let resolved = false;
+
+ const slowPromise = new Promise((resolve) => {
+ setTimeout(() => {
+ resolved = true;
+ resolve('done');
+ }, 500);
+ });
+
+ // Start the promise
+ slowPromise.then(() => {});
+
+ expect(resolved).toBe(false);
+
+ await rstest.advanceTimersByTimeAsync(500);
+
+ expect(resolved).toBe(true);
+ });
+
+ it('should handle multiple concurrent timers', async () => {
+ const results: string[] = [];
+
+ setTimeout(() => results.push('first'), 100);
+ setTimeout(() => results.push('second'), 200);
+ setTimeout(() => results.push('third'), 300);
+
+ expect(results).toEqual([]);
+
+ await rstest.advanceTimersByTimeAsync(150);
+ expect(results).toEqual(['first']);
+
+ await rstest.advanceTimersByTimeAsync(100);
+ expect(results).toEqual(['first', 'second']);
+
+ await rstest.advanceTimersByTimeAsync(100);
+ expect(results).toEqual(['first', 'second', 'third']);
+ });
+});
diff --git a/rstest/fake-timers/tests/basic.test.ts b/rstest/fake-timers/tests/basic.test.ts
new file mode 100644
index 00000000..a2e7deca
--- /dev/null
+++ b/rstest/fake-timers/tests/basic.test.ts
@@ -0,0 +1,369 @@
+import { afterEach, beforeEach, describe, expect, it, rstest } from '@rstest/core';
+
+describe('rstest.useFakeTimers() - Basic Usage', () => {
+ /**
+ * rstest.useFakeTimers() replaces setTimeout, setInterval,
+ * Date.now, and other time-related globals with mock implementations
+ */
+
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ describe('setTimeout', () => {
+ it('should not execute callback until time advances', () => {
+ let called = false;
+
+ setTimeout(() => {
+ called = true;
+ }, 1000);
+
+ expect(called).toBe(false);
+ });
+
+ it('should execute callback after advancing time', () => {
+ let called = false;
+
+ setTimeout(() => {
+ called = true;
+ }, 1000);
+
+ rstest.advanceTimersByTime(1000);
+
+ expect(called).toBe(true);
+ });
+
+ it('should not execute if time advanced partially', () => {
+ let called = false;
+
+ setTimeout(() => {
+ called = true;
+ }, 1000);
+
+ rstest.advanceTimersByTime(500);
+
+ expect(called).toBe(false);
+
+ rstest.advanceTimersByTime(500);
+
+ expect(called).toBe(true);
+ });
+ });
+
+ describe('setInterval', () => {
+ it('should execute interval callback multiple times', () => {
+ let count = 0;
+
+ setInterval(() => {
+ count++;
+ }, 100);
+
+ rstest.advanceTimersByTime(350);
+
+ expect(count).toBe(3);
+ });
+
+ it('should stop interval with clearInterval', () => {
+ let count = 0;
+
+ const intervalId = setInterval(() => {
+ count++;
+ }, 100);
+
+ rstest.advanceTimersByTime(250);
+ expect(count).toBe(2);
+
+ clearInterval(intervalId);
+
+ rstest.advanceTimersByTime(200);
+ expect(count).toBe(2); // Should not increase
+ });
+ });
+});
+
+describe('rstest.advanceTimersByTime()', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should advance time by specified milliseconds', () => {
+ const results: number[] = [];
+
+ setTimeout(() => results.push(1), 100);
+ setTimeout(() => results.push(2), 200);
+ setTimeout(() => results.push(3), 300);
+
+ rstest.advanceTimersByTime(150);
+ expect(results).toEqual([1]);
+
+ rstest.advanceTimersByTime(100);
+ expect(results).toEqual([1, 2]);
+
+ rstest.advanceTimersByTime(100);
+ expect(results).toEqual([1, 2, 3]);
+ });
+
+ it('should handle nested timers', () => {
+ const results: string[] = [];
+
+ setTimeout(() => {
+ results.push('first');
+ setTimeout(() => {
+ results.push('nested');
+ }, 100);
+ }, 100);
+
+ rstest.advanceTimersByTime(100);
+ expect(results).toEqual(['first']);
+
+ rstest.advanceTimersByTime(100);
+ expect(results).toEqual(['first', 'nested']);
+ });
+});
+
+describe('rstest.advanceTimersByTimeAsync()', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should advance time and flush promises', async () => {
+ let resolved = false;
+
+ const promise = new Promise((resolve) => {
+ setTimeout(() => {
+ resolved = true;
+ resolve();
+ }, 1000);
+ });
+
+ await rstest.advanceTimersByTimeAsync(1000);
+
+ expect(resolved).toBe(true);
+ await promise;
+ });
+});
+
+describe('rstest.runAllTimers()', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should run all pending timers', () => {
+ const results: number[] = [];
+
+ setTimeout(() => results.push(1), 100);
+ setTimeout(() => results.push(2), 500);
+ setTimeout(() => results.push(3), 1000);
+
+ rstest.runAllTimers();
+
+ expect(results).toEqual([1, 2, 3]);
+ });
+
+ it('should run nested timers', () => {
+ const results: string[] = [];
+
+ setTimeout(() => {
+ results.push('a');
+ setTimeout(() => {
+ results.push('b');
+ setTimeout(() => {
+ results.push('c');
+ }, 100);
+ }, 100);
+ }, 100);
+
+ rstest.runAllTimers();
+
+ expect(results).toEqual(['a', 'b', 'c']);
+ });
+});
+
+describe('rstest.runAllTimersAsync()', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should run all timers and flush promises', async () => {
+ const results: string[] = [];
+
+ setTimeout(async () => {
+ await Promise.resolve();
+ results.push('async-1');
+ }, 100);
+
+ setTimeout(async () => {
+ await Promise.resolve();
+ results.push('async-2');
+ }, 200);
+
+ await rstest.runAllTimersAsync();
+
+ expect(results).toEqual(['async-1', 'async-2']);
+ });
+});
+
+describe('rstest.runOnlyPendingTimers()', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should only run currently pending timers', () => {
+ const results: string[] = [];
+
+ setTimeout(() => {
+ results.push('first');
+ setTimeout(() => {
+ results.push('nested');
+ }, 100);
+ }, 100);
+
+ rstest.runOnlyPendingTimers();
+
+ // Only first timer runs, nested is not yet pending
+ expect(results).toEqual(['first']);
+
+ rstest.runOnlyPendingTimers();
+
+ expect(results).toEqual(['first', 'nested']);
+ });
+
+ it('should be useful for interval testing', () => {
+ let count = 0;
+
+ setInterval(() => {
+ count++;
+ }, 100);
+
+ rstest.runOnlyPendingTimers();
+ expect(count).toBe(1);
+
+ rstest.runOnlyPendingTimers();
+ expect(count).toBe(2);
+ });
+});
+
+describe('rstest.advanceTimersToNextTimer()', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should advance to next timer only', () => {
+ const results: number[] = [];
+
+ setTimeout(() => results.push(1), 100);
+ setTimeout(() => results.push(2), 200);
+ setTimeout(() => results.push(3), 300);
+
+ rstest.advanceTimersToNextTimer();
+ expect(results).toEqual([1]);
+
+ rstest.advanceTimersToNextTimer();
+ expect(results).toEqual([1, 2]);
+
+ rstest.advanceTimersToNextTimer();
+ expect(results).toEqual([1, 2, 3]);
+ });
+
+ it('should handle multiple steps', () => {
+ const results: string[] = [];
+
+ setTimeout(() => results.push('a'), 50);
+ setTimeout(() => results.push('b'), 100);
+ setTimeout(() => results.push('c'), 150);
+
+ rstest.advanceTimersToNextTimer(2);
+
+ expect(results).toEqual(['a', 'b']);
+ });
+});
+
+describe('rstest.getTimerCount()', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should return count of pending timers', () => {
+ expect(rstest.getTimerCount()).toBe(0);
+
+ setTimeout(() => {}, 100);
+ expect(rstest.getTimerCount()).toBe(1);
+
+ setTimeout(() => {}, 200);
+ setTimeout(() => {}, 300);
+ expect(rstest.getTimerCount()).toBe(3);
+
+ rstest.runAllTimers();
+ expect(rstest.getTimerCount()).toBe(0);
+ });
+
+ it('should count intervals as one timer each', () => {
+ const id = setInterval(() => {}, 100);
+ expect(rstest.getTimerCount()).toBe(1);
+
+ clearInterval(id);
+ expect(rstest.getTimerCount()).toBe(0);
+ });
+});
+
+describe('rstest.clearAllTimers()', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should clear all pending timers', () => {
+ let called = false;
+
+ setTimeout(() => {
+ called = true;
+ }, 1000);
+ setInterval(() => {
+ called = true;
+ }, 500);
+
+ expect(rstest.getTimerCount()).toBe(2);
+
+ rstest.clearAllTimers();
+
+ expect(rstest.getTimerCount()).toBe(0);
+
+ rstest.advanceTimersByTime(2000);
+ expect(called).toBe(false);
+ });
+});
diff --git a/rstest/fake-timers/tests/date.test.ts b/rstest/fake-timers/tests/date.test.ts
new file mode 100644
index 00000000..6b75f294
--- /dev/null
+++ b/rstest/fake-timers/tests/date.test.ts
@@ -0,0 +1,180 @@
+import { afterEach, beforeEach, describe, expect, it, rstest } from '@rstest/core';
+
+describe('rstest.setSystemTime() - Mocking Date', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should set system time to specific date', () => {
+ rstest.setSystemTime(new Date('2024-01-01T00:00:00.000Z'));
+
+ expect(new Date().toISOString()).toBe('2024-01-01T00:00:00.000Z');
+ expect(Date.now()).toBe(new Date('2024-01-01T00:00:00.000Z').getTime());
+ });
+
+ it('should set system time using timestamp', () => {
+ const timestamp = 1704067200000; // 2024-01-01
+ rstest.setSystemTime(timestamp);
+
+ expect(Date.now()).toBe(timestamp);
+ });
+
+ it('should advance time from set date', () => {
+ rstest.setSystemTime(new Date('2024-01-01T00:00:00.000Z'));
+
+ rstest.advanceTimersByTime(1000 * 60 * 60); // 1 hour
+
+ expect(new Date().toISOString()).toBe('2024-01-01T01:00:00.000Z');
+ });
+
+ it('should work with setTimeout and set date', () => {
+ rstest.setSystemTime(new Date('2024-06-15T12:00:00.000Z'));
+
+ const captured: { date: Date | null } = { date: null };
+
+ setTimeout(() => {
+ captured.date = new Date();
+ }, 5000);
+
+ rstest.advanceTimersByTime(5000);
+
+ expect(captured.date?.toISOString()).toBe('2024-06-15T12:00:05.000Z');
+ });
+});
+
+describe('Verifying Mocked System Time', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should verify mocked time via Date.now()', () => {
+ rstest.setSystemTime(new Date('2024-01-01T00:00:00.000Z'));
+
+ expect(Date.now()).toBe(new Date('2024-01-01T00:00:00.000Z').getTime());
+
+ rstest.advanceTimersByTime(1000);
+
+ expect(Date.now()).toBe(new Date('2024-01-01T00:00:01.000Z').getTime());
+ });
+});
+
+describe('Date Constructor with Fake Timers', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ rstest.setSystemTime(new Date('2024-03-15T10:30:00.000Z'));
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should create Date with mocked time', () => {
+ const date = new Date();
+
+ expect(date.getFullYear()).toBe(2024);
+ expect(date.getMonth()).toBe(2); // March (0-indexed)
+ expect(date.getDate()).toBe(15);
+ expect(date.getUTCHours()).toBe(10);
+ expect(date.getUTCMinutes()).toBe(30);
+ });
+
+ it('should still allow creating specific dates', () => {
+ const specificDate = new Date('2020-05-20T08:00:00.000Z');
+
+ expect(specificDate.getFullYear()).toBe(2020);
+ expect(specificDate.getMonth()).toBe(4); // May
+ expect(specificDate.getDate()).toBe(20);
+ });
+
+ it('should work with date comparisons', () => {
+ const now = new Date();
+ const past = new Date('2024-01-01');
+ const future = new Date('2024-12-31');
+
+ expect(now > past).toBe(true);
+ expect(now < future).toBe(true);
+ });
+});
+
+describe('Performance.now() with Fake Timers', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should mock performance.now()', () => {
+ const start = performance.now();
+
+ rstest.advanceTimersByTime(1000);
+
+ const end = performance.now();
+
+ expect(end - start).toBe(1000);
+ });
+});
+
+describe('Real-world Date Scenarios', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should test expiration logic', () => {
+ rstest.setSystemTime(new Date('2024-01-15T00:00:00.000Z'));
+
+ const expirationDate = new Date('2024-01-20T00:00:00.000Z');
+
+ const isExpired = () => new Date() > expirationDate;
+
+ expect(isExpired()).toBe(false);
+
+ // Advance 4 days
+ rstest.advanceTimersByTime(4 * 24 * 60 * 60 * 1000);
+ expect(isExpired()).toBe(false);
+
+ // Advance 2 more days (total 6 days = Jan 21)
+ rstest.advanceTimersByTime(2 * 24 * 60 * 60 * 1000);
+ expect(isExpired()).toBe(true);
+ });
+
+ it('should test scheduled task logic', () => {
+ rstest.setSystemTime(new Date('2024-01-01T08:00:00.000Z'));
+
+ const scheduledTasks: string[] = [];
+
+ // Task scheduled for noon
+ const noonTime = new Date('2024-01-01T12:00:00.000Z').getTime();
+ const checkTime = () => {
+ if (Date.now() >= noonTime) {
+ scheduledTasks.push('noon-task');
+ }
+ };
+
+ checkTime();
+ expect(scheduledTasks).toEqual([]);
+
+ // Advance to 11:00
+ rstest.advanceTimersByTime(3 * 60 * 60 * 1000);
+ checkTime();
+ expect(scheduledTasks).toEqual([]);
+
+ // Advance to 12:30
+ rstest.advanceTimersByTime(1.5 * 60 * 60 * 1000);
+ checkTime();
+ expect(scheduledTasks).toEqual(['noon-task']);
+ });
+});
diff --git a/rstest/fake-timers/tests/timing.test.ts b/rstest/fake-timers/tests/timing.test.ts
new file mode 100644
index 00000000..3dc0f4a4
--- /dev/null
+++ b/rstest/fake-timers/tests/timing.test.ts
@@ -0,0 +1,181 @@
+import { afterEach, beforeEach, describe, expect, it, rstest } from '@rstest/core';
+import { debounce, delay, throttle } from '../src/timing';
+
+describe('Testing debounce with Fake Timers', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should not call function before delay', () => {
+ const fn = rstest.fn();
+ const debounced = debounce(fn, 500);
+
+ debounced();
+
+ expect(fn).not.toHaveBeenCalled();
+ });
+
+ it('should call function after delay', () => {
+ const fn = rstest.fn();
+ const debounced = debounce(fn, 500);
+
+ debounced();
+
+ rstest.advanceTimersByTime(500);
+
+ expect(fn).toHaveBeenCalledTimes(1);
+ });
+
+ it('should reset timer on subsequent calls', () => {
+ const fn = rstest.fn();
+ const debounced = debounce(fn, 500);
+
+ debounced();
+ rstest.advanceTimersByTime(300);
+
+ debounced();
+ rstest.advanceTimersByTime(300);
+
+ expect(fn).not.toHaveBeenCalled();
+
+ rstest.advanceTimersByTime(200);
+
+ expect(fn).toHaveBeenCalledTimes(1);
+ });
+
+ it('should pass arguments to debounced function', () => {
+ const fn = rstest.fn();
+ const debounced = debounce(fn, 500);
+
+ debounced('arg1', 'arg2');
+
+ rstest.advanceTimersByTime(500);
+
+ expect(fn).toHaveBeenCalledWith('arg1', 'arg2');
+ });
+
+ it('should use last arguments when called multiple times', () => {
+ const fn = rstest.fn();
+ const debounced = debounce(fn, 500);
+
+ debounced('first');
+ debounced('second');
+ debounced('third');
+
+ rstest.advanceTimersByTime(500);
+
+ expect(fn).toHaveBeenCalledTimes(1);
+ expect(fn).toHaveBeenCalledWith('third');
+ });
+});
+
+describe('Testing throttle with Fake Timers', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should call function immediately on first call', () => {
+ const fn = rstest.fn();
+ const throttled = throttle(fn, 500);
+
+ throttled();
+
+ expect(fn).toHaveBeenCalledTimes(1);
+ });
+
+ it('should not call function again within wait period', () => {
+ const fn = rstest.fn();
+ const throttled = throttle(fn, 500);
+
+ throttled();
+ throttled();
+ throttled();
+
+ expect(fn).toHaveBeenCalledTimes(1);
+ });
+
+ it('should call function again after wait period', () => {
+ const fn = rstest.fn();
+ const throttled = throttle(fn, 500);
+
+ throttled();
+ expect(fn).toHaveBeenCalledTimes(1);
+
+ rstest.advanceTimersByTime(500);
+
+ throttled();
+ expect(fn).toHaveBeenCalledTimes(2);
+ });
+
+ it('should queue trailing call', () => {
+ const fn = rstest.fn();
+ const throttled = throttle(fn, 500);
+
+ throttled('first');
+ throttled('second');
+
+ expect(fn).toHaveBeenCalledTimes(1);
+ expect(fn).toHaveBeenCalledWith('first');
+
+ rstest.advanceTimersByTime(500);
+
+ expect(fn).toHaveBeenCalledTimes(2);
+ expect(fn).toHaveBeenLastCalledWith('second');
+ });
+});
+
+describe('Testing delay with Fake Timers', () => {
+ beforeEach(() => {
+ rstest.useFakeTimers();
+ });
+
+ afterEach(() => {
+ rstest.useRealTimers();
+ });
+
+ it('should resolve after specified delay', async () => {
+ let resolved = false;
+
+ delay(1000).then(() => {
+ resolved = true;
+ });
+
+ expect(resolved).toBe(false);
+
+ await rstest.advanceTimersByTimeAsync(1000);
+
+ expect(resolved).toBe(true);
+ });
+
+ it('should work with async/await', async () => {
+ const sequence: string[] = [];
+
+ const asyncFn = async () => {
+ sequence.push('start');
+ await delay(500);
+ sequence.push('middle');
+ await delay(500);
+ sequence.push('end');
+ };
+
+ const promise = asyncFn();
+
+ expect(sequence).toEqual(['start']);
+
+ await rstest.advanceTimersByTimeAsync(500);
+ expect(sequence).toEqual(['start', 'middle']);
+
+ await rstest.advanceTimersByTimeAsync(500);
+ expect(sequence).toEqual(['start', 'middle', 'end']);
+
+ await promise;
+ });
+});
diff --git a/rstest/fake-timers/tsconfig.json b/rstest/fake-timers/tsconfig.json
new file mode 100644
index 00000000..5d1c2508
--- /dev/null
+++ b/rstest/fake-timers/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "declaration": true,
+ "types": ["@rstest/core"]
+ },
+ "include": ["src", "tests", "rstest.config.ts"]
+}
diff --git a/rstest/mocking/package.json b/rstest/mocking/package.json
new file mode 100644
index 00000000..4ae75db2
--- /dev/null
+++ b/rstest/mocking/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "rstest-mocking",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "test": "rstest run"
+ },
+ "devDependencies": {
+ "@rstest/core": "^0.7.9",
+ "typescript": "^5.8.3"
+ }
+}
diff --git a/rstest/mocking/rstest.config.ts b/rstest/mocking/rstest.config.ts
new file mode 100644
index 00000000..9ee3cbab
--- /dev/null
+++ b/rstest/mocking/rstest.config.ts
@@ -0,0 +1,3 @@
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({});
diff --git a/rstest/mocking/src/api.ts b/rstest/mocking/src/api.ts
new file mode 100644
index 00000000..3075eccf
--- /dev/null
+++ b/rstest/mocking/src/api.ts
@@ -0,0 +1,55 @@
+/**
+ * API client for mocking demonstration
+ */
+
+export interface User {
+ id: number;
+ name: string;
+ email: string;
+}
+
+export interface Post {
+ id: number;
+ title: string;
+ body: string;
+ userId: number;
+}
+
+const API_BASE = 'https://api.example.com';
+
+/**
+ * Fetch user by ID
+ */
+export async function fetchUser(id: number): Promise {
+ const response = await fetch(`${API_BASE}/users/${id}`);
+ if (!response.ok) {
+ throw new Error(`Failed to fetch user: ${response.status}`);
+ }
+ return response.json();
+}
+
+/**
+ * Fetch posts by user ID
+ */
+export async function fetchUserPosts(userId: number): Promise {
+ const response = await fetch(`${API_BASE}/users/${userId}/posts`);
+ if (!response.ok) {
+ throw new Error(`Failed to fetch posts: ${response.status}`);
+ }
+ return response.json();
+}
+
+/**
+ * Create a new post
+ */
+export async function createPost(post: Omit): Promise {
+ const response = await fetch(`${API_BASE}/posts`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(post),
+ });
+ if (!response.ok) {
+ throw new Error(`Failed to create post: ${response.status}`);
+ }
+ return response.json();
+}
diff --git a/rstest/mocking/src/index.ts b/rstest/mocking/src/index.ts
new file mode 100644
index 00000000..350dad1c
--- /dev/null
+++ b/rstest/mocking/src/index.ts
@@ -0,0 +1,4 @@
+export * from './api';
+export * from './logger';
+export * from './user-service';
+export * from './utils';
diff --git a/rstest/mocking/src/logger.ts b/rstest/mocking/src/logger.ts
new file mode 100644
index 00000000..7badc642
--- /dev/null
+++ b/rstest/mocking/src/logger.ts
@@ -0,0 +1,67 @@
+/**
+ * Logger service for mocking demonstration
+ */
+
+export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
+
+export interface LogEntry {
+ level: LogLevel;
+ message: string;
+ timestamp: Date;
+ metadata?: Record;
+}
+
+export class Logger {
+ private name: string;
+ private entries: LogEntry[] = [];
+
+ constructor(name: string) {
+ this.name = name;
+ }
+
+ private createEntry(
+ level: LogLevel,
+ message: string,
+ metadata?: Record,
+ ): LogEntry {
+ const entry: LogEntry = {
+ level,
+ message: `[${this.name}] ${message}`,
+ timestamp: new Date(),
+ metadata,
+ };
+ this.entries.push(entry);
+ return entry;
+ }
+
+ debug(message: string, metadata?: Record): void {
+ const entry = this.createEntry('debug', message, metadata);
+ console.debug(entry.message, metadata);
+ }
+
+ info(message: string, metadata?: Record): void {
+ const entry = this.createEntry('info', message, metadata);
+ console.info(entry.message, metadata);
+ }
+
+ warn(message: string, metadata?: Record): void {
+ const entry = this.createEntry('warn', message, metadata);
+ console.warn(entry.message, metadata);
+ }
+
+ error(message: string, metadata?: Record): void {
+ const entry = this.createEntry('error', message, metadata);
+ console.error(entry.message, metadata);
+ }
+
+ getEntries(): LogEntry[] {
+ return [...this.entries];
+ }
+
+ clear(): void {
+ this.entries = [];
+ }
+}
+
+// Default logger instance
+export const logger = new Logger('App');
diff --git a/rstest/mocking/src/user-service.ts b/rstest/mocking/src/user-service.ts
new file mode 100644
index 00000000..43a6f71a
--- /dev/null
+++ b/rstest/mocking/src/user-service.ts
@@ -0,0 +1,35 @@
+import { fetchUser, fetchUserPosts, type Post, type User } from './api';
+import { logger } from './logger';
+
+/**
+ * User service that uses API and logger
+ */
+export class UserService {
+ async getUser(id: number): Promise {
+ logger.info(`Fetching user with ID: ${id}`);
+ try {
+ const user = await fetchUser(id);
+ logger.info(`Successfully fetched user: ${user.name}`);
+ return user;
+ } catch (error) {
+ logger.error(`Failed to fetch user: ${error}`);
+ throw error;
+ }
+ }
+
+ async getUserWithPosts(id: number): Promise<{ user: User; posts: Post[] }> {
+ logger.info(`Fetching user and posts for ID: ${id}`);
+
+ const user = await fetchUser(id);
+ const posts = await fetchUserPosts(id);
+
+ logger.info(`Fetched ${posts.length} posts for user: ${user.name}`);
+
+ return { user, posts };
+ }
+
+ async getUserNames(ids: number[]): Promise {
+ const users = await Promise.all(ids.map((id) => fetchUser(id)));
+ return users.map((user) => user.name);
+ }
+}
diff --git a/rstest/mocking/src/utils.ts b/rstest/mocking/src/utils.ts
new file mode 100644
index 00000000..c7df1558
--- /dev/null
+++ b/rstest/mocking/src/utils.ts
@@ -0,0 +1,38 @@
+/**
+ * Utility functions for mocking demonstration
+ */
+
+/**
+ * Generate a random ID
+ */
+export function generateId(): string {
+ return Math.random().toString(36).substring(2, 15);
+}
+
+/**
+ * Get current timestamp
+ */
+export function getTimestamp(): number {
+ return Date.now();
+}
+
+/**
+ * Format a date
+ */
+export function formatDate(date: Date): string {
+ return date.toISOString();
+}
+
+/**
+ * Sleep for a given number of milliseconds
+ */
+export function sleep(ms: number): Promise {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+}
+
+/**
+ * Log a message with timestamp
+ */
+export function log(message: string): void {
+ console.log(`[${new Date().toISOString()}] ${message}`);
+}
diff --git a/rstest/mocking/tests/dynamic-mock.test.ts b/rstest/mocking/tests/dynamic-mock.test.ts
new file mode 100644
index 00000000..c7d57e21
--- /dev/null
+++ b/rstest/mocking/tests/dynamic-mock.test.ts
@@ -0,0 +1,158 @@
+import { afterEach, beforeEach, describe, expect, it, rs, rstest } from '@rstest/core';
+
+describe('rs.doMock() - Dynamic Module Mocking', () => {
+ /**
+ * rs.doMock() is NOT hoisted, allowing you to mock modules dynamically
+ * based on test conditions. Use with dynamic imports.
+ */
+
+ beforeEach(() => {
+ rs.resetModules();
+ });
+
+ afterEach(() => {
+ rs.resetModules();
+ });
+
+ it('should dynamically mock a module', async () => {
+ rs.doMock('../src/utils', () => ({
+ generateId: () => 'fixed-id-123',
+ getTimestamp: () => 1000000,
+ }));
+
+ const { generateId, getTimestamp } = await import('../src/utils');
+
+ expect(generateId()).toBe('fixed-id-123');
+ expect(getTimestamp()).toBe(1000000);
+ });
+
+ it('should use different mocks in different tests', async () => {
+ rs.doMock('../src/utils', () => ({
+ generateId: () => 'different-id',
+ getTimestamp: () => 2000000,
+ }));
+
+ const { generateId, getTimestamp } = await import('../src/utils');
+
+ expect(generateId()).toBe('different-id');
+ expect(getTimestamp()).toBe(2000000);
+ });
+
+ it('should conditionally mock based on test conditions', async () => {
+ const shouldMock = true;
+
+ if (shouldMock) {
+ rs.doMock('../src/utils', () => ({
+ generateId: () => 'conditional-mock',
+ getTimestamp: () => 0,
+ }));
+ }
+
+ const { generateId } = await import('../src/utils');
+
+ expect(generateId()).toBe('conditional-mock');
+ });
+});
+
+describe('rs.doUnmock() - Remove Dynamic Mocks', () => {
+ beforeEach(() => {
+ rs.resetModules();
+ });
+
+ afterEach(() => {
+ rs.resetModules();
+ });
+
+ it('should remove dynamic mock', async () => {
+ rs.doMock('../src/utils', () => ({
+ formatDate: () => 'mocked-date',
+ }));
+
+ const mocked = await import('../src/utils');
+ expect(mocked.formatDate(new Date())).toBe('mocked-date');
+
+ rs.doUnmock('../src/utils');
+ rs.resetModules();
+
+ const original = await import('../src/utils');
+ // Now uses original implementation
+ expect(original.formatDate(new Date('2024-01-01'))).toBe('2024-01-01T00:00:00.000Z');
+ });
+});
+
+describe('rs.importActual() - Import Original Module', () => {
+ /**
+ * When you need to use the original module implementation
+ * alongside mocked parts
+ */
+
+ it('should import actual module inside mock', async () => {
+ rs.doMock('../src/utils', async () => {
+ const actual = await rs.importActual('../src/utils');
+
+ return {
+ ...actual,
+ // Override only specific functions
+ generateId: () => 'overridden-id',
+ };
+ });
+
+ const utils = await import('../src/utils');
+
+ // Overridden function
+ expect(utils.generateId()).toBe('overridden-id');
+
+ // Original functions still work
+ expect(utils.formatDate(new Date('2024-01-01'))).toBe('2024-01-01T00:00:00.000Z');
+ });
+});
+
+describe('rs.importMock() - Import Mocked Module', () => {
+ /**
+ * rs.importMock() returns the mocked version of a module
+ * Note: This requires the module to be mocked first
+ */
+
+ beforeEach(() => {
+ rs.resetModules();
+ });
+
+ it('should get mocked module via dynamic import', async () => {
+ const mockFn = rstest.fn().mockReturnValue('mock-id');
+
+ rs.doMock('../src/utils', () => ({
+ generateId: mockFn,
+ }));
+
+ // Use dynamic import to get the mocked module
+ const mockedUtils = await import('../src/utils');
+
+ expect(mockedUtils.generateId()).toBe('mock-id');
+ expect(mockFn).toHaveBeenCalled();
+ });
+});
+
+describe('rs.resetModules() - Reset Module Registry', () => {
+ /**
+ * rs.resetModules() clears the module cache,
+ * allowing fresh imports
+ */
+
+ it('should reset module cache', async () => {
+ rs.doMock('../src/utils', () => ({
+ generateId: () => 'first-mock',
+ }));
+
+ const first = await import('../src/utils');
+ expect(first.generateId()).toBe('first-mock');
+
+ rs.resetModules();
+
+ rs.doMock('../src/utils', () => ({
+ generateId: () => 'second-mock',
+ }));
+
+ const second = await import('../src/utils');
+ expect(second.generateId()).toBe('second-mock');
+ });
+});
diff --git a/rstest/mocking/tests/fn.test.ts b/rstest/mocking/tests/fn.test.ts
new file mode 100644
index 00000000..1f502523
--- /dev/null
+++ b/rstest/mocking/tests/fn.test.ts
@@ -0,0 +1,231 @@
+import { afterEach, beforeEach, describe, expect, it, rstest } from '@rstest/core';
+
+describe('rstest.fn() - Mock Functions', () => {
+ /**
+ * rstest.fn() creates a mock function that tracks calls,
+ * arguments, and return values
+ */
+
+ describe('Basic Usage', () => {
+ it('should create a mock function', () => {
+ const mockFn = rstest.fn();
+
+ mockFn();
+ mockFn('arg1', 'arg2');
+
+ expect(mockFn).toHaveBeenCalled();
+ expect(mockFn).toHaveBeenCalledTimes(2);
+ });
+
+ it('should track call arguments', () => {
+ const mockFn = rstest.fn();
+
+ mockFn('first');
+ mockFn('second', 123);
+ mockFn({ key: 'value' });
+
+ expect(mockFn).toHaveBeenCalledWith('first');
+ expect(mockFn).toHaveBeenCalledWith('second', 123);
+ expect(mockFn).toHaveBeenCalledWith({ key: 'value' });
+ expect(mockFn).toHaveBeenLastCalledWith({ key: 'value' });
+ expect(mockFn).toHaveBeenNthCalledWith(1, 'first');
+ });
+ });
+
+ describe('mockReturnValue', () => {
+ it('should return specified value', () => {
+ const mockFn = rstest.fn().mockReturnValue(42);
+
+ expect(mockFn()).toBe(42);
+ expect(mockFn()).toBe(42);
+ });
+
+ it('should return different values with mockReturnValueOnce', () => {
+ const mockFn = rstest.fn().mockReturnValueOnce(1).mockReturnValueOnce(2).mockReturnValue(0);
+
+ expect(mockFn()).toBe(1);
+ expect(mockFn()).toBe(2);
+ expect(mockFn()).toBe(0);
+ expect(mockFn()).toBe(0);
+ });
+ });
+
+ describe('mockImplementation', () => {
+ it('should use custom implementation', () => {
+ const mockFn = rstest.fn().mockImplementation((a: number, b: number) => a + b);
+
+ expect(mockFn(2, 3)).toBe(5);
+ expect(mockFn(10, 20)).toBe(30);
+ });
+
+ it('should use mockImplementationOnce', () => {
+ const mockFn = rstest
+ .fn()
+ .mockImplementationOnce(() => 'first')
+ .mockImplementationOnce(() => 'second')
+ .mockImplementation(() => 'default');
+
+ expect(mockFn()).toBe('first');
+ expect(mockFn()).toBe('second');
+ expect(mockFn()).toBe('default');
+ });
+ });
+
+ describe('mockResolvedValue / mockRejectedValue', () => {
+ it('should return resolved promise', async () => {
+ const mockFn = rstest.fn().mockResolvedValue('success');
+
+ const result = await mockFn();
+ expect(result).toBe('success');
+ });
+
+ it('should return resolved values sequentially', async () => {
+ const mockFn = rstest
+ .fn()
+ .mockResolvedValueOnce('first')
+ .mockResolvedValueOnce('second')
+ .mockResolvedValue('default');
+
+ expect(await mockFn()).toBe('first');
+ expect(await mockFn()).toBe('second');
+ expect(await mockFn()).toBe('default');
+ });
+
+ it('should return rejected promise', async () => {
+ const mockFn = rstest.fn().mockRejectedValue(new Error('failed'));
+
+ await expect(mockFn()).rejects.toThrow('failed');
+ });
+
+ it('should reject once then resolve', async () => {
+ const mockFn = rstest
+ .fn()
+ .mockRejectedValueOnce(new Error('first attempt failed'))
+ .mockResolvedValue('success');
+
+ await expect(mockFn()).rejects.toThrow('first attempt failed');
+ expect(await mockFn()).toBe('success');
+ });
+ });
+
+ describe('Mock Properties', () => {
+ it('should access mock.calls', () => {
+ const mockFn = rstest.fn();
+
+ mockFn('a', 1);
+ mockFn('b', 2);
+
+ expect(mockFn.mock.calls).toEqual([
+ ['a', 1],
+ ['b', 2],
+ ]);
+ });
+
+ it('should access mock.results', () => {
+ const mockFn = rstest.fn().mockReturnValueOnce(10).mockReturnValueOnce(20);
+
+ mockFn();
+ mockFn();
+
+ expect(mockFn.mock.results).toEqual([
+ { type: 'return', value: 10 },
+ { type: 'return', value: 20 },
+ ]);
+ });
+
+ it('should access mock.lastCall', () => {
+ const mockFn = rstest.fn();
+
+ mockFn('first');
+ mockFn('last');
+
+ expect(mockFn.mock.lastCall).toEqual(['last']);
+ });
+ });
+
+ describe('Reset and Clear', () => {
+ it('should clear mock with mockClear', () => {
+ const mockFn = rstest.fn().mockReturnValue(42);
+
+ mockFn();
+ mockFn();
+
+ mockFn.mockClear();
+
+ expect(mockFn.mock.calls).toEqual([]);
+ expect(mockFn()).toBe(42); // Implementation still works
+ });
+
+ it('should reset mock with mockReset', () => {
+ const mockFn = rstest.fn().mockReturnValue(42);
+
+ mockFn();
+
+ mockFn.mockReset();
+
+ expect(mockFn.mock.calls).toEqual([]);
+ expect(mockFn()).toBeUndefined(); // Implementation is reset
+ });
+
+ it('should restore mock with mockRestore', () => {
+ const mockFn = rstest.fn().mockReturnValue(42);
+
+ mockFn();
+
+ mockFn.mockRestore();
+
+ expect(mockFn.mock.calls).toEqual([]);
+ });
+ });
+});
+
+describe('rstest.isMockFunction()', () => {
+ it('should return true for mock functions', () => {
+ const mockFn = rstest.fn();
+ expect(rstest.isMockFunction(mockFn)).toBe(true);
+ });
+
+ it('should return false for regular functions', () => {
+ const regularFn = () => {};
+ expect(rstest.isMockFunction(regularFn)).toBe(false);
+ });
+});
+
+describe('Global Mock Management', () => {
+ let mock1: ReturnType;
+ let mock2: ReturnType;
+
+ beforeEach(() => {
+ mock1 = rstest.fn().mockReturnValue('a');
+ mock2 = rstest.fn().mockReturnValue('b');
+
+ mock1();
+ mock2();
+ });
+
+ afterEach(() => {
+ rstest.restoreAllMocks();
+ });
+
+ it('should clear all mocks with rstest.clearAllMocks', () => {
+ rstest.clearAllMocks();
+
+ expect(mock1.mock.calls).toEqual([]);
+ expect(mock2.mock.calls).toEqual([]);
+
+ // Implementations still work
+ expect(mock1()).toBe('a');
+ expect(mock2()).toBe('b');
+ });
+
+ it('should reset all mocks with rstest.resetAllMocks', () => {
+ rstest.resetAllMocks();
+
+ expect(mock1.mock.calls).toEqual([]);
+ expect(mock2.mock.calls).toEqual([]);
+
+ // Implementations are reset
+ expect(mock1()).toBeUndefined();
+ expect(mock2()).toBeUndefined();
+ });
+});
diff --git a/rstest/mocking/tests/matchers.test.ts b/rstest/mocking/tests/matchers.test.ts
new file mode 100644
index 00000000..355efa34
--- /dev/null
+++ b/rstest/mocking/tests/matchers.test.ts
@@ -0,0 +1,197 @@
+import { describe, expect, it, rstest } from '@rstest/core';
+
+describe('Mock Matchers', () => {
+ /**
+ * Special matchers for asserting mock function calls
+ */
+
+ describe('toHaveBeenCalled / toHaveBeenCalledTimes', () => {
+ it('should check if mock was called', () => {
+ const mockFn = rstest.fn();
+
+ expect(mockFn).not.toHaveBeenCalled();
+
+ mockFn();
+
+ expect(mockFn).toHaveBeenCalled();
+ });
+
+ it('should check exact call count', () => {
+ const mockFn = rstest.fn();
+
+ mockFn();
+ mockFn();
+ mockFn();
+
+ expect(mockFn).toHaveBeenCalledTimes(3);
+ });
+ });
+
+ describe('toHaveBeenCalledWith', () => {
+ it('should check call arguments', () => {
+ const mockFn = rstest.fn();
+
+ mockFn('hello', 123, { key: 'value' });
+
+ expect(mockFn).toHaveBeenCalledWith('hello', 123, { key: 'value' });
+ });
+
+ it('should work with partial matchers', () => {
+ const mockFn = rstest.fn();
+
+ mockFn({ name: 'John', age: 30, city: 'NYC' });
+
+ expect(mockFn).toHaveBeenCalledWith(expect.objectContaining({ name: 'John' }));
+ });
+
+ it('should work with array matchers', () => {
+ const mockFn = rstest.fn();
+
+ mockFn([1, 2, 3, 4, 5]);
+
+ expect(mockFn).toHaveBeenCalledWith(expect.arrayContaining([1, 2, 3]));
+ });
+ });
+
+ describe('toHaveBeenLastCalledWith', () => {
+ it('should check last call arguments', () => {
+ const mockFn = rstest.fn();
+
+ mockFn('first');
+ mockFn('second');
+ mockFn('last');
+
+ expect(mockFn).toHaveBeenLastCalledWith('last');
+ });
+ });
+
+ describe('toHaveBeenNthCalledWith', () => {
+ it('should check nth call arguments', () => {
+ const mockFn = rstest.fn();
+
+ mockFn('a');
+ mockFn('b');
+ mockFn('c');
+
+ expect(mockFn).toHaveBeenNthCalledWith(1, 'a');
+ expect(mockFn).toHaveBeenNthCalledWith(2, 'b');
+ expect(mockFn).toHaveBeenNthCalledWith(3, 'c');
+ });
+ });
+
+ describe('toHaveReturned / toHaveReturnedWith', () => {
+ it('should check if mock returned', () => {
+ const mockFn = rstest.fn().mockReturnValue(42);
+
+ mockFn();
+
+ expect(mockFn).toHaveReturned();
+ });
+
+ it('should check return value', () => {
+ const mockFn = rstest.fn().mockReturnValue('result');
+
+ mockFn();
+
+ expect(mockFn).toHaveReturnedWith('result');
+ });
+
+ it('should check return times', () => {
+ const mockFn = rstest.fn().mockReturnValue(true);
+
+ mockFn();
+ mockFn();
+
+ expect(mockFn).toHaveReturnedTimes(2);
+ });
+
+ it('should check last return value', () => {
+ const mockFn = rstest
+ .fn()
+ .mockReturnValueOnce(1)
+ .mockReturnValueOnce(2)
+ .mockReturnValueOnce(3);
+
+ mockFn();
+ mockFn();
+ mockFn();
+
+ expect(mockFn).toHaveLastReturnedWith(3);
+ });
+
+ it('should check nth return value', () => {
+ const mockFn = rstest.fn().mockReturnValueOnce('first').mockReturnValueOnce('second');
+
+ mockFn();
+ mockFn();
+
+ expect(mockFn).toHaveNthReturnedWith(1, 'first');
+ expect(mockFn).toHaveNthReturnedWith(2, 'second');
+ });
+ });
+});
+
+describe('Asymmetric Matchers with Mocks', () => {
+ it('should use expect.any()', () => {
+ const mockFn = rstest.fn();
+
+ mockFn(42, 'string', new Date());
+
+ expect(mockFn).toHaveBeenCalledWith(expect.any(Number), expect.any(String), expect.any(Date));
+ });
+
+ it('should use expect.stringContaining()', () => {
+ const mockFn = rstest.fn();
+
+ mockFn('Hello, World!');
+
+ expect(mockFn).toHaveBeenCalledWith(expect.stringContaining('World'));
+ });
+
+ it('should use expect.stringMatching()', () => {
+ const mockFn = rstest.fn();
+
+ mockFn('user@example.com');
+
+ expect(mockFn).toHaveBeenCalledWith(expect.stringMatching(/@example\.com$/));
+ });
+
+ it('should use expect.objectContaining()', () => {
+ const mockFn = rstest.fn();
+
+ mockFn({
+ id: 1,
+ name: 'Test',
+ email: 'test@example.com',
+ createdAt: new Date(),
+ });
+
+ expect(mockFn).toHaveBeenCalledWith(
+ expect.objectContaining({
+ id: 1,
+ name: 'Test',
+ }),
+ );
+ });
+
+ it('should combine multiple asymmetric matchers', () => {
+ const mockFn = rstest.fn();
+
+ mockFn({
+ user: { id: 123, name: 'John' },
+ timestamp: Date.now(),
+ message: 'User logged in successfully',
+ });
+
+ expect(mockFn).toHaveBeenCalledWith(
+ expect.objectContaining({
+ user: expect.objectContaining({
+ id: expect.any(Number),
+ name: expect.any(String),
+ }),
+ timestamp: expect.any(Number),
+ message: expect.stringContaining('logged in'),
+ }),
+ );
+ });
+});
diff --git a/rstest/mocking/tests/module-mock.test.ts b/rstest/mocking/tests/module-mock.test.ts
new file mode 100644
index 00000000..5c945106
--- /dev/null
+++ b/rstest/mocking/tests/module-mock.test.ts
@@ -0,0 +1,91 @@
+import { afterEach, beforeEach, describe, expect, it, rs } from '@rstest/core';
+
+/**
+ * rs.mock() is hoisted to the top of the file and replaces the module.
+ * Use rs.hoisted() to define mock values that need to be available during hoisting.
+ */
+
+// Using rs.hoisted to define mock values before rs.mock is hoisted
+const { mockFetchUser, mockFetchUserPosts } = rs.hoisted(() => {
+ return {
+ mockFetchUser: rstest.fn(),
+ mockFetchUserPosts: rstest.fn(),
+ };
+});
+
+// Mock the API module
+rs.mock('../src/api', () => ({
+ fetchUser: mockFetchUser,
+ fetchUserPosts: mockFetchUserPosts,
+ createPost: rstest.fn(),
+}));
+
+import { rstest } from '@rstest/core';
+// Import after mocking - the import will use the mocked version
+import { UserService } from '../src/user-service';
+
+describe('rs.mock() - Module Mocking', () => {
+ let userService: UserService;
+
+ beforeEach(() => {
+ userService = new UserService();
+ rstest.clearAllMocks();
+ });
+
+ afterEach(() => {
+ rstest.resetAllMocks();
+ });
+
+ describe('Mocking API Calls', () => {
+ it('should use mocked fetchUser', async () => {
+ mockFetchUser.mockResolvedValue({
+ id: 1,
+ name: 'Mock User',
+ email: 'mock@example.com',
+ });
+
+ const user = await userService.getUser(1);
+
+ expect(user.name).toBe('Mock User');
+ expect(mockFetchUser).toHaveBeenCalledWith(1);
+ });
+
+ it('should use mocked fetchUserPosts', async () => {
+ mockFetchUser.mockResolvedValue({
+ id: 1,
+ name: 'Test User',
+ email: 'test@example.com',
+ });
+
+ mockFetchUserPosts.mockResolvedValue([
+ { id: 1, title: 'Post 1', body: 'Body 1', userId: 1 },
+ { id: 2, title: 'Post 2', body: 'Body 2', userId: 1 },
+ ]);
+
+ const { user, posts } = await userService.getUserWithPosts(1);
+
+ expect(user.name).toBe('Test User');
+ expect(posts.length).toBe(2);
+ expect(mockFetchUser).toHaveBeenCalledWith(1);
+ expect(mockFetchUserPosts).toHaveBeenCalledWith(1);
+ });
+
+ it('should handle errors from mocked API', async () => {
+ mockFetchUser.mockRejectedValue(new Error('API Error'));
+
+ await expect(userService.getUser(1)).rejects.toThrow('API Error');
+ });
+
+ it('should mock multiple user fetches', async () => {
+ mockFetchUser
+ .mockResolvedValueOnce({ id: 1, name: 'User 1', email: 'user1@example.com' })
+ .mockResolvedValueOnce({ id: 2, name: 'User 2', email: 'user2@example.com' })
+ .mockResolvedValueOnce({ id: 3, name: 'User 3', email: 'user3@example.com' });
+
+ const names = await userService.getUserNames([1, 2, 3]);
+
+ expect(names).toEqual(['User 1', 'User 2', 'User 3']);
+ expect(mockFetchUser).toHaveBeenCalledTimes(3);
+ });
+ });
+});
diff --git a/rstest/mocking/tests/spy.test.ts b/rstest/mocking/tests/spy.test.ts
new file mode 100644
index 00000000..62b0d161
--- /dev/null
+++ b/rstest/mocking/tests/spy.test.ts
@@ -0,0 +1,176 @@
+import { afterEach, beforeEach, describe, expect, it, rstest } from '@rstest/core';
+import { Logger } from '../src/logger';
+
+describe('rstest.spyOn() - Spying on Methods', () => {
+ /**
+ * rstest.spyOn() creates a spy on an existing method,
+ * allowing you to track calls while preserving the original implementation
+ */
+
+ describe('Basic Usage', () => {
+ it('should spy on object method', () => {
+ const calculator = {
+ add: (a: number, b: number) => a + b,
+ };
+
+ const spy = rstest.spyOn(calculator, 'add');
+
+ const result = calculator.add(2, 3);
+
+ expect(result).toBe(5);
+ expect(spy).toHaveBeenCalled();
+ expect(spy).toHaveBeenCalledWith(2, 3);
+ });
+
+ it('should track multiple calls', () => {
+ const obj = {
+ greet: (name: string) => `Hello, ${name}!`,
+ };
+
+ const spy = rstest.spyOn(obj, 'greet');
+
+ obj.greet('Alice');
+ obj.greet('Bob');
+ obj.greet('Charlie');
+
+ expect(spy).toHaveBeenCalledTimes(3);
+ expect(spy).toHaveBeenNthCalledWith(1, 'Alice');
+ expect(spy).toHaveBeenNthCalledWith(2, 'Bob');
+ expect(spy).toHaveBeenNthCalledWith(3, 'Charlie');
+ });
+ });
+
+ describe('Mocking Implementation', () => {
+ it('should mock the implementation', () => {
+ const api = {
+ fetchData: () => 'real data',
+ };
+
+ const spy = rstest.spyOn(api, 'fetchData').mockReturnValue('mocked data');
+
+ expect(api.fetchData()).toBe('mocked data');
+ expect(spy).toHaveBeenCalled();
+ });
+
+ it('should mock with custom implementation', () => {
+ const math = {
+ multiply: (a: number, b: number) => a * b,
+ };
+
+ rstest.spyOn(math, 'multiply').mockImplementation((a, b) => a + b);
+
+ expect(math.multiply(2, 3)).toBe(5); // Returns sum instead of product
+ });
+
+ it('should restore original implementation', () => {
+ const obj = {
+ getValue: () => 'original',
+ };
+
+ const spy = rstest.spyOn(obj, 'getValue').mockReturnValue('mocked');
+
+ expect(obj.getValue()).toBe('mocked');
+
+ spy.mockRestore();
+
+ expect(obj.getValue()).toBe('original');
+ });
+ });
+
+ describe('Spying on Class Methods', () => {
+ let logger: Logger;
+
+ beforeEach(() => {
+ logger = new Logger('Test');
+ });
+
+ afterEach(() => {
+ rstest.restoreAllMocks();
+ });
+
+ it('should spy on class instance method', () => {
+ const infoSpy = rstest.spyOn(logger, 'info');
+
+ logger.info('Test message');
+
+ expect(infoSpy).toHaveBeenCalledWith('Test message');
+ });
+
+ it('should spy on multiple methods', () => {
+ const infoSpy = rstest.spyOn(logger, 'info');
+ const errorSpy = rstest.spyOn(logger, 'error');
+
+ logger.info('Info message');
+ logger.error('Error message');
+
+ expect(infoSpy).toHaveBeenCalledWith('Info message');
+ expect(errorSpy).toHaveBeenCalledWith('Error message');
+ });
+
+ it('should mock class method implementation', () => {
+ const warnSpy = rstest.spyOn(logger, 'warn').mockImplementation(() => {
+ // Do nothing - suppress output
+ });
+
+ logger.warn('This should be suppressed');
+
+ expect(warnSpy).toHaveBeenCalled();
+ });
+ });
+
+ describe('Spying on Console', () => {
+ afterEach(() => {
+ rstest.restoreAllMocks();
+ });
+
+ it('should spy on console.log', () => {
+ const consoleSpy = rstest.spyOn(console, 'log').mockImplementation(() => {});
+
+ console.log('Hello', 'World');
+
+ expect(consoleSpy).toHaveBeenCalledWith('Hello', 'World');
+ });
+
+ it('should spy on console.error', () => {
+ const consoleSpy = rstest.spyOn(console, 'error').mockImplementation(() => {});
+
+ console.error('Error occurred');
+
+ expect(consoleSpy).toHaveBeenCalledWith('Error occurred');
+ });
+ });
+
+ describe('Spying on Getters and Setters', () => {
+ it('should spy on getter', () => {
+ const obj = {
+ _value: 42,
+ get value() {
+ return this._value;
+ },
+ };
+
+ const spy = rstest.spyOn(obj, 'value', 'get').mockReturnValue(100);
+
+ expect(obj.value).toBe(100);
+ expect(spy).toHaveBeenCalled();
+ });
+
+ it('should spy on setter', () => {
+ const obj = {
+ _value: 0,
+ get value() {
+ return this._value;
+ },
+ set value(v: number) {
+ this._value = v;
+ },
+ };
+
+ const spy = rstest.spyOn(obj, 'value', 'set');
+
+ obj.value = 42;
+
+ expect(spy).toHaveBeenCalledWith(42);
+ });
+ });
+});
diff --git a/rstest/mocking/tsconfig.json b/rstest/mocking/tsconfig.json
new file mode 100644
index 00000000..5d1c2508
--- /dev/null
+++ b/rstest/mocking/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "declaration": true,
+ "types": ["@rstest/core"]
+ },
+ "include": ["src", "tests", "rstest.config.ts"]
+}
diff --git a/rstest/rsbuild-adapter/package.json b/rstest/rsbuild-adapter/package.json
new file mode 100644
index 00000000..fefef090
--- /dev/null
+++ b/rstest/rsbuild-adapter/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "rstest-rsbuild-adapter",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "dev": "rsbuild dev",
+ "build": "rsbuild build",
+ "test": "rstest run"
+ },
+ "devDependencies": {
+ "@rsbuild/core": "^1.7.2",
+ "@rstest/adapter-rsbuild": "^0.1.0",
+ "@rstest/core": "^0.7.9",
+ "happy-dom": "^18.0.1",
+ "typescript": "^5.8.3"
+ }
+}
diff --git a/rstest/rsbuild-adapter/rsbuild.config.ts b/rstest/rsbuild-adapter/rsbuild.config.ts
new file mode 100644
index 00000000..9f8ca5b6
--- /dev/null
+++ b/rstest/rsbuild-adapter/rsbuild.config.ts
@@ -0,0 +1,18 @@
+import { defineConfig } from '@rsbuild/core';
+
+export default defineConfig({
+ source: {
+ entry: {
+ index: './src/index.ts',
+ },
+ define: {
+ __APP_VERSION__: JSON.stringify('2.0.0'),
+ __PLATFORM__: JSON.stringify('web'),
+ 'process.env.APP_NAME': JSON.stringify('my-app'),
+ },
+ alias: {
+ '@': './src',
+ '@utils': './src/utils',
+ },
+ },
+});
diff --git a/rstest/rsbuild-adapter/rstest.config.ts b/rstest/rsbuild-adapter/rstest.config.ts
new file mode 100644
index 00000000..33007c16
--- /dev/null
+++ b/rstest/rsbuild-adapter/rstest.config.ts
@@ -0,0 +1,9 @@
+import { withRsbuildConfig } from '@rstest/adapter-rsbuild';
+import { defineConfig, type ExtendConfigFn } from '@rstest/core';
+
+export default defineConfig({
+ // Type assertion needed due to adapter package version mismatch
+ extends: withRsbuildConfig({
+ environmentName: 'web',
+ }) as ExtendConfigFn,
+});
diff --git a/rstest/rsbuild-adapter/src/index.ts b/rstest/rsbuild-adapter/src/index.ts
new file mode 100644
index 00000000..298506d5
--- /dev/null
+++ b/rstest/rsbuild-adapter/src/index.ts
@@ -0,0 +1,22 @@
+import { capitalize } from '@utils/string';
+
+declare const __APP_VERSION__: string;
+declare const __PLATFORM__: string;
+
+export function getAppVersion(): string {
+ return __APP_VERSION__;
+}
+
+export function getPlatform(): string {
+ return __PLATFORM__;
+}
+
+export function getAppName(): string {
+ return process.env.APP_NAME || 'unknown';
+}
+
+export function formatAppInfo(): string {
+ return `${capitalize(getAppName())} v${getAppVersion()} (${getPlatform()})`;
+}
+
+export { capitalize } from '@utils/string';
diff --git a/rstest/rsbuild-adapter/src/utils/index.ts b/rstest/rsbuild-adapter/src/utils/index.ts
new file mode 100644
index 00000000..57f9f48d
--- /dev/null
+++ b/rstest/rsbuild-adapter/src/utils/index.ts
@@ -0,0 +1 @@
+export * from './string';
diff --git a/rstest/rsbuild-adapter/src/utils/string.ts b/rstest/rsbuild-adapter/src/utils/string.ts
new file mode 100644
index 00000000..e9619c59
--- /dev/null
+++ b/rstest/rsbuild-adapter/src/utils/string.ts
@@ -0,0 +1,8 @@
+export function capitalize(str: string): string {
+ if (!str) return '';
+ return str.charAt(0).toUpperCase() + str.slice(1);
+}
+
+export function lowercase(str: string): string {
+ return str.toLowerCase();
+}
diff --git a/rstest/rsbuild-adapter/tests/index.test.ts b/rstest/rsbuild-adapter/tests/index.test.ts
new file mode 100644
index 00000000..fb0242c9
--- /dev/null
+++ b/rstest/rsbuild-adapter/tests/index.test.ts
@@ -0,0 +1,28 @@
+import { describe, expect, it } from '@rstest/core';
+import { formatAppInfo, getAppName, getAppVersion, getPlatform } from '@/index';
+
+describe('withRsbuildConfig - define inheritance', () => {
+ it('should inherit __APP_VERSION__ from rsbuild.config.ts', () => {
+ expect(getAppVersion()).toBe('2.0.0');
+ });
+
+ it('should inherit __PLATFORM__ from rsbuild.config.ts', () => {
+ expect(getPlatform()).toBe('web');
+ });
+
+ it('should inherit process.env.APP_NAME from rsbuild.config.ts', () => {
+ expect(getAppName()).toBe('my-app');
+ });
+});
+
+describe('withRsbuildConfig - alias inheritance', () => {
+ it('should resolve @/ alias to src directory', () => {
+ expect(typeof formatAppInfo).toBe('function');
+ expect(formatAppInfo()).toBe('My-app v2.0.0 (web)');
+ });
+
+ it('should resolve @utils alias', async () => {
+ const { capitalize } = await import('@utils/string');
+ expect(capitalize('hello')).toBe('Hello');
+ });
+});
diff --git a/rstest/rsbuild-adapter/tsconfig.json b/rstest/rsbuild-adapter/tsconfig.json
new file mode 100644
index 00000000..71d7f7aa
--- /dev/null
+++ b/rstest/rsbuild-adapter/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"],
+ "@utils/*": ["./src/utils/*"]
+ },
+ "types": ["@rstest/core"]
+ },
+ "include": ["src", "tests", "rstest.config.ts", "rsbuild.config.ts"]
+}
diff --git a/rstest/rslib-adapter/package.json b/rstest/rslib-adapter/package.json
new file mode 100644
index 00000000..024f5ff2
--- /dev/null
+++ b/rstest/rslib-adapter/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "rstest-rslib-adapter",
+ "version": "1.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "build": "rslib build",
+ "test": "rstest run"
+ },
+ "devDependencies": {
+ "@rslib/core": "^0.19.1",
+ "@rstest/adapter-rslib": "^0.1.1",
+ "@rstest/core": "^0.7.9",
+ "typescript": "^5.8.3"
+ }
+}
diff --git a/rstest/rslib-adapter/rslib.config.ts b/rstest/rslib-adapter/rslib.config.ts
new file mode 100644
index 00000000..80188408
--- /dev/null
+++ b/rstest/rslib-adapter/rslib.config.ts
@@ -0,0 +1,34 @@
+import { defineConfig } from '@rslib/core';
+
+export default defineConfig({
+ lib: [
+ {
+ id: 'esm',
+ format: 'esm',
+ syntax: 'es2021',
+ dts: true,
+ },
+ {
+ id: 'cjs',
+ format: 'cjs',
+ syntax: 'es2021',
+ },
+ ],
+ source: {
+ entry: {
+ index: './src/index.ts',
+ utils: './src/utils.ts',
+ },
+ // Define compile-time constants
+ define: {
+ __VERSION__: JSON.stringify('1.0.0'),
+ __DEV__: JSON.stringify(true),
+ 'process.env.LIB_NAME': JSON.stringify('rstest-rslib-adapter'),
+ },
+ // Alias configuration
+ alias: {
+ '@': './src',
+ '@utils': './src/utils.ts',
+ },
+ },
+});
diff --git a/rstest/rslib-adapter/rstest.config.ts b/rstest/rslib-adapter/rstest.config.ts
new file mode 100644
index 00000000..603c456c
--- /dev/null
+++ b/rstest/rslib-adapter/rstest.config.ts
@@ -0,0 +1,17 @@
+import { withRslibConfig } from '@rstest/adapter-rslib';
+import { defineConfig, type ExtendConfigFn } from '@rstest/core';
+
+export default defineConfig({
+ // Type assertion needed due to adapter package version mismatch
+ extends: withRslibConfig({
+ libId: 'esm',
+ modifyLibConfig: (config) => {
+ config.source ??= {};
+ config.source.alias = {
+ ...config.source.alias,
+ '@test-utils': './tests/test-utils.ts',
+ };
+ return config;
+ },
+ }) as ExtendConfigFn,
+});
diff --git a/rstest/rslib-adapter/src/index.ts b/rstest/rslib-adapter/src/index.ts
new file mode 100644
index 00000000..cf73f6d0
--- /dev/null
+++ b/rstest/rslib-adapter/src/index.ts
@@ -0,0 +1,27 @@
+// Declare compile-time constants
+declare const __VERSION__: string;
+declare const __DEV__: boolean;
+
+/**
+ * Get library version from define
+ */
+export function getVersion(): string {
+ return __VERSION__;
+}
+
+/**
+ * Check if in development mode
+ */
+export function isDev(): boolean {
+ return __DEV__;
+}
+
+/**
+ * Get library name from environment
+ */
+export function getLibName(): string {
+ return process.env.LIB_NAME || 'unknown';
+}
+
+// Re-export utilities
+export * from '@utils';
diff --git a/rstest/rslib-adapter/src/utils.ts b/rstest/rslib-adapter/src/utils.ts
new file mode 100644
index 00000000..ccce9b15
--- /dev/null
+++ b/rstest/rslib-adapter/src/utils.ts
@@ -0,0 +1,86 @@
+/**
+ * Utility functions for the library
+ */
+
+/**
+ * Adds two numbers together
+ */
+export function add(a: number, b: number): number {
+ return a + b;
+}
+
+/**
+ * Subtracts b from a
+ */
+export function subtract(a: number, b: number): number {
+ return a - b;
+}
+
+/**
+ * Multiplies two numbers
+ */
+export function multiply(a: number, b: number): number {
+ return a * b;
+}
+
+/**
+ * Divides a by b
+ * @throws Error if b is zero
+ */
+export function divide(a: number, b: number): number {
+ if (b === 0) {
+ throw new Error('Division by zero');
+ }
+ return a / b;
+}
+
+/**
+ * Clamps a value between min and max
+ */
+export function clamp(value: number, min: number, max: number): number {
+ return Math.min(Math.max(value, min), max);
+}
+
+/**
+ * Deep clones an object
+ */
+export function deepClone(obj: T): T {
+ return JSON.parse(JSON.stringify(obj));
+}
+
+/**
+ * Debounces a function
+ */
+export function debounce unknown>(
+ fn: T,
+ delay: number,
+): (...args: Parameters) => void {
+ let timeoutId: ReturnType | null = null;
+
+ return (...args: Parameters) => {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ timeoutId = setTimeout(() => {
+ fn(...args);
+ }, delay);
+ };
+}
+
+/**
+ * Throttles a function
+ */
+export function throttle unknown>(
+ fn: T,
+ delay: number,
+): (...args: Parameters) => void {
+ let lastCall = 0;
+
+ return (...args: Parameters) => {
+ const now = Date.now();
+ if (now - lastCall >= delay) {
+ lastCall = now;
+ fn(...args);
+ }
+ };
+}
diff --git a/rstest/rslib-adapter/tests/index.test.ts b/rstest/rslib-adapter/tests/index.test.ts
new file mode 100644
index 00000000..eab9a867
--- /dev/null
+++ b/rstest/rslib-adapter/tests/index.test.ts
@@ -0,0 +1,43 @@
+import { describe, expect, it } from '@rstest/core';
+import { getLibName, getVersion, isDev } from '@/index';
+
+describe('Define - Compile-time Constants', () => {
+ /**
+ * Tests that the `define` configuration in rslib.config.ts is inherited
+ * by rstest via withRslibConfig adapter
+ */
+
+ it('should get version from __VERSION__ define', () => {
+ const version = getVersion();
+ expect(version).toBe('1.0.0');
+ });
+
+ it('should get dev mode from __DEV__ define', () => {
+ const dev = isDev();
+ expect(dev).toBe(true);
+ });
+
+ it('should get lib name from process.env.LIB_NAME define', () => {
+ const libName = getLibName();
+ expect(libName).toBe('rstest-rslib-adapter');
+ });
+});
+
+describe('Alias - Path Resolution', () => {
+ /**
+ * Tests that the `alias` configuration in rslib.config.ts is inherited
+ * by rstest via withRslibConfig adapter
+ */
+
+ it('should resolve @ alias to src directory', async () => {
+ // This import uses the @ alias configured in rslib.config.ts
+ const { add } = await import('@/index');
+ expect(add(1, 2)).toBe(3);
+ });
+
+ it('should resolve @utils alias', async () => {
+ // This import uses the @utils alias
+ const { multiply } = await import('@utils');
+ expect(multiply(3, 4)).toBe(12);
+ });
+});
diff --git a/rstest/rslib-adapter/tests/modify-config.test.ts b/rstest/rslib-adapter/tests/modify-config.test.ts
new file mode 100644
index 00000000..8756f2b6
--- /dev/null
+++ b/rstest/rslib-adapter/tests/modify-config.test.ts
@@ -0,0 +1,34 @@
+import { describe, expect, it } from '@rstest/core';
+import { createTestContext, waitFor } from '@test-utils';
+
+describe('modifyLibConfig - Custom Test Aliases', () => {
+ /**
+ * Tests that the modifyLibConfig option in withRslibConfig
+ * allows adding custom aliases for testing
+ */
+
+ it('should resolve @test-utils alias added via modifyLibConfig', () => {
+ // The @test-utils alias is added in rstest.config.ts via modifyLibConfig
+ const ctx = createTestContext({ value: 0 });
+
+ expect(ctx.get()).toEqual({ value: 0 });
+ });
+
+ it('should use test utilities correctly', () => {
+ const ctx = createTestContext({ name: 'test', count: 0 });
+
+ ctx.set({ count: 5 });
+ expect(ctx.get()).toEqual({ name: 'test', count: 5 });
+
+ ctx.reset();
+ expect(ctx.get()).toEqual({ name: 'test', count: 0 });
+ });
+
+ it('should support async test utilities', async () => {
+ const start = Date.now();
+ await waitFor(50);
+ const elapsed = Date.now() - start;
+
+ expect(elapsed).toBeGreaterThanOrEqual(45);
+ });
+});
diff --git a/rstest/rslib-adapter/tests/test-utils.ts b/rstest/rslib-adapter/tests/test-utils.ts
new file mode 100644
index 00000000..29c7278c
--- /dev/null
+++ b/rstest/rslib-adapter/tests/test-utils.ts
@@ -0,0 +1,21 @@
+/**
+ * Test utilities for rslib-adapter example
+ */
+
+export function createTestContext(initial: T) {
+ let context = { ...initial };
+
+ return {
+ get: () => context,
+ set: (partial: Partial) => {
+ context = { ...context, ...partial };
+ },
+ reset: () => {
+ context = { ...initial };
+ },
+ };
+}
+
+export function waitFor(ms: number): Promise {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+}
diff --git a/rstest/rslib-adapter/tests/utils.test.ts b/rstest/rslib-adapter/tests/utils.test.ts
new file mode 100644
index 00000000..961f3477
--- /dev/null
+++ b/rstest/rslib-adapter/tests/utils.test.ts
@@ -0,0 +1,115 @@
+import { describe, expect, it } from '@rstest/core';
+import { add, clamp, deepClone, divide, multiply, subtract } from '@utils';
+
+describe('Utils - Basic Math Operations', () => {
+ describe('add', () => {
+ it('should add two positive numbers', () => {
+ expect(add(2, 3)).toBe(5);
+ });
+
+ it('should add negative numbers', () => {
+ expect(add(-2, -3)).toBe(-5);
+ });
+
+ it('should add mixed numbers', () => {
+ expect(add(-2, 5)).toBe(3);
+ });
+
+ it('should handle zero', () => {
+ expect(add(0, 5)).toBe(5);
+ expect(add(5, 0)).toBe(5);
+ });
+
+ it('should handle decimals', () => {
+ expect(add(0.1, 0.2)).toBeCloseTo(0.3);
+ });
+ });
+
+ describe('subtract', () => {
+ it('should subtract two numbers', () => {
+ expect(subtract(5, 3)).toBe(2);
+ });
+
+ it('should handle negative result', () => {
+ expect(subtract(3, 5)).toBe(-2);
+ });
+ });
+
+ describe('multiply', () => {
+ it('should multiply two numbers', () => {
+ expect(multiply(3, 4)).toBe(12);
+ });
+
+ it('should handle zero', () => {
+ expect(multiply(5, 0)).toBe(0);
+ });
+
+ it('should handle negative numbers', () => {
+ expect(multiply(-3, 4)).toBe(-12);
+ expect(multiply(-3, -4)).toBe(12);
+ });
+ });
+
+ describe('divide', () => {
+ it('should divide two numbers', () => {
+ expect(divide(10, 2)).toBe(5);
+ });
+
+ it('should handle decimal result', () => {
+ expect(divide(5, 2)).toBe(2.5);
+ });
+
+ it('should throw on division by zero', () => {
+ expect(() => divide(10, 0)).toThrow('Division by zero');
+ });
+ });
+});
+
+describe('Utils - Helper Functions', () => {
+ describe('clamp', () => {
+ it('should return value when within range', () => {
+ expect(clamp(5, 0, 10)).toBe(5);
+ });
+
+ it('should clamp to min', () => {
+ expect(clamp(-5, 0, 10)).toBe(0);
+ });
+
+ it('should clamp to max', () => {
+ expect(clamp(15, 0, 10)).toBe(10);
+ });
+
+ it('should handle edge cases', () => {
+ expect(clamp(0, 0, 10)).toBe(0);
+ expect(clamp(10, 0, 10)).toBe(10);
+ });
+ });
+
+ describe('deepClone', () => {
+ it('should clone simple objects', () => {
+ const obj = { a: 1, b: 2 };
+ const cloned = deepClone(obj);
+
+ expect(cloned).toEqual(obj);
+ expect(cloned).not.toBe(obj);
+ });
+
+ it('should clone nested objects', () => {
+ const obj = { a: { b: { c: 1 } } };
+ const cloned = deepClone(obj);
+
+ expect(cloned).toEqual(obj);
+ expect(cloned.a).not.toBe(obj.a);
+ expect(cloned.a.b).not.toBe(obj.a.b);
+ });
+
+ it('should clone arrays', () => {
+ const arr = [1, 2, [3, 4]];
+ const cloned = deepClone(arr);
+
+ expect(cloned).toEqual(arr);
+ expect(cloned).not.toBe(arr);
+ expect(cloned[2]).not.toBe(arr[2]);
+ });
+ });
+});
diff --git a/rstest/rslib-adapter/tsconfig.json b/rstest/rslib-adapter/tsconfig.json
new file mode 100644
index 00000000..d774a831
--- /dev/null
+++ b/rstest/rslib-adapter/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "target": "ES2021",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "declaration": true,
+ "outDir": "dist",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"],
+ "@utils": ["./src/utils.ts"],
+ "@test-utils": ["./tests/test-utils.ts"]
+ },
+ "types": ["@rstest/core"]
+ },
+ "include": ["src", "tests", "rstest.config.ts", "rslib.config.ts"]
+}
diff --git a/rstest/snapshot/package.json b/rstest/snapshot/package.json
new file mode 100644
index 00000000..81ae689f
--- /dev/null
+++ b/rstest/snapshot/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "rstest-snapshot",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "test": "rstest run",
+ "test:update": "rstest run --update"
+ },
+ "devDependencies": {
+ "@rstest/core": "^0.7.9",
+ "typescript": "^5.8.3"
+ }
+}
diff --git a/rstest/snapshot/rstest.config.ts b/rstest/snapshot/rstest.config.ts
new file mode 100644
index 00000000..80b4dc1e
--- /dev/null
+++ b/rstest/snapshot/rstest.config.ts
@@ -0,0 +1,10 @@
+import path from 'node:path';
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({
+ resolveSnapshotPath: (testPath, snapshotExtension) => {
+ const testDir = path.dirname(testPath);
+ const testFileName = path.basename(testPath);
+ return path.join(testDir, '__snapshots__', testFileName + snapshotExtension);
+ },
+});
diff --git a/rstest/snapshot/src/config.ts b/rstest/snapshot/src/config.ts
new file mode 100644
index 00000000..b1ca2454
--- /dev/null
+++ b/rstest/snapshot/src/config.ts
@@ -0,0 +1,79 @@
+/**
+ * Config interface for snapshot testing
+ */
+export interface AppConfig {
+ app: {
+ name: string;
+ version: string;
+ debug: boolean;
+ };
+ database: {
+ host: string;
+ port: number;
+ name: string;
+ pool: {
+ min: number;
+ max: number;
+ };
+ };
+ features: string[];
+ logging: {
+ level: 'debug' | 'info' | 'warn' | 'error';
+ format: 'json' | 'text';
+ };
+}
+
+/**
+ * Create default config
+ */
+export function createConfig(overrides: Partial = {}): AppConfig {
+ return {
+ app: {
+ name: 'MyApp',
+ version: '1.0.0',
+ debug: false,
+ ...overrides.app,
+ },
+ database: {
+ host: 'localhost',
+ port: 5432,
+ name: 'myapp_db',
+ pool: {
+ min: 2,
+ max: 10,
+ ...overrides.database?.pool,
+ },
+ ...overrides.database,
+ },
+ features: overrides.features ?? ['auth', 'api', 'websocket'],
+ logging: {
+ level: 'info',
+ format: 'json',
+ ...overrides.logging,
+ },
+ };
+}
+
+/**
+ * Validate config
+ */
+export function validateConfig(config: AppConfig): { valid: boolean; errors: string[] } {
+ const errors: string[] = [];
+
+ if (!config.app.name) {
+ errors.push('app.name is required');
+ }
+
+ if (config.database.port < 1 || config.database.port > 65535) {
+ errors.push('database.port must be between 1 and 65535');
+ }
+
+ if (config.database.pool.min > config.database.pool.max) {
+ errors.push('database.pool.min must be <= database.pool.max');
+ }
+
+ return {
+ valid: errors.length === 0,
+ errors,
+ };
+}
diff --git a/rstest/snapshot/src/html.ts b/rstest/snapshot/src/html.ts
new file mode 100644
index 00000000..ec9c455f
--- /dev/null
+++ b/rstest/snapshot/src/html.ts
@@ -0,0 +1,62 @@
+/**
+ * HTML template generator for snapshot testing
+ */
+
+export interface ComponentProps {
+ id?: string;
+ className?: string;
+ children?: string;
+ attributes?: Record;
+}
+
+/**
+ * Generate a div element HTML
+ */
+export function div(props: ComponentProps = {}): string {
+ const { id, className, children = '', attributes = {} } = props;
+
+ const attrs = [
+ id ? `id="${id}"` : '',
+ className ? `class="${className}"` : '',
+ ...Object.entries(attributes).map(([key, value]) => `${key}="${value}"`),
+ ]
+ .filter(Boolean)
+ .join(' ');
+
+ return `${children}
`;
+}
+
+/**
+ * Generate a button element HTML
+ */
+export function button(props: ComponentProps & { type?: string; disabled?: boolean } = {}): string {
+ const {
+ id,
+ className,
+ children = '',
+ type = 'button',
+ disabled = false,
+ attributes = {},
+ } = props;
+
+ const attrs = [
+ `type="${type}"`,
+ id ? `id="${id}"` : '',
+ className ? `class="${className}"` : '',
+ disabled ? 'disabled' : '',
+ ...Object.entries(attributes).map(([key, value]) => `${key}="${value}"`),
+ ]
+ .filter(Boolean)
+ .join(' ');
+
+ return `${children} `;
+}
+
+/**
+ * Generate a list HTML
+ */
+export function list(items: string[], ordered = false): string {
+ const tag = ordered ? 'ol' : 'ul';
+ const listItems = items.map((item) => ` ${item} `).join('\n');
+ return `<${tag}>\n${listItems}\n${tag}>`;
+}
diff --git a/rstest/snapshot/src/index.ts b/rstest/snapshot/src/index.ts
new file mode 100644
index 00000000..393aa880
--- /dev/null
+++ b/rstest/snapshot/src/index.ts
@@ -0,0 +1,3 @@
+export * from './config';
+export * from './html';
+export * from './user';
diff --git a/rstest/snapshot/src/user.ts b/rstest/snapshot/src/user.ts
new file mode 100644
index 00000000..4b05cacc
--- /dev/null
+++ b/rstest/snapshot/src/user.ts
@@ -0,0 +1,45 @@
+/**
+ * User interface for snapshot testing
+ */
+export interface User {
+ id: number;
+ name: string;
+ email: string;
+ role: 'admin' | 'user' | 'guest';
+ createdAt: Date;
+ metadata?: Record;
+}
+
+/**
+ * Create a user object
+ */
+export function createUser(overrides: Partial = {}): User {
+ return {
+ id: 1,
+ name: 'John Doe',
+ email: 'john@example.com',
+ role: 'user',
+ createdAt: new Date('2024-01-01T00:00:00.000Z'),
+ ...overrides,
+ };
+}
+
+/**
+ * Format user for display
+ */
+export function formatUser(user: User): string {
+ return `${user.name} <${user.email}> (${user.role})`;
+}
+
+/**
+ * Generate user list
+ */
+export function generateUsers(count: number): User[] {
+ return Array.from({ length: count }, (_, i) =>
+ createUser({
+ id: i + 1,
+ name: `User ${i + 1}`,
+ email: `user${i + 1}@example.com`,
+ }),
+ );
+}
diff --git a/rstest/snapshot/tests/__snapshots__/basic.test.ts.snap b/rstest/snapshot/tests/__snapshots__/basic.test.ts.snap
new file mode 100644
index 00000000..ec3248f4
--- /dev/null
+++ b/rstest/snapshot/tests/__snapshots__/basic.test.ts.snap
@@ -0,0 +1,119 @@
+// Rstest Snapshot v1
+
+exports[`toMatchSnapshot - Basic Usage > should match array snapshot 1`] = `
+[
+ {
+ "createdAt": 2024-01-01T00:00:00.000Z,
+ "email": "user1@example.com",
+ "id": 1,
+ "name": "User 1",
+ "role": "user",
+ },
+ {
+ "createdAt": 2024-01-01T00:00:00.000Z,
+ "email": "user2@example.com",
+ "id": 2,
+ "name": "User 2",
+ "role": "user",
+ },
+ {
+ "createdAt": 2024-01-01T00:00:00.000Z,
+ "email": "user3@example.com",
+ "id": 3,
+ "name": "User 3",
+ "role": "user",
+ },
+]
+`;
+
+exports[`toMatchSnapshot - Basic Usage > should match formatted user snapshot 1`] = `"Jane Doe (user)"`;
+
+exports[`toMatchSnapshot - Basic Usage > should match user object snapshot 1`] = `
+{
+ "createdAt": 2024-01-01T00:00:00.000Z,
+ "email": "john@example.com",
+ "id": 1,
+ "name": "John Doe",
+ "role": "user",
+}
+`;
+
+exports[`toMatchSnapshot - Basic Usage > should match with custom snapshot name > admin user 1`] = `
+{
+ "createdAt": 2024-01-01T00:00:00.000Z,
+ "email": "john@example.com",
+ "id": 1,
+ "name": "John Doe",
+ "role": "admin",
+}
+`;
+
+exports[`toMatchSnapshot - Complex Objects > should match nested object snapshot 1`] = `
+{
+ "meta": {
+ "generatedAt": "2024-01-01T00:00:00.000Z",
+ "version": "1.0.0",
+ },
+ "pagination": {
+ "page": 1,
+ "perPage": 10,
+ "total": 100,
+ },
+ "users": [
+ {
+ "createdAt": 2024-01-01T00:00:00.000Z,
+ "email": "user1@example.com",
+ "id": 1,
+ "name": "User 1",
+ "role": "user",
+ },
+ {
+ "createdAt": 2024-01-01T00:00:00.000Z,
+ "email": "user2@example.com",
+ "id": 2,
+ "name": "User 2",
+ "role": "user",
+ },
+ ],
+}
+`;
+
+exports[`toMatchSnapshot - Complex Objects > should match user with metadata snapshot 1`] = `
+{
+ "createdAt": 2024-01-01T00:00:00.000Z,
+ "email": "john@example.com",
+ "id": 1,
+ "metadata": {
+ "lastLogin": "2024-01-15T10:30:00.000Z",
+ "preferences": {
+ "language": "en",
+ "theme": "dark",
+ },
+ },
+ "name": "John Doe",
+ "role": "user",
+}
+`;
+
+exports[`toMatchSnapshot - Property Matchers > should match array with property matchers 1`] = `
+[
+ {
+ "id": Any,
+ "name": "User 1",
+ },
+ {
+ "id": Any,
+ "name": "User 2",
+ },
+]
+`;
+
+exports[`toMatchSnapshot - Property Matchers > should match with property matchers for dynamic values 1`] = `
+{
+ "createdAt": Any,
+ "email": "test@example.com",
+ "id": Any,
+ "name": "Test User",
+ "role": "user",
+}
+`;
diff --git a/rstest/snapshot/tests/basic.test.ts b/rstest/snapshot/tests/basic.test.ts
new file mode 100644
index 00000000..a6e81d40
--- /dev/null
+++ b/rstest/snapshot/tests/basic.test.ts
@@ -0,0 +1,102 @@
+import { describe, expect, it } from '@rstest/core';
+import { createUser, formatUser, generateUsers, type User } from '../src/user';
+
+describe('toMatchSnapshot - Basic Usage', () => {
+ /**
+ * toMatchSnapshot() creates a snapshot file on first run,
+ * then compares against it on subsequent runs
+ */
+
+ it('should match user object snapshot', () => {
+ const user = createUser();
+
+ // Snapshot will be saved to __snapshots__/basic.test.ts.snap
+ expect(user).toMatchSnapshot();
+ });
+
+ it('should match formatted user snapshot', () => {
+ const user = createUser({ name: 'Jane Doe', email: 'jane@example.com' });
+ const formatted = formatUser(user);
+
+ expect(formatted).toMatchSnapshot();
+ });
+
+ it('should match array snapshot', () => {
+ const users = generateUsers(3);
+
+ expect(users).toMatchSnapshot();
+ });
+
+ it('should match with custom snapshot name', () => {
+ const user = createUser({ role: 'admin' });
+
+ // Use a custom name for the snapshot
+ expect(user).toMatchSnapshot('admin user');
+ });
+});
+
+describe('toMatchSnapshot - Complex Objects', () => {
+ it('should match nested object snapshot', () => {
+ const data = {
+ users: generateUsers(2),
+ pagination: {
+ page: 1,
+ perPage: 10,
+ total: 100,
+ },
+ meta: {
+ generatedAt: '2024-01-01T00:00:00.000Z',
+ version: '1.0.0',
+ },
+ };
+
+ expect(data).toMatchSnapshot();
+ });
+
+ it('should match user with metadata snapshot', () => {
+ const user = createUser({
+ metadata: {
+ preferences: {
+ theme: 'dark',
+ language: 'en',
+ },
+ lastLogin: '2024-01-15T10:30:00.000Z',
+ },
+ });
+
+ expect(user).toMatchSnapshot();
+ });
+});
+
+describe('toMatchSnapshot - Property Matchers', () => {
+ /**
+ * Use property matchers for dynamic values like dates or IDs
+ */
+
+ it('should match with property matchers for dynamic values', () => {
+ const user: User = {
+ id: Math.random() * 1000, // Dynamic ID
+ name: 'Test User',
+ email: 'test@example.com',
+ role: 'user',
+ createdAt: new Date(), // Dynamic date
+ };
+
+ expect(user).toMatchSnapshot({
+ id: expect.any(Number),
+ createdAt: expect.any(Date),
+ });
+ });
+
+ it('should match array with property matchers', () => {
+ const users = [
+ { id: Math.random(), name: 'User 1' },
+ { id: Math.random(), name: 'User 2' },
+ ];
+
+ expect(users).toMatchSnapshot([
+ { id: expect.any(Number), name: 'User 1' },
+ { id: expect.any(Number), name: 'User 2' },
+ ]);
+ });
+});
diff --git a/rstest/snapshot/tests/file.test.ts b/rstest/snapshot/tests/file.test.ts
new file mode 100644
index 00000000..ddc05987
--- /dev/null
+++ b/rstest/snapshot/tests/file.test.ts
@@ -0,0 +1,109 @@
+import { describe, expect, it } from '@rstest/core';
+import { createConfig, validateConfig } from '../src/config';
+
+describe('toMatchFileSnapshot - Basic Usage', () => {
+ /**
+ * toMatchFileSnapshot() saves the snapshot to a separate file
+ * that you specify. Useful for large outputs or when you want
+ * to organize snapshots in a specific way.
+ */
+
+ it('should match config file snapshot', async () => {
+ const config = createConfig();
+
+ // Save to a specific file
+ await expect(JSON.stringify(config, null, 2)).toMatchFileSnapshot(
+ './tests/__snapshots__/config.json',
+ );
+ });
+
+ it('should match config with overrides file snapshot', async () => {
+ const config = createConfig({
+ app: { name: 'CustomApp', version: '2.0.0', debug: true },
+ features: ['custom-feature'],
+ });
+
+ await expect(JSON.stringify(config, null, 2)).toMatchFileSnapshot(
+ './tests/__snapshots__/custom-config.json',
+ );
+ });
+});
+
+describe('toMatchFileSnapshot - Different Formats', () => {
+ it('should match YAML-like format file snapshot', async () => {
+ const config = createConfig();
+
+ // Format as YAML-like string
+ const yaml = `app:
+ name: ${config.app.name}
+ version: ${config.app.version}
+ debug: ${config.app.debug}
+database:
+ host: ${config.database.host}
+ port: ${config.database.port}
+ name: ${config.database.name}
+ pool:
+ min: ${config.database.pool.min}
+ max: ${config.database.pool.max}
+features:
+${config.features.map((f) => ` - ${f}`).join('\n')}
+logging:
+ level: ${config.logging.level}
+ format: ${config.logging.format}`;
+
+ await expect(yaml).toMatchFileSnapshot('./tests/__snapshots__/config.yaml');
+ });
+
+ it('should match validation result file snapshot', async () => {
+ const config = createConfig({
+ database: {
+ host: 'localhost',
+ port: 70000, // Invalid port
+ name: 'test',
+ pool: { min: 10, max: 5 }, // Invalid: min > max
+ },
+ });
+
+ const result = validateConfig(config);
+
+ await expect(JSON.stringify(result, null, 2)).toMatchFileSnapshot(
+ './tests/__snapshots__/validation-errors.json',
+ );
+ });
+});
+
+describe('toMatchFileSnapshot - HTML Output', () => {
+ it('should match HTML page file snapshot', async () => {
+ const html = `
+
+
+
+
+ Test Page
+
+
+
+
+
+ Content
+ This is the main content area.
+
+
+
+
+`;
+
+ await expect(html).toMatchFileSnapshot('./tests/__snapshots__/page.html');
+ });
+});
diff --git a/rstest/snapshot/tests/inline.test.ts b/rstest/snapshot/tests/inline.test.ts
new file mode 100644
index 00000000..2d3a757d
--- /dev/null
+++ b/rstest/snapshot/tests/inline.test.ts
@@ -0,0 +1,93 @@
+import { describe, expect, it } from '@rstest/core';
+import { button, div, list } from '../src/html';
+
+describe('toMatchInlineSnapshot - Basic Usage', () => {
+ /**
+ * toMatchInlineSnapshot() stores the snapshot directly in the test file.
+ * On first run, it will update the test file with the snapshot.
+ * Run with --update flag to update existing inline snapshots.
+ */
+
+ it('should match div inline snapshot', () => {
+ const html = div({ className: 'container', children: 'Hello World' });
+
+ expect(html).toMatchInlineSnapshot(`"Hello World
"`);
+ });
+
+ it('should match button inline snapshot', () => {
+ const html = button({
+ id: 'submit-btn',
+ className: 'btn btn-primary',
+ children: 'Submit',
+ });
+
+ expect(html).toMatchInlineSnapshot(
+ `"Submit "`,
+ );
+ });
+
+ it('should match disabled button inline snapshot', () => {
+ const html = button({
+ className: 'btn',
+ children: 'Disabled',
+ disabled: true,
+ });
+
+ expect(html).toMatchInlineSnapshot(
+ `"Disabled "`,
+ );
+ });
+});
+
+describe('toMatchInlineSnapshot - Lists', () => {
+ it('should match unordered list inline snapshot', () => {
+ const html = list(['Item 1', 'Item 2', 'Item 3']);
+
+ expect(html).toMatchInlineSnapshot(`
+"
+ Item 1
+ Item 2
+ Item 3
+ "
+`);
+ });
+
+ it('should match ordered list inline snapshot', () => {
+ const html = list(['First', 'Second', 'Third'], true);
+
+ expect(html).toMatchInlineSnapshot(`
+"
+ First
+ Second
+ Third
+ "
+`);
+ });
+});
+
+describe('toMatchInlineSnapshot - Objects', () => {
+ it('should match simple object inline snapshot', () => {
+ const obj = { name: 'test', value: 42 };
+
+ expect(obj).toMatchInlineSnapshot(`
+{
+ "name": "test",
+ "value": 42,
+}
+`);
+ });
+
+ it('should match array inline snapshot', () => {
+ const arr = [1, 2, 3, 4, 5];
+
+ expect(arr).toMatchInlineSnapshot(`
+[
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+]
+`);
+ });
+});
diff --git a/rstest/snapshot/tests/serializer.test.ts b/rstest/snapshot/tests/serializer.test.ts
new file mode 100644
index 00000000..b5d28fde
--- /dev/null
+++ b/rstest/snapshot/tests/serializer.test.ts
@@ -0,0 +1,128 @@
+import { afterAll, beforeAll, describe, expect, it } from '@rstest/core';
+import { createUser, type User } from '../src/user';
+
+describe('Custom Snapshot Serializer', () => {
+ /**
+ * expect.addSnapshotSerializer() allows you to customize
+ * how specific types are serialized in snapshots
+ */
+
+ beforeAll(() => {
+ // Add custom serializer for User objects
+ expect.addSnapshotSerializer({
+ // Check if this serializer should handle the value
+ test: (val): val is User => {
+ return (
+ val !== null &&
+ typeof val === 'object' &&
+ 'id' in val &&
+ 'name' in val &&
+ 'email' in val &&
+ 'role' in val
+ );
+ },
+ // Serialize the value
+ serialize: (val: User, config, indentation, _depth, _refs, _printer) => {
+ const indent = indentation + config.indent;
+ return `User {\n${indent}id: ${val.id}\n${indent}name: "${val.name}"\n${indent}email: "${val.email}"\n${indent}role: "${val.role}"\n${indentation}}`;
+ },
+ });
+ });
+
+ afterAll(() => {
+ // Note: In a real scenario, you might want to reset serializers
+ // rstest provides ways to manage this
+ });
+
+ it('should use custom serializer for User', () => {
+ const user = createUser({
+ id: 1,
+ name: 'Alice',
+ email: 'alice@example.com',
+ role: 'admin',
+ });
+
+ expect(user).toMatchInlineSnapshot(`
+User {
+ id: 1
+ name: "Alice"
+ email: "alice@example.com"
+ role: "admin"
+}
+`);
+ });
+
+ it('should use custom serializer for another User', () => {
+ const user = createUser({
+ id: 2,
+ name: 'Bob',
+ email: 'bob@example.com',
+ role: 'guest',
+ });
+
+ expect(user).toMatchInlineSnapshot(`
+User {
+ id: 2
+ name: "Bob"
+ email: "bob@example.com"
+ role: "guest"
+}
+`);
+ });
+});
+
+describe('Custom Serializer - Date Formatting', () => {
+ beforeAll(() => {
+ // Add custom serializer for Date objects
+ expect.addSnapshotSerializer({
+ test: (val): val is Date => val instanceof Date,
+ serialize: (val: Date) => {
+ return `Date<${val.toISOString().split('T')[0]}>`;
+ },
+ });
+ });
+
+ it('should format dates nicely', () => {
+ const date = new Date('2024-06-15T10:30:00.000Z');
+
+ expect(date).toMatchInlineSnapshot(`Date<2024-06-15>`);
+ });
+
+ it('should format dates in objects', () => {
+ const event = {
+ name: 'Conference',
+ date: new Date('2024-12-01T09:00:00.000Z'),
+ };
+
+ expect(event).toMatchInlineSnapshot(`
+{
+ "date": Date<2024-12-01>,
+ "name": "Conference",
+}
+`);
+ });
+});
+
+describe('Custom Serializer - Error Objects', () => {
+ beforeAll(() => {
+ // Add custom serializer for Error objects
+ expect.addSnapshotSerializer({
+ test: (val): val is Error => val instanceof Error,
+ serialize: (val: Error) => {
+ return `[Error: ${val.message}]`;
+ },
+ });
+ });
+
+ it('should serialize errors cleanly', () => {
+ const error = new Error('Something went wrong');
+
+ expect(error).toMatchInlineSnapshot(`[Error: Something went wrong]`);
+ });
+
+ it('should serialize TypeError', () => {
+ const error = new TypeError('Invalid type provided');
+
+ expect(error).toMatchInlineSnapshot(`[Error: Invalid type provided]`);
+ });
+});
diff --git a/rstest/snapshot/tests/tests/__snapshots__/config.json b/rstest/snapshot/tests/tests/__snapshots__/config.json
new file mode 100644
index 00000000..635405dd
--- /dev/null
+++ b/rstest/snapshot/tests/tests/__snapshots__/config.json
@@ -0,0 +1,25 @@
+{
+ "app": {
+ "name": "MyApp",
+ "version": "1.0.0",
+ "debug": false
+ },
+ "database": {
+ "host": "localhost",
+ "port": 5432,
+ "name": "myapp_db",
+ "pool": {
+ "min": 2,
+ "max": 10
+ }
+ },
+ "features": [
+ "auth",
+ "api",
+ "websocket"
+ ],
+ "logging": {
+ "level": "info",
+ "format": "json"
+ }
+}
\ No newline at end of file
diff --git a/rstest/snapshot/tests/tests/__snapshots__/config.yaml b/rstest/snapshot/tests/tests/__snapshots__/config.yaml
new file mode 100644
index 00000000..37a01d2c
--- /dev/null
+++ b/rstest/snapshot/tests/tests/__snapshots__/config.yaml
@@ -0,0 +1,18 @@
+app:
+ name: MyApp
+ version: 1.0.0
+ debug: false
+database:
+ host: localhost
+ port: 5432
+ name: myapp_db
+ pool:
+ min: 2
+ max: 10
+features:
+ - auth
+ - api
+ - websocket
+logging:
+ level: info
+ format: json
\ No newline at end of file
diff --git a/rstest/snapshot/tests/tests/__snapshots__/custom-config.json b/rstest/snapshot/tests/tests/__snapshots__/custom-config.json
new file mode 100644
index 00000000..952f3081
--- /dev/null
+++ b/rstest/snapshot/tests/tests/__snapshots__/custom-config.json
@@ -0,0 +1,23 @@
+{
+ "app": {
+ "name": "CustomApp",
+ "version": "2.0.0",
+ "debug": true
+ },
+ "database": {
+ "host": "localhost",
+ "port": 5432,
+ "name": "myapp_db",
+ "pool": {
+ "min": 2,
+ "max": 10
+ }
+ },
+ "features": [
+ "custom-feature"
+ ],
+ "logging": {
+ "level": "info",
+ "format": "json"
+ }
+}
\ No newline at end of file
diff --git a/rstest/snapshot/tests/tests/__snapshots__/page.html b/rstest/snapshot/tests/tests/__snapshots__/page.html
new file mode 100644
index 00000000..8be5646f
--- /dev/null
+++ b/rstest/snapshot/tests/tests/__snapshots__/page.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+ Test Page
+
+
+
+
+
+ Content
+ This is the main content area.
+
+
+
+
+
\ No newline at end of file
diff --git a/rstest/snapshot/tests/tests/__snapshots__/validation-errors.json b/rstest/snapshot/tests/tests/__snapshots__/validation-errors.json
new file mode 100644
index 00000000..21d58965
--- /dev/null
+++ b/rstest/snapshot/tests/tests/__snapshots__/validation-errors.json
@@ -0,0 +1,7 @@
+{
+ "valid": false,
+ "errors": [
+ "database.port must be between 1 and 65535",
+ "database.pool.min must be <= database.pool.max"
+ ]
+}
\ No newline at end of file
diff --git a/rstest/snapshot/tsconfig.json b/rstest/snapshot/tsconfig.json
new file mode 100644
index 00000000..5d1c2508
--- /dev/null
+++ b/rstest/snapshot/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "declaration": true,
+ "types": ["@rstest/core"]
+ },
+ "include": ["src", "tests", "rstest.config.ts"]
+}