temporary = sys_get_temp_dir().'/livewire-material-errors-'.Str::random(8); File::ensureDirectoryExists($this->temporary); Route::middleware('web')->get('/abort/{code}', fn (string $code) => abort((int) $code, request()->query('message', ''))); }); afterEach(function () { File::deleteDirectory($this->temporary); }); /** * Testbench's skeleton application ships an errors/503.blade.php of its own — an application * view, which rightly wins over the package's. A test of the package's 503 stands in an * application without one. */ function withoutSkeletonErrorViews(): void { $views = test()->temporary.'/views'; File::ensureDirectoryExists($views); config(['view.paths' => [$views, LivewireMaterialServiceProvider::errorViewPath()]]); } it('renders the package page for each status', function (int $code, string $headline, string $sentence) { withoutSkeletonErrorViews(); $this->withoutVite() ->get("/abort/{$code}") ->assertStatus($code) ->assertSee('data-md-error-page', false) ->assertSee("{$code}
", false) ->assertSee($headline) ->assertSee($sentence); })->with([ [403, 'You don’t have access', 'Your account isn’t allowed to open this page.'], [404, 'Page not found', 'The page you’re looking for doesn’t exist or has moved.'], [419, 'This page has expired', 'Refresh the page'], [429, 'Slow down a little', 'Wait a moment, then try again.'], [500, 'Something went wrong', 'An error on our side'], [503, 'We’ll be right back', 'We’re making some improvements.'], ]); it('draws the framework\'s own error pages in the package layout', function () { $this->withoutVite() ->get('/abort/401') ->assertStatus(401) ->assertSee('data-md-error-page', false) ->assertSee('Unauthorized') ->assertSee('Go home'); }); it('appends the error-view root to view.paths after the application\'s, before the view finder is built', function () { $root = LivewireMaterialServiceProvider::errorViewPath(); expect(realpath($root))->toBe(realpath(__DIR__.'/../../resources/views/error-pages')) ->and(File::directories($root))->toBe([$root.'/errors']) ->and(config('view.paths'))->toBe([resource_path('views'), $root]) ->and(app()->viewPath())->toBe(resource_path('views')) ->and(app('view')->getFinder()->getPaths())->toContain($root); $provider = app()->getProvider(LivewireMaterialServiceProvider::class); (fn () => $this->registerErrorViews())->call($provider); expect(config('view.paths'))->toBe([resource_path('views'), $root]); }); it('lets the application\'s own error view win', function () { File::ensureDirectoryExists($this->temporary.'/errors'); File::put($this->temporary.'/errors/404.blade.php', 'The application view'); config(['view.paths' => [$this->temporary, ...config('view.paths')]]); $this->withoutVite() ->get('/abort/404') ->assertNotFound() ->assertSee('The application view') ->assertDontSee('data-md-error-page', false); $this->get('/abort/403')->assertSee('data-md-error-page', false); }); /** * 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], 'workbench/resources/js/app.js' => ['file' => 'assets/app-probe.js', 'src' => 'workbench/resources/js/app.js', 'isEntry' => true], ])); app()->usePublicPath($this->temporary); Vite::useHotFile($this->temporary.'/hot'); $html = $this->get('/abort/404') ->assertNotFound() ->assertSee('build/assets/app-probe.css', false) ->assertSee('build/assets/app-probe.js', false) ->assertSee('', false) ->assertDontSee('data-md-error-fallback', false) ->getContent(); preg_match('~~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, '', false) ->assertDontSee('data-md-error-styles', false) ->getContent(); expect(substr_count($html, '