Skip to content
Merged
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
9 changes: 6 additions & 3 deletions .github/skills/add-e2e-test/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,13 @@ The worker-scoped fixture launches Bloom on a temp copy of that collection and y

- `page` — Playwright's `page`, overridden to be Bloom's shell document (the top bar and
the showing tab). Most tests need nothing else.
- `bloomApp` — `{ page, httpPort, cdpPort, bloomPid, collectionDir, restart }`. Build book
paths from `collectionDir`, never from `output/testing-inputs`. `restart(callback)`
- `bloomApp` — `{ page, httpPort, cdpPort, bloomPid, collectionDir, userSettingsDir, restart }`.
Build book paths from `collectionDir`, never from `output/testing-inputs`. `restart(callback)`
stops Bloom, runs the callback, and starts it again, which is how a test changes what
Bloom reads only at startup, such as the collection's languages.
Bloom reads only at startup, such as the collection's languages. `userSettingsDir` is where
this Bloom keeps its user settings (`user.config`): a folder of its own in the temp folder,
empty at launch, so a test's Bloom starts from default settings and shares none with the
developer's Bloom or the previous run. `helpers/userSettings.ts` reads what it saved there.

The fixture also watches for the "Bloom had a problem" dialog and fails the test with the
exception it scrapes from behind the dialog's own "Learn More" link. See
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,13 @@ jobs:
# also work, but asking for it here says what the run needs instead of leaving
# it to whatever the runner happens to do.
BLOOM_AUTOMATION_MONITOR: headless
# The password of the Bloom Library test account (e2e-tester@example.org on
# dev.bloomlibrary.org), for the tests that sign in and upload for real. The
# account exists only for this; the email is a constant in the suite
# (src/BloomE2E/helpers/bloomLibraryAccount.ts). Locally the same variable is set
# at User scope; a developer without it has those tests skipped, while a run on CI
# without it fails, so a missing secret is noticed.
BLOOM_E2E_TESTER_EMAIL_BLORG_PASSWORD: ${{ secrets.BLOOM_E2E_TESTER_EMAIL_BLORG_PASSWORD }}
run: pnpm test --reporter=list,junit,html

# What explains an e2e failure: Playwright's HTML report, plus the trace and failure
Expand Down
65 changes: 16 additions & 49 deletions src/BloomE2E/AUTOMATION-DEBT.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ and ask its author.
| --- | --- | --- |
| #8299 | `BL-16799-page-change` | `editView/jumpToPage` refuses a jump it cannot do, and every page-changing helper waits for the Edit tab to settle. |
| #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. |
| #8306 | `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 `e2e-private-user-settings`, which gave every Bloom a test launches a settings folder of its own and removed its entry from this file. |

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.
Expand Down Expand Up @@ -149,29 +148,19 @@ use.
it, so it was left alone -- but it is the same shape, and worth remembering if a test ever finds
that api reporting a stale subscription.

## The Bloom Library login cannot be done for real in a test

Bloom's login state lives in machine-wide settings (`Settings.Default.WebUserId`), which an e2e
Bloom shares with the developer's own Bloom, and signing in goes out to an external browser with
real credentials. So a test can drive neither half of it: posting `account/logout` would sign the
developer out of their own Bloom, and `account/login` would sit waiting for a human. The e2e hook
`e2e/loginState` therefore makes Bloom *report* a login state without touching the real one, which
is enough for the gate the upload screen enforces (Upload is offered only to a signed-in user) but
covers neither the real sign-in and sign-out buttons nor anything that needs a real account —
which is every manual case that uploads for real (#204, #205, #211-#213, #215, #217, #218, #220),
so none of those can be automated either. Fix direction: a test account plus a per-instance login
store (a login the `--e2e` instance keeps to itself), so a run can sign in for real and upload to
dev.bloomlibrary.org without touching the developer's settings. The per-instance half of that is
the same fix "Every Bloom of one build shares one user.config" asks for, below; the test account
is the rest. Note that the pretense changes only what Bloom reports, so Bloom under `--e2e` now
refuses to upload at all rather than let an automated click publish under the developer's real
account.
(Found 2026-09-02 automating Test Case ID 606, `upload-required-items.spec.ts`.)

being fixed on `e2e-real-library-login`, once `e2e-private-user-settings` (the user.config entry
below) has landed: with a settings folder of its own, a test's Bloom can be given a real test
account's login before it starts, and can sign out for real without touching anyone else's login.
The test account, and where its credentials live, are the rest of this branch.
## The Sign in button's trip through the system browser cannot be driven

A test can now sign a Bloom in to dev.bloomlibrary.org for real (`signBloomIntoLibraryForReal`,
`helpers/bloomLibraryAccount.ts`): it signs the test account in the way the website does and posts
the result to `external/login`, the endpoint the website posts a browser login back to, and Bloom
keeps it in the run's own settings folder. What stays out of reach is the button itself: Sign in
opens the system browser on bloomlibrary.org's login page, and Playwright drives only Bloom's
WebView2, so the click, the browser round trip, and the hand-back to Bloom are never exercised.
Sign out is a plain button and could be, but no test does yet. Cost: the manual cases about the
sign-in and sign-out buttons stay manual; every case that merely needs to be signed in (the upload
cases) can run. Fix direction: none cheap. A Bloom-hosted login page under `--e2e` would test a
different flow from the one users have.
(Found 2026-09-04 automating Test Case ID 211, `bulk-upload-quick-test.spec.ts`.)

## Which front end the e2e suite tests depends on what else is running

Expand Down Expand Up @@ -355,28 +344,6 @@ copy is a feature we want; if it is, put the page on the real clipboard, and giv
fixture a way to run a second instance.
(Found 2026-09-01 while automating Test Case ID 348.)

## Every Bloom of one build shares one user.config, so a run inherits another Bloom's settings

Bloom keeps its user settings (UI language, page zoom, and the rest of `Settings.Default`) in
`%LOCALAPPDATA%\SIL\Bloom\<version>\user.config`, one file per build version, and `--e2e` does
nothing to change that. So the Bloom a test launches starts from whatever the last Bloom of the
same version saved, and saves its own changes for the next one. The e2e lock keeps suites from
running at once, but a developer's own Bloom from a worktree of the same version is outside the
lock and shares the file all the same, and so does the previous run of any suite.

Seen 2026-09-02 (Test Case ID 356, `format-gear-positioning.spec.ts`): two runs found every
factory template named in Turkish, then in French, and failed in `makeBookFromTemplate`, which
matches the English title; a Bloom nobody in the suite had started was running at the time, and
the file said `en` again a moment later. The same test has to restore the zoom it changes, because
that setting is shared too. Fix direction: under `--e2e`, point the settings provider at a
per-instance folder (a sibling of the temp collection would do), so a test's Bloom starts from
defaults and its changes die with it.

being fixed on `e2e-private-user-settings`: a command-line argument names the folder Bloom keeps
its user settings in, and the launch fixture gives every Bloom it starts a folder inside the run's
temp folder, so the settings start from defaults, or from whatever the test puts there first, and
are deleted with the rest of the run.

## No way to run the suite at a chosen monitor resolution and scale factor

Every run takes the resolution and the scale factor of whatever monitor it lands on, so a
Expand Down Expand Up @@ -408,8 +375,8 @@ Fix direction, cheapest first, none of it tried yet:
driver is told to offer. Setting that monitor's *scale factor* is the harder half: Windows
exposes per-monitor scale only through display-config calls Microsoft does not document.
Worth an afternoon of investigation before committing to it.
- **A virtual machine or Windows Sandbox** at a chosen resolution and scale. Heaviest, but it
is the only one that also isolates the shared `user.config` described above.
- **A virtual machine or Windows Sandbox** at a chosen resolution and scale. Heaviest, and the
only one that would also isolate everything else on the machine a run could inherit.

Whatever the mechanism, the suite needs the same thing from it: a way to say "run these tests
at 1920x1080 at 150%" and have the run either honour it or refuse, rather than silently using
Expand Down
14 changes: 14 additions & 0 deletions src/BloomE2E/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,20 @@ and whatever tab is showing. Most tests need nothing else.
| `cdpPort` | The port the embedded WebView2 answers CDP on. |
| `bloomPid` | The process id of the Bloom serving this collection. |
| `collectionDir` | The temp copy of the collection. Build book paths from this, never from `output/testing-inputs`. |
| `userSettingsDir` | The folder this Bloom keeps its user settings in (its `user.config`), beside the collection in the temp folder. |
| `restart` | Stop Bloom, run an optional callback, start it again on the same collection, and return the new page. |

Every Bloom the fixture launches keeps its user settings, the contents of `user.config` (UI
language, page zoom, the Bloom Library login, and the rest of `Settings.Default`), in
`userSettingsDir`, passed to Bloom as `--user-settings-folder`. Every Bloom of one build otherwise
shares one `user.config` in `%LOCALAPPDATA%\SIL\Bloom\<version>`, so a run would start from
whatever the developer's Bloom, or the previous run, saved last, and leave its own changes behind
for them. Instead the folder starts empty, so Bloom starts from default settings, and it is deleted
with the rest of the run. A restart keeps it, so what one launch saved the next one reads, as on a
real machine. `helpers/userSettings.ts` reads what Bloom has saved there. The fixture checks that
the Bloom it started really is using the folder, so a stale `Bloom.exe` fails the launch rather
than quietly sharing settings.

`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
Expand Down Expand Up @@ -112,6 +124,8 @@ real bug in the code under test; read the message and fix it rather than working
- `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.
- `helpers/files.ts` — `fingerprintFolder`, `isInsideFolder`, for checks against the disk.
- `helpers/userSettings.ts` — `getUserSettingsFolder`, `readSavedUserSetting`: where the Bloom
under test keeps its user settings, and what it has saved there.
- `helpers/api.ts` — `apiGet`, `apiPost`, `apiGetJson`. These run `fetch` inside the page with a
relative URL, which is not a style choice: Bloom's server rejects a `127.0.0.1` Host header, and
the CDP endpoint does not answer on `localhost`. The file explains it.
Expand Down
7 changes: 7 additions & 0 deletions src/BloomE2E/fixtures/bloomTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ export interface IBloomApp {
bloomPid: number;
/** The collection folder Bloom has open: a temp folder, never the inputs repository itself. */
collectionDir: string;
/**
* The folder this Bloom keeps its user settings in (its user.config), beside the collection in
* the temp folder, so nothing it saves reaches the developer's own Bloom or the next run. It
* starts empty, and a restart keeps it. See helpers/userSettings.ts to read what is in it.
*/
userSettingsDir: string;
/**
* Quit Bloom and start it again on the same collection folder, and return the new shell page.
*
Expand Down Expand Up @@ -256,6 +262,7 @@ export const test = base.extend<IBloomTestFixtures, IBloomWorkerFixtures>({
cdpPort: launched.cdpPort,
bloomPid: launched.bloomPid,
collectionDir: launched.collectionDir,
userSettingsDir: launched.userSettingsDir,
restart: async (betweenStopAndStart) => {
// Close the old CDP connection first: it holds a socket into the process
// that is about to be killed.
Expand Down
Loading