Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/BloomBrowserUI/bookEdit/topbar/editTopBarControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,9 @@ export const EditingControlButton: React.FunctionComponent<{
<BloomButton
enabled={props.enabled}
l10nKey={props.l10nKey}
// For the e2e tests (src/BloomE2E/helpers/workspace.ts), which must find the
// button by something other than its localized label.
data-testid={`edit-top-bar-${props.onClickAction}-button`}
onMouseDown={(e) => {
// Keep focus in the main editable browser; otherwise this button takes focus
// first and copy/cut/paste/undo may run against the wrong context.
Expand Down
20 changes: 20 additions & 0 deletions src/BloomE2E/AUTOMATION-DEBT.md
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,26 @@ So: **to test the working tree, serve the dev server on 5173 and set
`BLOOM_E2E_VITE_PORT=5173`.** Fix direction: emit the port into those two pug files the way the
shell gets it, so `--vite-port` means what it says. (Found 2026-09-02.)

## The text color palette sometimes does not open on the first click

The formatting toolbar's Text Color button drops down a CKEditor panel of swatches. `attachToCkEditor`
in `bookEdit/js/bloomEditing.ts` hides every `.cke_panel` on every CKEditor `selectionCheck`, to stop
the palette from popping up on its own each time the toolbar shows (its comment says nobody knows
why it does). CKEditor checks the selection a moment after each mouseup, on a timer, so when that
check lands after the click has opened the panel, the panel is hidden again while the button still
shows as "on": the click looks as if it did nothing, and the next click closes the panel CKEditor
thinks is open rather than showing it. Seen 2026-09-08 (Test Case ID 364,
`text-formatting-shortcuts.spec.ts`): the second color pick of a run failed this way about one run in
two, the first never did. Worse, a swatch click delivered into a panel that closed at that moment
landed on the text beneath it and moved the selection, so the color went on half a word.
`helpers/textFormatting.ts pickTextColorFromToolbar` therefore waits out CKEditor's 200ms
selection-check throttle after the panel opens, clicks a swatch only in a panel that is still
showing, clicks the button again otherwise (which is what a person does), and fails if the
selection moved. Fix direction: find out why the panel reappears
on its own (CKEditor's floatpanel remembers `showBlockParams`, and Bloom's `display:none` bypasses
its `hide`, so its state and the DOM disagree from then on) and hide it through `panel.hide()`
instead, or only on `selectionChange` rather than on every check. A person can hit this too.

## Canvas element toolbar buttons are anonymous

The floating toolbar over a selected canvas element (`#canvas-element-context-controls`,
Expand Down
31 changes: 31 additions & 0 deletions src/BloomE2E/helpers/bookMaking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,37 @@ export async function typeInGroup(
await expect(box).toHaveText(text, { timeout: 15000 });
}

/**
* Type several paragraphs into one language's box of one translation group, the way a person does:
* the first as typeInGroup types it, then Enter and the next, and so on. The box ends up holding one
* <p> per paragraph, which is what a test of anything paragraph-shaped needs to start from.
*
* As with typeInGroup, nothing reaches the file until the book leaves this page — see goToPage.
*/
export async function typeParagraphsInGroup(
page: Page,
groupSelector: string,
languageTag: string,
paragraphs: string[],
): Promise<void> {
if (paragraphs.length === 0)
throw new Error("typeParagraphsInGroup needs at least one paragraph.");
await typeInGroup(page, groupSelector, languageTag, paragraphs[0]);
// typeInGroup leaves the caret at the end of what it typed, so each Enter starts a new paragraph
// after the last one. Enter is a real key press because CKEditor makes the paragraph from it.
for (const paragraph of paragraphs.slice(1)) {
await page.keyboard.press("Enter");
await page.keyboard.insertText(paragraph);
}
const box = editablePageFrame(page)
.locator(`${groupSelector} .bloom-editable[lang="${languageTag}"]`)
.first();
await expect(
box.locator(":scope > p"),
`The "${languageTag}" box of "${groupSelector}" did not end up with one paragraph per string typed.`,
).toHaveText(paragraphs, { timeout: 15000 });
}

/**
* The font one language's box of one translation group is shown in: the first family of its computed
* font-family, without quotes, e.g. "Andika". This is how a test checks that a font chosen in the
Expand Down
6 changes: 3 additions & 3 deletions src/BloomE2E/helpers/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
// text box raises no key events".)
//
// Every press goes through Playwright's keyboard, which sends the same CDP raw key events the
// browser would build from a physical key. What it cannot do is send a key that Bloom's WinForms
// shell claims as an accelerator: Ctrl+Z never reaches the page at all, which is why undo has its
// own helper in workspace.ts rather than a press here.
// browser would build from a physical key. That includes Ctrl+Z: the shell lets it through to the
// page, where CKEditor's undo plugin handles it (workspace.ts `pressUndoKey` is that press, beside
// the other undo routes).

import { expect, type Locator, type Page } from "@playwright/test";

Expand Down
Loading