Summary#
Ui.HoldServer() makes the server's answers to the page wait. The page still opens and paints — its frame, its
skeleton { }, its static parts — but its rows and the results of its server calls do not arrive until the test says
Ui.ReleaseServer(). In between, every assertion reads the page exactly as a person on a slow connection sees it.
Signature#
Ui.HoldServer(); // from now on, answers to the page's reads wait
Ui.ReleaseServer(); // let them through, and wait for the page to finish with themDescription#
Why a test needs it#
Every UI verb waits for the page to finish before it returns, which is what makes an assertion trustworthy — and is
also why a loading state could never be tested: by the time Assert.Visible ran, the skeleton it meant to check had
already given way to the data. Holding the server is how the moment in between becomes a screen a test can stand on.
What is held, and what is not#
| Held until released | Never held |
|---|---|
| a page's ROWS — the second half of the answer that opens a page, after its layout | the page itself: its tree, its layout, the app's bundle and its controls |
the answer to a server call — an on mount that reads a report, a live var that calls a function, an action's function | a live stream that stays open (a .Listen(), a subscription) |
| the answer to a query a page asks for later (a refetch, a search) |
So a held page paints — that is the point — and stops where its data would have started.
How the other verbs behave while it is held#
Ui.Visitand a navigatingUi.Clickreturn once the page has painted and is waiting, instead of waiting for data that cannot come. That includes a page inside a[Layout]that waits on its OWN reads — alive varserver call with noskeleton { }holds its whole body,Outlet()included, until the answer lands — so the visit returns on the layout's stand-in and the page arrives in its outlet on release.- An action stays in flight. A button whose action is waiting on a held answer shows its busy state and
save.Pendingreadstrue(see Pending), so the in-flight state of an action is testable the same way. - An assertion does not wait out its bound for held work.
Assert.Hidden("Walnut flat")answers at once: the row is not there, and nothing will put it there until the release. - It is sticky. It stays on across
Ui.VisituntilUi.ReleaseServer(), as a slow network would.
Releasing#
Ui.ReleaseServer() lets every held answer through and then waits for the page as it does after a click: the reads
land, the skeleton gives way, the actions that were waiting finish. Releasing when nothing is held is not an error.
⚠ Assert what the stand-in SAYS, not how it is built. A skeleton's grey blocks have no words; what a test can read
is the frame the stand-in draws for real (a title, a card's heading) and a [[ui-skeleton#kit-parts|LoadingNote]]'s
"Loading…". After the release, assert the content — and that the note is gone.
It runs the same in both renderers: osy test (in-process) and osy test --pixels (a real browser).
Examples#
[Role] enum DeskRole { Member }
[Principal] entity Keeper {
[Required, Unique, MaxLength(200)] string Email;
security { allow read when IsAuthenticated; }
}
[RoleGrant] entity DeskGrant { Keeper Grantee; [Required] DeskRole Level; }
policy IsMember => DeskGrant.Any(g => g.Grantee == user && g.Level == DeskRole.Member);
entity Comet {
[Required, MaxLength(80)] string Name;
security { allow read, create when IsMember; }
}
public sealed class SkyTally { public int Comets; }
SkyTally CountTheSky() { return new SkyTally { Comets = Comet.Count() }; }
[Route("/sky")] [Authorize(IsMember)] [Render(CSR)]
component SkyPage() {
SkyTally tally = new SkyTally();
on mount { tally = CountTheSky(); }
skeleton { Stack(gap: 3) { Text("Tonight's sky"); Text("Loading…"); } }
render { Stack(gap: 3) { Text("Tonight's sky"); Text($"{tally.Comets} comets"); } }
}
[TestFixture]
void TwoComets() {
var k = new Keeper { Email = "[email protected]" };
new DeskGrant { Grantee = k, Level = DeskRole.Member };
new Comet { Name = "Halley" };
new Comet { Name = "Encke" };
}
principal Kim => Keeper.Single(k => k.Email == "[email protected]");
[Test(TwoComets)]
void the_sky_shows_its_shape_while_it_is_counted() {
Ui.SignInAs(Kim);
Ui.HoldServer();
Ui.Visit("/sky");
Assert.Visible("Loading…");
Assert.Hidden("2 comets");
Ui.ReleaseServer();
Assert.Visible("2 comets");
Assert.Hidden("Loading…");
}See also#
- skeleton — the stand-in a page draws while its first read is out, and the kit's parts for one
- Pending — an action's busy state, which a held server keeps on
- Ui — drive the app's UI from a test — the other UI verbs, and how an assertion waits for a settled screen
- Ui.Tick — the same idea for a poll: make the moment happen when the test says so