Introduce a focal Home opening, chapter index, and restrained continuity reveals while keeping reduced-motion and visual baselines intact. Co-authored-by: Cursor <cursoragent@cursor.com>
122 lines
3.9 KiB
PHP
122 lines
3.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\Service;
|
|
use App\Models\SiteSetting;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function (): void {
|
|
SiteSetting::instance()->update([
|
|
'hero_title' => 'Celebrações com propósito',
|
|
'default_og_image_path' => 'media/hero.jpg',
|
|
'default_og_image_alt' => 'Capa editorial',
|
|
]);
|
|
|
|
Service::factory()->published()->featured()->create([
|
|
'title' => 'Casamentos',
|
|
'summary' => 'Assessoria completa',
|
|
]);
|
|
});
|
|
|
|
it('keeps home motion in final visible state when reduced motion is preferred', function (): void {
|
|
$page = $this->visit('/', [
|
|
'reducedMotion' => 'reduce',
|
|
]);
|
|
|
|
$state = $page->script(<<<'JS'
|
|
() => {
|
|
const hero = document.querySelector('[data-motion="dossie-hero"]');
|
|
const title = document.querySelector('#hero-heading');
|
|
const index = document.querySelector('[data-chapter-index]');
|
|
const active = document.querySelector('[data-chapter-index] [aria-current="true"]');
|
|
const titleStyle = title ? getComputedStyle(title) : null;
|
|
|
|
return {
|
|
heroActive: hero?.classList.contains('is-active') ?? false,
|
|
titleOpacity: titleStyle ? Number.parseFloat(titleStyle.opacity) : 0,
|
|
hasIndex: Boolean(index),
|
|
hasActiveChapter: Boolean(active),
|
|
};
|
|
}
|
|
JS);
|
|
|
|
expect($state['heroActive'])->toBeTrue();
|
|
expect($state['titleOpacity'])->toBeGreaterThan(0.9);
|
|
expect($state['hasIndex'])->toBeTrue();
|
|
expect($state['hasActiveChapter'])->toBeTrue();
|
|
});
|
|
|
|
it('activates the home focal opening once when motion is allowed', function (): void {
|
|
$page = $this->visit('/', [
|
|
'reducedMotion' => 'no-preference',
|
|
]);
|
|
|
|
$page->script(<<<'JS'
|
|
() => new Promise((resolve) => {
|
|
const wait = () => {
|
|
const hero = document.querySelector('[data-motion="dossie-hero"]');
|
|
if (hero?.classList.contains('is-active')) {
|
|
resolve(true);
|
|
return;
|
|
}
|
|
requestAnimationFrame(wait);
|
|
};
|
|
wait();
|
|
setTimeout(() => resolve(false), 2000);
|
|
})
|
|
JS);
|
|
|
|
$active = $page->script(<<<'JS'
|
|
() => document.querySelector('[data-motion="dossie-hero"]')?.classList.contains('is-active') ?? false
|
|
JS);
|
|
|
|
expect($active)->toBeTrue();
|
|
});
|
|
|
|
it('updates the active chapter while scrolling the home dossier', function (): void {
|
|
$page = $this->visit('/', [
|
|
'reducedMotion' => 'no-preference',
|
|
]);
|
|
|
|
$page->resize(1440, 1000);
|
|
|
|
$before = $page->script(<<<'JS'
|
|
() => document.querySelector('[data-chapter-index] [aria-current="true"]')?.getAttribute('href') ?? null
|
|
JS);
|
|
|
|
$page->script(<<<'JS'
|
|
() => {
|
|
const target = document.querySelector('#method-heading');
|
|
target?.scrollIntoView({ block: 'start' });
|
|
window.dispatchEvent(new Event('scroll'));
|
|
return Boolean(target);
|
|
}
|
|
JS);
|
|
|
|
$page->script(<<<'JS'
|
|
() => new Promise((resolve) => {
|
|
let frames = 0;
|
|
const check = () => {
|
|
const href = document.querySelector('[data-chapter-index] [aria-current="true"]')?.getAttribute('href');
|
|
if (href === '#method-heading' || frames > 60) {
|
|
resolve(href);
|
|
return;
|
|
}
|
|
frames += 1;
|
|
requestAnimationFrame(check);
|
|
};
|
|
check();
|
|
})
|
|
JS);
|
|
|
|
$after = $page->script(<<<'JS'
|
|
() => document.querySelector('[data-chapter-index] [aria-current="true"]')?.getAttribute('href') ?? null
|
|
JS);
|
|
|
|
expect($before)->not->toBeNull();
|
|
expect($after)->toBe('#method-heading');
|
|
});
|