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
99 lines
3.1 KiB
PHP
99 lines
3.1 KiB
PHP
<?php
|
|
|
|
use App\Livewire\Admin\AdminDashboard;
|
|
use App\Models\Share;
|
|
use App\Models\ShareFile;
|
|
use App\Models\User;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Livewire\Livewire;
|
|
|
|
test('admin dashboard requires authentication', function () {
|
|
$response = $this->get(route('admin.dashboard'));
|
|
|
|
$response->assertRedirect(route('login'));
|
|
});
|
|
|
|
test('non-admin user cannot access admin dashboard', function () {
|
|
$user = User::factory()->create(['is_admin' => false]);
|
|
|
|
$response = $this->actingAs($user)->get(route('admin.dashboard'));
|
|
|
|
$response->assertForbidden();
|
|
});
|
|
|
|
test('admin can access dashboard', function () {
|
|
$admin = User::query()->where('is_admin', true)->first();
|
|
|
|
$response = $this->actingAs($admin)->get(route('admin.dashboard'));
|
|
|
|
$response->assertOk();
|
|
});
|
|
|
|
test('admin dashboard shows stats', function () {
|
|
$admin = User::query()->where('is_admin', true)->first();
|
|
|
|
$share = Share::factory()->create(['total_size' => 1024]);
|
|
ShareFile::factory()->create(['share_id' => $share->id]);
|
|
|
|
$response = $this->actingAs($admin)->get(route('admin.dashboard'));
|
|
|
|
$response->assertOk();
|
|
$response->assertSee('Total Shares');
|
|
$response->assertSee('Active Shares');
|
|
$response->assertSee('Total Files');
|
|
$response->assertSee('Disk Usage');
|
|
});
|
|
|
|
test('admin can delete share', function () {
|
|
Storage::fake('shares');
|
|
|
|
$admin = User::query()->where('is_admin', true)->first();
|
|
|
|
$share = Share::factory()->create();
|
|
$shareId = $share->id;
|
|
|
|
Livewire::actingAs($admin)
|
|
->test(AdminDashboard::class)
|
|
->set('deletingShareId', $shareId)
|
|
->call('deleteShare', $shareId)
|
|
->assertSet('deletingShareId', null);
|
|
|
|
expect(Share::query()->find($shareId))->toBeNull();
|
|
});
|
|
|
|
test('admin dashboard shows shares table', function () {
|
|
$admin = User::query()->where('is_admin', true)->first();
|
|
|
|
$share = Share::factory()->create(['token' => 'testtoken12345678']);
|
|
|
|
$response = $this->actingAs($admin)->get(route('admin.dashboard'));
|
|
|
|
$response->assertOk();
|
|
$response->assertSee('testtoken12345678');
|
|
});
|
|
|
|
test('the shares table sorts only by its own columns', function () {
|
|
$admin = User::query()->where('is_admin', true)->first();
|
|
|
|
Share::factory()->create(['token' => 'aaaaaaaaaaaaaaaa', 'download_count' => 9]);
|
|
Share::factory()->create(['token' => 'zzzzzzzzzzzzzzzz', 'download_count' => 1]);
|
|
|
|
Livewire::actingAs($admin)
|
|
->test(AdminDashboard::class)
|
|
->set('sortBy', ['column' => 'download_count', 'direction' => 'asc'])
|
|
->assertSeeInOrder(['zzzzzzzzzzzzzzzz', 'aaaaaaaaaaaaaaaa'])
|
|
->set('sortBy', ['column' => 'token; drop table shares', 'direction' => 'sideways'])
|
|
->assertOk();
|
|
|
|
expect(Share::query()->count())->toBe(2);
|
|
});
|
|
|
|
test('without shares the dashboard shows an empty state instead of the table', function () {
|
|
$admin = User::query()->where('is_admin', true)->first();
|
|
|
|
$this->actingAs($admin)->get(route('admin.dashboard'))
|
|
->assertOk()
|
|
->assertSee('No shares yet')
|
|
->assertDontSee('<table', false);
|
|
});
|