From 07337d9df6d9862b27135f2c7c744af4c3cdb91a Mon Sep 17 00:00:00 2001 From: Rohit Tiwari Date: Tue, 26 May 2026 15:19:32 +0530 Subject: [PATCH 1/4] feat: add search functionality for environment variables --- .../Shared/EnvironmentVariable/All.php | 28 +++++++++++ .../shared/environment-variable/all.blade.php | 47 +++++++++++++++---- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Livewire/Project/Shared/EnvironmentVariable/All.php index 53b55009e..1ddf024a7 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/All.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/All.php @@ -25,6 +25,8 @@ class All extends Component public string $view = 'normal'; + public string $search = ''; + public bool $is_env_sorting_enabled = false; public bool $use_build_secrets = false; @@ -35,6 +37,14 @@ class All extends Component 'environmentVariableDeleted' => 'refreshEnvs', ]; + public function updatedSearch() + { + unset($this->environmentVariables); + unset($this->environmentVariablesPreview); + unset($this->hardcodedEnvironmentVariables); + unset($this->hardcodedEnvironmentVariablesPreview); + } + public function mount() { $this->is_env_sorting_enabled = data_get($this->resource, 'settings.is_env_sorting_enabled', false); @@ -68,6 +78,10 @@ class All extends Component $query = $this->resource->environment_variables() ->orderByRaw("CASE WHEN is_required = true AND (value IS NULL OR value = '') THEN 0 ELSE 1 END"); + if ($this->search !== '') { + $query->where('key', 'like', "%{$this->search}%"); + } + if ($this->is_env_sorting_enabled) { $query->orderBy('key'); } else { @@ -82,6 +96,10 @@ class All extends Component $query = $this->resource->environment_variables_preview() ->orderByRaw("CASE WHEN is_required = true AND (value IS NULL OR value = '') THEN 0 ELSE 1 END"); + if ($this->search !== '') { + $query->where('key', 'like', "%{$this->search}%"); + } + if ($this->is_env_sorting_enabled) { $query->orderBy('key'); } else { @@ -138,6 +156,12 @@ class All extends Component return ! in_array($var['key'], $managedKeys); }); + if ($this->search !== '') { + $hardcodedVars = $hardcodedVars->filter(function ($var) { + return str($var['key'])->contains($this->search, true); + }); + } + // Apply sorting based on is_env_sorting_enabled if ($this->is_env_sorting_enabled) { $hardcodedVars = $hardcodedVars->sortBy('key')->values(); @@ -285,6 +309,8 @@ class All extends Component // Clear computed property cache to force refresh unset($this->environmentVariables); unset($this->environmentVariablesPreview); + unset($this->hardcodedEnvironmentVariables); + unset($this->hardcodedEnvironmentVariablesPreview); $this->dispatch('success', 'Environment variable added.'); } @@ -416,6 +442,8 @@ class All extends Component // Clear computed property cache to force refresh unset($this->environmentVariables); unset($this->environmentVariablesPreview); + unset($this->hardcodedEnvironmentVariables); + unset($this->hardcodedEnvironmentVariablesPreview); $this->getDevView(); } } diff --git a/resources/views/livewire/project/shared/environment-variable/all.blade.php b/resources/views/livewire/project/shared/environment-variable/all.blade.php index 2ae3ad166..5e308d980 100644 --- a/resources/views/livewire/project/shared/environment-variable/all.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/all.blade.php @@ -43,9 +43,35 @@ @endif @if ($view === 'normal') -
-

Production Environment Variables

-
Environment (secrets) variables for Production.
+
+
+

Production Environment Variables

+
Environment (secrets) variables for Production.
+
+
+
+ +
+
+ + + + + + + +
+
+
+
@forelse ($this->environmentVariables as $env) type() === 'service' || $resource?->build_pack === 'dockercompose') && $this->hardcodedEnvironmentVariables->isNotEmpty()) @foreach ($this->hardcodedEnvironmentVariables as $index => $env) + wire:key="hardcoded-prod-{{ $env['key'] }}-{{ $env['service_name'] ?? 'default' }}-{{ $index }}" :env="$env" /> @endforeach @endif @if ($resource->type() === 'application' && $resource->environment_variables_preview->count() > 0 && $showPreview) @@ -88,8 +113,9 @@ label="Production Environment Variables"> @if ($showPreview) - + @endif Save All Environment Variables @@ -98,10 +124,11 @@ label="Production Environment Variables" disabled> @if ($showPreview) - + @endif @endcan @endif -
+ \ No newline at end of file From d525c12457e5fa862cac719257869e50865d3b63 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Wed, 3 Jun 2026 11:40:54 +0200 Subject: [PATCH 2/4] fix(env): keep dev view env saves independent of search Search environment variables case-insensitively by key, add accessible search labeling, and ensure switching to developer view after searching loads the full variable set so non-matching entries are not removed on save. --- .../Shared/EnvironmentVariable/All.php | 48 +++--- .../shared/environment-variable/all.blade.php | 10 +- .../Feature/EnvironmentVariableSearchTest.php | 163 ++++++++++++++++++ 3 files changed, 193 insertions(+), 28 deletions(-) create mode 100644 tests/Feature/EnvironmentVariableSearchTest.php diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Livewire/Project/Shared/EnvironmentVariable/All.php index 1ddf024a7..65f0bd437 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/All.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/All.php @@ -7,6 +7,8 @@ use App\Models\EnvironmentVariable; use App\Support\ValidationPatterns; use App\Traits\EnvironmentVariableProtection; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; +use Illuminate\Support\Collection; +use Illuminate\Support\Str; use Livewire\Component; class All extends Component @@ -75,11 +77,24 @@ class All extends Component public function getEnvironmentVariablesProperty() { - $query = $this->resource->environment_variables() - ->orderByRaw("CASE WHEN is_required = true AND (value IS NULL OR value = '') THEN 0 ELSE 1 END"); + return $this->getEnvironmentVariables(false); + } - if ($this->search !== '') { - $query->where('key', 'like', "%{$this->search}%"); + public function getEnvironmentVariablesPreviewProperty() + { + return $this->getEnvironmentVariables(true); + } + + private function getEnvironmentVariables(bool $isPreview, bool $withSearch = true): Collection + { + $query = $isPreview + ? $this->resource->environment_variables_preview() + : $this->resource->environment_variables(); + + $query->orderByRaw("CASE WHEN is_required = true AND (value IS NULL OR value = '') THEN 0 ELSE 1 END"); + + if ($withSearch && $this->searchTerm() !== '') { + $query->whereRaw('LOWER(key) LIKE ?', ['%'.Str::lower($this->searchTerm()).'%']); } if ($this->is_env_sorting_enabled) { @@ -91,22 +106,9 @@ class All extends Component return $query->get(); } - public function getEnvironmentVariablesPreviewProperty() + private function searchTerm(): string { - $query = $this->resource->environment_variables_preview() - ->orderByRaw("CASE WHEN is_required = true AND (value IS NULL OR value = '') THEN 0 ELSE 1 END"); - - if ($this->search !== '') { - $query->where('key', 'like', "%{$this->search}%"); - } - - if ($this->is_env_sorting_enabled) { - $query->orderBy('key'); - } else { - $query->orderBy('order'); - } - - return $query->get(); + return trim($this->search); } public function getHardcodedEnvironmentVariablesProperty() @@ -156,9 +158,9 @@ class All extends Component return ! in_array($var['key'], $managedKeys); }); - if ($this->search !== '') { + if ($this->searchTerm() !== '') { $hardcodedVars = $hardcodedVars->filter(function ($var) { - return str($var['key'])->contains($this->search, true); + return str($var['key'])->contains($this->searchTerm(), true); }); } @@ -173,9 +175,9 @@ class All extends Component public function getDevView() { - $this->variables = $this->formatEnvironmentVariables($this->environmentVariables); + $this->variables = $this->formatEnvironmentVariables($this->getEnvironmentVariables(false, false)); if ($this->showPreview) { - $this->variablesPreview = $this->formatEnvironmentVariables($this->environmentVariablesPreview); + $this->variablesPreview = $this->formatEnvironmentVariables($this->getEnvironmentVariables(true, false)); } } diff --git a/resources/views/livewire/project/shared/environment-variable/all.blade.php b/resources/views/livewire/project/shared/environment-variable/all.blade.php index 5e308d980..77ac92ffe 100644 --- a/resources/views/livewire/project/shared/environment-variable/all.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/all.blade.php @@ -50,17 +50,17 @@
- +
- -
\ No newline at end of file +
diff --git a/tests/Feature/EnvironmentVariableSearchTest.php b/tests/Feature/EnvironmentVariableSearchTest.php new file mode 100644 index 000000000..5f49776c9 --- /dev/null +++ b/tests/Feature/EnvironmentVariableSearchTest.php @@ -0,0 +1,163 @@ + 0]); + + $this->user = User::factory()->create(); + $this->team = Team::factory()->create(); + $this->team->members()->attach($this->user, ['role' => 'owner']); + $this->project = Project::factory()->create([ + 'team_id' => $this->team->id, + ]); + $this->environment = Environment::factory()->create([ + 'project_id' => $this->project->id, + ]); + + $this->actingAs($this->user); +}); + +it('filters production environment variables by key case-insensitively', function () { + $application = Application::factory()->create([ + 'environment_id' => $this->environment->id, + ]); + + EnvironmentVariable::create([ + 'key' => 'API_KEY', + 'value' => 'secret', + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + ]); + + EnvironmentVariable::create([ + 'key' => 'DATABASE_URL', + 'value' => 'postgres://example', + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + ]); + + $component = Livewire::test(All::class, ['resource' => $application]) + ->set('search', 'api'); + + expect($component->instance()->environmentVariables->pluck('key')->all()) + ->toBe(['API_KEY']); +}); + +it('filters preview environment variables by key case-insensitively', function () { + $application = Application::factory()->create([ + 'environment_id' => $this->environment->id, + ]); + + EnvironmentVariable::create([ + 'key' => 'PREVIEW_TOKEN', + 'value' => 'preview-secret', + 'is_preview' => true, + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + ]); + + EnvironmentVariable::create([ + 'key' => 'OTHER_PREVIEW_VALUE', + 'value' => 'preview-other', + 'is_preview' => true, + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + ]); + + $component = Livewire::test(All::class, ['resource' => $application]) + ->set('search', 'token'); + + expect($component->instance()->environmentVariablesPreview->pluck('key')->all()) + ->toBe(['PREVIEW_TOKEN']); +}); + +it('filters hardcoded Docker Compose environment variables by key case-insensitively', function () { + $service = Service::factory()->create([ + 'environment_id' => $this->environment->id, + 'docker_compose_raw' => <<<'YAML' +services: + app: + image: nginx + environment: + API_TOKEN: hardcoded-secret + DATABASE_URL: postgres://example +YAML, + ]); + + $component = Livewire::test(All::class, ['resource' => $service]) + ->set('search', 'api'); + + expect($component->instance()->hardcodedEnvironmentVariables->pluck('key')->all()) + ->toBe(['API_TOKEN']); +}); + +it('keeps developer view unfiltered after searching', function () { + $application = Application::factory()->create([ + 'environment_id' => $this->environment->id, + ]); + + EnvironmentVariable::create([ + 'key' => 'API_KEY', + 'value' => 'secret', + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + ]); + + EnvironmentVariable::create([ + 'key' => 'DATABASE_URL', + 'value' => 'postgres://example', + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + ]); + + $component = Livewire::test(All::class, ['resource' => $application]) + ->set('search', 'api') + ->call('switch') + ->assertSet('view', 'dev'); + + expect($component->get('variables')) + ->toContain('API_KEY=secret') + ->toContain('DATABASE_URL=postgres://example'); +}); + +it('does not delete non-matching variables when saving developer view after searching', function () { + $application = Application::factory()->create([ + 'environment_id' => $this->environment->id, + ]); + + EnvironmentVariable::create([ + 'key' => 'API_KEY', + 'value' => 'secret', + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + ]); + + EnvironmentVariable::create([ + 'key' => 'DATABASE_URL', + 'value' => 'postgres://example', + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + ]); + + Livewire::test(All::class, ['resource' => $application]) + ->set('search', 'api') + ->call('switch') + ->call('submit'); + + expect($application->environment_variables()->pluck('key')->all()) + ->toContain('API_KEY') + ->toContain('DATABASE_URL'); +}); From d7524a743d3a78a12bac4ce51baab25043c845ec Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Wed, 3 Jun 2026 12:54:50 +0200 Subject: [PATCH 3/4] fix(env-vars): show single empty state for searches Move the environment variable search field above the production section and hide production and preview headings when a search has no results. --- .../Shared/EnvironmentVariable/All.php | 16 ++++ .../shared/environment-variable/all.blade.php | 94 ++++++++++--------- ...ronmentVariableMultilineToggleViewTest.php | 16 ++++ 3 files changed, 80 insertions(+), 46 deletions(-) diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Livewire/Project/Shared/EnvironmentVariable/All.php index 65f0bd437..2a1a47b4d 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/All.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/All.php @@ -45,6 +45,7 @@ class All extends Component unset($this->environmentVariablesPreview); unset($this->hardcodedEnvironmentVariables); unset($this->hardcodedEnvironmentVariablesPreview); + unset($this->hasEnvironmentVariables); } public function mount() @@ -111,6 +112,19 @@ class All extends Component return trim($this->search); } + public function getHasEnvironmentVariablesProperty(): bool + { + return $this->environmentVariables->isNotEmpty() || + $this->environmentVariablesPreview->isNotEmpty() || + $this->hardcodedEnvironmentVariables->isNotEmpty() || + $this->hardcodedEnvironmentVariablesPreview->isNotEmpty(); + } + + public function getIsSearchActiveProperty(): bool + { + return $this->searchTerm() !== ''; + } + public function getHardcodedEnvironmentVariablesProperty() { return $this->getHardcodedVariables(false); @@ -313,6 +327,7 @@ class All extends Component unset($this->environmentVariablesPreview); unset($this->hardcodedEnvironmentVariables); unset($this->hardcodedEnvironmentVariablesPreview); + unset($this->hasEnvironmentVariables); $this->dispatch('success', 'Environment variable added.'); } @@ -446,6 +461,7 @@ class All extends Component unset($this->environmentVariablesPreview); unset($this->hardcodedEnvironmentVariables); unset($this->hardcodedEnvironmentVariablesPreview); + unset($this->hasEnvironmentVariables); $this->getDevView(); } } diff --git a/resources/views/livewire/project/shared/environment-variable/all.blade.php b/resources/views/livewire/project/shared/environment-variable/all.blade.php index 77ac92ffe..f49765286 100644 --- a/resources/views/livewire/project/shared/environment-variable/all.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/all.blade.php @@ -43,64 +43,66 @@ @endif
@if ($view === 'normal') -
-
-

Production Environment Variables

-
Environment (secrets) variables for Production.
-
-
-
- -
-
- - -
+
+
+ +
+
+ +
- @forelse ($this->environmentVariables as $env) - - @empty + @if ($this->isSearchActive && ! $this->hasEnvironmentVariables)
No environment variables found.
- @endforelse - @if (($resource->type() === 'service' || $resource?->build_pack === 'dockercompose') && $this->hardcodedEnvironmentVariables->isNotEmpty()) - @foreach ($this->hardcodedEnvironmentVariables as $index => $env) - - @endforeach - @endif - @if ($resource->type() === 'application' && $resource->environment_variables_preview->count() > 0 && $showPreview) + @else
-

Preview Deployments Environment Variables

-
Environment (secrets) variables for Preview Deployments.
+

Production Environment Variables

+
Environment (secrets) variables for Production.
- @foreach ($this->environmentVariablesPreview as $env) + @forelse ($this->environmentVariables as $env) - @endforeach - @if (($resource->type() === 'service' || $resource?->build_pack === 'dockercompose') && $this->hardcodedEnvironmentVariablesPreview->isNotEmpty()) - @foreach ($this->hardcodedEnvironmentVariablesPreview as $index => $env) + @empty +
No environment variables found.
+ @endforelse + @if (($resource->type() === 'service' || $resource?->build_pack === 'dockercompose') && $this->hardcodedEnvironmentVariables->isNotEmpty()) + @foreach ($this->hardcodedEnvironmentVariables as $index => $env) + wire:key="hardcoded-prod-{{ $env['key'] }}-{{ $env['service_name'] ?? 'default' }}-{{ $index }}" :env="$env" /> @endforeach @endif + @if ($resource->type() === 'application' && $resource->environment_variables_preview->count() > 0 && $showPreview) +
+

Preview Deployments Environment Variables

+
Environment (secrets) variables for Preview Deployments.
+
+ @foreach ($this->environmentVariablesPreview as $env) + + @endforeach + @if (($resource->type() === 'service' || $resource?->build_pack === 'dockercompose') && $this->hardcodedEnvironmentVariablesPreview->isNotEmpty()) + @foreach ($this->hardcodedEnvironmentVariablesPreview as $index => $env) + + @endforeach + @endif + @endif @endif @else
diff --git a/tests/Feature/EnvironmentVariableMultilineToggleViewTest.php b/tests/Feature/EnvironmentVariableMultilineToggleViewTest.php index e27fd0b4b..632f6f943 100644 --- a/tests/Feature/EnvironmentVariableMultilineToggleViewTest.php +++ b/tests/Feature/EnvironmentVariableMultilineToggleViewTest.php @@ -29,3 +29,19 @@ it('uses sans font for the developer bulk environment variable editor', function ->not->toContain('wire:model="variables" monospace') ->not->toContain('wire:model="variablesPreview" monospace'); }); + +it('renders the environment variable search field above the production title', function () { + $view = file_get_contents(resource_path('views/livewire/project/shared/environment-variable/all.blade.php')); + + expect(strpos($view, 'aria-label="Search environment variables"')) + ->toBeLessThan(strpos($view, '

Production Environment Variables

')); +}); + +it('renders a single no results message for empty environment variable searches', function () { + $view = file_get_contents(resource_path('views/livewire/project/shared/environment-variable/all.blade.php')); + + expect($view) + ->toContain('@if ($this->isSearchActive && ! $this->hasEnvironmentVariables)') + ->toContain('
No environment variables found.
') + ->toContain('@else'); +}); From 1802522c608619df5f00e411a565d10a7d39c1f3 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Wed, 3 Jun 2026 13:43:26 +0200 Subject: [PATCH 4/4] fix(env-vars): treat search wildcards literally Escape SQL LIKE wildcard characters in environment variable searches and hide production or preview sections when the filtered results are empty. --- .../Shared/EnvironmentVariable/All.php | 25 +++--- .../shared/environment-variable/all.blade.php | 34 +++---- ...ronmentVariableMultilineToggleViewTest.php | 9 ++ .../Feature/EnvironmentVariableSearchTest.php | 88 +++++++++++++++++++ 4 files changed, 127 insertions(+), 29 deletions(-) diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Livewire/Project/Shared/EnvironmentVariable/All.php index 2a1a47b4d..a19837e16 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/All.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/All.php @@ -39,7 +39,12 @@ class All extends Component 'environmentVariableDeleted' => 'refreshEnvs', ]; - public function updatedSearch() + public function updatedSearch(): void + { + $this->clearEnvironmentVariableCaches(); + } + + private function clearEnvironmentVariableCaches(): void { unset($this->environmentVariables); unset($this->environmentVariablesPreview); @@ -95,7 +100,9 @@ class All extends Component $query->orderByRaw("CASE WHEN is_required = true AND (value IS NULL OR value = '') THEN 0 ELSE 1 END"); if ($withSearch && $this->searchTerm() !== '') { - $query->whereRaw('LOWER(key) LIKE ?', ['%'.Str::lower($this->searchTerm()).'%']); + $escapedSearch = addcslashes(Str::lower($this->searchTerm()), '%_\\'); + + $query->whereRaw("LOWER(key) LIKE ? ESCAPE '\\'", ['%'.$escapedSearch.'%']); } if ($this->is_env_sorting_enabled) { @@ -322,12 +329,7 @@ class All extends Component $environment->order = $maxOrder + 1; $environment->save(); - // Clear computed property cache to force refresh - unset($this->environmentVariables); - unset($this->environmentVariablesPreview); - unset($this->hardcodedEnvironmentVariables); - unset($this->hardcodedEnvironmentVariablesPreview); - unset($this->hasEnvironmentVariables); + $this->clearEnvironmentVariableCaches(); $this->dispatch('success', 'Environment variable added.'); } @@ -456,12 +458,7 @@ class All extends Component public function refreshEnvs() { $this->resource->refresh(); - // Clear computed property cache to force refresh - unset($this->environmentVariables); - unset($this->environmentVariablesPreview); - unset($this->hardcodedEnvironmentVariables); - unset($this->hardcodedEnvironmentVariablesPreview); - unset($this->hasEnvironmentVariables); + $this->clearEnvironmentVariableCaches(); $this->getDevView(); } } diff --git a/resources/views/livewire/project/shared/environment-variable/all.blade.php b/resources/views/livewire/project/shared/environment-variable/all.blade.php index f49765286..991327265 100644 --- a/resources/views/livewire/project/shared/environment-variable/all.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/all.blade.php @@ -70,23 +70,27 @@ @if ($this->isSearchActive && ! $this->hasEnvironmentVariables)
No environment variables found.
@else -
-

Production Environment Variables

-
Environment (secrets) variables for Production.
-
- @forelse ($this->environmentVariables as $env) - - @empty -
No environment variables found.
- @endforelse - @if (($resource->type() === 'service' || $resource?->build_pack === 'dockercompose') && $this->hardcodedEnvironmentVariables->isNotEmpty()) - @foreach ($this->hardcodedEnvironmentVariables as $index => $env) - + @if ($this->environmentVariables->isNotEmpty() || $this->hardcodedEnvironmentVariables->isNotEmpty()) +
+

Production Environment Variables

+
Environment (secrets) variables for Production.
+
+ @foreach ($this->environmentVariables as $env) + @endforeach + @if (($resource->type() === 'service' || $resource?->build_pack === 'dockercompose') && $this->hardcodedEnvironmentVariables->isNotEmpty()) + @foreach ($this->hardcodedEnvironmentVariables as $index => $env) + + @endforeach + @endif @endif - @if ($resource->type() === 'application' && $resource->environment_variables_preview->count() > 0 && $showPreview) + @if ( + $resource->type() === 'application' && + $showPreview && + ($this->environmentVariablesPreview->isNotEmpty() || $this->hardcodedEnvironmentVariablesPreview->isNotEmpty()) + )

Preview Deployments Environment Variables

Environment (secrets) variables for Preview Deployments.
diff --git a/tests/Feature/EnvironmentVariableMultilineToggleViewTest.php b/tests/Feature/EnvironmentVariableMultilineToggleViewTest.php index 632f6f943..de97e6095 100644 --- a/tests/Feature/EnvironmentVariableMultilineToggleViewTest.php +++ b/tests/Feature/EnvironmentVariableMultilineToggleViewTest.php @@ -45,3 +45,12 @@ it('renders a single no results message for empty environment variable searches' ->toContain('
No environment variables found.
') ->toContain('@else'); }); + +it('only renders the production section when production variables are visible', function () { + $view = file_get_contents(resource_path('views/livewire/project/shared/environment-variable/all.blade.php')); + + expect($view) + ->toContain('@if ($this->environmentVariables->isNotEmpty() || $this->hardcodedEnvironmentVariables->isNotEmpty())') + ->not->toContain('@forelse ($this->environmentVariables as $env)') + ->not->toContain('@empty'); +}); diff --git a/tests/Feature/EnvironmentVariableSearchTest.php b/tests/Feature/EnvironmentVariableSearchTest.php index 5f49776c9..613144d53 100644 --- a/tests/Feature/EnvironmentVariableSearchTest.php +++ b/tests/Feature/EnvironmentVariableSearchTest.php @@ -56,6 +56,44 @@ it('filters production environment variables by key case-insensitively', functio ->toBe(['API_KEY']); }); +it('treats production environment variable search wildcards literally', function () { + $application = Application::factory()->create([ + 'environment_id' => $this->environment->id, + ]); + + EnvironmentVariable::create([ + 'key' => 'API_KEY', + 'value' => 'secret', + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + ]); + + EnvironmentVariable::create([ + 'key' => 'APIXKEY', + 'value' => 'other-secret', + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + ]); + + EnvironmentVariable::create([ + 'key' => 'PERCENT%KEY', + 'value' => 'percent-secret', + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + ]); + + $component = Livewire::test(All::class, ['resource' => $application]) + ->set('search', 'api_key'); + + expect($component->instance()->environmentVariables->pluck('key')->all()) + ->toBe(['API_KEY']); + + $component->set('search', '%KEY'); + + expect($component->instance()->environmentVariables->pluck('key')->all()) + ->toBe(['PERCENT%KEY']); +}); + it('filters preview environment variables by key case-insensitively', function () { $application = Application::factory()->create([ 'environment_id' => $this->environment->id, @@ -104,6 +142,26 @@ YAML, ->toBe(['API_TOKEN']); }); +it('does not show the empty production message when search only matches hardcoded variables', function () { + $service = Service::factory()->create([ + 'environment_id' => $this->environment->id, + 'docker_compose_raw' => <<<'YAML' +services: + app: + image: nginx + environment: + API_TOKEN: hardcoded-secret + DATABASE_URL: postgres://example +YAML, + ]); + + Livewire::test(All::class, ['resource' => $service]) + ->set('search', 'api') + ->assertSee('Production Environment Variables') + ->assertSee('API_TOKEN') + ->assertDontSee('No environment variables found.'); +}); + it('keeps developer view unfiltered after searching', function () { $application = Application::factory()->create([ 'environment_id' => $this->environment->id, @@ -161,3 +219,33 @@ it('does not delete non-matching variables when saving developer view after sear ->toContain('API_KEY') ->toContain('DATABASE_URL'); }); + +it('hides the preview section when search filters out all preview variables', function () { + $application = Application::factory()->create([ + 'environment_id' => $this->environment->id, + ]); + + EnvironmentVariable::create([ + 'key' => 'API_KEY', + 'value' => 'secret', + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + ]); + + $application->environment_variables_preview()->where('key', 'API_KEY')->delete(); + + EnvironmentVariable::create([ + 'key' => 'PREVIEW_TOKEN', + 'value' => 'preview-secret', + 'is_preview' => true, + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + ]); + + Livewire::test(All::class, ['resource' => $application]) + ->set('search', 'api') + ->assertSee('Production Environment Variables') + ->assertSee('API_KEY') + ->assertDontSee('Preview Deployments Environment Variables') + ->assertDontSee('PREVIEW_TOKEN'); +});