157 lines
6.2 KiB
PHP
157 lines
6.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use Database\Seeders\VisualContentSeeder;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function (): void {
|
|
Artisan::call('db:seed', ['--class' => VisualContentSeeder::class, '--force' => true]);
|
|
});
|
|
|
|
it('keeps the hero asymmetric on desktop and stacked through md and mobile', function (): void {
|
|
$page = $this->visit('/', [
|
|
'reducedMotion' => 'reduce',
|
|
]);
|
|
|
|
$waitFrames = '() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)))';
|
|
|
|
$page->resize(1440, 1000);
|
|
$page->script($waitFrames);
|
|
|
|
$desktop = $page->script(<<<'JS'
|
|
() => {
|
|
const header = document.querySelector('.site-header');
|
|
const hero = document.querySelector('[data-chapter="hero"]');
|
|
const content = hero?.querySelector('[data-hero-content]');
|
|
const media = hero?.querySelector('[data-split-hero]');
|
|
const headerRect = header?.getBoundingClientRect();
|
|
const heroRect = hero?.getBoundingClientRect();
|
|
const contentRect = content?.getBoundingClientRect();
|
|
const mediaRect = media?.getBoundingClientRect();
|
|
|
|
return {
|
|
hasContentColumn: Boolean(content),
|
|
startsBelowHeader: Math.abs((heroRect?.top ?? -1) - (headerRect?.bottom ?? -2)) <= 1,
|
|
mediaBesideContent: (mediaRect?.left ?? 0) >= (contentRect?.right ?? Number.POSITIVE_INFINITY) - 1,
|
|
mediaMinHeight: Math.round(mediaRect?.height ?? 0) >= 519,
|
|
mediaTouchesViewportRight: (mediaRect?.right ?? 0) >= window.innerWidth - 4,
|
|
horizontalOverflow: document.documentElement.scrollWidth > window.innerWidth,
|
|
};
|
|
}
|
|
JS);
|
|
|
|
expect($desktop)->toBe([
|
|
'hasContentColumn' => true,
|
|
'startsBelowHeader' => true,
|
|
'mediaBesideContent' => true,
|
|
'mediaMinHeight' => true,
|
|
'mediaTouchesViewportRight' => true,
|
|
'horizontalOverflow' => false,
|
|
]);
|
|
|
|
$page->resize(767, 1000);
|
|
$page->script($waitFrames);
|
|
|
|
$md = $page->script(<<<'JS'
|
|
() => {
|
|
const hero = document.querySelector('[data-chapter="hero"]');
|
|
const content = hero?.querySelector('[data-hero-content]');
|
|
const media = hero?.querySelector('[data-split-hero]');
|
|
const contentRect = content?.getBoundingClientRect();
|
|
const mediaRect = media?.getBoundingClientRect();
|
|
|
|
return {
|
|
hasContentColumn: Boolean(content),
|
|
mediaFollowsContent: (mediaRect?.top ?? 0) >= (contentRect?.bottom ?? Number.POSITIVE_INFINITY) - 1,
|
|
mediaFillsContainerWidth: (mediaRect?.width ?? 0) > window.innerWidth - 100,
|
|
mediaMinHeight: Math.round(mediaRect?.height ?? 0) >= 319,
|
|
horizontalOverflow: document.documentElement.scrollWidth > window.innerWidth,
|
|
};
|
|
}
|
|
JS);
|
|
|
|
expect($md)->toBe([
|
|
'hasContentColumn' => true,
|
|
'mediaFollowsContent' => true,
|
|
'mediaFillsContainerWidth' => true,
|
|
'mediaMinHeight' => true,
|
|
'horizontalOverflow' => false,
|
|
]);
|
|
|
|
$page->resize(390, 844);
|
|
$page->script($waitFrames);
|
|
|
|
$mobile = $page->script(<<<'JS'
|
|
() => {
|
|
const hero = document.querySelector('[data-chapter="hero"]');
|
|
const content = hero?.querySelector('[data-hero-content]');
|
|
const media = hero?.querySelector('[data-split-hero]');
|
|
const contentRect = content?.getBoundingClientRect();
|
|
const mediaRect = media?.getBoundingClientRect();
|
|
|
|
return {
|
|
hasContentColumn: Boolean(content),
|
|
mediaFollowsContent: (mediaRect?.top ?? 0) >= (contentRect?.bottom ?? Number.POSITIVE_INFINITY) - 1,
|
|
mediaFillsContainerWidth: (mediaRect?.width ?? 0) > window.innerWidth - 100,
|
|
horizontalOverflow: document.documentElement.scrollWidth > window.innerWidth,
|
|
};
|
|
}
|
|
JS);
|
|
|
|
expect($mobile)->toBe([
|
|
'hasContentColumn' => true,
|
|
'mediaFollowsContent' => true,
|
|
'mediaFillsContainerWidth' => true,
|
|
'horizontalOverflow' => false,
|
|
]);
|
|
});
|
|
|
|
it('keeps the full home cadence accessible and contained at both viewports', function (): void {
|
|
$page = $this->visit('/', [
|
|
'reducedMotion' => 'reduce',
|
|
]);
|
|
|
|
foreach ([[1440, 1000], [390, 844]] as [$width, $height]) {
|
|
$page->resize($width, $height);
|
|
|
|
$page->assertNoAccessibilityIssues(1);
|
|
|
|
$layout = $page->script(<<<'JS'
|
|
() => {
|
|
const chapters = Array.from(document.querySelectorAll('.home-chapter'));
|
|
const clippedValues = new Set(['clip', 'hidden']);
|
|
const textNodes = Array.from(document.querySelectorAll(
|
|
'.home-chapters h1, .home-chapters h2, .home-chapters h3, .home-chapters p, .home-chapters a, .home-chapters span'
|
|
));
|
|
|
|
return {
|
|
horizontalOverflow: document.documentElement.scrollWidth > window.innerWidth,
|
|
overlappingChapters: chapters.slice(1).some((chapter, index) => {
|
|
const previous = chapters[index].getBoundingClientRect();
|
|
const current = chapter.getBoundingClientRect();
|
|
return current.top < previous.bottom - 1;
|
|
}),
|
|
ornamentalFolios: document.querySelectorAll('[data-home-folio]').length,
|
|
clippedText: textNodes.filter((node) => {
|
|
const style = getComputedStyle(node);
|
|
const clippedX = clippedValues.has(style.overflowX) && node.scrollWidth > node.clientWidth + 1;
|
|
const clippedY = clippedValues.has(style.overflowY) && node.scrollHeight > node.clientHeight + 1;
|
|
return clippedX || clippedY;
|
|
}).length,
|
|
};
|
|
}
|
|
JS);
|
|
|
|
expect($layout)->toBe([
|
|
'horizontalOverflow' => false,
|
|
'overlappingChapters' => false,
|
|
'ornamentalFolios' => 0,
|
|
'clippedText' => 0,
|
|
]);
|
|
}
|
|
});
|