71 lines
4.3 KiB
PHP
71 lines
4.3 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
require dirname(__DIR__) . '/tests/widget-system/bootstrap.php';
|
||
|
||
use AKSRT\WidgetSystem\WidgetRegistry;
|
||
|
||
$root = dirname(__DIR__);
|
||
$fixture_path = $root . '/tests/fixtures/widget-golden-v1.json';
|
||
$fixture = json_decode((string) file_get_contents($fixture_path), true, 512, JSON_THROW_ON_ERROR);
|
||
$errors = [];
|
||
if (($fixture['schema'] ?? '') !== 'aksrt.widget-golden.v1') $errors[] = 'Invalid golden fixture schema.';
|
||
$components = (array) ($fixture['components'] ?? []);
|
||
$matrix = (array) ($fixture['screenshot_matrix'] ?? []);
|
||
if (array_keys($components) !== array_keys(WidgetRegistry::all()) || count($components) !== 37) $errors[] = 'Golden fixture must contain the exact 37 registry widgets.';
|
||
if (count($matrix) !== 222) $errors[] = 'Screenshot matrix must contain 37 × 3 × 2 = 222 entries.';
|
||
foreach ($components as $slug => $component) {
|
||
$definition = WidgetRegistry::get((string) $slug);
|
||
if ($definition === null) continue;
|
||
if (($component['id_base'] ?? '') !== $definition->id_base()) $errors[] = $slug . ': id_base mismatch.';
|
||
if (($component['settings'] ?? []) !== $definition->normalize((array) ($component['settings'] ?? []))) $errors[] = $slug . ': settings are not normalized.';
|
||
foreach (['default', 'hover', 'focus'] as $state) {
|
||
if (!in_array($state, (array) ($component['states'] ?? []), true)) $errors[] = $slug . ': missing state ' . $state . '.';
|
||
}
|
||
}
|
||
foreach ((array) ($fixture['reference']['files'] ?? []) as $relative => $expected) {
|
||
$path = rtrim((string) ($fixture['reference']['source_root'] ?? ''), '\\/') . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, (string) $relative);
|
||
if (!is_file($path) || hash_file('sha256', $path) !== $expected) $errors[] = 'Reference drift: ' . $relative;
|
||
}
|
||
|
||
$require_results = in_array('--require-results', $argv, true);
|
||
if ($require_results) {
|
||
$result_path = $root . '/tests/visual-results/manifest.json';
|
||
if (!is_file($result_path)) {
|
||
$errors[] = 'Visual result manifest is missing.';
|
||
} else {
|
||
$result = json_decode((string) file_get_contents($result_path), true, 512, JSON_THROW_ON_ERROR);
|
||
if (($result['schema'] ?? '') !== 'aksrt.widget-visual-results.v1') $errors[] = 'Invalid visual result schema.';
|
||
$screenshots = (array) ($result['screenshots'] ?? []);
|
||
foreach ($matrix as $id => $expected) {
|
||
$actual = (array) ($screenshots[$id] ?? []);
|
||
$path = $root . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, (string) ($expected['path'] ?? ''));
|
||
if (!$actual || !is_file($path)) $errors[] = $id . ': screenshot result is missing.';
|
||
}
|
||
foreach ($components as $slug => $component) {
|
||
$states = (array) ($result['components'][$slug]['states'] ?? []);
|
||
foreach ((array) ($component['states'] ?? []) as $state) {
|
||
if (($states[$state] ?? '') !== 'pass') $errors[] = $slug . ': state not passed: ' . $state . '.';
|
||
}
|
||
}
|
||
$acceptance = (array) ($fixture['acceptance'] ?? []);
|
||
$metrics = (array) ($result['metrics'] ?? []);
|
||
if ((float) ($metrics['dimension_delta_px_max'] ?? INF) > (float) $acceptance['dimension_delta_px_max']) $errors[] = 'Dimension delta exceeds threshold.';
|
||
if ((float) ($metrics['pixel_difference_percent_max'] ?? INF) > (float) $acceptance['pixel_difference_percent_max']) $errors[] = 'Pixel difference exceeds threshold.';
|
||
if ((int) ($result['console']['errors'] ?? PHP_INT_MAX) > (int) $acceptance['console_errors_max']) $errors[] = 'Console contains errors.';
|
||
if ((int) ($result['network']['unexpected_failures'] ?? PHP_INT_MAX) > (int) $acceptance['unexpected_network_failures_max']) $errors[] = 'Network contains unexpected failures.';
|
||
if ((int) ($result['a11y']['critical'] ?? PHP_INT_MAX) > (int) $acceptance['a11y_critical_max']) $errors[] = 'Accessibility critical findings remain.';
|
||
if ((int) ($result['a11y']['serious'] ?? PHP_INT_MAX) > (int) $acceptance['a11y_serious_max']) $errors[] = 'Accessibility serious findings remain.';
|
||
if (empty($result['reduced_motion']['passed'])) $errors[] = 'Reduced-motion behavior was not verified.';
|
||
}
|
||
}
|
||
|
||
if ($errors) {
|
||
fwrite(STDERR, "Widget golden validation failed:\n- " . implode("\n- ", array_slice($errors, 0, 50)) . "\n");
|
||
exit(1);
|
||
}
|
||
|
||
echo 'Widget golden fixture passed: 37 components, 222 screenshot targets, frozen reference intact';
|
||
echo $require_results ? ", runtime results passed.\n" : ", runtime results pending.\n";
|