presseportale/tests/Feature/BladeAlpineTranslationGuardTest.php

46 lines
1.9 KiB
PHP

<?php
use Illuminate\Support\Facades\File;
/**
* `:attr="__('…')"` ist nur an Blade-Komponenten (`<x-…>`, `<flux:…>`)
* ein PHP-Ausdruck. An einem normalen HTML-Element reicht Blade das
* Attribut unverändert an Alpine durch, und der Browser wirft
* „ReferenceError: __ is not defined“ — so geschehen auf /register
* (GlitchTip browser-pressekonto, Issue 15, 06.10.2026). Für HTML-Elemente
* gilt: `attr="{{ __('…') }}"`.
*/
function alpineTranslationBindingPattern(): string
{
// HTML-Tag (keine Komponente), beliebige Attribute auch über mehrere
// Zeilen, dann ein Alpine-Binding, dessen Ausdruck mit __( beginnt.
return '/<(?!x-|flux:|livewire:|\/)([a-z][a-z0-9-]*)\b((?:[^>"]|"[^"]*")*?)\s(:[a-zA-Z.-]+|x-bind:[a-zA-Z.-]+)="\s*__\(/s';
}
test('no html element binds an alpine attribute to the php translation helper', function () {
$pattern = alpineTranslationBindingPattern();
$offenders = [];
foreach (File::allFiles(resource_path('views')) as $view) {
if (! str_ends_with($view->getFilename(), '.blade.php')) {
continue;
}
$contents = File::get($view->getRealPath());
preg_match_all($pattern, $contents, $matches, PREG_OFFSET_CAPTURE);
foreach ($matches[3] as [$attribute, $offset]) {
$offenders[] = sprintf('%s:%d %s', $view->getRelativePathname(), substr_count($contents, "\n", 0, $offset) + 1, $attribute);
}
}
expect($offenders)->toBe([], "Alpine-Binding mit __() an einem HTML-Element — auf attr=\"{{ __('…') }}\" umstellen:\n".implode("\n", $offenders));
});
test('the guard catches the pattern that broke the registration', function () {
$pattern = alpineTranslationBindingPattern();
expect(preg_match($pattern, "<input\n wire:model=\"name\"\n :placeholder=\"__('Vor- und Nachname')\"\n/>"))->toBe(1)
->and(preg_match($pattern, "<x-web.page-header\n :title=\"__('static.faq.title')\"\n/>"))->toBe(0);
});