feat: submit account flows structurally, not by English caption - #13
Merged
Merged
Conversation
E2ETestBase located the login and registration submit controls by their visible English text, so the helpers could not drive a page rendered in another language. Writing German E2E coverage for finance-app, a test set Accept-Language: de, called LoginAsAdminAsync(), and hung until Playwright's 30s timeout waiting for a button named "Log in". Both flows now go through ClickAccountSubmitAsync, which finds the control by type="submit" instead. The selector is page-wide rather than the tempting form-scoped variant, and that is load-bearing: Login.razor renders its submit button *outside* the form, in the action bar beside the passkey control, wired up by the HTML form="login-form" attribute, while the form itself comes from the nested LoginForm component. `form button[type=submit]` matches nothing there. Register.razor does have its button inside the form, so checking that page alone would have given the wrong answer. A guard test pins the form-scoped variant matching zero elements on the login shape. Page-wide is unambiguous: every account page in the identity package renders exactly one submit control, the passkey button is type="button", and the language switcher renders anchors. A caption parameter was the alternative and lost. The rest of these helpers is already structural — the hard-coded /Account/Login path, the [name='Email'] field lookups — so an option would only have handed every consumer a knob to keep in sync with a translation file. Source-compatible: additions only, no signature changed. AngleSharp becomes a direct test-only reference. It was already in the graph transitively via bUnit at the same 1.7.0, so the lockfile moves are Transitive -> CentralTransitive with no version drift. Closes #12
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
E2ETestBase's account helpers located their submit control by visibleEnglish text, so they could not drive a login or registration page rendered
in another language.
It surfaced writing German E2E coverage for finance-app: a test set
Accept-Language: de, calledLoginAsAdminAsync(), and hung until Playwright's30s timeout waiting for a button named "Log in". finance-app currently works
around it by logging in under English and switching culture afterwards.
Both flows now submit through
ClickAccountSubmitAsync→PageExtensions.ClickSubmitAsync, which finds the control bytype="submit".Why the selector is page-wide, and why that is load-bearing
The obvious form is
form button[type=submit]. On/Account/Loginit matchesnothing.
Login.razor:30-33renders the submit button outside the form — in the actionbar so it can sit beside the passkey button — wired up by the HTML
form="login-form"attribute, while the form itself comes from the nestedLoginForm.razor:10(<RadzenTemplateForm … id="login-form">).Register.razor:49does have its button inside its form, so checking thatpage alone would have produced the wrong answer and a login helper that hangs.
FormScopedSubmitSelector_LoginPageMarkup_MatchesNothingpins this, so a future"tightening" of the selector fails here rather than in a consumer's CI.
Page-wide is unambiguous anyway: every account page in the identity package
renders exactly one
ButtonType.Submit;PasskeySubmit.razor:5istype="button"; the sign-in handoff form has no button; the language switcherrenders plain anchors.
Why not a caption parameter
Backward compatible, but self-defeating. The rest of these helpers is already
structural —
LoginAsynchard-codes/Account/Loginand fills[name='Email']/
[name='NewPassword'], which only work against this one package's markupregardless. The submit click was the last caption-based step; an option would
have handed every consumer a knob to keep in sync with a translation file, for
a page with exactly one submit control.
Escape hatch instead of a knob:
ClickSubmitAsynctakes an optional CSSscope, andClickAccountSubmitAsyncisvirtual.Compatibility
Source-compatible.
LoginAsAdminAsync(),LoginAsync,RegisterAsync,ClickButtonAsynckeep their signatures; nothing removed or made stricter.Additions only. Release
v1.3.0after merge (current release is v1.2.0).Tests, and their honest limit
10 new tests pin
SubmitControlSelectoragainst AngleSharp-parsed markuptranscribed from the identity pages' structure — detached submit,
type="button"passkey sibling, button-less handoff form, anchor switcher —with German captions in the fixtures.
What this does not prove: it exercises AngleSharp's CSS engine, not
Playwright's, against transcribed rather than captured HTML. It validates the
selector's shape decisions, not that the real page loads. A real localized login
cannot be rendered here —
/Account/Loginlives inAndreGoepel.Marten.Identity.Blazor, which consumes this package, soreferencing it would point the dependency arrow backwards. The live proof stays
in each consuming repo's
*.E2ETests, asCLAUDE.mdalready says.AngleSharp becomes a direct test-only reference. It was already in the graph
transitively via bUnit at the same 1.7.0, so the lockfile moves are
Transitive→CentralTransitivewith no version drift, anddotnet restore --locked-modepasses.Follow-up (not in this PR)
finance-app's
LocalizationTestscan drop its workaround once it takes 1.3.0 —inverting to
UseCultureAsync("de")thenLoginAsAdminAsync().ProvisionAdminAsyncruns in its own throwaway context with no locale set, soProvisionAdminButtonText = "Create admin"keeps matching. That caption stayscaption-based deliberately: the Setup page belongs to each host app, not to the
identity package.
Verification
dotnet build— 0 errors, 0 warningsdotnet csharpier check .clean ·dotnet restore --locked-modeclean ·dotnet list package --vulnerable --include-transitivecleanCloses #12