Skip to content

Add e2e test for UI language switching, including unapproved translations - #8275

Open
JohnThomson wants to merge 14 commits into
masterfrom
automateTests
Open

Add e2e test for UI language switching, including unapproved translations#8275
JohnThomson wants to merge 14 commits into
masterfrom
automateTests

Conversation

@JohnThomson

@JohnThomson JohnThomson commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Problem. Nothing automatically verifies that changing Bloom's UI language actually updates the UI, even though localized strings reach the web UI through several independent mechanisms, any of which can silently break. The "Show translations which have not been approved yet" setting, and the language control's second home in the Choose Collection dialog, had no automated coverage at all. This is manual test case "change UI language repeatedly" (Test Case ID 69).

Fix. Two new BloomE2E journey tests, with all their mechanics in a new helpers/uiLanguage.ts surface module:

  • tests/ui-language.spec.ts launches Bloom on a collection created for the test, makes a book, and drives the real language menu through English → French → Spanish → Turkish, verifying after each switch one visible string from each localization pathway (the useL10n hook, LocalizableElement components, the top-bar <Span>, and C#-localized data delivered via the API). It then proves the unapproved-translations gate both ways with Turkish — translated on Crowdin but never approved, unchanged since 2018: setting off → English fallbacks; setting on → Turkish text.
  • tests/ui-language-chooser.spec.ts blanks the MRU (backed up and spliced back afterward) so Bloom starts in the Choose Collection dialog, switches that dialog to French through its own language menu, verifies the dialog's strings change, then opens a collection and verifies the whole workspace comes up in French.

Both tests record the developer's machine-wide language settings and restore them even on failure.

Supporting changes the tests needed:

  • In e2e mode only, the unapproved-translations toggle no longer makes Bloom restart itself (the self-restart spawns an instance the fixture cannot track); the fixture's restart supplies that. Under --automation, the Choose Collection dialog now opens on the BLOOM_AUTOMATION_MONITOR screen and no ReactDialog steals the user's focus — the same rules Shell and SplashScreen already follow. Production behavior is unchanged.
  • Fixture hardening: findBloomExe picks the newest built Bloom.exe (a stale Debug/x64 build silently ran the suite against two-week-old code); reattachToShell() reconnects over CDP after Bloom reopens its project (an old connection never sees the new page); API calls retry the transient errors a shell-document reload causes (GETs only — a lost POST reply is tolerated at the one call site that races a reload, and confirmed by effect); the collection-ready poll tolerates the 404 window while a project reopens.

Targets master deliberately (test automation, an exception to the Version6.5 directive).


Devin review


This change is Reviewable

Devin review

JohnThomson and others added 2 commits September 1, 2026 16:56
…ions

New bloom-exe CDP test (bloom-exe-ui-language.uitest.ts) that attaches to a
running Bloom, switches the UI language through the real menu (en, fr, es, tr),
and after each switch verifies one visible string from each mechanism that puts
localized text into the web UI: the useL10n hook, LocalizableElement components,
the top-bar Span, and C#-localized data delivered via the API. It then turns on
"Show translations which have not been approved yet" (which restarts Bloom) and
verifies that Turkish - translated but never approved on Crowdin, unchanged
since 2018 - falls back to English with the setting off and shows Turkish with
it on. The test records the developer's language/setting state, normalizes to
English/approved-only, and restores the recorded state even on failure.

Supporting changes in bloomExeCdp.ts: export cdpEndpoints, add bloomApiUrl(),
and add discoverLauncherPorts(), which asks the ./go.sh launcher's control
server for Bloom's current HTTP/CDP ports - they are not stable across the
restarts this test triggers. Also logs a papercut about documenting that.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds end-to-end coverage for workspace and startup-dialog UI-language switching, including unapproved translations.

  • Adds reusable Playwright helpers for changing and validating UI languages.
  • Adds chooser-mode launch, CDP reattachment, and transient reload handling.
  • Adjusts automation-only window and restart behavior while preserving production behavior.

Important Files Changed

Filename Overview
src/BloomE2E/fixtures/launchBloom.ts Adds chooser-mode launch, profile backup/restoration, process discovery, and cleanup; no follow-up-eligible issue was available to publish.
src/BloomE2E/fixtures/bloomTest.ts Extends the Playwright fixture with separate collection and chooser modes plus CDP reconnection support.
src/BloomE2E/helpers/uiLanguage.ts Adds language-menu automation, localization assertions, restart handling, and settings restoration helpers.
src/BloomE2E/helpers/api.ts Adds bounded retries for idempotent GET requests interrupted by shell-document reloads.
src/BloomE2E/tests/ui-language.spec.ts Covers repeated workspace language switching and both states of the unapproved-translation gate.
src/BloomE2E/tests/ui-language-chooser.spec.ts Covers language switching in the startup chooser and propagation into the opened workspace.
src/BloomExe/Program.cs Positions the chooser on the automation monitor and suppresses focus stealing only during automation.
src/BloomExe/Workspace/WorkspaceView.cs Prevents the production self-restart during E2E runs so the fixture can perform a tracked restart.
src/BloomExe/MiscUI/ReactDialog.cs Makes React dialogs open without activation during automation runs.

Reviews (2): Last reviewed commit: "Restore the papercut heading an earlier ..." | Re-trigger Greptile

JohnThomson and others added 2 commits September 1, 2026 18:30
… 69)

Replaces the bloom-exe CDP version (which attached to the developer's own
running Bloom) with tests/ui-language.spec.ts in src/BloomE2E, per the
add-e2e-test skill: the test launches its own Bloom on a collection created
for it, drives the real UI-language menu through en/fr/es/tr verifying one
visible string per localization mechanism after each switch, and proves the
"Show translations which have not been approved yet" gate both ways with
Turkish (translated on Crowdin since 2018, never approved). All the how-to
lives in a new helpers/uiLanguage.ts surface module, with the UI routes and
the API setup/cleanup route side by side.

Supporting changes, each needed to make that work:

- WorkspaceView.ToggleShowingOnlyApprovedTranslations no longer restarts
  Bloom in e2e mode: the self-restart would relaunch without the --e2e /
  --automation flags and the collection argument, giving the fixture an
  instance it cannot track. The e2e fixture provides the restart instead.
- bloomTest gains reattachToShell() for actions that make Bloom reopen its
  project in the same process (a language change does): it reconnects over
  CDP, because a connection from before the reopen never sees the new page.
- launchBloom.findBloomExe now picks the most recently built Bloom.exe
  instead of the first in a fixed order; a stale Debug/x64 build silently
  ran a whole suite against two-week-old code.
- helpers/api.ts retries briefly when an evaluate dies with "Execution
  context was destroyed": Bloom reloads the shell document when switching
  workspace tabs, and any in-flight API call can lose that race.
- collection.waitForCollectionReady tolerates the e2e endpoints briefly
  answering 404 while the project reopens.

The test records the developer's machine-wide UI language and
unapproved-translations settings and restores them even on failure, in a
way that cannot mask the error that actually failed the test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r their own result

waitForCollectionReady now swallows only the 404 that means the project-scoped
e2e endpoints are briefly unregistered during a reopen; a closed page or dead
server fails immediately instead of becoming a generic timeout. And
setShowUnapprovedTranslations waits for the restarted Bloom to be usable
(collection loaded, Collections tab active) before returning, so callers no
longer add their own waits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@JohnThomson

Copy link
Copy Markdown
Contributor Author

[Claude Fable 5] Consulted Devin on 2026-09-01 up to commit 3bd777906465e0b39f5334498052f47a5b46939c.

Final round: no bugs and no investigate flags against the current code; two informational notes acknowledging fixes made during the run (bounded 404 handling in waitForCollectionReady; setShowUnapprovedTranslations now waiting for its own result). Earlier rounds' findings (2 bugs, 3 flags) all targeted the earlier bloom-exe-suite version of this test, which the PR no longer contains after the rework into src/BloomE2E: "Hidden original languages stay unrestored" is fixed by the threshold-aware ordering in helpers/uiLanguage.ts setUiStateViaApi; "Language restoration changes profile behavior" (OS-language-following cannot be restored exactly) is recorded as a known limitation in the header of tests/ui-language.spec.ts; and the three flags (manual-only, selectors outside helpers, fixed sleeps) are resolved by the rework itself. CI (pr-automation) green.

JohnThomson and others added 4 commits September 2, 2026 09:58
… dialog

The other place the language control lives (Test Case ID 69's startup half):
tests/ui-language-chooser.spec.ts launches Bloom with no collection to reopen,
lands in the Choose Collection dialog, switches the language there through the
real menu, verifies the dialog's own strings change (its title, the Create New
Collection button, and the language button's label - one per localization
pathway the dialog uses), then opens a collection and verifies the whole
workspace comes up in that language.

Reaching the chooser needs an empty MRU list, which lives in the developer's
machine-wide user.config - so launchBloomIntoChooser backs the file up
byte-for-byte, blanks the MRU (and normalizes the two UI-language settings the
assertions assume), and restores the original bytes when it stops, even on an
aborted run. Choosing a language rebuilds the dialog in a new WebView2, so the
helpers re-attach over a fresh CDP connection, the same lesson the workspace
version of this test taught.

Supporting changes:
- Under --automation, the Choose Collection dialog now opens on the
  BLOOM_AUTOMATION_MONITOR screen and never steals the user's focus
  (Program.ChooseACollection skips BringToFrontWhenShown; ReactDialog gains the
  same ShowWithoutActivation override Shell and SplashScreen have).
- openUiLanguageMenu presses Escape only when a menu is actually open: an
  unconditional Escape closes the chooser dialog itself, which quits Bloom.
- helpers/api.ts retries reload-transient errors ("Execution context was
  destroyed", "Failed to fetch") for GETs only; POSTs must not repeat a
  possibly-processed action, so the one call site that knowingly races a shell
  reload (makeFromSelectedBook) tolerates the lost reply and confirms the
  effect instead.
- expectUiStrings' Edit-tab entry is now optional, for collections with no
  selected book (the top bar hides the tab).
- Recorded fresh evidence on publish-text-languages' known-flake test: the
  failing assertion is French staying required after the collection rewrite,
  pointing at the previous test's content-language change not being persisted
  before restart()'s hard kill. Not addressed here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ncher

Three Devin findings on launchBloomIntoChooser, each real:
- stop() now splices only the settings the test disturbed (MRU, UI language,
  unapproved translations) back into user.config instead of restoring the
  whole original file, so concurrent saves from the developer's own Bloom
  survive the test.
- findUserConfig now picks the profile folder matching BloomExe.csproj's
  Version - the profile of any Bloom built from this repo - rather than
  whichever profile was written most recently.
- Successor-process discovery now also requires the instance to come from our
  own Bloom.exe, so a failed startup cannot adopt (and later kill) an
  unrelated Bloom that happens to be showing its own chooser.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Devin caught that restoreDisturbedSettings spliced back the UI language but
not UserInterfaceLanguageSetExplicitly, so a profile that had been following
the operating-system language would come out pinned to one language. Also
logs a papercut about BloomE2E failing opaquely when output/browser predates
the checked-out front-end code, which bit twice today.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread src/BloomExe/Program.cs
Comment thread src/BloomE2E/tests/ui-language-chooser.spec.ts Outdated
Comment thread src/BloomE2E/fixtures/launchBloom.ts
@JohnThomson

Copy link
Copy Markdown
Contributor Author

[Claude Fable 5] Consulted Devin on 2026-09-02 up to commit 75554f4.

Final round: no bugs against the current code. Three Devin findings from this branch's later rounds were real and are fixed in it (surgical settings restore that preserves concurrent saves, profile selection by the exe's version, successor discovery restricted to our own exe, and restoring the explicitly-chosen-language flag). Three investigate flags were mirrored as review threads with their outcomes recorded and resolved: the C# indentation claim (refuted - CSharpier reports the files clean), the chooser test's reduced diagnostics (documented trade-off), and executable-timestamp freshness (acknowledged limitation, noted in PAPERCUTS.md). Two informational notes acknowledge earlier fixes. Remaining entries in Devin's payload carry pre-rework commit shas against a deleted file; their substance is addressed as recorded in the previous consultation log. CI (pr-automation) green.

JohnThomson and others added 5 commits September 2, 2026 14:53
The Choose Collection dialog test no longer hand-rolls its lifecycle with raw
Playwright: bloomTest gains a startAtChooser worker option that launches Bloom
with no collection via launchBloomIntoChooser, and a chooserApp fixture typed
distinctly from bloomApp. The two collection roles John identified get distinct
homes: collectionDir stays "the collection Bloom has open" (collection mode
only), while chooserApp.collectionToOpen is "the collection card the test can
click" - the argument openCollectionFromChooser posts, exactly what clicking
that card posts. Using the wrong fixture for the launch mode fails immediately
with a message naming the right one, instead of silently launching a second
Bloom.

The big win is error handling: the problem-dialog watcher and the automatic
failOnBloomProblem fixture now cover chooser tests too - they only ever needed
a live CDP connection, which the shared _launchedApp provides in both modes.
The chooser helpers take chooserApp and keep its page current across the
dialog rebuilds, so their signatures now mirror the workspace helpers', and
the spec shrinks to pure behavior. The startAtChooser doc comment carries the
warning that this launch mode edits (and restores) the developer's
machine-wide user.config.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three more Devin catches on launchBloomIntoChooser, each real:

- On a machine with no Bloom profile at all (fresh CI), there was nothing to
  back up, so the profile the launched Bloom created - test language, a temp
  collection in the MRU - survived the run. Restore now deletes a profile
  that did not exist before.
- A launch that threw after blanking the MRU but before discovery armed its
  cleanup (findBloomExe throwing on an unbuilt machine, say) left the
  developer's profile blanked. The whole launch now runs inside a guard that
  restores the profile and deletes the temp collection on any throw.
- The splice-restore now puts back all the disturbed settings or none: a
  partial splice (some landmark missing from the rewritten file) falls back
  to restoring the full original, instead of leaving the unmatched test
  values behind.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…red profile

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A killed runner during the launch/discovery window (up to two minutes) left
the profile un-restored because the process-exit hook was only armed once
discovery finished. It now arms right after the profile is modified, with the
pid list filling in as processes become known, and a thrown launch cleans up
through the same path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread src/BloomE2E/fixtures/launchBloom.ts
Comment thread src/BloomExe/MiscUI/ReactDialog.cs
Comment thread src/BloomE2E/fixtures/launchBloom.ts
@JohnThomson

Copy link
Copy Markdown
Contributor Author

[Claude Fable 5] Consulted Devin on 2026-09-02 up to commit d3ca8d8.

Across the fixture-enhancement rounds Devin raised six findings on current code; four are fixed in the branch (fresh-machine profile residue, restore lost on a throw before cleanup armed, all-or-nothing settings splice, cleanup hook now armed before the discovery window), and two are recorded as review threads: the splice's millisecond read-modify-write window (acknowledged residual, resolved) and ShowWithoutActivation applying to all dialogs under --automation (intended design, resolved). One thread is deliberately LEFT OPEN for the developer: successor discovery could adopt and later kill a developer's own Bloom from this same worktree if it sits at its Choose Collection dialog during a failed test launch. Recurring stale entries against the deleted bloom-exe test file remain superseded per earlier logs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread src/BloomE2E/fixtures/launchBloom.ts

@JohnThomson JohnThomson left a comment

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.

@JohnThomson made 1 comment.
Reviewable status: 0 of 13 files reviewed, 7 unresolved discussions.

Comment thread src/BloomE2E/fixtures/launchBloom.ts
@JohnThomson
JohnThomson marked this pull request as ready for review September 2, 2026 21:13
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.

2 participants