toBeTrue(); $lines = preg_grep('/^[^@\/ }].*\{/', file($path, FILE_IGNORE_NEW_LINES)); $unscoped = array_filter($lines, fn ($l) => ! str_contains($l, 'body.site') && ! str_contains($l, 'html:has(body.site)')); expect($unscoped)->toBe([]); }); test('public script modules exist', function () { foreach (['public.js', 'site/reveal.js', 'site/progress.js'] as $f) { expect(file_exists(resource_path("js/$f")))->toBeTrue(); } }); test('vite config lists the public entries', function () { $cfg = file_get_contents(base_path('vite.config.js')); expect($cfg)->toContain('resources/css/public.css')->toContain('resources/js/public.js'); }); test('home page uses the site foundation', function () { $html = $this->get('/')->assertOk()->getContent(); expect($html) ->toContain('admin()->create(), '/admin/dashboard'], [User::factory()->individual()->create(), '/client/dashboard']] as [$user, $url]) { $html = $this->actingAs($user)->get($url)->assertOk()->getContent(); expect($html) ->not->toContain('public.css') ->not->toContain('public.js') ->not->toContain('Markazi') ->not->toContain('class="site'); } }); test('head theme colour is the forest green', function () { $head = file_get_contents(resource_path('views/partials/head.blade.php')); expect($head)->toContain('content="#2D3624"')->not->toContain('#4A4A42'); }); test('site lang files carry the progress label', function () { expect(trans('site.progress_label', [], 'en'))->toBe('Reading progress'); expect(trans('site.progress_label', [], 'ar'))->toBe('تقدّم القراءة'); }); test('body does not become a scroll container, so the sticky navigation sticks', function () { $css = file_get_contents(resource_path('css/public.css')); expect($css)->toContain('overflow-x:clip')->toContain('box-sizing:border-box;height:72px'); }); test('reveal uses the translate property so hover lifts (transform) are not overridden', function () { $css = file_get_contents(resource_path('css/public.css')); expect($css)->toContain('body.site.js [data-reveal]{opacity:0;translate:0 28px') ->toContain('body.site.js [data-reveal].in{opacity:1;translate:none}') ->not->toContain('[data-reveal].in{opacity:1;transform:none}'); });