From 9ea564c81167be9feaf28fc74ee4e77201b24c67 Mon Sep 17 00:00:00 2001 From: yousefed Date: Thu, 28 May 2026 19:52:22 +0200 Subject: [PATCH 1/4] basic tests --- .pnpm-store/v11/index.db | Bin 0 -> 8192 bytes pnpm-lock.yaml | 395 +++++++++++++----- pnpm-workspace.yaml | 4 +- ...62b079035cf9613a94c981f8de2d1e2e409165.png | Bin 0 -> 2689 bytes ...9f1c2c11fad3416f5b9251115f2ae2cb760757.png | Bin 0 -> 4313 bytes tests/package.json | 10 +- ...e---hello-world------hello-universe--1.png | Bin 0 -> 4313 bytes ...ggestion-mode-add-bold-chromium-darwin.png | Bin 0 -> 1639 bytes ...ode-add-italic-to-bold-chromium-darwin.png | Bin 0 -> 1806 bytes ...stion-mode-remove-bold-chromium-darwin.png | Bin 0 -> 1592 bytes ...ggestion-mode-universe-chromium-darwin.png | Bin 0 -> 2689 bytes .../browser/y-prosemirror/basicText.test.tsx | 312 ++++++++++++++ .../fixtures/suggestionFixture.tsx | 145 +++++++ tests/vite.config.browser.ts | 46 ++ 14 files changed, 795 insertions(+), 117 deletions(-) create mode 100644 .pnpm-store/v11/index.db create mode 100644 tests/.vitest-attachments/4062b079035cf9613a94c981f8de2d1e2e409165.png create mode 100644 tests/.vitest-attachments/a09f1c2c11fad3416f5b9251115f2ae2cb760757.png create mode 100644 tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode---hello-world------hello-universe--1.png create mode 100644 tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-add-bold-chromium-darwin.png create mode 100644 tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-add-italic-to-bold-chromium-darwin.png create mode 100644 tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-remove-bold-chromium-darwin.png create mode 100644 tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-universe-chromium-darwin.png create mode 100644 tests/src/browser/y-prosemirror/basicText.test.tsx create mode 100644 tests/src/browser/y-prosemirror/fixtures/suggestionFixture.tsx create mode 100644 tests/vite.config.browser.ts diff --git a/.pnpm-store/v11/index.db b/.pnpm-store/v11/index.db new file mode 100644 index 0000000000000000000000000000000000000000..3dd0ca39ec0a4c0c393f0fd0f983253cdc41a552 GIT binary patch literal 8192 zcmeIuKMw&h7zXfOBauji&FI~bh{YFhM-vz2I2A0UC=q`nH!v71KDRr$T$t?gJZby3 zeY^BG9LQ~8XnndIbU<~MXPmPJ5o3(UGc4~|;hKu~QHjpK!ddNE$%e12+>C_-fdB*` z009U<00Izz00bZa0SNq|z@uoT3#F3vxCeT%H)p-v>5Z9hk1KQiI`F11Y$Zt9juiCO zqH>0k?97dnm?-M|ZrApw)DhF_M}MUQO^);&>UoNKBPo++g~qOGdm}}@H+Ea`eI`=R e{~eBJcu9EHftn literal 0 HcmV?d00001 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c1ca0e9ff1..dd0f9993cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,8 +8,8 @@ overrides: '@headlessui/react': ^2.2.4 '@tiptap/core': ^3.0.0 '@tiptap/pm': ^3.0.0 - vitest: 4.1.2 - '@vitest/runner': 4.1.2 + vitest: 4.1.7 + '@vitest/runner': 4.1.7 '@y/prosemirror>lib0': 1.0.0-rc.13 patchedDependencies: @@ -59,8 +59,8 @@ importers: specifier: ^5.9.3 version: 5.9.3 vitest: - specifier: 4.1.2 - version: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: 4.1.7 + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) wait-on: specifier: 9.0.5 version: 9.0.5 @@ -159,7 +159,7 @@ importers: version: 3.1.18 '@polar-sh/better-auth': specifier: ^1.6.4 - version: 1.8.3(@polar-sh/sdk@0.42.5)(@stripe/react-stripe-js@4.0.2(@stripe/stripe-js@7.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@stripe/stripe-js@7.9.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(better-auth@1.4.22(better-sqlite3@12.8.0)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.51.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(pg@8.20.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(vitest@4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.5.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))))(react-dom@19.2.5(react@19.2.5))(react-is@19.2.4)(react@19.2.5)(redux@5.0.1)(zod@4.3.6) + version: 1.8.3(@polar-sh/sdk@0.42.5)(@stripe/react-stripe-js@4.0.2(@stripe/stripe-js@7.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@stripe/stripe-js@7.9.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(better-auth@1.4.22(better-sqlite3@12.8.0)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.51.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(pg@8.20.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(vitest@4.1.7))(react-dom@19.2.5(react@19.2.5))(react-is@19.2.4)(react@19.2.5)(redux@5.0.1)(zod@4.3.6) '@polar-sh/sdk': specifier: ^0.42.2 version: 0.42.5 @@ -249,7 +249,7 @@ importers: version: 6.0.5(zod@4.3.6) better-auth: specifier: ~1.4.15 - version: 1.4.22(better-sqlite3@12.8.0)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.51.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(pg@8.20.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(vitest@4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.5.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 1.4.22(better-sqlite3@12.8.0)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.51.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(pg@8.20.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(vitest@4.1.7) better-sqlite3: specifier: ^12.6.2 version: 12.8.0 @@ -4904,8 +4904,8 @@ importers: specifier: ^1.8.1 version: 1.8.1(eslint@8.57.1)(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) vitest: - specifier: 4.1.2 - version: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: 4.1.7 + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) packages/core: dependencies: @@ -5013,8 +5013,8 @@ importers: specifier: ^1.8.1 version: 1.8.1(eslint@8.57.1)(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) vitest: - specifier: 4.1.2 - version: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: 4.1.7 + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) y-prosemirror: specifier: ^1.3.7 version: 1.3.7(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30) @@ -5205,8 +5205,8 @@ importers: specifier: ^0.10.0 version: 0.10.0(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) vitest: - specifier: 4.1.2 - version: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: 4.1.7 + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) packages/server-util: dependencies: @@ -5266,8 +5266,8 @@ importers: specifier: ^1.8.1 version: 1.8.1(eslint@8.57.1)(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) vitest: - specifier: 4.1.2 - version: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(jsdom@25.0.1(canvas@2.11.2))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: 4.1.7 + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@25.0.1(canvas@2.11.2))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) packages/shadcn: dependencies: @@ -5481,8 +5481,8 @@ importers: specifier: ^6.0.1 version: 6.0.1(babel-plugin-react-compiler@1.0.0)(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/runner': - specifier: 4.1.2 - version: 4.1.2 + specifier: 4.1.7 + version: 4.1.7 eslint: specifier: ^8.57.1 version: 8.57.1 @@ -5520,8 +5520,8 @@ importers: specifier: ^0.10.0 version: 0.10.0(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) vitest: - specifier: 4.1.2 - version: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: 4.1.7 + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) packages/xl-ai-server: dependencies: @@ -5584,8 +5584,8 @@ importers: specifier: ^0.10.0 version: 0.10.0(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) vitest: - specifier: 4.1.2 - version: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: 4.1.7 + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) packages/xl-docx-exporter: dependencies: @@ -5636,8 +5636,8 @@ importers: specifier: ^1.8.1 version: 1.8.1(eslint@8.57.1)(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) vitest: - specifier: 4.1.2 - version: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: 4.1.7 + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) xml-formatter: specifier: ^3.6.7 version: 3.7.0 @@ -5697,8 +5697,8 @@ importers: specifier: ^1.8.1 version: 1.8.1(eslint@8.57.1)(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) vitest: - specifier: 4.1.2 - version: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: 4.1.7 + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) packages/xl-multi-column: dependencies: @@ -5764,8 +5764,8 @@ importers: specifier: ^1.8.1 version: 1.8.1(eslint@8.57.1)(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) vitest: - specifier: 4.1.2 - version: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(jsdom@25.0.1(canvas@2.11.2))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: 4.1.7 + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@25.0.1(canvas@2.11.2))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) packages/xl-odt-exporter: dependencies: @@ -5816,8 +5816,8 @@ importers: specifier: ^1.8.1 version: 1.8.1(eslint@8.57.1)(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) vitest: - specifier: 4.1.2 - version: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: 4.1.7 + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) xml-formatter: specifier: ^3.6.7 version: 3.7.0 @@ -5889,8 +5889,8 @@ importers: specifier: ^1.8.1 version: 1.8.1(eslint@8.57.1)(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) vitest: - specifier: 4.1.2 - version: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: 4.1.7 + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) playground: dependencies: @@ -6128,6 +6128,21 @@ importers: '@types/react-dom': specifier: ^19.2.3 version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: ^6.0.1 + version: 6.0.1(babel-plugin-react-compiler@1.0.0)(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/browser': + specifier: 4.1.7 + version: 4.1.7(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.7) + '@vitest/browser-playwright': + specifier: 4.1.7 + version: 4.1.7(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(playwright@1.51.1)(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.7) + '@y/protocols': + specifier: ^1.0.6-rc.1 + version: 1.0.6-rc.1(@y/y@14.0.0-rc.16) + '@y/y': + specifier: ^14.0.0-rc.16 + version: 14.0.0-rc.16 eslint: specifier: ^8.57.1 version: 8.57.1 @@ -6153,8 +6168,11 @@ importers: specifier: ^1.8.1 version: 1.8.1(eslint@8.57.1)(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) vitest: - specifier: 4.1.2 - version: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@20.19.37)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: 4.1.7 + version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.19.37)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + vitest-browser-react: + specifier: ^2.2.0 + version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(vitest@4.1.7) packages: @@ -7138,6 +7156,9 @@ packages: '@better-fetch/fetch@1.1.21': resolution: {integrity: sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==} + '@blazediff/core@1.9.1': + resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==} + '@bramus/specificity@2.4.2': resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true @@ -11391,11 +11412,22 @@ packages: babel-plugin-react-compiler: optional: true - '@vitest/expect@4.1.2': - resolution: {integrity: sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==} + '@vitest/browser-playwright@4.1.7': + resolution: {integrity: sha512-OlTlJej7YN6VwV7zJJoNeaCsctF+JXpzpZ4oBHUbrQFfIq+0KW2f07rprCLh9N/zRIZ0v4Mchn1QDDmWMUhPKw==} + peerDependencies: + playwright: '*' + vitest: 4.1.7 + + '@vitest/browser@4.1.7': + resolution: {integrity: sha512-N2JFGfXoEGVAut+kHeru9dD4BUMq/q5xDvBARNl0tUsly3m5KglLOu8VO/6MkDfOlgxXTycojkt6gBKsuyR+IQ==} + peerDependencies: + vitest: 4.1.7 + + '@vitest/expect@4.1.7': + resolution: {integrity: sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==} - '@vitest/mocker@4.1.2': - resolution: {integrity: sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==} + '@vitest/mocker@4.1.7': + resolution: {integrity: sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -11405,20 +11437,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.2': - resolution: {integrity: sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==} + '@vitest/pretty-format@4.1.7': + resolution: {integrity: sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==} - '@vitest/runner@4.1.2': - resolution: {integrity: sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==} + '@vitest/runner@4.1.7': + resolution: {integrity: sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==} - '@vitest/snapshot@4.1.2': - resolution: {integrity: sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==} + '@vitest/snapshot@4.1.7': + resolution: {integrity: sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==} - '@vitest/spy@4.1.2': - resolution: {integrity: sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==} + '@vitest/spy@4.1.7': + resolution: {integrity: sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==} - '@vitest/utils@4.1.2': - resolution: {integrity: sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==} + '@vitest/utils@4.1.7': + resolution: {integrity: sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==} '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -11861,7 +11893,7 @@ packages: react-dom: ^18.0.0 || ^19.0.0 solid-js: ^1.0.0 svelte: ^4.0.0 || ^5.0.0 - vitest: 4.1.2 + vitest: 4.1.7 vue: ^3.0.0 peerDependenciesMeta: '@lynx-js/react': @@ -14999,6 +15031,10 @@ packages: resolution: {integrity: sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==} engines: {node: '>=12.13.0'} + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -16603,21 +16639,37 @@ packages: yaml: optional: true + vitest-browser-react@2.2.0: + resolution: {integrity: sha512-oY3KM6305kwJMa6nHo92vVtkOsih7mjEf12dLKuphaF+9ywWPEc+qanIBd394SZ6m5LadVEaG6dicvvizOzmjA==} + peerDependencies: + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + vitest: 4.1.7 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + vitest-tsconfig-paths@3.4.1: resolution: {integrity: sha512-CnRpA/jcqgZfnkk0yvwFW92UmIpf03wX/wLiQBNWAcOG7nv6Sdz3GsPESAMEqbVy8kHBoWB3XeNamu6PUrFZLA==} - vitest@4.1.2: - resolution: {integrity: sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==} + vitest@4.1.7: + resolution: {integrity: sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.2 - '@vitest/browser-preview': 4.1.2 - '@vitest/browser-webdriverio': 4.1.2 - '@vitest/ui': 4.1.2 + '@vitest/browser-playwright': 4.1.7 + '@vitest/browser-preview': 4.1.7 + '@vitest/browser-webdriverio': 4.1.7 + '@vitest/coverage-istanbul': 4.1.7 + '@vitest/coverage-v8': 4.1.7 + '@vitest/ui': 4.1.7 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -16634,6 +16686,10 @@ packages: optional: true '@vitest/browser-webdriverio': optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true '@vitest/ui': optional: true happy-dom: @@ -18423,6 +18479,8 @@ snapshots: '@better-fetch/fetch@1.1.21': {} + '@blazediff/core@1.9.1': {} + '@bramus/specificity@2.4.2': dependencies: css-tree: 3.2.1 @@ -20011,11 +20069,11 @@ snapshots: dependencies: playwright: 1.51.1 - '@polar-sh/better-auth@1.8.3(@polar-sh/sdk@0.42.5)(@stripe/react-stripe-js@4.0.2(@stripe/stripe-js@7.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@stripe/stripe-js@7.9.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(better-auth@1.4.22(better-sqlite3@12.8.0)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.51.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(pg@8.20.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(vitest@4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.5.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))))(react-dom@19.2.5(react@19.2.5))(react-is@19.2.4)(react@19.2.5)(redux@5.0.1)(zod@4.3.6)': + '@polar-sh/better-auth@1.8.3(@polar-sh/sdk@0.42.5)(@stripe/react-stripe-js@4.0.2(@stripe/stripe-js@7.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@stripe/stripe-js@7.9.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(better-auth@1.4.22(better-sqlite3@12.8.0)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.51.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(pg@8.20.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(vitest@4.1.7))(react-dom@19.2.5(react@19.2.5))(react-is@19.2.4)(react@19.2.5)(redux@5.0.1)(zod@4.3.6)': dependencies: '@polar-sh/checkout': 0.2.0(@stripe/react-stripe-js@4.0.2(@stripe/stripe-js@7.9.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@stripe/stripe-js@7.9.0)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react-is@19.2.4)(react@19.2.5)(redux@5.0.1) '@polar-sh/sdk': 0.42.5 - better-auth: 1.4.22(better-sqlite3@12.8.0)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.51.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(pg@8.20.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(vitest@4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.5.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))) + better-auth: 1.4.22(better-sqlite3@12.8.0)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.51.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(pg@8.20.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(vitest@4.1.7) zod: 4.3.6 transitivePeerDependencies: - '@stripe/react-stripe-js' @@ -22883,27 +22941,121 @@ snapshots: optionalDependencies: babel-plugin-react-compiler: 1.0.0 - '@vitest/expect@4.1.2': + '@vitest/browser-playwright@4.1.7(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(playwright@1.51.1)(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.7)': + dependencies: + '@vitest/browser': 4.1.7(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.7) + '@vitest/mocker': 4.1.7(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + playwright: 1.51.1 + tinyrainbow: 3.1.0 + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.19.37)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/browser-playwright@4.1.7(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(playwright@1.51.1)(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.7)': + dependencies: + '@vitest/browser': 4.1.7(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.7) + '@vitest/mocker': 4.1.7(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + playwright: 1.51.1 + tinyrainbow: 3.1.0 + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.5.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + optional: true + + '@vitest/browser-playwright@4.1.7(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(playwright@1.51.1)(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.7)': + dependencies: + '@vitest/browser': 4.1.7(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.7) + '@vitest/mocker': 4.1.7(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + playwright: 1.51.1 + tinyrainbow: 3.1.0 + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + optional: true + + '@vitest/browser@4.1.7(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.7)': + dependencies: + '@blazediff/core': 1.9.1 + '@vitest/mocker': 4.1.7(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/utils': 4.1.7 + magic-string: 0.30.21 + pngjs: 7.0.0 + sirv: 3.0.2 + tinyrainbow: 3.1.0 + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.19.37)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + ws: 8.20.0 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/browser@4.1.7(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.7)': + dependencies: + '@blazediff/core': 1.9.1 + '@vitest/mocker': 4.1.7(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/utils': 4.1.7 + magic-string: 0.30.21 + pngjs: 7.0.0 + sirv: 3.0.2 + tinyrainbow: 3.1.0 + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.5.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + ws: 8.20.0 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + optional: true + + '@vitest/browser@4.1.7(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.7)': + dependencies: + '@blazediff/core': 1.9.1 + '@vitest/mocker': 4.1.7(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/utils': 4.1.7 + magic-string: 0.30.21 + pngjs: 7.0.0 + sirv: 3.0.2 + tinyrainbow: 3.1.0 + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + ws: 8.20.0 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + optional: true + + '@vitest/expect@4.1.7': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.2 - '@vitest/utils': 4.1.2 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.2(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@4.1.7(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: - '@vitest/spy': 4.1.2 + '@vitest/spy': 4.1.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.11.5(@types/node@20.19.37)(typescript@5.9.3) vite: 8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3) - '@vitest/mocker@4.1.2(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@4.1.7(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: - '@vitest/spy': 4.1.2 + '@vitest/spy': 4.1.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: @@ -22911,36 +23063,36 @@ snapshots: vite: 8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3) optional: true - '@vitest/mocker@4.1.2(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@4.1.7(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: - '@vitest/spy': 4.1.2 + '@vitest/spy': 4.1.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.11.5(@types/node@25.9.0)(typescript@5.9.3) vite: 8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3) - '@vitest/pretty-format@4.1.2': + '@vitest/pretty-format@4.1.7': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.2': + '@vitest/runner@4.1.7': dependencies: - '@vitest/utils': 4.1.2 + '@vitest/utils': 4.1.7 pathe: 2.0.3 - '@vitest/snapshot@4.1.2': + '@vitest/snapshot@4.1.7': dependencies: - '@vitest/pretty-format': 4.1.2 - '@vitest/utils': 4.1.2 + '@vitest/pretty-format': 4.1.7 + '@vitest/utils': 4.1.7 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.2': {} + '@vitest/spy@4.1.7': {} - '@vitest/utils@4.1.2': + '@vitest/utils@4.1.7': dependencies: - '@vitest/pretty-format': 4.1.2 + '@vitest/pretty-format': 4.1.7 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -23445,7 +23597,7 @@ snapshots: baseline-browser-mapping@2.10.17: {} - better-auth@1.4.22(better-sqlite3@12.8.0)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.51.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(pg@8.20.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(vitest@4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.5.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))): + better-auth@1.4.22(better-sqlite3@12.8.0)(next@16.2.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.1)(@playwright/test@1.51.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(pg@8.20.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(vitest@4.1.7): dependencies: '@better-auth/core': 1.4.22(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.2)(kysely@0.28.17)(nanostores@1.2.0) '@better-auth/telemetry': 1.4.22(@better-auth/core@1.4.22(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.2)(kysely@0.28.17)(nanostores@1.2.0)) @@ -23465,7 +23617,7 @@ snapshots: pg: 8.20.0 react: 19.2.5 react-dom: 19.2.5(react@19.2.5) - vitest: 4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.5.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.5.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) better-call@1.1.8(zod@4.3.6): dependencies: @@ -24837,7 +24989,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 esutils@2.0.3: {} @@ -26422,7 +26574,7 @@ snapshots: micromark-extension-mdx-expression@3.0.1: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 devlop: 1.1.0 micromark-factory-mdx-expression: 2.0.3 micromark-factory-space: 2.0.1 @@ -26433,7 +26585,7 @@ snapshots: micromark-extension-mdx-jsx@3.0.2: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 devlop: 1.1.0 estree-util-is-identifier-name: 3.0.0 micromark-factory-mdx-expression: 2.0.3 @@ -26450,7 +26602,7 @@ snapshots: micromark-extension-mdxjs-esm@3.0.0: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 micromark-util-character: 2.1.1 @@ -26486,7 +26638,7 @@ snapshots: micromark-factory-mdx-expression@2.0.3: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 devlop: 1.1.0 micromark-factory-space: 2.0.1 micromark-util-character: 2.1.1 @@ -26550,7 +26702,7 @@ snapshots: micromark-util-events-to-acorn@2.0.3: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 '@types/unist': 3.0.3 devlop: 1.1.0 estree-util-visit: 2.0.0 @@ -27307,6 +27459,8 @@ snapshots: pngjs@6.0.0: {} + pngjs@7.0.0: {} + possible-typed-array-names@1.1.0: {} postcss-selector-parser@7.1.1: @@ -27780,7 +27934,7 @@ snapshots: recma-parse@1.0.0: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 esast-util-from-js: 2.0.1 unified: 11.0.5 vfile: 6.0.3 @@ -29156,6 +29310,15 @@ snapshots: tsx: 4.21.0 yaml: 2.8.3 + vitest-browser-react@2.2.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(vitest@4.1.7): + dependencies: + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.19.37)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + vitest-tsconfig-paths@3.4.1: dependencies: debug: 4.4.3 @@ -29165,15 +29328,15 @@ snapshots: transitivePeerDependencies: - supports-color - vitest@4.1.2(@opentelemetry/api@1.9.1)(@types/node@20.19.37)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)): + vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.19.37)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: - '@vitest/expect': 4.1.2 - '@vitest/mocker': 4.1.2(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.2 - '@vitest/runner': 4.1.2 - '@vitest/snapshot': 4.1.2 - '@vitest/spy': 4.1.2 - '@vitest/utils': 4.1.2 + '@vitest/expect': 4.1.7 + '@vitest/mocker': 4.1.7(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/pretty-format': 4.1.7 + '@vitest/runner': 4.1.7 + '@vitest/snapshot': 4.1.7 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -29190,19 +29353,20 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 20.19.37 + '@vitest/browser-playwright': 4.1.7(msw@2.11.5(@types/node@20.19.37)(typescript@5.9.3))(playwright@1.51.1)(vite@8.0.8(@types/node@20.19.37)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.7) jsdom: 29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0) transitivePeerDependencies: - msw - vitest@4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.5.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)): + vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.5.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: - '@vitest/expect': 4.1.2 - '@vitest/mocker': 4.1.2(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.2 - '@vitest/runner': 4.1.2 - '@vitest/snapshot': 4.1.2 - '@vitest/spy': 4.1.2 - '@vitest/utils': 4.1.2 + '@vitest/expect': 4.1.7 + '@vitest/mocker': 4.1.7(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/pretty-format': 4.1.7 + '@vitest/runner': 4.1.7 + '@vitest/snapshot': 4.1.7 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -29219,20 +29383,21 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 25.5.0 + '@vitest/browser-playwright': 4.1.7(msw@2.11.5(@types/node@25.5.0)(typescript@5.9.3))(playwright@1.51.1)(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.7) jsdom: 29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0) transitivePeerDependencies: - msw optional: true - vitest@4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(jsdom@25.0.1(canvas@2.11.2))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)): + vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@25.0.1(canvas@2.11.2))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: - '@vitest/expect': 4.1.2 - '@vitest/mocker': 4.1.2(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.2 - '@vitest/runner': 4.1.2 - '@vitest/snapshot': 4.1.2 - '@vitest/spy': 4.1.2 - '@vitest/utils': 4.1.2 + '@vitest/expect': 4.1.7 + '@vitest/mocker': 4.1.7(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/pretty-format': 4.1.7 + '@vitest/runner': 4.1.7 + '@vitest/snapshot': 4.1.7 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -29249,19 +29414,20 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 25.9.0 + '@vitest/browser-playwright': 4.1.7(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(playwright@1.51.1)(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.7) jsdom: 25.0.1(canvas@2.11.2) transitivePeerDependencies: - msw - vitest@4.1.2(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)): + vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.0)(@vitest/browser-playwright@4.1.7)(jsdom@29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0))(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: - '@vitest/expect': 4.1.2 - '@vitest/mocker': 4.1.2(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.2 - '@vitest/runner': 4.1.2 - '@vitest/snapshot': 4.1.2 - '@vitest/spy': 4.1.2 - '@vitest/utils': 4.1.2 + '@vitest/expect': 4.1.7 + '@vitest/mocker': 4.1.7(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/pretty-format': 4.1.7 + '@vitest/runner': 4.1.7 + '@vitest/snapshot': 4.1.7 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -29278,6 +29444,7 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 25.9.0 + '@vitest/browser-playwright': 4.1.7(msw@2.11.5(@types/node@25.9.0)(typescript@5.9.3))(playwright@1.51.1)(vite@8.0.8(@types/node@25.9.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.47.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.7) jsdom: 29.0.2(@noble/hashes@2.0.1)(canvas@3.1.0) transitivePeerDependencies: - msw diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1f6cd63d35..2a5eb50b3c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -17,8 +17,8 @@ overrides: "@headlessui/react": "^2.2.4" "@tiptap/core": "^3.0.0" "@tiptap/pm": "^3.0.0" - "vitest": "4.1.2" - "@vitest/runner": "4.1.2" + "vitest": "4.1.7" + "@vitest/runner": "4.1.7" "@y/prosemirror>lib0": "1.0.0-rc.13" allowBuilds: "@parcel/watcher": true diff --git a/tests/.vitest-attachments/4062b079035cf9613a94c981f8de2d1e2e409165.png b/tests/.vitest-attachments/4062b079035cf9613a94c981f8de2d1e2e409165.png new file mode 100644 index 0000000000000000000000000000000000000000..16ddf2305c941713d05d0aea69dbcbdcb4175aee GIT binary patch literal 2689 zcmb`JdpOf?AIEk0sidMbqdA0-A2D-^**dIj)Kq?Ca!kw6rbcp{kTPteBIi{OsWgS= zkh5uWHW48tMM;h1%yaK~p6mJN`RBQw>-W8`@Avv%*L`1~@ArP+@AvC-(@)_own@lK zh>3}9!(lOJ#KhK}1n-uc#laIDA>Jq^CV|Ca4*wOf?t5(*>Fw+WzVOSJ&}&nawyp%z zG{iRY_ zw#?qw@d~qbFntDJvOF-iv``l+_wYAvF@iQ1gv{qv?^RLZKrtAeVPtf4=|w4N>35PU zDj!o1V=#`0=;&x~acSu*t;%4M-2X9Yutqq__w@4mXa_^n(sOgwVOOtSecRJR)iCuN zml_J4Q&=k|88mffS(GJgl|68I{-+ye)dgA}-qd8-mVU7QSzdnrrc9K?^LJP>a2`+vG`jyu6MLXDr!h={&~%*Dnzb& zsGZTvWe^GWNm2D&GVC=JM#n^8RKnpmvzg58AtgYk&%&|ogM#cz_&dCt9JR{RgT>z_k&tQf?C6Q_!%3Ui6$;N4(?b+jY6+2-`P_Z zvY=0^X78c%M>not$I*bqCZwjuG=6WiGVbi?sIgEA#kjkxt}M-YG{o$_aPi{D`H4qt z`&a%t3IGlOQN-+*eqAQ#%~4HcszbPQ^Cw%N+OEna&-(CDkQlbwG?OQ-J|DPBlhI!)4*@fN_r~-5tTu+D*IvE5h3%Je@_is z(P*f%9LJG&E@2I)U0hi2zKzv{Z$Pds&)leYgXlQ$Htaywq#yK+=8uN_tle~}_j&ju zHcj5RZba|Fsc2y2i>;{*wUL|Tj43du-LkSa&dzuoZc7qTIMa^q>g#(`zXRO@Y~5EC zYUbUOzgJZ?a`4r)q5$D}?xns6{%CEdKybv?cDIg>PC7F1c3T@BR0hWUV=E;7-aY;v zpNhl5!Dh|(bopE|7}+2$>Dv9owqk8{DGmycuz7@k;WLDyQmH0Ezcl+xd^}oH4KG}{ z@H}{q0?~DDNzys@b0H*p;DF>H&&JyPSbL_qaA^g(@afLm?(Pv2|4ArN)?>WmLGjC% zodquF;|%$R7w-Jh6yQq)u}MZBfq=h0WpCdIG&vZ$XyD{j5ddr~51h3yKguGMP!z%s zT~{;@dQKTMHKGh)O<*SBH84Ebp%8aT zIXSsF1+%2`a#M~W^%E9mXlQ5xwrRh%HV;~#X&#(x;JFhN2Q^;D$<=p1J?lv#DNp3w zbFTX`Gtyh;s{thY%g(OQtzJAeHPs|+70C{n-r^FrVpthG_qi@=D;WGZ8-%m68h!iN zhIbx*oC$j~*;jN0+_!Ld)TK9XBzb$gwaw_-6mdMUZUmN+S;lGRysSJJBOQIS~1br*H1Pg%PZ05 z_kzEH9i-mq>9K2SYQmZZPOCjVTacTdFDXi$$nuGaiOBik$P;W-28h&u)srsE2$=XR zzD=Gt%0jpG`&73M!ATv;DhZpNj;TmbPfwR-R#YHq^CO#R%R-A>e1@eq#C<}LV_;;Y z?>}h|n;Y=m@ue+8H392NfB+~Tv9p6PfhjtAd$DJHeGw&3N=uX9IR`U9mBruRn=9u& z#JcmMrB7rcQ|P-is5<@JvJ@cnPC8~>YbU+K*5BV~Y-~(~QlF}Yo5x}y!i6tv#ChIk zb?eL)9(@=1VY%|M(^sq7B61+#2KeI+pvf`tn{{81)A)+Parazka(q=5@(6`;py*=j z!?3kgkvBl-eV@j}#E8heOZ|lMZ1QC8Pk4JtWpUG6urlUA+7;zy1S*G4W2`AQ}n=gJuV=t!8( zKU=A#0MU+&9Qwg$073`^0=?J6(Z{nU-@lB_4nX3SrG|sfzkoh|{8$Y@x$BX&5{9zL z`BQb!+*IojkpQW6jSq7GG%T|lstPT$*%*14(_P}@f|>Gj{>1z_p2ebSSXo3y}>v*2ube(X3L94*Cg$M6`=5!Z-+0OzI=)c^nh literal 0 HcmV?d00001 diff --git a/tests/.vitest-attachments/a09f1c2c11fad3416f5b9251115f2ae2cb760757.png b/tests/.vitest-attachments/a09f1c2c11fad3416f5b9251115f2ae2cb760757.png new file mode 100644 index 0000000000000000000000000000000000000000..8f82524878e2f0ad14a0266363a92801b73349eb GIT binary patch literal 4313 zcmeI0`BRfg9>#I3)twoUo%LWH1$NgG5rm8)1dyXqg+B8S{^8%P3%aEE{aLI_E=**{@^UAnsJt@rJ>`t5$cpXceS z_sKslBlN%d{u@0#J^hQnUbv#Cw+pGK_f_8?zXDHk?laTDWmn`C#7}yRo?~-*dVfm4 zc;QD>Y_3cg{Fr)M(zZf0MYe^e9d;|$Z?>hstwLRIA51jM39WIuZb|Ui{RdWXmdCYc zx4yId^Uy17z+lqh&Z?G6==N7dEhSfj@1&&IXQW%}4EOHwP*n5XSGy{EXNI`GN-1s+ zZ+vDP5oj(L8~gUJ*QG98y$Pr|vi*1Tkt_!q91=Y};ijFRfOx8+f`UoNPcTiG$&2xe z*Q{A)6_k|BX_R8|@&kYFnBhL6}>oGEtMfm;#QZ~aw~oiqoTsd<}M0}?A~qQ*cZ-{{2~|}JQq4z6_M3; zEV9cD#*G^G3CfH8{6x~0>d%grH9n9xE--LVCs?y~x!E9z{`^cpYgSM`esiwaKX&q= zH1(*oB%i!Fmz|w`#5%B?J<*b6=#Q6DU;aY*-sbM$3@u(1P8u%vv9Yl+^u5<~BGdQH zji&own)HLF#V^xwT2L(@eIbuWrp2w}r_vCK^lOb#a~ms?w&Wv<&q=V$+2?{go}Hpl zDDlyCx6cJDU_WTD(#Mx^?szRA#Z!!z7lrR`ePfq4LfG}I|h^N!4d!5ybT%}0e?*^k7J8fp5!-BC`%T#|> zs83v+rmx`V%+o&o0>O22>h{|LfdFW|tX0cR;9=uQFy2O+N&K@*g~P+c?4`z#cmMf% zpa{vHFD(NEc^+&r4V)IgNXL=ieSI>trrTD?j&ad$NcgJh9J<+bPaa!LR_6Oj2|s%cG~-)2LJ`m#8#8 zd+S#`(`di*#suB013sB$9kl(SH*dZGk?iJPrI(hLf>{jCFIF)_M#xHGjC7@#Mkb6f zFiG@SDb+i}5^al{9|V%$`|$Jxz_nA7>Y@dlLwwD;g;)7=2i)CED-2D$ndEtGD~c2Z3VTBSIg>*0@lYPdntZnqoa0F zvwbPT`T2P;`s0H=)EEQ1iGPJlS}~E`Hq8n8>e-j6Dd(5!Gcq##$*S7`rqGZOwlo7h z-IX;kI2bj6gj3sKn8wxI$R2w+N6`UU*xnt()?px~k-52ff`N&1jt(I&*jYLHUK5$%67#6)@Mv>NysYu%o_a)rJOq(GZ7aZw*WUS)rLByN~{=Oex8 z!QM)E;POP12@nU6L|Xq8R99P@Y~rE?R@<(T$3ZY<8;H2Ev9V6~&cl}1Q6LQtwt%8* zK&E8E{DHluD@pGke+OK$HqrDTMi5*KfM%N|f(+GlS@$}wygx8vikd{ zg8`xnJ5=hK>iDHS4Poqa(3MZth~kvOgoFfOa3+HhrqKCea5x;zInLBQ-|^=A$0>&{ zgPTupZcF98t;t7f2y635Ul9m};DI70lW7BoyVIPxBn~(%-gH`_c_e&Ym=NS>rDvHJ zv#w58nDt>`PCf%{xiDHC0BYX@+*VOocza`cQlnOiXWNgbI!1h?yJq?kf>jzFh#b+% zsTa-Lj_I^w?>lB!#W{a(;w@OBAD2Llk%P?v@SGG~*5K^ePT_``eDuKDrTiholGHMl#grYK}5 zQ9(mPLr`zN6ei2b&UVmV?Tg>iZu0}I-W(s&y#-rXRVwy_gHkiS53FqTX?qfVG%yD{ zEEEP8=do_FEbBy_s*L=gy~DZvV2-yXboP0^_F}_x}q6p+%km literal 0 HcmV?d00001 diff --git a/tests/package.json b/tests/package.json index ea7da9e138..a276b22593 100644 --- a/tests/package.json +++ b/tests/package.json @@ -7,6 +7,8 @@ "lint": "eslint src --max-warnings 0", "playwright": "playwright test", "test": "vitest --run", + "test:browser": "vitest --run --config ./vite.config.browser.ts", + "test:browser:updateSnaps": "vitest --run --config ./vite.config.browser.ts -u", "test:updateSnaps": "docker run --rm -e RUN_IN_DOCKER=true --network host -v $(pwd)/..:/work/ -w /work/tests -it mcr.microsoft.com/playwright:v1.51.1-noble npx playwright test -u", "test-ct": "playwright test -c playwright-ct.config.ts --headed", "test-ct:updateSnaps": "docker run --rm -e RUN_IN_DOCKER=true --network host -v $(pwd)/..:/work/ -w /work/tests -it mcr.microsoft.com/playwright:v1.51.1-noble npx playwright test -c playwright-ct.config.ts -u", @@ -24,6 +26,11 @@ "@types/node": "^20.19.22", "@types/react": "^19.2.3", "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "@vitest/browser": "4.1.7", + "@vitest/browser-playwright": "4.1.7", + "@y/protocols": "^1.0.6-rc.1", + "@y/y": "^14.0.0-rc.16", "eslint": "^8.57.1", "htmlfy": "^0.6.7", "react": "^19.2.5", @@ -32,7 +39,8 @@ "rimraf": "^5.0.10", "vite": "^8.0.8", "vite-plugin-eslint": "^1.8.1", - "vitest": "^4.1.2" + "vitest": "4.1.7", + "vitest-browser-react": "^2.2.0" }, "eslintConfig": { "extends": [ diff --git a/tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode---hello-world------hello-universe--1.png b/tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode---hello-world------hello-universe--1.png new file mode 100644 index 0000000000000000000000000000000000000000..8f82524878e2f0ad14a0266363a92801b73349eb GIT binary patch literal 4313 zcmeI0`BRfg9>#I3)twoUo%LWH1$NgG5rm8)1dyXqg+B8S{^8%P3%aEE{aLI_E=**{@^UAnsJt@rJ>`t5$cpXceS z_sKslBlN%d{u@0#J^hQnUbv#Cw+pGK_f_8?zXDHk?laTDWmn`C#7}yRo?~-*dVfm4 zc;QD>Y_3cg{Fr)M(zZf0MYe^e9d;|$Z?>hstwLRIA51jM39WIuZb|Ui{RdWXmdCYc zx4yId^Uy17z+lqh&Z?G6==N7dEhSfj@1&&IXQW%}4EOHwP*n5XSGy{EXNI`GN-1s+ zZ+vDP5oj(L8~gUJ*QG98y$Pr|vi*1Tkt_!q91=Y};ijFRfOx8+f`UoNPcTiG$&2xe z*Q{A)6_k|BX_R8|@&kYFnBhL6}>oGEtMfm;#QZ~aw~oiqoTsd<}M0}?A~qQ*cZ-{{2~|}JQq4z6_M3; zEV9cD#*G^G3CfH8{6x~0>d%grH9n9xE--LVCs?y~x!E9z{`^cpYgSM`esiwaKX&q= zH1(*oB%i!Fmz|w`#5%B?J<*b6=#Q6DU;aY*-sbM$3@u(1P8u%vv9Yl+^u5<~BGdQH zji&own)HLF#V^xwT2L(@eIbuWrp2w}r_vCK^lOb#a~ms?w&Wv<&q=V$+2?{go}Hpl zDDlyCx6cJDU_WTD(#Mx^?szRA#Z!!z7lrR`ePfq4LfG}I|h^N!4d!5ybT%}0e?*^k7J8fp5!-BC`%T#|> zs83v+rmx`V%+o&o0>O22>h{|LfdFW|tX0cR;9=uQFy2O+N&K@*g~P+c?4`z#cmMf% zpa{vHFD(NEc^+&r4V)IgNXL=ieSI>trrTD?j&ad$NcgJh9J<+bPaa!LR_6Oj2|s%cG~-)2LJ`m#8#8 zd+S#`(`di*#suB013sB$9kl(SH*dZGk?iJPrI(hLf>{jCFIF)_M#xHGjC7@#Mkb6f zFiG@SDb+i}5^al{9|V%$`|$Jxz_nA7>Y@dlLwwD;g;)7=2i)CED-2D$ndEtGD~c2Z3VTBSIg>*0@lYPdntZnqoa0F zvwbPT`T2P;`s0H=)EEQ1iGPJlS}~E`Hq8n8>e-j6Dd(5!Gcq##$*S7`rqGZOwlo7h z-IX;kI2bj6gj3sKn8wxI$R2w+N6`UU*xnt()?px~k-52ff`N&1jt(I&*jYLHUK5$%67#6)@Mv>NysYu%o_a)rJOq(GZ7aZw*WUS)rLByN~{=Oex8 z!QM)E;POP12@nU6L|Xq8R99P@Y~rE?R@<(T$3ZY<8;H2Ev9V6~&cl}1Q6LQtwt%8* zK&E8E{DHluD@pGke+OK$HqrDTMi5*KfM%N|f(+GlS@$}wygx8vikd{ zg8`xnJ5=hK>iDHS4Poqa(3MZth~kvOgoFfOa3+HhrqKCea5x;zInLBQ-|^=A$0>&{ zgPTupZcF98t;t7f2y635Ul9m};DI70lW7BoyVIPxBn~(%-gH`_c_e&Ym=NS>rDvHJ zv#w58nDt>`PCf%{xiDHC0BYX@+*VOocza`cQlnOiXWNgbI!1h?yJq?kf>jzFh#b+% zsTa-Lj_I^w?>lB!#W{a(;w@OBAD2Llk%P?v@SGG~*5K^ePT_``eDuKDrTiholGHMl#grYK}5 zQ9(mPLr`zN6ei2b&UVmV?Tg>iZu0}I-W(s&y#-rXRVwy_gHkiS53FqTX?qfVG%yD{ zEEEP8=do_FEbBy_s*L=gy~DZvV2-yXboP0^_F}_x}q6p+%km literal 0 HcmV?d00001 diff --git a/tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-add-bold-chromium-darwin.png b/tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-add-bold-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..f645bc8552003c15a3a19e848faecbe5d2888736 GIT binary patch literal 1639 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 Gm;M56C1MW% literal 0 HcmV?d00001 diff --git a/tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-remove-bold-chromium-darwin.png b/tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-remove-bold-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..1ce6528cf3c1391e67ceb0fb28589d3a0cfcee9c GIT binary patch literal 1592 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 literal 0 HcmV?d00001 diff --git a/tests/src/browser/y-prosemirror/basicText.test.tsx b/tests/src/browser/y-prosemirror/basicText.test.tsx new file mode 100644 index 0000000000..a9ac29aa14 --- /dev/null +++ b/tests/src/browser/y-prosemirror/basicText.test.tsx @@ -0,0 +1,312 @@ +/* eslint-disable testing-library/render-result-naming-convention */ +/** + * Vitest browser-mode tests for suggestion-mode editing. Each test + * sets up a fresh editor + base/suggestion Y.Doc pair via + * `setupSuggestionTest()`, applies an edit in suggestion mode, and + * captures a screenshot plus inline XML snapshots of both Y.Docs and + * the ProseMirror document. The PM doc is where the suggestion marks + * live – the Y.Docs only carry the content of the different branches. + */ +import { SuggestionsExtension } from "@blocknote/core/y"; +import { expect, test } from "vitest"; + +import { + editorHtml, + setupSuggestionTest, + waitForSuggestion, + ydocXml, +} from "./fixtures/suggestionFixture.js"; + +// Pure text edit: replace one word with another and confirm the diff +// is rendered as inline / spans around the changed letters. +test("suggestion mode: 'hello world' -> 'hello universe'", async () => { + const { editor, screen, baseDoc, suggestionDoc, sync } = + await setupSuggestionTest(); + + // 1. Set the base doc to "hello world". The block id is pinned so the + // snapshots stay deterministic. + editor.replaceBlocks(editor.document, [ + { id: "block-hello", type: "paragraph", content: "hello world" }, + ]); + + // 2. Replay base updates into the suggestion doc so both docs start + // from the same state. + sync(); + + await expect.element(screen.getByText("hello world")).toBeVisible(); + + // 3. Subsequent edits are recorded as suggestions instead of mutating + // the doc directly. + editor.getExtension(SuggestionsExtension)!.enableSuggestions(); + + // 4. Replace "world" with "universe" via updateBlock. + const [block] = editor.document; + editor.updateBlock(block, { type: "paragraph", content: "hello universe" }); + + // Wait for the suggestion edit to land in the DOM (React commits the + // re-render on the next frame; without this the screenshot can race + // the update). "unive" only exists once "world" -> "universe" has + // been split into / spans, so this is a precise sentinel. + await expect.element(screen.getByText("unive")).toBeVisible(); + + // 5a. Visual snapshot of the rendered editor. + await expect(screen.getByTestId("editor-root")).toMatchScreenshot( + "suggestion-mode-universe", + ); + + // 5b. Y.Doc XML – just the merged textual state; suggestion marks + // don't live here. + expect(ydocXml(baseDoc)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(ydocXml(suggestionDoc)).toMatchInlineSnapshot(` + " + + hello universe + + " + `); + + // 5c. ProseMirror XML – this is where the suggestion marks + // (`y-attributed-insert` / `y-attributed-delete`) live. + expect(editorHtml(editor)).toMatchInlineSnapshot(` + " + + + + hello + wo + unive + r + ld + se + + + + " + `); +}); + +// TODO: format-only suggestions have no visual marker in the rendered +// editor – the screenshot for this test shows "hello **world**" with +// no indication that the bold is a *pending* suggestion. Only +// `y-attributed-insert` / `y-attributed-delete` have a `toDOM` in +// SuggestionMarks.ts; `y-attributed-format` does not. Decide whether +// this is intentional (formats considered "trivial") or a UX gap. +// +// Format-only addition: text content stays the same but a style mark +// (bold) is added on top. Surfaces how suggestions track pure format +// changes (currently `y-attributed-format`, with no visual marker). +test("suggestion mode: add bold to 'world'", async () => { + const { editor, screen, baseDoc, suggestionDoc, sync } = + await setupSuggestionTest(); + + // Base: plain "hello world". + editor.replaceBlocks(editor.document, [ + { id: "block-hello", type: "paragraph", content: "hello world" }, + ]); + sync(); + await expect.element(screen.getByText("hello world")).toBeVisible(); + + editor.getExtension(SuggestionsExtension)!.enableSuggestions(); + + // Suggestion edit: bold the word "world" (content text is unchanged, + // only the style differs). + const [block] = editor.document; + editor.updateBlock(block, { + type: "paragraph", + content: [ + { type: "text", text: "hello ", styles: {} }, + { type: "text", text: "world", styles: { bold: true } }, + ], + }); + + await waitForSuggestion(editor); + + await expect(screen.getByTestId("editor-root")).toMatchScreenshot( + "suggestion-mode-add-bold", + ); + + // TODO: the base and suggestion YDoc XML snapshots below are + // identical even though one represents the "before bold" state and + // the other the "after bold" state. `Y.XmlFragment.toString()` only + // serialises element/text structure – marks and attribution data + // live elsewhere and don't surface here. Consider a richer YDoc + // serializer that includes formatting + attribution metadata so + // these snapshots actually differ. + expect(ydocXml(baseDoc)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(ydocXml(suggestionDoc)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(editorHtml(editor)).toMatchInlineSnapshot(` + " + + + + hello + + world + + + + + " + `); +}); + +// Format-only removal: bold mark is stripped from an already-styled +// word, text content unchanged. Mirror of the add-bold case to check +// removal is handled symmetrically. +test("suggestion mode: remove bold from 'world'", async () => { + const { editor, screen, baseDoc, suggestionDoc, sync } = + await setupSuggestionTest(); + + // Base: "hello " + bold "world". + editor.replaceBlocks(editor.document, [ + { + id: "block-hello", + type: "paragraph", + content: [ + { type: "text", text: "hello ", styles: {} }, + { type: "text", text: "world", styles: { bold: true } }, + ], + }, + ]); + sync(); + await expect.element(screen.getByText("world")).toBeVisible(); + + editor.getExtension(SuggestionsExtension)!.enableSuggestions(); + + // Suggestion edit: strip bold from "world". + const [block] = editor.document; + editor.updateBlock(block, { + type: "paragraph", + content: "hello world", + }); + + await waitForSuggestion(editor); + + await expect(screen.getByTestId("editor-root")).toMatchScreenshot( + "suggestion-mode-remove-bold", + ); + + expect(ydocXml(baseDoc)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(ydocXml(suggestionDoc)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(editorHtml(editor)).toMatchInlineSnapshot(` + " + + + + hello + world + + + + " + `); +}); + +// TODO: the snapshot below reveals that `y-attributed-format` wraps +// *all* marks on the affected range, not just the newly added one. +// The PM XML shows +// world +// so from the attribution data alone we can't tell which mark is new +// (italic) and which is pre-existing (bold). If accept/reject logic +// needs to revert only the new mark, this granularity is insufficient. +// +// Format added on top of an existing format: bold "world" gets italic +// layered on (bold is preserved). Checks that suggestion attribution +// is recorded only for the new mark, not the pre-existing one. +test("suggestion mode: add italic to already-bold 'world'", async () => { + const { editor, screen, baseDoc, suggestionDoc, sync } = + await setupSuggestionTest(); + + // Base: "hello " + bold "world". + editor.replaceBlocks(editor.document, [ + { + id: "block-hello", + type: "paragraph", + content: [ + { type: "text", text: "hello ", styles: {} }, + { type: "text", text: "world", styles: { bold: true } }, + ], + }, + ]); + sync(); + await expect.element(screen.getByText("world")).toBeVisible(); + + editor.getExtension(SuggestionsExtension)!.enableSuggestions(); + + // Suggestion edit: add italic to "world" while keeping it bold. + const [block] = editor.document; + editor.updateBlock(block, { + type: "paragraph", + content: [ + { type: "text", text: "hello ", styles: {} }, + { type: "text", text: "world", styles: { bold: true, italic: true } }, + ], + }); + + await waitForSuggestion(editor); + + await expect(screen.getByTestId("editor-root")).toMatchScreenshot( + "suggestion-mode-add-italic-to-bold", + ); + + expect(ydocXml(baseDoc)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(ydocXml(suggestionDoc)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(editorHtml(editor)).toMatchInlineSnapshot(` + " + + + + hello + + + world + + + + + + " + `); +}); diff --git a/tests/src/browser/y-prosemirror/fixtures/suggestionFixture.tsx b/tests/src/browser/y-prosemirror/fixtures/suggestionFixture.tsx new file mode 100644 index 0000000000..e32da7623a --- /dev/null +++ b/tests/src/browser/y-prosemirror/fixtures/suggestionFixture.tsx @@ -0,0 +1,145 @@ +/* eslint-disable testing-library/render-result-naming-convention */ +/** + * Shared fixture for browser-mode suggestion tests. + * + * Mounts a single BlockNote editor bound to an in-memory `baseDoc`, + * with a second in-memory `suggestionDoc` set up as the diff target. + * The provider/yhub round-trip is replaced by a manual `sync()`. + */ +import "@blocknote/core/fonts/inter.css"; +import "@blocknote/mantine/style.css"; + +import { BlockNoteEditor } from "@blocknote/core"; +import { withCollaboration } from "@blocknote/core/y"; +import { BlockNoteView } from "@blocknote/mantine"; +import { useCreateBlockNote } from "@blocknote/react"; +import { Node as PMNode } from "@tiptap/pm/model"; +import { Awareness } from "@y/protocols/awareness"; +import * as Y from "@y/y"; +import { prettify } from "htmlfy"; +import { expect } from "vitest"; +import { render } from "vitest-browser-react"; + +export interface SuggestionFixture { + editor: BlockNoteEditor; + screen: Awaited>; + baseDoc: Y.Doc; + suggestionDoc: Y.Doc; + /** Replay updates from `baseDoc` into `suggestionDoc`. */ + sync: () => void; +} + +export async function setupSuggestionTest(): Promise { + const baseDoc = new Y.Doc(); + const baseAwareness = new Awareness(baseDoc); + baseAwareness.setLocalStateField("user", { + name: "Author", + color: "#30bced", + }); + + const suggestionDoc = new Y.Doc({ isSuggestionDoc: true }); + const attributionManager = Y.createAttributionManagerFromDiff( + baseDoc, + suggestionDoc, + { attrs: new Y.Attributions() }, + ); + attributionManager.suggestionMode = true; + + let editor!: BlockNoteEditor; + function Editor() { + editor = useCreateBlockNote( + withCollaboration({ + collaboration: { + fragment: baseDoc.get("doc"), + provider: { awareness: baseAwareness }, + suggestionDoc, + attributionManager, + user: { name: "Author", color: "#30bced" }, + }, + }), + ); + return ( +
+ +
+ ); + } + + const screen = await render(); + + return { + editor, + screen, + baseDoc, + suggestionDoc, + sync: () => Y.applyUpdate(suggestionDoc, Y.encodeStateAsUpdate(baseDoc)), + }; +} + +/** + * Wait until any suggestion mark (`y-attributed-insert` / + * `y-attributed-delete`) is present in the editor's PM doc. Use this + * after a suggestion-mode edit before snapshotting/screenshotting – + * the PM transaction is sync but the React/DOM commit is not. + * + * For tests whose edit changes visible text, prefer waiting on the + * inserted text via `expect.element(getByText(...))` – it's more + * meaningful. + */ +export async function waitForSuggestion( + editor: BlockNoteEditor, +): Promise { + await expect + .poll(() => editor.prosemirrorState.doc.toString().includes("y-attributed")) + .toBe(true); +} + +/** Pretty-print a Y.Doc's `doc` XmlFragment for an inline snapshot. */ +export function ydocXml(doc: Y.Doc): string { + return prettify(doc.get("doc").toString(), { tag_wrap: true }); +} + +/** + * Pretty-print the editor's ProseMirror doc for an inline snapshot. + * + * We walk the node tree directly rather than going through + * `DOMSerializer` (BlockNote's `renderHTML` adds CSS scaffolding that + * we don't want in snapshots) or `Node.toString()` (drops attrs, so + * block ids and suggestion-mark colors would disappear). + */ +export function editorHtml(editor: BlockNoteEditor): string { + return prettify(pmNodeToXml(editor.prosemirrorState.doc), { + tag_wrap: true, + }); +} + +function pmNodeToXml(node: PMNode): string { + if (node.isText) { + let out = escapeXml(node.text ?? ""); + // PM stores marks outermost-first; wrap innermost-first to preserve order. + for (const mark of node.marks) { + out = `<${mark.type.name}${formatAttrs(mark.attrs)}>${out}`; + } + return out; + } + let inner = ""; + node.content.forEach((child) => { + inner += pmNodeToXml(child); + }); + return `<${node.type.name}${formatAttrs(node.attrs)}>${inner}`; +} + +function formatAttrs(attrs: Record): string { + return Object.entries(attrs) + .filter(([, v]) => v !== null && v !== undefined) + .map(([k, v]) => ` ${k}="${escapeXml(String(v))}"`) + .join(""); +} + +function escapeXml(text: string): string { + return text + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """); +} diff --git a/tests/vite.config.browser.ts b/tests/vite.config.browser.ts new file mode 100644 index 0000000000..037fb8217a --- /dev/null +++ b/tests/vite.config.browser.ts @@ -0,0 +1,46 @@ +import react from "@vitejs/plugin-react"; +import * as path from "path"; +import { defineConfig } from "vite"; +import { playwright } from "@vitest/browser-playwright"; + +// Vitest browser mode config – runs tests in real Chromium via Playwright. +// Run with: pnpm test:browser (from /tests) +export default defineConfig((conf) => ({ + plugins: [react()], + test: { + include: ["./src/browser/**/*.test.ts", "./src/browser/**/*.test.tsx"], + browser: { + enabled: true, + provider: playwright(), + headless: true, + instances: [{ browser: "chromium" }], + // toMatchScreenshot defaults – be lenient since BlockNote renders + // include things like blinking cursors / awareness markers. + expect: { + toMatchScreenshot: { + comparatorName: "pixelmatch", + comparatorOptions: { + threshold: 0.2, + allowedMismatchedPixelRatio: 0.02, + }, + }, + }, + }, + }, + resolve: { + alias: + conf.command === "build" + ? ({ + "@shared": path.resolve(__dirname, "../shared/"), + } as Record) + : ({ + "@shared": path.resolve(__dirname, "../shared/"), + // load live from sources with live reload working + "@blocknote/core": path.resolve(__dirname, "../packages/core/src/"), + "@blocknote/react": path.resolve( + __dirname, + "../packages/react/src/", + ), + } as Record), + }, +})); From 0ad6fc9594e37d96ab8d949be6d1223441b03c5a Mon Sep 17 00:00:00 2001 From: yousefed Date: Thu, 28 May 2026 19:52:26 +0200 Subject: [PATCH 2/4] basic tests --- ...62b079035cf9613a94c981f8de2d1e2e409165.png | Bin 2689 -> 0 bytes ...9f1c2c11fad3416f5b9251115f2ae2cb760757.png | Bin 4313 -> 0 bytes ...e---hello-world------hello-universe--1.png | Bin 4313 -> 0 bytes ...ggestion-mode-add-bold-chromium-darwin.png | Bin 1639 -> 0 bytes ...ode-add-italic-to-bold-chromium-darwin.png | Bin 1806 -> 0 bytes ...stion-mode-remove-bold-chromium-darwin.png | Bin 1592 -> 0 bytes ...ggestion-mode-universe-chromium-darwin.png | Bin 2689 -> 0 bytes ...current-bold-vs-italic-chromium-darwin.png | Bin 0 -> 10914 bytes ...ent-typo-fix-vs-delete-chromium-darwin.png | Bin 0 -> 10597 bytes ...ggestion-mode-add-bold-chromium-darwin.png | Bin 0 -> 7086 bytes ...ode-add-italic-to-bold-chromium-darwin.png | Bin 0 -> 8444 bytes ...stion-mode-remove-bold-chromium-darwin.png | Bin 0 -> 7355 bytes ...ggestion-mode-universe-chromium-darwin.png | Bin 0 -> 8884 bytes .../basicText.concurrent.test.tsx | 250 ++++++++++++++++++ .../browser/y-prosemirror/basicText.test.tsx | 30 ++- .../fixtures/concurrentSuggestionFixture.tsx | 242 +++++++++++++++++ .../fixtures/suggestionFixture.tsx | 75 +++++- 17 files changed, 576 insertions(+), 21 deletions(-) delete mode 100644 tests/.vitest-attachments/4062b079035cf9613a94c981f8de2d1e2e409165.png delete mode 100644 tests/.vitest-attachments/a09f1c2c11fad3416f5b9251115f2ae2cb760757.png delete mode 100644 tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode---hello-world------hello-universe--1.png delete mode 100644 tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-add-bold-chromium-darwin.png delete mode 100644 tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-add-italic-to-bold-chromium-darwin.png delete mode 100644 tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-remove-bold-chromium-darwin.png delete mode 100644 tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-universe-chromium-darwin.png create mode 100644 tests/src/browser/y-prosemirror/__screenshots__/basicText.concurrent.test.tsx/concurrent-bold-vs-italic-chromium-darwin.png create mode 100644 tests/src/browser/y-prosemirror/__screenshots__/basicText.concurrent.test.tsx/concurrent-typo-fix-vs-delete-chromium-darwin.png create mode 100644 tests/src/browser/y-prosemirror/__screenshots__/basicText.test.tsx/suggestion-mode-add-bold-chromium-darwin.png create mode 100644 tests/src/browser/y-prosemirror/__screenshots__/basicText.test.tsx/suggestion-mode-add-italic-to-bold-chromium-darwin.png create mode 100644 tests/src/browser/y-prosemirror/__screenshots__/basicText.test.tsx/suggestion-mode-remove-bold-chromium-darwin.png create mode 100644 tests/src/browser/y-prosemirror/__screenshots__/basicText.test.tsx/suggestion-mode-universe-chromium-darwin.png create mode 100644 tests/src/browser/y-prosemirror/basicText.concurrent.test.tsx create mode 100644 tests/src/browser/y-prosemirror/fixtures/concurrentSuggestionFixture.tsx diff --git a/tests/.vitest-attachments/4062b079035cf9613a94c981f8de2d1e2e409165.png b/tests/.vitest-attachments/4062b079035cf9613a94c981f8de2d1e2e409165.png deleted file mode 100644 index 16ddf2305c941713d05d0aea69dbcbdcb4175aee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2689 zcmb`JdpOf?AIEk0sidMbqdA0-A2D-^**dIj)Kq?Ca!kw6rbcp{kTPteBIi{OsWgS= zkh5uWHW48tMM;h1%yaK~p6mJN`RBQw>-W8`@Avv%*L`1~@ArP+@AvC-(@)_own@lK zh>3}9!(lOJ#KhK}1n-uc#laIDA>Jq^CV|Ca4*wOf?t5(*>Fw+WzVOSJ&}&nawyp%z zG{iRY_ zw#?qw@d~qbFntDJvOF-iv``l+_wYAvF@iQ1gv{qv?^RLZKrtAeVPtf4=|w4N>35PU zDj!o1V=#`0=;&x~acSu*t;%4M-2X9Yutqq__w@4mXa_^n(sOgwVOOtSecRJR)iCuN zml_J4Q&=k|88mffS(GJgl|68I{-+ye)dgA}-qd8-mVU7QSzdnrrc9K?^LJP>a2`+vG`jyu6MLXDr!h={&~%*Dnzb& zsGZTvWe^GWNm2D&GVC=JM#n^8RKnpmvzg58AtgYk&%&|ogM#cz_&dCt9JR{RgT>z_k&tQf?C6Q_!%3Ui6$;N4(?b+jY6+2-`P_Z zvY=0^X78c%M>not$I*bqCZwjuG=6WiGVbi?sIgEA#kjkxt}M-YG{o$_aPi{D`H4qt z`&a%t3IGlOQN-+*eqAQ#%~4HcszbPQ^Cw%N+OEna&-(CDkQlbwG?OQ-J|DPBlhI!)4*@fN_r~-5tTu+D*IvE5h3%Je@_is z(P*f%9LJG&E@2I)U0hi2zKzv{Z$Pds&)leYgXlQ$Htaywq#yK+=8uN_tle~}_j&ju zHcj5RZba|Fsc2y2i>;{*wUL|Tj43du-LkSa&dzuoZc7qTIMa^q>g#(`zXRO@Y~5EC zYUbUOzgJZ?a`4r)q5$D}?xns6{%CEdKybv?cDIg>PC7F1c3T@BR0hWUV=E;7-aY;v zpNhl5!Dh|(bopE|7}+2$>Dv9owqk8{DGmycuz7@k;WLDyQmH0Ezcl+xd^}oH4KG}{ z@H}{q0?~DDNzys@b0H*p;DF>H&&JyPSbL_qaA^g(@afLm?(Pv2|4ArN)?>WmLGjC% zodquF;|%$R7w-Jh6yQq)u}MZBfq=h0WpCdIG&vZ$XyD{j5ddr~51h3yKguGMP!z%s zT~{;@dQKTMHKGh)O<*SBH84Ebp%8aT zIXSsF1+%2`a#M~W^%E9mXlQ5xwrRh%HV;~#X&#(x;JFhN2Q^;D$<=p1J?lv#DNp3w zbFTX`Gtyh;s{thY%g(OQtzJAeHPs|+70C{n-r^FrVpthG_qi@=D;WGZ8-%m68h!iN zhIbx*oC$j~*;jN0+_!Ld)TK9XBzb$gwaw_-6mdMUZUmN+S;lGRysSJJBOQIS~1br*H1Pg%PZ05 z_kzEH9i-mq>9K2SYQmZZPOCjVTacTdFDXi$$nuGaiOBik$P;W-28h&u)srsE2$=XR zzD=Gt%0jpG`&73M!ATv;DhZpNj;TmbPfwR-R#YHq^CO#R%R-A>e1@eq#C<}LV_;;Y z?>}h|n;Y=m@ue+8H392NfB+~Tv9p6PfhjtAd$DJHeGw&3N=uX9IR`U9mBruRn=9u& z#JcmMrB7rcQ|P-is5<@JvJ@cnPC8~>YbU+K*5BV~Y-~(~QlF}Yo5x}y!i6tv#ChIk zb?eL)9(@=1VY%|M(^sq7B61+#2KeI+pvf`tn{{81)A)+Parazka(q=5@(6`;py*=j z!?3kgkvBl-eV@j}#E8heOZ|lMZ1QC8Pk4JtWpUG6urlUA+7;zy1S*G4W2`AQ}n=gJuV=t!8( zKU=A#0MU+&9Qwg$073`^0=?J6(Z{nU-@lB_4nX3SrG|sfzkoh|{8$Y@x$BX&5{9zL z`BQb!+*IojkpQW6jSq7GG%T|lstPT$*%*14(_P}@f|>Gj{>1z_p2ebSSXo3y}>v*2ube(X3L94*Cg$M6`=5!Z-+0OzI=)c^nh diff --git a/tests/.vitest-attachments/a09f1c2c11fad3416f5b9251115f2ae2cb760757.png b/tests/.vitest-attachments/a09f1c2c11fad3416f5b9251115f2ae2cb760757.png deleted file mode 100644 index 8f82524878e2f0ad14a0266363a92801b73349eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4313 zcmeI0`BRfg9>#I3)twoUo%LWH1$NgG5rm8)1dyXqg+B8S{^8%P3%aEE{aLI_E=**{@^UAnsJt@rJ>`t5$cpXceS z_sKslBlN%d{u@0#J^hQnUbv#Cw+pGK_f_8?zXDHk?laTDWmn`C#7}yRo?~-*dVfm4 zc;QD>Y_3cg{Fr)M(zZf0MYe^e9d;|$Z?>hstwLRIA51jM39WIuZb|Ui{RdWXmdCYc zx4yId^Uy17z+lqh&Z?G6==N7dEhSfj@1&&IXQW%}4EOHwP*n5XSGy{EXNI`GN-1s+ zZ+vDP5oj(L8~gUJ*QG98y$Pr|vi*1Tkt_!q91=Y};ijFRfOx8+f`UoNPcTiG$&2xe z*Q{A)6_k|BX_R8|@&kYFnBhL6}>oGEtMfm;#QZ~aw~oiqoTsd<}M0}?A~qQ*cZ-{{2~|}JQq4z6_M3; zEV9cD#*G^G3CfH8{6x~0>d%grH9n9xE--LVCs?y~x!E9z{`^cpYgSM`esiwaKX&q= zH1(*oB%i!Fmz|w`#5%B?J<*b6=#Q6DU;aY*-sbM$3@u(1P8u%vv9Yl+^u5<~BGdQH zji&own)HLF#V^xwT2L(@eIbuWrp2w}r_vCK^lOb#a~ms?w&Wv<&q=V$+2?{go}Hpl zDDlyCx6cJDU_WTD(#Mx^?szRA#Z!!z7lrR`ePfq4LfG}I|h^N!4d!5ybT%}0e?*^k7J8fp5!-BC`%T#|> zs83v+rmx`V%+o&o0>O22>h{|LfdFW|tX0cR;9=uQFy2O+N&K@*g~P+c?4`z#cmMf% zpa{vHFD(NEc^+&r4V)IgNXL=ieSI>trrTD?j&ad$NcgJh9J<+bPaa!LR_6Oj2|s%cG~-)2LJ`m#8#8 zd+S#`(`di*#suB013sB$9kl(SH*dZGk?iJPrI(hLf>{jCFIF)_M#xHGjC7@#Mkb6f zFiG@SDb+i}5^al{9|V%$`|$Jxz_nA7>Y@dlLwwD;g;)7=2i)CED-2D$ndEtGD~c2Z3VTBSIg>*0@lYPdntZnqoa0F zvwbPT`T2P;`s0H=)EEQ1iGPJlS}~E`Hq8n8>e-j6Dd(5!Gcq##$*S7`rqGZOwlo7h z-IX;kI2bj6gj3sKn8wxI$R2w+N6`UU*xnt()?px~k-52ff`N&1jt(I&*jYLHUK5$%67#6)@Mv>NysYu%o_a)rJOq(GZ7aZw*WUS)rLByN~{=Oex8 z!QM)E;POP12@nU6L|Xq8R99P@Y~rE?R@<(T$3ZY<8;H2Ev9V6~&cl}1Q6LQtwt%8* zK&E8E{DHluD@pGke+OK$HqrDTMi5*KfM%N|f(+GlS@$}wygx8vikd{ zg8`xnJ5=hK>iDHS4Poqa(3MZth~kvOgoFfOa3+HhrqKCea5x;zInLBQ-|^=A$0>&{ zgPTupZcF98t;t7f2y635Ul9m};DI70lW7BoyVIPxBn~(%-gH`_c_e&Ym=NS>rDvHJ zv#w58nDt>`PCf%{xiDHC0BYX@+*VOocza`cQlnOiXWNgbI!1h?yJq?kf>jzFh#b+% zsTa-Lj_I^w?>lB!#W{a(;w@OBAD2Llk%P?v@SGG~*5K^ePT_``eDuKDrTiholGHMl#grYK}5 zQ9(mPLr`zN6ei2b&UVmV?Tg>iZu0}I-W(s&y#-rXRVwy_gHkiS53FqTX?qfVG%yD{ zEEEP8=do_FEbBy_s*L=gy~DZvV2-yXboP0^_F}_x}q6p+%km diff --git a/tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode---hello-world------hello-universe--1.png b/tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode---hello-world------hello-universe--1.png deleted file mode 100644 index 8f82524878e2f0ad14a0266363a92801b73349eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4313 zcmeI0`BRfg9>#I3)twoUo%LWH1$NgG5rm8)1dyXqg+B8S{^8%P3%aEE{aLI_E=**{@^UAnsJt@rJ>`t5$cpXceS z_sKslBlN%d{u@0#J^hQnUbv#Cw+pGK_f_8?zXDHk?laTDWmn`C#7}yRo?~-*dVfm4 zc;QD>Y_3cg{Fr)M(zZf0MYe^e9d;|$Z?>hstwLRIA51jM39WIuZb|Ui{RdWXmdCYc zx4yId^Uy17z+lqh&Z?G6==N7dEhSfj@1&&IXQW%}4EOHwP*n5XSGy{EXNI`GN-1s+ zZ+vDP5oj(L8~gUJ*QG98y$Pr|vi*1Tkt_!q91=Y};ijFRfOx8+f`UoNPcTiG$&2xe z*Q{A)6_k|BX_R8|@&kYFnBhL6}>oGEtMfm;#QZ~aw~oiqoTsd<}M0}?A~qQ*cZ-{{2~|}JQq4z6_M3; zEV9cD#*G^G3CfH8{6x~0>d%grH9n9xE--LVCs?y~x!E9z{`^cpYgSM`esiwaKX&q= zH1(*oB%i!Fmz|w`#5%B?J<*b6=#Q6DU;aY*-sbM$3@u(1P8u%vv9Yl+^u5<~BGdQH zji&own)HLF#V^xwT2L(@eIbuWrp2w}r_vCK^lOb#a~ms?w&Wv<&q=V$+2?{go}Hpl zDDlyCx6cJDU_WTD(#Mx^?szRA#Z!!z7lrR`ePfq4LfG}I|h^N!4d!5ybT%}0e?*^k7J8fp5!-BC`%T#|> zs83v+rmx`V%+o&o0>O22>h{|LfdFW|tX0cR;9=uQFy2O+N&K@*g~P+c?4`z#cmMf% zpa{vHFD(NEc^+&r4V)IgNXL=ieSI>trrTD?j&ad$NcgJh9J<+bPaa!LR_6Oj2|s%cG~-)2LJ`m#8#8 zd+S#`(`di*#suB013sB$9kl(SH*dZGk?iJPrI(hLf>{jCFIF)_M#xHGjC7@#Mkb6f zFiG@SDb+i}5^al{9|V%$`|$Jxz_nA7>Y@dlLwwD;g;)7=2i)CED-2D$ndEtGD~c2Z3VTBSIg>*0@lYPdntZnqoa0F zvwbPT`T2P;`s0H=)EEQ1iGPJlS}~E`Hq8n8>e-j6Dd(5!Gcq##$*S7`rqGZOwlo7h z-IX;kI2bj6gj3sKn8wxI$R2w+N6`UU*xnt()?px~k-52ff`N&1jt(I&*jYLHUK5$%67#6)@Mv>NysYu%o_a)rJOq(GZ7aZw*WUS)rLByN~{=Oex8 z!QM)E;POP12@nU6L|Xq8R99P@Y~rE?R@<(T$3ZY<8;H2Ev9V6~&cl}1Q6LQtwt%8* zK&E8E{DHluD@pGke+OK$HqrDTMi5*KfM%N|f(+GlS@$}wygx8vikd{ zg8`xnJ5=hK>iDHS4Poqa(3MZth~kvOgoFfOa3+HhrqKCea5x;zInLBQ-|^=A$0>&{ zgPTupZcF98t;t7f2y635Ul9m};DI70lW7BoyVIPxBn~(%-gH`_c_e&Ym=NS>rDvHJ zv#w58nDt>`PCf%{xiDHC0BYX@+*VOocza`cQlnOiXWNgbI!1h?yJq?kf>jzFh#b+% zsTa-Lj_I^w?>lB!#W{a(;w@OBAD2Llk%P?v@SGG~*5K^ePT_``eDuKDrTiholGHMl#grYK}5 zQ9(mPLr`zN6ei2b&UVmV?Tg>iZu0}I-W(s&y#-rXRVwy_gHkiS53FqTX?qfVG%yD{ zEEEP8=do_FEbBy_s*L=gy~DZvV2-yXboP0^_F}_x}q6p+%km diff --git a/tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-add-bold-chromium-darwin.png b/tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-add-bold-chromium-darwin.png deleted file mode 100644 index f645bc8552003c15a3a19e848faecbe5d2888736..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1639 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 Gm;M56C1MW% diff --git a/tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-remove-bold-chromium-darwin.png b/tests/src/browser/__screenshots__/suggestionMode.test.tsx/suggestion-mode-remove-bold-chromium-darwin.png deleted file mode 100644 index 1ce6528cf3c1391e67ceb0fb28589d3a0cfcee9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1592 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 diff --git a/tests/src/browser/y-prosemirror/__screenshots__/basicText.concurrent.test.tsx/concurrent-bold-vs-italic-chromium-darwin.png b/tests/src/browser/y-prosemirror/__screenshots__/basicText.concurrent.test.tsx/concurrent-bold-vs-italic-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..73757b062304306089b37e2115289d72cb5c31bd GIT binary patch literal 10914 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 soZx@&J-+{eR|Ws~7XKfwZ7y*#stmn~bi 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 literal 0 HcmV?d00001 diff --git a/tests/src/browser/y-prosemirror/__screenshots__/basicText.test.tsx/suggestion-mode-add-bold-chromium-darwin.png b/tests/src/browser/y-prosemirror/__screenshots__/basicText.test.tsx/suggestion-mode-add-bold-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..b410bde1ed54d7e1751ee8e13d53be6ea8d18d58 GIT binary patch literal 7086 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 literal 0 HcmV?d00001 diff --git a/tests/src/browser/y-prosemirror/__screenshots__/basicText.test.tsx/suggestion-mode-remove-bold-chromium-darwin.png b/tests/src/browser/y-prosemirror/__screenshots__/basicText.test.tsx/suggestion-mode-remove-bold-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..b81e90f85231548d8f9ab0e8bae5fb3b395c2987 GIT binary patch literal 7355 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 literal 0 HcmV?d00001 diff --git a/tests/src/browser/y-prosemirror/__screenshots__/basicText.test.tsx/suggestion-mode-universe-chromium-darwin.png b/tests/src/browser/y-prosemirror/__screenshots__/basicText.test.tsx/suggestion-mode-universe-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..1082952788ee45729f5ac83bc0acda7c520925e8 GIT binary patch literal 8884 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 literal 0 HcmV?d00001 diff --git a/tests/src/browser/y-prosemirror/basicText.concurrent.test.tsx b/tests/src/browser/y-prosemirror/basicText.concurrent.test.tsx new file mode 100644 index 0000000000..e51227a903 --- /dev/null +++ b/tests/src/browser/y-prosemirror/basicText.concurrent.test.tsx @@ -0,0 +1,250 @@ +/* eslint-disable testing-library/render-result-naming-convention */ +/** + * Vitest browser-mode tests for two-user concurrent suggestion edits. + * Each test sets up three side-by-side editors (User A, User B, + * Merged) backed by `baseDoc` + `suggestionDocA`/`B`/`Merged`, applies + * independent suggestion edits from A and B, calls `sync()` to fan + * both updates into the merged doc, and snapshots the converged state. + * + * TODO: BlockNote's `mapAttributionToMark` (YSync.ts) hashes user IDs + * from the attribution data to pick a color from a fixed palette, but + * `Y.Attributions()` ships empty and nothing in the editor pipeline + * populates it from the editor's `user` / awareness. Result: every + * mark in every test renders as `userColorPalette[0]` (#30bced), + * regardless of which user actually made the edit. In the merged + * snapshots below we therefore cannot tell A's marks from B's. Decide + * whether the attribution layer should automatically tag writes with + * the local awareness user, or whether tests should construct an + * `Attributions` instance with pre-registered client-id → user-id + * mappings. + */ +import { expect, test } from "vitest"; + +import { setupConcurrentSuggestionTest } from "./fixtures/concurrentSuggestionFixture.js"; +import { + editorHtml, + waitForSuggestion, + ydocXml, +} from "./fixtures/suggestionFixture.js"; + +// Concurrent text edits on overlapping range: A fixes a typo while B +// deletes the whole word. After CRDT merge, snapshot what the merged +// editor ends up displaying. +test("concurrent: A fixes typo, B deletes the word", async () => { + const { + userA, + userB, + merged, + baseDoc, + suggestionDocA, + suggestionDocB, + suggestionDocMerged, + screen, + seed, + enableSuggestions, + sync, + } = await setupConcurrentSuggestionTest({ + userAAction: "fix typo", + userBAction: "delete word", + }); + + // Seed: A writes "hello wrold" (typo) directly to baseDoc since + // suggestion mode isn't on yet. Then `seed()` fans baseDoc into + // all three suggestion docs so everyone starts from the same state. + userA.editor.replaceBlocks(userA.editor.document, [ + { id: "block-hello", type: "paragraph", content: "hello wrold" }, + ]); + seed(); + + await expect + .element(screen.getByTestId(userA.testId).getByText("hello wrold")) + .toBeVisible(); + + // Switch all editors into suggestion mode (subsequent edits in A + // and B are recorded as suggestions, merged starts watching its + // suggestion doc for incoming updates). + enableSuggestions(); + + // A: fix typo "wrold" -> "world". + const [blockA] = userA.editor.document; + userA.editor.updateBlock(blockA, { + type: "paragraph", + content: "hello world", + }); + + // B: delete the misspelled word entirely. + const [blockB] = userB.editor.document; + userB.editor.updateBlock(blockB, { type: "paragraph", content: "hello " }); + + await waitForSuggestion(userA.editor); + await waitForSuggestion(userB.editor); + + // Merge A's and B's suggestions into the merged doc. + sync(); + await waitForSuggestion(merged.editor); + + await expect(screen.getByTestId("editor-root")).toMatchScreenshot( + "concurrent-typo-fix-vs-delete", + ); + + // TODO: the merged YDoc ends up at "hello o" – an `o` survives even + // though both A (who replaced "wrold" with "world") and B (who + // deleted "wrold" outright) effectively wanted "wrold" gone. The + // CRDT keeps A's inserted `o` because B's delete-range covered the + // original "wrold" letters but not A's freshly-inserted characters, + // so the union of "delete everything B saw" + "keep what A added" + // leaves a stray `o`. Worth deciding whether this is the desired + // merge semantic for the product or whether the suggestion layer + // should resolve overlapping edits differently. + expect(ydocXml(baseDoc)).toMatchInlineSnapshot(` + " + + hello wrold + + " + `); + expect(ydocXml(suggestionDocA)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(ydocXml(suggestionDocB)).toMatchInlineSnapshot(` + " + + hello + + " + `); + expect(ydocXml(suggestionDocMerged)).toMatchInlineSnapshot(` + " + + hello o + + " + `); + expect(editorHtml(merged.editor)).toMatchInlineSnapshot(` + " + + + + hello + w + o + rold + + + + " + `); +}); + +// Concurrent format edits on the same word: A adds bold, B adds +// italic. After CRDT merge, both marks should land on "world". +test("concurrent: A bolds the word, B italicises the word", async () => { + const { + userA, + userB, + merged, + baseDoc, + suggestionDocA, + suggestionDocB, + suggestionDocMerged, + screen, + seed, + enableSuggestions, + sync, + } = await setupConcurrentSuggestionTest({ + userAAction: "bold 'world'", + userBAction: "italicise 'world'", + }); + + // Seed: A writes plain "hello world" directly to baseDoc, then + // `seed()` fans it into all three suggestion docs. + userA.editor.replaceBlocks(userA.editor.document, [ + { id: "block-hello", type: "paragraph", content: "hello world" }, + ]); + seed(); + + await expect + .element(screen.getByTestId(userA.testId).getByText("hello world")) + .toBeVisible(); + + enableSuggestions(); + + // A: bold "world". + const [blockA] = userA.editor.document; + userA.editor.updateBlock(blockA, { + type: "paragraph", + content: [ + { type: "text", text: "hello ", styles: {} }, + { type: "text", text: "world", styles: { bold: true } }, + ], + }); + + // B: italic "world". + const [blockB] = userB.editor.document; + userB.editor.updateBlock(blockB, { + type: "paragraph", + content: [ + { type: "text", text: "hello ", styles: {} }, + { type: "text", text: "world", styles: { italic: true } }, + ], + }); + + await waitForSuggestion(userA.editor); + await waitForSuggestion(userB.editor); + + sync(); + await waitForSuggestion(merged.editor); + + await expect(screen.getByTestId("editor-root")).toMatchScreenshot( + "concurrent-bold-vs-italic", + ); + + expect(ydocXml(baseDoc)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(ydocXml(suggestionDocA)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(ydocXml(suggestionDocB)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(ydocXml(suggestionDocMerged)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(editorHtml(merged.editor)).toMatchInlineSnapshot(` + " + + + + hello + + + world + + + + + + " + `); +}); diff --git a/tests/src/browser/y-prosemirror/basicText.test.tsx b/tests/src/browser/y-prosemirror/basicText.test.tsx index a9ac29aa14..7b7afcc7d2 100644 --- a/tests/src/browser/y-prosemirror/basicText.test.tsx +++ b/tests/src/browser/y-prosemirror/basicText.test.tsx @@ -21,7 +21,7 @@ import { // is rendered as inline / spans around the changed letters. test("suggestion mode: 'hello world' -> 'hello universe'", async () => { const { editor, screen, baseDoc, suggestionDoc, sync } = - await setupSuggestionTest(); + await setupSuggestionTest({ userAction: "rename last word" }); // 1. Set the base doc to "hello world". The block id is pinned so the // snapshots stay deterministic. @@ -33,7 +33,9 @@ test("suggestion mode: 'hello world' -> 'hello universe'", async () => { // from the same state. sync(); - await expect.element(screen.getByText("hello world")).toBeVisible(); + await expect + .element(screen.getByTestId("editor-A").getByText("hello world")) + .toBeVisible(); // 3. Subsequent edits are recorded as suggestions instead of mutating // the doc directly. @@ -47,7 +49,9 @@ test("suggestion mode: 'hello world' -> 'hello universe'", async () => { // re-render on the next frame; without this the screenshot can race // the update). "unive" only exists once "world" -> "universe" has // been split into / spans, so this is a precise sentinel. - await expect.element(screen.getByText("unive")).toBeVisible(); + await expect + .element(screen.getByTestId("editor-A").getByText("unive")) + .toBeVisible(); // 5a. Visual snapshot of the rendered editor. await expect(screen.getByTestId("editor-root")).toMatchScreenshot( @@ -103,14 +107,16 @@ test("suggestion mode: 'hello world' -> 'hello universe'", async () => { // changes (currently `y-attributed-format`, with no visual marker). test("suggestion mode: add bold to 'world'", async () => { const { editor, screen, baseDoc, suggestionDoc, sync } = - await setupSuggestionTest(); + await setupSuggestionTest({ userAction: "bold 'world'" }); // Base: plain "hello world". editor.replaceBlocks(editor.document, [ { id: "block-hello", type: "paragraph", content: "hello world" }, ]); sync(); - await expect.element(screen.getByText("hello world")).toBeVisible(); + await expect + .element(screen.getByTestId("editor-A").getByText("hello world")) + .toBeVisible(); editor.getExtension(SuggestionsExtension)!.enableSuggestions(); @@ -173,7 +179,7 @@ test("suggestion mode: add bold to 'world'", async () => { // removal is handled symmetrically. test("suggestion mode: remove bold from 'world'", async () => { const { editor, screen, baseDoc, suggestionDoc, sync } = - await setupSuggestionTest(); + await setupSuggestionTest({ userAction: "unbold 'world'" }); // Base: "hello " + bold "world". editor.replaceBlocks(editor.document, [ @@ -187,7 +193,11 @@ test("suggestion mode: remove bold from 'world'", async () => { }, ]); sync(); - await expect.element(screen.getByText("world")).toBeVisible(); + // Use the full paragraph text – the User A column heading also + // contains the word "world", which would clash with getByText. + await expect + .element(screen.getByTestId("editor-A").getByText("hello world")) + .toBeVisible(); editor.getExtension(SuggestionsExtension)!.enableSuggestions(); @@ -245,7 +255,7 @@ test("suggestion mode: remove bold from 'world'", async () => { // is recorded only for the new mark, not the pre-existing one. test("suggestion mode: add italic to already-bold 'world'", async () => { const { editor, screen, baseDoc, suggestionDoc, sync } = - await setupSuggestionTest(); + await setupSuggestionTest({ userAction: "italic on top of bold" }); // Base: "hello " + bold "world". editor.replaceBlocks(editor.document, [ @@ -259,7 +269,9 @@ test("suggestion mode: add italic to already-bold 'world'", async () => { }, ]); sync(); - await expect.element(screen.getByText("world")).toBeVisible(); + await expect + .element(screen.getByTestId("editor-A").getByText("hello world")) + .toBeVisible(); editor.getExtension(SuggestionsExtension)!.enableSuggestions(); diff --git a/tests/src/browser/y-prosemirror/fixtures/concurrentSuggestionFixture.tsx b/tests/src/browser/y-prosemirror/fixtures/concurrentSuggestionFixture.tsx new file mode 100644 index 0000000000..0b81bc775e --- /dev/null +++ b/tests/src/browser/y-prosemirror/fixtures/concurrentSuggestionFixture.tsx @@ -0,0 +1,242 @@ +/* eslint-disable testing-library/render-result-naming-convention */ +/** + * Fixture for two-user concurrent suggestion tests. + * + * Layout: + * ┌──────┬─────────────────────┬─────────────────────┬────────┐ + * │ Base │ User A: │ User B: >; + /** + * Replay `baseDoc` into all three suggestion docs. Call after + * seeding the initial content via A's editor (with suggestion mode + * off – writes go straight to `baseDoc`) so all four docs start aligned. + */ + seed: () => void; + /** + * Switch all three editors into suggestion mode. Call after `seed()` + * – subsequent edits in A and B are recorded as suggestions, and the + * merged editor starts observing `suggestionDocMerged` for updates. + */ + enableSuggestions: () => void; + /** Fan A's and B's suggestion updates into `suggestionDocMerged`. */ + sync: () => void; +} + +const USER_A = { name: "User A", color: "#30bced" }; +const USER_B = { name: "User B", color: "#ee6352" }; +const USER_MERGED = { name: "Merged", color: "#888888" }; +const USER_BASE = { name: "Base", color: "#888888" }; + +export interface ConcurrentSuggestionFixtureOptions { + /** 1-5 word description of what User A does (rendered as column heading). */ + userAAction: string; + /** 1-5 word description of what User B does (rendered as column heading). */ + userBAction: string; +} + +export async function setupConcurrentSuggestionTest({ + userAAction, + userBAction, +}: ConcurrentSuggestionFixtureOptions): Promise { + const baseDoc = new Y.Doc(); + const suggestionDocA = new Y.Doc({ isSuggestionDoc: true }); + const suggestionDocB = new Y.Doc({ isSuggestionDoc: true }); + const suggestionDocMerged = new Y.Doc({ isSuggestionDoc: true }); + + const managerA = Y.createAttributionManagerFromDiff( + baseDoc, + suggestionDocA, + { attrs: new Y.Attributions() }, + ); + managerA.suggestionMode = true; + + const managerB = Y.createAttributionManagerFromDiff( + baseDoc, + suggestionDocB, + { attrs: new Y.Attributions() }, + ); + managerB.suggestionMode = true; + + // Merged is a viewer – it shows both users' suggestions but doesn't + // record new ones, so `suggestionMode = false`. + const managerMerged = Y.createAttributionManagerFromDiff( + baseDoc, + suggestionDocMerged, + { attrs: new Y.Attributions() }, + ); + managerMerged.suggestionMode = false; + + const awarenessA = makeAwareness(baseDoc, USER_A); + const awarenessB = makeAwareness(baseDoc, USER_B); + const awarenessMerged = makeAwareness(baseDoc, USER_MERGED); + + let editorBase!: BlockNoteEditor; + let editorA!: BlockNoteEditor; + let editorB!: BlockNoteEditor; + let editorMerged!: BlockNoteEditor; + + function Editors() { + editorBase = useCreateBlockNote( + withCollaboration({ + collaboration: { + fragment: baseDoc.get("doc"), + provider: { awareness: new Awareness(baseDoc) }, + user: USER_BASE, + }, + }), + ); + editorA = useCreateBlockNote( + withCollaboration({ + collaboration: { + fragment: baseDoc.get("doc"), + provider: { awareness: awarenessA }, + suggestionDoc: suggestionDocA, + attributionManager: managerA, + user: USER_A, + }, + }), + ); + editorB = useCreateBlockNote( + withCollaboration({ + collaboration: { + fragment: baseDoc.get("doc"), + provider: { awareness: awarenessB }, + suggestionDoc: suggestionDocB, + attributionManager: managerB, + user: USER_B, + }, + }), + ); + editorMerged = useCreateBlockNote( + withCollaboration({ + collaboration: { + fragment: baseDoc.get("doc"), + provider: { awareness: awarenessMerged }, + suggestionDoc: suggestionDocMerged, + attributionManager: managerMerged, + user: USER_MERGED, + }, + }), + ); + + return ( +
+
+ Base + +
+
+ User A: {userAAction} + +
+
+ User B: {userBAction} + +
+
+ Merged + +
+
+ ); + } + + // Four columns at 1fr each need a wider viewport so the rightmost + // column doesn't clip BlockNote content. + await page.viewport(1800, 800); + + const screen = await render(); + + return { + userA: { editor: editorA, testId: "editor-A" }, + userB: { editor: editorB, testId: "editor-B" }, + merged: { editor: editorMerged, testId: "editor-merged" }, + baseDoc, + suggestionDocA, + suggestionDocB, + suggestionDocMerged, + screen, + seed: () => { + const update = Y.encodeStateAsUpdate(baseDoc); + Y.applyUpdate(suggestionDocA, update); + Y.applyUpdate(suggestionDocB, update); + Y.applyUpdate(suggestionDocMerged, update); + }, + enableSuggestions: () => { + editorA.getExtension(SuggestionsExtension)!.enableSuggestions(); + editorB.getExtension(SuggestionsExtension)!.enableSuggestions(); + editorMerged.getExtension(SuggestionsExtension)!.enableSuggestions(); + }, + sync: () => { + Y.applyUpdate(suggestionDocMerged, Y.encodeStateAsUpdate(suggestionDocA)); + Y.applyUpdate(suggestionDocMerged, Y.encodeStateAsUpdate(suggestionDocB)); + }, + }; +} + +function makeAwareness( + doc: Y.Doc, + user: { name: string; color: string }, +): Awareness { + const a = new Awareness(doc); + a.setLocalStateField("user", user); + return a; +} diff --git a/tests/src/browser/y-prosemirror/fixtures/suggestionFixture.tsx b/tests/src/browser/y-prosemirror/fixtures/suggestionFixture.tsx index e32da7623a..86ae919865 100644 --- a/tests/src/browser/y-prosemirror/fixtures/suggestionFixture.tsx +++ b/tests/src/browser/y-prosemirror/fixtures/suggestionFixture.tsx @@ -2,8 +2,18 @@ /** * Shared fixture for browser-mode suggestion tests. * - * Mounts a single BlockNote editor bound to an in-memory `baseDoc`, - * with a second in-memory `suggestionDoc` set up as the diff target. + * Layout: + * ┌──────────┬──────────────────────┐ + * │ Base │ User A: │ + * └──────────┴──────────────────────┘ + * + * - `Base` is a read-only editor bound to `baseDoc` – it shows the + * pre-suggestion state and is visible in the screenshot so the + * reviewer can see the "before" without leaving the file. + * - `User A` is the suggesting editor. Its column heading includes a + * short caller-supplied action description so the screenshot is + * self-explanatory. + * * The provider/yhub round-trip is replaced by a manual `sync()`. */ import "@blocknote/core/fonts/inter.css"; @@ -18,9 +28,11 @@ import { Awareness } from "@y/protocols/awareness"; import * as Y from "@y/y"; import { prettify } from "htmlfy"; import { expect } from "vitest"; +import { page } from "vitest/browser"; import { render } from "vitest-browser-react"; export interface SuggestionFixture { + /** User A's editor – this is the one the test makes suggestions through. */ editor: BlockNoteEditor; screen: Awaited>; baseDoc: Y.Doc; @@ -29,11 +41,22 @@ export interface SuggestionFixture { sync: () => void; } -export async function setupSuggestionTest(): Promise { +export interface SuggestionFixtureOptions { + /** + * 1-5 word description of what User A does (e.g. "fix typo", + * "bold world"). Rendered in the User A column heading so the + * screenshot is self-explanatory. + */ + userAction: string; +} + +export async function setupSuggestionTest({ + userAction, +}: SuggestionFixtureOptions): Promise { const baseDoc = new Y.Doc(); const baseAwareness = new Awareness(baseDoc); baseAwareness.setLocalStateField("user", { - name: "Author", + name: "User A", color: "#30bced", }); @@ -45,30 +68,58 @@ export async function setupSuggestionTest(): Promise { ); attributionManager.suggestionMode = true; - let editor!: BlockNoteEditor; - function Editor() { - editor = useCreateBlockNote( + let editorA!: BlockNoteEditor; + let editorBase!: BlockNoteEditor; + + function Editors() { + editorA = useCreateBlockNote( withCollaboration({ collaboration: { fragment: baseDoc.get("doc"), provider: { awareness: baseAwareness }, suggestionDoc, attributionManager, - user: { name: "Author", color: "#30bced" }, + user: { name: "User A", color: "#30bced" }, + }, + }), + ); + editorBase = useCreateBlockNote( + withCollaboration({ + collaboration: { + fragment: baseDoc.get("doc"), + provider: { awareness: new Awareness(baseDoc) }, + user: { name: "Base", color: "#888888" }, }, }), ); return ( -
- +
+
+ Base + +
+
+ User A: {userAction} + +
); } - const screen = await render(); + await page.viewport(1200, 800); + + const screen = await render(); return { - editor, + editor: editorA, screen, baseDoc, suggestionDoc, From 9fae8f9adb856effc28861e477ae5a0e88a90938 Mon Sep 17 00:00:00 2001 From: yousefed Date: Thu, 28 May 2026 20:24:47 +0200 Subject: [PATCH 3/4] propchange tests --- ...ge-image-source-actual-chromium-darwin.png | Bin 0 -> 9701 bytes tests/src/browser/vitestSetup.ts | 13 + ...lor-vs-backgroundColor-chromium-darwin.png | Bin 0 -> 10822 bytes ...p-change-heading-level-chromium-darwin.png | Bin 0 -> 17756 bytes ...op-change-image-source-chromium-darwin.png | Bin 0 -> 11369 bytes ...rop-change-image-width-chromium-darwin.png | Bin 0 -> 12257 bytes ...-change-text-alignment-chromium-darwin.png | Bin 0 -> 7234 bytes .../fixtures/suggestionFixture.tsx | 13 +- .../propChanges.concurrent.test.tsx | 123 ++++++ .../y-prosemirror/propChanges.test.tsx | 390 ++++++++++++++++++ tests/vite.config.browser.ts | 1 + 11 files changed, 539 insertions(+), 1 deletion(-) create mode 100644 tests/.vitest-attachments/src/browser/y-prosemirror/propChanges.test.tsx/prop-change-image-source-actual-chromium-darwin.png create mode 100644 tests/src/browser/vitestSetup.ts create mode 100644 tests/src/browser/y-prosemirror/__screenshots__/propChanges.concurrent.test.tsx/concurrent-textColor-vs-backgroundColor-chromium-darwin.png create mode 100644 tests/src/browser/y-prosemirror/__screenshots__/propChanges.test.tsx/prop-change-heading-level-chromium-darwin.png create mode 100644 tests/src/browser/y-prosemirror/__screenshots__/propChanges.test.tsx/prop-change-image-source-chromium-darwin.png create mode 100644 tests/src/browser/y-prosemirror/__screenshots__/propChanges.test.tsx/prop-change-image-width-chromium-darwin.png create mode 100644 tests/src/browser/y-prosemirror/__screenshots__/propChanges.test.tsx/prop-change-text-alignment-chromium-darwin.png create mode 100644 tests/src/browser/y-prosemirror/propChanges.concurrent.test.tsx create mode 100644 tests/src/browser/y-prosemirror/propChanges.test.tsx diff --git a/tests/.vitest-attachments/src/browser/y-prosemirror/propChanges.test.tsx/prop-change-image-source-actual-chromium-darwin.png b/tests/.vitest-attachments/src/browser/y-prosemirror/propChanges.test.tsx/prop-change-image-source-actual-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..37b96b3b1d5cb25f254a544982430fed0fff387d GIT binary patch literal 9701 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 { + (window as Window & { __TEST_OPTIONS?: any }).__TEST_OPTIONS = {}; +}); + +afterEach(() => { + delete (window as Window & { __TEST_OPTIONS?: any }).__TEST_OPTIONS; +}); diff --git a/tests/src/browser/y-prosemirror/__screenshots__/propChanges.concurrent.test.tsx/concurrent-textColor-vs-backgroundColor-chromium-darwin.png b/tests/src/browser/y-prosemirror/__screenshots__/propChanges.concurrent.test.tsx/concurrent-textColor-vs-backgroundColor-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..b9cbae43a56768141c0f461baff9a0f6f18544cf GIT binary patch literal 10822 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 NdCcf&*5NO|{tx9uF7E&U literal 0 HcmV?d00001 diff --git a/tests/src/browser/y-prosemirror/__screenshots__/propChanges.test.tsx/prop-change-image-width-chromium-darwin.png b/tests/src/browser/y-prosemirror/__screenshots__/propChanges.test.tsx/prop-change-image-width-chromium-darwin.png new file mode 100644 index 0000000000000000000000000000000000000000..f8197139f182d5fae11da5b564f461b87c329684 GIT binary patch literal 12257 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 NRnk<<`or}0{{Z5dm_+~p literal 0 HcmV?d00001 diff --git a/tests/src/browser/y-prosemirror/fixtures/suggestionFixture.tsx b/tests/src/browser/y-prosemirror/fixtures/suggestionFixture.tsx index 86ae919865..e49bba8a1b 100644 --- a/tests/src/browser/y-prosemirror/fixtures/suggestionFixture.tsx +++ b/tests/src/browser/y-prosemirror/fixtures/suggestionFixture.tsx @@ -147,7 +147,18 @@ export async function waitForSuggestion( /** Pretty-print a Y.Doc's `doc` XmlFragment for an inline snapshot. */ export function ydocXml(doc: Y.Doc): string { - return prettify(doc.get("doc").toString(), { tag_wrap: true }); + // `Y.XmlFragment.toString()` emits HTML5-style unquoted attributes + // for non-string values (e.g. `level=1`, `isToggleable=false`). + // htmlfy mangles those, so we wrap each unquoted value in quotes + // before pretty-printing. + const raw = doc + .get("doc") + .toString() + .replace( + /(\w[\w-]*)=([^"'\s>]+)/g, + (_, name, value) => `${name}="${value}"`, + ); + return prettify(raw, { tag_wrap: true }); } /** diff --git a/tests/src/browser/y-prosemirror/propChanges.concurrent.test.tsx b/tests/src/browser/y-prosemirror/propChanges.concurrent.test.tsx new file mode 100644 index 0000000000..2a7d2fb9b0 --- /dev/null +++ b/tests/src/browser/y-prosemirror/propChanges.concurrent.test.tsx @@ -0,0 +1,123 @@ +/* eslint-disable testing-library/render-result-naming-convention */ +/** + * Vitest browser-mode tests for two-user concurrent prop-change + * suggestions. Same shape as `basicText.concurrent.test.tsx` but the + * edits are block-level prop changes rather than content edits. + * + * See `propChanges.test.tsx` for the TODO on prop changes producing no + * `y-attributed-*` mark – the same applies here. + */ +import { expect, test } from "vitest"; + +import { setupConcurrentSuggestionTest } from "./fixtures/concurrentSuggestionFixture.js"; +import { + editorHtml, + ydocXml, +} from "./fixtures/suggestionFixture.js"; + +// Two users edit independent props on the same block: A changes +// `textColor`, B changes `backgroundColor`. Neither edit touches the +// other's prop, so the CRDT merge should preserve both. +test("concurrent: A changes textColor, B changes backgroundColor", async () => { + const { + userA, + userB, + merged, + baseDoc, + suggestionDocA, + suggestionDocB, + suggestionDocMerged, + screen, + seed, + enableSuggestions, + sync, + } = await setupConcurrentSuggestionTest({ + userAAction: "red text", + userBAction: "yellow background", + }); + + // Seed: plain "hello world" with default colors. + userA.editor.replaceBlocks(userA.editor.document, [ + { id: "block-hello", type: "paragraph", content: "hello world" }, + ]); + seed(); + await expect + .element(screen.getByTestId(userA.testId).getByText("hello world")) + .toBeVisible(); + + enableSuggestions(); + + // A: change textColor to red. + const [blockA] = userA.editor.document; + userA.editor.updateBlock(blockA, { + type: "paragraph", + props: { textColor: "red" }, + }); + + // B: change backgroundColor to yellow. + const [blockB] = userB.editor.document; + userB.editor.updateBlock(blockB, { + type: "paragraph", + props: { backgroundColor: "yellow" }, + }); + + // Prop changes don't generate y-attributed marks, so we poll on the + // individual editor doc states instead. + await expect + .poll(() => userA.editor.document[0]?.props?.textColor) + .toBe("red"); + await expect + .poll(() => userB.editor.document[0]?.props?.backgroundColor) + .toBe("yellow"); + + sync(); + + await expect + .poll(() => merged.editor.document[0]?.props?.textColor) + .toBe("red"); + await expect + .poll(() => merged.editor.document[0]?.props?.backgroundColor) + .toBe("yellow"); + + await expect(screen.getByTestId("editor-root")).toMatchScreenshot( + "concurrent-textColor-vs-backgroundColor", + ); + + expect(ydocXml(baseDoc)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(ydocXml(suggestionDocA)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(ydocXml(suggestionDocB)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(ydocXml(suggestionDocMerged)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(editorHtml(merged.editor)).toMatchInlineSnapshot(` + " + + + hello world + + + " + `); +}); diff --git a/tests/src/browser/y-prosemirror/propChanges.test.tsx b/tests/src/browser/y-prosemirror/propChanges.test.tsx new file mode 100644 index 0000000000..5d29b4a20f --- /dev/null +++ b/tests/src/browser/y-prosemirror/propChanges.test.tsx @@ -0,0 +1,390 @@ +/* eslint-disable testing-library/render-result-naming-convention */ +/** + * Vitest browser-mode tests for prop-change suggestions: block-level + * attribute edits (text alignment, heading level, image width / source, + * etc.) rather than content/text edits. Each test follows the same + * shape as `basicText.test.tsx`: seed, enable suggestions, edit, then + * screenshot + inline snapshots of base/suggestion docs + PM doc. + */ +import { SuggestionsExtension } from "@blocknote/core/y"; +import { expect, test } from "vitest"; + +import { + editorHtml, + setupSuggestionTest, + ydocXml, +} from "./fixtures/suggestionFixture.js"; + +// Tiny inline SVG data URLs – avoids a network fetch (placehold.co +// occasionally returns after the screenshot is taken). +const IMG_SRC_BASE = + "data:image/svg+xml;utf8,"; +const IMG_SRC_NEW = + "data:image/svg+xml;utf8,"; + +// TODO: block-level prop changes generate NO `y-attributed-*` mark in +// the editor's PM doc – the suggestion doc carries the new value but +// the editor shows it as if it were already accepted. Compare with the +// inline-format case in `basicText.test.tsx` which at least produces a +// `y-attributed-format` mark (still no visual style, but at least +// detectable from the data). Decide whether block-prop suggestions +// should also be wrapped in a `y-attributed-format` (or similar) so +// reviewers / accept-reject UI can target them. +// +// Block-level prop change: paragraph's `textAlignment` flips from +// "left" to "center". Text content is unchanged. +test("suggestion mode: change text alignment to center", async () => { + const { editor, screen, baseDoc, suggestionDoc, sync } = + await setupSuggestionTest({ userAction: "center align" }); + + editor.replaceBlocks(editor.document, [ + { id: "block-hello", type: "paragraph", content: "hello world" }, + ]); + sync(); + await expect + .element(screen.getByTestId("editor-A").getByText("hello world")) + .toBeVisible(); + + editor.getExtension(SuggestionsExtension)!.enableSuggestions(); + + const [block] = editor.document; + editor.updateBlock(block, { + type: "paragraph", + props: { textAlignment: "center" }, + }); + + // Prop changes don't generate `y-attributed-*` marks, so the + // `waitForSuggestion` helper used elsewhere is too narrow here. + // Poll on the editor's view of the prop instead. + await expect + .poll(() => editor.document[0]?.props?.textAlignment) + .toBe("center"); + + await expect(screen.getByTestId("editor-root")).toMatchScreenshot( + "prop-change-text-alignment", + ); + + expect(ydocXml(baseDoc)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(ydocXml(suggestionDoc)).toMatchInlineSnapshot(` + " + + hello world + + " + `); + expect(editorHtml(editor)).toMatchInlineSnapshot(` + " + + + hello world + + + " + `); +}); + +// Block-level prop change on a heading: bump `level` from 1 to 2. +// Same lack of attribution as the alignment case. +test("suggestion mode: change heading level from 1 to 2", async () => { + const { editor, screen, baseDoc, suggestionDoc, sync } = + await setupSuggestionTest({ userAction: "demote heading" }); + + editor.replaceBlocks(editor.document, [ + { + id: "block-hello", + type: "heading", + props: { level: 1 }, + content: "hello world", + }, + ]); + sync(); + await expect + .element(screen.getByTestId("editor-A").getByText("hello world")) + .toBeVisible(); + + editor.getExtension(SuggestionsExtension)!.enableSuggestions(); + + const [block] = editor.document; + editor.updateBlock(block, { + type: "heading", + props: { level: 2 }, + }); + + await expect.poll(() => editor.document[0]?.props?.level).toBe(2); + + await expect(screen.getByTestId("editor-root")).toMatchScreenshot( + "prop-change-heading-level", + ); + + expect(ydocXml(baseDoc)).toMatchInlineSnapshot(` + " + + hello world + + + + + " + `); + expect(ydocXml(suggestionDoc)).toMatchInlineSnapshot(` + " + + hello world + + + + + " + `); + expect(editorHtml(editor)).toMatchInlineSnapshot(` + " + + + hello world + + + + + + " + `); +}); + +// Image block prop change: `previewWidth`. Resizes the image, no +// content/text change. +test("suggestion mode: resize image (previewWidth)", async () => { + const { editor, screen, baseDoc, suggestionDoc, sync } = + await setupSuggestionTest({ userAction: "resize image" }); + + editor.replaceBlocks(editor.document, [ + { + id: "block-image", + type: "image", + props: { + url: IMG_SRC_BASE, + previewWidth: 200, + }, + }, + ]); + sync(); + // Default `alt=""` on the image makes it decorative, so + // `getByRole("img")` doesn't see it. Poll on the prop having + // landed in the editor instead. + await expect + .poll(() => editor.document[0]?.props?.url) + .toBe(IMG_SRC_BASE); + + editor.getExtension(SuggestionsExtension)!.enableSuggestions(); + + const [block] = editor.document; + editor.updateBlock(block, { + type: "image", + props: { previewWidth: 400 }, + }); + + await expect + .poll(() => editor.document[0]?.props?.previewWidth) + .toBe(400); + + await expect(screen.getByTestId("editor-root")).toMatchScreenshot( + "prop-change-image-width", + ); + + expect(ydocXml(baseDoc)).toMatchInlineSnapshot(` + " + + + + + + + " + `); + expect(ydocXml(suggestionDoc)).toMatchInlineSnapshot(` + " + + + + + + + " + `); + expect(editorHtml(editor)).toMatchInlineSnapshot(` + " + + + + + + + + + " + `); +}); + +// Image block prop change: `url`. Swaps the image source. +test("suggestion mode: change image source", async () => { + const { editor, screen, baseDoc, suggestionDoc, sync } = + await setupSuggestionTest({ userAction: "swap image src" }); + + editor.replaceBlocks(editor.document, [ + { + id: "block-image", + type: "image", + props: { + url: IMG_SRC_BASE, + previewWidth: 200, + }, + }, + ]); + sync(); + // Default `alt=""` on the image makes it decorative, so + // `getByRole("img")` doesn't see it. Poll on the prop having + // landed in the editor instead. + await expect.poll(() => editor.document[0]?.props?.url).toBe(IMG_SRC_BASE); + + editor.getExtension(SuggestionsExtension)!.enableSuggestions(); + + const [block] = editor.document; + editor.updateBlock(block, { + type: "image", + props: { url: IMG_SRC_NEW }, + }); + + await expect.poll(() => editor.document[0]?.props?.url).toBe(IMG_SRC_NEW); + + await expect(screen.getByTestId("editor-root")).toMatchScreenshot( + "prop-change-image-source", + ); + + expect(ydocXml(baseDoc)).toMatchInlineSnapshot(` + " + + + + + + + " + `); + expect(ydocXml(suggestionDoc)).toMatchInlineSnapshot(` + " + + + + + + + " + `); + expect(editorHtml(editor)).toMatchInlineSnapshot(` + " + + + + + + + + + " + `); +}); diff --git a/tests/vite.config.browser.ts b/tests/vite.config.browser.ts index 037fb8217a..5a3f5507b9 100644 --- a/tests/vite.config.browser.ts +++ b/tests/vite.config.browser.ts @@ -9,6 +9,7 @@ export default defineConfig((conf) => ({ plugins: [react()], test: { include: ["./src/browser/**/*.test.ts", "./src/browser/**/*.test.tsx"], + setupFiles: ["./src/browser/vitestSetup.ts"], browser: { enabled: true, provider: playwright(), From 395f7ee18a077324e3d8be64bda2f89e483453b5 Mon Sep 17 00:00:00 2001 From: yousefed Date: Thu, 28 May 2026 20:40:43 +0200 Subject: [PATCH 4/4] block type change tests --- tests/.gitignore | 6 + .../typeChanges.concurrent.test.tsx | 143 ++++++++++++++++++ .../y-prosemirror/typeChanges.test.tsx | 85 +++++++++++ 3 files changed, 234 insertions(+) create mode 100644 tests/.gitignore create mode 100644 tests/src/browser/y-prosemirror/typeChanges.concurrent.test.tsx create mode 100644 tests/src/browser/y-prosemirror/typeChanges.test.tsx diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000000..362e4126db --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,6 @@ +# vitest-browser auto-saved debug screenshots on test failure (separate +# from `toMatchScreenshot` reference shots, which use `*-chromium-darwin.png`). +src/browser/**/__screenshots__/**/*-1.png + +# vitest-browser attachments (debug artifacts saved during test runs). +.vitest-attachments diff --git a/tests/src/browser/y-prosemirror/typeChanges.concurrent.test.tsx b/tests/src/browser/y-prosemirror/typeChanges.concurrent.test.tsx new file mode 100644 index 0000000000..54d228fb14 --- /dev/null +++ b/tests/src/browser/y-prosemirror/typeChanges.concurrent.test.tsx @@ -0,0 +1,143 @@ +/* eslint-disable testing-library/render-result-naming-convention */ +/** + * Vitest browser-mode tests for two-user concurrent type-change + * suggestions. Same shape as `propChanges.concurrent.test.tsx`. + * + * KNOWN BUG: see `typeChanges.test.tsx` – block-type changes in + * suggestion mode currently throw in y-prosemirror's `deltaToPSteps`. + * Both tests below are marked `test.fails`; when the upstream bug is + * fixed they will flip red and we can capture proper snapshots. + */ +import { expect, test } from "vitest"; + +import { setupConcurrentSuggestionTest } from "./fixtures/concurrentSuggestionFixture.js"; +import { + editorHtml, + ydocXml, +} from "./fixtures/suggestionFixture.js"; + +// Two competing type changes on the same block: A wants a heading, B +// wants a list item. +test.fails( + "concurrent: A → heading, B → list item", + async () => { + const { + userA, + userB, + merged, + baseDoc, + suggestionDocA, + suggestionDocB, + suggestionDocMerged, + screen, + seed, + enableSuggestions, + sync, + } = await setupConcurrentSuggestionTest({ + userAAction: "→ heading", + userBAction: "→ list item", + }); + + userA.editor.replaceBlocks(userA.editor.document, [ + { id: "block-hello", type: "paragraph", content: "hello world" }, + ]); + seed(); + await expect + .element(screen.getByTestId(userA.testId).getByText("hello world")) + .toBeVisible(); + + enableSuggestions(); + + const [blockA] = userA.editor.document; + userA.editor.updateBlock(blockA, { + type: "heading", + props: { level: 1 }, + }); + + const [blockB] = userB.editor.document; + userB.editor.updateBlock(blockB, { type: "bulletListItem" }); + + await expect.poll(() => userA.editor.document[0]?.type).toBe("heading"); + await expect + .poll(() => userB.editor.document[0]?.type) + .toBe("bulletListItem"); + + sync(); + + await expect(screen.getByTestId("editor-root")).toMatchScreenshot( + "concurrent-heading-vs-list", + ); + + expect(ydocXml(baseDoc)).toMatchInlineSnapshot(); + expect(ydocXml(suggestionDocA)).toMatchInlineSnapshot(); + expect(ydocXml(suggestionDocB)).toMatchInlineSnapshot(); + expect(ydocXml(suggestionDocMerged)).toMatchInlineSnapshot(); + expect(editorHtml(merged.editor)).toMatchInlineSnapshot(); + }, +); + +// Mixed: A does a text edit (no type change), B changes the type. +// Exercises the path where one user's suggestion is a regular text +// diff and the other's is a block-type swap. +test.fails( + "concurrent: A edits text, B → heading", + async () => { + const { + userA, + userB, + merged, + baseDoc, + suggestionDocA, + suggestionDocB, + suggestionDocMerged, + screen, + seed, + enableSuggestions, + sync, + } = await setupConcurrentSuggestionTest({ + userAAction: "world → universe", + userBAction: "→ heading", + }); + + userA.editor.replaceBlocks(userA.editor.document, [ + { id: "block-hello", type: "paragraph", content: "hello world" }, + ]); + seed(); + await expect + .element(screen.getByTestId(userA.testId).getByText("hello world")) + .toBeVisible(); + + enableSuggestions(); + + const [blockA] = userA.editor.document; + userA.editor.updateBlock(blockA, { + type: "paragraph", + content: "hello universe", + }); + + const [blockB] = userB.editor.document; + userB.editor.updateBlock(blockB, { + type: "heading", + props: { level: 1 }, + }); + + await expect + .poll(() => + userA.editor.prosemirrorState.doc.toString().includes("y-attributed"), + ) + .toBe(true); + await expect.poll(() => userB.editor.document[0]?.type).toBe("heading"); + + sync(); + + await expect(screen.getByTestId("editor-root")).toMatchScreenshot( + "concurrent-text-edit-vs-heading", + ); + + expect(ydocXml(baseDoc)).toMatchInlineSnapshot(); + expect(ydocXml(suggestionDocA)).toMatchInlineSnapshot(); + expect(ydocXml(suggestionDocB)).toMatchInlineSnapshot(); + expect(ydocXml(suggestionDocMerged)).toMatchInlineSnapshot(); + expect(editorHtml(merged.editor)).toMatchInlineSnapshot(); + }, +); diff --git a/tests/src/browser/y-prosemirror/typeChanges.test.tsx b/tests/src/browser/y-prosemirror/typeChanges.test.tsx new file mode 100644 index 0000000000..db9ddbe1cb --- /dev/null +++ b/tests/src/browser/y-prosemirror/typeChanges.test.tsx @@ -0,0 +1,85 @@ +/* eslint-disable testing-library/render-result-naming-convention */ +/** + * Vitest browser-mode tests for type-change suggestions: swapping the + * block type (paragraph ↔ heading ↔ list item) while preserving its + * inline content. Same shape as `propChanges.test.tsx`. + * + * KNOWN BUG: `editor.updateBlock(block, { type: ... })` in suggestion + * mode currently throws `TransformError: No node at mark step's + * position` from y-prosemirror's `deltaToPSteps`. Tests are marked + * `test.fails` so they pass while the bug exists – when the + * underlying issue is fixed, the tests will start passing for real + * and `test.fails` will flip them red, signalling that snapshots need + * to be captured. + */ +import { SuggestionsExtension } from "@blocknote/core/y"; +import { expect, test } from "vitest"; + +import { + editorHtml, + setupSuggestionTest, + ydocXml, +} from "./fixtures/suggestionFixture.js"; + +// Demote a bullet-list item to a plain paragraph. Inline content +// "hello world" stays the same; only the wrapping node type changes. +test.fails("suggestion mode: change list item to paragraph", async () => { + const { editor, screen, baseDoc, suggestionDoc, sync } = + await setupSuggestionTest({ userAction: "list → paragraph" }); + + editor.replaceBlocks(editor.document, [ + { + id: "block-hello", + type: "bulletListItem", + content: "hello world", + }, + ]); + sync(); + await expect + .element(screen.getByTestId("editor-A").getByText("hello world")) + .toBeVisible(); + + editor.getExtension(SuggestionsExtension)!.enableSuggestions(); + + const [block] = editor.document; + editor.updateBlock(block, { type: "paragraph" }); + + await expect.poll(() => editor.document[0]?.type).toBe("paragraph"); + + await expect(screen.getByTestId("editor-root")).toMatchScreenshot( + "type-change-list-to-paragraph", + ); + + expect(ydocXml(baseDoc)).toMatchInlineSnapshot(); + expect(ydocXml(suggestionDoc)).toMatchInlineSnapshot(); + expect(editorHtml(editor)).toMatchInlineSnapshot(); +}); + +// Promote a paragraph to a level-1 heading. Same inline content. +test.fails("suggestion mode: change paragraph to heading", async () => { + const { editor, screen, baseDoc, suggestionDoc, sync } = + await setupSuggestionTest({ userAction: "paragraph → heading" }); + + editor.replaceBlocks(editor.document, [ + { id: "block-hello", type: "paragraph", content: "hello world" }, + ]); + sync(); + await expect + .element(screen.getByTestId("editor-A").getByText("hello world")) + .toBeVisible(); + + editor.getExtension(SuggestionsExtension)!.enableSuggestions(); + + const [block] = editor.document; + editor.updateBlock(block, { type: "heading", props: { level: 1 } }); + + await expect.poll(() => editor.document[0]?.type).toBe("heading"); + + await expect(screen.getByTestId("editor-root")).toMatchScreenshot( + "type-change-paragraph-to-heading", + ); + + expect(ydocXml(baseDoc)).toMatchInlineSnapshot(); + expect(ydocXml(suggestionDoc)).toMatchInlineSnapshot(); + expect(editorHtml(editor)).toMatchInlineSnapshot(); +});