Files
amare/tests/Browser/HardeningTest.php
Manoel Freitas 2fe262ab59 Hardening public blades: overflow, fallbacks, branded errors, JS robustness (#14)
* fix: prevent long strings from overflowing public layout

* fix: fall back to default hero copy and tighten hero line-height

* fix: skip blank testimonials and guard empty portfolio metadata

* feat: add branded 419, 429 and 503 error pages

* fix: reset submit state on bfcache restore, swap label while sending, trap mobile menu focus

* chore: track hardening task status

* fix: restore contact submit state after bfcache

* fix: allow contact links to wrap long unbroken strings
2026-08-06 00:22:53 -03:00

87 lines
2.9 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\SiteSetting;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
beforeEach(function (): void {
SiteSetting::instance();
});
it('does not overflow horizontally when contact info contains long unbroken strings', function (): void {
SiteSetting::instance()->update([
'email' => str_repeat('a', 50).'@email.'.str_repeat('b', 40),
'phone' => '(11) 99999-9999',
]);
$page = $this->visit('/contato');
$overflow = $page->script(
'() => document.documentElement.scrollWidth - document.documentElement.clientWidth',
);
expect((int) $overflow)->toBeLessThanOrEqual(0);
});
it('shows sending state on submit and resets on bfcache restore', function (): void {
$page = $this->visit('/contato');
$state = $page->script(<<<'JS'
() => {
const form = document.querySelector("form[data-contact-form]");
const button = document.querySelector("[data-submit-button]");
form?.dispatchEvent(new Event("submit", { bubbles: true, cancelable: true }));
const afterSubmit = {
label: button?.textContent?.trim(),
disabled: button?.disabled ?? false,
formBusy: form?.getAttribute("aria-busy"),
};
window.dispatchEvent(new PageTransitionEvent("pageshow", { persisted: true }));
return {
afterSubmit,
afterRestore: {
label: button?.textContent?.trim(),
disabled: button?.disabled ?? false,
formBusy: form?.getAttribute("aria-busy"),
},
};
}
JS);
expect($state['afterSubmit']['label'])->toBe('Enviando…');
expect($state['afterSubmit']['disabled'])->toBeTrue();
expect($state['afterSubmit']['formBusy'])->toBe('true');
expect($state['afterRestore']['label'])->toBe('Enviar briefing');
expect($state['afterRestore']['disabled'])->toBeFalse();
expect($state['afterRestore']['formBusy'])->toBeNull();
});
it('traps focus within the open mobile menu', function (): void {
$page = $this->visit('/');
$page->resize(390, 844);
$page->click('[data-menu-button]');
for ($i = 0; $i < 20; $i++) {
$inside = $page->script(
'() => document.querySelector("[data-main-nav]")?.contains(document.activeElement) ?? false',
);
expect($inside)->toBeTrue();
$page->keys(':focus', 'Tab');
}
$page->script('() => document.querySelector("[data-main-nav] a")?.focus()');
$page->keys(':focus', 'Shift+Tab');
$wrappedToLast = $page->script('() => {
const nav = document.querySelector("[data-main-nav]");
const links = nav?.querySelectorAll("a[href], button:not([disabled])") ?? [];
return links.length > 0 && document.activeElement === links[links.length - 1];
}');
expect($wrappedToLast)->toBeTrue();
});