Skip to content

Commit f8caff9

Browse files
committed
Stabilize browser CI timing probes
1 parent 617f15d commit f8caff9

5 files changed

Lines changed: 73 additions & 13 deletions

File tree

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,10 @@ public async Task EditorScreen_DoesNotRenderLegacyStructureInspectorPanel()
288288

289289
try
290290
{
291-
await EditorIsolatedDraftDriver.CreateSeededDraftAsync(page, BrowserTestConstants.Scripts.QuantumId);
291+
await EditorIsolatedDraftDriver.CreateSeededDraftAsync(
292+
page,
293+
BrowserTestConstants.Scripts.QuantumId,
294+
waitForPersistedRoute: false);
292295
await Expect(page.GetByTestId(UiTestIds.Editor.ActiveSegmentName)).ToHaveCountAsync(0);
293296
await Expect(page.GetByTestId(UiTestIds.Editor.ActiveBlockName)).ToHaveCountAsync(0);
294297
await Expect(page.GetByTestId(UiTestIds.Editor.SegmentNavigation(0))).ToContainTextAsync("Introduction");

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public async Task EditorScreen_NewDraftDoesNotReplaceRouteWhileTypingIsStillSett
158158
await page.Keyboard.TypeAsync(BrowserTestConstants.Editor.SecondProbeCharacter);
159159
await Expect(EditorMonacoDriver.SourceInput(page)).ToHaveValueAsync(BrowserTestConstants.Editor.NewDraftProbeText);
160160

161-
await page.WaitForTimeoutAsync(BrowserTestConstants.Timing.NewDraftPersistSettleDelayMs);
161+
await EditorIsolatedDraftDriver.WaitForAssignedScriptRouteAsync(page);
162162

163163
var currentUri = new Uri(page.Url);
164164
await Assert.That(currentUri.AbsolutePath).IsEqualTo(BrowserTestConstants.Routes.Editor);

tests/PrompterOne.Web.UITests.Reader/Reader/ReaderPlaybackTimingTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ await Expect(playToggle)
5252
await Assert.That(samples.Select(sample => sample.EffectiveWpm).ToArray()).IsEquivalentTo(TeleprompterEffectiveWpmSequence, CollectionOrdering.Matching);
5353

5454
var timingTolerance = BrowserTestConstants.ReaderTiming.TeleprompterTimingToleranceMs
55-
+ BrowserTestConstants.ReaderTiming.CapturePollIntervalMs;
55+
+ BrowserTestConstants.ReaderTiming.CapturePollIntervalMs * 2;
5656

5757
for (var sampleIndex = 1; sampleIndex < samples.Length; sampleIndex++)
5858
{

tests/PrompterOne.Web.UITests.Reader/Teleprompter/TeleprompterAlignmentTooltipFlowTests.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public sealed class TeleprompterAlignmentTooltipFlowTests(StandaloneAppFixture f
1212
private const string LeftRailTooltipStep = "01-left-rail-tooltip";
1313
private const string RightRailTooltipScenario = "teleprompter-alignment-tooltips-right";
1414
private const string RightRailTooltipStep = "02-right-rail-tooltip";
15+
private const int RevealProbeSchedulerSlackPolls = 4;
1516

1617
private readonly record struct ElementBounds(double Left, double Top, double Right, double Bottom);
1718

@@ -33,9 +34,7 @@ await StartTooltipRevealDelayProbeAsync(
3334
UiTestIds.Teleprompter.RailTooltip(UiTestIds.Teleprompter.AlignmentTooltipJustifyKey));
3435
await trigger.HoverAsync();
3536
var revealDelayMs = await ReadTooltipRevealDelayAsync(page);
36-
await Assert.That(revealDelayMs).IsBetween(
37-
BrowserTestConstants.TeleprompterFlow.TooltipEarlyCheckDelayMs - BrowserTestConstants.Timing.DiagnosticPollDelayMs,
38-
BrowserTestConstants.TeleprompterFlow.TooltipSettleDelayMs + BrowserTestConstants.TeleprompterFlow.TooltipRevealTimingSlackMs);
37+
await AssertTooltipRevealDelayWithinBudgetAsync(revealDelayMs);
3938
await Expect(tooltip).ToBeVisibleAsync();
4039
await Expect(tooltip).ToHaveTextAsync(BrowserTestConstants.TeleprompterFlow.AlignmentJustifyTooltipText);
4140

@@ -68,9 +67,7 @@ await StartTooltipRevealDelayProbeAsync(
6867
UiTestIds.Teleprompter.RailTooltip(UiTestIds.Teleprompter.AlignmentTooltipWidthKey));
6968
await trigger.HoverAsync();
7069
var revealDelayMs = await ReadTooltipRevealDelayAsync(page);
71-
await Assert.That(revealDelayMs).IsBetween(
72-
BrowserTestConstants.TeleprompterFlow.TooltipEarlyCheckDelayMs - BrowserTestConstants.Timing.DiagnosticPollDelayMs,
73-
BrowserTestConstants.TeleprompterFlow.TooltipSettleDelayMs + BrowserTestConstants.TeleprompterFlow.TooltipRevealTimingSlackMs);
70+
await AssertTooltipRevealDelayWithinBudgetAsync(revealDelayMs);
7471
await Expect(tooltip).ToBeVisibleAsync();
7572
await Expect(tooltip).ToHaveTextAsync(BrowserTestConstants.TeleprompterFlow.WidthSliderTooltipText);
7673

@@ -92,6 +89,13 @@ private static double CalculateIntersectionArea(ElementBounds left, ElementBound
9289
return overlapWidth * overlapHeight;
9390
}
9491

92+
private static async Task AssertTooltipRevealDelayWithinBudgetAsync(int revealDelayMs) =>
93+
await Assert.That(revealDelayMs).IsBetween(
94+
BrowserTestConstants.TeleprompterFlow.TooltipEarlyCheckDelayMs - BrowserTestConstants.Timing.DiagnosticPollDelayMs,
95+
BrowserTestConstants.TeleprompterFlow.TooltipSettleDelayMs
96+
+ BrowserTestConstants.TeleprompterFlow.TooltipRevealTimingSlackMs
97+
+ BrowserTestConstants.Timing.DiagnosticPollDelayMs * RevealProbeSchedulerSlackPolls);
98+
9599
private static async Task OpenTeleprompterAsync(IPage page)
96100
{
97101
await ReaderRouteDriver.OpenTeleprompterAsync(page, BrowserTestConstants.Routes.TeleprompterDemo);

tests/PrompterOne.Web.UITests/Media/recording-file-harness.js

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
const minimumVisiblePixelCount = 16;
99
const visibleVideoProbeTimeoutMs = 1500;
1010
const visibleVideoPollDelayMs = 100;
11+
const visibleVideoSeekSamples = Object.freeze([0, 0.2, 0.45, 0.7, 0.92]);
1112
const readyStateHaveCurrentData = 2;
1213
const runtimeGlobalName = "__prompterOneRuntime";
1314
const mediaContractProperty = "media";
@@ -193,6 +194,60 @@
193194
});
194195
}
195196

197+
async function seekToVideoSample(videoElement, timeSeconds) {
198+
await new Promise(resolve => {
199+
let completed = false;
200+
201+
const finish = () => {
202+
if (completed) {
203+
return;
204+
}
205+
206+
completed = true;
207+
videoElement.removeEventListener("seeked", finish);
208+
resolve();
209+
};
210+
211+
window.setTimeout(finish, visibleVideoPollDelayMs * 4);
212+
videoElement.addEventListener("seeked", finish, { once: true });
213+
214+
try {
215+
videoElement.currentTime = timeSeconds;
216+
}
217+
catch {
218+
finish();
219+
}
220+
});
221+
}
222+
223+
async function detectVisibleVideoAcrossSeekSamples(videoElement, highestVisiblePixelCount) {
224+
const duration = Number.isFinite(videoElement.duration)
225+
? videoElement.duration
226+
: 0;
227+
if (duration <= 0) {
228+
return {
229+
hasVisibleVideo: highestVisiblePixelCount >= minimumVisiblePixelCount,
230+
nonBlackPixelCount: highestVisiblePixelCount
231+
};
232+
}
233+
234+
const maximumSeekTime = Math.max(0, duration - 0.01);
235+
for (const sample of visibleVideoSeekSamples) {
236+
await seekToVideoSample(videoElement, Math.min(maximumSeekTime, duration * sample));
237+
238+
const frame = detectVisibleVideo(videoElement);
239+
highestVisiblePixelCount = Math.max(highestVisiblePixelCount, frame.nonBlackPixelCount);
240+
if (frame.hasVisibleVideo) {
241+
return frame;
242+
}
243+
}
244+
245+
return {
246+
hasVisibleVideo: highestVisiblePixelCount >= minimumVisiblePixelCount,
247+
nonBlackPixelCount: highestVisiblePixelCount
248+
};
249+
}
250+
196251
async function detectVisibleVideoAcrossFrames(videoElement) {
197252
const deadline = Date.now() + visibleVideoProbeTimeoutMs;
198253
let highestVisiblePixelCount = 0;
@@ -208,10 +263,7 @@
208263
await waitForNextVideoFrame(videoElement);
209264
}
210265

211-
return {
212-
hasVisibleVideo: highestVisiblePixelCount >= minimumVisiblePixelCount,
213-
nonBlackPixelCount: highestVisiblePixelCount
214-
};
266+
return await detectVisibleVideoAcrossSeekSamples(videoElement, highestVisiblePixelCount);
215267
}
216268

217269
async function analyzeSavedRecording() {
@@ -223,6 +275,7 @@
223275
const videoElement = document.createElement("video");
224276
videoElement.muted = true;
225277
videoElement.playsInline = true;
278+
videoElement.loop = true;
226279
videoElement.src = objectUrl;
227280

228281
try {

0 commit comments

Comments
 (0)