mirror of
https://github.com/snipe/snipe-it.git
synced 2026-08-18 11:15:42 +00:00
128 lines
5.5 KiB
PHP
128 lines
5.5 KiB
PHP
<?php
|
||
|
||
namespace Tests\Feature\Locations\Api;
|
||
|
||
use App\Models\Location;
|
||
use App\Models\User;
|
||
use Illuminate\Testing\Fluent\AssertableJson;
|
||
use Tests\TestCase;
|
||
|
||
class LocationsForSelectListTest extends TestCase
|
||
{
|
||
public function test_getting_location_list_requires_proper_permission()
|
||
{
|
||
$this->actingAsForApi(User::factory()->create())
|
||
->getJson(route('api.locations.selectlist'))
|
||
->assertForbidden();
|
||
}
|
||
|
||
public function test_locations_returned()
|
||
{
|
||
Location::factory()->create();
|
||
|
||
// see the where the "view.selectlists" is defined in the AuthServiceProvider
|
||
// for info on why "createUsers()" is used here.
|
||
$this->actingAsForApi(User::factory()->createUsers()->create())
|
||
->getJson(route('api.locations.selectlist'))
|
||
->assertOk()
|
||
->assertJsonStructure([
|
||
'results',
|
||
'pagination',
|
||
'total_count',
|
||
'page',
|
||
'page_count',
|
||
])
|
||
->assertJson(fn (AssertableJson $json) => $json->has('results', 1)->etc());
|
||
}
|
||
|
||
public function test_location_is_excluded_from_selectlist_when_exclude_id_matches()
|
||
{
|
||
[$locationA, $locationB] = Location::factory()->count(2)->create();
|
||
|
||
$this->actingAsForApi(User::factory()->createUsers()->create())
|
||
->getJson(route('api.locations.selectlist', ['excludeId' => $locationA->id]))
|
||
->assertOk()
|
||
->assertJson(fn (AssertableJson $json) => $json->where('results', fn ($results) => collect($results)->doesntContain('id', $locationA->id) &&
|
||
collect($results)->contains('id', $locationB->id)
|
||
)->etc()
|
||
);
|
||
}
|
||
|
||
public function test_locations_are_returned_when_user_is_updating_their_profile_and_has_permission_to_update_location()
|
||
{
|
||
$this->actingAsForApi(User::factory()->canEditOwnLocation()->create())
|
||
->withHeader('referer', route('profile'))
|
||
->getJson(route('api.locations.selectlist'))
|
||
->assertOk();
|
||
}
|
||
|
||
public function test_search_result_shows_plain_names_without_parent_chain(): void
|
||
{
|
||
// Per #19398, the location dropdown reverted from the breadcrumb
|
||
// form (`DC1 › RackA`) to plain indentation. Search results are
|
||
// cherry-picked out of the tree so there's no depth to indent by;
|
||
// they render as plain names and the user's search term supplies
|
||
// the disambiguation context.
|
||
$dc1 = Location::factory()->create(['name' => 'DC1']);
|
||
$dc2 = Location::factory()->create(['name' => 'DC2']);
|
||
Location::factory()->create(['name' => 'RackA', 'parent_id' => $dc1->id]);
|
||
Location::factory()->create(['name' => 'RackB', 'parent_id' => $dc2->id]);
|
||
|
||
$response = $this->actingAsForApi(User::factory()->createUsers()->create())
|
||
->getJson(route('api.locations.selectlist', ['search' => 'Rack']))
|
||
->assertOk();
|
||
|
||
$texts = collect($response->json('results'))->pluck('text');
|
||
$this->assertTrue($texts->contains('RackA'));
|
||
$this->assertTrue($texts->contains('RackB'));
|
||
}
|
||
|
||
public function test_search_result_for_deeply_nested_match_shows_plain_name(): void
|
||
{
|
||
// Deeper tree: HQ > DC1 > Rack 1. Only the matched leaf's name
|
||
// renders in the search result — no ancestor chain.
|
||
$hq = Location::factory()->create(['name' => 'HQ']);
|
||
$dc1 = Location::factory()->create(['name' => 'DC1', 'parent_id' => $hq->id]);
|
||
Location::factory()->create(['name' => 'Rack 1', 'parent_id' => $dc1->id]);
|
||
|
||
$response = $this->actingAsForApi(User::factory()->createUsers()->create())
|
||
->getJson(route('api.locations.selectlist', ['search' => 'Rack 1']))
|
||
->assertOk();
|
||
|
||
$texts = collect($response->json('results'))->pluck('text');
|
||
$this->assertTrue($texts->contains('Rack 1'));
|
||
}
|
||
|
||
public function test_search_result_for_top_level_location_has_no_prefix(): void
|
||
{
|
||
// A match at the top of the tree has no ancestors, so its text
|
||
// should be just its own name with no leading breadcrumb.
|
||
Location::factory()->create(['name' => 'Standalone Site']);
|
||
|
||
$response = $this->actingAsForApi(User::factory()->createUsers()->create())
|
||
->getJson(route('api.locations.selectlist', ['search' => 'Standalone']))
|
||
->assertOk();
|
||
|
||
$texts = collect($response->json('results'))->pluck('text');
|
||
$this->assertTrue($texts->contains('Standalone Site'));
|
||
}
|
||
|
||
public function test_unsearched_dropdown_uses_dash_indentation_for_nested_locations(): void
|
||
{
|
||
// Pins the reverted-to-old-style dropdown display. Root shows plain
|
||
// name, children get "-- " prefix, grandchildren "---- ", etc.
|
||
$hq = Location::factory()->create(['name' => 'HQ']);
|
||
$dc1 = Location::factory()->create(['name' => 'DC1', 'parent_id' => $hq->id]);
|
||
Location::factory()->create(['name' => 'Rack 1', 'parent_id' => $dc1->id]);
|
||
|
||
$response = $this->actingAsForApi(User::factory()->createUsers()->create())
|
||
->getJson(route('api.locations.selectlist'))
|
||
->assertOk();
|
||
|
||
$texts = collect($response->json('results'))->pluck('text');
|
||
$this->assertTrue($texts->contains('HQ'), 'Top-level location renders without indent prefix.');
|
||
$this->assertTrue($texts->contains('-- DC1'), 'One-level-deep location gets a two-dash indent.');
|
||
$this->assertTrue($texts->contains('---- Rack 1'), 'Two-levels-deep location gets a four-dash indent.');
|
||
}
|
||
}
|