diff --git a/app/Http/Controllers/Api/V1/ProjectController.php b/app/Http/Controllers/Api/V1/ProjectController.php index 72ee24f7..3eeee6d5 100644 --- a/app/Http/Controllers/Api/V1/ProjectController.php +++ b/app/Http/Controllers/Api/V1/ProjectController.php @@ -48,7 +48,8 @@ public function index(Organization $organization, ProjectIndexRequest $request): $user = $this->user(); $projectsQuery = Project::query() - ->whereBelongsTo($organization, 'organization'); + ->whereBelongsTo($organization, 'organization') + ->orderBy('name'); if (! $canViewAllProjects) { $projectsQuery->visibleByEmployee($user); diff --git a/database/factories/ProjectFactory.php b/database/factories/ProjectFactory.php index 6bffbfcd..7a4d6cc1 100644 --- a/database/factories/ProjectFactory.php +++ b/database/factories/ProjectFactory.php @@ -38,6 +38,15 @@ public function definition(): array ]; } + public function named(string $name): self + { + return $this->state(function (array $attributes) use ($name): array { + return [ + 'name' => $name, + ]; + }); + } + public function withEstimatedTime(): self { return $this->state(function (array $attributes): array { diff --git a/tests/Unit/Endpoint/Api/V1/ProjectEndpointTest.php b/tests/Unit/Endpoint/Api/V1/ProjectEndpointTest.php index c606ed1f..cfe6f3af 100644 --- a/tests/Unit/Endpoint/Api/V1/ProjectEndpointTest.php +++ b/tests/Unit/Endpoint/Api/V1/ProjectEndpointTest.php @@ -141,6 +141,31 @@ public function test_index_endpoint_with_filter_archived_all_returns_all_project $response->assertJsonCount(4, 'data'); } + public function test_index_endpoint_sorts_projects_by_name(): void + { + // Arrange + $data = $this->createUserWithPermission([ + 'projects:view', + 'projects:view:all', + ]); + $archivedProjects = Project::factory()->forOrganization($data->organization)->archived()->createMany(2); + $nonArchivedProjects = Project::factory()->forOrganization($data->organization)->createMany(2); + Passport::actingAs($data->user); + + // Act + $response = $this->getJson(route('api.v1.projects.index', [ + $data->organization->getKey(), + 'archived' => 'all', + ])); + + // Assert + $response->assertStatus(200); + $response->assertJsonCount(4, 'data'); + + $projectNames = $archivedProjects->merge($nonArchivedProjects)->pluck('name')->sort()->values()->all(); + $this->assertEquals($projectNames, $response->json('data.*.name')); + } + public function test_index_endpoint_returns_list_of_projects_of_organization_which_are_public_or_where_user_is_member_for_user_with_restricted_permission(): void { // Arrange @@ -196,9 +221,9 @@ public function test_index_endpoint_does_not_set_billable_rate_to_null_if_member $organization = $data->organization; $organization->employees_can_see_billable_rates = true; $organization->save(); - $privateProjects = Project::factory()->forOrganization($data->organization)->isPrivate()->billable(111)->createdAt(now()->subMinutes(4))->createMany(2); - $publicProjects = Project::factory()->forOrganization($data->organization)->isPublic()->billable(112)->createdAt(now()->subMinutes(3))->createMany(2); - $privateProjectsWithMembership = Project::factory()->forOrganization($data->organization)->addMember($data->member)->billable(113)->isPrivate()->createdAt(now()->subMinutes(2))->createMany(2); + $privateProjects = Project::factory()->forOrganization($data->organization)->isPrivate()->billable(111)->named('not returned because private')->createMany(2); + $publicProjects = Project::factory()->forOrganization($data->organization)->isPublic()->billable(112)->named('a - returned first')->createMany(2); + $privateProjectsWithMembership = Project::factory()->forOrganization($data->organization)->addMember($data->member)->billable(113)->isPrivate()->named('b - returned second')->createMany(2); Passport::actingAs($data->user); // Act