Skip to content
Closed
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
73 changes: 34 additions & 39 deletions src/BloomE2E/AUTOMATION-DEBT.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,11 @@ and ask its author.

| Pull request | Branch | What it pays down |
| --- | --- | --- |
| #8300 | `BL-16799-collection-languages` | The `e2e/setCollectionLanguages` hook, so no test composes `.bloomCollection` XML. |
| not yet open | `e2e-private-user-settings` | Every Bloom a test launches keeps its user settings in a folder of its own, named on the command line, so a run starts from defaults and its settings die with its temp folder. |
| not yet open | `e2e-real-library-login` | A test can sign in to dev.bloomlibrary.org for real, with a test account whose credentials the run supplies, so the upload cases can run to the end. Branches off the one above. |

Three of these also add entries of their own, for the debt that is left after the fix. The
stack replaces PR #8276, which did all of this at once.
The second of these branches off the first. Both add entries of their own, for the debt that
is left after the fix.

---

Expand All @@ -40,18 +39,13 @@ force-foreground trick. Fix direction: move these surfaces to the web UI (the te
direction anyway), or expose each dialog's WebView2 on a discoverable CDP port.
(Promoted from PAPERCUTS 2026-07-11.)

seen again 2026-09-01 (Test Case ID 169, `publish-text-languages.spec.ts`): the case
turns on which languages the collection has, and there is no API for that either.
`collectionSettings/changeLanguage` is not one: its only listener is the open WinForms
`CollectionSettingsDialog` (`CollectionSettingsDialog.cs:368`), so a POST to it while
the dialog is closed does nothing. The test therefore changes a collection language by
stopping Bloom, rewriting the `.bloomCollection`, and starting again, which is what the
new `bloomApp.restart(betweenStopAndStart)` fixture method is for. Each restart costs
about six seconds and loses whatever the editor had not yet saved.

being fixed on `BL-16799-collection-languages` (#8300): the `e2e/setCollectionLanguages` hook
does the work of the Settings dialog's OK button, so no test composes `.bloomCollection`
XML. The dialog itself stays on this entry: nothing can drive or screenshot it.
seen again 2026-09-01, and the collection-languages half of it is now fixed: tests set
the collection's languages through the `e2e/setCollectionLanguages` hook, which does
the same work as clicking OK in the Collection Settings dialog, and
`helpers/collection.ts setCollectionLanguages` wraps it with the restart Bloom still
needs (about six seconds, and it loses whatever the editor had not yet saved). No test
composes `.bloomCollection` XML any more. What remains is the dialog itself: nothing
can drive or screenshot the Settings UI, so the journey test for it cannot be written.

seen again 2026-09-01 (Test Case ID 349, `duplicate-page.spec.ts`): "Duplicate Page Many
Times..." asks how many copies in `DuplicateManyDialog`, which is `WireUpForWinforms`, so the
Expand Down Expand Up @@ -128,7 +122,12 @@ established 2026-09-01:
- **Leaving the variable unset does not mean "no dev server".** A dev build probes port 5173 by
itself (`ReactControl.TryGetActiveViteDevPort`), so a developer's own dev server silently
decides what the suite tests, and Bloom offers no option that means "ignore any dev server"
(`--vite-port` rejects 0, and `ValidateStartupVitePort` requires the port to answer).
(`--vite-port` rejects 0, and `ValidateStartupVitePort` requires the port to answer). This is
what went wrong on 2026-09-02: the variable was unset, so a run served a day-old bundle and
`duplicate-page.spec.ts` reported a new test id as absent. `--vite-port` itself honours any
port. `ReactControl.ReplaceViteDevOrigin` rewrites the literal `http://localhost:5173` in all
three Edit frames, the two `*.vite-dev.pug` files included, and a run on 5199 on 2026-09-03
loaded the shell, the toolbox and the page list from 5199.

What remains: the fixture neither starts a dev server of its own nor checks that `output/browser`
is newer than `src/BloomBrowserUI`, so a run with the variable unset can still test a stale
Expand Down Expand Up @@ -239,6 +238,25 @@ shows a `.bloom-page`. Two page adds in a row therefore lost the second one.
helpers no longer act early; the production endpoints still reply success to a request they
dropped.

## Bloom names a dropped language differently on CI and on a developer machine (BL-16806)

`publish-text-languages.spec.ts` used to drop a language by rewriting the `.bloomCollection`,
and it then expected the publish list to show that language's own name for itself, "español".
On CI that assertion failed every time, with `Expected: español Received: espagnol` — French
for Spanish. Everything else about the row was right.

The cause is known, and BL-16806 is the card that fixes it. Bloom asks LibPalaso for the name of
the dropped language "in" the collection's metadata language, French here. LibPalaso honors such
a request only where it can find a native ICU library, and Bloom ships icu.net but no
`icuuc.dll`, so the answer depends on the machine, not on the run: the CI runner answers
"espagnol", and a developer machine ignores the request and gives the autonym "español". It is
a real difference in what Bloom shows a user, not a test problem.

No test on this branch covers it any more. The test now drops the language through
`e2e/setCollectionLanguages`, the code the Settings dialog's OK button runs, which keeps the
language's collection name, so the list reads "Spanish" on every machine and the lookup that
differs is never reached. Recorded here so the coverage loss is visible; the defect itself
belongs to BL-16806. (Found 2026-09-01, diagnosed on master 2026-09-02.)
## Filling a text box directly leaves part of the old text behind

A `.bloom-editable` is a CKEditor surface, and Playwright's `fill()` on one leaves a
Expand Down Expand Up @@ -407,29 +425,6 @@ meantime: run small and stay red, or stay large until the tests are fixed.
developer chose to record the plan here rather than carry a red suite. The code is not in the
history, so rebuilding it from this entry is part of the job.)

## A Vite dev server only reaches the whole UI on port 5173

`--vite-port` tells Bloom's shell which dev server to load the front end from, but two of the
Edit tab's frames ignore it. `bookEdit/pageThumbnailList/pageThumbnailList.vite-dev.pug` and
`bookEdit/toolbox/toolbox.vite-dev.pug` write `http://localhost:5173/...` into every import
they emit, so on any other port the page list and the toolbox load nothing and come up empty.

That failure looks like the feature being missing, not like a port problem. A run on port 5199
failed `duplicate-page.spec.ts` on 2026-09-02 with "waiting for
getByTestId('duplicate-page-button') to be visible", 30 seconds, because `#PageControls` had
never been filled. Nothing in the message points at the dev server.

The same run showed the second half of it: `BLOOM_E2E_VITE_PORT` was unset, so Bloom fell back
to probing 5173 by itself, found nothing there, and served the built `output/browser` instead.
That bundle was a day old, so the suite silently tested yesterday's front end and reported the
new test id as absent.

So both halves say the same thing: **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; and give Bloom an option that means "ignore
any dev server", so a run can state which front end it is testing rather than inherit it from
the machine. (Found 2026-09-02.)

## Typing in a text box raises no key events

`typeInGroup` puts the whole string in with `keyboard.insertText`, which raises `input`
Expand Down
39 changes: 18 additions & 21 deletions src/BloomE2E/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,11 @@ and whatever tab is showing. Most tests need nothing else.
| `restart` | Stop Bloom, run an optional callback, start it again on the same collection, and return the new page. |

`restart(betweenStopAndStart)` is how a test changes something Bloom only reads at startup. The
collection's languages are the case that needed it: the collection Settings dialog is a WinForms
surface CDP cannot reach, and `collectionSettings/changeLanguage` only answers while that dialog
is open, so the way to change a language is to stop Bloom, rewrite the `.bloomCollection` with
`makeCollectionXml`, and start again. Bloom is killed rather than asked to quit, so leave the
page being edited before restarting or what was typed on it is lost. Use the page `restart`
returns; the old one is closed.
collection's languages are the case that needed it, and a test does not call `restart` itself for
that: `setCollectionLanguages(bloomApp, tags)` posts the new languages to the `e2e/` hook that
writes the `.bloomCollection`, then restarts Bloom and returns the new page. Bloom is killed
rather than asked to quit, so leave the page being edited before restarting or what was typed on
it is lost. Use the page `restart` returns; the old one is closed.

Teardown kills the process tree, waits for the HTTP port to go dark, and deletes the temp copy.

Expand Down Expand Up @@ -93,7 +92,7 @@ real bug in the code under test; read the message and fix it rather than working
dialog it opens: `openFormatDialog`, `clickOutsideFormatDialog`, `dragFormatDialog`,
`getFormatDialogPlacement`, and the scrolling and zooming that put the gear at the edge of the
screen.
- `helpers/collection.ts` — `selectBook`, `waitForCollectionReady`.
- `helpers/collection.ts` — `selectBook`, `waitForCollectionReady`, `setCollectionLanguages`.
- `helpers/bookMaking.ts` — make a book, add pages, type into it, read its pages.
- `helpers/addPageDialog.ts` — open, read, scroll and close the real Add Page dialog, and add a
page through it. Tests that only need a page in their book call `addPage` instead.
Expand Down Expand Up @@ -220,24 +219,22 @@ start a Vite dev server and name its port in `BLOOM_E2E_VITE_PORT`; the fixture
`--vite-port` to Bloom, which loads every React control from the dev server.

```bash
# In one terminal, in src/BloomBrowserUI. Set PORT as well as --port: the port in
# vite.config.mts comes from process.env.PORT, and --port alone leaves the HMR and
# React-Refresh URLs pointing at 5173, which makes the page fail to load its entry module.
PORT=5173 pnpm exec vite --port 5173 --strictPort
# In one terminal, in src/BloomBrowserUI. Any free port works. Set PORT as well as --port:
# the port in vite.config.mts comes from process.env.PORT, and --port alone leaves the HMR
# and React-Refresh URLs pointing at 5173, which makes the page fail to load its entry
# module.
PORT=5199 pnpm exec vite --port 5199 --strictPort

# In another, in src/BloomE2E
BLOOM_E2E_VITE_PORT=5173 pnpm exec playwright test
BLOOM_E2E_VITE_PORT=5199 pnpm exec playwright test
```

**Use 5173, and set the variable.** The port is not free to choose: the page list and the toolbox
write `http://localhost:5173` into their own imports, so on any other port those two frames load
nothing and come up empty, which reads as the feature being missing rather than as a port
problem. And leaving `BLOOM_E2E_VITE_PORT` unset does not mean "no dev server": a dev build of
Bloom probes 5173 by itself, so an unset variable and a server somewhere else means the run
quietly tests the built bundle, however old it is. Both halves are in AUTOMATION-DEBT.md under
"A Vite dev server only reaches the whole UI on port 5173".

So stop a Bloom that is already using 5173 before a run, rather than moving the dev server.
**Always set the variable.** An unset `BLOOM_E2E_VITE_PORT` does not mean "no dev server": a dev
build of Bloom probes 5173 by itself, so an unset variable and a dev server on another port means
the run quietly tests the built `output/browser`, however old it is. A stale bundle reads as the
feature being missing rather than as a stale bundle, which is what the 2026-09-02 failure looked
like. See AUTOMATION-DEBT.md under "Which front end the e2e suite tests depends on what else is
running".

## In CI

Expand Down
33 changes: 33 additions & 0 deletions src/BloomE2E/helpers/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { expect, type Page } from "@playwright/test";
import { apiGet, apiPost } from "./api";
import type { IBloomApp } from "../fixtures/bloomTest";

/**
* Wait until the editable collection is loaded and its books can be enumerated. Switching to the
Expand Down Expand Up @@ -45,3 +46,35 @@ export async function selectBook(
`&collection-id=${encodeURIComponent(collectionId)}`,
);
}

/**
* Set the collection's languages, and give Bloom back to the test with the change in effect.
*
* `tags` holds one to three language tags, for Language 1, Language 2 and Language 3. Fewer than
* three leaves the rest empty, which is how a collection ends up with no Language 3.
*
* Bloom reads a collection's languages when it opens the collection, so this restarts it, exactly
* as clicking OK in the Collection Settings dialog makes a user reopen the collection. The restart
* kills the process, so leave the page being edited first or what was typed on it is lost, and use
* the page this returns: the old one points at a dead target.
*
* The `e2e/setCollectionLanguages` hook writes the .bloomCollection with Bloom's own code, so
* everything else in the collection's settings survives. See AUTOMATION-DEBT.md for why there is
* no production API for this.
*/
export async function setCollectionLanguages(
bloomApp: IBloomApp,
tags: string[],
): Promise<Page> {
if (tags.length < 1 || tags.length > 3)
throw new Error(
`A collection has one to three languages; setCollectionLanguages was given ${tags.length}.`,
);
await apiPost(
bloomApp.page,
"e2e/setCollectionLanguages",
JSON.stringify(tags),
"application/json",
);
return bloomApp.restart();
}
48 changes: 30 additions & 18 deletions src/BloomE2E/helpers/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,24 +175,36 @@ export async function clickTextLanguage(
export async function showBloomPubPreview(page: Page): Promise<Frame> {
await page.locator('[aria-label="refresh preview"]').click();
let player: Frame | undefined;
await expect
.poll(
async () => {
player = page
.frames()
.find((f) => f.url().includes("bloomplayer.htm"));
if (!player) return 0;
return player
.locator('[aria-label="Choose Language"]')
.count()
.catch(() => 0);
},
{
timeout: 120000,
message: "The BloomPUB preview never showed the book.",
},
)
.toBeGreaterThan(0);
try {
await expect
.poll(
async () => {
player = page
.frames()
.find((f) => f.url().includes("bloomplayer.htm"));
if (!player) return 0;
return player
.locator('[aria-label="Choose Language"]')
.count()
.catch(() => 0);
},
{ timeout: 120000 },
)
.toBeGreaterThan(0);
} catch {
// Say which of the two steps did not happen: Bloom never gave the player a book to show,
// or the player has one and never finished loading it. Without this the failure looks the
// same either way, and the two have nothing to do with each other.
const frameUrls = page.frames().map((frame) => frame.url());
throw new Error(
`The BloomPUB preview never showed the book. ` +
(player
? `bloom-player is loaded at ${player.url()} but never offered the language ` +
`menu, so it never finished showing a book.`
: `No frame of this page is bloom-player, so Bloom never staged the ` +
`publication. Frames: ${frameUrls.join(", ")}.`),
);
}
return player!;
}

Expand Down
Loading