feat(KAN-25): build personal dashboard
CI/CD / test_and_check_app (push) Canceled after 0s
CI/CD / Publish immutable image (push) Canceled after 0s
CI/CD / test_and_check_app (pull_request) Canceled after 0s
CI/CD / Publish immutable image (pull_request) Canceled after 0s

This commit is contained in:
2026-09-11 14:20:27 +02:00
parent 4d6da2c5f5
commit 3914fd5432
10 changed files with 721 additions and 39 deletions
+95 -4
View File
@@ -2,26 +2,117 @@
namespace Tests\Feature;
use App\Models\Project;
use App\Models\Team;
use App\Models\Ticket;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Inertia\Testing\AssertableInertia as Assert;
use Symfony\Component\Process\Process;
use Tests\TestCase;
class DashboardTest extends TestCase
{
use RefreshDatabase;
public function test_guests_are_redirected_to_the_login_page()
public function test_guests_are_redirected_to_the_login_page(): void
{
$response = $this->get(route('dashboard'));
$response->assertRedirect(route('login'));
}
public function test_authenticated_users_can_visit_the_dashboard()
public function test_authenticated_users_see_an_empty_dashboard_without_assigned_tickets(): void
{
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->get(route('dashboard'));
$response->assertOk();
$this->get(route('dashboard'))
->assertOk()
->assertInertia(fn (Assert $page) => $page
->component('Dashboard')
->has('assignedTicketsByProject', 0));
}
public function test_dashboard_only_contains_the_users_tickets_from_visible_projects(): void
{
$user = User::factory()->create();
$otherUser = User::factory()->create();
$directProject = Project::factory()->create([
'key' => 'WEB',
'name' => 'Web platform',
]);
$teamProject = Project::factory()->create([
'key' => 'OPS',
'name' => 'Operations',
]);
$hiddenProject = Project::factory()->create([
'key' => 'SEC',
'name' => 'Private security',
]);
$team = Team::factory()->create();
$team->users()->attach($user);
$this->syncProjectPermissions($directProject, $user, ['view']);
$this->syncProjectPermissions($teamProject, $team, ['view']);
Ticket::factory()->create([
'assignee_user_id' => $user->id,
'project_id' => $directProject->id,
'sequence_number' => 1,
'ticket_key' => 'WEB-1',
'title' => 'Older assigned ticket',
]);
Ticket::factory()->create([
'assignee_user_id' => $user->id,
'project_id' => $directProject->id,
'sequence_number' => 2,
'ticket_key' => 'WEB-2',
'title' => 'Newest assigned ticket',
]);
Ticket::factory()->create([
'assignee_user_id' => $user->id,
'project_id' => $teamProject->id,
'sequence_number' => 1,
'ticket_key' => 'OPS-1',
'title' => 'Team-visible assigned ticket',
]);
Ticket::factory()->create([
'assignee_user_id' => $otherUser->id,
'project_id' => $directProject->id,
'sequence_number' => 3,
'ticket_key' => 'WEB-3',
'title' => 'Another users ticket',
]);
Ticket::factory()->create([
'assignee_user_id' => $user->id,
'project_id' => $hiddenProject->id,
'sequence_number' => 1,
'ticket_key' => 'SEC-1',
'title' => 'Hidden project ticket',
]);
$this->actingAs($user)
->get(route('dashboard'))
->assertOk()
->assertInertia(fn (Assert $page) => $page
->component('Dashboard')
->has('assignedTicketsByProject', 2)
->where('assignedTicketsByProject.0.project.id', $teamProject->id)
->has('assignedTicketsByProject.0.tickets', 1)
->where('assignedTicketsByProject.0.tickets.0.ticket_key', 'OPS-1')
->where('assignedTicketsByProject.1.project.id', $directProject->id)
->has('assignedTicketsByProject.1.tickets', 2)
->where('assignedTicketsByProject.1.tickets.0.ticket_key', 'WEB-2')
->where('assignedTicketsByProject.1.tickets.1.ticket_key', 'WEB-1'));
}
public function test_dashboard_presentation_uses_real_data_and_handles_empty_state(): void
{
$process = new Process(['node', 'tests/Fixtures/dashboard.mjs'], base_path());
$process->setTimeout(60);
$process->run();
$this->assertTrue($process->isSuccessful(), $process->getOutput().$process->getErrorOutput());
$this->assertStringContainsString('Dashboard behavior checks passed.', $process->getOutput());
}
}