108 lines
4.6 KiB
PHP
108 lines
4.6 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Project;
|
|
use App\Models\Team;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Inertia\Testing\AssertableInertia as Assert;
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
use Symfony\Component\Process\Process;
|
|
use Tests\TestCase;
|
|
|
|
class NavigationTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
#[DataProvider('projectRoles')]
|
|
public function test_navigation_uses_effective_project_permissions(string $role, bool $teamAccess, bool $canManage, bool $canCreateTickets): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create();
|
|
$assignee = $user;
|
|
if ($teamAccess) {
|
|
$assignee = Team::factory()->create();
|
|
$assignee->users()->attach($user);
|
|
}
|
|
$this->syncProjectPermissions($project, $assignee, [$role]);
|
|
|
|
$this->actingAs($user)->get(route('projects.board.index', $project))
|
|
->assertOk()->assertInertia(fn (Assert $page) => $page
|
|
->where('navigation.project', [
|
|
'id' => $project->id,
|
|
'name' => $project->name,
|
|
'key' => $project->key,
|
|
'canManage' => $canManage,
|
|
'canCreateTickets' => $canCreateTickets,
|
|
]));
|
|
}
|
|
|
|
/** @return array<string, array{string, bool, bool, bool}> */
|
|
public static function projectRoles(): array
|
|
{
|
|
return [
|
|
'reader' => ['read', false, false, false],
|
|
'writer' => ['write', false, false, true],
|
|
'project admin' => ['admin', false, true, true],
|
|
'team reader' => ['read', true, false, false],
|
|
'team writer' => ['write', true, false, true],
|
|
'team admin' => ['admin', true, true, true],
|
|
];
|
|
}
|
|
|
|
public function test_global_admin_has_project_actions_without_direct_membership(): void
|
|
{
|
|
$user = User::factory()->admin()->create();
|
|
$project = Project::factory()->create();
|
|
$this->actingAs($user)->get(route('projects.show', $project))
|
|
->assertOk()->assertInertia(fn (Assert $page) => $page
|
|
->where('navigation.project.canManage', true)
|
|
->where('navigation.project.canCreateTickets', true)
|
|
->where('auth.permissions.globalAdmin', true));
|
|
}
|
|
|
|
public function test_every_project_admin_navigation_destination_is_authorized(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create();
|
|
$this->syncProjectPermissions($project, $user, ['admin']);
|
|
foreach (['projects.show', 'projects.board.index', 'projects.backlog.index', 'projects.settings.general', 'projects.settings.board', 'projects.settings.metadata', 'projects.settings.repositories', 'projects.settings.notifications', 'projects.access'] as $route) {
|
|
$this->actingAs($user)->get(route($route, $project))
|
|
->assertOk()->assertInertia(fn (Assert $page) => $page
|
|
->where('navigation.project.id', $project->id)
|
|
->where('navigation.project.canManage', true)
|
|
->where('auth.permissions.globalAdmin', false));
|
|
}
|
|
}
|
|
|
|
public function test_project_context_is_cleared_when_leaving_a_project(): void
|
|
{
|
|
$user = User::factory()->admin()->create();
|
|
$project = Project::factory()->create();
|
|
$this->actingAs($user)->get(route('projects.show', $project))
|
|
->assertInertia(fn (Assert $page) => $page->where('navigation.project.id', $project->id));
|
|
$this->get(route('dashboard'))->assertOk()
|
|
->assertInertia(fn (Assert $page) => $page->where('navigation.project', null));
|
|
$this->get(route('projects.create'))->assertOk()
|
|
->assertInertia(fn (Assert $page) => $page->where('navigation.project', null));
|
|
}
|
|
|
|
public function test_unrelated_projects_remain_inaccessible(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create(['name' => 'Private project']);
|
|
$this->actingAs($user)->get(route('projects.show', $project))
|
|
->assertForbidden()->assertDontSee('Private project');
|
|
}
|
|
|
|
public function test_navigation_rendering_and_url_matching(): void
|
|
{
|
|
$process = new Process(['node', 'tests/Fixtures/navigation.mjs'], base_path());
|
|
$process->setTimeout(60);
|
|
$process->run();
|
|
$this->assertTrue($process->isSuccessful(), $process->getOutput().$process->getErrorOutput());
|
|
$this->assertStringContainsString('Navigation behavior checks passed.', $process->getOutput());
|
|
}
|
|
}
|