The top app bar repeated the site's name, which already heads the upload and download pages, and spread its actions to the window's far edge. A floating toolbar centred above the bottom edge, as wide as its buttons, now holds them: Upload and the admin pages with the current one filled, and the account menu; guests get Upload, the theme toggle and Log in, without tooltips. On a phone the sign-in card no longer stretches to the full height, and the admin dashboard's empty state sits outside the scrolling table. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
59 lines
2.4 KiB
PHP
59 lines
2.4 KiB
PHP
<?php
|
|
|
|
use App\Models\User;
|
|
|
|
/**
|
|
* The page's main navigation, the floating toolbar at the bottom.
|
|
*/
|
|
function toolbar(string $html): string
|
|
{
|
|
preg_match('/<nav aria-label="Main">.*?<\/nav>/s', $html, $matches);
|
|
|
|
return $matches[0] ?? '';
|
|
}
|
|
|
|
/**
|
|
* The opening tag of the toolbar's link to a URL.
|
|
*/
|
|
function toolbarLink(string $html, string $url): string
|
|
{
|
|
preg_match('/<a\s[^>]*href="'.preg_quote($url, '/').'"[^>]*>/', toolbar($html), $matches);
|
|
|
|
return $matches[0] ?? '';
|
|
}
|
|
|
|
test('pages have a floating toolbar at the bottom instead of a top app bar', function () {
|
|
$html = $this->get(route('upload'))->assertOk()->getContent();
|
|
|
|
expect($html)->not->toContain('data-app-bar')
|
|
->and(toolbar($html))->toContain('role="toolbar"')->toContain('data-toolbar-place="bottom"');
|
|
});
|
|
|
|
test('a guest gets the upload page, the theme toggle and a way to log in, without tooltips', function () {
|
|
$html = $this->get(route('upload'))->getContent();
|
|
|
|
expect(toolbarLink($html, route('upload')))->toContain('aria-current="page"')->toContain('aria-label="Upload"')
|
|
->and(toolbarLink($html, route('login')))->not->toBe('')
|
|
->and(toolbar($html))->toContain('Log in')->toContain('data-theme-toggle')->not->toContain('popover')->not->toContain('data-account-menu');
|
|
|
|
expect(toolbar($this->get(route('login'))->getContent()))->not->toContain(route('login').'"');
|
|
});
|
|
|
|
test('an admin gets the admin pages and the account menu, the current page marked', function () {
|
|
$admin = User::query()->where('is_admin', true)->first();
|
|
|
|
$html = $this->actingAs($admin)->get(route('admin.dashboard'))->assertOk()->getContent();
|
|
|
|
expect(toolbarLink($html, route('admin.dashboard')))->toContain('aria-current="page"')
|
|
->and(toolbarLink($html, route('upload')))->not->toContain('aria-current')
|
|
->and(toolbarLink($html, route('admin.settings')))->not->toContain('aria-current')
|
|
->and(toolbar($html))->toContain('data-account-menu')->toContain('data-test="logout-button"')->not->toContain('Log in');
|
|
});
|
|
|
|
test('a user who is not an admin gets no admin pages', function () {
|
|
$html = $this->actingAs(User::factory()->create(['is_admin' => false]))->get(route('profile.edit'))->assertOk()->getContent();
|
|
|
|
expect(toolbarLink($html, route('upload')))->not->toBe('')
|
|
->and(toolbar($html))->not->toContain(route('admin.dashboard'))->not->toContain(route('admin.settings'));
|
|
});
|