Skip to content

Commit dc9727b

Browse files
committed
Harden Learn WPM browser cadence test
1 parent 27b9db0 commit dc9727b

File tree

1 file changed

+83
-39
lines changed

1 file changed

+83
-39
lines changed

tests/PrompterOne.App.UITests/Learn/LearnFidelityTests.cs

Lines changed: 83 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
using System.Globalization;
12
using PrompterOne.Shared.Contracts;
23
using static Microsoft.Playwright.Assertions;
34

45
namespace PrompterOne.App.UITests;
56

67
public sealed class LearnFidelityTests(StandaloneAppFixture fixture) : IClassFixture<StandaloneAppFixture>
78
{
9+
private const string LearnPlaybackProbeStartKey = "__prompterOneLearnFidelityPlaybackStartMs";
810
private readonly record struct ContextGapMeasurement(double LeftGapPx, double RightGapPx);
911
private readonly record struct ContextRailClipMeasurement(double LeftClipPx, double RightClipPx);
1012
private readonly record struct FocusWordSlackMeasurement(double SlackPx);
@@ -370,34 +372,25 @@ await StepUntilWordAsync(
370372
[Fact]
371373
public async Task LearnScreen_WpmIncrease_AcceleratesPlaybackCadence()
372374
{
373-
var page = await fixture.NewPageAsync();
374-
375-
try
376-
{
377-
await page.GotoAsync(BrowserTestConstants.Routes.LearnDemo);
378-
await Expect(page.GetByTestId(UiTestIds.Learn.Page))
379-
.ToBeVisibleAsync(new() { Timeout = BrowserTestConstants.Timing.ExtendedVisibleTimeoutMs });
380-
381-
var baselineAdvance = await MeasurePlaybackAdvanceAsync(page);
382-
383-
await page.GotoAsync(BrowserTestConstants.Routes.LearnDemo);
384-
await Expect(page.GetByTestId(UiTestIds.Learn.Page))
385-
.ToBeVisibleAsync(new() { Timeout = BrowserTestConstants.Timing.ExtendedVisibleTimeoutMs });
386-
387-
await IncreaseLearnSpeedAsync(page, BrowserTestConstants.Learn.PlaybackSpeedIncreaseClicks);
388-
await Expect(page.Locator($"#{UiDomIds.Learn.Speed}"))
389-
.ToHaveTextAsync(BrowserTestConstants.Learn.FasterPlaybackSpeedText);
375+
var slowPlaybackDuration = await MeasureLearnPlaybackDurationAsync(
376+
async page =>
377+
{
378+
await DecreaseLearnSpeedAsync(page, BrowserTestConstants.ReaderTiming.LearnSlowWpmAdjustmentClicks);
379+
await Expect(page.Locator($"#{UiDomIds.Learn.Speed}"))
380+
.ToHaveTextAsync(BrowserTestConstants.ReaderTiming.LearnSlowWpm.ToString(CultureInfo.InvariantCulture));
381+
});
390382

391-
var fasterAdvance = await MeasurePlaybackAdvanceAsync(page);
383+
var fastPlaybackDuration = await MeasureLearnPlaybackDurationAsync(
384+
async page =>
385+
{
386+
await IncreaseLearnSpeedAsync(page, BrowserTestConstants.ReaderTiming.LearnFastWpmAdjustmentClicks);
387+
await Expect(page.Locator($"#{UiDomIds.Learn.Speed}"))
388+
.ToHaveTextAsync(BrowserTestConstants.ReaderTiming.LearnFastWpm.ToString(CultureInfo.InvariantCulture));
389+
});
392390

393-
Assert.True(
394-
fasterAdvance >= baselineAdvance + BrowserTestConstants.Learn.MinimumPlaybackAdvanceDeltaWords,
395-
$"Expected higher WPM to advance more words. Baseline advanced {baselineAdvance}, faster mode advanced {fasterAdvance}.");
396-
}
397-
finally
398-
{
399-
await page.Context.CloseAsync();
400-
}
391+
Assert.True(
392+
fastPlaybackDuration <= slowPlaybackDuration - BrowserTestConstants.ReaderTiming.MinimumSpeedProbePlaybackDeltaMs,
393+
$"Expected higher WPM to finish faster. Slow mode took {slowPlaybackDuration} ms, faster mode took {fastPlaybackDuration} ms.");
401394
}
402395

403396
private static Task<double> MeasureOrpDeltaAsync(Microsoft.Playwright.IPage page) =>
@@ -483,25 +476,76 @@ private static async Task IncreaseLearnSpeedAsync(Microsoft.Playwright.IPage pag
483476
}
484477
}
485478

486-
private static async Task<int> MeasurePlaybackAdvanceAsync(Microsoft.Playwright.IPage page)
479+
private static async Task DecreaseLearnSpeedAsync(Microsoft.Playwright.IPage page, int clickCount)
487480
{
488-
var startWordNumber = await ReadProgressWordNumberAsync(page);
481+
for (var clickIndex = 0; clickIndex < clickCount; clickIndex++)
482+
{
483+
await page.GetByTestId(UiTestIds.Learn.SpeedDown).ClickAsync();
484+
}
485+
}
489486

490-
await page.GetByTestId(UiTestIds.Learn.PlayToggle).ClickAsync();
491-
await page.WaitForTimeoutAsync(BrowserTestConstants.Timing.LearnPlaybackProbeWindowMs);
492-
await page.GetByTestId(UiTestIds.Learn.PlayToggle).ClickAsync();
487+
private async Task<int> MeasureLearnPlaybackDurationAsync(Func<Microsoft.Playwright.IPage, Task> configurePageAsync)
488+
{
489+
var page = await fixture.NewPageAsync();
493490

494-
var endWordNumber = await ReadProgressWordNumberAsync(page);
495-
return endWordNumber - startWordNumber;
491+
try
492+
{
493+
await page.GotoAsync(BrowserTestConstants.Routes.LearnReaderTiming);
494+
await Expect(page.GetByTestId(UiTestIds.Learn.Page))
495+
.ToBeVisibleAsync(new() { Timeout = BrowserTestConstants.Timing.ExtendedVisibleTimeoutMs });
496+
497+
await configurePageAsync(page);
498+
499+
return await MeasurePlaybackDurationToWordAsync(
500+
page,
501+
BrowserTestConstants.ReaderTiming.WordCount);
502+
}
503+
finally
504+
{
505+
await page.Context.CloseAsync();
506+
}
496507
}
497508

498-
private static async Task<int> ReadProgressWordNumberAsync(Microsoft.Playwright.IPage page)
509+
private static async Task<int> MeasurePlaybackDurationToWordAsync(Microsoft.Playwright.IPage page, int targetWordNumber)
499510
{
500-
var progressLabel = await page.GetByTestId(UiTestIds.Learn.ProgressLabel).TextContentAsync() ?? string.Empty;
501-
var parts = progressLabel.Split(' ', StringSplitOptions.RemoveEmptyEntries);
502-
return parts.Length >= 2 && int.TryParse(parts[1], out var wordNumber)
503-
? wordNumber
504-
: 0;
511+
await page.EvaluateAsync(
512+
"""
513+
probe => {
514+
window[probe.key] = performance.now();
515+
}
516+
""",
517+
new
518+
{
519+
key = LearnPlaybackProbeStartKey
520+
});
521+
await page.GetByTestId(UiTestIds.Learn.PlayToggle).ClickAsync();
522+
await page.WaitForFunctionAsync(
523+
"""
524+
probe => {
525+
const element = document.querySelector(`[data-testid="${probe.progressLabelTestId}"]`);
526+
const text = element?.textContent ?? '';
527+
const match = /Word\s+(\d+)/.exec(text);
528+
return match !== null && Number(match[1]) >= probe.targetWordNumber;
529+
}
530+
""",
531+
new
532+
{
533+
progressLabelTestId = UiTestIds.Learn.ProgressLabel,
534+
targetWordNumber
535+
},
536+
new()
537+
{
538+
Timeout = BrowserTestConstants.ReaderTiming.SampleCaptureTimeoutMs
539+
});
540+
541+
return await page.EvaluateAsync<int>(
542+
"""
543+
probe => Math.round(performance.now() - (window[probe.key] ?? performance.now()))
544+
""",
545+
new
546+
{
547+
key = LearnPlaybackProbeStartKey
548+
});
505549
}
506550

507551
private static Task<ContextGapMeasurement> MeasureContextGapsAsync(Microsoft.Playwright.IPage page) =>

0 commit comments

Comments
 (0)