Inline the error layout's own rules beside the app's build
Plan step 46, from SealShare's breakpoint walk: an application imports the stylesheets its own views render, and none renders the error layout, so under a real build its 403/404/500 pages drew unstyled. With a build the page now keeps the app's Vite tags (foundation, tokens, scheme, font) and inlines Stylesheets::bundle() of components/error-page.css and its imports beside them (ErrorPage::layoutStyles(), cached). Without a build the fallback is unchanged. An application imports nothing for its error pages; one that imports all.css gets the same layered rules twice, which is harmless. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
05f115ab36
commit
70c983b7bb
@@ -8,6 +8,7 @@ use Illuminate\Support\Str;
|
||||
use NoNameWeb\LivewireMaterial\LivewireMaterialServiceProvider;
|
||||
use NoNameWeb\LivewireMaterial\Support\ErrorPage;
|
||||
use NoNameWeb\LivewireMaterial\Support\Scheme;
|
||||
use NoNameWeb\LivewireMaterial\Support\Stylesheets;
|
||||
use NoNameWeb\LivewireMaterial\Tests\Support\ComponentStylesheet;
|
||||
use NoNameWeb\LivewireMaterial\Tests\Support\ViewClasses;
|
||||
|
||||
@@ -94,7 +95,13 @@ it('lets the application\'s own error view win', function () {
|
||||
$this->get('/abort/403')->assertSee('data-md-error-page', false);
|
||||
});
|
||||
|
||||
it('loads the application\'s Vite entries', function () {
|
||||
/**
|
||||
* Plan step 46: an application's entry imports the stylesheets its own views render, and none of
|
||||
* them renders this layout, so with a build the page inlines its own layout's rules beside the
|
||||
* application's tags rather than hoping the entry imported `error-page.css` — SealShare's didn't,
|
||||
* and its 403/404/500 pages drew unstyled under its real build.
|
||||
*/
|
||||
it('loads the application\'s Vite entries and inlines the error layout\'s own rules beside them', function () {
|
||||
File::ensureDirectoryExists($this->temporary.'/build');
|
||||
File::put($this->temporary.'/build/manifest.json', json_encode([
|
||||
'workbench/resources/css/app.css' => ['file' => 'assets/app-probe.css', 'src' => 'workbench/resources/css/app.css', 'isEntry' => true],
|
||||
@@ -104,11 +111,27 @@ it('loads the application\'s Vite entries', function () {
|
||||
app()->usePublicPath($this->temporary);
|
||||
Vite::useHotFile($this->temporary.'/hot');
|
||||
|
||||
$this->get('/abort/404')
|
||||
$html = $this->get('/abort/404')
|
||||
->assertNotFound()
|
||||
->assertSee('build/assets/app-probe.css', false)
|
||||
->assertSee('build/assets/app-probe.js', false)
|
||||
->assertDontSee('data-md-error-fallback', false);
|
||||
->assertSee('<style data-md-error-styles>'.ErrorPage::layoutStyles().'</style>', false)
|
||||
->assertDontSee('data-md-error-fallback', false)
|
||||
->getContent();
|
||||
|
||||
preg_match('~<style data-md-error-styles>(.*?)</style>~s', $html, $inlined);
|
||||
$plain = (string) preg_replace('~/\*.*?\*/~s', '', $inlined[1] ?? '');
|
||||
|
||||
// After the application's stylesheet, so its layer statement has already set the order.
|
||||
expect(strpos($html, '<style data-md-error-styles>'))->toBeGreaterThan(strpos($html, 'build/assets/app-probe.css'))
|
||||
// The layout's rules, and those of the button and shape stylesheets it imports.
|
||||
->and($plain)->toContain('body:has(> [data-md-error-page])')
|
||||
->toContain('[data-md-error-shape] svg')
|
||||
->toContain('[data-md-button]')
|
||||
->toContain('[data-md-shape]')
|
||||
// Not the foundation or a scheme: the application's build brings those.
|
||||
->not->toContain('box-sizing: border-box')
|
||||
->not->toMatch('/--md-sys-color-surface\s*:/');
|
||||
});
|
||||
|
||||
it('still renders, in the application\'s scheme, when the Vite manifest is missing', function () {
|
||||
@@ -132,6 +155,20 @@ it('still renders, in the application\'s scheme, when the Vite manifest is missi
|
||||
->assertDontSee('/build/', false);
|
||||
});
|
||||
|
||||
it('keeps the fallback as the only stylesheet when the Vite manifest is missing', function () {
|
||||
app()->usePublicPath($this->temporary);
|
||||
Vite::useHotFile($this->temporary.'/hot');
|
||||
|
||||
$html = $this->get('/abort/404')
|
||||
->assertNotFound()
|
||||
->assertSee('<style data-md-error-fallback>'.ErrorPage::fallbackStyles().'</style>', false)
|
||||
->assertDontSee('data-md-error-styles', false)
|
||||
->getContent();
|
||||
|
||||
expect(substr_count($html, '<style'))->toBe(1)
|
||||
->and($html)->not->toContain('<link rel="stylesheet"');
|
||||
});
|
||||
|
||||
it('draws the standard, medium and high contrast levels when the Vite manifest is missing', function () {
|
||||
File::put($this->temporary.'/material-scheme.json', json_encode([
|
||||
'light' => ['surface' => '#fafaf0', 'primary' => '#123456'],
|
||||
@@ -349,3 +386,23 @@ it('bundles the foundation and the error layout into the fallback, without an im
|
||||
->toContain('box-sizing: border-box')
|
||||
->toContain('--md-sys-color-surface');
|
||||
});
|
||||
|
||||
/**
|
||||
* Plan step 46: what the page inlines beside a build is `Stylesheets::bundle()` of the error layout
|
||||
* alone. It reaches no `@font-face` — the package's only one is tokens/font.css's, which only the
|
||||
* foundation imports, and the application's build serves it — and no `url()` a request from the
|
||||
* page would have to resolve, so nothing needs dropping from it the way the fallback drops the face.
|
||||
*/
|
||||
it('inlines beside a build the error layout\'s bundle, which holds no font face, url or import', function () {
|
||||
$layout = realpath(__DIR__.'/../../resources/css/components/error-page.css');
|
||||
$css = (string) ErrorPage::layoutStyles();
|
||||
$plain = (string) preg_replace('~/\*.*?\*/~s', '', $css);
|
||||
|
||||
expect($css)->toBe(Stylesheets::bundle([$layout]))
|
||||
->and(array_map(basename(...), Stylesheets::resolvedFiles([$layout])))
|
||||
->toEqualCanonicalizing(['error-page.css', 'button.css', 'icon.css', 'loading.css', 'tooltip.css', 'shape.css'])
|
||||
->and($plain)->not->toContain('@font-face')
|
||||
->not->toContain('@import')
|
||||
->not->toMatch('/\burl\(/i')
|
||||
->toContain('@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user