Skip to content

E2E test for text formatting shortcuts (Notion test case 364) - #8333

Draft
JohnThomson wants to merge 3 commits into
masterfrom
tc364-text-formatting-shortcuts
Draft

E2E test for text formatting shortcuts (Notion test case 364)#8333
JohnThomson wants to merge 3 commits into
masterfrom
tc364-text-formatting-shortcuts

Conversation

@JohnThomson

@JohnThomson JohnThomson commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Bloom production code changes

  • src/BloomBrowserUI/bookEdit/topbar/editTopBarControls.tsx: EditingControlButton now puts a data-testid of the form edit-top-bar-<command>-button on its button (Paste, Cut, Copy, Undo), so an e2e test can find the Undo button in any UI language. No behavior change.

Purpose

Automates Notion test case 364 (Text Formatting Shortcuts): https://app.notion.com/p/Text-Formatting-Shortcuts-3904bb19df1281dab22bd07b14769b38

The test protects character formatting in a text box: Bold, Italic, Underline, Superscript and text color applied through the CKEditor formatting toolbar and through Ctrl+B / Ctrl+I / Ctrl+U, singly and layered; clearing it with Ctrl+Space and with the Remove Formatting button; and undo putting it back.

Steps

src/BloomE2E/tests/text-formatting-shortcuts.spec.ts, four serial tests on a Basic Book made at run time:

  1. Adds a "Basic Text & Image" page and types two paragraphs into its text box.
  2. Selects eight different words with the keyboard (click before the word, Shift+ArrowRight per character) and formats each: Bold button; Ctrl+I; Underline button; Superscript button; a palette color from the Text Color button; Ctrl+B then Underline button; Italic button then Ctrl+B then Ctrl+U; palette color then Superscript button.
  3. Ctrl+A, Ctrl+Space; checks every word is plain; clicks the top bar's Undo button; checks every word has its formatting again.
  4. Selects "brown fox" (two words formatted differently), clicks Remove Formatting; checks only those are plain; undoes; checks everything is restored.

How it verifies the result

A new helper, getFormattedRuns, reads the box back as runs of text with {bold, italic, underline, superscript, color} derived from the <strong>/<em>/<u>/<sup>/<span style=color> ancestors CKEditor writes, so assertions name formatting rather than markup. Every assertion is polled. The test also checks that the paragraph texts are unchanged after each operation, and that two never-formatted words stay plain.

Three green runs in a row locally (about 20 s each after the first launch); no Bloom.exe survived any run.

Test Steps covered / not covered

Covered: every step of the card. The "CTRL + Z" step is covered with a caveat: Ctrl+Z is a WinForms accelerator the shell handles before the browser sees it, so no test can press it. For that step the test calls helpers/workspace.ts undo, which runs the same workspaceBundle.handleUndo() the shell calls for the key, so what undo does is verified and only the key press itself is not. The Undo button step is a real click.

Helpers added

  • helpers/textFormatting.ts (new): selectTextInGroup, selectAllInGroup, clickFormatButton, pressFormatShortcut, pickTextColorFromToolbar, getFormattedRuns, formattingOf, expectFormatting, paragraphTextsOf, describeRuns, PLAIN.
  • helpers/bookMaking.ts: typeParagraphsInGroup.
  • helpers/workspace.ts: clickUndoButton (the UI route, beside the existing undo).

Automation debt

  • New entry "The text color palette sometimes does not open on the first click": Bloom hides every CKEditor panel on each selection check, which runs on a timer after the click, so the palette closes under the click about one run in two. The helper clicks the button again, as a person would. This looks like something a person can hit too and may deserve a card.
  • "WinForms surfaces are invisible to CDP" gets a seen-again line for Ctrl+Z (above), with a fix direction: an e2e/ hook that runs the shell's accelerator handling for a named key.

🤖 Generated with Claude Code

Devin review


This change is Reviewable

JohnThomson and others added 2 commits September 8, 2026 16:23
The new spec drives the Edit tab's CKEditor formatting toolbar and its
shortcuts on a two-paragraph text box: bold, italic, underline,
superscript and text color go on words through the toolbar buttons and
Ctrl+B/I/U, singly and in layers; Ctrl+A then Ctrl+Space takes every
formatting off and the top bar's Undo button puts it back; the Remove
Formatting button clears only the selected text and undo restores it.

helpers/textFormatting.ts is the new surface module: keyboard selection
of a stretch of text, the toolbar buttons and shortcuts, the text color
palette, and reading a box back as formatted runs. bookMaking.ts gains
typeParagraphsInGroup and workspace.ts gains clickUndoButton, the UI
route beside the existing production-path undo.

Bloom production code: EditingControlButton in editTopBarControls.tsx
gets a data-testid per command so the Undo button can be found in any UI
language.

AUTOMATION-DEBT.md records that Ctrl+Z is a WinForms accelerator no test
can press, and a new entry for the text color palette, which Bloom's
selection-check hiding closes under the click about one run in two; the
helper clicks again as a person would.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The Undo button is a React top-bar button that clickUndoButton clicks; only Ctrl+Z is the WinForms accelerator a test cannot press.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread src/BloomE2E/helpers/textFormatting.ts Outdated
Comment thread src/BloomE2E/helpers/textFormatting.ts Outdated
Comment on lines +278 to +290
for (let attempt = 1; attempt <= 6; attempt++) {
await clickFormatButton(page, "textColor");
const opened = await panel
.waitFor({ state: "visible", timeout: 2000 })
.then(() => true)
.catch(() => false);
if (!opened) continue;
// The palette is showing. Click the swatch straight away, before the selection check can
// hide the panel; if the panel goes anyway, the click times out and the loop tries again.
try {
await swatch.click({ timeout: 2000 });
await panel.waitFor({ state: "hidden", timeout: 15000 });
return;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Devin] Investigate: Color retry masks first-click failures

pickTextColorFromToolbar clicks the Text Color button up to six times to get around a race in which Bloom hides the palette right after it opens. That race is something a person can hit too (the palette appears not to open). With the retry in place the suite stays green while that user-facing behavior stays broken, so nothing in the nightly run would tell anyone if it got worse.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assessment (Claude Fable 5.1): agreed that the retry hides the product-side flakiness from the nightly run, and that is a deliberate trade-off rather than an oversight. The behavior under test on this card is the character formatting, not whether the palette opens on the first click, and a test that failed one run in two on that would be red for the wrong reason. The product problem is recorded in src/BloomE2E/AUTOMATION-DEBT.md ("The text color palette sometimes does not open on the first click"), with what causes it: Bloom hides every CKEditor panel on each selection check, so a palette that CKEditor thinks is open ends up hidden.

Leaving this thread open for the developer to decide whether that gets its own YouTrack card, or whether the test should instead fail when the first click does not open the palette.

Comment thread src/BloomE2E/helpers/textFormatting.ts Outdated
… their result

selectTextInGroup now presses Shift+ArrowRight once per grapheme (Intl.Segmenter) rather than per UTF-16 code unit, so an emoji or a combining sequence is one caret step as the browser counts it.

clickFormatButton and pressFormatShortcut wait for the command to land: a style command until its toolbar button toggles, Remove Formatting until the selection is plain.

pickTextColorFromToolbar waits out CKEditor's selection-check throttle after the palette opens before clicking a swatch, because a click into a panel that closes at that moment landed on the text and moved the selection; it now fails if the selection moved. AUTOMATION-DEBT.md records that.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
// that has just opened. A swatch click delivered into a panel that closes at that moment
// lands on the text beneath it and moves the selection, which is worse than a retry. So
// wait out that one check, and click only a panel that is still showing afterwards.
await page.waitForTimeout(kSelectionCheckMs);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Devin] Investigate: Fixed sleep bypasses state synchronization

pickTextColorFromToolbar waits a fixed 250 milliseconds after the palette opens instead of observing CKEditor's selection check. On a slow machine the check could still arrive after the wait, so the race is narrowed rather than closed, and every successful run pays the delay.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assessment (Claude Fable 5.1): this wait is deliberate, and it is the one place in the helper where a state to wait on does not exist. CKEditor runs its selection check from a private timer (a 200 ms throttle in its selection plugin); nothing on the page says whether one is still pending, and it is that check in which Bloom hides the palette. Without the wait, a swatch click delivered into a panel that closes at that moment landed on the text underneath and moved the selection, which is what the previous run did (the color went onto "liq" instead of "liquor"). The wait is named after the throttle it waits out, and the helper still checks the panel is showing afterwards and fails if the selection moved, so a slow machine gets a clear failure rather than a corrupted test.

The proper fix is in Bloom, not the helper: stop hiding a palette CKEditor has just opened (see the "text color palette" entry in src/BloomE2E/AUTOMATION-DEBT.md). Leaving this open with the related thread above for the developer to decide.

@JohnThomson

Copy link
Copy Markdown
Contributor Author

Consulted Devin on 2026-09-08 up to commit 4144d98 (two rounds: 663018c, then the fix commit).

Round 1 raised one bug and two flags, all in the new e2e helper textFormatting.ts: selecting text counted UTF-16 code units rather than caret steps (fixed); two action helpers returned before their result landed (fixed); and the text-color helper's retry hides a palette race that a user can hit too (assessed, left open for the developer). Round 2 repeated the two fixed findings against code that already contains the fixes (not re-posted) and added one new flag about the fixed 250 ms wait in the same helper (assessed, left open beside the related thread). No informational items in either round. CI (pr-automation) is green; no other bot commented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant