From 582ffe81540b713642e04b1a10878b974d0c7958 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Wed, 11 Mar 2026 01:29:31 +0800 Subject: [PATCH 01/29] Squashed commit of the following: commit ff2cb9220ef5a4dad7be55f0f3ebc2749598c8b5 Merge: b2d5322f f66e3196 Author: Yiming Li Date: Wed Aug 20 16:36:09 2025 +0800 Merge remote-tracking branch 'gh/main' into feat/rltl-rstest commit b2d5322fbfa0e534fb14ccb08db1797dff98bfbf Merge: 44fbd572 f0d483ca Author: Yiming Li Date: Wed Aug 20 16:24:41 2025 +0800 Merge remote-tracking branch 'gh/main' into feat/rltl-rstest commit 44fbd57283236327f09680a8fd67c9d8cb1b472e Author: Yiming Li Date: Thu Aug 14 11:18:57 2025 +0800 fix: rstest css module classname is diff from vitest commit 2825c3c100e1d7bdfcfb90eea3f1a77ca6389db5 Merge: 24cd0a2b fcec513a Author: Yiming Li Date: Thu Aug 14 10:20:05 2025 +0800 Merge remote-tracking branch 'gh/main' into feat/rltl-rstest commit 24cd0a2b94fa4c66be226106ae2e8f2d793a3277 Merge: 41f71f15 cb6d23c3 Author: Yiming Li Date: Wed Aug 13 19:32:36 2025 +0800 Merge remote-tracking branch 'gh/main' into feat/rltl-rstest commit 41f71f151cfb28170751703b865823a2bdb26f2d Author: Yiming Li Date: Mon Aug 11 14:47:06 2025 +0800 fix: update testing cases commit 1a18210717e62f0fb0d52171e3c747535d55372b Author: Yiming Li Date: Mon Aug 11 12:23:48 2025 +0800 chore: remove process.env['DEBUG'] commit 0c3830006d8abad6690e938188aa4531866ca8bf Merge: 1f51a709 05b00961 Author: Yiming Li Date: Mon Aug 11 12:18:52 2025 +0800 Merge remote-tracking branch 'gh/main' into feat/rltl-rstest commit 1f51a70966b7a596b2a8204a2e36a403629d1d17 Author: Yiming Li Date: Wed Aug 6 21:03:31 2025 +0800 fix: snapshot commit 76872a4c7f1acb601a6368ef3118e33fdf9b262a Merge: 095bc281 0eeae9de Author: Yiming Li Date: Wed Aug 6 21:00:19 2025 +0800 Merge remote-tracking branch 'gh/main' into feat/rltl-rstest commit 095bc2814afc9615162d6d623017d7505f9c63b8 Author: Yiming Li Date: Wed Aug 6 20:56:49 2025 +0800 feat: remove enableDefine and use lynx.config to define __ALOG__ commit 9d8bd7cee1c27a9aa5e06e37de2af2d7a51fb53e Author: Yiming Li Date: Wed Aug 6 20:53:33 2025 +0800 feat: use getRspeedyAPI and remove applyStubRspeedyAPI commit b898c19d95de39515b1b3f5707ba61ca924b0e70 Merge: e884c075 6baeb9f8 Author: Yiming Li Date: Wed Aug 6 15:47:53 2025 +0800 Merge remote-tracking branch 'gh/main' into feat/rltl-rstest commit e884c07506dad55290708d9d3b0c12e1a426340e Author: Yiming Li Date: Mon Aug 4 10:44:41 2025 +0800 fix: remove test.only commit b5567fa97d4009238b6d6a729fd04163a8318777 Merge: 34bd9213 8e6321a8 Author: Yiming Li Date: Mon Aug 4 10:45:30 2025 +0800 Merge branch 'main' into feat/rltl-rstest commit 34bd92135bf8811f7ceb11e75f1414632a30a50d Author: Yiming Li Date: Sat Aug 2 00:21:42 2025 +0800 feat: add test cases for callerName: rstest commit 37ccc688e4d6920ab6f93ab1f9dad9d8142bcd2c Author: Yiming Li Date: Fri Aug 1 23:51:31 2025 +0800 feat: add applyStubRspeedyAPI commit 37cd1c892f92a1a14e304918f26a6141cc4f8317 Merge: 8863f55b 22ca433e Author: Yiming Li Date: Fri Aug 1 22:45:43 2025 +0800 Merge remote-tracking branch 'gh/main' into feat/rltl-rstest commit 8863f55bf5dccb6ca180a6a1e4c6f2931dde208a Author: Yiming Li Date: Fri Aug 1 22:08:53 2025 +0800 fix: cr commit cf78e1a732c2fe66e9027641973d3e8e44d91d15 Author: Yiming Li Date: Wed Jul 30 17:51:42 2025 +0800 chore: changeset commit 0d4e0c96a038e19ec0e39106b84b9e8dd2b1689e Merge: bfdebd07 a4ee4fdd Author: Yiming Li Date: Wed Jul 30 16:49:08 2025 +0800 Merge remote-tracking branch 'gh/main' into feat/rltl-rstest commit bfdebd07ddafe4280c7a139c6bf13715bb96c9c7 Author: Yiming Li Date: Wed Jul 30 16:48:09 2025 +0800 feat: remove layer from testing rules commit 4591409c9b19f04e25a238babf5eed4651dc2c42 Author: Yiming Li Date: Wed Jul 30 15:35:44 2025 +0800 Update .changeset/red-lamps-arrive.md Co-authored-by: Zhiyuan Hong <28915578+hzy@users.noreply.github.com> Signed-off-by: Yiming Li commit fc2038040600b8081d77d83df90f19b78498cd29 Merge: b9c1ca26 343cedb0 Author: Yiming Li Date: Fri Jul 25 15:29:13 2025 +0800 Merge remote-tracking branch 'gh/main' into feat/rltl-rstest commit b9c1ca26cdcf18427de7cc06e411b451dfea834c Author: Yiming Li Date: Fri Jul 25 15:18:16 2025 +0800 fix: cr commit 2300227be32809774f2b5933849470cf42efd4a9 Author: Yiming Li Date: Thu Jul 24 20:20:50 2025 +0800 docs: update changeset commit 6d0d454801a05dbd26279b1047f4284a0984ad20 Merge: fe86acde 3788b611 Author: Yiming Li Date: Thu Jul 24 19:29:31 2025 +0800 Merge branch 'main' into feat/rltl-rstest commit fe86acdebba1789f5b24954c86b36a6973ade529 Author: Yiming Li Date: Thu Jul 24 17:30:30 2025 +0800 fix: cr commit a769bc575445e4351a8c0c1db956ac17ebc58b5b Author: Yiming Li Date: Thu Jul 24 17:29:10 2025 +0800 chore: pnpm dedupe commit 1163c32aa7615c5ad0b1eb50adc9955384b36b5b Author: Yiming Li Date: Thu Jul 24 17:24:09 2025 +0800 feat: using callerName and reuse lynx.config for rstest commit b391cb8dd3e742fecbb36f971c31d11ffd6a0a4b Author: Yiming Li Date: Thu Jul 24 16:07:41 2025 +0800 feat: keep the `createVitestConfig` API commit 000961d73805c4d77c983280150bed1c41b2c6f3 Author: Yiming Li Date: Thu Jul 24 14:58:17 2025 +0800 fix: use cjs for polyfill commit df4d0a32bc5939ec84b1b5f3897810a59c27c1c0 Author: Yiming Li Date: Thu Jul 24 14:51:00 2025 +0800 chore: use catalog for rstest version commit fea9d67fad5cbda7625554c89406fe6ec6769a0f Merge: 04cbe347 a6230e2a Author: Yiming Li Date: Thu Jul 24 14:45:14 2025 +0800 Merge remote-tracking branch 'gh/main' into feat/rltl-rstest commit 04cbe3475b5b57231c68200883f08ed3c781bb3e Merge: dae4616e 16a3ccb2 Author: Yiming Li Date: Fri Jul 18 23:48:43 2025 +0800 Merge branch 'main' into feat/rltl-rstest commit dae4616e777b56b9fb01a5acb532af336455b477 Author: Yiming Date: Fri Jul 18 23:47:07 2025 +0800 chore: update changeset commit 5b45cfb494f95ee68771cd2ce6303ca76c657ae7 Author: Yiming Date: Fri Jul 18 17:27:52 2025 +0800 feat: update snapshot commit a0a21b692f79ed63e6a95ca8cab103a265ec59fc Author: Yiming Li Date: Fri Jul 18 17:05:11 2025 +0800 fix coderabbitai cr commit 9a44a9abfecb5625a6cb63f238df7674202d3cf1 Author: Yiming Li Date: Fri Jul 18 16:51:32 2025 +0800 feat: add rstest template commit c51a15a5a541d64ad497572fc33925821dc4eca4 Author: Yiming Date: Fri Jul 18 16:43:18 2025 +0800 chore: add testTimeout for rstest commit d3dce4a4870acf8fd14f9796c1a4e055b62bea1c Author: Yiming Date: Fri Jul 18 16:39:50 2025 +0800 fix: rstest setup commit d8a0221b0ab12e949eeac9c4552a79f8e8d0d3fb Merge: dc747267 7eb5c36e Author: Yiming Date: Fri Jul 18 15:05:25 2025 +0800 Merge branch 'feat/rltl-rstest' of github.com:upupming/lynx-stack into feat/rltl-rstest commit dc747267d2fc56d686c89008e5669521ec9b5a6f Author: Yiming Date: Fri Jul 18 15:05:07 2025 +0800 fix: setup file order in vitest commit fe0fd93dcdd79752c26037bb012faff59c317a65 Author: Yiming Date: Fri Jul 18 14:19:42 2025 +0800 feat: pre-bundled @lynx-js/testing-environment should shared between vitest and rstest commit 7eb5c36e44c1aa3a8b4642481d7f3f418be0de66 Author: Yiming Li Date: Fri Jul 18 14:02:03 2025 +0800 fix: enableTestingLibrary passing to ReactWebpackPlugin commit f4a9651d1950f7933f5af653b507d082deafd2ea Author: Yiming Li Date: Fri Jul 18 13:54:19 2025 +0800 feat: add template to create-rspeedy commit 410e1c96b070a4456eded00f8564c4d23e80e571 Author: Yiming Li Date: Fri Jul 18 11:34:59 2025 +0800 fix: use `enableTestingLibrary` instead of process.env.NODE_ENV to avoid breaking current tests commit a0f22755257d9d91ad8ec6f09af2388761dbc8b7 Author: Yiming Li Date: Thu Jul 17 22:17:00 2025 +0800 fix: api commit 2442e36e63f58960db85470c2e81495420d71fa0 Author: Yiming Li Date: Thu Jul 17 22:08:32 2025 +0800 fix: unused Console type commit 854e2f16aadbdaa2d9c9e672cb0afbf20fc8ca1c Author: Yiming Li Date: Thu Jul 17 21:46:15 2025 +0800 fix: global.console in rstest cannot be modify commit 852025fb7a8e9df76d39db1e074e90a8af53e981 Merge: af8a651e 6af75c14 Author: Yiming Li Date: Thu Jul 17 21:45:02 2025 +0800 Merge remote-tracking branch 'gh/main' into feat/rltl-rstest commit af8a651e8ca25ffa1fdaa78693b6b6da6ed6a43f Author: Yiming Li Date: Thu Jul 17 21:05:01 2025 +0800 feat: support rstest in testing library --- .changeset/easy-waves-pump.md | 6 + .changeset/ninety-pants-tease.md | 5 + .changeset/red-lamps-arrive.md | 29 ++ .github/workflows/test.yml | 24 + package.json | 1 + packages/react/package.json | 3 +- packages/react/testing-library/.npmignore | 3 +- packages/react/testing-library/package.json | 3 + .../react/testing-library/rslib.config.ts | 15 +- .../react/testing-library/rstest.config.ts | 37 ++ .../src/__tests__/act.test.jsx | 8 +- .../src/__tests__/alog.test.jsx | 432 ++++++++++++++++-- .../src/__tests__/auto-cleanup-skip.test.jsx | 2 + .../src/__tests__/cleanup.test.jsx | 2 +- .../src/__tests__/css/index.test.jsx | 4 +- .../src/__tests__/end-to-end.test.jsx | 10 +- .../src/__tests__/lazy-bundle/index.test.jsx | 36 +- .../src/__tests__/list.test.jsx | 120 ++++- .../src/__tests__/lynx.test.jsx | 2 +- .../src/__tests__/rerender.test.jsx | 2 +- .../src/__tests__/worklet.test.jsx | 10 +- .../react/testing-library/src/env/index.ts | 1 + .../react/testing-library/src/env/rstest.ts | 8 + .../react/testing-library/src/env/vitest.ts | 25 +- .../testing-library/src/plugins/index.ts | 2 + .../{vitest.config.js => plugins/vitest.ts} | 222 +++++---- .../src/setupFiles/common/bootstrap.js | 6 + .../common/runtime-setup.js} | 57 ++- .../src/setupFiles/inner/rstest.js | 21 + .../src/setupFiles/inner/vitest.js | 17 + .../testing-library/src/setupFiles/rstest.js | 4 + .../testing-library/src/setupFiles/vitest.js | 3 + .../testing-library/src/vitest.config.ts | 11 + .../testing-library/types/vitest-config.d.ts | 20 - .../react/testing-library/vitest-polyfill.cjs | 6 + .../react/testing-library/vitest.config.ts | 2 +- packages/rspeedy/create-rspeedy/package.json | 3 +- packages/rspeedy/create-rspeedy/src/index.ts | 29 +- .../lynx.config.js | 19 + .../package.json | 25 + .../rstest.config.js | 11 + .../template-react-rstest-rltl-js/src/App.jsx | 53 +++ .../src/__tests__/index.test.jsx | 102 +++++ .../src/index.jsx | 9 + .../lynx.config.ts | 18 + .../package.json | 29 ++ .../rstest.config.ts | 11 + .../template-react-rstest-rltl-ts/src/App.tsx | 55 +++ .../src/__tests__/index.test.tsx | 102 +++++ .../src/index.tsx | 9 + .../src/rspeedy-env.d.ts | 1 + .../tsconfig.json | 17 + .../vitest.config.js | 2 +- .../vitest.config.ts | 2 +- packages/rspeedy/plugin-react/package.json | 3 +- packages/rspeedy/plugin-react/src/loaders.ts | 84 ++-- .../plugin-react/src/pluginReactLynx.ts | 17 +- .../rspeedy/plugin-react/src/rspeedy-api.ts | 14 + packages/rspeedy/plugin-react/src/rstest.ts | 15 + .../rspeedy/plugin-react/test/config.test.ts | 69 +++ .../examples/basic/package.json | 1 + .../examples/basic/rstest.config.ts | 11 + .../basic/src/__tests__/index.test.tsx | 10 +- .../examples/basic/src/rspeedy-env.d.ts | 2 + .../examples/basic/vitest.config.ts | 2 +- .../testing-environment/src/index.ts | 25 +- .../use-sync-external-store/vitest.config.ts | 2 +- .../etc/react-webpack-plugin.api.md | 2 +- .../src/ReactWebpackPlugin.ts | 3 +- .../src/loaders/options.ts | 4 +- .../src/loaders/testing.ts | 119 +++++ pnpm-lock.yaml | 95 +++- pnpm-workspace.yaml | 4 + 73 files changed, 1831 insertions(+), 307 deletions(-) create mode 100644 .changeset/easy-waves-pump.md create mode 100644 .changeset/ninety-pants-tease.md create mode 100644 .changeset/red-lamps-arrive.md create mode 100644 packages/react/testing-library/rstest.config.ts create mode 100644 packages/react/testing-library/src/env/index.ts create mode 100644 packages/react/testing-library/src/env/rstest.ts create mode 100644 packages/react/testing-library/src/plugins/index.ts rename packages/react/testing-library/src/{vitest.config.js => plugins/vitest.ts} (63%) create mode 100644 packages/react/testing-library/src/setupFiles/common/bootstrap.js rename packages/react/testing-library/src/{vitest-global-setup.js => setupFiles/common/runtime-setup.js} (68%) create mode 100644 packages/react/testing-library/src/setupFiles/inner/rstest.js create mode 100644 packages/react/testing-library/src/setupFiles/inner/vitest.js create mode 100644 packages/react/testing-library/src/setupFiles/rstest.js create mode 100644 packages/react/testing-library/src/setupFiles/vitest.js create mode 100644 packages/react/testing-library/src/vitest.config.ts delete mode 100644 packages/react/testing-library/types/vitest-config.d.ts create mode 100644 packages/react/testing-library/vitest-polyfill.cjs create mode 100644 packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/lynx.config.js create mode 100644 packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/package.json create mode 100644 packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/rstest.config.js create mode 100644 packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/App.jsx create mode 100644 packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/__tests__/index.test.jsx create mode 100644 packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/index.jsx create mode 100644 packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/lynx.config.ts create mode 100644 packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/package.json create mode 100644 packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/rstest.config.ts create mode 100644 packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/App.tsx create mode 100644 packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/__tests__/index.test.tsx create mode 100644 packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/index.tsx create mode 100644 packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/rspeedy-env.d.ts create mode 100644 packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/tsconfig.json create mode 100644 packages/rspeedy/plugin-react/src/rspeedy-api.ts create mode 100644 packages/rspeedy/plugin-react/src/rstest.ts create mode 100644 packages/testing-library/examples/basic/rstest.config.ts create mode 100644 packages/webpack/react-webpack-plugin/src/loaders/testing.ts diff --git a/.changeset/easy-waves-pump.md b/.changeset/easy-waves-pump.md new file mode 100644 index 0000000000..6b6239c210 --- /dev/null +++ b/.changeset/easy-waves-pump.md @@ -0,0 +1,6 @@ +--- +"@lynx-js/react-webpack-plugin": patch +"@lynx-js/react-rsbuild-plugin": patch +--- + +Support rstest for testing library using a dedicated testing loader. diff --git a/.changeset/ninety-pants-tease.md b/.changeset/ninety-pants-tease.md new file mode 100644 index 0000000000..644f29a76b --- /dev/null +++ b/.changeset/ninety-pants-tease.md @@ -0,0 +1,5 @@ +--- +"create-rspeedy": patch +--- + +Add Rstest ReactLynx Testing Library template. diff --git a/.changeset/red-lamps-arrive.md b/.changeset/red-lamps-arrive.md new file mode 100644 index 0000000000..cac3bda3b2 --- /dev/null +++ b/.changeset/red-lamps-arrive.md @@ -0,0 +1,29 @@ +--- +"@lynx-js/react": patch +--- + +Support rstest for testing library, you can use rstest with RLTL now: + +Create a config file `rstest.config.ts` with the following content: + +```ts +import { defineConfig, RstestConfig } from '@rstest/core'; +import lynxConfig from './lynx.config.js'; + +export default defineConfig({ + ...lynxConfig as RstestConfig, + testEnvironment: 'jsdom', + setupFiles: [ + require.resolve('@lynx-js/react/testing-library/setupFiles/rstest'), + ], + globals: true, +}); +``` + +And then use rstest as usual: + +```bash +$ rstest +``` + +For more usage detail, see https://rstest.rs/ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2090c18e6e..4c9834b089 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -207,6 +207,12 @@ jobs: pnpm run build --mode development pnpm run lint pnpm run test + npx --registry http://localhost:4873 create-rspeedy-canary@latest --template react-rstest-rltl --dir create-rspeedy-regression-rstest-rltl + cd create-rspeedy-regression-rstest-rltl + pnpm install --registry=http://localhost:4873 + pnpm run build + pnpm run build --mode development + pnpm run test test-react: needs: build uses: ./.github/workflows/workflow-test.yml @@ -230,6 +236,23 @@ jobs: --no-cache --logHeapUsage --silent + test-rstest: + needs: build + uses: ./.github/workflows/workflow-test.yml + strategy: + matrix: + runs-on: + - name: Ubuntu + label: lynx-ubuntu-24.04-medium + - name: Windows + label: lynx-windows-2022-large + name: Rstest (${{ matrix.runs-on.name }}) + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + with: + runs-on: ${{ matrix.runs-on.label }} + run: | + pnpm -r run rstest --testTimeout=50000 test-rust: uses: ./.github/workflows/rust.yml secrets: @@ -293,6 +316,7 @@ jobs: - test-api - test-publish - test-react + - test-rstest - test-rust - test-type - test-typos diff --git a/package.json b/package.json index a1ef3188ab..3275a25531 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@rsbuild/core": "catalog:rsbuild", "@rslib/core": "^0.19.6", "@rspack/core": "catalog:rspack", + "@rstest/core": "catalog:rstest", "@svitejs/changesets-changelog-github-compact": "^1.2.0", "@tsconfig/node22": "^22.0.5", "@tsconfig/strictest": "^2.0.8", diff --git a/packages/react/package.json b/packages/react/package.json index db5ca991b2..3178d6408b 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -101,9 +101,10 @@ "default": "./testing-library/dist/pure.js" }, "./testing-library/vitest-config": { - "types": "./testing-library/types/vitest-config.d.ts", + "types": "./testing-library/dist/vitest.config.d.ts", "default": "./testing-library/dist/vitest.config.js" }, + "./testing-library/setupFiles/rstest": "./testing-library/dist/setupFiles/rstest.js", "./package.json": "./package.json" }, "types": "./types/react.d.ts", diff --git a/packages/react/testing-library/.npmignore b/packages/react/testing-library/.npmignore index cb433b7a07..07238e1865 100644 --- a/packages/react/testing-library/.npmignore +++ b/packages/react/testing-library/.npmignore @@ -1,4 +1,3 @@ * -!dist/* -!dist/env/* +!dist/**/* !types/* diff --git a/packages/react/testing-library/package.json b/packages/react/testing-library/package.json index cedf70b6a0..b0129a3394 100644 --- a/packages/react/testing-library/package.json +++ b/packages/react/testing-library/package.json @@ -6,6 +6,7 @@ "scripts": { "build": "rslib build", "dev": "rslib build --watch", + "rstest": "rstest", "test": "npm run test:base && npm run test:3.1", "test:3.1": "vitest --config vitest.3.1.config.ts", "test:base": "vitest", @@ -13,7 +14,9 @@ }, "devDependencies": { "@lynx-js/react": "workspace:*", + "@lynx-js/react-rsbuild-plugin": "workspace:*", "@lynx-js/testing-environment": "workspace:*", + "@rsbuild/core": "catalog:rsbuild", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1" } diff --git a/packages/react/testing-library/rslib.config.ts b/packages/react/testing-library/rslib.config.ts index 10790990fe..5389d7c0d7 100644 --- a/packages/react/testing-library/rslib.config.ts +++ b/packages/react/testing-library/rslib.config.ts @@ -9,12 +9,13 @@ export default defineConfig({ { format: 'esm', syntax: 'es2022', - dts: false, + dts: true, bundle: true, source: { entry: { 'pure': './src/pure.jsx', - 'env/vitest': './src/env/vitest.ts', + 'env/index': './src/env/index.ts', + 'plugins/index': './src/plugins/index.ts', }, }, output: { @@ -33,9 +34,13 @@ export default defineConfig({ bundle: false, source: { entry: { - 'index': './src/index.jsx', - 'vitest.config': './src/vitest.config.js', - 'vitest-global-setup': './src/vitest-global-setup.js', + 'index': [ + './src/index.jsx', + './src/vitest.config.ts', + './src/env/vitest.ts', + './src/env/rstest.ts', + './src/setupFiles/**/*.js', + ], }, }, output: { diff --git a/packages/react/testing-library/rstest.config.ts b/packages/react/testing-library/rstest.config.ts new file mode 100644 index 0000000000..f2ae44339b --- /dev/null +++ b/packages/react/testing-library/rstest.config.ts @@ -0,0 +1,37 @@ +import { defineConfig } from '@rstest/core'; +import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin'; + +export default defineConfig({ + tools: { + swc: { + jsc: { + transform: { + useDefineForClassFields: true, + }, + }, + }, + }, + plugins: [ + pluginReactLynx(), + ], + source: { + define: { + __RSTEST__: 'true', + __ALOG__: 'true', + }, + }, + testEnvironment: 'jsdom', + setupFiles: [ + require.resolve('@lynx-js/react/testing-library/setupFiles/rstest'), + ], + globals: true, + resolve: { + // in order to make our test case work for + // both vitest and rstest, we need to alias + // `vitest` to `@rstest/core` + alias: { + vitest: require.resolve('./vitest-polyfill.cjs'), + }, + }, + include: ['src/**/*.test.{js,jsx,ts,tsx}', '!src/__tests__/3.1/**/*.{js,jsx,ts,tsx}'], +}); diff --git a/packages/react/testing-library/src/__tests__/act.test.jsx b/packages/react/testing-library/src/__tests__/act.test.jsx index cc1e04329f..31e7850c9a 100644 --- a/packages/react/testing-library/src/__tests__/act.test.jsx +++ b/packages/react/testing-library/src/__tests__/act.test.jsx @@ -201,7 +201,7 @@ test('fireEvent triggers useEffect calls', async () => { [ "rLynxChange", { - "data": "{"patchList":[{"snapshotPatch":[0,"__snapshot_e8d0a_test_4",2,4,2,[1],0,null,3,4,3,[0],1,2,3,null,1,-1,2,null],"id":2}]}", + "data": "{"patchList":[{"snapshotPatch":[0,"__snapshot_268b9_test_4",2,4,2,[1],0,null,3,4,3,[0],1,2,3,null,1,-1,2,null],"id":2}]}", "patchOptions": { "isHydration": true, "pipelineOptions": { @@ -236,7 +236,7 @@ test('fireEvent triggers useEffect calls', async () => { ], "extraProps": undefined, "id": 2, - "type": "__snapshot_e8d0a_test_4", + "type": "__snapshot_268b9_test_4", "values": [ "2:0:", ], @@ -261,7 +261,7 @@ test('fireEvent triggers useEffect calls', async () => { ], "extraProps": undefined, "id": 2, - "type": "__snapshot_e8d0a_test_4", + "type": "__snapshot_268b9_test_4", "values": [ "2:0:", ], @@ -285,7 +285,7 @@ test('fireEvent triggers useEffect calls', async () => { [ "rLynxChange", { - "data": "{"patchList":[{"snapshotPatch":[0,"__snapshot_e8d0a_test_4",2,4,2,[1],0,null,3,4,3,[0],1,2,3,null,1,-1,2,null],"id":2}]}", + "data": "{"patchList":[{"snapshotPatch":[0,"__snapshot_268b9_test_4",2,4,2,[1],0,null,3,4,3,[0],1,2,3,null,1,-1,2,null],"id":2}]}", "patchOptions": { "isHydration": true, "pipelineOptions": { diff --git a/packages/react/testing-library/src/__tests__/alog.test.jsx b/packages/react/testing-library/src/__tests__/alog.test.jsx index 00a34ac41f..e20919b4bf 100644 --- a/packages/react/testing-library/src/__tests__/alog.test.jsx +++ b/packages/react/testing-library/src/__tests__/alog.test.jsx @@ -154,7 +154,98 @@ describe('alog', () => { "[ReactLynxDebug] FiberElement API call #31: __AppendElement(WRAPPER#8, VIEW#11)", ], [ - "[ReactLynxDebug] FiberElement API call #32: __OnLifecycleEvent(["rLynxFirstScreen", {"root":"{\\"id\\":-1,\\"type\\":\\"root\\",\\"children\\":[{\\"id\\":-2,\\"type\\":\\"__snapshot_426db_test_1\\",\\"values\\":[\\"-2:0:\\",\\"-2:1:\\"],\\"children\\":[{\\"id\\":-3,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-7,\\"type\\":null,\\"values\\":[0]}]},{\\"id\\":-4,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-5,\\"type\\":\\"__snapshot_426db_test_2\\"},{\\"id\\":-6,\\"type\\":\\"__snapshot_426db_test_3\\"}]}]}]}","jsReadyEventIdSwap":{}}])", + "[ReactLynxDebug] FiberElement API call #32: __OnLifecycleEvent(["rLynxFirstScreen", {"root":"{\\"id\\":-1,\\"type\\":\\"root\\",\\"children\\":[{\\"id\\":-2,\\"type\\":\\"__snapshot_d6fb6_test_1\\",\\"values\\":[\\"-2:0:\\",\\"-2:1:\\"],\\"children\\":[{\\"id\\":-3,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-7,\\"type\\":null,\\"values\\":[0]}]},{\\"id\\":-4,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-5,\\"type\\":\\"__snapshot_d6fb6_test_2\\"},{\\"id\\":-6,\\"type\\":\\"__snapshot_d6fb6_test_3\\"}]}]}]}","jsReadyEventIdSwap":{}}])", + ], + [ + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: 6", + ], + [ + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: 7", + ], + [ + "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: 2", + ], + [ + "[BackgroundThread Component Render] name: Fragment, uniqID: __snapshot_d6fb6_test_1, __id: 2", + ], + [ + "[ReactLynxDebug] MTS -> BTS OnLifecycleEvent: + { + "root": { + "id": -1, + "type": "root", + "children": [ + { + "id": -2, + "type": "__snapshot_d6fb6_test_1", + "values": [ + "-2:0:", + "-2:1:" + ], + "children": [ + { + "id": -3, + "type": "wrapper", + "children": [ + { + "id": -7, + "type": null, + "values": [ + 0 + ] + } + ] + }, + { + "id": -4, + "type": "wrapper", + "children": [ + { + "id": -5, + "type": "__snapshot_d6fb6_test_2" + }, + { + "id": -6, + "type": "__snapshot_d6fb6_test_3" + } + ] + } + ] + } + ] + }, + "jsReadyEventIdSwap": {} + }", + ], + [ + "[ReactLynxDebug] SnapshotInstance tree for first screen hydration: + | -1(root): undefined + | -2(__snapshot_d6fb6_test_1): ["-2:0:","-2:1:"] + | -3(wrapper): undefined + | -7(null): [0] + | -4(wrapper): undefined + | -5(__snapshot_d6fb6_test_2): undefined + | -6(__snapshot_d6fb6_test_3): undefined", + ], + [ + "[ReactLynxDebug] BackgroundSnapshotInstance tree before hydration: + | 1(root): undefined + | 2(__snapshot_d6fb6_test_1): [null,null] + | 3(wrapper): undefined + | 4(null): [0] + | 5(wrapper): undefined + | 6(__snapshot_d6fb6_test_2): undefined + | 7(__snapshot_d6fb6_test_3): undefined", + ], + [ + "[ReactLynxDebug] BackgroundSnapshotInstance after hydration: + | -1(root): undefined + | -2(__snapshot_d6fb6_test_1): [null,null] + | -3(wrapper): undefined + | -7(null): [0] + | -4(wrapper): undefined + | -5(__snapshot_d6fb6_test_2): undefined + | -6(__snapshot_d6fb6_test_3): undefined", ], [ "[ReactLynxDebug] BTS -> MTS updateMainThread: @@ -183,6 +274,33 @@ describe('alog', () => { [ "[ReactLynxDebug] FiberElement API call #33: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", ], + [ + "[ReactLynxDebug] BTS received event: + { + "handlerName": "-2:0:", + "type": "bindEvent:tap", + "snapshotType": "__snapshot_d6fb6_test_1", + "jsFunctionName": "" + }", + ], + [ + "[ReactLynxDebug] BTS received event: + { + "handlerName": "-2:1:", + "type": "catchEvent:focus", + "snapshotType": "__snapshot_d6fb6_test_1", + "jsFunctionName": "handleFocus" + }", + ], + [ + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -5", + ], + [ + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -6", + ], + [ + "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", + ], [ "[ReactLynxDebug] BTS -> MTS updateMainThread: { @@ -224,16 +342,121 @@ describe('alog', () => { expect(lynxTestingEnv.backgroundThread.console.alog.mock.calls).toMatchInlineSnapshot(` [ [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_426db_test_2, __id: 6", + "[ReactLynxDebug] FiberElement API call #1: __CreatePage("0", 0) => PAGE#0", + ], + [ + "[ReactLynxDebug] FiberElement API call #2: __GetElementUniqueID(PAGE#0) => 0", + ], + [ + "[ReactLynxDebug] FiberElement API call #3: __SetCSSId([PAGE#0], 0)", + ], + [ + "[MainThread Component Render] name: ClassComponent", + ], + [ + "[MainThread Component Render] name: FunctionComponent", + ], + [ + "[MainThread Component Render] name: App", + ], + [ + "[ReactLynxDebug] FiberElement API call #4: __CreateView(0) => VIEW#1", + ], + [ + "[ReactLynxDebug] FiberElement API call #5: __CreateText(0) => TEXT#2", + ], + [ + "[ReactLynxDebug] FiberElement API call #6: __AddDataset(TEXT#2, "testid", "count-text")", + ], + [ + "[ReactLynxDebug] FiberElement API call #7: __AppendElement(VIEW#1, TEXT#2)", + ], + [ + "[ReactLynxDebug] FiberElement API call #8: __CreateRawText("count: ") => #text#3", + ], + [ + "[ReactLynxDebug] FiberElement API call #9: __AppendElement(TEXT#2, #text#3)", + ], + [ + "[ReactLynxDebug] FiberElement API call #10: __CreateWrapperElement(0) => WRAPPER#4", + ], + [ + "[ReactLynxDebug] FiberElement API call #11: __AppendElement(TEXT#2, WRAPPER#4)", + ], + [ + "[ReactLynxDebug] FiberElement API call #12: __CreateWrapperElement(0) => WRAPPER#5", + ], + [ + "[ReactLynxDebug] FiberElement API call #13: __AppendElement(VIEW#1, WRAPPER#5)", + ], + [ + "[ReactLynxDebug] FiberElement API call #14: __AppendElement(PAGE#0, VIEW#1)", + ], + [ + "[ReactLynxDebug] FiberElement API call #15: __AddEvent(TEXT#2, "bindEvent", "tap", "-2:0:")", + ], + [ + "[ReactLynxDebug] FiberElement API call #16: __AddEvent(TEXT#2, "catchEvent", "focus", "-2:1:")", + ], + [ + "[ReactLynxDebug] FiberElement API call #17: __CreateWrapperElement(0) => WRAPPER#6", + ], + [ + "[ReactLynxDebug] FiberElement API call #18: __ReplaceElement(WRAPPER#6, WRAPPER#4)", + ], + [ + "[ReactLynxDebug] FiberElement API call #19: __CreateRawText("") => #text#7", + ], + [ + "[ReactLynxDebug] FiberElement API call #20: __SetAttribute(#text#7, "text", 0)", + ], + [ + "[ReactLynxDebug] FiberElement API call #21: __AppendElement(WRAPPER#6, #text#7)", + ], + [ + "[ReactLynxDebug] FiberElement API call #22: __CreateWrapperElement(0) => WRAPPER#8", + ], + [ + "[ReactLynxDebug] FiberElement API call #23: __ReplaceElement(WRAPPER#8, WRAPPER#5)", + ], + [ + "[ReactLynxDebug] FiberElement API call #24: __CreateView(0) => VIEW#9", + ], + [ + "[ReactLynxDebug] FiberElement API call #25: __CreateRawText("Class Component") => #text#10", + ], + [ + "[ReactLynxDebug] FiberElement API call #26: __AppendElement(VIEW#9, #text#10)", + ], + [ + "[ReactLynxDebug] FiberElement API call #27: __AppendElement(WRAPPER#8, VIEW#9)", + ], + [ + "[ReactLynxDebug] FiberElement API call #28: __CreateView(0) => VIEW#11", + ], + [ + "[ReactLynxDebug] FiberElement API call #29: __CreateRawText("Function Component") => #text#12", + ], + [ + "[ReactLynxDebug] FiberElement API call #30: __AppendElement(VIEW#11, #text#12)", + ], + [ + "[ReactLynxDebug] FiberElement API call #31: __AppendElement(WRAPPER#8, VIEW#11)", + ], + [ + "[ReactLynxDebug] FiberElement API call #32: __OnLifecycleEvent(["rLynxFirstScreen", {"root":"{\\"id\\":-1,\\"type\\":\\"root\\",\\"children\\":[{\\"id\\":-2,\\"type\\":\\"__snapshot_d6fb6_test_1\\",\\"values\\":[\\"-2:0:\\",\\"-2:1:\\"],\\"children\\":[{\\"id\\":-3,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-7,\\"type\\":null,\\"values\\":[0]}]},{\\"id\\":-4,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-5,\\"type\\":\\"__snapshot_d6fb6_test_2\\"},{\\"id\\":-6,\\"type\\":\\"__snapshot_d6fb6_test_3\\"}]}]}]}","jsReadyEventIdSwap":{}}])", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_426db_test_3, __id: 7", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: 6", ], [ - "[BackgroundThread Component Render] name: App, uniqID: __snapshot_426db_test_1, __id: 2", + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: 7", ], [ - "[BackgroundThread Component Render] name: Fragment, uniqID: __snapshot_426db_test_1, __id: 2", + "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: 2", + ], + [ + "[BackgroundThread Component Render] name: Fragment, uniqID: __snapshot_d6fb6_test_1, __id: 2", ], [ "[ReactLynxDebug] MTS -> BTS OnLifecycleEvent: @@ -244,7 +467,7 @@ describe('alog', () => { "children": [ { "id": -2, - "type": "__snapshot_426db_test_1", + "type": "__snapshot_d6fb6_test_1", "values": [ "-2:0:", "-2:1:" @@ -269,11 +492,11 @@ describe('alog', () => { "children": [ { "id": -5, - "type": "__snapshot_426db_test_2" + "type": "__snapshot_d6fb6_test_2" }, { "id": -6, - "type": "__snapshot_426db_test_3" + "type": "__snapshot_d6fb6_test_3" } ] } @@ -287,39 +510,66 @@ describe('alog', () => { [ "[ReactLynxDebug] SnapshotInstance tree for first screen hydration: | -1(root): undefined - | -2(__snapshot_426db_test_1): ["-2:0:","-2:1:"] + | -2(__snapshot_d6fb6_test_1): ["-2:0:","-2:1:"] | -3(wrapper): undefined | -7(null): [0] | -4(wrapper): undefined - | -5(__snapshot_426db_test_2): undefined - | -6(__snapshot_426db_test_3): undefined", + | -5(__snapshot_d6fb6_test_2): undefined + | -6(__snapshot_d6fb6_test_3): undefined", ], [ "[ReactLynxDebug] BackgroundSnapshotInstance tree before hydration: | 1(root): undefined - | 2(__snapshot_426db_test_1): [null,null] + | 2(__snapshot_d6fb6_test_1): [null,null] | 3(wrapper): undefined | 4(null): [0] | 5(wrapper): undefined - | 6(__snapshot_426db_test_2): undefined - | 7(__snapshot_426db_test_3): undefined", + | 6(__snapshot_d6fb6_test_2): undefined + | 7(__snapshot_d6fb6_test_3): undefined", ], [ "[ReactLynxDebug] BackgroundSnapshotInstance after hydration: | -1(root): undefined - | -2(__snapshot_426db_test_1): [null,null] + | -2(__snapshot_d6fb6_test_1): [null,null] | -3(wrapper): undefined | -7(null): [0] | -4(wrapper): undefined - | -5(__snapshot_426db_test_2): undefined - | -6(__snapshot_426db_test_3): undefined", + | -5(__snapshot_d6fb6_test_2): undefined + | -6(__snapshot_d6fb6_test_3): undefined", + ], + [ + "[ReactLynxDebug] BTS -> MTS updateMainThread: + { + "data": { + "patchList": [ + { + "snapshotPatch": [], + "id": 2 + } + ] + }, + "patchOptions": { + "isHydration": true, + "reloadVersion": 0, + "pipelineOptions": { + "pipelineID": "pipelineID", + "needTimestamps": true, + "pipelineOrigin": "reactLynxHydrate", + "dsl": "reactLynx", + "stage": "hydrate" + } + } + }", + ], + [ + "[ReactLynxDebug] FiberElement API call #33: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", ], [ "[ReactLynxDebug] BTS received event: { "handlerName": "-2:0:", "type": "bindEvent:tap", - "snapshotType": "__snapshot_426db_test_1", + "snapshotType": "__snapshot_d6fb6_test_1", "jsFunctionName": "" }", ], @@ -328,18 +578,54 @@ describe('alog', () => { { "handlerName": "-2:1:", "type": "catchEvent:focus", - "snapshotType": "__snapshot_426db_test_1", + "snapshotType": "__snapshot_d6fb6_test_1", "jsFunctionName": "handleFocus" }", ], [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_426db_test_2, __id: -5", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -5", + ], + [ + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -6", + ], + [ + "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", + ], + [ + "[ReactLynxDebug] BTS -> MTS updateMainThread: + { + "data": { + "patchList": [ + { + "id": 3, + "snapshotPatch": [ + { + "op": "SetAttribute", + "id": -7, + "dynamicPartIndex": 0, + "value": 1 + } + ] + } + ] + }, + "patchOptions": { + "reloadVersion": 0, + "pipelineOptions": { + "pipelineID": "pipelineID", + "needTimestamps": true, + "pipelineOrigin": "reactLynxHydrate", + "dsl": "reactLynx", + "stage": "hydrate" + } + } + }", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_426db_test_3, __id: -6", + "[ReactLynxDebug] FiberElement API call #34: __SetAttribute(#text#7, "text", 1)", ], [ - "[BackgroundThread Component Render] name: App, uniqID: __snapshot_426db_test_1, __id: -2", + "[ReactLynxDebug] FiberElement API call #35: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", ], ] `); @@ -353,6 +639,15 @@ describe('alog', () => { expect(lynxTestingEnv.mainThread.console.alog.mock.calls).toMatchInlineSnapshot(` [ + [ + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -5", + ], + [ + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -6", + ], + [ + "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", + ], [ "[ReactLynxDebug] BTS -> MTS updateMainThread: { @@ -394,13 +689,49 @@ describe('alog', () => { expect(lynxTestingEnv.backgroundThread.console.alog.mock.calls).toMatchInlineSnapshot(` [ [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_426db_test_2, __id: -5", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -5", + ], + [ + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -6", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_426db_test_3, __id: -6", + "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", + ], + [ + "[ReactLynxDebug] BTS -> MTS updateMainThread: + { + "data": { + "patchList": [ + { + "id": 4, + "snapshotPatch": [ + { + "op": "SetAttribute", + "id": -7, + "dynamicPartIndex": 0, + "value": 0 + } + ] + } + ] + }, + "patchOptions": { + "reloadVersion": 0, + "pipelineOptions": { + "pipelineID": "pipelineID", + "needTimestamps": true, + "pipelineOrigin": "reactLynxHydrate", + "dsl": "reactLynx", + "stage": "hydrate" + } + } + }", ], [ - "[BackgroundThread Component Render] name: App, uniqID: __snapshot_426db_test_1, __id: -2", + "[ReactLynxDebug] FiberElement API call #36: __SetAttribute(#text#7, "text", 0)", + ], + [ + "[ReactLynxDebug] FiberElement API call #37: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", ], ] `); @@ -414,6 +745,15 @@ describe('alog', () => { expect(lynxTestingEnv.mainThread.console.alog.mock.calls).toMatchInlineSnapshot(` [ + [ + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -5", + ], + [ + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -6", + ], + [ + "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", + ], [ "[ReactLynxDebug] BTS -> MTS updateMainThread: { @@ -455,13 +795,49 @@ describe('alog', () => { expect(lynxTestingEnv.backgroundThread.console.alog.mock.calls).toMatchInlineSnapshot(` [ [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_426db_test_2, __id: -5", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -5", + ], + [ + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -6", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_426db_test_3, __id: -6", + "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", ], [ - "[BackgroundThread Component Render] name: App, uniqID: __snapshot_426db_test_1, __id: -2", + "[ReactLynxDebug] BTS -> MTS updateMainThread: + { + "data": { + "patchList": [ + { + "id": 5, + "snapshotPatch": [ + { + "op": "SetAttribute", + "id": -7, + "dynamicPartIndex": 0, + "value": 1 + } + ] + } + ] + }, + "patchOptions": { + "reloadVersion": 0, + "pipelineOptions": { + "pipelineID": "pipelineID", + "needTimestamps": true, + "pipelineOrigin": "reactLynxHydrate", + "dsl": "reactLynx", + "stage": "hydrate" + } + } + }", + ], + [ + "[ReactLynxDebug] FiberElement API call #38: __SetAttribute(#text#7, "text", 1)", + ], + [ + "[ReactLynxDebug] FiberElement API call #39: __FlushElementTree(PAGE#0, {"pipelineOptions":{"pipelineID":"pipelineID","needTimestamps":true,"pipelineOrigin":"reactLynxHydrate","dsl":"reactLynx","stage":"hydrate"}})", ], ] `); diff --git a/packages/react/testing-library/src/__tests__/auto-cleanup-skip.test.jsx b/packages/react/testing-library/src/__tests__/auto-cleanup-skip.test.jsx index ac369899e4..d729637359 100644 --- a/packages/react/testing-library/src/__tests__/auto-cleanup-skip.test.jsx +++ b/packages/react/testing-library/src/__tests__/auto-cleanup-skip.test.jsx @@ -1,3 +1,5 @@ +import { beforeAll, test, expect } from 'vitest'; + let render; beforeAll(async () => { diff --git a/packages/react/testing-library/src/__tests__/cleanup.test.jsx b/packages/react/testing-library/src/__tests__/cleanup.test.jsx index 00bd089395..ffbebe04ed 100644 --- a/packages/react/testing-library/src/__tests__/cleanup.test.jsx +++ b/packages/react/testing-library/src/__tests__/cleanup.test.jsx @@ -1,4 +1,4 @@ -import { expect } from 'vitest'; +import { expect, vi } from 'vitest'; import { render, cleanup } from '..'; import { Component } from '@lynx-js/react'; diff --git a/packages/react/testing-library/src/__tests__/css/index.test.jsx b/packages/react/testing-library/src/__tests__/css/index.test.jsx index e7e7c7c99b..ba72bea45b 100644 --- a/packages/react/testing-library/src/__tests__/css/index.test.jsx +++ b/packages/react/testing-library/src/__tests__/css/index.test.jsx @@ -34,8 +34,8 @@ describe('CSS', () => { `); }); it('should render a component with CSS module styles object', () => { - // Assert stable shape (prefix) rather than exact hash - expect(style3.baz).toMatch(/^_baz_[a-z0-9]+$/); + // to be an string + expect(style3.baz).toBeTypeOf('string'); const TestComponent = () => Hello World; const { container } = render(); diff --git a/packages/react/testing-library/src/__tests__/end-to-end.test.jsx b/packages/react/testing-library/src/__tests__/end-to-end.test.jsx index 4a0f1aa25c..eb79b30144 100644 --- a/packages/react/testing-library/src/__tests__/end-to-end.test.jsx +++ b/packages/react/testing-library/src/__tests__/end-to-end.test.jsx @@ -1,6 +1,6 @@ import '@testing-library/jest-dom'; import { Component } from 'preact'; -import { expect } from 'vitest'; +import { expect, vi } from 'vitest'; import { render, screen, waitForElementToBeRemoved } from '..'; import { snapshotInstanceManager } from '../../../runtime/lib/snapshot.js'; @@ -67,7 +67,7 @@ test('state change will cause re-render', async () => { "children": undefined, "extraProps": undefined, "id": 2, - "type": "__snapshot_354a3_test_1", + "type": "__snapshot_f46c5_test_1", "values": undefined, }, ], @@ -80,7 +80,7 @@ test('state change will cause re-render', async () => { "children": undefined, "extraProps": undefined, "id": 2, - "type": "__snapshot_354a3_test_1", + "type": "__snapshot_f46c5_test_1", "values": undefined, }, } @@ -100,7 +100,7 @@ test('state change will cause re-render', async () => { [ "rLynxChange", { - "data": "{"patchList":[{"snapshotPatch":[0,"__snapshot_354a3_test_1",2,1,-1,2,null],"id":2}]}", + "data": "{"patchList":[{"snapshotPatch":[0,"__snapshot_f46c5_test_1",2,1,-1,2,null],"id":2}]}", "patchOptions": { "isHydration": true, "pipelineOptions": { @@ -118,7 +118,7 @@ test('state change will cause re-render', async () => { [ "rLynxChange", { - "data": "{"patchList":[{"id":3,"snapshotPatch":[2,-1,2,0,"__snapshot_354a3_test_2",3,0,null,4,3,4,0,"Hello World",1,3,4,null,1,-1,3,null]}]}", + "data": "{"patchList":[{"id":3,"snapshotPatch":[2,-1,2,0,"__snapshot_f46c5_test_2",3,0,null,4,3,4,0,"Hello World",1,3,4,null,1,-1,3,null]}]}", "patchOptions": { "pipelineOptions": { "dsl": "reactLynx", diff --git a/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx b/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx index 9e23c0a39b..3535ca3220 100644 --- a/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx +++ b/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx @@ -22,7 +22,7 @@ function LazyComponentLoader({ url }) { return ( loading...}> - + {(typeof __RSTEST__ !== 'undefined' && __RSTEST__) ? null : } ); } @@ -55,7 +55,18 @@ describe('lazy bundle', () => { timeout: 50_000, }); - expect(container.firstChild).toMatchInlineSnapshot(` + if (typeof __RSTEST__ !== 'undefined' && __RSTEST__) { + expect(container.firstChild).toMatchInlineSnapshot(` + + + + Hello from LazyComponent + + + + `); + } else { + expect(container.firstChild).toMatchInlineSnapshot(` @@ -67,6 +78,7 @@ describe('lazy bundle', () => { `); + } }); }); @@ -166,12 +178,12 @@ describe('Suspense', () => { { "id": 2, "op": "CreateElement", - "type": "__snapshot_fffe1_test_3", + "type": "__snapshot_50869_test_3", }, { "id": 7, "op": "CreateElement", - "type": "__snapshot_fffe1_test_4", + "type": "__snapshot_50869_test_4", }, { "beforeId": null, @@ -193,7 +205,7 @@ describe('Suspense', () => { { "id": 2, "op": "CreateElement", - "type": "__snapshot_fffe1_test_3", + "type": "__snapshot_50869_test_3", }, { "id": 8, @@ -203,7 +215,7 @@ describe('Suspense', () => { { "id": 9, "op": "CreateElement", - "type": "__snapshot_fffe1_test_4", + "type": "__snapshot_50869_test_4", }, { "beforeId": null, @@ -294,7 +306,8 @@ describe('Suspense', () => { if (name === 'PreactSuspense') { // is torn down, (it is triggered in first render but delayed 10_000ms to execute, we use `vi.runAllTimers()` to simulate the situation that will cause the bug) // loading... is torn down - expect(tearDownInstances).toMatchInlineSnapshot(` + if (typeof __RSTEST__ === 'undefined') { + expect(tearDownInstances).toMatchInlineSnapshot(` [ { "__id": 3, @@ -318,7 +331,7 @@ describe('Suspense', () => { el4 ]; }", - "type": "__snapshot_fffe1_test_5", + "type": "__snapshot_50869_test_5", }, { "__id": 7, @@ -333,12 +346,14 @@ describe('Suspense', () => { el1 ]; }", - "type": "__snapshot_fffe1_test_4", + "type": "__snapshot_50869_test_4", }, ] `); + } } else { - expect(tearDownInstances).toMatchInlineSnapshot(` + if (typeof __RSTEST__ === 'undefined') { + expect(tearDownInstances).toMatchInlineSnapshot(` [ { "__id": 8, @@ -352,6 +367,7 @@ describe('Suspense', () => { }, ] `); + } } act(() => { diff --git a/packages/react/testing-library/src/__tests__/list.test.jsx b/packages/react/testing-library/src/__tests__/list.test.jsx index 1bdfef64f8..a3e787fdab 100644 --- a/packages/react/testing-library/src/__tests__/list.test.jsx +++ b/packages/react/testing-library/src/__tests__/list.test.jsx @@ -3,7 +3,7 @@ // LICENSE file in the root directory of this source tree. import { act } from 'preact/test-utils'; -import { describe, expect } from 'vitest'; +import { describe, expect, vi } from 'vitest'; import { Component, useState } from '@lynx-js/react'; @@ -31,7 +31,7 @@ describe('list', () => { expect(container).toMatchInlineSnapshot(` `); @@ -42,7 +42,7 @@ describe('list', () => { expect(container).toMatchInlineSnapshot(` { expect(container).toMatchInlineSnapshot(` { expect(container).toMatchInlineSnapshot(` { expect(container).toMatchInlineSnapshot(` { > @@ -395,7 +395,95 @@ describe('list', () => { expect(list).toMatchInlineSnapshot(` + + + + + 4 + + + 4 + + + + + hello + + + + + + + + + 5 + + + 5 + + + + + hello + + + + + + + + + 2 + + + 2 + + + + + hello + + + + + + + + + 1 + + + 1 + + + + + hello + + + + + + `); + + expect(list).toMatchInlineSnapshot(` + should render as normal', () => { `); @@ -585,7 +673,7 @@ describe('list - deferred should render as normal', () => { `); @@ -597,7 +685,7 @@ describe('list - deferred should render as normal', () => { should render as normal', () => { `); @@ -766,7 +854,7 @@ describe('list - deferred should render as normal', () => { `); @@ -786,7 +874,7 @@ describe('list - deferred should render as normal', () => { `); @@ -805,7 +893,7 @@ describe('list - deferred should render as normal', () => { should render as normal', () => { "rLynxFirstScreen", { "jsReadyEventIdSwap": {}, - "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_a9e46_test_30","children":[{"id":-3,"type":"__snapshot_a9e46_test_31","values":[{"item-key":0}],"children":[{"id":-4,"type":"__snapshot_a9e46_test_29","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-5,"type":"__snapshot_a9e46_test_32","children":[{"id":-12,"type":null,"values":[0]}]}]}]},{"id":-6,"type":"__snapshot_a9e46_test_31","values":[{"item-key":1}],"children":[{"id":-7,"type":"__snapshot_a9e46_test_29","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-8,"type":"__snapshot_a9e46_test_32","children":[{"id":-13,"type":null,"values":[1]}]}]}]},{"id":-9,"type":"__snapshot_a9e46_test_31","values":[{"item-key":2}],"children":[{"id":-10,"type":"__snapshot_a9e46_test_29","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-11,"type":"__snapshot_a9e46_test_32","children":[{"id":-14,"type":null,"values":[2]}]}]}]}]}]}", + "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_d0c07_test_30","children":[{"id":-3,"type":"__snapshot_d0c07_test_31","values":[{"item-key":0}],"children":[{"id":-4,"type":"__snapshot_d0c07_test_29","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-5,"type":"__snapshot_d0c07_test_32","children":[{"id":-12,"type":null,"values":[0]}]}]}]},{"id":-6,"type":"__snapshot_d0c07_test_31","values":[{"item-key":1}],"children":[{"id":-7,"type":"__snapshot_d0c07_test_29","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-8,"type":"__snapshot_d0c07_test_32","children":[{"id":-13,"type":null,"values":[1]}]}]}]},{"id":-9,"type":"__snapshot_d0c07_test_31","values":[{"item-key":2}],"children":[{"id":-10,"type":"__snapshot_d0c07_test_29","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-11,"type":"__snapshot_d0c07_test_32","children":[{"id":-14,"type":null,"values":[2]}]}]}]}]}]}", }, ], ], diff --git a/packages/react/testing-library/src/__tests__/lynx.test.jsx b/packages/react/testing-library/src/__tests__/lynx.test.jsx index 6151c8f730..392a35bb88 100644 --- a/packages/react/testing-library/src/__tests__/lynx.test.jsx +++ b/packages/react/testing-library/src/__tests__/lynx.test.jsx @@ -62,7 +62,7 @@ describe('lynx global API', () => { { "id": 2, "op": "CreateElement", - "type": "__snapshot_d4b6f_test_1", + "type": "__snapshot_034cb_test_1", }, { "beforeId": null, diff --git a/packages/react/testing-library/src/__tests__/rerender.test.jsx b/packages/react/testing-library/src/__tests__/rerender.test.jsx index 559e632fc6..df6a9a9bde 100644 --- a/packages/react/testing-library/src/__tests__/rerender.test.jsx +++ b/packages/react/testing-library/src/__tests__/rerender.test.jsx @@ -1,6 +1,6 @@ import '@testing-library/jest-dom'; import { render } from '..'; -import { expect } from 'vitest'; +import { expect, vi } from 'vitest'; import { useEffect, useState } from '@lynx-js/react'; test('rerender will re-render the element', async () => { diff --git a/packages/react/testing-library/src/__tests__/worklet.test.jsx b/packages/react/testing-library/src/__tests__/worklet.test.jsx index ac02d55fac..6c1b3f3a74 100644 --- a/packages/react/testing-library/src/__tests__/worklet.test.jsx +++ b/packages/react/testing-library/src/__tests__/worklet.test.jsx @@ -77,7 +77,7 @@ describe('worklet', () => { [ "rLynxChange", { - "data": "{"patchList":[{"snapshotPatch":[3,-2,0,{"_wkltId":"a45f:test:2","_workletType":"main-thread","_execId":1}],"id":2}]}", + "data": "{"patchList":[{"snapshotPatch":[3,-2,0,{"_wkltId":"15ab:test:2","_workletType":"main-thread","_execId":1}],"id":2}]}", "patchOptions": { "isHydration": true, "pipelineOptions": { @@ -159,7 +159,7 @@ describe('worklet', () => { [ "rLynxChange", { - "data": "{"patchList":[{"snapshotPatch":[3,-2,1,{"_c":{"props":{"main-thread:onClick":{"_wkltId":"a45f:test:3"}}},"_wkltId":"a45f:test:4","_execId":1}],"id":2}]}", + "data": "{"patchList":[{"snapshotPatch":[3,-2,1,{"_c":{"props":{"main-thread:onClick":{"_wkltId":"15ab:test:3"}}},"_wkltId":"15ab:test:4","_execId":1}],"id":2}]}", "patchOptions": { "isHydration": true, "pipelineOptions": { @@ -249,7 +249,7 @@ describe('worklet', () => { [ "rLynxChange", { - "data": "{"patchList":[{"snapshotPatch":[3,-2,0,{"_c":{"props":{"main-thread:onScroll":{"_wkltId":"a45f:test:5"}}},"_wkltId":"a45f:test:6","_execId":1}],"id":2}]}", + "data": "{"patchList":[{"snapshotPatch":[3,-2,0,{"_c":{"props":{"main-thread:onScroll":{"_wkltId":"15ab:test:5"}}},"_wkltId":"15ab:test:6","_execId":1}],"id":2}]}", "patchOptions": { "isHydration": true, "pipelineOptions": { @@ -343,7 +343,7 @@ describe('worklet', () => { [ "rLynxChange", { - "data": "{"patchList":[{"snapshotPatch":[3,-2,0,{"_wkltId":"a45f:test:8","_jsFn":{"_jsFn1":{"_jsFnId":2,"_fn":"[BackgroundFunction]"}},"_execId":1}],"id":2}]}", + "data": "{"patchList":[{"snapshotPatch":[3,-2,0,{"_wkltId":"15ab:test:8","_jsFn":{"_jsFn1":{"_jsFnId":2,"_fn":"[BackgroundFunction]"}},"_execId":1}],"id":2}]}", "patchOptions": { "isHydration": true, "pipelineOptions": { @@ -447,7 +447,7 @@ describe('worklet', () => { [ "rLynxChange", { - "data": "{"patchList":[{"snapshotPatch":[3,-2,0,{"_wvid":1},3,-2,1,{"_c":{"ref":{"_wvid":1},"num":{"_wvid":2}},"_wkltId":"a45f:test:9","_execId":1}],"id":2}]}", + "data": "{"patchList":[{"snapshotPatch":[3,-2,0,{"_wvid":1},3,-2,1,{"_c":{"ref":{"_wvid":1},"num":{"_wvid":2}},"_wkltId":"15ab:test:9","_execId":1}],"id":2}]}", "patchOptions": { "isHydration": true, "pipelineOptions": { diff --git a/packages/react/testing-library/src/env/index.ts b/packages/react/testing-library/src/env/index.ts new file mode 100644 index 0000000000..672db9f107 --- /dev/null +++ b/packages/react/testing-library/src/env/index.ts @@ -0,0 +1 @@ +export { LynxTestingEnv } from '@lynx-js/testing-environment'; diff --git a/packages/react/testing-library/src/env/rstest.ts b/packages/react/testing-library/src/env/rstest.ts new file mode 100644 index 0000000000..d4b2359730 --- /dev/null +++ b/packages/react/testing-library/src/env/rstest.ts @@ -0,0 +1,8 @@ +import { LynxTestingEnv } from './index.js'; + +// @ts-ignore +global.jsdom = { + window, +}; +const lynxTestingEnv = new LynxTestingEnv(); +global.lynxTestingEnv = lynxTestingEnv; diff --git a/packages/react/testing-library/src/env/vitest.ts b/packages/react/testing-library/src/env/vitest.ts index cf89eb9fdb..47da348ef0 100644 --- a/packages/react/testing-library/src/env/vitest.ts +++ b/packages/react/testing-library/src/env/vitest.ts @@ -1,3 +1,26 @@ -import env from '@lynx-js/testing-environment/env/vitest'; +import { builtinEnvironments, type Environment } from 'vitest/environments'; +import { LynxTestingEnv } from './index.js'; + +const env: Environment = { + name: 'lynxTestingEnv', + transformMode: 'web', + async setup(global) { + const fakeGlobal: { + jsdom?: any; + } = {}; + await builtinEnvironments.jsdom.setup(fakeGlobal, {}); + global.jsdom = fakeGlobal.jsdom; + + const lynxTestingEnv = new LynxTestingEnv(); + global.lynxTestingEnv = lynxTestingEnv; + + return { + teardown(global) { + delete global.lynxTestingEnv; + delete global.jsdom; + }, + }; + }, +}; export default env; diff --git a/packages/react/testing-library/src/plugins/index.ts b/packages/react/testing-library/src/plugins/index.ts new file mode 100644 index 0000000000..2b150f8d92 --- /dev/null +++ b/packages/react/testing-library/src/plugins/index.ts @@ -0,0 +1,2 @@ +export { testingLibraryPlugin as vitestTestingLibraryPlugin } from './vitest.js'; +export type { TestingLibraryOptions } from './vitest.js'; diff --git a/packages/react/testing-library/src/vitest.config.js b/packages/react/testing-library/src/plugins/vitest.ts similarity index 63% rename from packages/react/testing-library/src/vitest.config.js rename to packages/react/testing-library/src/plugins/vitest.ts index 7c37d9dda5..eff13db97b 100644 --- a/packages/react/testing-library/src/vitest.config.js +++ b/packages/react/testing-library/src/plugins/vitest.ts @@ -1,62 +1,69 @@ -import { defineConfig } from 'vitest/config'; +// Copyright 2025 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +import type { ResolvedConfig, Vite } from 'vitest/node'; import { VitestPackageInstaller } from 'vitest/node'; -import path from 'path'; -import { fileURLToPath } from 'url'; -import { createRequire } from 'module'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { createRequire } from 'node:module'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const require = createRequire(import.meta.url); -async function ensurePackagesInstalled() { - const installer = new VitestPackageInstaller(); - const installed = await installer.ensureInstalled( - 'jsdom', - process.cwd(), - ); - if (!installed) { - console.log('ReactLynx Testing Library requires jsdom to be installed.'); - process.exit(1); - } -} +export interface TestingLibraryOptions { + /** + * The package name of the ReactLynx runtime package. + * + * @default `@lynx-js/react` + */ + runtimePkgName?: string; -/** - * @returns {import('vitest/config').ViteUserConfig} - */ -export const createVitestConfig = async (options) => { - await ensurePackagesInstalled(); + /** + * The engine version to use for the transform. + * + * @default `''` + */ + engineVersion?: string; + /** + * Enable experimental React Compiler support. + * + * Requires `@babel/core`, `babel-plugin-react-compiler`, + * `@babel/plugin-syntax-jsx`, and `@babel/plugin-syntax-typescript` + * to be installed in your project. + * + * @default `false` + */ + experimental_enableReactCompiler?: boolean; +} + +export function testingLibraryPlugin( + options?: TestingLibraryOptions, +): Vite.Plugin[] { const runtimeOSSPkgName = '@lynx-js/react'; const runtimePkgName = options?.runtimePkgName ?? runtimeOSSPkgName; - const runtimeDir = path.dirname(require.resolve(`${runtimePkgName}/package.json`)); + const runtimeDir = path.dirname( + require.resolve(`${runtimePkgName}/package.json`), + ); const runtimeOSSDir = path.dirname( require.resolve(`${runtimeOSSPkgName}/package.json`, { paths: [runtimeDir, __dirname], }), ); + const preactDir = path.dirname( require.resolve('preact/package.json', { paths: [runtimeOSSDir], }), ); - const generateAlias = (pkgName, pkgDir, resolveDir) => { - const pkgExports = require(path.join(pkgDir, 'package.json')).exports; - const pkgAlias = []; - Object.keys(pkgExports).forEach((key) => { - const name = path.posix.join(pkgName, key); - pkgAlias.push({ - find: new RegExp('^' + name + '$'), - replacement: require.resolve(name, { - paths: [resolveDir, __dirname], - }), - }); - }); - return pkgAlias; - }; - - const runtimeOSSAlias = generateAlias(runtimeOSSPkgName, runtimeOSSDir, runtimeDir); - let runtimeAlias = []; + const runtimeOSSAlias = generateAlias( + runtimeOSSPkgName, + runtimeOSSDir, + runtimeDir, + ); + let runtimeAlias: Vite.Alias[] = []; if (runtimePkgName !== runtimeOSSPkgName) { runtimeAlias = generateAlias(runtimePkgName, runtimeDir, __dirname); } @@ -85,11 +92,11 @@ export const createVitestConfig = async (options) => { }, ]; - function transformReactCompilerPlugin() { - let rootContext, compilerDeps, babel; + function transformReactCompilerPlugin(): Vite.Plugin { + let rootContext: string, compilerDeps: ReturnType, babel: any; - function resolveCompilerDeps(rootContext) { - const missingBabelPackages = []; + function resolveCompilerDeps(rootContext: string) { + const missingBabelPackages: string[] = []; const [ babelPath, babelPluginReactCompilerPath, @@ -132,9 +139,9 @@ export const createVitestConfig = async (options) => { name: 'transformReactCompilerPlugin', enforce: 'pre', config(config) { - rootContext = config.root; + rootContext = config.root!; - const reactCompilerRuntimeAlias = []; + const reactCompilerRuntimeAlias: Vite.Alias[] = []; try { reactCompilerRuntimeAlias.push( { @@ -150,14 +157,15 @@ export const createVitestConfig = async (options) => { }, ); } catch (e) { - // console.log('react-compiler-runtime not found, skip alias'); + // react-compiler-runtime not found, skip alias } + // @ts-ignore config.test.alias.push(...reactCompilerRuntimeAlias); compilerDeps = resolveCompilerDeps(rootContext); const { babelPath } = compilerDeps; - babel = require(babelPath); + babel = require(babelPath!); }, async transform(sourceText, sourcePath) { if (/\.(?:jsx|tsx)$/.test(sourcePath)) { @@ -192,7 +200,7 @@ export const createVitestConfig = async (options) => { ); } } catch (e) { - this.error(e); + this.error(e as Error); } } @@ -201,10 +209,15 @@ export const createVitestConfig = async (options) => { }; } - function transformReactLynxPlugin() { + let config: ResolvedConfig; + + function transformReactLynxPlugin(): Vite.Plugin { return { name: 'transformReactLynxPlugin', enforce: 'pre', + async buildStart() { + await ensurePackagesInstalled(); + }, transform(sourceText, sourcePath) { const id = sourcePath; // Only transform JS files @@ -214,12 +227,11 @@ export const createVitestConfig = async (options) => { const { transformReactLynxSync } = require( '@lynx-js/react/transform', - ); + ) as typeof import('@lynx-js/react/transform'); // relativePath should be stable between different runs with different cwd - const relativePath = normalizeSlashes(path.relative( - __dirname, - sourcePath, - )); + const relativePath = normalizeSlashes( + path.relative(config.root, sourcePath), + ); const basename = path.basename(sourcePath); const result = transformReactLynxSync(sourceText, { mode: 'test', @@ -252,60 +264,88 @@ export const createVitestConfig = async (options) => { refresh: false, cssScope: false, }); + if (result.errors.length > 0) { // https://rollupjs.org/plugin-development/#this-error - result.errors.forEach(error => { - this.error( - error.text, - error.location, - ); + result.errors.forEach((error) => { + this.error(error.text ?? 'Unknown error', { + line: 1, + column: 1, + ...error.location, + }); }); } if (result.warnings.length > 0) { - result.warnings.forEach(warning => { - this.warn( - warning.text, - warning.location, - ); + result.warnings.forEach((warning) => { + this.warn(warning.text ?? 'Unknown warning', { + line: 1, + column: 1, + ...warning.location, + }); }); } return { code: result.code, - map: result.map, + map: result.map!, }; }, + config: () => ({ + test: { + environment: require.resolve( + `${runtimeOSSDir}/testing-library/dist/env/vitest`, + ), + globals: true, + setupFiles: [ + require.resolve('../setupFiles/vitest'), + ], + alias: [...runtimeOSSAlias, ...runtimeAlias, ...preactAlias, ...reactAlias], + }, + }), + configResolved(_config) { + // @ts-ignore + config = _config; + }, }; } - return defineConfig({ - server: { - fs: { - allow: [ - path.join(__dirname, '..'), - ], - }, - }, - plugins: [ - ...(options?.experimental_enableReactCompiler - ? [ - transformReactCompilerPlugin(), - ] - : []), - transformReactLynxPlugin(), - ], - test: { - environment: require.resolve( - './env/vitest', - ), - globals: true, - setupFiles: [path.join(__dirname, 'vitest-global-setup')], - alias: [...runtimeOSSAlias, ...runtimeAlias, ...preactAlias, ...reactAlias], - include: options?.include ?? ['src/**/*.test.{js,jsx,ts,tsx}'], - }, + return [ + ...(options?.experimental_enableReactCompiler + ? [transformReactCompilerPlugin()] + : []), + transformReactLynxPlugin(), + ]; +} + +async function ensurePackagesInstalled() { + const installer = new VitestPackageInstaller(); + const installed = await installer.ensureInstalled('jsdom', process.cwd()); + if (!installed) { + console.log('ReactLynx Testing Library requires jsdom to be installed.'); + process.exit(1); + } +} + +function generateAlias(pkgName: string, pkgDir: string, resolveDir: string) { + const pkgExports = require(path.join(pkgDir, 'package.json')).exports; + if (!pkgExports || typeof pkgExports !== 'object') { + return []; + } + const pkgAlias: Vite.Alias[] = []; + Object.keys(pkgExports).forEach((key) => { + const name = path.posix.join(pkgName, key); + // Escape special regex characters in the package name + const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + pkgAlias.push({ + find: new RegExp('^' + escapedName + '$'), + replacement: require.resolve(name, { + paths: [resolveDir, __dirname], + }), + }); }); -}; + return pkgAlias; +} -function normalizeSlashes(file) { +function normalizeSlashes(file: string) { return file.replaceAll(path.win32.sep, '/'); } diff --git a/packages/react/testing-library/src/setupFiles/common/bootstrap.js b/packages/react/testing-library/src/setupFiles/common/bootstrap.js new file mode 100644 index 0000000000..f85071e11f --- /dev/null +++ b/packages/react/testing-library/src/setupFiles/common/bootstrap.js @@ -0,0 +1,6 @@ +globalThis.onInjectMainThreadGlobals( + globalThis.lynxTestingEnv.mainThread.globalThis, +); +globalThis.onInjectBackgroundThreadGlobals( + globalThis.lynxTestingEnv.backgroundThread.globalThis, +); diff --git a/packages/react/testing-library/src/vitest-global-setup.js b/packages/react/testing-library/src/setupFiles/common/runtime-setup.js similarity index 68% rename from packages/react/testing-library/src/vitest-global-setup.js rename to packages/react/testing-library/src/setupFiles/common/runtime-setup.js index 70e627e480..3f51d7531f 100644 --- a/packages/react/testing-library/src/vitest-global-setup.js +++ b/packages/react/testing-library/src/setupFiles/common/runtime-setup.js @@ -1,25 +1,25 @@ import { options } from 'preact'; import { expect } from 'vitest'; -import { BackgroundSnapshotInstance } from '../../runtime/lib/backgroundSnapshot.js'; -import { clearCommitTaskId, replaceCommitHook } from '../../runtime/lib/lifecycle/patch/commit.js'; -import { deinitGlobalSnapshotPatch } from '../../runtime/lib/lifecycle/patch/snapshotPatch.js'; -import { injectUpdateMainThread } from '../../runtime/lib/lifecycle/patch/updateMainThread.js'; -import { injectUpdateMTRefInitValue } from '../../runtime/lib/worklet/ref/updateInitValue.js'; -import { injectCalledByNative } from '../../runtime/lib/lynx/calledByNative.js'; -import { flushDelayedLifecycleEvents, injectTt } from '../../runtime/lib/lynx/tt.js'; -import { initElementPAPICallAlog } from '../../runtime/lib/alog/elementPAPICall.js'; -import { addCtxNotFoundEventListener } from '../../runtime/lib/lifecycle/patch/error.js'; -import { setRoot } from '../../runtime/lib/root.js'; +import { BackgroundSnapshotInstance } from '../../../../runtime/lib/backgroundSnapshot.js'; +import { clearCommitTaskId, replaceCommitHook } from '../../../../runtime/lib/lifecycle/patch/commit.js'; +import { deinitGlobalSnapshotPatch } from '../../../../runtime/lib/lifecycle/patch/snapshotPatch.js'; +import { injectUpdateMainThread } from '../../../../runtime/lib/lifecycle/patch/updateMainThread.js'; +import { injectUpdateMTRefInitValue } from '../../../../runtime/lib/worklet/ref/updateInitValue.js'; +import { injectCalledByNative } from '../../../../runtime/lib/lynx/calledByNative.js'; +import { flushDelayedLifecycleEvents, injectTt } from '../../../../runtime/lib/lynx/tt.js'; +import { initElementPAPICallAlog } from '../../../../runtime/lib/alog/elementPAPICall.js'; +import { addCtxNotFoundEventListener } from '../../../../runtime/lib/lifecycle/patch/error.js'; +import { setRoot } from '../../../../runtime/lib/root.js'; import { SnapshotInstance, backgroundSnapshotInstanceManager, snapshotInstanceManager, -} from '../../runtime/lib/snapshot.js'; -import { destroyWorklet } from '../../runtime/lib/worklet/destroy.js'; -import { initApiEnv } from '../../worklet-runtime/lib/api/lynxApi.js'; -import { initEventListeners } from '../../worklet-runtime/lib/listeners.js'; -import { initWorklet } from '../../worklet-runtime/lib/workletRuntime.js'; +} from '../../../../runtime/lib/snapshot.js'; +import { destroyWorklet } from '../../../../runtime/lib/worklet/destroy.js'; +import { initApiEnv } from '../../../../worklet-runtime/lib/api/lynxApi.js'; +import { initEventListeners } from '../../../../worklet-runtime/lib/listeners.js'; +import { initWorklet } from '../../../../worklet-runtime/lib/workletRuntime.js'; expect.addSnapshotSerializer({ test(val) { @@ -110,8 +110,12 @@ globalThis.onInjectMainThreadGlobals = (target) => { target.globalPipelineOptions = undefined; - if (typeof __ALOG_ELEMENT_API__ !== 'undefined' && __ALOG_ELEMENT_API__) { + if ( + typeof target.__ALOG_ELEMENT_API__ !== 'undefined' && target.__ALOG_ELEMENT_API__ + && !target.__initElementPAPICallAlogInjected + ) { initElementPAPICallAlog(target); + target.__initElementPAPICallAlogInjected = true; } }; globalThis.onInjectBackgroundThreadGlobals = (target) => { @@ -146,13 +150,13 @@ globalThis.onInjectBackgroundThreadGlobals = (target) => { target._document = setupBackgroundDocument({}); target.globalPipelineOptions = undefined; - target.lynx.requireModuleAsync = async (url, callback) => { - try { - callback(null, await __vite_ssr_dynamic_import__(url)); - } catch (err) { - callback(err, null); - } - }; + // TODO: can we only inject to target(mainThread.globalThis) instead of globalThis? + // packages/react/runtime/src/lynx.ts + // intercept lynxCoreInject assignments to lynxTestingEnv.backgroundThread.globalThis.lynxCoreInject + const oldLynxCoreInject = globalThis.lynxCoreInject; + globalThis.lynxCoreInject = target.lynxCoreInject; + injectTt(); + globalThis.lynxCoreInject = oldLynxCoreInject; // re-init global snapshot patch to undefined deinitGlobalSnapshotPatch(); @@ -189,10 +193,3 @@ globalThis.onSwitchedToBackgroundThread = () => { setRoot(globalThis.__root); options.document = globalThis._document; }; - -globalThis.onInjectMainThreadGlobals( - globalThis.lynxTestingEnv.mainThread.globalThis, -); -globalThis.onInjectBackgroundThreadGlobals( - globalThis.lynxTestingEnv.backgroundThread.globalThis, -); diff --git a/packages/react/testing-library/src/setupFiles/inner/rstest.js b/packages/react/testing-library/src/setupFiles/inner/rstest.js new file mode 100644 index 0000000000..6f8c194217 --- /dev/null +++ b/packages/react/testing-library/src/setupFiles/inner/rstest.js @@ -0,0 +1,21 @@ +import { LynxTestingEnv } from '../../env'; + +global.jsdom = { + window, +}; +const lynxTestingEnv = new LynxTestingEnv(); +global.lynxTestingEnv = lynxTestingEnv; + +const { + onInjectBackgroundThreadGlobals, +} = globalThis; + +globalThis.onInjectBackgroundThreadGlobals = (target) => { + if (onInjectBackgroundThreadGlobals) { + onInjectBackgroundThreadGlobals(target); + } + + target.lynx.requireModuleAsync = async (url, callback) => { + throw new Error('lynx.requireModuleAsync not implemented for rstest'); + }; +}; diff --git a/packages/react/testing-library/src/setupFiles/inner/vitest.js b/packages/react/testing-library/src/setupFiles/inner/vitest.js new file mode 100644 index 0000000000..b8421fa88f --- /dev/null +++ b/packages/react/testing-library/src/setupFiles/inner/vitest.js @@ -0,0 +1,17 @@ +const { + onInjectBackgroundThreadGlobals, +} = globalThis; + +globalThis.onInjectBackgroundThreadGlobals = (target) => { + if (onInjectBackgroundThreadGlobals) { + onInjectBackgroundThreadGlobals(target); + } + + target.lynx.requireModuleAsync = async (url, callback) => { + try { + callback(null, await __vite_ssr_dynamic_import__(url)); + } catch (err) { + callback(err, null); + } + }; +}; diff --git a/packages/react/testing-library/src/setupFiles/rstest.js b/packages/react/testing-library/src/setupFiles/rstest.js new file mode 100644 index 0000000000..f938cfa636 --- /dev/null +++ b/packages/react/testing-library/src/setupFiles/rstest.js @@ -0,0 +1,4 @@ +import '../env/rstest.js'; +import './common/runtime-setup.js'; +import './inner/rstest.js'; +import './common/bootstrap.js'; diff --git a/packages/react/testing-library/src/setupFiles/vitest.js b/packages/react/testing-library/src/setupFiles/vitest.js new file mode 100644 index 0000000000..a1080d35a5 --- /dev/null +++ b/packages/react/testing-library/src/setupFiles/vitest.js @@ -0,0 +1,3 @@ +import './common/runtime-setup.js'; +import './inner/vitest.js'; +import './common/bootstrap.js'; diff --git a/packages/react/testing-library/src/vitest.config.ts b/packages/react/testing-library/src/vitest.config.ts new file mode 100644 index 0000000000..e3b3094711 --- /dev/null +++ b/packages/react/testing-library/src/vitest.config.ts @@ -0,0 +1,11 @@ +import { defineConfig, type ViteUserConfig } from 'vitest/config'; +import { vitestTestingLibraryPlugin } from './plugins/index.js'; +import type { TestingLibraryOptions } from './plugins/index.js'; + +export function createVitestConfig(options?: TestingLibraryOptions): ViteUserConfig { + return defineConfig({ + plugins: [ + ...vitestTestingLibraryPlugin(options), + ], + }); +} diff --git a/packages/react/testing-library/types/vitest-config.d.ts b/packages/react/testing-library/types/vitest-config.d.ts deleted file mode 100644 index 3b20fe6013..0000000000 --- a/packages/react/testing-library/types/vitest-config.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { ViteUserConfig } from 'vitest/config.js'; - -export interface CreateVitestConfigOptions { - /** - * The package name of the ReactLynx runtime package. - * - * @defaultValue `@lynx-js/react` - */ - runtimePkgName?: string; - /** - * Enable React Compiler for this build. - * - * @link https://react.dev/learn/react-compiler - * - * @defaultValue false - */ - experimental_enableReactCompiler?: boolean; -} - -export function createVitestConfig(options?: CreateVitestConfigOptions): Promise; diff --git a/packages/react/testing-library/vitest-polyfill.cjs b/packages/react/testing-library/vitest-polyfill.cjs new file mode 100644 index 0000000000..647f2e4a8d --- /dev/null +++ b/packages/react/testing-library/vitest-polyfill.cjs @@ -0,0 +1,6 @@ +// in order to make our test case work for +// both vitest and rstest, we need to alias +// `vitest` to `@rstest/core` + +global['@rstest/core'].vi = global['@rstest/core'].rs; +module.exports = global['@rstest/core']; diff --git a/packages/react/testing-library/vitest.config.ts b/packages/react/testing-library/vitest.config.ts index 65fcb43cdb..cb9cf11351 100644 --- a/packages/react/testing-library/vitest.config.ts +++ b/packages/react/testing-library/vitest.config.ts @@ -3,11 +3,11 @@ import { createVitestConfig } from './dist/vitest.config'; const defaultConfig = await createVitestConfig({ runtimePkgName: '@lynx-js/react', - include: ['src/**/*.test.{js,jsx,ts,tsx}', '!src/__tests__/3.1/**/*.{js,jsx,ts,tsx}'], }); const config = defineConfig({ test: { name: 'react/testing-library', + include: ['src/**/*.test.{js,jsx,ts,tsx}', '!src/__tests__/3.1/**/*.{js,jsx,ts,tsx}'], }, }); diff --git a/packages/rspeedy/create-rspeedy/package.json b/packages/rspeedy/create-rspeedy/package.json index 47f3de1050..8d8b6cf119 100644 --- a/packages/rspeedy/create-rspeedy/package.json +++ b/packages/rspeedy/create-rspeedy/package.json @@ -42,7 +42,8 @@ "@lynx-js/react": "workspace:^", "@lynx-js/react-rsbuild-plugin": "workspace:^", "@lynx-js/rspeedy": "workspace:^", - "@rsbuild/plugin-type-check": "1.3.4" + "@rsbuild/plugin-type-check": "1.3.4", + "@rstest/core": "catalog:rstest" }, "engines": { "node": ">=18" diff --git a/packages/rspeedy/create-rspeedy/src/index.ts b/packages/rspeedy/create-rspeedy/src/index.ts index e96f4d98c5..2091d22f36 100644 --- a/packages/rspeedy/create-rspeedy/src/index.ts +++ b/packages/rspeedy/create-rspeedy/src/index.ts @@ -8,7 +8,7 @@ import path from 'node:path' import { fileURLToPath } from 'node:url' import type { Argv } from 'create-rstack' -import { checkCancel, create, multiselect, select } from 'create-rstack' +import { checkCancel, create, select } from 'create-rstack' type LANG = 'js' | 'ts' @@ -67,22 +67,31 @@ async function getTemplateName({ template }: Argv) { }), ) - const tools = checkCancel( - await multiselect({ - message: - 'Select development tools (Use to select, to continue)', - required: false, + const tools = [] + const testingTools = checkCancel( + await select({ + message: 'Select testing framework', options: [ { value: 'vitest-rltl', - label: 'Add ReactLynx Testing Library for unit testing', + label: 'Vitest', + }, + { + value: 'rstest-rltl', + label: 'Rstest', + hint: 'recommended', + }, + { + value: '', + label: 'Skip', }, ], - initialValues: [ - 'vitest-rltl', - ], + initialValue: 'vitest-rltl', }), ) + if (testingTools) { + tools.push(testingTools) + } return composeTemplateName({ template: 'react', diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/lynx.config.js b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/lynx.config.js new file mode 100644 index 0000000000..17d2cecc5c --- /dev/null +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/lynx.config.js @@ -0,0 +1,19 @@ +import { defineConfig } from '@lynx-js/rspeedy' + +import { pluginQRCode } from '@lynx-js/qrcode-rsbuild-plugin' +import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin' + +export default defineConfig({ + source: { + entry: './src/index.jsx', + }, + plugins: [ + pluginQRCode({ + schema(url) { + // We use `?fullscreen=true` to open the page in LynxExplorer in full screen mode + return `${url}?fullscreen=true` + }, + }), + pluginReactLynx(), + ], +}) diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/package.json b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/package.json new file mode 100644 index 0000000000..4a1c98b1b7 --- /dev/null +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/package.json @@ -0,0 +1,25 @@ +{ + "name": "rspeedy-react-js", + "version": "0.0.0", + "type": "module", + "scripts": { + "build": "rspeedy build", + "dev": "rspeedy dev", + "preview": "rspeedy preview", + "test": "rstest run" + }, + "dependencies": { + "@lynx-js/react": "workspace:*" + }, + "devDependencies": { + "@lynx-js/qrcode-rsbuild-plugin": "workspace:*", + "@lynx-js/react-rsbuild-plugin": "workspace:*", + "@lynx-js/rspeedy": "workspace:*", + "@rstest/core": "catalog:rstest", + "@testing-library/jest-dom": "^6.6.3", + "jsdom": "^26.1.0" + }, + "engines": { + "node": ">=18" + } +} diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/rstest.config.js b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/rstest.config.js new file mode 100644 index 0000000000..ec22dfed14 --- /dev/null +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/rstest.config.js @@ -0,0 +1,11 @@ +import { defineConfig } from '@rstest/core' +import lynxConfig from './lynx.config.js' + +export default defineConfig({ + ...lynxConfig, + testEnvironment: 'jsdom', + setupFiles: [ + require.resolve('@lynx-js/react/testing-library/setupFiles/rstest'), + ], + globals: true, +}) diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/App.jsx b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/App.jsx new file mode 100644 index 0000000000..7618894b24 --- /dev/null +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/App.jsx @@ -0,0 +1,53 @@ +import { useCallback, useEffect, useState } from '@lynx-js/react' + +import './App.css' +import arrow from './assets/arrow.png' +import lynxLogo from './assets/lynx-logo.png' +import reactLynxLogo from './assets/react-logo.png' + +export function App(props) { + const [alterLogo, setAlterLogo] = useState(false) + + useEffect(() => { + console.info('Hello, ReactLynx') + props.onMounted?.() + }, []) + + const onTap = useCallback(() => { + 'background only' + setAlterLogo(!alterLogo) + }, [alterLogo]) + + return ( + + + + + + {alterLogo + ? + : } + + React + on Lynx + + + + Tap the logo and have fun! + + Edit + {' src/App.tsx '} + + to see updates! + + + + + + ) +} diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/__tests__/index.test.jsx b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/__tests__/index.test.jsx new file mode 100644 index 0000000000..e7f2736b0b --- /dev/null +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/__tests__/index.test.jsx @@ -0,0 +1,102 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +import '@testing-library/jest-dom' +import { expect, test, rstest } from '@rstest/core' +import { render, getQueriesForElement } from '@lynx-js/react/testing-library' + +import { App } from '../App.jsx' + +test('App', async () => { + const cb = rstest.fn() + + render( + { + cb(`__MAIN_THREAD__: ${__MAIN_THREAD__}`) + }} + />, + ) + expect(cb).toBeCalledTimes(1) + expect(cb.mock.calls).toMatchInlineSnapshot(` + [ + [ + "__MAIN_THREAD__: false", + ], + ] + `) + expect(elementTree.root).toMatchInlineSnapshot(` + + + + + + + + + Tap the logo and have fun! + + + Edit + + src/App.tsx + + to see updates! + + + + + + + `) + const { + findByText, + } = getQueriesForElement(elementTree.root) + const element = await findByText('Tap the logo and have fun!') + expect(element).toBeInTheDocument() + expect(element).toMatchInlineSnapshot(` + + Tap the logo and have fun! + + `) +}) diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/index.jsx b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/index.jsx new file mode 100644 index 0000000000..ab7f2c6da0 --- /dev/null +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/index.jsx @@ -0,0 +1,9 @@ +import { root } from '@lynx-js/react' + +import { App } from './App.jsx' + +root.render() + +if (import.meta.webpackHot) { + import.meta.webpackHot.accept() +} diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/lynx.config.ts b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/lynx.config.ts new file mode 100644 index 0000000000..057fa166d2 --- /dev/null +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/lynx.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from '@lynx-js/rspeedy' + +import { pluginQRCode } from '@lynx-js/qrcode-rsbuild-plugin' +import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin' +import { pluginTypeCheck } from '@rsbuild/plugin-type-check' + +export default defineConfig({ + plugins: [ + pluginQRCode({ + schema(url) { + // We use `?fullscreen=true` to open the page in LynxExplorer in full screen mode + return `${url}?fullscreen=true` + }, + }), + pluginReactLynx(), + pluginTypeCheck(), + ], +}) diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/package.json b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/package.json new file mode 100644 index 0000000000..643273986b --- /dev/null +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/package.json @@ -0,0 +1,29 @@ +{ + "name": "rspeedy-react-ts", + "version": "0.0.0", + "type": "module", + "scripts": { + "build": "rspeedy build", + "dev": "rspeedy dev", + "preview": "rspeedy preview", + "test": "rstest run" + }, + "dependencies": { + "@lynx-js/react": "workspace:*" + }, + "devDependencies": { + "@lynx-js/qrcode-rsbuild-plugin": "workspace:*", + "@lynx-js/react-rsbuild-plugin": "workspace:*", + "@lynx-js/rspeedy": "workspace:*", + "@lynx-js/types": "3.3.0", + "@rsbuild/plugin-type-check": "1.2.3", + "@rstest/core": "catalog:rstest", + "@testing-library/jest-dom": "^6.6.3", + "@types/react": "^18.3.23", + "jsdom": "^26.1.0", + "typescript": "~5.8.3" + }, + "engines": { + "node": ">=18" + } +} diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/rstest.config.ts b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/rstest.config.ts new file mode 100644 index 0000000000..788a77ba69 --- /dev/null +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/rstest.config.ts @@ -0,0 +1,11 @@ +import { defineConfig, type RstestConfig } from '@rstest/core' +import lynxConfig from './lynx.config.js' + +export default defineConfig({ + ...lynxConfig as RstestConfig, + testEnvironment: 'jsdom', + setupFiles: [ + require.resolve('@lynx-js/react/testing-library/setupFiles/rstest'), + ], + globals: true, +}) diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/App.tsx b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/App.tsx new file mode 100644 index 0000000000..fb6ed15fc9 --- /dev/null +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/App.tsx @@ -0,0 +1,55 @@ +import { useCallback, useEffect, useState } from '@lynx-js/react' + +import './App.css' +import arrow from './assets/arrow.png' +import lynxLogo from './assets/lynx-logo.png' +import reactLynxLogo from './assets/react-logo.png' + +export function App(props: { + onMounted?: () => void +}) { + const [alterLogo, setAlterLogo] = useState(false) + + useEffect(() => { + console.info('Hello, ReactLynx') + props.onMounted?.() + }, []) + + const onTap = useCallback(() => { + 'background only' + setAlterLogo(!alterLogo) + }, [alterLogo]) + + return ( + + + + + + {alterLogo + ? + : } + + React + on Lynx + + + + Tap the logo and have fun! + + Edit + {' src/App.tsx '} + + to see updates! + + + + + + ) +} diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/__tests__/index.test.tsx b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/__tests__/index.test.tsx new file mode 100644 index 0000000000..c7c43c3cb5 --- /dev/null +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/__tests__/index.test.tsx @@ -0,0 +1,102 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +import '@testing-library/jest-dom' +import { expect, test, rstest } from '@rstest/core' +import { render, getQueriesForElement } from '@lynx-js/react/testing-library' + +import { App } from '../App.js' + +test('App', async () => { + const cb = rstest.fn() + + render( + { + cb(`__MAIN_THREAD__: ${__MAIN_THREAD__}`) + }} + />, + ) + expect(cb).toBeCalledTimes(1) + expect(cb.mock.calls).toMatchInlineSnapshot(` + [ + [ + "__MAIN_THREAD__: false", + ], + ] + `) + expect(elementTree.root).toMatchInlineSnapshot(` + + + + + + + + + Tap the logo and have fun! + + + Edit + + src/App.tsx + + to see updates! + + + + + + + `) + const { + findByText, + } = getQueriesForElement(elementTree.root!) + const element = await findByText('Tap the logo and have fun!') + expect(element).toBeInTheDocument() + expect(element).toMatchInlineSnapshot(` + + Tap the logo and have fun! + + `) +}) diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/index.tsx b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/index.tsx new file mode 100644 index 0000000000..465aeacec6 --- /dev/null +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/index.tsx @@ -0,0 +1,9 @@ +import { root } from '@lynx-js/react' + +import { App } from './App.js' + +root.render() + +if (import.meta.webpackHot) { + import.meta.webpackHot.accept() +} diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/rspeedy-env.d.ts b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/rspeedy-env.d.ts new file mode 100644 index 0000000000..1c813a68b0 --- /dev/null +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/src/rspeedy-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/tsconfig.json b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/tsconfig.json new file mode 100644 index 0000000000..d9460d5509 --- /dev/null +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "@lynx-js/react", + + "module": "node18", + "moduleResolution": "node16", + + "strict": true, + "isolatedModules": true, + "verbatimModuleSyntax": true, + + "esModuleInterop": true, + "skipLibCheck": true, + }, + "exclude": ["dist/"], +} diff --git a/packages/rspeedy/create-rspeedy/template-react-vitest-rltl-js/vitest.config.js b/packages/rspeedy/create-rspeedy/template-react-vitest-rltl-js/vitest.config.js index 98425e53c0..7af00982cb 100644 --- a/packages/rspeedy/create-rspeedy/template-react-vitest-rltl-js/vitest.config.js +++ b/packages/rspeedy/create-rspeedy/template-react-vitest-rltl-js/vitest.config.js @@ -1,7 +1,7 @@ import { defineConfig, mergeConfig } from 'vitest/config' import { createVitestConfig } from '@lynx-js/react/testing-library/vitest-config' -const defaultConfig = await createVitestConfig() +const defaultConfig = createVitestConfig() const config = defineConfig({ test: {}, }) diff --git a/packages/rspeedy/create-rspeedy/template-react-vitest-rltl-ts/vitest.config.ts b/packages/rspeedy/create-rspeedy/template-react-vitest-rltl-ts/vitest.config.ts index 98425e53c0..7af00982cb 100644 --- a/packages/rspeedy/create-rspeedy/template-react-vitest-rltl-ts/vitest.config.ts +++ b/packages/rspeedy/create-rspeedy/template-react-vitest-rltl-ts/vitest.config.ts @@ -1,7 +1,7 @@ import { defineConfig, mergeConfig } from 'vitest/config' import { createVitestConfig } from '@lynx-js/react/testing-library/vitest-config' -const defaultConfig = await createVitestConfig() +const defaultConfig = createVitestConfig() const config = defineConfig({ test: {}, }) diff --git a/packages/rspeedy/plugin-react/package.json b/packages/rspeedy/plugin-react/package.json index 1f9619c3f2..300f6fe13e 100644 --- a/packages/rspeedy/plugin-react/package.json +++ b/packages/rspeedy/plugin-react/package.json @@ -44,7 +44,8 @@ "@lynx-js/runtime-wrapper-webpack-plugin": "workspace:*", "@lynx-js/template-webpack-plugin": "workspace:*", "@lynx-js/use-sync-external-store": "workspace:*", - "background-only": "workspace:^" + "background-only": "workspace:^", + "tiny-invariant": "^1.3.3" }, "devDependencies": { "@lynx-js/config-rsbuild-plugin": "workspace:*", diff --git a/packages/rspeedy/plugin-react/src/loaders.ts b/packages/rspeedy/plugin-react/src/loaders.ts index 2bc826a5fe..03d77a5a94 100644 --- a/packages/rspeedy/plugin-react/src/loaders.ts +++ b/packages/rspeedy/plugin-react/src/loaders.ts @@ -7,19 +7,65 @@ import { LAYERS, ReactWebpackPlugin } from '@lynx-js/react-webpack-plugin' import type { PluginReactLynxOptions } from './pluginReactLynx.js' -export function applyLoaders( +function getLoaderOptions( api: RsbuildPluginAPI, options: Required, -): void { + isMainThread = false, +) { + const { output } = api.getRsbuildConfig() + + const inlineSourcesContent: boolean = output?.sourceMap === true || !( + // `false` + output?.sourceMap === false + // `false` + || output?.sourceMap?.js === false + // explicitly disable source content + || output?.sourceMap?.js?.includes('nosources') + ) + const { compat, enableRemoveCSSScope, - shake, defineDCE, - engineVersion, + shake, + experimental_isLazyBundle, } = options + return { + compat, + enableRemoveCSSScope, + isDynamicComponent: experimental_isLazyBundle, + inlineSourcesContent, + defineDCE, + ...isMainThread + ? { + shake, + } + : {}, + } +} + +const TESTING_RULE_NAME = 'react:testing' +export function applyTestingLoaders( + api: RsbuildPluginAPI, + options: Required, +): void { + api.modifyBundlerChain((chain, { CHAIN_ID }) => { + const rule = chain.module.rules.get(CHAIN_ID.RULE.JS) + + rule + .use(TESTING_RULE_NAME) + .loader(ReactWebpackPlugin.loaders.TESTING) + .options(getLoaderOptions(api, options)) + .end() + }) +} + +export function applyLoaders( + api: RsbuildPluginAPI, + options: Required, +): void { api.modifyBundlerChain((chain, { CHAIN_ID }) => { const rule = chain.module.rules.get(CHAIN_ID.RULE.JS) // The Rsbuild default loaders: @@ -30,17 +76,6 @@ export function applyLoaders( // - Webpack: None const uses = rule.uses.entries() ?? {} - const { output } = api.getRsbuildConfig() - - const inlineSourcesContent: boolean = output?.sourceMap === true || !( - // `false` - output?.sourceMap === false - // `false` - || output?.sourceMap?.js === false - // explicitly disable source content - || output?.sourceMap?.js?.includes('nosources') - ) - const backgroundRule = rule.oneOf(LAYERS.BACKGROUND) // dprint-ignore backgroundRule @@ -50,14 +85,7 @@ export function applyLoaders( .end() .use(LAYERS.BACKGROUND) .loader(ReactWebpackPlugin.loaders.BACKGROUND) - .options({ - compat, - enableRemoveCSSScope, - isDynamicComponent: experimental_isLazyBundle, - inlineSourcesContent, - defineDCE, - engineVersion, - }) + .options(getLoaderOptions(api, options)) .end() const mainThreadRule = rule.oneOf(LAYERS.MAIN_THREAD) @@ -89,15 +117,7 @@ export function applyLoaders( }) .use(LAYERS.MAIN_THREAD) .loader(ReactWebpackPlugin.loaders.MAIN_THREAD) - .options({ - compat, - enableRemoveCSSScope, - inlineSourcesContent, - isDynamicComponent: experimental_isLazyBundle, - engineVersion, - shake, - defineDCE, - }) + .options(getLoaderOptions(api, options, true)) .end() // Clear the Rsbuild default loader. diff --git a/packages/rspeedy/plugin-react/src/pluginReactLynx.ts b/packages/rspeedy/plugin-react/src/pluginReactLynx.ts index c73b921824..cb34ef74c3 100644 --- a/packages/rspeedy/plugin-react/src/pluginReactLynx.ts +++ b/packages/rspeedy/plugin-react/src/pluginReactLynx.ts @@ -28,9 +28,10 @@ import { applyCSS } from './css.js' import { applyEntry } from './entry.js' import { applyGenerator } from './generator.js' import { applyLazy } from './lazy.js' -import { applyLoaders } from './loaders.js' +import { applyLoaders, applyTestingLoaders } from './loaders.js' import { applyNodeEnv } from './nodeEnv.js' import { applyRefresh } from './refresh.js' +import { applyRstest } from './rstest.js' import { applySplitChunksRule } from './splitChunks.js' import { applySWC } from './swc.js' import { applyUseSyncExternalStore } from './useSyncExternalStore.js' @@ -329,6 +330,7 @@ export function pluginReactLynx( pre: ['lynx:rsbuild:plugin-api', 'lynx:config'], setup(api) { const isRslib = api.context.callerName === 'rslib' + const isRstest = api.context.callerName === 'rstest' const exposedConfig = api.useExposed<{ config: Config }>( Symbol.for('lynx.config'), @@ -343,11 +345,20 @@ export function pluginReactLynx( }) } - applyCSS(api, resolvedOptions) + if (isRstest) { + applyRstest(api) + } + if (!isRstest) { + applyCSS(api, resolvedOptions) + } applyEntry(api, resolvedOptions) applyBackgroundOnly(api) applyGenerator(api, resolvedOptions) - applyLoaders(api, resolvedOptions) + if (isRstest) { + applyTestingLoaders(api, resolvedOptions) + } else { + applyLoaders(api, resolvedOptions) + } applyRefresh(api) applySplitChunksRule(api) applySWC(api) diff --git a/packages/rspeedy/plugin-react/src/rspeedy-api.ts b/packages/rspeedy/plugin-react/src/rspeedy-api.ts new file mode 100644 index 0000000000..5a2de15cc6 --- /dev/null +++ b/packages/rspeedy/plugin-react/src/rspeedy-api.ts @@ -0,0 +1,14 @@ +// Copyright 2025 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +import invariant from 'tiny-invariant' + +import type { ExposedAPI, RsbuildPluginAPI } from '@lynx-js/rspeedy' + +export function getRspeedyAPI(api: RsbuildPluginAPI): ExposedAPI { + const rspeedyAPI = api.useExposed( + Symbol.for('rspeedy.api'), + )! + invariant(rspeedyAPI, 'Should have rspeedy.api') + return rspeedyAPI +} diff --git a/packages/rspeedy/plugin-react/src/rstest.ts b/packages/rspeedy/plugin-react/src/rstest.ts new file mode 100644 index 0000000000..6797a23039 --- /dev/null +++ b/packages/rspeedy/plugin-react/src/rstest.ts @@ -0,0 +1,15 @@ +// Copyright 2025 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +import type { RsbuildPluginAPI } from '@rsbuild/core' + +export function applyRstest(api: RsbuildPluginAPI): void { + api.modifyRsbuildConfig((config, { mergeRsbuildConfig }) => { + return mergeRsbuildConfig({ + output: { + // Allow using Node.js internal modules in testing framework directly + externals: /^node:/, + }, + }, config) + }) +} diff --git a/packages/rspeedy/plugin-react/test/config.test.ts b/packages/rspeedy/plugin-react/test/config.test.ts index 3c0e9559ff..58b334bf76 100644 --- a/packages/rspeedy/plugin-react/test/config.test.ts +++ b/packages/rspeedy/plugin-react/test/config.test.ts @@ -2516,6 +2516,75 @@ describe('Config', () => { ).toBe('main/background.js') }) }) + + describe('callerName: rstest', async () => { + const { pluginReactLynx } = await import('../src/pluginReactLynx.js') + + const rsbuild = await createRspeedy({ + rspeedyConfig: { + plugins: [ + pluginReactLynx(), + ], + }, + callerName: 'rstest', + }) + const [config] = await rsbuild.initConfigs() + interface Rule { + test?: RegExp + use?: Array<{ loader: string }> + [key: string]: unknown + } + + const rules = config?.module?.rules as Rule[] | undefined + + test('css rules should be rsbuild default', () => { + expect( + rules?.filter((rule: Rule) => + rule + && typeof rule === 'object' + && rule.test + && rule.test.toString() === (/\.css$/).toString() + ).map((rule: Rule) => + (rule?.use?.map((u: { loader: string }) => u.loader)) ?? [] + ), + ).toMatchInlineSnapshot(` + [ + [ + "/node_modules//@rspack/core/dist/cssExtractLoader.js", + "/node_modules//@rsbuild/core/compiled/css-loader/index.js", + "builtin:lightningcss-loader", + ], + [ + "/node_modules//@rsbuild/core/compiled/css-loader/index.js", + "builtin:lightningcss-loader", + ], + [], + ] + `) + }) + test('js loaders should be testing loaders', () => { + expect( + rules?.filter((rule: Rule) => + rule + && typeof rule === 'object' + && rule.test + && rule.test.toString() + === (/\.(?:js|jsx|mjs|cjs|ts|tsx|mts|cts)$/).toString() + ).map((rule: Rule) => + (rule?.use?.map((u: { loader: string }) => u.loader)) ?? [] + ), + ).toMatchInlineSnapshot(` + [ + [ + "builtin:swc-loader", + "/packages/webpack/react-webpack-plugin/lib/loaders/testing.js", + ], + [], + [], + ] + `) + }) + }) }) describe('MPA Config', () => { diff --git a/packages/testing-library/examples/basic/package.json b/packages/testing-library/examples/basic/package.json index 5c0f9c0a15..999c960b68 100644 --- a/packages/testing-library/examples/basic/package.json +++ b/packages/testing-library/examples/basic/package.json @@ -6,6 +6,7 @@ "scripts": { "build": "rspeedy build", "dev": "rspeedy dev", + "rstest": "rstest", "test": "vitest", "test:type": "vitest --typecheck.only" }, diff --git a/packages/testing-library/examples/basic/rstest.config.ts b/packages/testing-library/examples/basic/rstest.config.ts new file mode 100644 index 0000000000..278ef168b2 --- /dev/null +++ b/packages/testing-library/examples/basic/rstest.config.ts @@ -0,0 +1,11 @@ +import { defineConfig, RstestConfig } from '@rstest/core'; +import lynxConfig from './lynx.config.js'; + +export default defineConfig({ + ...lynxConfig as RstestConfig, + testEnvironment: 'jsdom', + setupFiles: [ + require.resolve('@lynx-js/react/testing-library/setupFiles/rstest'), + ], + globals: true, +}); diff --git a/packages/testing-library/examples/basic/src/__tests__/index.test.tsx b/packages/testing-library/examples/basic/src/__tests__/index.test.tsx index d877834a08..1b6a7c4992 100644 --- a/packages/testing-library/examples/basic/src/__tests__/index.test.tsx +++ b/packages/testing-library/examples/basic/src/__tests__/index.test.tsx @@ -2,7 +2,6 @@ // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. import '@testing-library/jest-dom'; -import { expect, test, vi } from 'vitest'; import { render, getQueriesForElement } from '@lynx-js/react/testing-library'; // @ts-expect-error preact is aliased to the dep of @lynx-js/react import { Component as PreacComponent } from 'preact'; @@ -10,8 +9,15 @@ import { Component } from '@lynx-js/react'; import { App } from '../App.jsx'; +let fn; +if (typeof rstest !== 'undefined') { + fn = rstest.fn; +} else { + fn = vi.fn; +} + test('App', async () => { - const cb = vi.fn(); + const cb = fn(); render( +/// +/// diff --git a/packages/testing-library/examples/basic/vitest.config.ts b/packages/testing-library/examples/basic/vitest.config.ts index cbe326697f..242ad24f04 100644 --- a/packages/testing-library/examples/basic/vitest.config.ts +++ b/packages/testing-library/examples/basic/vitest.config.ts @@ -1,7 +1,7 @@ import { defineConfig, mergeConfig } from 'vitest/config'; import { createVitestConfig } from '@lynx-js/react/testing-library/vitest-config'; -const defaultConfig = await createVitestConfig({ +const defaultConfig = createVitestConfig({ runtimePkgName: '@lynx-js/react', }); const config = defineConfig({ diff --git a/packages/testing-library/testing-environment/src/index.ts b/packages/testing-library/testing-environment/src/index.ts index 8d2aa611ec..888363cd6e 100644 --- a/packages/testing-library/testing-environment/src/index.ts +++ b/packages/testing-library/testing-environment/src/index.ts @@ -5,11 +5,10 @@ * notably the {@link https://lynxjs.org/api/engine/element-api | Element PAPI} and {@link https://lynxjs.org/guide/spec#dual-threaded-model | Dual-threaded Model} for use with Node.js. */ -import EventEmitter from 'events'; +import EventEmitter from 'node:events'; import { JSDOM } from 'jsdom'; import { createGlobalThis, LynxGlobalThis } from './lynx/GlobalThis.js'; import { initElementTree, type LynxElement } from './lynx/ElementPAPI.js'; -import { Console } from 'console'; import { GlobalEventEmitter } from './lynx/GlobalEventEmitter.js'; export { initElementTree } from './lynx/ElementPAPI.js'; export type { LynxElement } from './lynx/ElementPAPI.js'; @@ -212,18 +211,6 @@ function createPolyfills() { }; } -function createPreconfiguredConsole() { - const console = new Console( - process.stdout, - process.stderr, - ); - console.profile = () => {}; - console.profileEnd = () => {}; - // @ts-expect-error Lynx has console.alog - console.alog = () => {}; - return console; -} - function injectMainThreadGlobals(target?: any, polyfills?: any) { __injectElementApi(target); @@ -301,7 +288,10 @@ function injectMainThreadGlobals(target?: any, polyfills?: any) { target.requestAnimationFrame = setTimeout; target.cancelAnimationFrame = clearTimeout; - target.console = createPreconfiguredConsole(); + target.console.profile = console.profile = () => {}; + target.console.profileEnd = console.profileEnd = () => {}; + // @ts-expect-error Lynx has console.alog + target.console.alog = console.alog = () => {}; target.__LoadLepusChunk = __LoadLepusChunk; @@ -449,7 +439,10 @@ function injectBackgroundThreadGlobals(target?: any, polyfills?: any) { target.requestAnimationFrame = setTimeout; target.cancelAnimationFrame = clearTimeout; - target.console = createPreconfiguredConsole(); + target.console.profile = console.profile = () => {}; + target.console.profileEnd = console.profileEnd = () => {}; + // @ts-expect-error Lynx has console.alog + target.console.alog = console.alog = () => {}; // TODO: user-configurable target.SystemInfo = { diff --git a/packages/use-sync-external-store/vitest.config.ts b/packages/use-sync-external-store/vitest.config.ts index 94b3576d8e..352dc3f58d 100644 --- a/packages/use-sync-external-store/vitest.config.ts +++ b/packages/use-sync-external-store/vitest.config.ts @@ -3,7 +3,7 @@ import type { UserWorkspaceConfig } from 'vitest/config'; import { createVitestConfig } from '@lynx-js/react/testing-library/vitest-config'; -const defaultConfig = await createVitestConfig(); +const defaultConfig = createVitestConfig(); const config: UserWorkspaceConfig = defineProject({ test: { diff --git a/packages/webpack/react-webpack-plugin/etc/react-webpack-plugin.api.md b/packages/webpack/react-webpack-plugin/etc/react-webpack-plugin.api.md index e7d777aa74..0ae9f26603 100644 --- a/packages/webpack/react-webpack-plugin/etc/react-webpack-plugin.api.md +++ b/packages/webpack/react-webpack-plugin/etc/react-webpack-plugin.api.md @@ -38,7 +38,7 @@ export class ReactWebpackPlugin { constructor(options?: ReactWebpackPluginOptions | undefined); apply(compiler: Compiler): void; static defaultOptions: Readonly>; - static loaders: Record; + static loaders: Record; } // @public diff --git a/packages/webpack/react-webpack-plugin/src/ReactWebpackPlugin.ts b/packages/webpack/react-webpack-plugin/src/ReactWebpackPlugin.ts index af12ce91f5..98e6b8704f 100644 --- a/packages/webpack/react-webpack-plugin/src/ReactWebpackPlugin.ts +++ b/packages/webpack/react-webpack-plugin/src/ReactWebpackPlugin.ts @@ -119,9 +119,10 @@ class ReactWebpackPlugin { * * @public */ - static loaders: Record = { + static loaders: Record = { BACKGROUND: require.resolve('../lib/loaders/background.js'), MAIN_THREAD: require.resolve('../lib/loaders/main-thread.js'), + TESTING: require.resolve('../lib/loaders/testing.js'), }; constructor( diff --git a/packages/webpack/react-webpack-plugin/src/loaders/options.ts b/packages/webpack/react-webpack-plugin/src/loaders/options.ts index 5c70935dda..b3b681164c 100644 --- a/packages/webpack/react-webpack-plugin/src/loaders/options.ts +++ b/packages/webpack/react-webpack-plugin/src/loaders/options.ts @@ -14,12 +14,12 @@ import type { } from '@lynx-js/react/transform'; const PLUGIN_NAME = 'react:webpack'; -const JSX_IMPORT_SOURCE = { +export const JSX_IMPORT_SOURCE = { MAIN_THREAD: '@lynx-js/react/lepus', BACKGROUND: '@lynx-js/react', }; const PUBLIC_RUNTIME_PKG = '@lynx-js/react'; -const RUNTIME_PKG = '@lynx-js/react/internal'; +export const RUNTIME_PKG = '@lynx-js/react/internal'; const OLD_RUNTIME_PKG = '@lynx-js/react-runtime'; const COMPONENT_PKG = '@lynx-js/react-components'; diff --git a/packages/webpack/react-webpack-plugin/src/loaders/testing.ts b/packages/webpack/react-webpack-plugin/src/loaders/testing.ts new file mode 100644 index 0000000000..443489c9fc --- /dev/null +++ b/packages/webpack/react-webpack-plugin/src/loaders/testing.ts @@ -0,0 +1,119 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +import { createRequire } from 'node:module'; +import path from 'node:path'; + +import type { LoaderContext } from '@rspack/core'; + +import { JSX_IMPORT_SOURCE, RUNTIME_PKG } from './options.js'; +import type { ReactLoaderOptions } from './options.js'; + +function normalizeSlashes(file: string) { + return file.replaceAll(path.win32.sep, '/'); +} + +function testingLoader( + this: LoaderContext, + content: string, +): void { + const require = createRequire(import.meta.url); + const { transformPath = '@lynx-js/react/transform' } = this.getOptions(); + const { transformReactLynxSync } = require( + transformPath, + ) as typeof import('@lynx-js/react/transform'); + const filename = normalizeSlashes( + path.relative( + this.rootContext, + this.resourcePath, + ), + ); + const result = transformReactLynxSync( + content, + { + mode: 'test', + pluginName: '', + filename: this.resourcePath, + sourcemap: true, + snapshot: { + preserveJsx: false, + runtimePkg: RUNTIME_PKG, + jsxImportSource: JSX_IMPORT_SOURCE.BACKGROUND, + filename, + target: 'MIXED', + }, + // snapshot: true, + directiveDCE: false, + defineDCE: false, + shake: false, + compat: false, + worklet: { + filename, + runtimePkg: RUNTIME_PKG, + target: 'MIXED', + }, + refresh: false, + cssScope: false, + }, + ); + + if (result.errors.length > 0) { + for (const error of result.errors) { + if (this.experiments?.emitDiagnostic) { + // Rspack with `emitDiagnostic` API + try { + this.experiments.emitDiagnostic({ + message: error.text!, + sourceCode: content, + location: { + line: error.location?.line ?? 1, + column: error.location?.column ?? 0, + length: error.location?.length ?? 0, + text: error.text ?? '', + }, + severity: 'error', + }); + } catch { + // Rspack may throw on invalid line & column when containing UTF-8. + // We catch it up here. + this.emitError(new Error(error.text)); + } + } else { + // Webpack or legacy Rspack + this.emitError(new Error(error.text)); + } + } + this.callback(new Error('react-transform failed')); + + return; + } + + for (const warning of result.warnings) { + if (this.experiments?.emitDiagnostic) { + // Rspack with `emitDiagnostic` API + try { + this.experiments.emitDiagnostic({ + message: warning.text!, + sourceCode: content, + location: { + line: warning.location?.line ?? 1, + column: warning.location?.column ?? 0, + length: warning.location?.length ?? 0, + text: warning.text ?? '', + }, + severity: 'warning', + }); + } catch { + // Rspack may throw on invalid line & column when containing UTF-8. + // We catch it up here. + this.emitWarning(new Error(warning.text)); + } + } else { + // Webpack or legacy Rspack + this.emitWarning(new Error(warning.text)); + } + } + this.callback(null, result.code, result.map); +} + +export default testingLoader; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cf80a6182e..c6cb295f70 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,6 +30,10 @@ catalogs: '@rspack/test-tools': specifier: 1.5.6 version: 1.5.6 + rstest: + '@rstest/core': + specifier: 0.0.10 + version: 0.0.10 overrides: '@rspack/core': 1.7.7 @@ -74,6 +78,9 @@ importers: '@rspack/core': specifier: 1.7.7 version: 1.7.7(@swc/helpers@0.5.18) + '@rstest/core': + specifier: catalog:rstest + version: 0.0.10(jsdom@27.4.0) '@svitejs/changesets-changelog-github-compact': specifier: ^1.2.0 version: 1.2.0 @@ -595,9 +602,15 @@ importers: '@lynx-js/react': specifier: workspace:* version: link:.. + '@lynx-js/react-rsbuild-plugin': + specifier: workspace:* + version: link:../../rspeedy/plugin-react '@lynx-js/testing-environment': specifier: workspace:* version: link:../../testing-library/testing-environment + '@rsbuild/core': + specifier: catalog:rsbuild + version: 1.7.3 '@testing-library/dom': specifier: ^10.4.1 version: 10.4.1 @@ -851,6 +864,9 @@ importers: '@rsbuild/plugin-type-check': specifier: 1.3.4 version: 1.3.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(@rspack/core@1.7.7(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.9.3) + '@rstest/core': + specifier: catalog:rstest + version: 0.0.10(jsdom@27.4.0) packages/rspeedy/lynx-bundle-rslib-config: dependencies: @@ -986,6 +1002,9 @@ importers: background-only: specifier: workspace:^ version: link:../../background-only + tiny-invariant: + specifier: ^1.3.3 + version: 1.3.3 devDependencies: '@lynx-js/config-rsbuild-plugin': specifier: workspace:* @@ -3804,6 +3823,11 @@ packages: cpu: [x64] os: [win32] + '@rsbuild/core@1.4.9': + resolution: {integrity: sha512-LvF0YQ2IQf6ddDQQCkWxgPxHJFrZT8bvwwsHYo8K9g8KJTlrrstMV85lU3DROaH5tm98jN3zYZIOCbqQzklx5g==} + engines: {node: '>=16.10.0'} + hasBin: true + '@rsbuild/core@1.7.3': resolution: {integrity: sha512-kI1oQvCXbQYxUvQPnDLdjSX4gFsbrFNpuUj6jXEJ7IcJ74Q+n4oeFj74/8tKerhxhe0L90m/ZQfzLeN5ORGA9w==} engines: {node: '>=18.12.0'} @@ -4012,6 +4036,10 @@ packages: peerDependencies: '@rspack/core': 1.7.7 + '@rspack/lite-tapable@1.0.1': + resolution: {integrity: sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==} + engines: {node: '>=16.0.0'} + '@rspack/lite-tapable@1.1.0': resolution: {integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==} @@ -4046,6 +4074,19 @@ packages: '@rstack-dev/doc-ui@1.12.3': resolution: {integrity: sha512-5W70pjRxxwyNT3R4kTYDE8cPaMjsJKXMeZQn7+Q54+RCJ1ahN4pADnpaY7WvSEBWkjXdI4IR4GGvBs7nSU/8MA==} + '@rstest/core@0.0.10': + resolution: {integrity: sha512-ytJ9LTCWARtBbt/+4RLV/lRgPCoXto0pazQ5lRdtv6M2gUvCZnIJKW2J2ioqQaVv0LXn5gqCCopoQYYFFE7eBQ==} + engines: {node: '>=18.0.0'} + hasBin: true + peerDependencies: + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + happy-dom: + optional: true + jsdom: + optional: true + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} @@ -4131,8 +4172,8 @@ packages: '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - '@sinclair/typebox@0.34.37': - resolution: {integrity: sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw==} + '@sinclair/typebox@0.34.38': + resolution: {integrity: sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==} '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} @@ -5156,6 +5197,9 @@ packages: resolution: {integrity: sha512-X7CUM9ZnwL/Ow++JH5AJKiemc82J7JyeryuPvXQdXBLcL/rqrC5KMUB1mHiORSolietH9sotvaOZlr6HSwPAlw==} hasBin: true + birpc@2.5.0: + resolution: {integrity: sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -5271,9 +5315,9 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chai@5.2.0: - resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} - engines: {node: '>=12'} + chai@5.2.1: + resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==} + engines: {node: '>=18'} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -5499,6 +5543,9 @@ packages: core-js-compat@3.45.0: resolution: {integrity: sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==} + core-js@3.44.0: + resolution: {integrity: sha512-aFCtd4l6GvAXwVEh3XbbVqJGHDJt0OZRa+5ePGx3LLwi12WfexqQxcsohb2wgsa/92xtl19Hd66G/L+TaAxDMw==} + core-js@3.47.0: resolution: {integrity: sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==} @@ -11254,7 +11301,7 @@ snapshots: '@jest/schemas@30.0.5': dependencies: - '@sinclair/typebox': 0.34.37 + '@sinclair/typebox': 0.34.38 '@jest/transform@29.7.0': dependencies: @@ -12038,6 +12085,14 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.34.9': optional: true + '@rsbuild/core@1.4.9': + dependencies: + '@rspack/core': 1.7.7(@swc/helpers@0.5.18) + '@rspack/lite-tapable': 1.0.1 + '@swc/helpers': 0.5.18 + core-js: 3.44.0 + jiti: 2.6.1 + '@rsbuild/core@1.7.3': dependencies: '@rspack/core': 1.7.7(@swc/helpers@0.5.18) @@ -12381,6 +12436,8 @@ snapshots: - webpack - webpack-cli + '@rspack/lite-tapable@1.0.1': {} + '@rspack/lite-tapable@1.1.0': {} '@rspack/plugin-react-refresh@1.6.0(react-refresh@0.18.0)': @@ -12499,6 +12556,20 @@ snapshots: - react - react-dom + '@rstest/core@0.0.10(jsdom@27.4.0)': + dependencies: + '@rsbuild/core': 1.4.9 + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/snapshot': 3.2.4 + birpc: 2.5.0 + chai: 5.2.1 + pathe: 2.0.3 + std-env: 3.9.0 + tinypool: 1.1.1 + optionalDependencies: + jsdom: 27.4.0 + '@rtsao/scc@1.1.0': {} '@rushstack/node-core-library@4.0.2(@types/node@24.10.13)': @@ -12618,7 +12689,7 @@ snapshots: '@sinclair/typebox@0.27.8': {} - '@sinclair/typebox@0.34.37': {} + '@sinclair/typebox@0.34.38': {} '@socket.io/component-emitter@3.1.2': {} @@ -13279,7 +13350,7 @@ snapshots: '@types/chai': 5.2.2 '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 - chai: 5.2.0 + chai: 5.2.1 tinyrainbow: 2.0.0 '@vitest/mocker@3.2.4(vite@5.4.2(@types/node@24.10.13)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.31.6))': @@ -13710,6 +13781,8 @@ snapshots: binaryen@125.0.0: {} + birpc@2.5.0: {} + bl@4.1.0: dependencies: buffer: 5.7.1 @@ -13868,7 +13941,7 @@ snapshots: ccount@2.0.1: {} - chai@5.2.0: + chai@5.2.1: dependencies: assertion-error: 2.0.1 check-error: 2.1.1 @@ -14070,6 +14143,8 @@ snapshots: dependencies: browserslist: 4.28.1 + core-js@3.44.0: {} + core-js@3.47.0: {} core-js@3.48.0: {} @@ -19267,7 +19342,7 @@ snapshots: '@vitest/snapshot': 3.2.4 '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 - chai: 5.2.0 + chai: 5.2.1 debug: 4.4.3 expect-type: 1.2.1 magic-string: 0.30.21 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3ee906f1e2..f2d62565b7 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -57,6 +57,10 @@ catalogs: "@rspack/core": "1.7.7" "@rspack/test-tools": "1.5.6" + # Rstest monorepo packages + rstest: + "@rstest/core": "0.0.10" + overrides: "@rspack/core": "$@rspack/core" "@rsbuild/core>@rspack/core": "$@rspack/core" From 49afabbc68a44ba692f53970d33fe1ba3b8eafed Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Wed, 11 Mar 2026 12:43:41 +0800 Subject: [PATCH 02/29] fix: test cases --- .../testing-library/src/plugins/vitest.ts | 19 +++++++++++++++++-- packages/rspeedy/plugin-react/src/loaders.ts | 4 +++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/react/testing-library/src/plugins/vitest.ts b/packages/react/testing-library/src/plugins/vitest.ts index eff13db97b..8b378d8476 100644 --- a/packages/react/testing-library/src/plugins/vitest.ts +++ b/packages/react/testing-library/src/plugins/vitest.ts @@ -160,8 +160,23 @@ export function testingLibraryPlugin( // react-compiler-runtime not found, skip alias } - // @ts-ignore - config.test.alias.push(...reactCompilerRuntimeAlias); + let mergedAlias: Vite.Alias[] = [...reactCompilerRuntimeAlias]; + if (config.test?.alias) { + if (Array.isArray(config.test.alias)) { + mergedAlias = [...config.test.alias, ...mergedAlias]; + } else { + mergedAlias = [ + ...Object.entries(config.test.alias).map(([key, value]) => ({ + find: key, + replacement: value, + })), + ...mergedAlias, + ]; + } + } + + config.test = config.test || {}; + config.test.alias = mergedAlias; compilerDeps = resolveCompilerDeps(rootContext); const { babelPath } = compilerDeps; diff --git a/packages/rspeedy/plugin-react/src/loaders.ts b/packages/rspeedy/plugin-react/src/loaders.ts index 03d77a5a94..78d4b814f6 100644 --- a/packages/rspeedy/plugin-react/src/loaders.ts +++ b/packages/rspeedy/plugin-react/src/loaders.ts @@ -26,8 +26,9 @@ function getLoaderOptions( const { compat, enableRemoveCSSScope, - defineDCE, shake, + defineDCE, + engineVersion, experimental_isLazyBundle, } = options @@ -38,6 +39,7 @@ function getLoaderOptions( isDynamicComponent: experimental_isLazyBundle, inlineSourcesContent, defineDCE, + engineVersion, ...isMainThread ? { shake, From 2f22d7d96e77cd2bde3c4a25002fe9d20b0f3bb6 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Wed, 11 Mar 2026 14:28:37 +0800 Subject: [PATCH 03/29] fix: use-sync-external-store test cases --- .../src/__tests__/lazy-bundle/index.test.jsx | 84 +++++++++---------- .../testing-library/vitest.3.1.config.ts | 6 +- ...est.ts => use-sync-external-store.test.ts} | 4 +- 3 files changed, 48 insertions(+), 46 deletions(-) rename packages/use-sync-external-store/test/{use-synx-external-store.test.ts => use-sync-external-store.test.ts} (98%) diff --git a/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx b/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx index 3535ca3220..f00e94af13 100644 --- a/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx +++ b/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx @@ -308,48 +308,48 @@ describe('Suspense', () => { // loading... is torn down if (typeof __RSTEST__ === 'undefined') { expect(tearDownInstances).toMatchInlineSnapshot(` - [ - { - "__id": 3, - "create": "function() { - const pageId = __vite_ssr_import_1__.__pageId; - const el = __CreateView(pageId); - __SetClasses(el, "lazy-wrapper"); - const el1 = __CreateWrapperElement(pageId); - __AppendElement(el, el1); - const el2 = __CreateText(pageId); - __AppendElement(el, el2); - const el3 = __CreateRawText("Hello, ReactLynx"); - __AppendElement(el2, el3); - const el4 = __CreateWrapperElement(pageId); - __AppendElement(el, el4); - return [ - el, - el1, - el2, - el3, - el4 - ]; - }", - "type": "__snapshot_50869_test_5", - }, - { - "__id": 7, - "create": "function() { - const pageId = __vite_ssr_import_1__.__pageId; - const el = __CreateText(pageId); - __SetClasses(el, "loading"); - const el1 = __CreateRawText("loading..."); - __AppendElement(el, el1); - return [ - el, - el1 - ]; - }", - "type": "__snapshot_50869_test_4", - }, - ] - `); + [ + { + "__id": 3, + "create": "function() { + const pageId = __vite_ssr_import_1__.__pageId; + const el = __CreateView(pageId); + __SetClasses(el, "lazy-wrapper"); + const el1 = __CreateWrapperElement(pageId); + __AppendElement(el, el1); + const el2 = __CreateText(pageId); + __AppendElement(el, el2); + const el3 = __CreateRawText("Hello, ReactLynx"); + __AppendElement(el2, el3); + const el4 = __CreateWrapperElement(pageId); + __AppendElement(el, el4); + return [ + el, + el1, + el2, + el3, + el4 + ]; + }", + "type": "__snapshot_50869_test_5", + }, + { + "__id": 7, + "create": "function() { + const pageId = __vite_ssr_import_1__.__pageId; + const el = __CreateText(pageId); + __SetClasses(el, "loading"); + const el1 = __CreateRawText("loading..."); + __AppendElement(el, el1); + return [ + el, + el1 + ]; + }", + "type": "__snapshot_50869_test_4", + }, + ] + `); } } else { if (typeof __RSTEST__ === 'undefined') { diff --git a/packages/react/testing-library/vitest.3.1.config.ts b/packages/react/testing-library/vitest.3.1.config.ts index 56d18337e2..dbe90ddfa9 100644 --- a/packages/react/testing-library/vitest.3.1.config.ts +++ b/packages/react/testing-library/vitest.3.1.config.ts @@ -4,13 +4,13 @@ import { createVitestConfig } from './dist/vitest.config'; const defaultConfig = await createVitestConfig({ runtimePkgName: '@lynx-js/react', engineVersion: '3.1', - include: [ - 'src/__tests__/3.1/**/*.{js,jsx,ts,tsx}', - ], }); const config = defineConfig({ test: { name: 'react/testing-library/engine-3.1', + include: [ + 'src/__tests__/3.1/**/*.{js,jsx,ts,tsx}', + ], }, }); diff --git a/packages/use-sync-external-store/test/use-synx-external-store.test.ts b/packages/use-sync-external-store/test/use-sync-external-store.test.ts similarity index 98% rename from packages/use-sync-external-store/test/use-synx-external-store.test.ts rename to packages/use-sync-external-store/test/use-sync-external-store.test.ts index a5298ca6e0..a1b8a66f97 100644 --- a/packages/use-sync-external-store/test/use-synx-external-store.test.ts +++ b/packages/use-sync-external-store/test/use-sync-external-store.test.ts @@ -180,7 +180,9 @@ describe('useSyncExternalStoreWithSelector', () => { const store = createExternalStore({ items: ['A', 'B'], }); - const shallowEqualArray = (a: T[], b: T[]) => { + // dprint-ignore the comma is required to avoid + // ts treat T as a JSX element + const shallowEqualArray = (a: T[], b: T[]) => { if (a.length !== b.length) { return false; } From 88ec796e7add33994a59344e1cbdc0a95718db25 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Wed, 11 Mar 2026 22:38:55 +0800 Subject: [PATCH 04/29] fix: expect for rstest --- .../react/testing-library/src/setupFiles/common/runtime-setup.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react/testing-library/src/setupFiles/common/runtime-setup.js b/packages/react/testing-library/src/setupFiles/common/runtime-setup.js index 3f51d7531f..f932a1cd3b 100644 --- a/packages/react/testing-library/src/setupFiles/common/runtime-setup.js +++ b/packages/react/testing-library/src/setupFiles/common/runtime-setup.js @@ -1,5 +1,4 @@ import { options } from 'preact'; -import { expect } from 'vitest'; import { BackgroundSnapshotInstance } from '../../../../runtime/lib/backgroundSnapshot.js'; import { clearCommitTaskId, replaceCommitHook } from '../../../../runtime/lib/lifecycle/patch/commit.js'; From f5a3757bfa44c66e29ebe1e5cd514cbfcd5c84c7 Mon Sep 17 00:00:00 2001 From: 9aoy <9aoyuao@gmail.com> Date: Wed, 18 Mar 2026 17:01:35 +0800 Subject: [PATCH 05/29] feat: support rstest-adapter in testing library (#2347) @coderabbitai summary `@lynx-js/react/testing-library/rstest-adapter` will automatically load your `lynx.config.ts` and apply the same configuration to rstest, so you can keep your test environment consistent with your development environment. ```ts // rstest.config.ts import { defineConfig } from '@rstest/core'; import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-adapter'; export default defineConfig({ extends: withLynxConfig(), }); ``` ## Checklist - [ ] Tests updated (or not required). - [ ] Documentation updated (or not required). - [ ] Changeset added, and when a BREAKING CHANGE occurs, it needs to be clearly marked (or not required). --- .changeset/red-lamps-arrive.md | 13 +- packages/react/package.json | 8 + packages/react/testing-library/README.md | 26 +++- packages/react/testing-library/package.json | 2 + .../react/testing-library/rslib.config.ts | 4 + .../react/testing-library/rstest.config.ts | 1 - .../src/__tests__/lazy-bundle/index.test.jsx | 8 +- .../testing-library/src/rstest-adapter.ts | 54 +++++++ .../src/setupFiles/common/runtime-setup.js | 25 --- .../testing-library/src/setupFiles/rstest.js | 26 ++++ .../testing-library/src/setupFiles/vitest.js | 26 ++++ .../rstest.config.js | 9 +- .../rstest.config.ts | 11 +- .../plugin-react/src/pluginReactLynx.ts | 4 - packages/rspeedy/plugin-react/src/refresh.ts | 2 +- packages/rspeedy/plugin-react/src/rstest.ts | 15 -- .../examples/basic/rstest.config.ts | 11 +- .../react-compiler/lynx.enable.config.ts | 35 +++++ .../examples/react-compiler/package.json | 3 + .../examples/react-compiler/rstest.config.ts | 19 +++ .../react-compiler/vitest-polyfill.cjs | 6 + pnpm-lock.yaml | 146 ++++++++++-------- pnpm-workspace.yaml | 2 +- 23 files changed, 305 insertions(+), 151 deletions(-) create mode 100644 packages/react/testing-library/src/rstest-adapter.ts delete mode 100644 packages/rspeedy/plugin-react/src/rstest.ts create mode 100644 packages/testing-library/examples/react-compiler/lynx.enable.config.ts create mode 100644 packages/testing-library/examples/react-compiler/rstest.config.ts create mode 100644 packages/testing-library/examples/react-compiler/vitest-polyfill.cjs diff --git a/.changeset/red-lamps-arrive.md b/.changeset/red-lamps-arrive.md index cac3bda3b2..75d2848d15 100644 --- a/.changeset/red-lamps-arrive.md +++ b/.changeset/red-lamps-arrive.md @@ -7,19 +7,16 @@ Support rstest for testing library, you can use rstest with RLTL now: Create a config file `rstest.config.ts` with the following content: ```ts -import { defineConfig, RstestConfig } from '@rstest/core'; -import lynxConfig from './lynx.config.js'; +import { defineConfig } from '@rstest/core'; +import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-adapter'; export default defineConfig({ - ...lynxConfig as RstestConfig, - testEnvironment: 'jsdom', - setupFiles: [ - require.resolve('@lynx-js/react/testing-library/setupFiles/rstest'), - ], - globals: true, + extends: withLynxConfig(), }); ``` +`@lynx-js/react/testing-library/rstest-adapter` will automatically load your `lynx.config.ts` and apply the same configuration to rstest, so you can keep your test environment consistent with your development environment. + And then use rstest as usual: ```bash diff --git a/packages/react/package.json b/packages/react/package.json index 3178d6408b..98dfe6d395 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -104,6 +104,10 @@ "types": "./testing-library/dist/vitest.config.d.ts", "default": "./testing-library/dist/vitest.config.js" }, + "./testing-library/rstest-adapter": { + "types": "./testing-library/dist/rstest-adapter.d.ts", + "default": "./testing-library/dist/rstest-adapter.js" + }, "./testing-library/setupFiles/rstest": "./testing-library/dist/setupFiles/rstest.js", "./package.json": "./package.json" }, @@ -184,11 +188,15 @@ }, "peerDependencies": { "@lynx-js/types": "*", + "@rstest/core": "*", "@types/react": "^18" }, "peerDependenciesMeta": { "@lynx-js/types": { "optional": true + }, + "@rstest/core": { + "optional": true } } } diff --git a/packages/react/testing-library/README.md b/packages/react/testing-library/README.md index b14b28d9e6..5be535422b 100644 --- a/packages/react/testing-library/README.md +++ b/packages/react/testing-library/README.md @@ -8,6 +8,28 @@ Similar to [react-testing-library](https://github.com/testing-library/react-test ## Setup +### Rstest + +Setup rstest with `@lynx-js/react/testing-library/rstest-adapter`: + +```ts +// rstest.config.ts +import { defineConfig } from '@rstest/core'; +import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-adapter'; + +export default defineConfig({ + extends: withLynxConfig(), +}); +``` + +`@lynx-js/react/testing-library/rstest-adapter` will automatically load your `lynx.config.ts` and apply the same configuration to rstest, so you can keep your test environment consistent with your development environment. + +Then you can start writing tests and run them with rstest! + +For more usage detail, see https://rstest.rs/ + +### Vitest + Setup vitest: ```js @@ -29,9 +51,9 @@ Then you can start writing tests and run them with vitest! ## Usage -```js +```jsx import '@testing-library/jest-dom'; -import { test, expect } from 'vitest'; +import { test, expect } from 'vitest'; // or '@rstest/core' import { render } from '@lynx-js/react/testing-library'; test('renders options.wrapper around node', async () => { diff --git a/packages/react/testing-library/package.json b/packages/react/testing-library/package.json index b0129a3394..cad2bd3569 100644 --- a/packages/react/testing-library/package.json +++ b/packages/react/testing-library/package.json @@ -15,8 +15,10 @@ "devDependencies": { "@lynx-js/react": "workspace:*", "@lynx-js/react-rsbuild-plugin": "workspace:*", + "@lynx-js/rspeedy": "workspace:*", "@lynx-js/testing-environment": "workspace:*", "@rsbuild/core": "catalog:rsbuild", + "@rstest/adapter-rsbuild": "^0.2.3", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1" } diff --git a/packages/react/testing-library/rslib.config.ts b/packages/react/testing-library/rslib.config.ts index 5389d7c0d7..0446793d03 100644 --- a/packages/react/testing-library/rslib.config.ts +++ b/packages/react/testing-library/rslib.config.ts @@ -16,6 +16,7 @@ export default defineConfig({ 'pure': './src/pure.jsx', 'env/index': './src/env/index.ts', 'plugins/index': './src/plugins/index.ts', + 'rstest-adapter': './src/rstest-adapter.ts', }, }, output: { @@ -24,6 +25,9 @@ export default defineConfig({ /^\.\.\/\.\.\/runtime\/lib/, /^preact/, /^vitest/, + '@rstest/core', + '@rsbuild/core', + '@lynx-js/rspeedy', ], }, }, diff --git a/packages/react/testing-library/rstest.config.ts b/packages/react/testing-library/rstest.config.ts index f2ae44339b..1100a0b882 100644 --- a/packages/react/testing-library/rstest.config.ts +++ b/packages/react/testing-library/rstest.config.ts @@ -16,7 +16,6 @@ export default defineConfig({ ], source: { define: { - __RSTEST__: 'true', __ALOG__: 'true', }, }, diff --git a/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx b/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx index f00e94af13..3e7e8159a4 100644 --- a/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx +++ b/packages/react/testing-library/src/__tests__/lazy-bundle/index.test.jsx @@ -22,7 +22,7 @@ function LazyComponentLoader({ url }) { return ( loading...}> - {(typeof __RSTEST__ !== 'undefined' && __RSTEST__) ? null : } + {process.env.RSTEST ? null : } ); } @@ -55,7 +55,7 @@ describe('lazy bundle', () => { timeout: 50_000, }); - if (typeof __RSTEST__ !== 'undefined' && __RSTEST__) { + if (process.env.RSTEST) { expect(container.firstChild).toMatchInlineSnapshot(` @@ -306,7 +306,7 @@ describe('Suspense', () => { if (name === 'PreactSuspense') { // is torn down, (it is triggered in first render but delayed 10_000ms to execute, we use `vi.runAllTimers()` to simulate the situation that will cause the bug) // loading... is torn down - if (typeof __RSTEST__ === 'undefined') { + if (!process.env.RSTEST) { expect(tearDownInstances).toMatchInlineSnapshot(` [ { @@ -352,7 +352,7 @@ describe('Suspense', () => { `); } } else { - if (typeof __RSTEST__ === 'undefined') { + if (!process.env.RSTEST) { expect(tearDownInstances).toMatchInlineSnapshot(` [ { diff --git a/packages/react/testing-library/src/rstest-adapter.ts b/packages/react/testing-library/src/rstest-adapter.ts new file mode 100644 index 0000000000..4c422ec2f6 --- /dev/null +++ b/packages/react/testing-library/src/rstest-adapter.ts @@ -0,0 +1,54 @@ +import type { ExtendConfigFn } from '@rstest/core'; +import { createRequire } from 'node:module'; +import type { RsbuildConfig } from '@rsbuild/core'; + +export interface TestingLibraryOptions { + /** + * The root path of the project. + * + * @default `process.cwd()` + */ + rootPath?: string; + + /** + * The path to the Lynx config file. + * + * @default `lynx.config.ts` + */ + configPath?: string; +} + +const require = createRequire(import.meta.url); + +export function withLynxConfig( + options?: TestingLibraryOptions, +): ExtendConfigFn { + return async () => { + const { loadConfig } = await import('@lynx-js/rspeedy'); + const lynxConfig = await loadConfig({ + cwd: options?.rootPath, + configPath: options?.configPath, + }); + + const { toRstestConfig } = await import('@rstest/adapter-rsbuild'); + + const rstestConfig = toRstestConfig({ + rsbuildConfig: lynxConfig.content as RsbuildConfig, + }); + + return { + ...rstestConfig, + plugins: [ + ...(rstestConfig.plugins || []), + { + name: 'lynx-adapter:remove-useless-plugins', + remove: ['lynx:rsbuild:qrcode'], + setup: () => {}, + }, + ], + testEnvironment: 'jsdom', + setupFiles: [require.resolve('./setupFiles/rstest')], + globals: true, + }; + }; +} diff --git a/packages/react/testing-library/src/setupFiles/common/runtime-setup.js b/packages/react/testing-library/src/setupFiles/common/runtime-setup.js index f932a1cd3b..2856ca02f2 100644 --- a/packages/react/testing-library/src/setupFiles/common/runtime-setup.js +++ b/packages/react/testing-library/src/setupFiles/common/runtime-setup.js @@ -20,31 +20,6 @@ import { initApiEnv } from '../../../../worklet-runtime/lib/api/lynxApi.js'; import { initEventListeners } from '../../../../worklet-runtime/lib/listeners.js'; import { initWorklet } from '../../../../worklet-runtime/lib/workletRuntime.js'; -expect.addSnapshotSerializer({ - test(val) { - return Boolean( - val - && typeof val === 'object' - && Array.isArray(val.refAttr) - && Object.prototype.hasOwnProperty.call(val, 'task') - && typeof val.exec === 'function', - ); - }, - print(val, serialize) { - const printed = serialize({ - refAttr: Array.isArray(val.refAttr) ? [...val.refAttr] : val.refAttr, - task: val.task, - }); - if (printed.startsWith('Object')) { - return printed.replace(/^Object/, 'RefProxy'); - } - if (printed.startsWith('{')) { - return `RefProxy ${printed}`; - } - return printed; - }, -}); - const { onInjectMainThreadGlobals, onInjectBackgroundThreadGlobals, diff --git a/packages/react/testing-library/src/setupFiles/rstest.js b/packages/react/testing-library/src/setupFiles/rstest.js index f938cfa636..ef0811e05f 100644 --- a/packages/react/testing-library/src/setupFiles/rstest.js +++ b/packages/react/testing-library/src/setupFiles/rstest.js @@ -2,3 +2,29 @@ import '../env/rstest.js'; import './common/runtime-setup.js'; import './inner/rstest.js'; import './common/bootstrap.js'; +import { expect } from '@rstest/core'; + +expect.addSnapshotSerializer({ + test(val) { + return Boolean( + val + && typeof val === 'object' + && Array.isArray(val.refAttr) + && Object.prototype.hasOwnProperty.call(val, 'task') + && typeof val.exec === 'function', + ); + }, + print(val, serialize) { + const printed = serialize({ + refAttr: Array.isArray(val.refAttr) ? [...val.refAttr] : val.refAttr, + task: val.task, + }); + if (printed.startsWith('Object')) { + return printed.replace(/^Object/, 'RefProxy'); + } + if (printed.startsWith('{')) { + return `RefProxy ${printed}`; + } + return printed; + }, +}); diff --git a/packages/react/testing-library/src/setupFiles/vitest.js b/packages/react/testing-library/src/setupFiles/vitest.js index a1080d35a5..71f8a24da1 100644 --- a/packages/react/testing-library/src/setupFiles/vitest.js +++ b/packages/react/testing-library/src/setupFiles/vitest.js @@ -1,3 +1,29 @@ import './common/runtime-setup.js'; import './inner/vitest.js'; import './common/bootstrap.js'; +import { expect } from 'vitest'; + +expect.addSnapshotSerializer({ + test(val) { + return Boolean( + val + && typeof val === 'object' + && Array.isArray(val.refAttr) + && Object.prototype.hasOwnProperty.call(val, 'task') + && typeof val.exec === 'function', + ); + }, + print(val, serialize) { + const printed = serialize({ + refAttr: Array.isArray(val.refAttr) ? [...val.refAttr] : val.refAttr, + task: val.task, + }); + if (printed.startsWith('Object')) { + return printed.replace(/^Object/, 'RefProxy'); + } + if (printed.startsWith('{')) { + return `RefProxy ${printed}`; + } + return printed; + }, +}); diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/rstest.config.js b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/rstest.config.js index ec22dfed14..a26a7142bd 100644 --- a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/rstest.config.js +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/rstest.config.js @@ -1,11 +1,6 @@ import { defineConfig } from '@rstest/core' -import lynxConfig from './lynx.config.js' +import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-adapter' export default defineConfig({ - ...lynxConfig, - testEnvironment: 'jsdom', - setupFiles: [ - require.resolve('@lynx-js/react/testing-library/setupFiles/rstest'), - ], - globals: true, + extends: withLynxConfig(), }) diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/rstest.config.ts b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/rstest.config.ts index 788a77ba69..a26a7142bd 100644 --- a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/rstest.config.ts +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-ts/rstest.config.ts @@ -1,11 +1,6 @@ -import { defineConfig, type RstestConfig } from '@rstest/core' -import lynxConfig from './lynx.config.js' +import { defineConfig } from '@rstest/core' +import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-adapter' export default defineConfig({ - ...lynxConfig as RstestConfig, - testEnvironment: 'jsdom', - setupFiles: [ - require.resolve('@lynx-js/react/testing-library/setupFiles/rstest'), - ], - globals: true, + extends: withLynxConfig(), }) diff --git a/packages/rspeedy/plugin-react/src/pluginReactLynx.ts b/packages/rspeedy/plugin-react/src/pluginReactLynx.ts index cb34ef74c3..bf8edf9551 100644 --- a/packages/rspeedy/plugin-react/src/pluginReactLynx.ts +++ b/packages/rspeedy/plugin-react/src/pluginReactLynx.ts @@ -31,7 +31,6 @@ import { applyLazy } from './lazy.js' import { applyLoaders, applyTestingLoaders } from './loaders.js' import { applyNodeEnv } from './nodeEnv.js' import { applyRefresh } from './refresh.js' -import { applyRstest } from './rstest.js' import { applySplitChunksRule } from './splitChunks.js' import { applySWC } from './swc.js' import { applyUseSyncExternalStore } from './useSyncExternalStore.js' @@ -345,9 +344,6 @@ export function pluginReactLynx( }) } - if (isRstest) { - applyRstest(api) - } if (!isRstest) { applyCSS(api, resolvedOptions) } diff --git a/packages/rspeedy/plugin-react/src/refresh.ts b/packages/rspeedy/plugin-react/src/refresh.ts index 69e20f7938..09d263e016 100644 --- a/packages/rspeedy/plugin-react/src/refresh.ts +++ b/packages/rspeedy/plugin-react/src/refresh.ts @@ -18,7 +18,7 @@ import { LAYERS } from '@lynx-js/react-webpack-plugin' const PLUGIN_NAME_REACT_REFRESH = 'lynx:react:refresh' export function applyRefresh(api: RsbuildPluginAPI): void { - api.modifyWebpackChain(async (chain, { CHAIN_ID, isProd }) => { + api.modifyWebpackChain?.(async (chain, { CHAIN_ID, isProd }) => { if (!isProd) { await applyRefreshRules(api, chain, CHAIN_ID, ReactRefreshWebpackPlugin) } diff --git a/packages/rspeedy/plugin-react/src/rstest.ts b/packages/rspeedy/plugin-react/src/rstest.ts deleted file mode 100644 index 6797a23039..0000000000 --- a/packages/rspeedy/plugin-react/src/rstest.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2025 The Lynx Authors. All rights reserved. -// Licensed under the Apache License Version 2.0 that can be found in the -// LICENSE file in the root directory of this source tree. -import type { RsbuildPluginAPI } from '@rsbuild/core' - -export function applyRstest(api: RsbuildPluginAPI): void { - api.modifyRsbuildConfig((config, { mergeRsbuildConfig }) => { - return mergeRsbuildConfig({ - output: { - // Allow using Node.js internal modules in testing framework directly - externals: /^node:/, - }, - }, config) - }) -} diff --git a/packages/testing-library/examples/basic/rstest.config.ts b/packages/testing-library/examples/basic/rstest.config.ts index 278ef168b2..beec012e7a 100644 --- a/packages/testing-library/examples/basic/rstest.config.ts +++ b/packages/testing-library/examples/basic/rstest.config.ts @@ -1,11 +1,6 @@ -import { defineConfig, RstestConfig } from '@rstest/core'; -import lynxConfig from './lynx.config.js'; +import { defineConfig } from '@rstest/core'; +import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-adapter'; export default defineConfig({ - ...lynxConfig as RstestConfig, - testEnvironment: 'jsdom', - setupFiles: [ - require.resolve('@lynx-js/react/testing-library/setupFiles/rstest'), - ], - globals: true, + extends: withLynxConfig(), }); diff --git a/packages/testing-library/examples/react-compiler/lynx.enable.config.ts b/packages/testing-library/examples/react-compiler/lynx.enable.config.ts new file mode 100644 index 0000000000..d2c5176d8a --- /dev/null +++ b/packages/testing-library/examples/react-compiler/lynx.enable.config.ts @@ -0,0 +1,35 @@ +import { pluginBabel } from '@rsbuild/plugin-babel'; + +import { pluginQRCode } from '@lynx-js/qrcode-rsbuild-plugin'; +import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin'; +import { defineConfig } from '@lynx-js/rspeedy'; + +export default defineConfig({ + source: { + entry: './src/index.tsx', + }, + plugins: [ + pluginReactLynx({ + enableRemoveCSSScope: true, + }), + pluginBabel({ + include: /\.(?:jsx|tsx)$/, + babelLoaderOptions(opts) { + opts.plugins?.unshift([ + 'babel-plugin-react-compiler', + // See https://react.dev/reference/react-compiler/configuration for config + { + // ReactLynx only supports target to version 17 + target: '17', + }, + ]); + }, + }), + pluginQRCode({ + schema(url) { + // We use `?fullscreen=true` to open the page in LynxExplorer in full screen mode + return `${url}?fullscreen=true`; + }, + }), + ], +}); diff --git a/packages/testing-library/examples/react-compiler/package.json b/packages/testing-library/examples/react-compiler/package.json index 3a5f1fe235..7824f3f319 100644 --- a/packages/testing-library/examples/react-compiler/package.json +++ b/packages/testing-library/examples/react-compiler/package.json @@ -6,6 +6,7 @@ "scripts": { "build": "rspeedy build", "dev": "rspeedy dev", + "rstest": "rstest", "test": "vitest" }, "dependencies": { @@ -18,9 +19,11 @@ "@babel/plugin-syntax-typescript": "^7.28.6", "@babel/preset-react": "^7.28.5", "@babel/preset-typescript": "^7.28.5", + "@lynx-js/qrcode-rsbuild-plugin": "workspace:*", "@lynx-js/react-rsbuild-plugin": "workspace:*", "@lynx-js/rspeedy": "workspace:*", "@lynx-js/types": "3.7.0", + "@rsbuild/plugin-babel": "1.1.0", "@testing-library/jest-dom": "^6.9.1", "@types/react": "^18.3.28", "babel-plugin-react-compiler": "0.0.0-experimental-fe727a3-20250909", diff --git a/packages/testing-library/examples/react-compiler/rstest.config.ts b/packages/testing-library/examples/react-compiler/rstest.config.ts new file mode 100644 index 0000000000..66912b932f --- /dev/null +++ b/packages/testing-library/examples/react-compiler/rstest.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from '@rstest/core'; +import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-adapter'; + +export default defineConfig({ + extends: withLynxConfig({ + configPath: './lynx.enable.config.ts', + }), + resolve: { + alias: { + // not necessary in real projects, just for compatibility with vitest tests in this repo + vitest: require.resolve('./vitest-polyfill.cjs'), + }, + }, + source: { + define: { + __FORGET__: 'true', + }, + }, +}); diff --git a/packages/testing-library/examples/react-compiler/vitest-polyfill.cjs b/packages/testing-library/examples/react-compiler/vitest-polyfill.cjs new file mode 100644 index 0000000000..647f2e4a8d --- /dev/null +++ b/packages/testing-library/examples/react-compiler/vitest-polyfill.cjs @@ -0,0 +1,6 @@ +// in order to make our test case work for +// both vitest and rstest, we need to alias +// `vitest` to `@rstest/core` + +global['@rstest/core'].vi = global['@rstest/core'].rs; +module.exports = global['@rstest/core']; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c6cb295f70..f87a79a39a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,8 +32,8 @@ catalogs: version: 1.5.6 rstest: '@rstest/core': - specifier: 0.0.10 - version: 0.0.10 + specifier: 0.8.1 + version: 0.8.1 overrides: '@rspack/core': 1.7.7 @@ -80,7 +80,7 @@ importers: version: 1.7.7(@swc/helpers@0.5.18) '@rstest/core': specifier: catalog:rstest - version: 0.0.10(jsdom@27.4.0) + version: 0.8.1(jsdom@27.4.0) '@svitejs/changesets-changelog-github-compact': specifier: ^1.2.0 version: 1.2.0 @@ -282,7 +282,7 @@ importers: version: 3.7.0 '@rsbuild/plugin-babel': specifier: 1.1.0 - version: 1.1.0(@rsbuild/core@1.7.3) + version: 1.1.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) '@types/react': specifier: ^18.3.28 version: 18.3.28 @@ -553,6 +553,9 @@ importers: packages/react: dependencies: + '@rstest/core': + specifier: '*' + version: 0.8.1(jsdom@27.4.0) preact: specifier: npm:@hongzhiyuan/preact@10.28.0-fc4af453 version: '@hongzhiyuan/preact@10.28.0-fc4af453' @@ -605,12 +608,18 @@ importers: '@lynx-js/react-rsbuild-plugin': specifier: workspace:* version: link:../../rspeedy/plugin-react + '@lynx-js/rspeedy': + specifier: workspace:* + version: link:../../rspeedy/core '@lynx-js/testing-environment': specifier: workspace:* version: link:../../testing-library/testing-environment '@rsbuild/core': specifier: catalog:rsbuild version: 1.7.3 + '@rstest/adapter-rsbuild': + specifier: ^0.2.3 + version: 0.2.3(@rsbuild/core@1.7.3)(@rstest/core@0.8.1(jsdom@27.4.0)) '@testing-library/dom': specifier: ^10.4.1 version: 10.4.1 @@ -866,7 +875,7 @@ importers: version: 1.3.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(@rspack/core@1.7.7(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.9.3) '@rstest/core': specifier: catalog:rstest - version: 0.0.10(jsdom@27.4.0) + version: 0.8.1(jsdom@27.4.0) packages/rspeedy/lynx-bundle-rslib-config: dependencies: @@ -1110,10 +1119,10 @@ importers: version: link:../../web-platform/web-elements '@rsbuild/plugin-less': specifier: 1.6.0 - version: 1.6.0(@rsbuild/core@1.7.3) + version: 1.6.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) '@rsbuild/plugin-sass': specifier: 1.5.0 - version: 1.5.0(@rsbuild/core@1.7.3) + version: 1.5.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) commander: specifier: ^13.1.0 version: 13.1.0 @@ -1128,7 +1137,7 @@ importers: version: 1.1.1 rsbuild-plugin-tailwindcss: specifier: 0.2.4 - version: 0.2.4(@rsbuild/core@1.7.3)(tailwindcss@4.2.1) + version: 0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@4.2.1) rslog: specifier: ^1.3.2 version: 1.3.2 @@ -1198,6 +1207,9 @@ importers: '@babel/preset-typescript': specifier: ^7.28.5 version: 7.28.5(@babel/core@7.29.0) + '@lynx-js/qrcode-rsbuild-plugin': + specifier: workspace:* + version: link:../../../rspeedy/plugin-qrcode '@lynx-js/react-rsbuild-plugin': specifier: workspace:* version: link:../../../rspeedy/plugin-react @@ -1207,6 +1219,9 @@ importers: '@lynx-js/types': specifier: 3.7.0 version: 3.7.0 + '@rsbuild/plugin-babel': + specifier: 1.1.0 + version: 1.1.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) '@testing-library/jest-dom': specifier: ^6.9.1 version: 6.9.1 @@ -1960,13 +1975,13 @@ importers: version: 7.33.4(@types/node@24.10.13) '@rsbuild/plugin-sass': specifier: 1.5.0 - version: 1.5.0(@rsbuild/core@1.7.3) + version: 1.5.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) '@rsbuild/plugin-type-check': specifier: 1.3.4 - version: 1.3.4(@rsbuild/core@1.7.3)(@rspack/core@1.7.7(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.9.3) + version: 1.3.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(@rspack/core@1.7.7(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.9.3) '@rsbuild/plugin-typed-css-modules': specifier: 1.2.2 - version: 1.2.2(@rsbuild/core@1.7.3) + version: 1.2.2(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) '@rspress/core': specifier: 2.0.3 version: 2.0.3(@types/react@19.2.14)(core-js@3.48.0) @@ -3823,9 +3838,9 @@ packages: cpu: [x64] os: [win32] - '@rsbuild/core@1.4.9': - resolution: {integrity: sha512-LvF0YQ2IQf6ddDQQCkWxgPxHJFrZT8bvwwsHYo8K9g8KJTlrrstMV85lU3DROaH5tm98jN3zYZIOCbqQzklx5g==} - engines: {node: '>=16.10.0'} + '@rsbuild/core@1.7.2': + resolution: {integrity: sha512-VAFO6cM+cyg2ntxNW6g3tB2Jc5J5mpLjLluvm7VtW2uceNzyUlVv41o66Yp1t1ikxd3ljtqegViXem62JqzveA==} + engines: {node: '>=18.12.0'} hasBin: true '@rsbuild/core@1.7.3': @@ -4036,10 +4051,6 @@ packages: peerDependencies: '@rspack/core': 1.7.7 - '@rspack/lite-tapable@1.0.1': - resolution: {integrity: sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==} - engines: {node: '>=16.0.0'} - '@rspack/lite-tapable@1.1.0': resolution: {integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==} @@ -4074,9 +4085,15 @@ packages: '@rstack-dev/doc-ui@1.12.3': resolution: {integrity: sha512-5W70pjRxxwyNT3R4kTYDE8cPaMjsJKXMeZQn7+Q54+RCJ1ahN4pADnpaY7WvSEBWkjXdI4IR4GGvBs7nSU/8MA==} - '@rstest/core@0.0.10': - resolution: {integrity: sha512-ytJ9LTCWARtBbt/+4RLV/lRgPCoXto0pazQ5lRdtv6M2gUvCZnIJKW2J2ioqQaVv0LXn5gqCCopoQYYFFE7eBQ==} - engines: {node: '>=18.0.0'} + '@rstest/adapter-rsbuild@0.2.3': + resolution: {integrity: sha512-ZKQkY3wI+PLyPJR41xFrAQ0AmenUivo5l1/g97p00+nk1peGMr9gjUxg5gqt3M7qLQZ0RiJZX/KPkPeOltd4lQ==} + peerDependencies: + '@rsbuild/core': '*' + '@rstest/core': '>=0.7.7' + + '@rstest/core@0.8.1': + resolution: {integrity: sha512-7d/2fm2V91pVx/rRtZ2gl6Zh4hVMivtDl4RgHFhBOrxi//UwhKISeF5gS/CSwpCgfOf10TzJRXqdI17ueUBNMQ==} + engines: {node: '>=18.12.0'} hasBin: true peerDependencies: happy-dom: '*' @@ -4403,6 +4420,9 @@ packages: '@types/chai@5.2.2': resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + '@types/connect-history-api-fallback@1.5.4': resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} @@ -5197,9 +5217,6 @@ packages: resolution: {integrity: sha512-X7CUM9ZnwL/Ow++JH5AJKiemc82J7JyeryuPvXQdXBLcL/rqrC5KMUB1mHiORSolietH9sotvaOZlr6HSwPAlw==} hasBin: true - birpc@2.5.0: - resolution: {integrity: sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==} - bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -5543,9 +5560,6 @@ packages: core-js-compat@3.45.0: resolution: {integrity: sha512-gRoVMBawZg0OnxaVv3zpqLLxaHmsubEGyTnqdpI/CEBvX4JadI1dMSHxagThprYRtSVbuQxvi6iUatdPxohHpA==} - core-js@3.44.0: - resolution: {integrity: sha512-aFCtd4l6GvAXwVEh3XbbVqJGHDJt0OZRa+5ePGx3LLwi12WfexqQxcsohb2wgsa/92xtl19Hd66G/L+TaAxDMw==} - core-js@3.47.0: resolution: {integrity: sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==} @@ -12085,12 +12099,12 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.34.9': optional: true - '@rsbuild/core@1.4.9': + '@rsbuild/core@1.7.2': dependencies: '@rspack/core': 1.7.7(@swc/helpers@0.5.18) - '@rspack/lite-tapable': 1.0.1 + '@rspack/lite-tapable': 1.1.0 '@swc/helpers': 0.5.18 - core-js: 3.44.0 + core-js: 3.47.0 jiti: 2.6.1 '@rsbuild/core@1.7.3': @@ -12109,13 +12123,13 @@ snapshots: optionalDependencies: core-js: 3.48.0 - '@rsbuild/plugin-babel@1.1.0(@rsbuild/core@1.7.3)': + '@rsbuild/plugin-babel@1.1.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0) '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@rsbuild/core': 1.7.3 + '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) '@types/babel__core': 7.20.5 deepmerge: 4.3.1 reduce-configs: 1.1.1 @@ -12155,9 +12169,9 @@ snapshots: optionalDependencies: '@rsbuild/core': 1.7.3 - '@rsbuild/plugin-less@1.6.0(@rsbuild/core@1.7.3)': + '@rsbuild/plugin-less@1.6.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': dependencies: - '@rsbuild/core': 1.7.3 + '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) deepmerge: 4.3.1 reduce-configs: 1.1.1 @@ -12186,6 +12200,15 @@ snapshots: reduce-configs: 1.1.1 sass-embedded: 1.97.3 + '@rsbuild/plugin-sass@1.5.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': + dependencies: + '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) + deepmerge: 4.3.1 + loader-utils: 2.0.4 + postcss: 8.5.6 + reduce-configs: 1.1.1 + sass-embedded: 1.97.3 + '@rsbuild/plugin-source-build@1.0.4(@rsbuild/core@1.7.3)': dependencies: fast-glob: 3.3.3 @@ -12194,19 +12217,6 @@ snapshots: optionalDependencies: '@rsbuild/core': 1.7.3 - '@rsbuild/plugin-type-check@1.3.4(@rsbuild/core@1.7.3)(@rspack/core@1.7.7(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.9.3)': - dependencies: - deepmerge: 4.3.1 - json5: 2.2.3 - reduce-configs: 1.1.1 - ts-checker-rspack-plugin: 1.3.0(@rspack/core@1.7.7(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.9.3) - optionalDependencies: - '@rsbuild/core': 1.7.3 - transitivePeerDependencies: - - '@rspack/core' - - tslib - - typescript - '@rsbuild/plugin-type-check@1.3.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(@rspack/core@1.7.7(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.9.3)': dependencies: deepmerge: 4.3.1 @@ -12224,6 +12234,10 @@ snapshots: optionalDependencies: '@rsbuild/core': 1.7.3 + '@rsbuild/plugin-typed-css-modules@1.2.2(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': + optionalDependencies: + '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) + '@rsdoctor/client@1.2.3': {} '@rsdoctor/core@1.2.3(@rsbuild/core@1.7.3)(@rspack/core@1.7.7(@swc/helpers@0.5.18))(webpack@5.105.2)': @@ -12436,8 +12450,6 @@ snapshots: - webpack - webpack-cli - '@rspack/lite-tapable@1.0.1': {} - '@rspack/lite-tapable@1.1.0': {} '@rspack/plugin-react-refresh@1.6.0(react-refresh@0.18.0)': @@ -12556,16 +12568,15 @@ snapshots: - react - react-dom - '@rstest/core@0.0.10(jsdom@27.4.0)': + '@rstest/adapter-rsbuild@0.2.3(@rsbuild/core@1.7.3)(@rstest/core@0.8.1(jsdom@27.4.0))': dependencies: - '@rsbuild/core': 1.4.9 - '@types/chai': 5.2.2 - '@vitest/expect': 3.2.4 - '@vitest/snapshot': 3.2.4 - birpc: 2.5.0 - chai: 5.2.1 - pathe: 2.0.3 - std-env: 3.9.0 + '@rsbuild/core': 1.7.3 + '@rstest/core': 0.8.1(jsdom@27.4.0) + + '@rstest/core@0.8.1(jsdom@27.4.0)': + dependencies: + '@rsbuild/core': 1.7.2 + '@types/chai': 5.2.3 tinypool: 1.1.1 optionalDependencies: jsdom: 27.4.0 @@ -12895,6 +12906,11 @@ snapshots: dependencies: '@types/deep-eql': 4.0.2 + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.5 @@ -13781,8 +13797,6 @@ snapshots: binaryen@125.0.0: {} - birpc@2.5.0: {} - bl@4.1.0: dependencies: buffer: 5.7.1 @@ -14143,8 +14157,6 @@ snapshots: dependencies: browserslist: 4.28.1 - core-js@3.44.0: {} - core-js@3.47.0: {} core-js@3.48.0: {} @@ -18108,15 +18120,15 @@ snapshots: optionalDependencies: '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) - rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@1.7.3)(tailwindcss@4.2.1): + rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@3.4.19): dependencies: - tailwindcss: 4.2.1 + tailwindcss: 3.4.19 optionalDependencies: - '@rsbuild/core': 1.7.3 + '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) - rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@3.4.19): + rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@4.2.1): dependencies: - tailwindcss: 3.4.19 + tailwindcss: 4.2.1 optionalDependencies: '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f2d62565b7..1a6654643b 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -59,7 +59,7 @@ catalogs: # Rstest monorepo packages rstest: - "@rstest/core": "0.0.10" + "@rstest/core": "0.8.1" overrides: "@rspack/core": "$@rspack/core" From c493518d47c7e432aa0211fd37841d58e3903398 Mon Sep 17 00:00:00 2001 From: 9aoy <9aoyuao@gmail.com> Date: Wed, 18 Mar 2026 19:46:19 +0800 Subject: [PATCH 06/29] fix: rstest lock version (#2349) @coderabbitai summary fix rstest lock version. https://github.com/lynx-family/lynx-stack/actions/runs/23236933928/job/67543097691?pr=2328 ## Checklist - [ ] Tests updated (or not required). - [ ] Documentation updated (or not required). - [ ] Changeset added, and when a BREAKING CHANGE occurs, it needs to be clearly marked (or not required). --- pnpm-lock.yaml | 71 +++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 55ff04d659..24f857e7d8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -282,7 +282,7 @@ importers: version: 3.7.0 '@rsbuild/plugin-babel': specifier: 1.1.0 - version: 1.1.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) + version: 1.1.0(@rsbuild/core@1.7.3) '@types/react': specifier: ^18.3.28 version: 18.3.28 @@ -1140,10 +1140,10 @@ importers: version: link:../../web-platform/web-elements '@rsbuild/plugin-less': specifier: 1.6.0 - version: 1.6.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) + version: 1.6.0(@rsbuild/core@1.7.3) '@rsbuild/plugin-sass': specifier: 1.5.0 - version: 1.5.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) + version: 1.5.0(@rsbuild/core@1.7.3) commander: specifier: ^13.1.0 version: 13.1.0 @@ -1158,7 +1158,7 @@ importers: version: 1.1.1 rsbuild-plugin-tailwindcss: specifier: 0.2.4 - version: 0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@4.2.1) + version: 0.2.4(@rsbuild/core@1.7.3)(tailwindcss@4.2.1) rslog: specifier: ^1.3.2 version: 1.3.2 @@ -1242,7 +1242,7 @@ importers: version: 3.7.0 '@rsbuild/plugin-babel': specifier: 1.1.0 - version: 1.1.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) + version: 1.1.0(@rsbuild/core@1.7.3) '@testing-library/jest-dom': specifier: ^6.9.1 version: 6.9.1 @@ -2008,13 +2008,13 @@ importers: version: 7.33.4(@types/node@24.10.13) '@rsbuild/plugin-sass': specifier: 1.5.0 - version: 1.5.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) + version: 1.5.0(@rsbuild/core@1.7.3) '@rsbuild/plugin-type-check': specifier: 1.3.4 - version: 1.3.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(@rspack/core@1.7.7(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.9.3) + version: 1.3.4(@rsbuild/core@1.7.3)(@rspack/core@1.7.7(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.9.3) '@rsbuild/plugin-typed-css-modules': specifier: 1.2.2 - version: 1.2.2(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) + version: 1.2.2(@rsbuild/core@1.7.3) '@rspress/core': specifier: 2.0.3 version: 2.0.3(@types/react@19.2.14)(core-js@3.48.0) @@ -4450,9 +4450,6 @@ packages: '@types/bonjour@3.5.13': resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} - '@types/chai@5.2.2': - resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} - '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -12161,13 +12158,13 @@ snapshots: optionalDependencies: core-js: 3.48.0 - '@rsbuild/plugin-babel@1.1.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': + '@rsbuild/plugin-babel@1.1.0(@rsbuild/core@1.7.3)': dependencies: '@babel/core': 7.29.0 '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0) '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) + '@rsbuild/core': 1.7.3 '@types/babel__core': 7.20.5 deepmerge: 4.3.1 reduce-configs: 1.1.1 @@ -12207,9 +12204,9 @@ snapshots: optionalDependencies: '@rsbuild/core': 1.7.3 - '@rsbuild/plugin-less@1.6.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': + '@rsbuild/plugin-less@1.6.0(@rsbuild/core@1.7.3)': dependencies: - '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) + '@rsbuild/core': 1.7.3 deepmerge: 4.3.1 reduce-configs: 1.1.1 @@ -12238,15 +12235,6 @@ snapshots: reduce-configs: 1.1.1 sass-embedded: 1.97.3 - '@rsbuild/plugin-sass@1.5.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': - dependencies: - '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) - deepmerge: 4.3.1 - loader-utils: 2.0.4 - postcss: 8.5.6 - reduce-configs: 1.1.1 - sass-embedded: 1.97.3 - '@rsbuild/plugin-source-build@1.0.4(@rsbuild/core@1.7.3)': dependencies: fast-glob: 3.3.3 @@ -12255,6 +12243,19 @@ snapshots: optionalDependencies: '@rsbuild/core': 1.7.3 + '@rsbuild/plugin-type-check@1.3.4(@rsbuild/core@1.7.3)(@rspack/core@1.7.7(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.9.3)': + dependencies: + deepmerge: 4.3.1 + json5: 2.2.3 + reduce-configs: 1.1.1 + ts-checker-rspack-plugin: 1.3.0(@rspack/core@1.7.7(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.9.3) + optionalDependencies: + '@rsbuild/core': 1.7.3 + transitivePeerDependencies: + - '@rspack/core' + - tslib + - typescript + '@rsbuild/plugin-type-check@1.3.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(@rspack/core@1.7.7(@swc/helpers@0.5.18))(tslib@2.8.1)(typescript@5.9.3)': dependencies: deepmerge: 4.3.1 @@ -12272,10 +12273,6 @@ snapshots: optionalDependencies: '@rsbuild/core': 1.7.3 - '@rsbuild/plugin-typed-css-modules@1.2.2(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': - optionalDependencies: - '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) - '@rsdoctor/client@1.2.3': {} '@rsdoctor/core@1.2.3(@rsbuild/core@1.7.3)(@rspack/core@1.7.7(@swc/helpers@0.5.18))(webpack@5.105.2)': @@ -12940,10 +12937,6 @@ snapshots: dependencies: '@types/node': 24.10.13 - '@types/chai@5.2.2': - dependencies: - '@types/deep-eql': 4.0.2 - '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -13401,7 +13394,7 @@ snapshots: '@vitest/expect@3.2.4': dependencies: - '@types/chai': 5.2.2 + '@types/chai': 5.2.3 '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 chai: 5.2.1 @@ -18162,15 +18155,15 @@ snapshots: optionalDependencies: '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) - rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@3.4.19): + rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@1.7.3)(tailwindcss@4.2.1): dependencies: - tailwindcss: 3.4.19 + tailwindcss: 4.2.1 optionalDependencies: - '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) + '@rsbuild/core': 1.7.3 - rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@4.2.1): + rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@3.4.19): dependencies: - tailwindcss: 4.2.1 + tailwindcss: 3.4.19 optionalDependencies: '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) @@ -19388,7 +19381,7 @@ snapshots: vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.13)(@vitest/ui@3.2.4)(jsdom@27.4.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.31.6): dependencies: - '@types/chai': 5.2.2 + '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 '@vitest/mocker': 3.2.4(vite@5.4.2(@types/node@24.10.13)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.31.6)) '@vitest/pretty-format': 3.2.4 From 62a148193b61368de9efe9ec6e18fc6b95b0f6f7 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 19 Mar 2026 11:14:57 +0800 Subject: [PATCH 07/29] Update .github/workflows/test.yml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Yiming Li --- .github/workflows/test.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9de5e733fe..db62a74397 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -207,6 +207,13 @@ jobs: pnpm run build --mode development pnpm run lint pnpm run test + npx --registry http://localhost:4873 create-rspeedy-canary@latest --template react-vitest-rltl --dir create-rspeedy-regression-vitest-rltl + cd create-rspeedy-regression-vitest-rltl + pnpm install --registry=http://localhost:4873 + pnpm run build + pnpm run lint + pnpm run test + cd "$(mktemp -d)" npx --registry http://localhost:4873 create-rspeedy-canary@latest --template react-rstest-rltl --dir create-rspeedy-regression-rstest-rltl cd create-rspeedy-regression-rstest-rltl pnpm install --registry=http://localhost:4873 From ee27d395f302a76ffd46b67c90025e2e127e1311 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 19 Mar 2026 11:15:14 +0800 Subject: [PATCH 08/29] Update packages/react/testing-library/.npmignore Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Yiming Li --- packages/react/testing-library/.npmignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react/testing-library/.npmignore b/packages/react/testing-library/.npmignore index 07238e1865..b1e6104efa 100644 --- a/packages/react/testing-library/.npmignore +++ b/packages/react/testing-library/.npmignore @@ -1,3 +1,6 @@ * +* +!dist !dist/**/* +!types !types/* From 07736513836eaeece873bfe49cab3f4df7d4c306 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 19 Mar 2026 11:15:36 +0800 Subject: [PATCH 09/29] Update packages/react/testing-library/src/__tests__/css/index.test.jsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Yiming Li --- packages/react/testing-library/src/__tests__/css/index.test.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/testing-library/src/__tests__/css/index.test.jsx b/packages/react/testing-library/src/__tests__/css/index.test.jsx index ba72bea45b..86d10c66fe 100644 --- a/packages/react/testing-library/src/__tests__/css/index.test.jsx +++ b/packages/react/testing-library/src/__tests__/css/index.test.jsx @@ -34,7 +34,7 @@ describe('CSS', () => { `); }); it('should render a component with CSS module styles object', () => { - // to be an string + // to be a string expect(style3.baz).toBeTypeOf('string'); const TestComponent = () => Hello World; From f581194cd664c3d78d58bb978e92cf5094a2ce56 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 19 Mar 2026 11:24:55 +0800 Subject: [PATCH 10/29] Update packages/react/testing-library/src/setupFiles/common/runtime-setup.js Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Yiming Li --- .../testing-library/src/setupFiles/common/runtime-setup.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/react/testing-library/src/setupFiles/common/runtime-setup.js b/packages/react/testing-library/src/setupFiles/common/runtime-setup.js index 2856ca02f2..d3dc752fed 100644 --- a/packages/react/testing-library/src/setupFiles/common/runtime-setup.js +++ b/packages/react/testing-library/src/setupFiles/common/runtime-setup.js @@ -129,8 +129,11 @@ globalThis.onInjectBackgroundThreadGlobals = (target) => { // intercept lynxCoreInject assignments to lynxTestingEnv.backgroundThread.globalThis.lynxCoreInject const oldLynxCoreInject = globalThis.lynxCoreInject; globalThis.lynxCoreInject = target.lynxCoreInject; - injectTt(); - globalThis.lynxCoreInject = oldLynxCoreInject; + try { + injectTt(); + } finally { + globalThis.lynxCoreInject = oldLynxCoreInject; + } // re-init global snapshot patch to undefined deinitGlobalSnapshotPatch(); From 1c8cf5fe72d5cc66e8a27e8ed0d36c5312ae6982 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 19 Mar 2026 12:59:22 +0800 Subject: [PATCH 11/29] feat: add rstest support to testing-environment --- .changeset/fair-plums-share.md | 11 ++++ .../react/testing-library/src/env/index.ts | 3 +- .../react/testing-library/src/env/rstest.ts | 3 +- packages/react/testing-library/src/pure.jsx | 4 +- .../src/setupFiles/inner/rstest.js | 10 +--- packages/react/testing-library/tsconfig.json | 2 +- .../testing-environment/README.md | 10 +++- .../testing-environment/package.json | 10 ++++ .../testing-environment/rstest.config.ts | 16 +++++ .../src/env/rstest/index.ts | 3 + .../src/env/vitest/index.ts | 21 ++++--- .../testing-environment/src/index.ts | 60 ++++++++++++++----- .../src/lynx/ElementPAPI.ts | 6 +- .../src/setupFiles/rstest.js | 1 + .../testing-environment/tsconfig.json | 2 + .../testing-environment/vitest-polyfill.cjs | 6 ++ 16 files changed, 123 insertions(+), 45 deletions(-) create mode 100644 .changeset/fair-plums-share.md create mode 100644 packages/testing-library/testing-environment/rstest.config.ts create mode 100644 packages/testing-library/testing-environment/src/env/rstest/index.ts create mode 100644 packages/testing-library/testing-environment/src/setupFiles/rstest.js create mode 100644 packages/testing-library/testing-environment/vitest-polyfill.cjs diff --git a/.changeset/fair-plums-share.md b/.changeset/fair-plums-share.md new file mode 100644 index 0000000000..a6a64189b7 --- /dev/null +++ b/.changeset/fair-plums-share.md @@ -0,0 +1,11 @@ +--- +"@lynx-js/testing-environment": minor +--- + +**BREAKING CHANGE**: + +Align the public test-environment API around `LynxEnv`. + +`LynxTestingEnv` now expects a `{ window }`-shaped environment instead of relying on a concrete `JSDOM` instance or `global.jsdom`. Callers that construct `LynxTestingEnv` manually or initialize the environment through globals should migrate to `new LynxTestingEnv({ window })` or set `global.lynxEnv`. + +This release also adds the `@lynx-js/testing-environment/env/rstest` entry for running the shared testing-environment suite under rstest. diff --git a/packages/react/testing-library/src/env/index.ts b/packages/react/testing-library/src/env/index.ts index 672db9f107..cd62eb3d54 100644 --- a/packages/react/testing-library/src/env/index.ts +++ b/packages/react/testing-library/src/env/index.ts @@ -1 +1,2 @@ -export { LynxTestingEnv } from '@lynx-js/testing-environment'; +export { LynxTestingEnv, installLynxTestingEnv, uninstallLynxTestingEnv } from '@lynx-js/testing-environment'; +export type { LynxEnv } from '@lynx-js/testing-environment'; diff --git a/packages/react/testing-library/src/env/rstest.ts b/packages/react/testing-library/src/env/rstest.ts index d4b2359730..de16c1a402 100644 --- a/packages/react/testing-library/src/env/rstest.ts +++ b/packages/react/testing-library/src/env/rstest.ts @@ -1,7 +1,6 @@ import { LynxTestingEnv } from './index.js'; -// @ts-ignore -global.jsdom = { +global.lynxEnv = { window, }; const lynxTestingEnv = new LynxTestingEnv(); diff --git a/packages/react/testing-library/src/pure.jsx b/packages/react/testing-library/src/pure.jsx index a8b6fbdcbb..95eab120a1 100644 --- a/packages/react/testing-library/src/pure.jsx +++ b/packages/react/testing-library/src/pure.jsx @@ -91,7 +91,7 @@ export function render( }); }, asFragment: () => { - const { document } = lynxTestingEnv.jsdom.window; + const { document } = lynxTestingEnv.env.window; const container = lynxTestingEnv.mainThread.elementTree.root; if (typeof document.createRange === 'function') { return document @@ -118,7 +118,7 @@ export function cleanup() { lynxTestingEnv.mainThread.elementTree.root = undefined; clearPage(); - lynxTestingEnv.jsdom.window.document.body.innerHTML = ''; + lynxTestingEnv.env.window.document.body.innerHTML = ''; if (isMainThread) { globalThis.lynxTestingEnv.switchToMainThread(); diff --git a/packages/react/testing-library/src/setupFiles/inner/rstest.js b/packages/react/testing-library/src/setupFiles/inner/rstest.js index 6f8c194217..26c510b596 100644 --- a/packages/react/testing-library/src/setupFiles/inner/rstest.js +++ b/packages/react/testing-library/src/setupFiles/inner/rstest.js @@ -1,11 +1,3 @@ -import { LynxTestingEnv } from '../../env'; - -global.jsdom = { - window, -}; -const lynxTestingEnv = new LynxTestingEnv(); -global.lynxTestingEnv = lynxTestingEnv; - const { onInjectBackgroundThreadGlobals, } = globalThis; @@ -15,7 +7,7 @@ globalThis.onInjectBackgroundThreadGlobals = (target) => { onInjectBackgroundThreadGlobals(target); } - target.lynx.requireModuleAsync = async (url, callback) => { + target.lynx.requireModuleAsync = async (url, _callback) => { throw new Error('lynx.requireModuleAsync not implemented for rstest'); }; }; diff --git a/packages/react/testing-library/tsconfig.json b/packages/react/testing-library/tsconfig.json index aac4b8ccfb..06f9114c72 100644 --- a/packages/react/testing-library/tsconfig.json +++ b/packages/react/testing-library/tsconfig.json @@ -6,7 +6,7 @@ "rootDir": "src", "stripInternal": true, "target": "ESNext", - "lib": ["es2021"], + "lib": ["es2021", "dom"], "module": "Node16", "moduleResolution": "Node16", "resolveJsonModule": true, diff --git a/packages/testing-library/testing-environment/README.md b/packages/testing-library/testing-environment/README.md index 9a67389df3..9d1cac587e 100644 --- a/packages/testing-library/testing-environment/README.md +++ b/packages/testing-library/testing-environment/README.md @@ -10,7 +10,7 @@ The Element PAPI implementation is based on jsdom, for example `__CreateElement` import { LynxTestingEnv } from '@lynx-js/testing-environment'; import { JSDOM } from 'jsdom'; -const lynxTestingEnv = new LynxTestingEnv(new JSDOM()); +const lynxTestingEnv = new LynxTestingEnv({ window: new JSDOM().window }); ``` To use `@lynx-js/testing-environment`, you will primarily use the `LynxTestingEnv` constructor, which is a named export of the package. You will get back a `LynxTestingEnv` instance, which has a number of methods of useful properties, notably `switchToMainThread` and `switchToBackgroundThread`, which allow you to switch between the main thread and background thread. @@ -66,6 +66,14 @@ If you want to use `@lynx-js/testing-environment` for unit testing in ReactLynx, Please refer to [ReactLynx Testing Library](https://lynxjs.org/react/reactlynx-testing-library.html) to inherit the configuration from `@lynx-js/react/testing-library`. +### Use in Rstest + +If your runner already provides a `window` global via jsdom, you can load the shared Lynx test environment with: + +```js +import '@lynx-js/testing-environment/env/rstest'; +``` + ## Credits Thanks to: diff --git a/packages/testing-library/testing-environment/package.json b/packages/testing-library/testing-environment/package.json index 7b8985e13a..50c06bebb8 100644 --- a/packages/testing-library/testing-environment/package.json +++ b/packages/testing-library/testing-environment/package.json @@ -30,6 +30,12 @@ "import": "./dist/env/vitest/index.js", "require": "./dist/env/vitest/index.cjs", "default": "./dist/env/vitest/index.js" + }, + "./env/rstest": { + "types": "./dist/env/rstest/index.d.ts", + "import": "./dist/env/rstest/index.js", + "require": "./dist/env/rstest/index.cjs", + "default": "./dist/env/rstest/index.js" } }, "main": "./dist/index.cjs", @@ -39,6 +45,9 @@ "*": { "env/vitest": [ "./dist/env/vitest/index.d.ts" + ], + "env/rstest": [ + "./dist/env/rstest/index.d.ts" ] } }, @@ -50,6 +59,7 @@ "api-extractor": "api-extractor run --verbose", "build": "rslib build", "dev": "rslib build --watch", + "rstest": "rstest", "test": "vitest" }, "devDependencies": { diff --git a/packages/testing-library/testing-environment/rstest.config.ts b/packages/testing-library/testing-environment/rstest.config.ts new file mode 100644 index 0000000000..107b5a06dd --- /dev/null +++ b/packages/testing-library/testing-environment/rstest.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from '@rstest/core'; + +export default defineConfig({ + testEnvironment: 'jsdom', + setupFiles: [ + require.resolve('./src/setupFiles/rstest.js'), + ], + globals: true, + resolve: { + alias: { + // Allow the shared test files to keep importing from `vitest`. + vitest: require.resolve('./vitest-polyfill.cjs'), + }, + }, + include: ['src/**/*.test.{js,jsx,ts,tsx}'], +}); diff --git a/packages/testing-library/testing-environment/src/env/rstest/index.ts b/packages/testing-library/testing-environment/src/env/rstest/index.ts new file mode 100644 index 0000000000..b4759c1c42 --- /dev/null +++ b/packages/testing-library/testing-environment/src/env/rstest/index.ts @@ -0,0 +1,3 @@ +import { installLynxTestingEnv } from '../../index.js'; + +installLynxTestingEnv(globalThis, { window }); diff --git a/packages/testing-library/testing-environment/src/env/vitest/index.ts b/packages/testing-library/testing-environment/src/env/vitest/index.ts index e5836eab90..fdd1c9d925 100644 --- a/packages/testing-library/testing-environment/src/env/vitest/index.ts +++ b/packages/testing-library/testing-environment/src/env/vitest/index.ts @@ -1,6 +1,5 @@ -import { builtinEnvironments, Environment } from 'vitest/environments'; -import { LynxTestingEnv } from '@lynx-js/testing-environment'; -import { JSDOM } from 'jsdom'; +import { builtinEnvironments, type Environment } from 'vitest/environments'; +import { installLynxTestingEnv, uninstallLynxTestingEnv } from '../../index.js'; const env = { name: 'lynxTestingEnv', @@ -9,17 +8,17 @@ const env = { const fakeGlobal: { jsdom?: any; } = {}; - await builtinEnvironments.jsdom.setup(fakeGlobal, {}); + const jsdomEnvironment = await builtinEnvironments.jsdom.setup( + fakeGlobal, + {}, + ); - const lynxTestingEnv = new LynxTestingEnv(fakeGlobal.jsdom as JSDOM); - global.lynxTestingEnv = lynxTestingEnv; - global.Node = lynxTestingEnv.jsdom.window.Node; + installLynxTestingEnv(global, fakeGlobal.jsdom); return { - teardown(global) { - delete global.lynxTestingEnv; - delete global.jsdom; - delete global.Node; + async teardown(global) { + await jsdomEnvironment.teardown(fakeGlobal); + uninstallLynxTestingEnv(global); }, }; }, diff --git a/packages/testing-library/testing-environment/src/index.ts b/packages/testing-library/testing-environment/src/index.ts index 888363cd6e..7d2e1ec1a0 100644 --- a/packages/testing-library/testing-environment/src/index.ts +++ b/packages/testing-library/testing-environment/src/index.ts @@ -6,13 +6,21 @@ */ import EventEmitter from 'node:events'; -import { JSDOM } from 'jsdom'; import { createGlobalThis, LynxGlobalThis } from './lynx/GlobalThis.js'; import { initElementTree, type LynxElement } from './lynx/ElementPAPI.js'; import { GlobalEventEmitter } from './lynx/GlobalEventEmitter.js'; export { initElementTree } from './lynx/ElementPAPI.js'; export type { LynxElement } from './lynx/ElementPAPI.js'; export type { LynxGlobalThis } from './lynx/GlobalThis.js'; +export interface LynxEnv { + window: Window & typeof globalThis; +} + +type LynxEnvironmentGlobal = typeof globalThis & { + lynxEnv?: LynxEnv; + lynxTestingEnv?: LynxTestingEnv; + Node?: typeof Node; +}; /** * @public * The lynx element tree @@ -35,6 +43,7 @@ export type PickUnderscoreKeys = Pick>; export type ElementTreeGlobals = PickUnderscoreKeys; declare global { + var lynxEnv: LynxEnv; var lynxTestingEnv: LynxTestingEnv; var elementTree: ElementTree; var __JS__: boolean; @@ -54,6 +63,23 @@ declare global { function onInitWorkletRuntime(): void; } +export function installLynxTestingEnv( + target: LynxEnvironmentGlobal, + env: LynxEnv, +): void { + target.lynxEnv = env; + target.lynxTestingEnv = new LynxTestingEnv(env); + target.Node = env.window.Node; +} + +export function uninstallLynxTestingEnv( + target: LynxEnvironmentGlobal, +): void { + delete target.lynxTestingEnv; + delete target.lynxEnv; + delete target.Node; +} + function __injectElementApi(target?: any) { const elementTree = initElementTree(); target.elementTree = elementTree; @@ -397,7 +423,7 @@ function injectBackgroundThreadGlobals(target?: any, polyfills?: any) { }); }, select: function(selector: string) { - const el = lynxTestingEnv.jsdom.window.document.querySelector( + const el = lynxTestingEnv.env.window.document.querySelector( selector, ) as LynxElement; if (!el) { @@ -469,8 +495,9 @@ function injectBackgroundThreadGlobals(target?: any, polyfills?: any) { * * ```ts * import { LynxTestingEnv } from '@lynx-js/testing-environment'; + * import { JSDOM } from 'jsdom'; * - * const lynxTestingEnv = new LynxTestingEnv(new JSDOM()); + * const lynxTestingEnv = new LynxTestingEnv({ window: new JSDOM().window }); * * lynxTestingEnv.switchToMainThread(); * // use the main thread Element PAPI @@ -491,8 +518,9 @@ export class LynxTestingEnv { * * ```ts * import { LynxTestingEnv } from '@lynx-js/testing-environment'; + * import { JSDOM } from 'jsdom'; * - * const lynxTestingEnv = new LynxTestingEnv(new JSDOM()); + * const lynxTestingEnv = new LynxTestingEnv({ window: new JSDOM().window }); * * lynxTestingEnv.switchToBackgroundThread(); * // use the background thread global object @@ -507,8 +535,9 @@ export class LynxTestingEnv { * * ```ts * import { LynxTestingEnv } from '@lynx-js/testing-environment'; + * import { JSDOM } from 'jsdom'; * - * const lynxTestingEnv = new LynxTestingEnv(new JSDOM()); + * const lynxTestingEnv = new LynxTestingEnv({ window: new JSDOM().window }); * * lynxTestingEnv.switchToMainThread(); * // use the main thread global object @@ -518,14 +547,15 @@ export class LynxTestingEnv { * ``` */ mainThread: LynxGlobalThis & ElementTreeGlobals; - jsdom: JSDOM; - constructor(jsdom?: JSDOM) { + env: LynxEnv; + constructor(env?: LynxEnv) { // Prefer explicit instance; fall back to test runner-provided global. - this.jsdom = jsdom ?? global.jsdom; - if (!this.jsdom) { + this.env = (env ?? global.lynxEnv) as LynxEnv; + if (!this.env) { throw new Error( - 'LynxTestingEnv requires a JSDOM instance. Pass one to the constructor, ' - + 'or ensure your test runner sets global.jsdom (e.g., via a setup file).', + 'LynxTestingEnv requires an object with a jsdom-like `window`. Pass ' + + '`{ window }` to the constructor, or ensure your test runner sets ' + + 'global.lynxEnv to that shape (e.g., via a setup file).', ); } @@ -533,13 +563,13 @@ export class LynxTestingEnv { this.mainThread = createGlobalThis() as any; const globalPolyfills = { - console: this.jsdom.window['console'], + console: this.env.window['console'], // `Event` is required by `fireEvent` in `@testing-library/dom` - Event: this.jsdom.window.Event, + Event: this.env.window.Event, // `window` is required by `getDocument` in `@testing-library/dom` - window: this.jsdom.window, + window: this.env.window, // `document` is required by `screen` in `@testing-library/dom` - document: this.jsdom.window.document, + document: this.env.window.document, }; Object.assign( diff --git a/packages/testing-library/testing-environment/src/lynx/ElementPAPI.ts b/packages/testing-library/testing-environment/src/lynx/ElementPAPI.ts index 5503194804..a3f5187c1f 100644 --- a/packages/testing-library/testing-environment/src/lynx/ElementPAPI.ts +++ b/packages/testing-library/testing-environment/src/lynx/ElementPAPI.ts @@ -80,12 +80,12 @@ export const initElementTree = () => { const page = this.__CreateElement('page', parentComponentUniqueId); this.root = page; document.body.innerHTML = ''; - lynxTestingEnv.jsdom.window.document.body.appendChild(page); + lynxTestingEnv.env.window.document.body.appendChild(page); return page; } __CreateRawText(text: string): LynxElement { - const element = lynxTestingEnv.jsdom.window.document + const element = lynxTestingEnv.env.window.document .createTextNode( text, ) as unknown as LynxElement; @@ -110,7 +110,7 @@ export const initElementTree = () => { return this.__CreateRawText(''); } - const element = lynxTestingEnv.jsdom.window.document + const element = lynxTestingEnv.env.window.document .createElement( tag, ) as LynxElement; diff --git a/packages/testing-library/testing-environment/src/setupFiles/rstest.js b/packages/testing-library/testing-environment/src/setupFiles/rstest.js new file mode 100644 index 0000000000..41d82b0219 --- /dev/null +++ b/packages/testing-library/testing-environment/src/setupFiles/rstest.js @@ -0,0 +1 @@ +import '../env/rstest/index.js'; diff --git a/packages/testing-library/testing-environment/tsconfig.json b/packages/testing-library/testing-environment/tsconfig.json index 88f6924cbf..75db74d64e 100644 --- a/packages/testing-library/testing-environment/tsconfig.json +++ b/packages/testing-library/testing-environment/tsconfig.json @@ -5,6 +5,8 @@ "noImplicitAny": false, "isolatedDeclarations": false, "rootDir": "src", + "outDir": "dist", + "declarationDir": "dist", }, "include": [ diff --git a/packages/testing-library/testing-environment/vitest-polyfill.cjs b/packages/testing-library/testing-environment/vitest-polyfill.cjs new file mode 100644 index 0000000000..647f2e4a8d --- /dev/null +++ b/packages/testing-library/testing-environment/vitest-polyfill.cjs @@ -0,0 +1,6 @@ +// in order to make our test case work for +// both vitest and rstest, we need to alias +// `vitest` to `@rstest/core` + +global['@rstest/core'].vi = global['@rstest/core'].rs; +module.exports = global['@rstest/core']; From 340592e72001074dd14af5cb3db48ef88ff3b185 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 19 Mar 2026 14:00:01 +0800 Subject: [PATCH 12/29] fix: align rstest environment and test loader options --- packages/react/package.json | 1 - .../react/testing-library/rstest.config.ts | 2 +- .../react/testing-library/src/env/vitest.ts | 17 +++++----- .../template-react-rstest-rltl-js/src/App.jsx | 2 +- .../src/__tests__/index.test.jsx | 2 +- .../etc/testing-environment.api.md | 28 +++++++++++++--- .../testing-environment/src/index.ts | 33 +++++++++++++++---- .../src/loaders/testing.ts | 15 ++++++--- 8 files changed, 72 insertions(+), 28 deletions(-) diff --git a/packages/react/package.json b/packages/react/package.json index c72fe907da..6195b645da 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -108,7 +108,6 @@ "types": "./testing-library/dist/rstest-adapter.d.ts", "default": "./testing-library/dist/rstest-adapter.js" }, - "./testing-library/setupFiles/rstest": "./testing-library/dist/setupFiles/rstest.js", "./package.json": "./package.json" }, "types": "./types/react.d.ts", diff --git a/packages/react/testing-library/rstest.config.ts b/packages/react/testing-library/rstest.config.ts index 1100a0b882..804a178e9b 100644 --- a/packages/react/testing-library/rstest.config.ts +++ b/packages/react/testing-library/rstest.config.ts @@ -21,7 +21,7 @@ export default defineConfig({ }, testEnvironment: 'jsdom', setupFiles: [ - require.resolve('@lynx-js/react/testing-library/setupFiles/rstest'), + require.resolve('./src/setupFiles/rstest.js'), ], globals: true, resolve: { diff --git a/packages/react/testing-library/src/env/vitest.ts b/packages/react/testing-library/src/env/vitest.ts index 47da348ef0..c6ff6608f0 100644 --- a/packages/react/testing-library/src/env/vitest.ts +++ b/packages/react/testing-library/src/env/vitest.ts @@ -1,5 +1,5 @@ import { builtinEnvironments, type Environment } from 'vitest/environments'; -import { LynxTestingEnv } from './index.js'; +import { installLynxTestingEnv, uninstallLynxTestingEnv } from './index.js'; const env: Environment = { name: 'lynxTestingEnv', @@ -8,16 +8,17 @@ const env: Environment = { const fakeGlobal: { jsdom?: any; } = {}; - await builtinEnvironments.jsdom.setup(fakeGlobal, {}); - global.jsdom = fakeGlobal.jsdom; + const jsdomEnvironment = await builtinEnvironments.jsdom.setup( + fakeGlobal, + {}, + ); - const lynxTestingEnv = new LynxTestingEnv(); - global.lynxTestingEnv = lynxTestingEnv; + installLynxTestingEnv(global, fakeGlobal.jsdom); return { - teardown(global) { - delete global.lynxTestingEnv; - delete global.jsdom; + async teardown(global) { + await jsdomEnvironment.teardown(fakeGlobal); + uninstallLynxTestingEnv(global); }, }; }, diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/App.jsx b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/App.jsx index 7618894b24..e1d178f90b 100644 --- a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/App.jsx +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/App.jsx @@ -41,7 +41,7 @@ export function App(props) { color: 'rgba(255, 255, 255, 0.85)', }} > - {' src/App.tsx '} + {' src/App.jsx '} to see updates! diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/__tests__/index.test.jsx b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/__tests__/index.test.jsx index e7f2736b0b..0d61dc1a96 100644 --- a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/__tests__/index.test.jsx +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl-js/src/__tests__/index.test.jsx @@ -75,7 +75,7 @@ test('App', async () => { - src/App.tsx + src/App.jsx to see updates! diff --git a/packages/testing-library/testing-environment/etc/testing-environment.api.md b/packages/testing-library/testing-environment/etc/testing-environment.api.md index 5e78499041..9b975bd350 100644 --- a/packages/testing-library/testing-environment/etc/testing-environment.api.md +++ b/packages/testing-library/testing-environment/etc/testing-environment.api.md @@ -4,8 +4,6 @@ ```ts -import { JSDOM } from 'jsdom'; - // @public export type ElementTree = ReturnType; @@ -73,6 +71,13 @@ export const initElementTree: () => { __GetElementByUniqueId(uniqueId: number): LynxElement | undefined; }; +// @public +export function installLynxTestingEnv(target: typeof globalThis & { + lynxEnv?: LynxEnv; + lynxTestingEnv?: LynxTestingEnv; + Node?: typeof Node; +}, env: LynxEnv): void; + // @public export interface LynxElement extends HTMLElement { cssId?: string; @@ -87,6 +92,12 @@ export interface LynxElement extends HTMLElement { parentNode: LynxElement; } +// @public +export interface LynxEnv { + // (undocumented) + window: Window & typeof globalThis; +} + // @public export interface LynxGlobalThis { // (undocumented) @@ -96,14 +107,14 @@ export interface LynxGlobalThis { // @public export class LynxTestingEnv { - constructor(jsdom?: JSDOM); + constructor(env?: LynxEnv); backgroundThread: LynxGlobalThis; // (undocumented) clearGlobal(): void; // (undocumented) - injectGlobals(): void; + env: LynxEnv; // (undocumented) - jsdom: JSDOM; + injectGlobals(): void; mainThread: LynxGlobalThis & ElementTreeGlobals; // (undocumented) reset(): void; @@ -116,4 +127,11 @@ export class LynxTestingEnv { // @public (undocumented) export type PickUnderscoreKeys = Pick>; +// @public +export function uninstallLynxTestingEnv(target: typeof globalThis & { + lynxEnv?: LynxEnv; + lynxTestingEnv?: LynxTestingEnv; + Node?: typeof Node; +}): void; + ``` diff --git a/packages/testing-library/testing-environment/src/index.ts b/packages/testing-library/testing-environment/src/index.ts index 7d2e1ec1a0..bb729f1aff 100644 --- a/packages/testing-library/testing-environment/src/index.ts +++ b/packages/testing-library/testing-environment/src/index.ts @@ -12,15 +12,16 @@ import { GlobalEventEmitter } from './lynx/GlobalEventEmitter.js'; export { initElementTree } from './lynx/ElementPAPI.js'; export type { LynxElement } from './lynx/ElementPAPI.js'; export type { LynxGlobalThis } from './lynx/GlobalThis.js'; + +/** + * The host environment used to initialize `LynxTestingEnv`. + * + * @public + */ export interface LynxEnv { window: Window & typeof globalThis; } -type LynxEnvironmentGlobal = typeof globalThis & { - lynxEnv?: LynxEnv; - lynxTestingEnv?: LynxTestingEnv; - Node?: typeof Node; -}; /** * @public * The lynx element tree @@ -63,8 +64,17 @@ declare global { function onInitWorkletRuntime(): void; } +/** + * Installs a `LynxTestingEnv` instance and its required globals onto a target. + * + * @public + */ export function installLynxTestingEnv( - target: LynxEnvironmentGlobal, + target: typeof globalThis & { + lynxEnv?: LynxEnv; + lynxTestingEnv?: LynxTestingEnv; + Node?: typeof Node; + }, env: LynxEnv, ): void { target.lynxEnv = env; @@ -72,8 +82,17 @@ export function installLynxTestingEnv( target.Node = env.window.Node; } +/** + * Removes the globals installed by `installLynxTestingEnv`. + * + * @public + */ export function uninstallLynxTestingEnv( - target: LynxEnvironmentGlobal, + target: typeof globalThis & { + lynxEnv?: LynxEnv; + lynxTestingEnv?: LynxTestingEnv; + Node?: typeof Node; + }, ): void { delete target.lynxTestingEnv; delete target.lynxEnv; diff --git a/packages/webpack/react-webpack-plugin/src/loaders/testing.ts b/packages/webpack/react-webpack-plugin/src/loaders/testing.ts index 443489c9fc..8028a56c82 100644 --- a/packages/webpack/react-webpack-plugin/src/loaders/testing.ts +++ b/packages/webpack/react-webpack-plugin/src/loaders/testing.ts @@ -18,7 +18,13 @@ function testingLoader( content: string, ): void { const require = createRequire(import.meta.url); - const { transformPath = '@lynx-js/react/transform' } = this.getOptions(); + const { + compat = false, + defineDCE = { define: {} }, + engineVersion = '', + shake = false, + transformPath = '@lynx-js/react/transform', + } = this.getOptions(); const { transformReactLynxSync } = require( transformPath, ) as typeof import('@lynx-js/react/transform'); @@ -44,9 +50,10 @@ function testingLoader( }, // snapshot: true, directiveDCE: false, - defineDCE: false, - shake: false, - compat: false, + defineDCE, + shake, + compat, + engineVersion, worklet: { filename, runtimePkg: RUNTIME_PKG, From b1636a36be0dd87aca2f506a058aa02089667dc3 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 19 Mar 2026 14:19:58 +0800 Subject: [PATCH 13/29] test: stabilize rstest list snapshot --- .github/workflows/test.yml | 2 +- .../src/__tests__/list.test.jsx | 59 +++++++++---------- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index db62a74397..0c3e8cd5b4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -207,7 +207,7 @@ jobs: pnpm run build --mode development pnpm run lint pnpm run test - npx --registry http://localhost:4873 create-rspeedy-canary@latest --template react-vitest-rltl --dir create-rspeedy-regression-vitest-rltl + npx --registry http://localhost:4873 create-rspeedy-canary@latest --template react-vitest-rltl --dir create-rspeedy-regression-vitest-rltl --tools eslint cd create-rspeedy-regression-vitest-rltl pnpm install --registry=http://localhost:4873 pnpm run build diff --git a/packages/react/testing-library/src/__tests__/list.test.jsx b/packages/react/testing-library/src/__tests__/list.test.jsx index a3e787fdab..70f46d1faa 100644 --- a/packages/react/testing-library/src/__tests__/list.test.jsx +++ b/packages/react/testing-library/src/__tests__/list.test.jsx @@ -359,38 +359,35 @@ describe('list', () => { ] `); expect(__FlushElementTree).toHaveBeenCalledTimes(1); - expect(__FlushElementTree.mock.calls).toMatchInlineSnapshot(` - [ - [ - - - - - 1 - - - 1 - - - - - hello - - - - , - { - "elementID": 33, - "listID": 2, - "operationID": undefined, - "triggerLayout": true, - }, - ], - ] + const [[flushedElement, flushInfo]] = __FlushElementTree.mock.calls; + expect(flushedElement).toMatchInlineSnapshot(` + + + + + 1 + + + 1 + + + + + hello + + + + `); + expect(flushInfo).toMatchObject({ + listID: 2, + operationID: undefined, + triggerLayout: true, + }); + expect(flushInfo.elementID).toBeTypeOf('number'); expect(list).toMatchInlineSnapshot(` Date: Tue, 31 Mar 2026 13:08:51 +0800 Subject: [PATCH 14/29] Update .github/workflows/test.yml Co-authored-by: Qingyu Wang <40660121+colinaaa@users.noreply.github.com> Signed-off-by: Yiming Li --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cf8db230dc..acd8dab0fb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -208,6 +208,7 @@ jobs: pnpm run build --mode development pnpm run lint pnpm run test + cd `mktemp -d` npx --registry http://localhost:4873 create-rspeedy-canary@latest --template react-vitest-rltl --dir create-rspeedy-regression-vitest-rltl --tools eslint cd create-rspeedy-regression-vitest-rltl pnpm install --registry=http://localhost:4873 From 8b0aad18028a36b137dd812410d8fa19f8d90e90 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Tue, 31 Mar 2026 13:21:21 +0800 Subject: [PATCH 15/29] fix(testing-library): externalize rstest dependencies --- packages/react/testing-library/rslib.config.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/react/testing-library/rslib.config.ts b/packages/react/testing-library/rslib.config.ts index 0446793d03..7517f03f37 100644 --- a/packages/react/testing-library/rslib.config.ts +++ b/packages/react/testing-library/rslib.config.ts @@ -22,10 +22,12 @@ export default defineConfig({ output: { externals: [ /^@lynx-js\/react/, + '@lynx-js/testing-environment', /^\.\.\/\.\.\/runtime\/lib/, /^preact/, /^vitest/, '@rstest/core', + '@rstest/adapter-rsbuild', '@rsbuild/core', '@lynx-js/rspeedy', ], From 014ff3872ce6c48fc0f3887bfc428d56abf9ded1 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 2 Apr 2026 10:58:02 +0800 Subject: [PATCH 16/29] Revert "fix(testing-library): externalize rstest dependencies" This reverts commit 8b0aad18028a36b137dd812410d8fa19f8d90e90. --- packages/react/testing-library/rslib.config.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/react/testing-library/rslib.config.ts b/packages/react/testing-library/rslib.config.ts index 7517f03f37..0446793d03 100644 --- a/packages/react/testing-library/rslib.config.ts +++ b/packages/react/testing-library/rslib.config.ts @@ -22,12 +22,10 @@ export default defineConfig({ output: { externals: [ /^@lynx-js\/react/, - '@lynx-js/testing-environment', /^\.\.\/\.\.\/runtime\/lib/, /^preact/, /^vitest/, '@rstest/core', - '@rstest/adapter-rsbuild', '@rsbuild/core', '@lynx-js/rspeedy', ], From f50b0a5bb0846eab26843c182bfa49dee2404628 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 2 Apr 2026 11:09:41 +0800 Subject: [PATCH 17/29] fix: test snapshots --- packages/rspeedy/plugin-react/test/config.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/rspeedy/plugin-react/test/config.test.ts b/packages/rspeedy/plugin-react/test/config.test.ts index cc11c34727..b229086cd7 100644 --- a/packages/rspeedy/plugin-react/test/config.test.ts +++ b/packages/rspeedy/plugin-react/test/config.test.ts @@ -2716,12 +2716,12 @@ describe('Config', () => { ).toMatchInlineSnapshot(` [ [ - "/node_modules//@rspack/core/dist/cssExtractLoader.js", - "/node_modules//@rsbuild/core/compiled/css-loader/index.js", + "/node_modules//@rspack/core/dist/cssExtractLoader.js", + "/node_modules//@rsbuild/core/compiled/css-loader/index.js", "builtin:lightningcss-loader", ], [ - "/node_modules//@rsbuild/core/compiled/css-loader/index.js", + "/node_modules//@rsbuild/core/compiled/css-loader/index.js", "builtin:lightningcss-loader", ], [], @@ -2743,7 +2743,7 @@ describe('Config', () => { [ [ "builtin:swc-loader", - "/packages/webpack/react-webpack-plugin/lib/loaders/testing.js", + "/packages/webpack/react-webpack-plugin/lib/loaders/testing.js", ], [], [], From e23963992737ed3d52f5c64484ebc019f7d1d3d2 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 2 Apr 2026 15:46:30 +0800 Subject: [PATCH 18/29] fix: remove extra code --- .../src/setupFiles/common/runtime-setup.js | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/packages/react/testing-library/src/setupFiles/common/runtime-setup.js b/packages/react/testing-library/src/setupFiles/common/runtime-setup.js index 997e50489a..45d3c12433 100644 --- a/packages/react/testing-library/src/setupFiles/common/runtime-setup.js +++ b/packages/react/testing-library/src/setupFiles/common/runtime-setup.js @@ -1,5 +1,4 @@ import { options } from 'preact'; -import { expect } from 'vitest'; import { clearCommitTaskId, replaceCommitHook } from '../../../../runtime/lib/lifecycle/patch/commit.js'; import { deinitGlobalSnapshotPatch } from '../../../../runtime/lib/lifecycle/patch/snapshotPatch.js'; @@ -21,31 +20,6 @@ import { initApiEnv } from '../../../../worklet-runtime/lib/api/lynxApi.js'; import { initEventListeners } from '../../../../worklet-runtime/lib/listeners.js'; import { initWorklet } from '../../../../worklet-runtime/lib/workletRuntime.js'; -expect.addSnapshotSerializer({ - test(val) { - return Boolean( - val - && typeof val === 'object' - && Array.isArray(val.refAttr) - && Object.prototype.hasOwnProperty.call(val, 'task') - && typeof val.exec === 'function', - ); - }, - print(val, serialize) { - const printed = serialize({ - refAttr: Array.isArray(val.refAttr) ? [...val.refAttr] : val.refAttr, - task: val.task, - }); - if (printed.startsWith('Object')) { - return printed.replace(/^Object/, 'RefProxy'); - } - if (printed.startsWith('{')) { - return `RefProxy ${printed}`; - } - return printed; - }, -}); - const { onInjectMainThreadGlobals, onInjectBackgroundThreadGlobals, From 085aed9ec53334ae000394741ffe7fd63478c4c6 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 2 Apr 2026 17:00:21 +0800 Subject: [PATCH 19/29] refactor(testing-library): deprecate createVitestConfig and adopt plugin API --- packages/lynx/gesture-runtime/vitest.config.ts | 12 ++++++------ packages/motion/vitest.config.ts | 12 ++++++------ packages/react/package.json | 4 ++++ packages/react/testing-library/README.md | 15 ++++++++------- .../testing-library/src/plugins/vitest.ts | 2 +- .../react/testing-library/src/vitest.config.ts | 5 ++++- .../react/testing-library/vitest.3.1.config.ts | 18 +++++++++--------- .../react/testing-library/vitest.config.ts | 16 ++++++++-------- .../vitest.config.js | 12 ++++++------ .../examples/basic/vitest.config.ts | 16 ++++++++-------- .../vitest.config.compiler-disabled.ts | 17 +++++++++-------- .../vitest.config.compiler-enabled.ts | 17 +++++++++-------- .../use-sync-external-store/vitest.config.ts | 13 ++++++------- 13 files changed, 84 insertions(+), 75 deletions(-) diff --git a/packages/lynx/gesture-runtime/vitest.config.ts b/packages/lynx/gesture-runtime/vitest.config.ts index 3e8c26be58..d7d6117429 100644 --- a/packages/lynx/gesture-runtime/vitest.config.ts +++ b/packages/lynx/gesture-runtime/vitest.config.ts @@ -1,8 +1,10 @@ -import { defineConfig, mergeConfig } from 'vitest/config'; -import { createVitestConfig } from '@lynx-js/react/testing-library/vitest-config'; +import { defineConfig } from 'vitest/config'; +import { vitestTestingLibraryPlugin } from '@lynx-js/react/testing-library/plugins'; -const defaultConfig = await createVitestConfig(); -const config = defineConfig({ +export default defineConfig({ + plugins: [ + vitestTestingLibraryPlugin(), + ], test: { name: 'lynx/gesture-runtime', setupFiles: ['__test__/utils/setup.ts'], @@ -13,5 +15,3 @@ const config = defineConfig({ exclude: ['__test__/utils/**'], }, }); - -export default mergeConfig(defaultConfig, config); diff --git a/packages/motion/vitest.config.ts b/packages/motion/vitest.config.ts index d403130d3b..4f96392ee9 100644 --- a/packages/motion/vitest.config.ts +++ b/packages/motion/vitest.config.ts @@ -1,8 +1,10 @@ -import { defineConfig, mergeConfig } from 'vitest/config'; -import { createVitestConfig } from '@lynx-js/react/testing-library/vitest-config'; +import { defineConfig } from 'vitest/config'; +import { vitestTestingLibraryPlugin } from '@lynx-js/react/testing-library/plugins'; -const defaultConfig = await createVitestConfig(); -const config = defineConfig({ +export default defineConfig({ + plugins: [ + vitestTestingLibraryPlugin(), + ], test: { include: ['__tests__/**/*.test.{js,ts,jsx,tsx}'], exclude: ['__tests__/utils/**'], @@ -11,5 +13,3 @@ const config = defineConfig({ }, }, }); - -export default mergeConfig(defaultConfig, config); diff --git a/packages/react/package.json b/packages/react/package.json index ef8d1eecb3..2c240e0a38 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -104,6 +104,10 @@ "types": "./testing-library/dist/vitest.config.d.ts", "default": "./testing-library/dist/vitest.config.js" }, + "./testing-library/plugins": { + "types": "./testing-library/dist/plugins/index.d.ts", + "default": "./testing-library/dist/plugins/index.js" + }, "./testing-library/rstest-adapter": { "types": "./testing-library/dist/rstest-adapter.d.ts", "default": "./testing-library/dist/rstest-adapter.js" diff --git a/packages/react/testing-library/README.md b/packages/react/testing-library/README.md index 5be535422b..6073b79a85 100644 --- a/packages/react/testing-library/README.md +++ b/packages/react/testing-library/README.md @@ -33,22 +33,23 @@ For more usage detail, see https://rstest.rs/ Setup vitest: ```js -// vitest.config.js -import { defineConfig, mergeConfig } from 'vitest/config'; -import { createVitestConfig } from '@lynx-js/react/testing-library/vitest-config'; +import { defineConfig } from 'vitest/config'; +import { vitestTestingLibraryPlugin } from '@lynx-js/react/testing-library/plugins'; -const defaultConfig = createVitestConfig(); -const config = defineConfig({ +export default defineConfig({ + plugins: [ + vitestTestingLibraryPlugin(), + ], test: { // ... }, }); - -export default mergeConfig(defaultConfig, config); ``` Then you can start writing tests and run them with vitest! +`createVitestConfig` is still supported for backward compatibility, but is deprecated. + ## Usage ```jsx diff --git a/packages/react/testing-library/src/plugins/vitest.ts b/packages/react/testing-library/src/plugins/vitest.ts index 8b378d8476..bf4ae89204 100644 --- a/packages/react/testing-library/src/plugins/vitest.ts +++ b/packages/react/testing-library/src/plugins/vitest.ts @@ -40,7 +40,7 @@ export interface TestingLibraryOptions { export function testingLibraryPlugin( options?: TestingLibraryOptions, -): Vite.Plugin[] { +): Vite.PluginOption { const runtimeOSSPkgName = '@lynx-js/react'; const runtimePkgName = options?.runtimePkgName ?? runtimeOSSPkgName; const runtimeDir = path.dirname( diff --git a/packages/react/testing-library/src/vitest.config.ts b/packages/react/testing-library/src/vitest.config.ts index e3b3094711..56c9e84ba3 100644 --- a/packages/react/testing-library/src/vitest.config.ts +++ b/packages/react/testing-library/src/vitest.config.ts @@ -2,10 +2,13 @@ import { defineConfig, type ViteUserConfig } from 'vitest/config'; import { vitestTestingLibraryPlugin } from './plugins/index.js'; import type { TestingLibraryOptions } from './plugins/index.js'; +/** + * @deprecated Use `vitestTestingLibraryPlugin` from `@lynx-js/react/testing-library/plugins` instead. + */ export function createVitestConfig(options?: TestingLibraryOptions): ViteUserConfig { return defineConfig({ plugins: [ - ...vitestTestingLibraryPlugin(options), + vitestTestingLibraryPlugin(options), ], }); } diff --git a/packages/react/testing-library/vitest.3.1.config.ts b/packages/react/testing-library/vitest.3.1.config.ts index dbe90ddfa9..43706ec69d 100644 --- a/packages/react/testing-library/vitest.3.1.config.ts +++ b/packages/react/testing-library/vitest.3.1.config.ts @@ -1,11 +1,13 @@ -import { defineConfig, mergeConfig } from 'vitest/config'; -import { createVitestConfig } from './dist/vitest.config'; +import { defineConfig } from 'vitest/config'; +import { vitestTestingLibraryPlugin } from './dist/plugins/index.js'; -const defaultConfig = await createVitestConfig({ - runtimePkgName: '@lynx-js/react', - engineVersion: '3.1', -}); -const config = defineConfig({ +export default defineConfig({ + plugins: [ + vitestTestingLibraryPlugin({ + runtimePkgName: '@lynx-js/react', + engineVersion: '3.1', + }), + ], test: { name: 'react/testing-library/engine-3.1', include: [ @@ -13,5 +15,3 @@ const config = defineConfig({ ], }, }); - -export default mergeConfig(defaultConfig, config); diff --git a/packages/react/testing-library/vitest.config.ts b/packages/react/testing-library/vitest.config.ts index cb9cf11351..e2c0b2de2f 100644 --- a/packages/react/testing-library/vitest.config.ts +++ b/packages/react/testing-library/vitest.config.ts @@ -1,14 +1,14 @@ -import { defineConfig, mergeConfig } from 'vitest/config'; -import { createVitestConfig } from './dist/vitest.config'; +import { defineConfig } from 'vitest/config'; +import { vitestTestingLibraryPlugin } from './dist/plugins/index.js'; -const defaultConfig = await createVitestConfig({ - runtimePkgName: '@lynx-js/react', -}); -const config = defineConfig({ +export default defineConfig({ + plugins: [ + vitestTestingLibraryPlugin({ + runtimePkgName: '@lynx-js/react', + }), + ], test: { name: 'react/testing-library', include: ['src/**/*.test.{js,jsx,ts,tsx}', '!src/__tests__/3.1/**/*.{js,jsx,ts,tsx}'], }, }); - -export default mergeConfig(defaultConfig, config); diff --git a/packages/rspeedy/create-rspeedy/template-react-vitest-rltl/vitest.config.js b/packages/rspeedy/create-rspeedy/template-react-vitest-rltl/vitest.config.js index 7af00982cb..a311413043 100644 --- a/packages/rspeedy/create-rspeedy/template-react-vitest-rltl/vitest.config.js +++ b/packages/rspeedy/create-rspeedy/template-react-vitest-rltl/vitest.config.js @@ -1,9 +1,9 @@ -import { defineConfig, mergeConfig } from 'vitest/config' -import { createVitestConfig } from '@lynx-js/react/testing-library/vitest-config' +import { defineConfig } from 'vitest/config' +import { vitestTestingLibraryPlugin } from '@lynx-js/react/testing-library/plugins' -const defaultConfig = createVitestConfig() -const config = defineConfig({ +export default defineConfig({ + plugins: [ + vitestTestingLibraryPlugin(), + ], test: {}, }) - -export default mergeConfig(defaultConfig, config) diff --git a/packages/testing-library/examples/basic/vitest.config.ts b/packages/testing-library/examples/basic/vitest.config.ts index 242ad24f04..37adc389db 100644 --- a/packages/testing-library/examples/basic/vitest.config.ts +++ b/packages/testing-library/examples/basic/vitest.config.ts @@ -1,13 +1,13 @@ -import { defineConfig, mergeConfig } from 'vitest/config'; -import { createVitestConfig } from '@lynx-js/react/testing-library/vitest-config'; +import { defineConfig } from 'vitest/config'; +import { vitestTestingLibraryPlugin } from '@lynx-js/react/testing-library/plugins'; -const defaultConfig = createVitestConfig({ - runtimePkgName: '@lynx-js/react', -}); -const config = defineConfig({ +export default defineConfig({ + plugins: [ + vitestTestingLibraryPlugin({ + runtimePkgName: '@lynx-js/react', + }), + ], test: { name: 'testing-library/examples/basic', }, }); - -export default mergeConfig(defaultConfig, config); diff --git a/packages/testing-library/examples/react-compiler/vitest.config.compiler-disabled.ts b/packages/testing-library/examples/react-compiler/vitest.config.compiler-disabled.ts index 1505d1e4b9..6a6b5a2207 100644 --- a/packages/testing-library/examples/react-compiler/vitest.config.compiler-disabled.ts +++ b/packages/testing-library/examples/react-compiler/vitest.config.compiler-disabled.ts @@ -1,11 +1,13 @@ -import { defineConfig, mergeConfig } from 'vitest/config'; -import { createVitestConfig } from '@lynx-js/react/testing-library/vitest-config'; +import { defineConfig } from 'vitest/config'; +import { vitestTestingLibraryPlugin } from '@lynx-js/react/testing-library/plugins'; -const defaultConfig = await createVitestConfig({ - runtimePkgName: '@lynx-js/react', - experimental_enableReactCompiler: false, -}); -const config = defineConfig({ +export default defineConfig({ + plugins: [ + vitestTestingLibraryPlugin({ + runtimePkgName: '@lynx-js/react', + experimental_enableReactCompiler: false, + }), + ], define: { __FORGET__: 'false', }, @@ -13,4 +15,3 @@ const config = defineConfig({ name: 'testing-library/examples/react-compiler-disabled', }, }); -export default mergeConfig(defaultConfig, config); diff --git a/packages/testing-library/examples/react-compiler/vitest.config.compiler-enabled.ts b/packages/testing-library/examples/react-compiler/vitest.config.compiler-enabled.ts index 7b66bfae5f..ca058654c0 100644 --- a/packages/testing-library/examples/react-compiler/vitest.config.compiler-enabled.ts +++ b/packages/testing-library/examples/react-compiler/vitest.config.compiler-enabled.ts @@ -1,11 +1,13 @@ -import { defineConfig, mergeConfig } from 'vitest/config'; -import { createVitestConfig } from '@lynx-js/react/testing-library/vitest-config'; +import { defineConfig } from 'vitest/config'; +import { vitestTestingLibraryPlugin } from '@lynx-js/react/testing-library/plugins'; -const defaultConfig = await createVitestConfig({ - runtimePkgName: '@lynx-js/react', - experimental_enableReactCompiler: true, -}); -const config = defineConfig({ +export default defineConfig({ + plugins: [ + vitestTestingLibraryPlugin({ + runtimePkgName: '@lynx-js/react', + experimental_enableReactCompiler: true, + }), + ], define: { __FORGET__: 'true', }, @@ -13,4 +15,3 @@ const config = defineConfig({ name: 'testing-library/examples/react-compiler-enabled', }, }); -export default mergeConfig(defaultConfig, config); diff --git a/packages/use-sync-external-store/vitest.config.ts b/packages/use-sync-external-store/vitest.config.ts index 352dc3f58d..9f657d6936 100644 --- a/packages/use-sync-external-store/vitest.config.ts +++ b/packages/use-sync-external-store/vitest.config.ts @@ -1,16 +1,15 @@ -import { defineProject, mergeConfig } from 'vitest/config'; +import { defineProject } from 'vitest/config'; import type { UserWorkspaceConfig } from 'vitest/config'; -import { createVitestConfig } from '@lynx-js/react/testing-library/vitest-config'; - -const defaultConfig = createVitestConfig(); +import { vitestTestingLibraryPlugin } from '@lynx-js/react/testing-library/plugins'; const config: UserWorkspaceConfig = defineProject({ + plugins: [ + vitestTestingLibraryPlugin(), + ], test: { name: 'use-sync-external-store', }, }); -const mergedConfig: UserWorkspaceConfig = mergeConfig(defaultConfig, config); - -export default mergedConfig; +export default config; From c2f397d6be468b5004416d062acfd09811e01118 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 2 Apr 2026 17:47:00 +0800 Subject: [PATCH 20/29] refactor(testing-library): replace rstest-adapter with rstest-config --- packages/react/package.json | 6 +- packages/react/testing-library/README.md | 22 ++++- .../react/testing-library/rslib.config.ts | 2 +- .../react/testing-library/rstest.config.ts | 66 +++++++------ .../testing-library/src/rstest-adapter.ts | 54 ---------- .../testing-library/src/rstest-config.ts | 98 +++++++++++++++++++ .../rstest.config.js | 2 +- .../examples/basic/rstest.config.ts | 2 +- .../examples/library/package.json | 18 ++++ .../examples/library/rstest.config.ts | 7 ++ .../examples/library/src/useCounter.ts | 23 +++++ .../examples/library/tests/useCounter.test.ts | 18 ++++ .../examples/library/tsconfig.json | 10 ++ .../examples/library/vitest.config.ts | 13 +++ .../examples/react-compiler/rstest.config.ts | 2 +- pnpm-lock.yaml | 90 +++++++++++------ 16 files changed, 310 insertions(+), 123 deletions(-) delete mode 100644 packages/react/testing-library/src/rstest-adapter.ts create mode 100644 packages/react/testing-library/src/rstest-config.ts create mode 100644 packages/testing-library/examples/library/package.json create mode 100644 packages/testing-library/examples/library/rstest.config.ts create mode 100644 packages/testing-library/examples/library/src/useCounter.ts create mode 100644 packages/testing-library/examples/library/tests/useCounter.test.ts create mode 100644 packages/testing-library/examples/library/tsconfig.json create mode 100644 packages/testing-library/examples/library/vitest.config.ts diff --git a/packages/react/package.json b/packages/react/package.json index 2c240e0a38..d764c89909 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -108,9 +108,9 @@ "types": "./testing-library/dist/plugins/index.d.ts", "default": "./testing-library/dist/plugins/index.js" }, - "./testing-library/rstest-adapter": { - "types": "./testing-library/dist/rstest-adapter.d.ts", - "default": "./testing-library/dist/rstest-adapter.js" + "./testing-library/rstest-config": { + "types": "./testing-library/dist/rstest-config.d.ts", + "default": "./testing-library/dist/rstest-config.js" }, "./package.json": "./package.json" }, diff --git a/packages/react/testing-library/README.md b/packages/react/testing-library/README.md index 6073b79a85..50e173cdeb 100644 --- a/packages/react/testing-library/README.md +++ b/packages/react/testing-library/README.md @@ -10,19 +10,35 @@ Similar to [react-testing-library](https://github.com/testing-library/react-test ### Rstest -Setup rstest with `@lynx-js/react/testing-library/rstest-adapter`: +Setup rstest with `@lynx-js/react/testing-library/rstest-config`. + +Recommended for library projects: + +```ts +import { defineConfig } from '@rstest/core'; +import { withDefaultConfig } from '@lynx-js/react/testing-library/rstest-config'; + +export default defineConfig({ + extends: withDefaultConfig(), +}); +``` + +Use `withLynxConfig` when you want to reuse your app's `lynx.config.ts`: ```ts // rstest.config.ts import { defineConfig } from '@rstest/core'; -import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-adapter'; +import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-config'; export default defineConfig({ extends: withLynxConfig(), }); ``` -`@lynx-js/react/testing-library/rstest-adapter` will automatically load your `lynx.config.ts` and apply the same configuration to rstest, so you can keep your test environment consistent with your development environment. +Difference between `withLynxConfig` and `withDefaultConfig`: + +- `withLynxConfig`: app-oriented. Loads your `lynx.config.ts` and converts it to rstest config, so rspeedy/lynx settings are reused in tests. +- `withDefaultConfig`: library-oriented. Only applies testing-library defaults (`jsdom`, setup files, globals) and lets you provide the rest via `modifyRstestConfig`. Then you can start writing tests and run them with rstest! diff --git a/packages/react/testing-library/rslib.config.ts b/packages/react/testing-library/rslib.config.ts index 0446793d03..117f815cda 100644 --- a/packages/react/testing-library/rslib.config.ts +++ b/packages/react/testing-library/rslib.config.ts @@ -16,7 +16,7 @@ export default defineConfig({ 'pure': './src/pure.jsx', 'env/index': './src/env/index.ts', 'plugins/index': './src/plugins/index.ts', - 'rstest-adapter': './src/rstest-adapter.ts', + 'rstest-config': './src/rstest-config.ts', }, }, output: { diff --git a/packages/react/testing-library/rstest.config.ts b/packages/react/testing-library/rstest.config.ts index 804a178e9b..803991dfd7 100644 --- a/packages/react/testing-library/rstest.config.ts +++ b/packages/react/testing-library/rstest.config.ts @@ -1,36 +1,44 @@ import { defineConfig } from '@rstest/core'; import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin'; +import { withDefaultConfig } from './src/rstest-config.ts'; export default defineConfig({ - tools: { - swc: { - jsc: { - transform: { - useDefineForClassFields: true, + extends: withDefaultConfig({ + modifyRstestConfig(config) { + return { + ...config, + tools: { + swc: { + jsc: { + transform: { + useDefineForClassFields: true, + }, + }, + }, }, - }, - }, - }, - plugins: [ - pluginReactLynx(), - ], - source: { - define: { - __ALOG__: 'true', - }, - }, - testEnvironment: 'jsdom', - setupFiles: [ - require.resolve('./src/setupFiles/rstest.js'), - ], - globals: true, - resolve: { - // in order to make our test case work for - // both vitest and rstest, we need to alias - // `vitest` to `@rstest/core` - alias: { - vitest: require.resolve('./vitest-polyfill.cjs'), + plugins: [ + ...(config.plugins || []), + pluginReactLynx(), + ], + source: { + ...config.source, + define: { + ...config.source?.define, + __ALOG__: 'true', + }, + }, + resolve: { + ...config.resolve, + // in order to make our test case work for + // both vitest and rstest, we need to alias + // `vitest` to `@rstest/core` + alias: { + ...config.resolve?.alias, + vitest: require.resolve('./vitest-polyfill.cjs'), + }, + }, + include: ['src/**/*.test.{js,jsx,ts,tsx}', '!src/__tests__/3.1/**/*.{js,jsx,ts,tsx}'], + }; }, - }, - include: ['src/**/*.test.{js,jsx,ts,tsx}', '!src/__tests__/3.1/**/*.{js,jsx,ts,tsx}'], + }), }); diff --git a/packages/react/testing-library/src/rstest-adapter.ts b/packages/react/testing-library/src/rstest-adapter.ts deleted file mode 100644 index 4c422ec2f6..0000000000 --- a/packages/react/testing-library/src/rstest-adapter.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { ExtendConfigFn } from '@rstest/core'; -import { createRequire } from 'node:module'; -import type { RsbuildConfig } from '@rsbuild/core'; - -export interface TestingLibraryOptions { - /** - * The root path of the project. - * - * @default `process.cwd()` - */ - rootPath?: string; - - /** - * The path to the Lynx config file. - * - * @default `lynx.config.ts` - */ - configPath?: string; -} - -const require = createRequire(import.meta.url); - -export function withLynxConfig( - options?: TestingLibraryOptions, -): ExtendConfigFn { - return async () => { - const { loadConfig } = await import('@lynx-js/rspeedy'); - const lynxConfig = await loadConfig({ - cwd: options?.rootPath, - configPath: options?.configPath, - }); - - const { toRstestConfig } = await import('@rstest/adapter-rsbuild'); - - const rstestConfig = toRstestConfig({ - rsbuildConfig: lynxConfig.content as RsbuildConfig, - }); - - return { - ...rstestConfig, - plugins: [ - ...(rstestConfig.plugins || []), - { - name: 'lynx-adapter:remove-useless-plugins', - remove: ['lynx:rsbuild:qrcode'], - setup: () => {}, - }, - ], - testEnvironment: 'jsdom', - setupFiles: [require.resolve('./setupFiles/rstest')], - globals: true, - }; - }; -} diff --git a/packages/react/testing-library/src/rstest-config.ts b/packages/react/testing-library/src/rstest-config.ts new file mode 100644 index 0000000000..d53b5ad020 --- /dev/null +++ b/packages/react/testing-library/src/rstest-config.ts @@ -0,0 +1,98 @@ +import type { ExtendConfig, ExtendConfigFn } from '@rstest/core'; +import { createRequire } from 'node:module'; +import type { RsbuildConfig } from '@rsbuild/core'; + +export interface LynxConfigOptions { + /** + * The root path of the project. + * + * @default `process.cwd()` + */ + rootPath?: string; + + /** + * The path to the Lynx config file. + * + * @default `lynx.config.ts` + */ + configPath?: string; +} + +export interface RstestConfigOptions { + /** + * Customize the generated rstest config. + */ + modifyRstestConfig?: (config: ExtendConfig) => ExtendConfig | Promise; +} + +export interface LynxRstestConfigOptions extends LynxConfigOptions, RstestConfigOptions {} + +const require = createRequire(import.meta.url); + +function createDefaultRstestConfig(): ExtendConfig { + return { + testEnvironment: 'jsdom', + setupFiles: [require.resolve('./setupFiles/rstest')], + globals: true, + }; +} + +async function applyRstestConfigModifier( + config: ExtendConfig, + modifyRstestConfig?: (config: ExtendConfig) => ExtendConfig | Promise, +): Promise { + if (!modifyRstestConfig) { + return config; + } + + return await modifyRstestConfig(config); +} + +export function withDefaultConfig( + options?: RstestConfigOptions, +): ExtendConfigFn { + return async () => { + return await applyRstestConfigModifier( + createDefaultRstestConfig(), + options?.modifyRstestConfig, + ); + }; +} + +export function withLynxConfig( + options?: LynxRstestConfigOptions, +): ExtendConfigFn { + return async () => { + const { loadConfig } = await import('@lynx-js/rspeedy'); + const lynxConfig = await loadConfig({ + cwd: options?.rootPath, + configPath: options?.configPath, + }); + + const { toRstestConfig } = await import('@rstest/adapter-rsbuild'); + const rstestConfig = toRstestConfig({ + rsbuildConfig: lynxConfig.content as RsbuildConfig, + }); + const defaultConfig = createDefaultRstestConfig(); + const setupFiles = defaultConfig.setupFiles ?? require.resolve('./setupFiles/rstest'); + + const mergedConfig: ExtendConfig = { + ...rstestConfig, + ...defaultConfig, + plugins: [ + ...(rstestConfig.plugins || []), + { + name: 'lynx-adapter:remove-useless-plugins', + remove: ['lynx:rsbuild:qrcode'], + setup: () => {}, + }, + ], + setupFiles, + }; + + return await applyRstestConfigModifier( + mergedConfig, + options?.modifyRstestConfig, + ); + }; +} diff --git a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl/rstest.config.js b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl/rstest.config.js index a26a7142bd..829e3b650f 100644 --- a/packages/rspeedy/create-rspeedy/template-react-rstest-rltl/rstest.config.js +++ b/packages/rspeedy/create-rspeedy/template-react-rstest-rltl/rstest.config.js @@ -1,5 +1,5 @@ import { defineConfig } from '@rstest/core' -import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-adapter' +import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-config' export default defineConfig({ extends: withLynxConfig(), diff --git a/packages/testing-library/examples/basic/rstest.config.ts b/packages/testing-library/examples/basic/rstest.config.ts index beec012e7a..cd0c3bba3a 100644 --- a/packages/testing-library/examples/basic/rstest.config.ts +++ b/packages/testing-library/examples/basic/rstest.config.ts @@ -1,5 +1,5 @@ import { defineConfig } from '@rstest/core'; -import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-adapter'; +import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-config'; export default defineConfig({ extends: withLynxConfig(), diff --git a/packages/testing-library/examples/library/package.json b/packages/testing-library/examples/library/package.json new file mode 100644 index 0000000000..c5887de3e6 --- /dev/null +++ b/packages/testing-library/examples/library/package.json @@ -0,0 +1,18 @@ +{ + "name": "@lynx-js/testing-library-example-library", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "rstest": "rstest run", + "test": "vitest run", + "test:rstest": "rstest run", + "test:vitest": "vitest run" + }, + "dependencies": { + "@lynx-js/react": "workspace:*" + }, + "devDependencies": { + "@testing-library/jest-dom": "^6.9.1" + } +} diff --git a/packages/testing-library/examples/library/rstest.config.ts b/packages/testing-library/examples/library/rstest.config.ts new file mode 100644 index 0000000000..dc8d629688 --- /dev/null +++ b/packages/testing-library/examples/library/rstest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from '@rstest/core'; +import { withDefaultConfig } from '@lynx-js/react/testing-library/rstest-config'; + +export default defineConfig({ + extends: withDefaultConfig(), + name: 'testing-library/examples/library/rstest', +}); diff --git a/packages/testing-library/examples/library/src/useCounter.ts b/packages/testing-library/examples/library/src/useCounter.ts new file mode 100644 index 0000000000..5cffd43900 --- /dev/null +++ b/packages/testing-library/examples/library/src/useCounter.ts @@ -0,0 +1,23 @@ +import { useState } from '@lynx-js/react'; + +export interface UseCounterResult { + count: number; + inc: () => void; + dec: () => void; + reset: () => void; +} + +export function useCounter(initial = 0): UseCounterResult { + const [count, setCount] = useState(initial); + + const inc = (): void => setCount((v) => v + 1); + const dec = (): void => setCount((v) => v - 1); + const reset = (): void => setCount(initial); + + return { + count: count, + inc: inc, + dec: dec, + reset: reset, + }; +} diff --git a/packages/testing-library/examples/library/tests/useCounter.test.ts b/packages/testing-library/examples/library/tests/useCounter.test.ts new file mode 100644 index 0000000000..99e7c8d321 --- /dev/null +++ b/packages/testing-library/examples/library/tests/useCounter.test.ts @@ -0,0 +1,18 @@ +import { act, renderHook } from '@lynx-js/react/testing-library'; +import { useCounter } from '../src/useCounter.js'; + +describe('library example', () => { + it('updates hook state', () => { + const { result } = renderHook(() => useCounter(2)); + + expect(result.current.count).toBe(2); + + act(() => { + result.current.inc(); + result.current.inc(); + result.current.dec(); + }); + + expect(result.current.count).toBe(3); + }); +}); diff --git a/packages/testing-library/examples/library/tsconfig.json b/packages/testing-library/examples/library/tsconfig.json new file mode 100644 index 0000000000..beaf7aee1b --- /dev/null +++ b/packages/testing-library/examples/library/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../../../tsconfig.json", + "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "@lynx-js/react", + "types": ["vitest/globals", "@rstest/core/globals"], + "noEmit": true, + }, + "include": ["src", "tests"], +} diff --git a/packages/testing-library/examples/library/vitest.config.ts b/packages/testing-library/examples/library/vitest.config.ts new file mode 100644 index 0000000000..8100ef308d --- /dev/null +++ b/packages/testing-library/examples/library/vitest.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vitest/config'; +import { vitestTestingLibraryPlugin } from '@lynx-js/react/testing-library/plugins'; + +export default defineConfig({ + plugins: [ + vitestTestingLibraryPlugin({ + runtimePkgName: '@lynx-js/react', + }), + ], + test: { + name: 'testing-library/examples/library/vitest', + }, +}); diff --git a/packages/testing-library/examples/react-compiler/rstest.config.ts b/packages/testing-library/examples/react-compiler/rstest.config.ts index 66912b932f..36bd78acf3 100644 --- a/packages/testing-library/examples/react-compiler/rstest.config.ts +++ b/packages/testing-library/examples/react-compiler/rstest.config.ts @@ -1,5 +1,5 @@ import { defineConfig } from '@rstest/core'; -import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-adapter'; +import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-config'; export default defineConfig({ extends: withLynxConfig({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 36fb8b646e..bd96a9918d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -282,7 +282,7 @@ importers: version: 3.7.0 '@rsbuild/plugin-babel': specifier: 1.1.0 - version: 1.1.0(@rsbuild/core@1.7.4) + version: 1.1.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) '@types/react': specifier: ^18.3.28 version: 18.3.28 @@ -1128,10 +1128,10 @@ importers: version: link:../../web-platform/web-elements '@rsbuild/plugin-less': specifier: 1.6.0 - version: 1.6.0(@rsbuild/core@1.7.4) + version: 1.6.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) '@rsbuild/plugin-sass': specifier: 1.5.0 - version: 1.5.0(@rsbuild/core@1.7.4) + version: 1.5.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) commander: specifier: ^13.1.0 version: 13.1.0 @@ -1146,7 +1146,7 @@ importers: version: 1.1.1 rsbuild-plugin-tailwindcss: specifier: 0.2.4 - version: 0.2.4(@rsbuild/core@1.7.4)(tailwindcss@4.2.1) + version: 0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@4.2.1) rslog: specifier: ^1.3.2 version: 1.3.2 @@ -1192,6 +1192,36 @@ importers: specifier: ^6.9.1 version: 6.9.1 + packages/testing-library/examples/library: + dependencies: + '@lynx-js/react': + specifier: workspace:* + version: link:../../../react + devDependencies: + '@testing-library/jest-dom': + specifier: ^6.9.1 + version: 6.9.1 + + packages/testing-library/examples/library-rstest: + dependencies: + '@lynx-js/react': + specifier: workspace:* + version: link:../../../react + devDependencies: + '@testing-library/jest-dom': + specifier: ^6.9.1 + version: 6.9.1 + + packages/testing-library/examples/library-vitest: + dependencies: + '@lynx-js/react': + specifier: workspace:* + version: link:../../../react + devDependencies: + '@testing-library/jest-dom': + specifier: ^6.9.1 + version: 6.9.1 + packages/testing-library/examples/react-compiler: dependencies: '@lynx-js/react': @@ -1230,7 +1260,7 @@ importers: version: 3.7.0 '@rsbuild/plugin-babel': specifier: 1.1.0 - version: 1.1.0(@rsbuild/core@1.7.4) + version: 1.1.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) '@testing-library/jest-dom': specifier: ^6.9.1 version: 6.9.1 @@ -1922,13 +1952,13 @@ importers: version: 7.33.4(@types/node@24.10.13) '@rsbuild/plugin-sass': specifier: 1.5.0 - version: 1.5.0(@rsbuild/core@1.7.4) + version: 1.5.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) '@rsbuild/plugin-type-check': specifier: 1.3.4 - version: 1.3.4(@rsbuild/core@1.7.4)(@rspack/core@1.7.9(@swc/helpers@0.5.20))(tslib@2.8.1)(typescript@5.9.3) + version: 1.3.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(@rspack/core@1.7.9(@swc/helpers@0.5.20))(tslib@2.8.1)(typescript@5.9.3) '@rsbuild/plugin-typed-css-modules': specifier: 1.2.2 - version: 1.2.2(@rsbuild/core@1.7.4) + version: 1.2.2(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) '@rspress/core': specifier: 2.0.3 version: 2.0.3(@types/react@19.2.14)(core-js@3.48.0) @@ -12141,13 +12171,13 @@ snapshots: optionalDependencies: core-js: 3.48.0 - '@rsbuild/plugin-babel@1.1.0(@rsbuild/core@1.7.4)': + '@rsbuild/plugin-babel@1.1.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0) '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@rsbuild/core': 1.7.4 + '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) '@types/babel__core': 7.20.5 deepmerge: 4.3.1 reduce-configs: 1.1.1 @@ -12187,9 +12217,9 @@ snapshots: optionalDependencies: '@rsbuild/core': 1.7.4 - '@rsbuild/plugin-less@1.6.0(@rsbuild/core@1.7.4)': + '@rsbuild/plugin-less@1.6.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': dependencies: - '@rsbuild/core': 1.7.4 + '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) deepmerge: 4.3.1 reduce-configs: 1.1.1 @@ -12218,6 +12248,15 @@ snapshots: reduce-configs: 1.1.1 sass-embedded: 1.97.3 + '@rsbuild/plugin-sass@1.5.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': + dependencies: + '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) + deepmerge: 4.3.1 + loader-utils: 2.0.4 + postcss: 8.5.6 + reduce-configs: 1.1.1 + sass-embedded: 1.97.3 + '@rsbuild/plugin-source-build@1.0.4(@rsbuild/core@1.7.4)': dependencies: fast-glob: 3.3.3 @@ -12226,19 +12265,6 @@ snapshots: optionalDependencies: '@rsbuild/core': 1.7.4 - '@rsbuild/plugin-type-check@1.3.4(@rsbuild/core@1.7.4)(@rspack/core@1.7.9(@swc/helpers@0.5.20))(tslib@2.8.1)(typescript@5.9.3)': - dependencies: - deepmerge: 4.3.1 - json5: 2.2.3 - reduce-configs: 1.1.1 - ts-checker-rspack-plugin: 1.3.0(@rspack/core@1.7.9(@swc/helpers@0.5.20))(tslib@2.8.1)(typescript@5.9.3) - optionalDependencies: - '@rsbuild/core': 1.7.4 - transitivePeerDependencies: - - '@rspack/core' - - tslib - - typescript - '@rsbuild/plugin-type-check@1.3.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(@rspack/core@1.7.9(@swc/helpers@0.5.20))(tslib@2.8.1)(typescript@5.9.3)': dependencies: deepmerge: 4.3.1 @@ -12256,6 +12282,10 @@ snapshots: optionalDependencies: '@rsbuild/core': 1.7.4 + '@rsbuild/plugin-typed-css-modules@1.2.2(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': + optionalDependencies: + '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) + '@rsdoctor/client@1.2.3': {} '@rsdoctor/core@1.2.3(@rsbuild/core@1.7.4)(@rspack/core@1.7.9(@swc/helpers@0.5.20))(webpack@5.105.2)': @@ -18199,15 +18229,15 @@ snapshots: optionalDependencies: '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) - rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@1.7.4)(tailwindcss@4.2.1): + rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@3.4.19): dependencies: - tailwindcss: 4.2.1 + tailwindcss: 3.4.19 optionalDependencies: - '@rsbuild/core': 1.7.4 + '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) - rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@3.4.19): + rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@4.2.1): dependencies: - tailwindcss: 3.4.19 + tailwindcss: 4.2.1 optionalDependencies: '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) From 41c16b25db83225dd91cf3e9014d348cf29d0740 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 2 Apr 2026 17:50:01 +0800 Subject: [PATCH 21/29] chore: dedupe pnpm lockfile --- pnpm-lock.yaml | 80 +++++++++++++++++++------------------------------- 1 file changed, 30 insertions(+), 50 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bd96a9918d..2abc1f0b8c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -282,7 +282,7 @@ importers: version: 3.7.0 '@rsbuild/plugin-babel': specifier: 1.1.0 - version: 1.1.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) + version: 1.1.0(@rsbuild/core@1.7.4) '@types/react': specifier: ^18.3.28 version: 18.3.28 @@ -1128,10 +1128,10 @@ importers: version: link:../../web-platform/web-elements '@rsbuild/plugin-less': specifier: 1.6.0 - version: 1.6.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) + version: 1.6.0(@rsbuild/core@1.7.4) '@rsbuild/plugin-sass': specifier: 1.5.0 - version: 1.5.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) + version: 1.5.0(@rsbuild/core@1.7.4) commander: specifier: ^13.1.0 version: 13.1.0 @@ -1146,7 +1146,7 @@ importers: version: 1.1.1 rsbuild-plugin-tailwindcss: specifier: 0.2.4 - version: 0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@4.2.1) + version: 0.2.4(@rsbuild/core@1.7.4)(tailwindcss@4.2.1) rslog: specifier: ^1.3.2 version: 1.3.2 @@ -1202,26 +1202,6 @@ importers: specifier: ^6.9.1 version: 6.9.1 - packages/testing-library/examples/library-rstest: - dependencies: - '@lynx-js/react': - specifier: workspace:* - version: link:../../../react - devDependencies: - '@testing-library/jest-dom': - specifier: ^6.9.1 - version: 6.9.1 - - packages/testing-library/examples/library-vitest: - dependencies: - '@lynx-js/react': - specifier: workspace:* - version: link:../../../react - devDependencies: - '@testing-library/jest-dom': - specifier: ^6.9.1 - version: 6.9.1 - packages/testing-library/examples/react-compiler: dependencies: '@lynx-js/react': @@ -1260,7 +1240,7 @@ importers: version: 3.7.0 '@rsbuild/plugin-babel': specifier: 1.1.0 - version: 1.1.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) + version: 1.1.0(@rsbuild/core@1.7.4) '@testing-library/jest-dom': specifier: ^6.9.1 version: 6.9.1 @@ -1952,13 +1932,13 @@ importers: version: 7.33.4(@types/node@24.10.13) '@rsbuild/plugin-sass': specifier: 1.5.0 - version: 1.5.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) + version: 1.5.0(@rsbuild/core@1.7.4) '@rsbuild/plugin-type-check': specifier: 1.3.4 - version: 1.3.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(@rspack/core@1.7.9(@swc/helpers@0.5.20))(tslib@2.8.1)(typescript@5.9.3) + version: 1.3.4(@rsbuild/core@1.7.4)(@rspack/core@1.7.9(@swc/helpers@0.5.20))(tslib@2.8.1)(typescript@5.9.3) '@rsbuild/plugin-typed-css-modules': specifier: 1.2.2 - version: 1.2.2(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0)) + version: 1.2.2(@rsbuild/core@1.7.4) '@rspress/core': specifier: 2.0.3 version: 2.0.3(@types/react@19.2.14)(core-js@3.48.0) @@ -12171,13 +12151,13 @@ snapshots: optionalDependencies: core-js: 3.48.0 - '@rsbuild/plugin-babel@1.1.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': + '@rsbuild/plugin-babel@1.1.0(@rsbuild/core@1.7.4)': dependencies: '@babel/core': 7.29.0 '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0) '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) + '@rsbuild/core': 1.7.4 '@types/babel__core': 7.20.5 deepmerge: 4.3.1 reduce-configs: 1.1.1 @@ -12217,9 +12197,9 @@ snapshots: optionalDependencies: '@rsbuild/core': 1.7.4 - '@rsbuild/plugin-less@1.6.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': + '@rsbuild/plugin-less@1.6.0(@rsbuild/core@1.7.4)': dependencies: - '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) + '@rsbuild/core': 1.7.4 deepmerge: 4.3.1 reduce-configs: 1.1.1 @@ -12248,15 +12228,6 @@ snapshots: reduce-configs: 1.1.1 sass-embedded: 1.97.3 - '@rsbuild/plugin-sass@1.5.0(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': - dependencies: - '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) - deepmerge: 4.3.1 - loader-utils: 2.0.4 - postcss: 8.5.6 - reduce-configs: 1.1.1 - sass-embedded: 1.97.3 - '@rsbuild/plugin-source-build@1.0.4(@rsbuild/core@1.7.4)': dependencies: fast-glob: 3.3.3 @@ -12265,6 +12236,19 @@ snapshots: optionalDependencies: '@rsbuild/core': 1.7.4 + '@rsbuild/plugin-type-check@1.3.4(@rsbuild/core@1.7.4)(@rspack/core@1.7.9(@swc/helpers@0.5.20))(tslib@2.8.1)(typescript@5.9.3)': + dependencies: + deepmerge: 4.3.1 + json5: 2.2.3 + reduce-configs: 1.1.1 + ts-checker-rspack-plugin: 1.3.0(@rspack/core@1.7.9(@swc/helpers@0.5.20))(tslib@2.8.1)(typescript@5.9.3) + optionalDependencies: + '@rsbuild/core': 1.7.4 + transitivePeerDependencies: + - '@rspack/core' + - tslib + - typescript + '@rsbuild/plugin-type-check@1.3.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(@rspack/core@1.7.9(@swc/helpers@0.5.20))(tslib@2.8.1)(typescript@5.9.3)': dependencies: deepmerge: 4.3.1 @@ -12282,10 +12266,6 @@ snapshots: optionalDependencies: '@rsbuild/core': 1.7.4 - '@rsbuild/plugin-typed-css-modules@1.2.2(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))': - optionalDependencies: - '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) - '@rsdoctor/client@1.2.3': {} '@rsdoctor/core@1.2.3(@rsbuild/core@1.7.4)(@rspack/core@1.7.9(@swc/helpers@0.5.20))(webpack@5.105.2)': @@ -18229,15 +18209,15 @@ snapshots: optionalDependencies: '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) - rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@3.4.19): + rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@1.7.4)(tailwindcss@4.2.1): dependencies: - tailwindcss: 3.4.19 + tailwindcss: 4.2.1 optionalDependencies: - '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) + '@rsbuild/core': 1.7.4 - rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@4.2.1): + rsbuild-plugin-tailwindcss@0.2.4(@rsbuild/core@2.0.0-beta.3(core-js@3.48.0))(tailwindcss@3.4.19): dependencies: - tailwindcss: 4.2.1 + tailwindcss: 3.4.19 optionalDependencies: '@rsbuild/core': 2.0.0-beta.3(core-js@3.48.0) From 8fae0350d1ca81c2d2e9364dad59fee8883cb903 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 2 Apr 2026 18:36:03 +0800 Subject: [PATCH 22/29] fix: address rstest review comments --- .changeset/red-lamps-arrive.md | 4 ++-- packages/react/package.json | 4 ++++ .../react/testing-library/src/rstest-config.ts | 17 ++++++++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.changeset/red-lamps-arrive.md b/.changeset/red-lamps-arrive.md index 75d2848d15..4ad4b4d80e 100644 --- a/.changeset/red-lamps-arrive.md +++ b/.changeset/red-lamps-arrive.md @@ -8,14 +8,14 @@ Create a config file `rstest.config.ts` with the following content: ```ts import { defineConfig } from '@rstest/core'; -import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-adapter'; +import { withLynxConfig } from '@lynx-js/react/testing-library/rstest-config'; export default defineConfig({ extends: withLynxConfig(), }); ``` -`@lynx-js/react/testing-library/rstest-adapter` will automatically load your `lynx.config.ts` and apply the same configuration to rstest, so you can keep your test environment consistent with your development environment. +`@lynx-js/react/testing-library/rstest-config` will automatically load your `lynx.config.ts` and apply the same configuration to rstest, so you can keep your test environment consistent with your development environment. And then use rstest as usual: diff --git a/packages/react/package.json b/packages/react/package.json index d764c89909..64f37e1440 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -190,11 +190,15 @@ "@types/react": "^18.3.28" }, "peerDependencies": { + "@lynx-js/rspeedy": "*", "@lynx-js/types": "*", "@rstest/core": "*", "@types/react": "^18" }, "peerDependenciesMeta": { + "@lynx-js/rspeedy": { + "optional": true + }, "@lynx-js/types": { "optional": true }, diff --git a/packages/react/testing-library/src/rstest-config.ts b/packages/react/testing-library/src/rstest-config.ts index d53b5ad020..dc7a053458 100644 --- a/packages/react/testing-library/src/rstest-config.ts +++ b/packages/react/testing-library/src/rstest-config.ts @@ -37,6 +37,16 @@ function createDefaultRstestConfig(): ExtendConfig { }; } +function normalizeSetupFiles( + setupFiles: ExtendConfig['setupFiles'], +): string[] { + if (!setupFiles) { + return []; + } + + return Array.isArray(setupFiles) ? setupFiles : [setupFiles]; +} + async function applyRstestConfigModifier( config: ExtendConfig, modifyRstestConfig?: (config: ExtendConfig) => ExtendConfig | Promise, @@ -74,7 +84,12 @@ export function withLynxConfig( rsbuildConfig: lynxConfig.content as RsbuildConfig, }); const defaultConfig = createDefaultRstestConfig(); - const setupFiles = defaultConfig.setupFiles ?? require.resolve('./setupFiles/rstest'); + const setupFiles = Array.from( + new Set([ + ...normalizeSetupFiles(rstestConfig.setupFiles), + ...normalizeSetupFiles(defaultConfig.setupFiles), + ]), + ); const mergedConfig: ExtendConfig = { ...rstestConfig, From b5e918019a11886a3a7672049c07373cbd6dd818 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 2 Apr 2026 18:38:02 +0800 Subject: [PATCH 23/29] revert: remove rspeedy peer dependency from react package --- packages/react/package.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/react/package.json b/packages/react/package.json index 64f37e1440..d764c89909 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -190,15 +190,11 @@ "@types/react": "^18.3.28" }, "peerDependencies": { - "@lynx-js/rspeedy": "*", "@lynx-js/types": "*", "@rstest/core": "*", "@types/react": "^18" }, "peerDependenciesMeta": { - "@lynx-js/rspeedy": { - "optional": true - }, "@lynx-js/types": { "optional": true }, From 333198d44bff6d46bb4c647106a9af6498dfac61 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 2 Apr 2026 19:27:06 +0800 Subject: [PATCH 24/29] chore: remove rstest peer dependency from react package --- packages/react/package.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/react/package.json b/packages/react/package.json index d764c89909..25d6f341aa 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -191,15 +191,11 @@ }, "peerDependencies": { "@lynx-js/types": "*", - "@rstest/core": "*", "@types/react": "^18" }, "peerDependenciesMeta": { "@lynx-js/types": { "optional": true - }, - "@rstest/core": { - "optional": true } } } From b30dd8817a4df99131547802e86e270d985d68de Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 2 Apr 2026 19:37:06 +0800 Subject: [PATCH 25/29] chore: sync lockfile after removing rstest core --- pnpm-lock.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2abc1f0b8c..2e01c9c5c3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -556,9 +556,6 @@ importers: packages/react: dependencies: - '@rstest/core': - specifier: '*' - version: 0.8.1(jsdom@27.4.0) preact: specifier: npm:@lynx-js/internal-preact@10.28.4-4842985 version: '@lynx-js/internal-preact@10.28.4-4842985' From b77f62262fa6a222c4cc0b97221f8978a0098f93 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 9 Apr 2026 14:26:20 +0800 Subject: [PATCH 26/29] fix: regenerate lockfile after merge --- pnpm-lock.yaml | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fda96c7763..96fff5551c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -634,10 +634,10 @@ importers: version: link:../../testing-library/testing-environment '@rsbuild/core': specifier: catalog:rsbuild - version: 1.7.4 + version: 1.7.5 '@rstest/adapter-rsbuild': specifier: ^0.2.3 - version: 0.2.3(@rsbuild/core@1.7.4)(@rstest/core@0.8.1(jsdom@27.4.0)) + version: 0.2.3(@rsbuild/core@1.7.5)(@rstest/core@0.8.1(jsdom@27.4.0)) '@testing-library/dom': specifier: ^10.4.1 version: 10.4.1 @@ -1237,7 +1237,7 @@ importers: version: 3.7.0 '@rsbuild/plugin-babel': specifier: 1.1.0 - version: 1.1.0(@rsbuild/core@1.7.4) + version: 1.1.0(@rsbuild/core@1.7.5) '@testing-library/jest-dom': specifier: ^6.9.1 version: 6.9.1 @@ -3792,6 +3792,11 @@ packages: cpu: [x64] os: [win32] + '@rsbuild/core@1.7.2': + resolution: {integrity: sha512-VAFO6cM+cyg2ntxNW6g3tB2Jc5J5mpLjLluvm7VtW2uceNzyUlVv41o66Yp1t1ikxd3ljtqegViXem62JqzveA==} + engines: {node: '>=18.12.0'} + hasBin: true + '@rsbuild/core@1.7.5': resolution: {integrity: sha512-i37urpoV4y9NSsGiUOuLdoI42KJ5h4gAZ8EG8Ilmsond3bxoAoOCu7YvC+1pJ7p+r16suVPW8cki891ZKHOoXQ==} engines: {node: '>=18.12.0'} @@ -12120,6 +12125,14 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.34.9': optional: true + '@rsbuild/core@1.7.2': + dependencies: + '@rspack/core': 1.7.9(@swc/helpers@0.5.20) + '@rspack/lite-tapable': 1.1.0 + '@swc/helpers': 0.5.20 + core-js: 3.47.0 + jiti: 2.6.1 + '@rsbuild/core@1.7.5': dependencies: '@rspack/core': 1.7.9(@swc/helpers@0.5.20) @@ -12581,9 +12594,9 @@ snapshots: - react - react-dom - '@rstest/adapter-rsbuild@0.2.3(@rsbuild/core@1.7.4)(@rstest/core@0.8.1(jsdom@27.4.0))': + '@rstest/adapter-rsbuild@0.2.3(@rsbuild/core@1.7.5)(@rstest/core@0.8.1(jsdom@27.4.0))': dependencies: - '@rsbuild/core': 1.7.4 + '@rsbuild/core': 1.7.5 '@rstest/core': 0.8.1(jsdom@27.4.0) '@rstest/core@0.8.1(jsdom@27.4.0)': From 6521e71ad110fb118915b59d243cf59de7aa815c Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 9 Apr 2026 14:32:35 +0800 Subject: [PATCH 27/29] fix: add explicit type for profileFlowId --- packages/react/runtime/src/debug/profile.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react/runtime/src/debug/profile.ts b/packages/react/runtime/src/debug/profile.ts index 6e14f42353..232c78913d 100644 --- a/packages/react/runtime/src/debug/profile.ts +++ b/packages/react/runtime/src/debug/profile.ts @@ -26,10 +26,10 @@ export const profileEnd = /* @__PURE__ */ ((() => { return p.profileEnd.bind(p); })()) as typeof lynx.performance.profileEnd; -export const profileFlowId = /* @__PURE__ */ (() => { +export const profileFlowId = /* @__PURE__ */ ((() => { let p; if (!(p = lynx.performance) || typeof p.profileFlowId !== 'function') { return noopFlowId; } return p.profileFlowId.bind(p); -})(); +})()) as typeof lynx.performance.profileFlowId; From 5722509c55e2b8aa9c54b858fb0578c2b15700ba Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 9 Apr 2026 14:57:39 +0800 Subject: [PATCH 28/29] fix: use runtime worklet-runtime paths in testing setup --- .../testing-library/src/setupFiles/common/runtime-setup.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react/testing-library/src/setupFiles/common/runtime-setup.js b/packages/react/testing-library/src/setupFiles/common/runtime-setup.js index 45d3c12433..778435c1de 100644 --- a/packages/react/testing-library/src/setupFiles/common/runtime-setup.js +++ b/packages/react/testing-library/src/setupFiles/common/runtime-setup.js @@ -16,9 +16,9 @@ import { snapshotInstanceManager, } from '../../../../runtime/lib/snapshot/index.js'; import { destroyWorklet } from '../../../../runtime/lib/worklet/destroy.js'; -import { initApiEnv } from '../../../../worklet-runtime/lib/api/lynxApi.js'; -import { initEventListeners } from '../../../../worklet-runtime/lib/listeners.js'; -import { initWorklet } from '../../../../worklet-runtime/lib/workletRuntime.js'; +import { initApiEnv } from '../../../../runtime/lib/worklet-runtime/api/lynxApi.js'; +import { initEventListeners } from '../../../../runtime/lib/worklet-runtime/listeners.js'; +import { initWorklet } from '../../../../runtime/lib/worklet-runtime/workletRuntime.js'; const { onInjectMainThreadGlobals, From 99592d3eadca83f7c32b8165202f5392f9f0e553 Mon Sep 17 00:00:00 2001 From: Yiming Li Date: Thu, 9 Apr 2026 15:27:44 +0800 Subject: [PATCH 29/29] test: fix vitest snapshot and defineDCE output assertion --- .../src/__tests__/alog.test.jsx | 102 +++++++++--------- .../src/__tests__/list.test.jsx | 2 +- .../rspeedy/plugin-react/test/config.test.ts | 28 +++-- 3 files changed, 74 insertions(+), 58 deletions(-) diff --git a/packages/react/testing-library/src/__tests__/alog.test.jsx b/packages/react/testing-library/src/__tests__/alog.test.jsx index b77342736d..e4d1acce1a 100644 --- a/packages/react/testing-library/src/__tests__/alog.test.jsx +++ b/packages/react/testing-library/src/__tests__/alog.test.jsx @@ -148,7 +148,13 @@ describe('alog', () => { "[ReactLynxDebug] FiberElement API call #31: __AppendElement(WRAPPER#8, VIEW#11)", ], [ - "[ReactLynxDebug] FiberElement API call #32: __OnLifecycleEvent(["rLynxFirstScreen", {"root":"{\\"id\\":-1,\\"type\\":\\"root\\",\\"children\\":[{\\"id\\":-2,\\"type\\":\\"__snapshot_d6fb6_test_1\\",\\"values\\":[\\"-2:0:\\",\\"-2:1:\\"],\\"children\\":[{\\"id\\":-3,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-7,\\"type\\":null,\\"values\\":[0]}]},{\\"id\\":-4,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-5,\\"type\\":\\"__snapshot_d6fb6_test_2\\"},{\\"id\\":-6,\\"type\\":\\"__snapshot_d6fb6_test_3\\"}]}]}]}","jsReadyEventIdSwap":{}}])", + "[MainThread Component Render] name: FunctionComponent", + ], + [ + "[MainThread Component Render] name: App", + ], + [ + "[ReactLynxDebug] FiberElement API call #32: __OnLifecycleEvent(["rLynxFirstScreen", {"root":"{\\"id\\":-1,\\"type\\":\\"root\\",\\"children\\":[{\\"id\\":-2,\\"type\\":\\"__snapshot_d6fb6_test_1\\",\\"values\\":[\\"-2:0:\\",\\"-2:1:\\"],\\"children\\":[{\\"id\\":-3,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-4,\\"type\\":null,\\"values\\":[0]}]},{\\"id\\":-5,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-6,\\"type\\":\\"__snapshot_d6fb6_test_2\\"},{\\"id\\":-7,\\"type\\":\\"__snapshot_d6fb6_test_3\\"}]}]}]}","jsReadyEventIdSwap":{}}])", ], [ "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: 6", @@ -182,7 +188,7 @@ describe('alog', () => { "type": "wrapper", "children": [ { - "id": -7, + "id": -4, "type": null, "values": [ 0 @@ -191,15 +197,15 @@ describe('alog', () => { ] }, { - "id": -4, + "id": -5, "type": "wrapper", "children": [ { - "id": -5, + "id": -6, "type": "__snapshot_d6fb6_test_2" }, { - "id": -6, + "id": -7, "type": "__snapshot_d6fb6_test_3" } ] @@ -216,10 +222,10 @@ describe('alog', () => { | -1(root): undefined | -2(__snapshot_d6fb6_test_1): ["-2:0:","-2:1:"] | -3(wrapper): undefined - | -7(null): [0] - | -4(wrapper): undefined - | -5(__snapshot_d6fb6_test_2): undefined - | -6(__snapshot_d6fb6_test_3): undefined", + | -4(null): [0] + | -5(wrapper): undefined + | -6(__snapshot_d6fb6_test_2): undefined + | -7(__snapshot_d6fb6_test_3): undefined", ], [ "[ReactLynxDebug] BackgroundSnapshotInstance tree before hydration: @@ -236,10 +242,10 @@ describe('alog', () => { | -1(root): undefined | -2(__snapshot_d6fb6_test_1): [null,null] | -3(wrapper): undefined - | -7(null): [0] - | -4(wrapper): undefined - | -5(__snapshot_d6fb6_test_2): undefined - | -6(__snapshot_d6fb6_test_3): undefined", + | -4(null): [0] + | -5(wrapper): undefined + | -6(__snapshot_d6fb6_test_2): undefined + | -7(__snapshot_d6fb6_test_3): undefined", ], [ "[ReactLynxDebug] BTS -> MTS updateMainThread: @@ -287,10 +293,10 @@ describe('alog', () => { }", ], [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -5", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -6", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -6", + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -7", ], [ "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", @@ -344,15 +350,6 @@ describe('alog', () => { [ "[ReactLynxDebug] FiberElement API call #3: __SetCSSId([PAGE#0], 0)", ], - [ - "[MainThread Component Render] name: ClassComponent", - ], - [ - "[MainThread Component Render] name: FunctionComponent", - ], - [ - "[MainThread Component Render] name: App", - ], [ "[ReactLynxDebug] FiberElement API call #4: __CreateView(0) => VIEW#1", ], @@ -425,6 +422,9 @@ describe('alog', () => { [ "[ReactLynxDebug] FiberElement API call #27: __AppendElement(WRAPPER#8, VIEW#9)", ], + [ + "[MainThread Component Render] name: ClassComponent", + ], [ "[ReactLynxDebug] FiberElement API call #28: __CreateView(0) => VIEW#11", ], @@ -438,7 +438,13 @@ describe('alog', () => { "[ReactLynxDebug] FiberElement API call #31: __AppendElement(WRAPPER#8, VIEW#11)", ], [ - "[ReactLynxDebug] FiberElement API call #32: __OnLifecycleEvent(["rLynxFirstScreen", {"root":"{\\"id\\":-1,\\"type\\":\\"root\\",\\"children\\":[{\\"id\\":-2,\\"type\\":\\"__snapshot_d6fb6_test_1\\",\\"values\\":[\\"-2:0:\\",\\"-2:1:\\"],\\"children\\":[{\\"id\\":-3,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-7,\\"type\\":null,\\"values\\":[0]}]},{\\"id\\":-4,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-5,\\"type\\":\\"__snapshot_d6fb6_test_2\\"},{\\"id\\":-6,\\"type\\":\\"__snapshot_d6fb6_test_3\\"}]}]}]}","jsReadyEventIdSwap":{}}])", + "[MainThread Component Render] name: FunctionComponent", + ], + [ + "[MainThread Component Render] name: App", + ], + [ + "[ReactLynxDebug] FiberElement API call #32: __OnLifecycleEvent(["rLynxFirstScreen", {"root":"{\\"id\\":-1,\\"type\\":\\"root\\",\\"children\\":[{\\"id\\":-2,\\"type\\":\\"__snapshot_d6fb6_test_1\\",\\"values\\":[\\"-2:0:\\",\\"-2:1:\\"],\\"children\\":[{\\"id\\":-3,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-4,\\"type\\":null,\\"values\\":[0]}]},{\\"id\\":-5,\\"type\\":\\"wrapper\\",\\"children\\":[{\\"id\\":-6,\\"type\\":\\"__snapshot_d6fb6_test_2\\"},{\\"id\\":-7,\\"type\\":\\"__snapshot_d6fb6_test_3\\"}]}]}]}","jsReadyEventIdSwap":{}}])", ], [ "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: 6", @@ -485,11 +491,11 @@ describe('alog', () => { "type": "wrapper", "children": [ { - "id": -5, + "id": -6, "type": "__snapshot_d6fb6_test_2" }, { - "id": -6, + "id": -7, "type": "__snapshot_d6fb6_test_3" } ] @@ -506,10 +512,10 @@ describe('alog', () => { | -1(root): undefined | -2(__snapshot_d6fb6_test_1): ["-2:0:","-2:1:"] | -3(wrapper): undefined - | -7(null): [0] - | -4(wrapper): undefined - | -5(__snapshot_d6fb6_test_2): undefined - | -6(__snapshot_d6fb6_test_3): undefined", + | -4(null): [0] + | -5(wrapper): undefined + | -6(__snapshot_d6fb6_test_2): undefined + | -7(__snapshot_d6fb6_test_3): undefined", ], [ "[ReactLynxDebug] BackgroundSnapshotInstance tree before hydration: @@ -526,10 +532,10 @@ describe('alog', () => { | -1(root): undefined | -2(__snapshot_d6fb6_test_1): [null,null] | -3(wrapper): undefined - | -7(null): [0] - | -4(wrapper): undefined - | -5(__snapshot_d6fb6_test_2): undefined - | -6(__snapshot_d6fb6_test_3): undefined", + | -4(null): [0] + | -5(wrapper): undefined + | -6(__snapshot_d6fb6_test_2): undefined + | -7(__snapshot_d6fb6_test_3): undefined", ], [ "[ReactLynxDebug] BTS -> MTS updateMainThread: @@ -577,10 +583,10 @@ describe('alog', () => { }", ], [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -5", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -6", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -6", + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -7", ], [ "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", @@ -595,7 +601,7 @@ describe('alog', () => { "snapshotPatch": [ { "op": "SetAttribute", - "id": -7, + "id": -4, "dynamicPartIndex": 0, "value": 1 } @@ -634,10 +640,10 @@ describe('alog', () => { expect(lynxTestingEnv.mainThread.console.alog.mock.calls).toMatchInlineSnapshot(` [ [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -5", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -6", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -6", + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -7", ], [ "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", @@ -683,10 +689,10 @@ describe('alog', () => { expect(lynxTestingEnv.backgroundThread.console.alog.mock.calls).toMatchInlineSnapshot(` [ [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -5", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -6", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -6", + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -7", ], [ "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", @@ -701,7 +707,7 @@ describe('alog', () => { "snapshotPatch": [ { "op": "SetAttribute", - "id": -7, + "id": -4, "dynamicPartIndex": 0, "value": 0 } @@ -740,10 +746,10 @@ describe('alog', () => { expect(lynxTestingEnv.mainThread.console.alog.mock.calls).toMatchInlineSnapshot(` [ [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -5", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -6", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -6", + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -7", ], [ "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", @@ -789,10 +795,10 @@ describe('alog', () => { expect(lynxTestingEnv.backgroundThread.console.alog.mock.calls).toMatchInlineSnapshot(` [ [ - "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -5", + "[BackgroundThread Component Render] name: ClassComponent, uniqID: __snapshot_d6fb6_test_2, __id: -6", ], [ - "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -6", + "[BackgroundThread Component Render] name: FunctionComponent, uniqID: __snapshot_d6fb6_test_3, __id: -7", ], [ "[BackgroundThread Component Render] name: App, uniqID: __snapshot_d6fb6_test_1, __id: -2", @@ -807,7 +813,7 @@ describe('alog', () => { "snapshotPatch": [ { "op": "SetAttribute", - "id": -7, + "id": -4, "dynamicPartIndex": 0, "value": 1 } diff --git a/packages/react/testing-library/src/__tests__/list.test.jsx b/packages/react/testing-library/src/__tests__/list.test.jsx index 09dc312230..f8b3bfe6cf 100644 --- a/packages/react/testing-library/src/__tests__/list.test.jsx +++ b/packages/react/testing-library/src/__tests__/list.test.jsx @@ -975,7 +975,7 @@ describe('list - deferred should render as normal', () => { "rLynxFirstScreen", { "jsReadyEventIdSwap": {}, - "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_d0c07_test_30","children":[{"id":-3,"type":"__snapshot_d0c07_test_31","values":[{"item-key":0}],"children":[{"id":-4,"type":"__snapshot_d0c07_test_29","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-5,"type":"__snapshot_d0c07_test_32","children":[{"id":-12,"type":null,"values":[0]}]}]}]},{"id":-6,"type":"__snapshot_d0c07_test_31","values":[{"item-key":1}],"children":[{"id":-7,"type":"__snapshot_d0c07_test_29","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-8,"type":"__snapshot_d0c07_test_32","children":[{"id":-13,"type":null,"values":[1]}]}]}]},{"id":-9,"type":"__snapshot_d0c07_test_31","values":[{"item-key":2}],"children":[{"id":-10,"type":"__snapshot_d0c07_test_29","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-11,"type":"__snapshot_d0c07_test_32","children":[{"id":-14,"type":null,"values":[2]}]}]}]}]}]}", + "root": "{"id":-1,"type":"root","children":[{"id":-2,"type":"__snapshot_d0c07_test_30","children":[{"id":-3,"type":"__snapshot_d0c07_test_31","values":[{"item-key":0}],"children":[{"id":-4,"type":"__snapshot_d0c07_test_29","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-5,"type":"__snapshot_d0c07_test_32","children":[{"id":-6,"type":null,"values":[0]}]}]}]},{"id":-7,"type":"__snapshot_d0c07_test_31","values":[{"item-key":1}],"children":[{"id":-8,"type":"__snapshot_d0c07_test_29","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-9,"type":"__snapshot_d0c07_test_32","children":[{"id":-10,"type":null,"values":[1]}]}]}]},{"id":-11,"type":"__snapshot_d0c07_test_31","values":[{"item-key":2}],"children":[{"id":-12,"type":"__snapshot_d0c07_test_29","values":[{"style":{"backgroundColor":"red","margin":"12px"}}],"children":[{"id":-13,"type":"__snapshot_d0c07_test_32","children":[{"id":-14,"type":null,"values":[2]}]}]}]}]}]}", }, ], ], diff --git a/packages/rspeedy/plugin-react/test/config.test.ts b/packages/rspeedy/plugin-react/test/config.test.ts index e398db5645..2e28a62396 100644 --- a/packages/rspeedy/plugin-react/test/config.test.ts +++ b/packages/rspeedy/plugin-react/test/config.test.ts @@ -2063,7 +2063,7 @@ describe('Config', () => { vi.stubEnv('NODE_ENV', 'production') const entryName = 'defineDCE' - const rsbuild = await createRspeedy({ + const rsbuild = await createRspeedyWithTempDistRoot({ rspeedyConfig: { source: { entry: { @@ -2096,18 +2096,28 @@ describe('Config', () => { expect.fail('build should succeed') } - const distPath = path.join( - rsbuild.context.distPath, - '.rspeedy', - entryName, - 'main-thread.js', + const candidateOutputPaths = [ + path.join( + rsbuild.context.distPath, + '.rspeedy', + entryName, + 'main-thread.js', + ), + path.join(rsbuild.context.distPath, `${entryName}.lynx.bundle`), + ] + const builtOutputPath = candidateOutputPaths.find( + outputPath => existsSync(outputPath), ) - if (!existsSync(distPath)) { - expect.fail(`Build output should exist at ${distPath}`) + if (!builtOutputPath) { + expect.fail( + `Build output should exist in one of: ${ + candidateOutputPaths.join(', ') + }`, + ) } - const builtCode = readFileSync(distPath, 'utf8') + const builtCode = readFileSync(builtOutputPath, 'utf8') expect(builtCode).not.toContain('profileStart(\'test\')') expect(builtCode).toContain('Config is: profile-off-mode') })