You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/components/molecules/MarkdownEditor/AGENTS.md
+8-7Lines changed: 8 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -57,13 +57,14 @@ These are cross-cutting trade-offs that are not localized in any single file. Th
57
57
58
58
Specs live next to their target as `*.spec.ts` / `*.spec.tsx`. Shared helpers in `__tests__/utils.tsx` — go through them so every test starts from the production-shaped editor.
| Pure `$`-helper |`codeBlock.spec.ts`| Pure structural / tree-walk helpers. Wrap with `createTestHeadlessEditor()` — no React, no DOM mount. |
63
-
| Transformer round-trip |`transformers.spec.ts`, `transformers.quote.spec.ts`| Import/export through `MARKDOWN_TRANSFORMERS`. `$setMarkdown` + `$readMarkdown` on a headless editor. |
64
-
| Plugin keyboard contract |`QuoteExitPlugin.spec.tsx`| A plugin gated on a key press. `renderTestEditor({plugins: <YourPlugin />})` + `dispatchKeyDown(editor, "Backspace")`. The helper wraps the dispatch in a discrete `editor.update` so the listener's mutations commit synchronously (default dispatch defers to a microtask). |
65
-
| Live-typing → rendered DOM |`markdownShortcuts.spec.tsx`| "Type chars → DOM mutates" (shortcuts, converters). `renderTestEditor` with the plugin and its transformer set, then `await user.click(textbox); await user.keyboard(...)`. Use real `userEvent` from `vitest/browser` (CDP events — composition / `beforeinput` / selection behave like a real keystroke). |
66
-
| Mount-time initial value |`MarkdownEditor.spec.tsx`| "Open an existing task body" path. `renderTestEditor({initialValue: "..."})` runs the production `buildInitialConfig` initializer end-to-end. |
| Pure `$`-helper |`codeBlock.spec.ts`| Pure structural / tree-walk helpers. Wrap with `createTestHeadlessEditor()` — no React, no DOM mount. |
63
+
| Transformer round-trip |`transformers.spec.ts`, `transformers.quote.spec.ts`, `transformers.table.spec.ts`| Import/export through `MARKDOWN_TRANSFORMERS`. `$setMarkdown` + `$readMarkdown` on a headless editor. |
64
+
| Plugin keyboard contract |`QuoteExitPlugin.spec.tsx`, `TableKeyboardPlugin.spec.tsx`| A plugin gated on a key press. `renderTestEditor({plugins: <YourPlugin />})` + `dispatchKeyDown(editor, "Backspace")`. The helper wraps the dispatch in a discrete `editor.update` so the listener's mutations commit synchronously (default dispatch defers to a microtask). Pair with `<TablePlugin>` when the plugin under test depends on TableNode behaviors. |
65
+
| Live-typing → rendered DOM |`markdownShortcuts.spec.tsx`, `tableShortcuts.spec.tsx`| "Type chars → DOM mutates" (shortcuts, converters). `renderTestEditor` with the plugin and its transformer set, then `await user.click(textbox); await user.keyboard(...)`. Use real `userEvent` from `vitest/browser` (CDP events — composition / `beforeinput` / selection behave like a real keystroke). The MarkdownShortcutPlugin only fires its element transformers when the LAST char typed is a space (or on Enter via `triggerOnEnter: true`); the trigger char shape determines which commit branch runs (e.g. TABLE's `$seedHeaderColumn` vs `$seedBodyRow`). |
66
+
| Node-transform safety net |`NoListInTablePlugin.spec.tsx`| A plugin whose contract is "if a node lands here in a forbidden shape, rewrite it" (no key press, no typing). Programmatically build the forbidden shape inside `editor.update({discrete: true})`; the plugin's `registerNodeTransform` callback fires on commit and Lexical re-fires until no dirty matching nodes remain — assert the post-transform shape directly. |
67
+
| Mount-time initial value |`MarkdownEditor.spec.tsx`| "Open an existing task body" path. `renderTestEditor({initialValue: "..."})` runs the production `buildInitialConfig` initializer end-to-end. |
67
68
68
69
**Assert against the structural triple** — `editorRoot.children` length + `firstElementChild.tagName` + `firstElementChild.textContent` — rather than `toBeInTheDocument()` / `toBeVisible()` alone. The matchers pass even when an extra paragraph renders alongside the expected one; the triple pins the rendered shape exactly without freezing theme classes or Lexical-internal attributes (`dir`, `data-lexical-text`, ...) that an `innerHTML` snapshot would lock down.
0 commit comments