mirror of
https://github.com/coollabsio/coolify.git
synced 2026-09-27 17:55:59 -04:00
refactor(application): extract status and internal access components
Move internal hostname loading and breadcrumb status into dedicated Livewire components with live refresh. Merge public/internal access into one Access section, bind Enter to save on the unsaved bar, and drop x-teleport wrappers from popup and global search.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\Project\Application\Heading as ApplicationHeading;
|
||||
use App\Livewire\Project\Application\Status as ApplicationStatus;
|
||||
use App\Models\Application;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Project;
|
||||
@@ -166,6 +167,21 @@ it('keeps activeRouteName when request is not an application page route', functi
|
||||
->assertSet('activeRouteName', 'project.application.webhooks');
|
||||
});
|
||||
|
||||
it('refreshes the breadcrumb application status after it changes', function () {
|
||||
$this->actingAs($this->admin);
|
||||
session(['currentTeam' => $this->team]);
|
||||
|
||||
$component = Livewire::test(ApplicationStatus::class, ['application' => $this->application])
|
||||
->assertSee('Running');
|
||||
|
||||
$this->application->update(['status' => 'exited']);
|
||||
|
||||
$component
|
||||
->call('refreshStatus')
|
||||
->assertSee('Stopped')
|
||||
->assertDontSee('Running');
|
||||
});
|
||||
|
||||
it('uses app-tab-active utility for resource heading active styles', function () {
|
||||
$utilities = file_get_contents(resource_path('css/utilities.css'));
|
||||
|
||||
|
||||
@@ -3,11 +3,19 @@
|
||||
it('shows internal Docker access details in application general settings', function () {
|
||||
$generalSettings = file_get_contents(resource_path('views/livewire/project/application/general.blade.php'));
|
||||
$generalComponent = file_get_contents(app_path('Livewire/Project/Application/General.php'));
|
||||
$internalAccessSettings = file_get_contents(resource_path('views/livewire/project/application/internal-access.blade.php'));
|
||||
$internalAccessComponent = file_get_contents(app_path('Livewire/Project/Application/InternalAccess.php'));
|
||||
$configurationSidebar = file_get_contents(resource_path('views/components/application/configuration-sidebar.blade.php'));
|
||||
|
||||
expect($generalSettings)
|
||||
->toContain('id="access-section" title="Access"')
|
||||
->toContain('<h3 class="mb-4 text-sm font-semibold text-black dark:text-fg">Public access</h3>')
|
||||
->toContain('<livewire:project.application.internal-access')
|
||||
->not->toContain('wire:init="loadCurrentInternalHostname"')
|
||||
->and($internalAccessSettings)
|
||||
->toContain('id="internal-access-section"')
|
||||
->toContain('title="Internal access"')
|
||||
->toContain('<h3 class="mb-4 text-sm font-semibold text-black dark:text-fg">Internal access</h3>')
|
||||
->not->toContain('<x-application.settings-section')
|
||||
->toContain('Internal hostname')
|
||||
->toContain('Docker network')
|
||||
->toContain('Exposed ports')
|
||||
@@ -18,12 +26,14 @@ it('shows internal Docker access details in application general settings', funct
|
||||
->not->toContain('Changes with each deployment')
|
||||
->toContain("window.scrollToSettingsSection?.('networking-section')")
|
||||
->and($generalComponent)
|
||||
->not->toContain('public function loadCurrentInternalHostname(): void')
|
||||
->and($internalAccessComponent)
|
||||
->toContain('public ?string $currentInternalHostname = null;')
|
||||
->toContain('public function loadCurrentInternalHostname(): void')
|
||||
->toContain('getCurrentApplicationContainerStatus(')
|
||||
->toContain("data_get(\$currentContainer, 'Names')")
|
||||
->and($configurationSidebar)
|
||||
->toContain("['id' => 'internal-access-section', 'label' => 'Internal access']");
|
||||
->toContain("['id' => 'access-section', 'label' => 'Access']");
|
||||
});
|
||||
|
||||
it('does not show the internal access section for Docker Compose applications', function () {
|
||||
@@ -33,5 +43,7 @@ it('does not show the internal access section for Docker Compose applications',
|
||||
expect($generalSettings)
|
||||
->toContain("@if (\$buildPack !== 'dockercompose')")
|
||||
->and($configurationSidebar)
|
||||
->toContain("\$isComposeApp ? null : ['id' => 'internal-access-section', 'label' => 'Internal access']");
|
||||
->toContain("['id' => 'access-section', 'label' => 'Access']")
|
||||
->not->toContain("['id' => 'public-access-section', 'label' => 'Public access']")
|
||||
->not->toContain("['id' => 'internal-access-section', 'label' => 'Internal access']");
|
||||
});
|
||||
|
||||
@@ -12,3 +12,16 @@ it('shows a loading overlay while resource selection steps load', function () {
|
||||
->toContain('runPaletteTransition')
|
||||
->toContain('Loading…');
|
||||
});
|
||||
|
||||
it('uses a single Alpine result renderer for every command palette result type', function () {
|
||||
$view = file_get_contents(resource_path('views/livewire/global-search.blade.php'));
|
||||
|
||||
expect($view)
|
||||
->not->toContain('Create mode (server-rendered path)')
|
||||
->not->toContain('!$wire.isCreateMode')
|
||||
->toContain("<!-- Command palette -->\n <div x-show=\"modalOpen\"")
|
||||
->not->toContain("<!-- Command palette -->\n <template x-teleport=\"body\">")
|
||||
->toContain("<div wire:ignore>\n <template x-if=\"searchQuery.length")
|
||||
->toContain('x-for="(result, index) in searchResults"')
|
||||
->toContain('x-for="[categoryName, items] in Object.entries(groupedCreatableItems)"');
|
||||
});
|
||||
|
||||
@@ -88,7 +88,7 @@ it('supports timed compact popup notifications', function () {
|
||||
->toContain("localStorage.setItem(this.storageKey, 'compact')")
|
||||
->toContain("localStorage.setItem(this.storageKey, 'icon')")
|
||||
->toContain('localStorage.removeItem(key)')
|
||||
->toContain('<template x-teleport="body">')
|
||||
->not->toContain('<template x-teleport="body">')
|
||||
->toContain('compact = true')
|
||||
->toContain('@click="restore()"')
|
||||
->toContain('@click.stop="minimizeToIcon()"')
|
||||
@@ -113,6 +113,22 @@ it('refreshes configuration changes when the event is received', function () {
|
||||
->assertSee('Build command');
|
||||
});
|
||||
|
||||
it('shows domain changes when the domain page dispatches a configuration change', function () {
|
||||
$application = configurationCheckerApplication($this->environment);
|
||||
markConfigurationCheckerApplicationDeployed($application);
|
||||
|
||||
$component = Livewire::test(ConfigurationChecker::class, ['resource' => $application->refresh()])
|
||||
->assertSet('isConfigurationChanged', false);
|
||||
|
||||
$application->update(['fqdn' => 'https://changed.example.com']);
|
||||
|
||||
$component
|
||||
->dispatch('configurationChanged')
|
||||
->assertSet('isConfigurationChanged', true)
|
||||
->assertSee('Domains')
|
||||
->assertSee('https://changed.example.com');
|
||||
});
|
||||
|
||||
it('shows an unapplied configuration warning after a directory mount is added', function () {
|
||||
$application = configurationCheckerApplication($this->environment);
|
||||
markConfigurationCheckerApplicationDeployed($application);
|
||||
|
||||
@@ -36,6 +36,17 @@ test('unsaved bar delays show and hides while loading to avoid instant-save flas
|
||||
->toContain('delay-0');
|
||||
});
|
||||
|
||||
test('unsaved bar saves with enter and shows the shortcut on the save button', function () {
|
||||
$contents = file_get_contents(resource_path('views/components/unsaved-bar.blade.php'));
|
||||
|
||||
expect($contents)
|
||||
->toContain('@keydown.enter.window')
|
||||
->toContain("classList.contains('is-dirty')")
|
||||
->toContain('$wire.{{ $action }}()')
|
||||
->toContain('<kbd')
|
||||
->toContain('Enter</kbd>');
|
||||
});
|
||||
|
||||
test('unsaved bar stays above floating notifications so save actions remain accessible', function () {
|
||||
$unsavedBar = file_get_contents(resource_path('views/components/unsaved-bar.blade.php'));
|
||||
$popup = file_get_contents(resource_path('views/components/popup-small.blade.php'));
|
||||
|
||||
@@ -38,12 +38,21 @@ it('renders service container statuses as shared status badges', function () {
|
||||
|
||||
it('uses bordered status badges in the top breadcrumb', function () {
|
||||
$breadcrumb = file_get_contents(resource_path('views/components/top-breadcrumb.blade.php'));
|
||||
$applicationStatus = file_get_contents(resource_path('views/livewire/project/application/status.blade.php'));
|
||||
$borderedBadgeClasses = 'rounded-full border border-neutral-200 bg-neutral-100';
|
||||
|
||||
expect(substr_count($breadcrumb, $borderedBadgeClasses))->toBe(4)
|
||||
expect(substr_count($breadcrumb.$applicationStatus, $borderedBadgeClasses))->toBe(4)
|
||||
->and(substr_count($breadcrumb, 'rounded-full bg-neutral-100'))->toBe(0);
|
||||
});
|
||||
|
||||
it('renders application status through a reactive livewire component', function () {
|
||||
$breadcrumb = file_get_contents(resource_path('views/components/top-breadcrumb.blade.php'));
|
||||
|
||||
expect($breadcrumb)
|
||||
->toContain('<livewire:project.application.status')
|
||||
->not->toContain('$applicationStatus = str($currentApplication->status');
|
||||
});
|
||||
|
||||
it('uses a shared refresh badge for resource status refresh actions', function () {
|
||||
$statusIndex = file_get_contents(resource_path('views/components/status/index.blade.php'));
|
||||
$serviceStatus = file_get_contents(resource_path('views/components/status/services.blade.php'));
|
||||
|
||||
Reference in New Issue
Block a user