Phones in the end-to-end tests
The browser fixture opens phones from Playwright's own device presets (360x800, iPhone SE 375x667, Pixel 7 412x839, and the Pixel 7 held sideways), with touch, a coarse pointer and reduced motion, and a phone session fails at once if the page doesn't see a touch screen. Mobile/fit.js measures a page one screen at a time down its main scroller: sideways scroll, boxes past the screen's sides, open menus and dialogs cut or outside the screen, targets without a 44px hit area (hit-tested at their centre and 21px around it, so a crowding neighbour fails them too) and control text under 12px. RouteSweepTests runs every feed tab, threads of each kind the town holds, profiles, search, Explore, lists, groups, filters, scheduled posts, settings and administration (every section opened) and the login page on the three portrait phones, as client.mobile.fit.* and client.mobile.touch.* cells; screenshots, findings and a contact sheet go to tests/e2e/out/mobile/. DesktopUnchangedTests holds desktop's control sizes. Category=Mobile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
88e55e671c
commit
63d83810d7
8 files changed
+732
-11
No files matched your search
@@ -28,21 +28,44 @@ namespace decePubClient.E2E.Support
|
||||
_playwright?.Dispose();
|
||||
}
|
||||
|
||||
public async Task<Session> Open(string storageState = null)
|
||||
// a desktop window of 1440x900, or a phone: touch, a coarse pointer, its size and scale, reduced motion so nothing is
|
||||
// measured halfway through a transition, and the mobile tests' measuring script (Mobile/fit.js) on every page
|
||||
public async Task<Session> Open(string storageState = null, Phone phone = null)
|
||||
{
|
||||
var context = await Chromium.NewContextAsync(new()
|
||||
var options = phone is null
|
||||
? new BrowserNewContextOptions { ViewportSize = new() { Width = 1440, Height = 900 } }
|
||||
: new BrowserNewContextOptions(_playwright.Devices[phone.Preset]) { ReducedMotion = ReducedMotion.Reduce };
|
||||
if (phone is { Width: > 0 })
|
||||
{
|
||||
IgnoreHTTPSErrors = true,
|
||||
Locale = "en-GB",
|
||||
TimezoneId = "Europe/Rome",
|
||||
ViewportSize = new() { Width = 1440, Height = 900 },
|
||||
ServiceWorkers = ServiceWorkerPolicy.Block,
|
||||
StorageStatePath = storageState
|
||||
});
|
||||
return new Session(context, await context.NewPageAsync());
|
||||
options.ViewportSize = new() { Width = phone.Width, Height = phone.Height };
|
||||
options.ScreenSize = new() { Width = phone.Width, Height = phone.Height };
|
||||
}
|
||||
options.IgnoreHTTPSErrors = true;
|
||||
options.Locale = "en-GB";
|
||||
options.TimezoneId = "Europe/Rome";
|
||||
options.ServiceWorkers = ServiceWorkerPolicy.Block;
|
||||
options.StorageStatePath = storageState;
|
||||
var context = await Chromium.NewContextAsync(options);
|
||||
if (phone is not null)
|
||||
await context.AddInitScriptAsync(Mobile.Fit.Script);
|
||||
return new Session(context, await context.NewPageAsync(), phone);
|
||||
}
|
||||
}
|
||||
|
||||
// A phone the tests hold: one of Playwright's device presets (they ship inside its package, nothing is downloaded),
|
||||
// at its own size or at another one
|
||||
public sealed record Phone(string Key, string Preset, int Width = 0, int Height = 0)
|
||||
{
|
||||
public static readonly Phone Narrow360 = new("narrow360", "Pixel 7", 360, 800);
|
||||
public static readonly Phone IPhoneSE3 = new("iphone-se3", "iPhone SE (3rd gen)");
|
||||
public static readonly Phone Pixel7 = new("pixel7", "Pixel 7");
|
||||
public static readonly Phone Pixel7Landscape = new("pixel7-landscape", "Pixel 7 landscape");
|
||||
|
||||
public static readonly Phone[] Portrait = [Narrow360, IPhoneSE3, Pixel7];
|
||||
|
||||
public static Phone ByKey(string key) => new[] { Narrow360, IPhoneSE3, Pixel7, Pixel7Landscape }.Single(p => p.Key == key);
|
||||
}
|
||||
|
||||
// A browser tab and what it did: every request's host, every console error, every 5xx and Blazor's error bar. The
|
||||
// guards hold for every test: the app talks only to its own site and to PrivaPub, and never fails silently.
|
||||
public sealed class Session : IAsyncDisposable
|
||||
@@ -54,10 +77,13 @@ namespace decePubClient.E2E.Support
|
||||
public List<string> ServerErrors { get; } = [];
|
||||
public List<(string Url, string Authorization)> ApiCalls { get; } = [];
|
||||
|
||||
public Session(IBrowserContext context, IPage page)
|
||||
public Phone Phone { get; }
|
||||
|
||||
public Session(IBrowserContext context, IPage page, Phone phone = null)
|
||||
{
|
||||
Context = context;
|
||||
Page = page;
|
||||
Phone = phone;
|
||||
page.Request += (_, request) =>
|
||||
{
|
||||
var uri = new Uri(request.Url);
|
||||
@@ -96,6 +122,15 @@ namespace decePubClient.E2E.Support
|
||||
Assert.All(clientApi, auth => Assert.Equal(3, auth["Bearer ".Length..].Split('.').Length));
|
||||
}
|
||||
|
||||
// a phone session that answers like a desktop would test desktop sizes without saying so: the page must see touch
|
||||
public async Task AssertPhone()
|
||||
{
|
||||
if (Phone is null)
|
||||
return;
|
||||
Assert.True(await Page.EvaluateAsync<bool>("() => matchMedia('(pointer: coarse)').matches && matchMedia('(hover: none)').matches"),
|
||||
$"{Phone.Key} is not emulated as a touch screen");
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync() => await Context.CloseAsync();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user