Move SealShare onto Livewire Material
Replaces maryUI and daisyUI with nonameweb/livewire-material: the Vibrant indigo scheme, a system/light/dark theme under sealshare-theme, one top app bar with the account menu, the upload drop zone and link-ready moments, M3 fields, dialogs instead of wire:confirm, snackbars instead of flashed messages, a sortable admin table, and the starter-kit cleanup. Docker builds assets after Composer; CI drops the Flux step. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
This commit is contained in:
co-authored by
Claude Opus 5
parent
6d7120e8e2
commit
c4a17b65c8
@@ -54,7 +54,9 @@ test('admin can delete share', function () {
|
||||
|
||||
Livewire::actingAs($admin)
|
||||
->test(AdminDashboard::class)
|
||||
->call('deleteShare', $shareId);
|
||||
->set('deletingShareId', $shareId)
|
||||
->call('deleteShare', $shareId)
|
||||
->assertSet('deletingShareId', null);
|
||||
|
||||
expect(Share::query()->find($shareId))->toBeNull();
|
||||
});
|
||||
@@ -69,3 +71,19 @@ test('admin dashboard shows shares table', function () {
|
||||
$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);
|
||||
});
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
use App\Livewire\Admin\AdminSettings;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Livewire\Livewire;
|
||||
|
||||
test('admin settings requires authentication', function () {
|
||||
@@ -41,7 +43,8 @@ test('admin can save settings', function () {
|
||||
->set('maxSizePerShare', 5)
|
||||
->set('defaultExpiration', '7d')
|
||||
->call('saveSettings')
|
||||
->assertHasNoErrors();
|
||||
->assertHasNoErrors()
|
||||
->assertDispatched('toast', type: 'success', title: 'Settings saved successfully.');
|
||||
|
||||
expect(Setting::get('max_file_size'))->toBe((string) (min(200, $phpMaxMb) * 1024 * 1024));
|
||||
expect(Setting::get('max_storage_quota'))->toBe((string) (50 * 1024 * 1024 * 1024));
|
||||
@@ -73,8 +76,11 @@ test('admin can clear system password', function () {
|
||||
|
||||
Livewire::actingAs($admin)
|
||||
->test(AdminSettings::class)
|
||||
->set('confirmingPasswordRemoval', true)
|
||||
->call('clearSystemPassword')
|
||||
->assertHasNoErrors();
|
||||
->assertHasNoErrors()
|
||||
->assertSet('confirmingPasswordRemoval', false)
|
||||
->assertDispatched('toast', type: 'success', title: 'System password cleared.');
|
||||
|
||||
expect(Setting::get('system_password'))->toBeNull();
|
||||
});
|
||||
@@ -103,3 +109,22 @@ test('settings validation rejects invalid values', function () {
|
||||
->call('saveSettings')
|
||||
->assertHasErrors(['maxFileSize', 'maxStorageQuota']);
|
||||
});
|
||||
|
||||
test('admin can remove the logo through its dialog', function () {
|
||||
Storage::fake('public');
|
||||
|
||||
$admin = User::query()->where('is_admin', true)->first();
|
||||
$path = UploadedFile::fake()->image('logo.png')->store('branding', 'public');
|
||||
Setting::set('site_logo', $path);
|
||||
|
||||
Livewire::actingAs($admin)
|
||||
->test(AdminSettings::class)
|
||||
->assertSeeHtml('data-test="remove-logo"')
|
||||
->set('confirmingLogoRemoval', true)
|
||||
->call('removeLogo')
|
||||
->assertSet('confirmingLogoRemoval', false)
|
||||
->assertDispatched('toast', type: 'success', title: 'Logo removed.');
|
||||
|
||||
expect(Setting::get('site_logo'))->toBeNull();
|
||||
Storage::disk('public')->assertMissing($path);
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@ test('users can authenticate using the login screen', function () {
|
||||
|
||||
$response
|
||||
->assertSessionHasNoErrors()
|
||||
->assertRedirect(route('dashboard', absolute: false));
|
||||
->assertRedirect(route('admin.dashboard', absolute: false));
|
||||
|
||||
$this->assertAuthenticated();
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ test('email can be verified', function () {
|
||||
Event::assertDispatched(Verified::class);
|
||||
|
||||
expect($user->fresh()->hasVerifiedEmail())->toBeTrue();
|
||||
$response->assertRedirect(route('dashboard', absolute: false).'?verified=1');
|
||||
$response->assertRedirect(route('admin.dashboard', absolute: false).'?verified=1');
|
||||
});
|
||||
|
||||
test('email is not verified with invalid hash', function () {
|
||||
@@ -60,7 +60,7 @@ test('already verified user visiting verification link is redirected without fir
|
||||
);
|
||||
|
||||
$this->actingAs($user)->get($verificationUrl)
|
||||
->assertRedirect(route('dashboard', absolute: false).'?verified=1');
|
||||
->assertRedirect(route('admin.dashboard', absolute: false).'?verified=1');
|
||||
|
||||
expect($user->fresh()->hasVerifiedEmail())->toBeTrue();
|
||||
Event::assertNotDispatched(Verified::class);
|
||||
|
||||
@@ -2,15 +2,17 @@
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
test('guests are redirected to the login page', function () {
|
||||
$response = $this->get(route('dashboard'));
|
||||
$response->assertRedirect(route('login'));
|
||||
test('the starter placeholder dashboard is gone', function () {
|
||||
$this->actingAs(User::factory()->admin()->create())
|
||||
->get('/dashboard')
|
||||
->assertNotFound();
|
||||
});
|
||||
|
||||
test('authenticated users can visit the dashboard', function () {
|
||||
$user = User::factory()->create();
|
||||
$this->actingAs($user);
|
||||
test('a signed-in admin lands on the admin dashboard', function () {
|
||||
$admin = User::factory()->admin()->create();
|
||||
|
||||
$response = $this->get(route('dashboard'));
|
||||
$response->assertOk();
|
||||
$this->post(route('login.store'), ['email' => $admin->email, 'password' => 'password'])
|
||||
->assertRedirect(route('admin.dashboard', absolute: false));
|
||||
|
||||
$this->get(route('admin.dashboard'))->assertOk();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\File;
|
||||
use NoNameWeb\LivewireMaterial\Testing\DesignGuard;
|
||||
|
||||
test('views and code use only what the design system compiles', function () {
|
||||
expect(DesignGuard::scan([resource_path('views'), resource_path('js'), app_path()])->violations())->toBe([]);
|
||||
});
|
||||
|
||||
test('nothing of maryUI or daisyUI is left behind', function () {
|
||||
$leftovers = collect(['resources/views', 'resources/js', 'resources/css', 'app'])
|
||||
->flatMap(fn (string $path) => File::allFiles(base_path($path)))
|
||||
->filter(fn (SplFileInfo $file): bool => preg_match('/\b(base-content|bg-base-\d|btn(-[a-z]+)?|x-mary-|daisyui|robsontenorio)\b/', $file->getContents()) === 1)
|
||||
->map(fn (SplFileInfo $file): string => str_replace(base_path().'/', '', $file->getPathname()))
|
||||
->values()
|
||||
->all();
|
||||
|
||||
expect($leftovers)->toBe([]);
|
||||
});
|
||||
@@ -17,7 +17,8 @@ test('password can be updated', function () {
|
||||
->set('password_confirmation', 'new-password')
|
||||
->call('updatePassword');
|
||||
|
||||
$response->assertHasNoErrors();
|
||||
$response->assertHasNoErrors()
|
||||
->assertDispatched('toast', type: 'success', title: 'Saved.');
|
||||
|
||||
expect(Hash::check('new-password', $user->refresh()->password))->toBeTrue();
|
||||
});
|
||||
|
||||
@@ -19,7 +19,9 @@ test('profile information can be updated', function () {
|
||||
->set('email', 'test@example.com')
|
||||
->call('updateProfileInformation');
|
||||
|
||||
$response->assertHasNoErrors();
|
||||
$response->assertHasNoErrors()
|
||||
->assertDispatched('profile-updated')
|
||||
->assertDispatched('toast', type: 'success', title: 'Saved.');
|
||||
|
||||
$user->refresh();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user