Merge remote-tracking branch 'origin/main'

This commit is contained in:
Andras Bacsai committed 2026-08-17 16:23:56 +02:00
commit 82fe61bfd0
20 files changed
+202 -161

No files matched your search

+20 -4
View File
@@ -236,6 +236,22 @@ it('adds a domain to the application', function () {
->toBe(['https://app.example.com', 'https://www.app.example.com']);
});
it('composes the complete port on the server without duplicating an existing www domain', function () {
$this->application->update(['fqdn' => 'https://www.example.com:3000']);
Livewire::test(Domains::class, ['application' => $this->application->fresh()])
->set('newDomainParts.host', 'example.com')
->set('newDomainParts.port', '3000')
->call('addDomain')
->assertHasNoErrors()
->assertDispatched('success');
expect(explode(',', (string) $this->application->fresh()->fqdn))->toBe([
'https://www.example.com:3000',
'https://example.com:3000',
]);
});
it('adds multiple domains without replacing existing ones', function () {
$this->application->update([
'fqdn' => 'https://app.example.com',
@@ -1222,16 +1238,16 @@ it('uses segmented fields when adding and editing application domains', function
$component = file_get_contents(resource_path('views/components/forms/domain-input.blade.php'));
expect($view)
->toContain('<x-forms.domain-input id="newDomain"')
->toContain('<x-forms.domain-input id="editingDomainLocal"')
->toContain('<x-forms.domain-input id="newDomainParts"')
->toContain('<x-forms.domain-input id="editingDomainParts"')
->not->toContain('placeholder="https://app.example.com"')
->and($component)
->toContain('Protocol')
->toContain('Domain')
->toContain('Port')
->toContain('Path')
->toContain("scheme: 'https'")
->toContain('<x-forms.listbox id="{{ $id }}-protocol"')
->toContain('wire:model="{{ $id }}.host"')
->toContain('<x-forms.listbox id="{{ $id }}.scheme"')
->not->toContain('<select id="{{ $id }}-protocol"')
->toContain("['value' => 'https', 'label' => 'https']")
->toContain("['value' => 'http', 'label' => 'http']")
@@ -60,6 +60,15 @@ test('mobile listbox panels stay anchored to their trigger', function () {
->not->toContain('transform: translate(-50%, -50%) !important;');
});
test('portaled listboxes measure content without inheriting the viewport width', function () {
$listbox = file_get_contents(resource_path('views/components/forms/listbox.blade.php'));
expect($listbox)
->toContain("panel.style.width = 'max-content';")
->toContain('panel.style.minWidth = `${triggerRect.width}px`;')
->toContain('Math.max(triggerRect.width, panel.offsetWidth)');
});
test('searchable listbox component uses shared trigger label truncation', function () {
$html = Blade::render(<<<'BLADE'
<x-forms.searchable-listbox id="tz" label="Timezone"
+2 -2
View File
@@ -141,8 +141,8 @@ it('uses segmented fields when adding and editing service domains', function ()
$view = file_get_contents(resource_path('views/livewire/project/service/domains.blade.php'));
expect($view)
->toContain('<x-forms.domain-input id="newDomain"')
->toContain('<x-forms.domain-input id="editingDomainLocal"')
->toContain('<x-forms.domain-input id="newDomainParts"')
->toContain('<x-forms.domain-input id="editingDomainParts"')
->not->toContain('placeholder="https://app.example.com"');
});
+15 -35
View File
@@ -1,51 +1,31 @@
<?php
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\View;
use Illuminate\Support\MessageBag;
use Illuminate\Support\ViewErrorBag;
it('renders configurable host copy for S3 endpoints', function () {
View::share('errors', new ViewErrorBag);
$html = Blade::render(<<<'BLADE'
<x-forms.domain-input id="endpoint" :wire="false" value="http://192.168.1.50:9000/s3"
host-label="Host" host-placeholder="minio.internal or 192.168.1.50" />
BLADE);
$html = file_get_contents(resource_path('views/components/forms/domain-input.blade.php'));
expect($html)
->toContain('Host')
->toContain('minio.internal or 192.168.1.50')
->toContain("scheme: 'https'")
->toContain("host: ''")
->toContain("port: ''")
->toContain("path: ''");
->toContain('{{ $hostLabel }}')
->toContain('{{ $hostPlaceholder }}')
->toContain('wire:model="{{ $id }}.host"')
->toContain('wire:model="{{ $id }}.port"')
->toContain('wire:model="{{ $id }}.path"');
});
it('does not reparse partial numeric hosts while typing', function () {
it('binds URL parts directly to Livewire without Alpine synchronization', function () {
$view = file_get_contents(resource_path('views/components/forms/domain-input.blade.php'));
$writeMethod = str($view)->between('write() {', "\n },\n}")->value();
expect($writeMethod)
->toContain('this.syncing = true;')
->toContain('this.value = next;')
->toContain('this.$nextTick(() => this.syncing = false);')
->and(strpos($writeMethod, 'this.syncing = true;'))
->toBeLessThan(strpos($writeMethod, 'this.value = next;'));
expect($view)
->toContain('wire:model="{{ $id }}.host"')
->toContain('wire:model="{{ $id }}.port"')
->not->toContain('x-data=')
->not->toContain('$watch');
});
it('keeps validation errors attached to the composed endpoint', function () {
$settingsUrl = route('settings.advanced').'#endpoint-section';
$errors = new ViewErrorBag;
$errors->put('default', new MessageBag([
'endpoint' => "The endpoint is invalid. Configure allowed internal targets: {$settingsUrl}",
]));
View::share('errors', $errors);
$html = Blade::render('<x-forms.domain-input id="endpoint" :wire="false" />');
$html = file_get_contents(resource_path('views/components/forms/domain-input.blade.php'));
expect($html)
->toContain('The endpoint is invalid.')
->toContain('href="'.$settingsUrl.'"')
->toContain('@error($errorId ?? "{$id}.host")')
->toContain('href="{{ $validationLink }}"')
->toContain('Set them here.');
});
@@ -24,6 +24,24 @@ test('wraps complex Docker install command with pipes in bash -c', function () {
expect($result[0])->toBe("sudo bash -c 'curl https://releases.rancher.com/install-docker/27.3.sh | sh || curl https://get.docker.com | sh'");
});
test('preserves command substitutions inside database and volume backup scripts', function () {
$script = 'compressor=$(if command -v pigz; then printf pigz; else printf gzip; fi); exec $compressor';
$command = 'docker exec database pg_dumpall | docker run --rm -i helper sh -c '.escapeshellarg($script);
$volumeCommand = 'docker run --rm helper sh -c '.escapeshellarg($script).' > /data/coolify/backups/volume.tar.gz';
$result = parseCommandsByLineForSudo(collect([$command, $volumeCommand]), $this->server);
expect($result[0])
->toStartWith("sudo bash -c '")
->toContain('compressor=$(if command -v pigz; then')
->not->toContain('$(sudo if')
->not->toContain('| sudo docker run')
->and($result[1])
->toStartWith("sudo bash -c '")
->toContain('compressor=$(if command -v pigz; then')
->not->toContain('$(sudo if');
});
test('wraps complex Docker install command with multiple fallbacks', function () {
$commands = collect([
'curl --max-time 300 https://releases.rancher.com/install-docker/27.3.sh | sh || curl https://get.docker.com | sh -s -- --version 27.3',
@@ -11,11 +11,11 @@ it('uses the shared split URL input without a Livewire blur request', function (
expect($createView)
->not->toContain('wire:model.blur="endpoint"')
->toContain('<x-forms.domain-input id="endpoint"')
->toContain('<x-forms.domain-input id="endpointParts"')
->toContain('host-label="Host"')
->toContain('host-placeholder="minio.internal or 192.168.1.50"')
->and($editView)
->toContain('<x-forms.domain-input id="endpoint"')
->toContain('<x-forms.domain-input id="endpointParts"')
->toContain('@can(\'update\', $storage)');
});