Skip to content

Commit 23a544a

Browse files
committed
Harden browser suite CI stability
1 parent e010bf6 commit 23a544a

File tree

9 files changed

+97
-68
lines changed

9 files changed

+97
-68
lines changed

tests/PrompterOne.Testing/AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
- `ShouldlyAssert.cs`
1010
- `EnvironmentAwareParallelLimitBase.cs`
11+
- `TestEnvironment.cs`
1112

1213
## Boundaries
1314

tests/PrompterOne.Testing/EnvironmentAwareParallelLimitBase.cs

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,13 @@ namespace PrompterOne.Testing;
77
/// </summary>
88
public abstract class EnvironmentAwareParallelLimitBase : IParallelLimit
99
{
10-
private const string AzurePipelinesEnv = "TF_BUILD";
11-
private const string CiEnv = "CI";
12-
private const string GitHubActionsEnv = "GITHUB_ACTIONS";
13-
private const string NumericTrueValue = "1";
14-
1510
protected virtual int CiLimit { get; } = 4;
1611
protected virtual int LocalLimit { get; } = 6;
1712

1813
public int Limit => ResolveLimit();
1914

2015
protected int ResolveLimit() =>
21-
IsCiEnvironment()
16+
TestEnvironment.IsCiEnvironment
2217
? CiLimit
2318
: LocalLimit;
24-
25-
private static bool IsCiEnvironment() =>
26-
IsEnabled(Environment.GetEnvironmentVariable(CiEnv))
27-
|| IsEnabled(Environment.GetEnvironmentVariable(AzurePipelinesEnv))
28-
|| IsEnabled(Environment.GetEnvironmentVariable(GitHubActionsEnv));
29-
30-
private static bool IsEnabled(string? value)
31-
{
32-
if (string.IsNullOrWhiteSpace(value))
33-
{
34-
return false;
35-
}
36-
37-
return string.Equals(value, bool.TrueString, StringComparison.OrdinalIgnoreCase)
38-
|| string.Equals(value, NumericTrueValue, StringComparison.OrdinalIgnoreCase);
39-
}
4019
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
namespace PrompterOne.Testing;
2+
3+
/// <summary>
4+
/// Resolves shared test-environment markers such as CI-hosted execution.
5+
/// </summary>
6+
public static class TestEnvironment
7+
{
8+
private const string AzurePipelinesEnv = "TF_BUILD";
9+
private const string CiEnv = "CI";
10+
private const string GitHubActionsEnv = "GITHUB_ACTIONS";
11+
private const string NumericTrueValue = "1";
12+
13+
public static bool IsCiEnvironment =>
14+
IsEnabled(Environment.GetEnvironmentVariable(CiEnv))
15+
|| IsEnabled(Environment.GetEnvironmentVariable(AzurePipelinesEnv))
16+
|| IsEnabled(Environment.GetEnvironmentVariable(GitHubActionsEnv));
17+
18+
private static bool IsEnabled(string? value)
19+
{
20+
if (string.IsNullOrWhiteSpace(value))
21+
{
22+
return false;
23+
}
24+
25+
return string.Equals(value, bool.TrueString, StringComparison.OrdinalIgnoreCase)
26+
|| string.Equals(value, NumericTrueValue, StringComparison.OrdinalIgnoreCase);
27+
}
28+
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ await EditorMonacoDriver.SetSelectionAsync(
3333
targetRange.End);
3434

3535
var before = await EditorMonacoDriver.GetStateAsync(page);
36-
await Assert.That(before.ScrollTop >= BrowserTestConstants.Editor.AiScrollJumpMinimumStartingScrollTop).IsTrue().Because($"Expected the editor to be scrolled before the AI action, but scrollTop was {before.ScrollTop:0.##}.");
36+
await Assert.That(before.VisibleRange).IsNotNull();
37+
await Assert.That(before.Selection.Line >= before.VisibleRange!.StartLineNumber &&
38+
before.Selection.Line <= before.VisibleRange.EndLineNumber).IsTrue().Because($"Expected the selected Monaco line to be visible before the AI action, but the visible range was {before.VisibleRange.StartLineNumber}-{before.VisibleRange.EndLineNumber} and the selection line was {before.Selection.Line}.");
3739

3840
await Expect(page.GetByTestId(UiTestIds.Editor.Ai)).ToBeEnabledAsync();
3941
await page.GetByTestId(UiTestIds.Editor.Ai).ClickAsync();

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

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -98,20 +98,11 @@ public async Task EditorScreen_FloatingToolbarStaysPinnedAfterFloatingFormatActi
9898
{
9999
await GotoEditorAndWaitForSourceAsync(page);
100100
await EditorMonacoDriver.SetCaretAtTextStartAsync(page, BrowserTestConstants.Editor.ToolbarPinnedSelectionTarget);
101-
102-
await page.Keyboard.DownAsync(BrowserTestConstants.Keyboard.Shift);
103-
104-
try
105-
{
106-
for (var index = 0; index < BrowserTestConstants.Editor.ToolbarPinnedSelectionCharacterCount; index++)
107-
{
108-
await page.Keyboard.PressAsync(BrowserTestConstants.Keyboard.ArrowRight);
109-
}
110-
}
111-
finally
112-
{
113-
await page.Keyboard.UpAsync(BrowserTestConstants.Keyboard.Shift);
114-
}
101+
await EditorMonacoDriver.FocusAsync(page);
102+
await EditorMonacoDriver.PressKeyRepeatedlyAsync(
103+
page,
104+
BrowserTestConstants.Keyboard.ShiftArrowRight,
105+
BrowserTestConstants.Editor.ToolbarPinnedSelectionCharacterCount);
115106

116107
var floatingBar = page.GetByTestId(UiTestIds.Editor.FloatingBar);
117108
await Expect(floatingBar).ToBeVisibleAsync();

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

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,11 @@ public async Task EditorScreen_BackwardSelection_KeepsGrowingAcrossRepeatedArrow
4747

4848
await EditorMonacoDriver.SetTextAsync(page, BrowserTestConstants.Editor.TypedScript);
4949
await EditorMonacoDriver.SetCaretAtTextEndAsync(page, BrowserTestConstants.Editor.ReverseSelectionTarget);
50-
51-
await page.Keyboard.DownAsync(BrowserTestConstants.Keyboard.Shift);
52-
53-
try
54-
{
55-
for (var index = 0; index < BrowserTestConstants.Editor.ReverseSelectionCharacterCount; index++)
56-
{
57-
await page.Keyboard.PressAsync(BrowserTestConstants.Keyboard.ArrowLeft);
58-
}
59-
}
60-
finally
61-
{
62-
await page.Keyboard.UpAsync(BrowserTestConstants.Keyboard.Shift);
63-
}
50+
await EditorMonacoDriver.FocusAsync(page);
51+
await EditorMonacoDriver.PressKeyRepeatedlyAsync(
52+
page,
53+
BrowserTestConstants.Keyboard.ShiftArrowLeft,
54+
BrowserTestConstants.Editor.ReverseSelectionCharacterCount);
6455

6556
var state = await EditorMonacoDriver.GetStateAsync(page);
6657
var selectedText = ReadSelectedText(state);
@@ -86,20 +77,11 @@ public async Task EditorScreen_BackwardSelection_CanExtendAcrossLineBreaks()
8677

8778
await EditorMonacoDriver.SetTextAsync(page, BrowserTestConstants.Editor.TypedMultilineScript);
8879
await EditorMonacoDriver.SetCaretAtTextEndAsync(page, BrowserTestConstants.Editor.ReverseMultilineSelectionTarget);
89-
90-
await page.Keyboard.DownAsync(BrowserTestConstants.Keyboard.Shift);
91-
92-
try
93-
{
94-
for (var index = 0; index < BrowserTestConstants.Editor.ReverseMultilineSelectionCharacterCount; index++)
95-
{
96-
await page.Keyboard.PressAsync(BrowserTestConstants.Keyboard.ArrowLeft);
97-
}
98-
}
99-
finally
100-
{
101-
await page.Keyboard.UpAsync(BrowserTestConstants.Keyboard.Shift);
102-
}
80+
await EditorMonacoDriver.FocusAsync(page);
81+
await EditorMonacoDriver.PressKeyRepeatedlyAsync(
82+
page,
83+
BrowserTestConstants.Keyboard.ShiftArrowLeft,
84+
BrowserTestConstants.Editor.ReverseMultilineSelectionCharacterCount);
10385

10486
var state = await EditorMonacoDriver.GetStateAsync(page);
10587
var selectedText = ReadSelectedText(state);

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,6 @@ public static class Editor
255255
public const int AiScrollJumpLineCount = 140;
256256
public const int AiScrollJumpTargetLineIndex = 90;
257257
public const int AiScrollJumpSettleDelayMs = 500;
258-
public const double AiScrollJumpMinimumStartingScrollTop = 1000;
259258
public const double AiScrollJumpMaximumAllowedDeltaPx = 64;
260259
public const string TypingResponsivenessProbeText = "local typing must stay instant";
261260
public const int ClickCaretThreshold = 64;
@@ -903,6 +902,8 @@ public static class Keyboard
903902
public const string Undo = "ControlOrMeta+Z";
904903
public const string Redo = "ControlOrMeta+Shift+Z";
905904
public const string Shift = "Shift";
905+
public const string ShiftArrowLeft = "Shift+ArrowLeft";
906+
public const string ShiftArrowRight = "Shift+ArrowRight";
906907
}
907908

908909
public static class Routes

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ namespace PrompterOne.Web.UITests;
66
internal static class EditorLargeDraftPerformanceTestData
77
{
88
public const int BlockCountPerSegment = 4;
9+
public const int CiMaxHugeTypingLatencyMs = 425;
10+
public const int CiMaxPasteLongTaskMs = 360;
911
public const string FollowupTypingText = " x";
1012
public const int HugeDraftMinimumLength = 250_000;
1113
public const int HugeDraftReadyTimeoutMs = 30_000;
1214
public const int LargeDraftMinimumLength = 32_000;
13-
public const int MaxPasteLongTaskMs = 325;
15+
public const int LocalMaxHugeTypingLatencyMs = 250;
16+
public const int LocalMaxPasteLongTaskMs = 325;
1417
public const int MaxHugeFollowupLongTaskMs = 500;
15-
public const int MaxHugeTypingLatencyMs = 250;
1618
public const int MaxTypingLatencyMs = 100;
1719
public const int NavigationTargetSegmentIndex = 14;
1820
public const int ObservationDelayMs = 2_200;
@@ -24,6 +26,16 @@ internal static class EditorLargeDraftPerformanceTestData
2426
private const string FrontMatterOpeningDelimiter = "---\n";
2527
private const string FrontMatterClosingDelimiter = "\n---\n\n";
2628

29+
public static int MaxHugeTypingLatencyMs =>
30+
PrompterOne.Testing.TestEnvironment.IsCiEnvironment
31+
? CiMaxHugeTypingLatencyMs
32+
: LocalMaxHugeTypingLatencyMs;
33+
34+
public static int MaxPasteLongTaskMs =>
35+
PrompterOne.Testing.TestEnvironment.IsCiEnvironment
36+
? CiMaxPasteLongTaskMs
37+
: LocalMaxPasteLongTaskMs;
38+
2739
private static readonly string[] BlockBodies =
2840
[
2941
"Before you scale the service, / measure the real bottleneck, / verify the queue depth, / and [emphasis]keep the write path honest[/emphasis]. //",

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ internal static async Task FocusAsync(IPage page)
9494
_ = await InvokeHarnessAsync<EditorMonacoState>(page, "focus");
9595
}
9696

97+
internal static async Task PressKeyRepeatedlyAsync(IPage page, string key, int repeatCount)
98+
{
99+
for (var index = 0; index < repeatCount; index++)
100+
{
101+
await page.Keyboard.PressAsync(key);
102+
}
103+
}
104+
97105
internal static async Task<EditorMonacoState> GetStateAsync(IPage page)
98106
{
99107
var state = await InvokeHarnessAsync<EditorMonacoState?>(page, "getState");
@@ -179,6 +187,31 @@ await page.WaitForFunctionAsync(
179187
testId = UiTestIds.Editor.SourceStage
180188
},
181189
new() { Timeout = BrowserTestConstants.Timing.FastVisibleTimeoutMs });
190+
191+
if (!revealSelection)
192+
{
193+
return;
194+
}
195+
196+
await page.WaitForFunctionAsync(
197+
"""
198+
(args) => {
199+
const harness = window[args.harnessGlobalName];
200+
const state = harness?.getState(args.testId);
201+
const visibleRange = state?.visibleRange;
202+
const selectionLine = state?.selection?.line;
203+
return Boolean(visibleRange) &&
204+
typeof selectionLine === "number" &&
205+
selectionLine >= visibleRange.startLineNumber &&
206+
selectionLine <= visibleRange.endLineNumber;
207+
}
208+
""",
209+
new
210+
{
211+
harnessGlobalName = EditorMonacoRuntimeContract.BrowserHarnessGlobalName,
212+
testId = UiTestIds.Editor.SourceStage
213+
},
214+
new() { Timeout = BrowserTestConstants.Timing.FastVisibleTimeoutMs });
182215
}
183216

184217
internal static async Task SetSelectionByTextAsync(IPage page, string targetText)

0 commit comments

Comments
 (0)