InstanceSettings::updateOrCreate(['id' => 0], ['id' => 0])); $this->team = Team::factory()->create(); $this->admin = User::factory()->create(); $this->admin->teams()->attach($this->team, ['role' => 'admin']); $this->actingAs($this->admin); session(['currentTeam' => $this->team]); }); test('provider token selection redirects to a token specific server creation url', function (string $component, string $provider, string $type) { $token = CloudProviderToken::factory()->create([ 'team_id' => $this->team->id, 'provider' => $provider, ]); Livewire::test($component) ->call('selectToken', $token->id) ->assertRedirect(route('server.create.token', [ 'type' => $type, 'token_uuid' => $token->uuid, ])); })->with([ 'hetzner' => [ByHetzner::class, 'hetzner', 'hetzner'], 'vultr' => [ByVultr::class, 'vultr', 'vultr'], 'digital-ocean' => [ByDigitalOcean::class, 'digitalocean', 'digital-ocean'], ]); test('provider token selection is rendered as clickable token boxes', function (string $component, string $provider, string $type, string $tokenName) { $token = CloudProviderToken::factory()->create([ 'team_id' => $this->team->id, 'provider' => $provider, 'name' => $tokenName, ]); Livewire::test($component) ->assertSee($tokenName) ->assertSee(route('server.create.token', ['type' => $type, 'token_uuid' => $token->uuid])) ->assertDontSee('loadingProviderTokenId', false) ->assertDontSee('coolbox-loading', false) ->assertSee('href="'.route('server.create.token', ['type' => $type, 'token_uuid' => $token->uuid]).'"', false) ->assertSee('wire:key="'.$provider.'-token-'.$token->id.'"', false) ->assertDontSee('wire:click="selectToken(', false) ->assertDontSee('loading-spinner', false) ->assertDontSee('Loading Hetzner details...') ->assertDontSee('Loading Vultr details...') ->assertDontSee('Loading DigitalOcean details...') ->assertDontSee('Select a saved token') ->assertDontSee('You need a saved') ->assertDontSee('Continue'); })->with([ 'hetzner' => [ByHetzner::class, 'hetzner', 'hetzner', 'Production Hetzner'], 'vultr' => [ByVultr::class, 'vultr', 'vultr', 'Production Vultr'], 'digital-ocean' => [ByDigitalOcean::class, 'digitalocean', 'digital-ocean', 'Production DigitalOcean'], ]); test('provider token selection shows an add token box when no tokens exist', function (string $component, string $provider, string $title) { Livewire::test($component) ->assertSee($title) ->assertSee('Add an API token to continue provisioning.') ->assertSee('Add token') ->assertDontSee('wire:key="'.$provider.'-token-', false) ->assertDontSee('wire:click="selectToken(', false); })->with([ 'hetzner' => [ByHetzner::class, 'hetzner', 'No Hetzner tokens'], 'vultr' => [ByVultr::class, 'vultr', 'No Vultr tokens'], 'digital-ocean' => [ByDigitalOcean::class, 'digitalocean', 'No DigitalOcean tokens'], ]); test('provider token urls pass the token uuid into the server creation page', function () { $token = CloudProviderToken::factory()->create([ 'team_id' => $this->team->id, 'provider' => 'hetzner', ]); expect(route('server.create.token', [ 'type' => 'hetzner', 'token_uuid' => $token->uuid, ]))->toEndWith('/servers/new/hetzner/'.$token->uuid); Livewire::test(CreatePage::class, [ 'type' => 'hetzner', 'token_uuid' => $token->uuid, ]) ->assertSet('type', 'hetzner') ->assertSet('token_uuid', $token->uuid) ->assertSet('title', 'Hetzner'); }); test('provider token pages defer provider api data loading until wire init', function (string $component, string $provider, string $type, string $loadingText, string $wireInitCall) { $token = CloudProviderToken::factory()->create([ 'team_id' => $this->team->id, 'provider' => $provider, ]); Http::fake([ '*' => Http::response([], 200), ]); Livewire::test($component, ['selectedTokenUuid' => $token->uuid]) ->assertSet('current_step', 2) ->assertSet('loading_data', true) ->assertSee($loadingText) ->assertSee($wireInitCall, false) ->assertSee('role="status"', false) ->assertDontSee('border-b-2 border-primary', false); Http::assertNothingSent(); })->with([ 'hetzner' => [ByHetzner::class, 'hetzner', 'hetzner', 'Loading Hetzner data', 'wire:init="loadHetznerData"'], 'vultr' => [ByVultr::class, 'vultr', 'vultr', 'Loading Vultr data', 'wire:init="loadVultrData"'], 'digital-ocean' => [ByDigitalOcean::class, 'digitalocean', 'digital-ocean', 'Loading DigitalOcean data', 'wire:init="loadDigitalOceanData"'], ]); test('provider token pages render api error details when a selected token is invalid', function (string $component, string $provider, string $type, string $loadMethod, string $providerName, string $apiMessage, array $responseBody) { $token = CloudProviderToken::factory()->create([ 'team_id' => $this->team->id, 'provider' => $provider, 'name' => $providerName.' Token', ]); Http::fake([ '*' => Http::response($responseBody, 401), ]); Livewire::test($component, ['selectedTokenUuid' => $token->uuid]) ->call($loadMethod) ->assertSet('loading_data', false) ->assertSet('provider_data_error', "{$providerName} API error: {$apiMessage}") ->assertDispatched('error') ->assertSee("Unable to load {$providerName}") ->assertSee($apiMessage) ->assertSee('Select another token') ->assertSee('href="'.route('server.create.type', ['type' => $type]).'"', false) ->assertSee('wire:navigate', false) ->assertDontSee('wire:click="previousStep"', false); })->with([ 'hetzner' => [ByHetzner::class, 'hetzner', 'hetzner', 'loadHetznerData', 'Hetzner', 'the token you have provided is invalid', ['error' => ['code' => 'unauthorized', 'message' => 'the token you have provided is invalid']]], 'vultr' => [ByVultr::class, 'vultr', 'vultr', 'loadVultrData', 'Vultr', 'Invalid API key', ['error' => 'Invalid API key', 'status' => 401]], 'digital-ocean' => [ByDigitalOcean::class, 'digitalocean', 'digital-ocean', 'loadDigitalOceanData', 'DigitalOcean', 'Unable to authenticate you', ['id' => 'Unauthorized', 'message' => 'Unable to authenticate you']], ]); test('change method button on token specific provider creation pages returns to server type selection', function (string $provider, string $type) { $token = CloudProviderToken::factory()->create([ 'team_id' => $this->team->id, 'provider' => $provider, ]); Livewire::test(CreatePage::class, [ 'type' => $type, 'token_uuid' => $token->uuid, ]) ->assertSee('Change method') ->assertSee('href="'.route('server.create').'"', false) ->assertDontSee('>Back<', false); })->with([ 'hetzner' => ['hetzner', 'hetzner'], 'vultr' => ['vultr', 'vultr'], 'digital-ocean' => ['digitalocean', 'digital-ocean'], ]); test('provider token picker is hidden on token specific provider creation pages', function (string $provider, string $type) { $token = CloudProviderToken::factory()->create([ 'team_id' => $this->team->id, 'provider' => $provider, ]); $tokenUrl = route('server.create.token', ['type' => $type, 'token_uuid' => $token->uuid]); Livewire::test(CreatePage::class, [ 'type' => $type, ]) ->assertSee('href="'.$tokenUrl.'"', false) ->assertDontSee('+ New Token'); Livewire::test(CreatePage::class, [ 'type' => $type, 'token_uuid' => $token->uuid, ]) ->assertDontSee('href="'.$tokenUrl.'"', false) ->assertDontSee('Add token') ->assertDontSee('+ New Token'); })->with([ 'hetzner' => ['hetzner', 'hetzner'], 'vultr' => ['vultr', 'vultr'], 'digital-ocean' => ['digitalocean', 'digital-ocean'], ]);