From c7fa1931334d85744310d451a9de839513394cd8 Mon Sep 17 00:00:00 2001 From: xarmian Date: Wed, 30 Sep 2026 05:10:20 +0000 Subject: [PATCH] test(materialize): pin the atBlank patch's equivalence under V8 too (TASK-2198 U2, codex r1) Codex r1 claimed upstream's /(^|\n)$/ is true for a trailing CR in JS. It is not (checked in V8: false for "\r" and "a\u2028", as the patch returns), but the only equivalence test ran under goja and the patch header cited a V8-side test that did not exist. This adds it, with the line-terminator edge cases, and it fails when the patch checks CR instead of LF. Claude-Session: https://claude.ai/code/session_01H63zaWhkb5pAafx2WGjrVC --- .../lib/collab/materializer/atBlank.test.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 web/src/lib/collab/materializer/atBlank.test.ts diff --git a/web/src/lib/collab/materializer/atBlank.test.ts b/web/src/lib/collab/materializer/atBlank.test.ts new file mode 100644 index 000000000..8d38b1b43 --- /dev/null +++ b/web/src/lib/collab/materializer/atBlank.test.ts @@ -0,0 +1,30 @@ +import { describe, it, expect } from 'vitest'; +import { upstreamAtBlank, patchedAtBlank } from './atBlank'; + +// The vendored atBlank patch must agree with upstream prosemirror-markdown in +// the engine a browser tab runs, not only in goja (internal/materialize has +// the goja leg). This runs under V8. The line-terminator cases are the ones +// where ECMAScript `$` could plausibly differ from an ends-with-LF check: it +// does not, without the `m` flag, and this pins that. +describe('atBlank patch equivalence under V8 (TASK-2198)', () => { + const call = (fn: typeof upstreamAtBlank, out: string) => fn.call({ out }); + + it('agrees on line-terminator edge cases', () => { + const cases = ['', '\n', '\r', '\r\n', '\n\r', '
', '
', 'a', 'a\n', 'a\r', 'a\r\n', 'a
', 'a\n\n', ' \n', '\n ']; + for (const out of cases) { + expect(call(patchedAtBlank, out), JSON.stringify(out)).toBe(call(upstreamAtBlank, out)); + } + }); + + it('agrees on generated strings', () => { + const alphabet = ['a', ' ', '\n', '\r', '\t', '
', '#', '`']; + let seed = 3252; + const rnd = () => (seed = (seed * 1103515245 + 12345) & 0x7fffffff) / 0x7fffffff; + for (let i = 0; i < 5000; i++) { + const len = Math.floor(rnd() * 12); + let out = ''; + for (let j = 0; j < len; j++) out += alphabet[Math.floor(rnd() * alphabet.length)]; + expect(call(patchedAtBlank, out), JSON.stringify(out)).toBe(call(upstreamAtBlank, out)); + } + }); +});