272 lines
8.2 KiB
PHP
272 lines
8.2 KiB
PHP
<?php
|
|
|
|
use App\Domains\Webhook\Models\Webhook;
|
|
use App\Domains\Workspace\Models\Workspace;
|
|
use App\Domains\Workspace\Models\WorkspaceInvitation;
|
|
use App\Domains\Workspace\Models\WorkspaceMember;
|
|
use App\Livewire\Modals\ConfirmAction;
|
|
use App\Livewire\Modals\CreateApiToken;
|
|
use App\Livewire\Modals\CreateWebhook;
|
|
use App\Livewire\Modals\CreateWorkspace;
|
|
use App\Livewire\Modals\DeleteWebhook;
|
|
use App\Livewire\Modals\DeleteWorkspace;
|
|
use App\Livewire\Modals\EditMemberRole;
|
|
use App\Livewire\Modals\InviteMember;
|
|
use App\Livewire\Modals\RemoveMember;
|
|
use App\Livewire\Modals\RevokeApiToken;
|
|
use App\Models\User;
|
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
use Livewire\Livewire;
|
|
|
|
function makeWsUser(): array
|
|
{
|
|
$user = User::factory()->create();
|
|
$ws = Workspace::factory()->create(['owner_id' => $user->id]);
|
|
WorkspaceMember::create([
|
|
'workspace_id' => $ws->id,
|
|
'user_id' => $user->id,
|
|
'role' => 'owner',
|
|
'joined_at' => now(),
|
|
]);
|
|
return [$user, $ws];
|
|
}
|
|
|
|
// ConfirmAction
|
|
|
|
it('renders confirm modal', function () {
|
|
[$user] = makeWsUser();
|
|
|
|
Livewire::actingAs($user)->test(ConfirmAction::class, [
|
|
'title' => 'Are you sure?',
|
|
'message' => 'This cannot be undone.',
|
|
'confirmEvent' => 'testConfirmed',
|
|
])->assertSee('Are you sure?');
|
|
});
|
|
|
|
it('dispatches confirmEvent on confirm', function () {
|
|
[$user] = makeWsUser();
|
|
|
|
Livewire::actingAs($user)->test(ConfirmAction::class, [
|
|
'confirmEvent' => 'testConfirmed',
|
|
])->call('confirm')
|
|
->assertDispatched('testConfirmed');
|
|
});
|
|
|
|
// InviteMember
|
|
|
|
it('renders invite member modal', function () {
|
|
[$user, $ws] = makeWsUser();
|
|
app()->instance('current_workspace', $ws);
|
|
|
|
Livewire::actingAs($user)->test(InviteMember::class)
|
|
->assertSee('Invite Team Member');
|
|
});
|
|
|
|
it('validates email on invite member', function () {
|
|
[$user, $ws] = makeWsUser();
|
|
app()->instance('current_workspace', $ws);
|
|
|
|
Livewire::actingAs($user)->test(InviteMember::class)
|
|
->set('email', 'not-an-email')
|
|
->call('invite')
|
|
->assertHasErrors(['email']);
|
|
});
|
|
|
|
it('creates invitation and dispatches memberInvited', function () {
|
|
[$user, $ws] = makeWsUser();
|
|
app()->instance('current_workspace', $ws);
|
|
|
|
Livewire::actingAs($user)->test(InviteMember::class)
|
|
->set('email', 'new@example.com')
|
|
->set('role', 'editor')
|
|
->call('invite')
|
|
->assertDispatched('memberInvited');
|
|
|
|
expect(WorkspaceInvitation::where('workspace_id', $ws->id)->where('email', 'new@example.com')->exists())->toBeTrue();
|
|
});
|
|
|
|
// EditMemberRole
|
|
|
|
it('renders edit member role and updates role', function () {
|
|
[$owner, $ws] = makeWsUser();
|
|
$member = User::factory()->create();
|
|
$membership = WorkspaceMember::create([
|
|
'workspace_id' => $ws->id,
|
|
'user_id' => $member->id,
|
|
'role' => 'editor',
|
|
'joined_at' => now(),
|
|
]);
|
|
app()->instance('current_workspace', $ws);
|
|
|
|
Livewire::actingAs($owner)->test(EditMemberRole::class, ['memberId' => $membership->id])
|
|
->assertSee('Change Role')
|
|
->set('role', 'admin')
|
|
->call('save')
|
|
->assertDispatched('memberRoleUpdated');
|
|
|
|
expect($membership->fresh()->role)->toBe('admin');
|
|
});
|
|
|
|
it('rejects edit member role for wrong workspace', function () {
|
|
[$owner, $ws] = makeWsUser();
|
|
app()->instance('current_workspace', $ws);
|
|
|
|
$otherOwner = User::factory()->create();
|
|
$otherWs = Workspace::factory()->create(['owner_id' => $otherOwner->id]);
|
|
$membership = WorkspaceMember::create([
|
|
'workspace_id' => $otherWs->id,
|
|
'user_id' => $otherOwner->id,
|
|
'role' => 'owner',
|
|
'joined_at' => now(),
|
|
]);
|
|
|
|
expect(fn () => Livewire::actingAs($owner)->test(EditMemberRole::class, ['memberId' => $membership->id]))
|
|
->toThrow(ModelNotFoundException::class);
|
|
});
|
|
|
|
// RemoveMember
|
|
|
|
it('removes member and dispatches memberRemoved', function () {
|
|
[$owner, $ws] = makeWsUser();
|
|
$member = User::factory()->create();
|
|
$membership = WorkspaceMember::create([
|
|
'workspace_id' => $ws->id,
|
|
'user_id' => $member->id,
|
|
'role' => 'editor',
|
|
'joined_at' => now(),
|
|
]);
|
|
app()->instance('current_workspace', $ws);
|
|
|
|
Livewire::actingAs($owner)->test(RemoveMember::class, ['memberId' => $membership->id])
|
|
->call('remove')
|
|
->assertDispatched('memberRemoved');
|
|
|
|
expect(WorkspaceMember::find($membership->id))->toBeNull();
|
|
});
|
|
|
|
// CreateApiToken
|
|
|
|
it('renders create api token modal', function () {
|
|
[$user] = makeWsUser();
|
|
|
|
Livewire::actingAs($user)->test(CreateApiToken::class)
|
|
->assertSee('Create API Token');
|
|
});
|
|
|
|
it('creates token and shows it once', function () {
|
|
[$user] = makeWsUser();
|
|
|
|
$component = Livewire::actingAs($user)->test(CreateApiToken::class)
|
|
->set('name', 'Test token')
|
|
->call('create')
|
|
->assertDispatched('apiTokenCreated');
|
|
|
|
expect($component->get('plainTextToken'))->not->toBeNull();
|
|
});
|
|
|
|
it('validates token name is required', function () {
|
|
[$user] = makeWsUser();
|
|
|
|
Livewire::actingAs($user)->test(CreateApiToken::class)
|
|
->call('create')
|
|
->assertHasErrors(['name' => 'required']);
|
|
});
|
|
|
|
// RevokeApiToken
|
|
|
|
it('revokes token and dispatches apiTokenRevoked', function () {
|
|
[$user] = makeWsUser();
|
|
$token = $user->createToken('My token');
|
|
$pat = $token->accessToken;
|
|
|
|
Livewire::actingAs($user)->test(RevokeApiToken::class, ['tokenId' => $pat->id])
|
|
->call('revoke')
|
|
->assertDispatched('apiTokenRevoked');
|
|
|
|
expect(\Laravel\Sanctum\PersonalAccessToken::find($pat->id))->toBeNull();
|
|
});
|
|
|
|
// CreateWebhook
|
|
|
|
it('creates webhook and dispatches webhookCreated', function () {
|
|
[$user, $ws] = makeWsUser();
|
|
app()->instance('current_workspace', $ws);
|
|
|
|
Livewire::actingAs($user)->test(CreateWebhook::class)
|
|
->set('url', 'https://example.com/hook')
|
|
->set('selectedEvents', ['link.clicked'])
|
|
->call('save')
|
|
->assertDispatched('webhookCreated');
|
|
|
|
expect(Webhook::where('workspace_id', $ws->id)->where('url', 'https://example.com/hook')->exists())->toBeTrue();
|
|
});
|
|
|
|
it('validates url and events on create webhook', function () {
|
|
[$user, $ws] = makeWsUser();
|
|
app()->instance('current_workspace', $ws);
|
|
|
|
Livewire::actingAs($user)->test(CreateWebhook::class)
|
|
->call('save')
|
|
->assertHasErrors(['url', 'selectedEvents']);
|
|
});
|
|
|
|
// DeleteWebhook
|
|
|
|
it('deletes webhook and dispatches webhookDeleted', function () {
|
|
[$user, $ws] = makeWsUser();
|
|
app()->instance('current_workspace', $ws);
|
|
|
|
$webhook = Webhook::factory()->create(['workspace_id' => $ws->id]);
|
|
|
|
Livewire::actingAs($user)->test(DeleteWebhook::class, ['webhookId' => $webhook->id])
|
|
->call('delete')
|
|
->assertDispatched('webhookDeleted');
|
|
|
|
expect(Webhook::find($webhook->id))->toBeNull();
|
|
});
|
|
|
|
// CreateWorkspace
|
|
|
|
it('creates workspace and dispatches workspaceCreated', function () {
|
|
[$user] = makeWsUser();
|
|
|
|
Livewire::actingAs($user)->test(CreateWorkspace::class)
|
|
->set('name', 'New Workspace')
|
|
->call('create')
|
|
->assertDispatched('workspaceCreated');
|
|
|
|
expect(Workspace::where('owner_id', $user->id)->where('name', 'New Workspace')->exists())->toBeTrue();
|
|
});
|
|
|
|
it('validates workspace name is required', function () {
|
|
[$user] = makeWsUser();
|
|
|
|
Livewire::actingAs($user)->test(CreateWorkspace::class)
|
|
->call('create')
|
|
->assertHasErrors(['name' => 'required']);
|
|
});
|
|
|
|
// DeleteWorkspace
|
|
|
|
it('requires confirmation to delete workspace', function () {
|
|
[$user, $ws] = makeWsUser();
|
|
|
|
Livewire::actingAs($user)->test(DeleteWorkspace::class, ['workspaceUlid' => $ws->ulid])
|
|
->set('confirmation', 'Wrong name')
|
|
->call('delete')
|
|
->assertHasErrors(['confirmation']);
|
|
|
|
expect(Workspace::find($ws->id))->not->toBeNull();
|
|
});
|
|
|
|
it('soft-deletes workspace with correct confirmation', function () {
|
|
[$user, $ws] = makeWsUser();
|
|
|
|
Livewire::actingAs($user)->test(DeleteWorkspace::class, ['workspaceUlid' => $ws->ulid])
|
|
->set('confirmation', $ws->name)
|
|
->call('delete')
|
|
->assertDispatched('workspaceDeleted');
|
|
|
|
expect(Workspace::withTrashed()->find($ws->id)->deleted_at)->not->toBeNull();
|
|
});
|