'4.0.0-beta.998']); InstanceSettings::forceCreate([ 'id' => 0, 'new_version_available' => true, ]); Cache::shouldReceive('remember') ->once() ->with('coolify:versions:all', 3600, Mockery::type(Closure::class)) ->andReturn([ 'coolify' => [ 'v4' => [ 'version' => '4.0.0-beta.999', ], ], ]); Livewire::test(Upgrade::class) ->assertSet('currentVersion', '4.0.0-beta.998') ->assertSet('latestVersion', '4.0.0-beta.999') ->assertSet('isUpgradeAvailable', true) ->assertSee('4.0.0-beta.998') ->assertSee('4.0.0-beta.999'); }); it('does not highlight the current upgrade stage with the warning yellow accent', function () { $progressView = file_get_contents(resource_path('views/components/upgrade-progress.blade.php')); expect($progressView) ->toContain('bg-neutral-100 text-neutral-900 dark:bg-white/[0.08] dark:text-fg') ->not->toContain('dark:text-warning') ->not->toContain('dark:bg-warning'); }); it('uses a current-color spinner on upgrade stages instead of the brand purple loader', function () { $progressView = file_get_contents(resource_path('views/components/upgrade-progress.blade.php')); $appCss = file_get_contents(resource_path('css/app.css')); expect($progressView) ->toContain('spinner-current') ->not->toContain('loading-indicator') ->and($appCss) ->toContain('.dark .animate-spin.spinner-current') ->toContain('html[data-theme="custom"] .animate-spin.spinner-current') ->toContain('color: inherit !important;'); }); it('treats a brief upgrade poll miss as a reconnect, not a lost-contact failure', function () { $upgradeView = file_get_contents(resource_path('views/livewire/upgrade.blade.php')); expect($upgradeView) ->toContain('Reconnecting. This is expected during an upgrade...') ->not->toContain('Lost contact with Coolify'); }); it('uses sidebar state css instead of nested alpine state for upgrade labels', function () { $upgradeView = file_get_contents(resource_path('views/livewire/upgrade.blade.php')); $utilitiesCss = file_get_contents(resource_path('css/utilities.css')); expect($upgradeView) ->toContain('class="text-left menu-item-label sidebar-collapsed-label"') ->toContain('>In progress') ->toContain('>Upgrade') ->not->toContain(':class="collapsed && \'lg:hidden\'"') ->and($utilitiesCss) ->toContain('.sidebar-collapsed .sidebar-collapsed-label') ->toContain('display: none;'); }); it('falls back to 0.0.0 during mount when cached versions data is unavailable', function () { InstanceSettings::forceCreate([ 'id' => 0, 'new_version_available' => false, ]); Cache::shouldReceive('remember') ->once() ->with('coolify:versions:all', 3600, Mockery::type(Closure::class)) ->andReturn(null); Livewire::test(Upgrade::class) ->assertSet('latestVersion', '0.0.0'); }); it('clears stale upgrade availability when current version already matches latest version', function () { config(['constants.coolify.version' => '4.0.0-beta.999']); InstanceSettings::forceCreate([ 'id' => 0, 'new_version_available' => true, ]); Cache::shouldReceive('remember') ->once() ->with('coolify:versions:all', 3600, Mockery::type(Closure::class)) ->andReturn([ 'coolify' => [ 'v4' => [ 'version' => '4.0.0-beta.999', ], ], ]); Livewire::test(Upgrade::class) ->assertSet('latestVersion', '4.0.0-beta.999') ->assertSet('isUpgradeAvailable', false); expect((bool) InstanceSettings::findOrFail(0)->new_version_available)->toBeFalse(); }); it('clears stale upgrade availability when current version is newer than cached latest version', function () { config(['constants.coolify.version' => '4.0.0-beta.1000']); InstanceSettings::forceCreate([ 'id' => 0, 'new_version_available' => true, ]); Cache::shouldReceive('remember') ->once() ->with('coolify:versions:all', 3600, Mockery::type(Closure::class)) ->andReturn([ 'coolify' => [ 'v4' => [ 'version' => '4.0.0-beta.999', ], ], ]); Livewire::test(Upgrade::class) ->assertSet('latestVersion', '4.0.0-beta.999') ->assertSet('isUpgradeAvailable', false); expect((bool) InstanceSettings::findOrFail(0)->new_version_available)->toBeFalse(); });