'file']); Storage::fake('shares'); }); test('the sign-in page has exactly one main landmark and never scrolls sideways', function () { foreach ([[393, 852], [1280, 800]] as [$width, $height]) { ready(visit('/login')->resize($width, $height)) ->assertScript("document.querySelectorAll('main').length === 1") ->assertScript('document.documentElement.scrollWidth <= window.innerWidth'); } }); test('the sign-in card is capped at 28rem and sits 16px from each edge on a phone', function () { $page = ready(visit('/login')->resize(393, 852)); $metrics = $page->script("(() => { const rect = document.querySelector('.auth-card').getBoundingClientRect(); return { left: rect.left, right: window.innerWidth - rect.right, width: rect.width }; })()"); expect($metrics['left'])->toEqualWithDelta(16, 1); expect($metrics['right'])->toEqualWithDelta(16, 1); $page->resize(1280, 800); $capped = $page->script("(() => { const remPx = parseFloat(getComputedStyle(document.documentElement).fontSize); return document.querySelector('.auth-card').getBoundingClientRect().width <= 28 * remPx + 0.5; })()"); expect($capped)->toBeTrue(); }); test('the sign-in card top-aligns below 600px and centres from 600px', function () { // The gap above the card and the gap below it, inside .auth-main's own content box (between // its top and bottom padding) — equal gaps is the geometric definition of "centred", and it // needs no assumption about the padding's pixel values, only that they exist on both edges. $gaps = fn (int $width) => ready(visit('/login')->resize($width, 900))->script("(() => { const main = document.querySelector('.auth-main'); const mainStyle = getComputedStyle(main); const mainRect = main.getBoundingClientRect(); const cardRect = document.querySelector('.auth-card').getBoundingClientRect(); const contentTop = mainRect.top + parseFloat(mainStyle.paddingTop); const contentBottom = mainRect.bottom - parseFloat(mainStyle.paddingBottom); return { above: cardRect.top - contentTop, below: contentBottom - cardRect.bottom }; })()"); $narrow = $gaps(599); // Below 600px the card is flush against the top of the content box: no extra gap above it. expect($narrow['above'])->toBeLessThan(2); expect($narrow['below'])->toBeGreaterThan($narrow['above'] + 10); $wide = $gaps(600); // From 600px the gap above equals the gap below: the card is vertically centred. expect($wide['above'])->toEqualWithDelta($wide['below'], 2); expect($wide['above'])->toBeGreaterThan($narrow['above'] + 10); }); test('the sign-in submit button is end-aligned at its own width, not stretched', function () { $page = ready(visit('/login')->resize(393, 852)); $metrics = $page->script("(() => { const form = document.querySelector('[data-md-form]').getBoundingClientRect(); const button = document.querySelector('[data-test=\"login-button\"]').getBoundingClientRect(); return { formWidth: form.width, formRight: form.right, buttonWidth: button.width, buttonRight: button.right }; })()"); expect($metrics['buttonWidth'])->toBeLessThan($metrics['formWidth']); expect($metrics['buttonRight'])->toEqualWithDelta($metrics['formRight'], 1); }); test('the floating toolbar never covers the sign-in card once scrolled to the bottom', function () { $page = ready(visit('/login')->resize(393, 667)); $page->script('window.scrollTo(0, document.body.scrollHeight)'); $overlap = $page->script("(() => { const card = document.querySelector('.auth-card').getBoundingClientRect(); const toolbar = document.querySelector('[data-test=\"app-toolbar\"]').getBoundingClientRect(); return card.bottom - toolbar.top; })()"); expect($overlap)->toBeLessThanOrEqual(0.5); }); test("a snackbar clears SealShare's floating toolbar", function () { $page = ready(visit('/login')->resize(393, 852)); $page->script("window.materialToast('Link copied', { timeout: 10000 })"); $page->wait(0.5); $gap = $page->script("(() => { const snackbar = document.querySelector('[data-md-toast-snackbar]').getBoundingClientRect(); const toolbar = document.querySelector('[data-test=\"app-toolbar\"]').getBoundingClientRect(); return toolbar.top - snackbar.bottom; })()"); expect($gap)->toBeGreaterThanOrEqual(16 - 0.5); }); test("the app layout's main column is capped at 64rem and centred on a wide window", function () { $page = ready(visit('/upload')->resize(1600, 900)); $metrics = $page->script("(() => { const remPx = parseFloat(getComputedStyle(document.documentElement).fontSize); const rect = document.querySelector('.app-main').getBoundingClientRect(); return { width: rect.width, cap: 64 * remPx, left: rect.left, right: window.innerWidth - rect.right, }; })()"); expect($metrics['width'])->toBeLessThanOrEqual($metrics['cap'] + 0.5); expect($metrics['left'])->toEqualWithDelta($metrics['right'], 1); }); test("the app layout's content keeps M3's margin at 599px and 600px", function () { // Below the 64rem cap the pane spans the full window (no centring offset), so the padding // it declares on its body is exactly the gap between its content and the window's edge. $margins = fn (int $width) => ready(visit('/upload')->resize($width, 900))->script("(() => { const main = document.querySelector('.app-main').getBoundingClientRect(); const style = getComputedStyle(document.querySelector('[data-md-pane-body]')); return { spansWindow: main.left === 0 && Math.abs(main.right - window.innerWidth) < 0.5, left: parseFloat(style.paddingLeft), right: parseFloat(style.paddingRight), }; })()"); $narrow = $margins(599); expect($narrow['spansWindow'])->toBeTrue(); expect($narrow['left'])->toEqualWithDelta(16, 1); expect($narrow['right'])->toEqualWithDelta(16, 1); $wide = $margins(600); expect($wide['spansWindow'])->toBeTrue(); expect($wide['left'])->toEqualWithDelta(24, 1); expect($wide['right'])->toEqualWithDelta(24, 1); });