using decePubClient.E2E.Support; using Microsoft.Playwright; namespace decePubClient.E2E.Mobile { // Controls the sweep doesn't reach, since it measures pages with their sections closed: Settings' Data import/export // opened on the narrowest phone, its file inputs and checkboxes thumb-sized and drawn like the rest of the app (a // ticked box filled, not a tick on nothing), in both themes. Photos go to tests/e2e/out/mobile-controls/. [Trait("Category", "Mobile")] public sealed class ControlsTests(Browser browser) { static readonly Phone Phone = Phone.Narrow360; static readonly string OutDir = Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "out", "mobile-controls"); [Fact] public async Task Data_transfer_controls_are_thumb_sized_and_drawn_like_the_rest() { await using var session = await SignIn.As(browser, Pasture.Personas().First(), Phone); var page = session.Page; await Results.Cell("client.mobile.controls.data-transfer", "privapub", Phone.Key, async () => { await page.GotoAsync($"{Pasture.Site}/settings"); var header = page.Locator(".collapse-title", new() { HasText = "Data import/export" }).First; await header.TapAsync(); var archive = page.Locator("[data-archive]"); await archive.WaitForAsync(new() { Timeout = 30000 }); Fit.AssertTouchable(await Fit.Now(page, "[data-archive]")); Fit.AssertTouchable(await Fit.Now(page, "[data-import]")); // a ticked box is filled: daisyUI leaves it transparent unless a colour asks otherwise var ticked = archive.Locator("input[type=checkbox]:checked").First; var fill = await ticked.EvaluateAsync("e => getComputedStyle(e).backgroundColor"); Assert.False(fill is "transparent" or "rgba(0, 0, 0, 0)", $"a ticked checkbox is drawn on nothing ({fill})"); // a file input is as tall as the other fields on a touch screen foreach (var input in await page.Locator("[data-archive] input[type=file], [data-import] input[type=file]").AllAsync()) Assert.True((await input.BoundingBoxAsync())!.Height >= 44, $"a file input is {(await input.BoundingBoxAsync())!.Height}px tall: " + await input.EvaluateAsync("e => { const s = getComputedStyle(e); return `${e.className} --size=${s.getPropertyValue('--size')} height=${s.height} coarse=${matchMedia('(pointer: coarse)').matches}`; }")); // photos the size of the screen: one of an element taller than the screen makes Playwright resize the page, // which drops the touch emulation for what comes after Directory.CreateDirectory(OutDir); foreach (var theme in new[] { "neo-dark", "neo-light" }) { await page.EvaluateAsync("t => document.documentElement.setAttribute('data-theme', t)", theme); foreach (var (part, what) in new[] { ("archive", "[data-archive-make]"), ("restore", "input[data-archive-file]"), ("csv", "[data-import]") }) { await page.Locator(what).First.EvaluateAsync("e => e.scrollIntoView({ block: 'start' })"); await page.ScreenshotAsync(new() { Path = Path.Combine(OutDir, $"{part}-{theme}.png"), Animations = ScreenshotAnimations.Disabled }); } } await session.AssertGuards(); }, page); } } }