120 lines
3.8 KiB
PHP
120 lines
3.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Views;
|
|
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
/**
|
|
* Static a11y checks for the shared layout
|
|
* (`app/views/layouts/main.php`). We verify the layout's *source*
|
|
* carries the right ARIA / landmark attributes rather than spinning
|
|
* up the full render (which would need nav, OG, structured-data,
|
|
* server-vars, and a dozen more locals). The home view is tested
|
|
* for content-level a11y in `HomeViewA11yTest`.
|
|
*/
|
|
final class LayoutA11yTest extends TestCase
|
|
{
|
|
private string $layoutSource;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$path = dirname(__DIR__, 2) . '/app/views/layouts/main.php';
|
|
self::assertFileExists($path);
|
|
$this->layoutSource = (string) file_get_contents($path);
|
|
self::assertNotEmpty($this->layoutSource);
|
|
}
|
|
|
|
#[Test]
|
|
public function htmlRootCarriesLangAttribute(): void
|
|
{
|
|
// The layout emits <html lang="..."> with the value rendered by PHP from
|
|
// the $locale variable (not literally `$locale` between quotes).
|
|
// We assert the source binds lang to the $locale variable inside PHP output.
|
|
self::assertStringContainsString('<html lang="', $this->layoutSource);
|
|
self::assertStringContainsString('$locale', $this->layoutSource);
|
|
// Make sure $locale actually flows into the lang attribute (basic
|
|
// co-occurrence check on the same <html ...> opening tag).
|
|
self::assertMatchesRegularExpression(
|
|
'~<html\b[^>]*\blang=[^>]*\$locale~s',
|
|
$this->layoutSource,
|
|
'Layout must bind <html lang> to the current locale'
|
|
);
|
|
}
|
|
|
|
#[Test]
|
|
public function skipLinkPointsAtMain(): void
|
|
{
|
|
self::assertStringContainsString('class="skip-link"', $this->layoutSource);
|
|
self::assertStringContainsString('href="#main"', $this->layoutSource);
|
|
}
|
|
|
|
#[Test]
|
|
public function mainLandmarkHasIdAndAriaLabel(): void
|
|
{
|
|
self::assertMatchesRegularExpression(
|
|
'/<main\b[^>]*\bid="main"/',
|
|
$this->layoutSource
|
|
);
|
|
self::assertMatchesRegularExpression(
|
|
'/<main\b[^>]*\baria-label=/',
|
|
$this->layoutSource
|
|
);
|
|
}
|
|
|
|
#[Test]
|
|
public function navLandmarkHasAriaLabel(): void
|
|
{
|
|
self::assertMatchesRegularExpression(
|
|
'/<nav\b[^>]*\baria-label=/',
|
|
$this->layoutSource
|
|
);
|
|
}
|
|
|
|
#[Test]
|
|
public function footerHasRoleAndLabel(): void
|
|
{
|
|
self::assertMatchesRegularExpression(
|
|
'/<footer\b[^>]*\baria-label=/',
|
|
$this->layoutSource
|
|
);
|
|
}
|
|
|
|
#[Test]
|
|
public function lightboxIsADialogModal(): void
|
|
{
|
|
self::assertStringContainsString('id="lightbox"', $this->layoutSource);
|
|
self::assertStringContainsString('role="dialog"', $this->layoutSource);
|
|
self::assertStringContainsString('aria-modal="true"', $this->layoutSource);
|
|
}
|
|
|
|
#[Test]
|
|
public function lightboxImageCarriesAltAttribute(): void
|
|
{
|
|
self::assertMatchesRegularExpression(
|
|
'/<img\b[^>]*\bid="lightboxImg"[^>]*\balt=/',
|
|
$this->layoutSource
|
|
);
|
|
}
|
|
|
|
#[Test]
|
|
public function localeSwitcherHostIsAriaLabeled(): void
|
|
{
|
|
// The LocaleSwitcher widget uses aria-label on its <ul>.
|
|
self::assertStringContainsString('aria-label="', $this->layoutSource);
|
|
}
|
|
|
|
#[Test]
|
|
public function mobileNavToggleIsKeyboardAccessible(): void
|
|
{
|
|
self::assertMatchesRegularExpression(
|
|
'/<button\b[^>]*\bclass="nav-hamburger"/',
|
|
$this->layoutSource
|
|
);
|
|
self::assertStringContainsString('aria-expanded=', $this->layoutSource);
|
|
self::assertStringContainsString('aria-controls=', $this->layoutSource);
|
|
}
|
|
}
|