Add e2e test for UI language switching, including unapproved translations - #8275
Add e2e test for UI language switching, including unapproved translations#8275JohnThomson wants to merge 14 commits into
Conversation
…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>
# Conflicts: # PAPERCUTS.md
|
| 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
… 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>
|
[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. |
… 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>
|
[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. |
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>
|
[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>
JohnThomson
left a comment
There was a problem hiding this comment.
@JohnThomson made 1 comment.
Reviewable status: 0 of 13 files reviewed, 7 unresolved discussions.
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.tssurface module:tests/ui-language.spec.tslaunches 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 (theuseL10nhook,LocalizableElementcomponents, 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.tsblanks 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:
--automation, the Choose Collection dialog now opens on theBLOOM_AUTOMATION_MONITORscreen and noReactDialogsteals the user's focus — the same rulesShellandSplashScreenalready follow. Production behavior is unchanged.findBloomExepicks the newest built Bloom.exe (a staleDebug/x64build 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
masterdeliberately (test automation, an exception to the Version6.5 directive).Devin review
This change is
Devin review