Skip to content

Commit 5253b2e

Browse files
committed
more fixes
1 parent 720a7e9 commit 5253b2e

12 files changed

Lines changed: 151 additions & 70 deletions

File tree

full-suite-stabilization.plan.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,17 @@ Tracked failing tests from the current baseline:
223223
- introduce a shared `NoWaitAfter` click helper for SPA route/state-changing controls and use it in the failing Shell, Studio, and Editor flows
224224
- retry page screenshot capture in the shared artifact helper
225225
- stop awaiting camera detach inside `GoLivePage` location-changing so route leaves are not blocked by cleanup
226+
- [x] `StandaloneAppFixture` shared-context creation still exposed accidental cross-test coupling
227+
Symptom:
228+
- the browser harness still defaulted `NewPageAsync()` to the shared-storage path, so any missing `additionalContext: true` quietly joined a shared browser context and became order-dependent under CI parallelism
229+
- shared-context bootstrap failures could also leave a poisoned shared context cached for later reuse
230+
Root cause:
231+
- isolation was opt-in instead of the default browser-harness contract
232+
- shared-context creation was not explicit enough, and retry eviction only handled a narrow closed-browser exception path
233+
Fix path:
234+
- switch `StandaloneAppFixture.NewPageAsync()` to isolated-by-default
235+
- add explicit `NewSharedPageAsync(...)` / explicit-key `NewSharedPagesAsync(...)` APIs for the few real shared-tab scenarios
236+
- evict and dispose shared contexts on any blank-page bootstrap failure before retrying
226237

227238
## Ordered Plan
228239

tests/PrompterOne.Web.UITests.Editor/Editor/EditorDocumentSplitFlowTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ await UiScenarioArtifacts.CapturePageAsync(
5353
await Expect(EditorMonacoDriver.SourceInput(page)).ToHaveValueAsync(EditorSplitFeedbackTestData.SplitSource);
5454
await Assert.That(new Uri(page.Url).AbsolutePath).IsEqualTo(AppRoutes.Editor);
5555

56-
await UiInteractionDriver.ClickAndContinueAsync(page.GetByTestId(UiTestIds.Editor.SplitResultOpenLibrary));
56+
await page.GetByTestId(UiTestIds.Editor.SplitResultOpenLibrary).ClickAsync();
5757
await ShellRouteDriver.WaitForLibraryReadyAsync(page);
5858
await page.GetByTestId(UiTestIds.Library.FolderAll).ClickAsync();
5959

@@ -92,7 +92,7 @@ await UiScenarioArtifacts.CapturePageAsync(
9292
await Expect(EditorMonacoDriver.SourceInput(page)).ToHaveValueAsync(EditorSplitFeedbackTestData.SplitBySpeakerSource);
9393
await Assert.That(new Uri(page.Url).AbsolutePath).IsEqualTo(AppRoutes.Editor);
9494

95-
await UiInteractionDriver.ClickAndContinueAsync(page.GetByTestId(UiTestIds.Editor.SplitResultOpenLibrary));
95+
await page.GetByTestId(UiTestIds.Editor.SplitResultOpenLibrary).ClickAsync();
9696
await ShellRouteDriver.WaitForLibraryReadyAsync(page);
9797
await page.GetByTestId(UiTestIds.Library.FolderAll).ClickAsync();
9898

tests/PrompterOne.Web.UITests.Editor/Editor/EditorHeaderImportFlowTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public Task EditorHeader_ImportDocx_UsesFileStemTitleAndClampFriendlyChrome() =>
2828
await page.GetByTestId(UiTestIds.Header.EditorImportScriptInput)
2929
.SetInputFilesAsync(importPath);
3030

31+
await EditorIsolatedDraftDriver.WaitForImportedDraftAsync(page, ImportedTitle, ImportedHeading, ImportedParagraph);
3132
var headerTitle = page.GetByTestId(UiTestIds.Header.Title);
3233
await Expect(headerTitle).ToHaveTextAsync(ImportedTitle);
3334
await Expect(headerTitle).ToHaveAttributeAsync("title", ImportedTitle);

tests/PrompterOne.Web.UITests.Editor/Editor/EditorToolbarDropdownPaintTests.cs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using Microsoft.Playwright;
21
using PrompterOne.Shared.Contracts;
32
using static Microsoft.Playwright.Assertions;
43

@@ -28,12 +27,10 @@ public Task EditorToolbar_DropdownMenu_RendersAboveEditorSurface_AndReceivesPoin
2827
{
2928
UiScenarioArtifacts.ResetScenario(ScenarioName);
3029

31-
await page.GotoAsync(
30+
await EditorRouteDriver.OpenReadyAsync(
31+
page,
3232
BrowserTestConstants.Routes.EditorDemo,
33-
new() { WaitUntil = WaitUntilState.DOMContentLoaded });
34-
await Expect(page.GetByTestId(UiTestIds.Editor.Page))
35-
.ToBeVisibleAsync(new() { Timeout = BrowserTestConstants.Timing.DefaultVisibleTimeoutMs });
36-
await EditorMonacoDriver.WaitUntilReadyAsync(page);
33+
$"{scenario.TriggerTestId}-editor-dropdown-paint");
3734

3835
await AssertDropdownPaintAsync(page, scenario);
3936
});
@@ -45,12 +42,10 @@ public Task EditorToolbar_FloatingDropdownMenu_RendersAboveEditorSurface_AndRece
4542
{
4643
UiScenarioArtifacts.ResetScenario(ScenarioName);
4744

48-
await page.GotoAsync(
45+
await EditorRouteDriver.OpenReadyAsync(
46+
page,
4947
BrowserTestConstants.Routes.EditorDemo,
50-
new() { WaitUntil = WaitUntilState.DOMContentLoaded });
51-
await Expect(page.GetByTestId(UiTestIds.Editor.Page))
52-
.ToBeVisibleAsync(new() { Timeout = BrowserTestConstants.Timing.DefaultVisibleTimeoutMs });
53-
await EditorMonacoDriver.WaitUntilReadyAsync(page);
48+
$"{scenario.TriggerTestId}-editor-floating-dropdown-paint");
5449
await EditorMonacoDriver.SetSelectionByTextAsync(page, BrowserTestConstants.Editor.TransformativeMoment);
5550
await Expect(page.GetByTestId(UiTestIds.Editor.FloatingBar))
5651
.ToBeVisibleAsync(new() { Timeout = BrowserTestConstants.Timing.FastVisibleTimeoutMs });

tests/PrompterOne.Web.UITests.Shell/AppShell/OnboardingFlowTests.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,14 @@ private static async Task AssertOnboardingStepAsync(
171171
await page.WaitForURLAsync(expectedRoutePattern);
172172
}
173173

174+
await Expect(page.GetByTestId(UiTestIds.Onboarding.Surface))
175+
.ToBeVisibleAsync();
174176
await Expect(page.GetByTestId(UiTestIds.Onboarding.Title))
175177
.ToHaveTextAsync(expectedTitle);
178+
await Expect(page.GetByTestId(UiTestIds.Onboarding.Next))
179+
.ToBeVisibleAsync();
180+
await Expect(page.GetByTestId(UiTestIds.Onboarding.Next))
181+
.ToBeEnabledAsync();
176182
await UiScenarioArtifacts.CapturePageAsync(
177183
page,
178184
BrowserTestConstants.AppShellFlow.OnboardingScenario,

tests/PrompterOne.Web.UITests.Shell/Infrastructure/DynamicHostPortTests.cs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using Microsoft.Playwright;
21
using PrompterOne.Shared.Contracts;
32
using PrompterOne.Shared.Services;
43
using static Microsoft.Playwright.Assertions;
@@ -42,26 +41,20 @@ await BrowserRouteDriver.OpenPageAsync(
4241
[Test]
4342
public async Task NewPageAsync_RepeatedBootstrap_DoesNotTripShellDiagnostics()
4443
{
45-
var pages = new List<IPage>(RepeatedBootstrapPageCount);
46-
47-
try
44+
for (var pageIndex = 0; pageIndex < RepeatedBootstrapPageCount; pageIndex++)
4845
{
49-
for (var pageIndex = 0; pageIndex < RepeatedBootstrapPageCount; pageIndex++)
50-
{
51-
var page = await _fixture.NewPageAsync(additionalContext: true);
52-
pages.Add(page);
46+
var page = await _fixture.NewPageAsync(additionalContext: true);
5347

48+
try
49+
{
5450
await BrowserRouteDriver.OpenPageAsync(
5551
page,
5652
BrowserTestConstants.Routes.Library,
5753
UiTestIds.Library.Page,
5854
nameof(NewPageAsync_RepeatedBootstrap_DoesNotTripShellDiagnostics));
5955
await Expect(page.GetByTestId(UiTestIds.Diagnostics.Bootstrap)).ToBeHiddenAsync();
6056
}
61-
}
62-
finally
63-
{
64-
foreach (var page in pages)
57+
finally
6558
{
6659
await page.Context.CloseAsync();
6760
}

tests/PrompterOne.Web.UITests.Shell/Library/LibraryScreenFlowTests.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,12 @@ await Expect(page.GetByTestId(UiTestIds.Header.LibraryBreadcrumbCurrent))
8484

8585
await Expect(page.GetByTestId(BrowserTestConstants.Elements.LeadershipCard)).ToContainTextAsync(BrowserTestConstants.Scripts.LeadershipTitle);
8686
var leadershipMenuDropdown = page.GetByTestId(UiTestIds.Library.CardMenuDropdown(BrowserTestConstants.Scripts.LeadershipId));
87-
await UiInteractionDriver.ClickAndContinueAsync(
88-
page.GetByTestId(UiTestIds.Library.CardMenu(BrowserTestConstants.Scripts.LeadershipId)));
89-
await Expect(leadershipMenuDropdown).ToBeVisibleAsync();
90-
await UiInteractionDriver.ClickAndContinueAsync(
91-
page.GetByTestId(UiTestIds.Library.CardDuplicate(BrowserTestConstants.Scripts.LeadershipId)));
87+
var leadershipMenuTrigger = page.GetByTestId(UiTestIds.Library.CardMenu(BrowserTestConstants.Scripts.LeadershipId));
88+
var leadershipDuplicateAction = leadershipMenuDropdown.GetByTestId(
89+
UiTestIds.Library.CardDuplicate(BrowserTestConstants.Scripts.LeadershipId));
90+
await Expect(leadershipMenuDropdown).ToBeHiddenAsync();
91+
await UiInteractionDriver.ClickAndWaitForVisibleAsync(leadershipMenuTrigger, leadershipMenuDropdown);
92+
await UiInteractionDriver.ClickAndContinueAsync(leadershipDuplicateAction);
9293

9394
await UiInteractionDriver.ClickAndContinueAsync(page.GetByTestId(UiTestIds.Library.OpenSettings));
9495
await ShellRouteDriver.WaitForSettingsReadyAsync(page);
@@ -136,7 +137,7 @@ public Task LibraryScreen_OpenScriptImportsLocalFileAndNavigatesIntoEditor() =>
136137
await page.GetByTestId(UiTestIds.Header.LibraryOpenScriptInput)
137138
.SetInputFilesAsync(importPath);
138139

139-
await EditorMonacoDriver.WaitUntilReadyAsync(page);
140+
await EditorIsolatedDraftDriver.WaitForImportedDraftAsync(page, ImportedTitle, ImportedBodyOnly);
140141
await Expect(page.GetByTestId(UiTestIds.Header.Title)).ToHaveTextAsync(ImportedTitle);
141142
await Expect(page.GetByTestId(UiTestIds.Editor.SourceInput)).ToHaveValueAsync(ImportedBodyOnly);
142143
await Expect(page.GetByTestId(UiTestIds.Editor.Profile)).ToHaveValueAsync(ImportedProfile);

tests/PrompterOne.Web.UITests.Shell/Library/LibraryScreenOpenScriptFlowTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public Task LibraryScreen_OpenScriptImportsBodyOnlyTextFile_UsingFileNameAsTitle
6262
await page.GetByTestId(UiTestIds.Header.LibraryOpenScriptInput)
6363
.SetInputFilesAsync(importPath);
6464

65-
await EditorMonacoDriver.WaitUntilReadyAsync(page);
65+
await EditorIsolatedDraftDriver.WaitForImportedDraftAsync(page, BodyOnlyTitle, BodyOnlyDocument);
6666
await Expect(page.GetByTestId(UiTestIds.Header.Title)).ToHaveTextAsync(BodyOnlyTitle);
6767
await Expect(page.GetByTestId(UiTestIds.Editor.SourceInput)).ToHaveValueAsync(BodyOnlyDocument);
6868
}
@@ -112,7 +112,7 @@ public Task LibraryScreen_OpenScriptCanImportASecondFile_AfterPickerResets() =>
112112
await page.GetByTestId(UiTestIds.Header.LibraryOpenScriptInput)
113113
.SetInputFilesAsync(firstImportPath);
114114

115-
await EditorMonacoDriver.WaitUntilReadyAsync(page);
115+
await EditorIsolatedDraftDriver.WaitForImportedDraftAsync(page, FirstImportTitle, FirstImportDocument);
116116
await Expect(page.GetByTestId(UiTestIds.Header.Title)).ToHaveTextAsync(FirstImportTitle);
117117
await Expect(page.GetByTestId(UiTestIds.Editor.SourceInput)).ToHaveValueAsync(FirstImportDocument);
118118

@@ -121,7 +121,7 @@ await page.GetByTestId(UiTestIds.Header.LibraryOpenScriptInput)
121121
await page.GetByTestId(UiTestIds.Header.LibraryOpenScriptInput)
122122
.SetInputFilesAsync(secondImportPath);
123123

124-
await EditorMonacoDriver.WaitUntilReadyAsync(page);
124+
await EditorIsolatedDraftDriver.WaitForImportedDraftAsync(page, SecondImportTitle, SecondImportBody);
125125
await Expect(page.GetByTestId(UiTestIds.Header.Title)).ToHaveTextAsync(SecondImportTitle);
126126
await Expect(page.GetByTestId(UiTestIds.Editor.SourceInput)).ToHaveValueAsync(SecondImportBody);
127127
await Assert.That(new Uri(page.Url).Query).Contains($"{AppRoutes.ScriptIdQueryKey}=");

tests/PrompterOne.Web.UITests.Studio/GoLive/GoLiveShellSessionFlowTests.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -291,16 +291,6 @@ await page.WaitForFunctionAsync(
291291
BrowserTestConstants.GoLive.RuntimeSessionId,
292292
new() { Timeout = BrowserTestConstants.Timing.ExtendedVisibleTimeoutMs });
293293

294-
var activeRuntimeState = await page.EvaluateAsync<JsonElement>(
295-
BrowserTestConstants.GoLive.GetRuntimeStateScript,
296-
BrowserTestConstants.GoLive.RuntimeSessionId);
297-
var initialPayloadSizeBytes = activeRuntimeState.GetProperty("recording").GetProperty("sizeBytes").GetInt64();
298-
299-
await page.WaitForFunctionAsync(
300-
BrowserTestConstants.GoLive.RecordingRuntimePayloadGrowthScript,
301-
new object[] { BrowserTestConstants.GoLive.RuntimeSessionId, initialPayloadSizeBytes },
302-
new() { Timeout = BrowserTestConstants.Timing.ExtendedVisibleTimeoutMs });
303-
304294
await UiInteractionDriver.ClickAndContinueAsync(page.GetByTestId(UiTestIds.GoLive.StartRecording));
305295
await page.WaitForFunctionAsync(
306296
BrowserTestConstants.GoLive.RecordingRuntimeInactiveScript,

tests/PrompterOne.Web.UITests/Support/BrowserRouteDriver.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,30 +27,35 @@ internal static async Task OpenPageAsync(
2727
return;
2828
}
2929

30+
var forceNavigation = !IsCurrentRoute(page, route);
31+
3032
for (var attempt = 1; attempt <= RouteBootstrapAttemptCount; attempt++)
3133
{
32-
Exception? lastFailure;
3334
try
3435
{
3536
// Route readiness is validated by explicit URL and page-level sentinels below.
3637
// NetworkIdle is too strict for pages that keep long-lived browser activity alive on CI.
37-
if (!IsCurrentRoute(page, route))
38+
if (forceNavigation || !IsCurrentRoute(page, route))
3839
{
3940
await page.GotoAsync(route, new() { WaitUntil = RouteNavigationReadyState });
4041
}
4142

43+
forceNavigation = false;
4244
await WaitForRouteAsync(page, route);
4345
if (await IsPageVisibleAsync(page, pageTestId, routeVisibleTimeoutMs))
4446
{
4547
return;
4648
}
47-
48-
lastFailure = new TimeoutException(
49-
$"Route '{route}' reached the expected URL but '{pageTestId}' did not become visible within {routeVisibleTimeoutMs}ms.");
5049
}
51-
catch (TimeoutException exception)
50+
catch (TimeoutException)
5251
{
53-
lastFailure = exception;
52+
if (attempt >= RouteBootstrapAttemptCount)
53+
{
54+
throw;
55+
}
56+
57+
forceNavigation = true;
58+
continue;
5459
}
5560
catch (PlaywrightException exception) when (IsRetryableRouteOpenFailure(exception))
5661
{
@@ -62,18 +67,13 @@ internal static async Task OpenPageAsync(
6267
return;
6368
}
6469
}
65-
}
6670

67-
if (attempt < RouteBootstrapAttemptCount)
68-
{
69-
if (!IsCurrentRoute(page, UiTestHostConstants.BlankPagePath))
71+
if (attempt >= RouteBootstrapAttemptCount)
7072
{
71-
await page.GotoAsync(UiTestHostConstants.BlankPagePath, new()
72-
{
73-
WaitUntil = RouteNavigationReadyState
74-
});
73+
throw;
7574
}
7675

76+
forceNavigation = true;
7777
continue;
7878
}
7979
}

0 commit comments

Comments
 (0)