From 936a2566c686577eaac0e8b1410cd4877de1fc60 Mon Sep 17 00:00:00 2001
From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Date: Wed, 9 Sep 2026 14:28:15 +0200
Subject: [PATCH] fix(analytics): hide traffic UI when disabled and skip
default-on (#11700)
---
app/Livewire/Analytics.php | 30 +++++++++-
.../Project/Application/Analytics.php | 11 +++-
app/Livewire/Server/Proxy.php | 1 -
.../Server/TrafficAnalyticsSettings.php | 2 +
app/Models/ServerSetting.php | 9 ---
.../livewire/analytics-placeholder.blade.php | 2 +
resources/views/livewire/analytics.blade.php | 12 ++--
resources/views/livewire/dashboard.blade.php | 5 +-
.../traffic-analytics-placeholder.blade.php | 20 +------
.../dashboard/traffic-analytics.blade.php | 26 +++------
.../project/application/analytics.blade.php | 2 +-
.../application/configuration.blade.php | 3 +-
.../project/application/general.blade.php | 3 -
.../livewire/project/shared/metrics.blade.php | 6 +-
.../livewire/server/analytics/show.blade.php | 3 +-
.../views/livewire/server/proxy.blade.php | 6 ++
.../traffic-analytics-settings.blade.php | 39 +++++++++++--
.../Feature/ApplicationGeneralLayoutTest.php | 6 ++
.../Feature/MetricsDisabledEmptyStateTest.php | 11 ++++
.../ProxyConfigurationLoadingStateTest.php | 18 ++++++
.../ApplicationAnalyticsTest.php | 43 +++++++++++++-
.../DashboardTrafficAnalyticsTest.php | 40 +++++++++++--
.../ServerAnalyticsPageTest.php | 58 +++++++++++++++++++
.../ServerSettingTrafficTest.php | 16 ++---
.../ToggleTrafficAnalyticsTest.php | 24 ++++++++
25 files changed, 309 insertions(+), 87 deletions(-)
create mode 100644 tests/Feature/MetricsDisabledEmptyStateTest.php
diff --git a/app/Livewire/Analytics.php b/app/Livewire/Analytics.php
index 24ba69d2d4..abf9d88cfd 100644
--- a/app/Livewire/Analytics.php
+++ b/app/Livewire/Analytics.php
@@ -11,6 +11,7 @@ use Illuminate\Contracts\View\View;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Livewire\Attributes\Lazy;
+use Livewire\Attributes\On;
use Livewire\Attributes\Url;
use Livewire\Component;
@@ -178,6 +179,23 @@ class Analytics extends Component
$this->live = ! $this->live;
}
+ #[On('trafficAnalyticsStateChanged')]
+ public function refreshTrafficAnalyticsState(): void
+ {
+ if ($this->scopedServerUuid === null) {
+ return;
+ }
+
+ $server = Server::ownedByCurrentTeam()->whereUuid($this->scopedServerUuid)->firstOrFail();
+ $this->overview = null;
+ $this->servers = $server->isTrafficAnalyticsEnabled() ? collect([$server]) : collect();
+
+ if ($this->servers->isNotEmpty()) {
+ $this->refreshAppOptions();
+ $this->loadData();
+ }
+ }
+
public function isLivePollable(): bool
{
return $this->live && $this->range === '24h';
@@ -564,10 +582,18 @@ class Analytics extends Component
return [$from->toIso8601ZuluString(), $to->toIso8601ZuluString()];
}
- public function placeholder(): View
+ public function placeholder(array $params = []): View
{
+ $scopedServerUuid = $params['scopedServerUuid'] ?? null;
+ $hideSkeleton = false;
+
+ if (is_string($scopedServerUuid)) {
+ $server = Server::ownedByCurrentTeamCached()->firstWhere('uuid', $scopedServerUuid);
+ $hideSkeleton = $server !== null && ! $server->isTrafficAnalyticsEnabled();
+ }
+
// Rendered instantly; the Sentinel round-trips run in the deferred lazy-load request.
- return view('livewire.analytics-placeholder');
+ return view('livewire.analytics-placeholder', compact('hideSkeleton'));
}
public function render()
diff --git a/app/Livewire/Project/Application/Analytics.php b/app/Livewire/Project/Application/Analytics.php
index 3db21ab400..0c5e30b737 100644
--- a/app/Livewire/Project/Application/Analytics.php
+++ b/app/Livewire/Project/Application/Analytics.php
@@ -221,8 +221,17 @@ class Analytics extends Component
return [$from->toIso8601ZuluString(), $to->toIso8601ZuluString()];
}
- public function placeholder(): View
+ public function placeholder(array $params = []): View
{
+ $application = $params['application'] ?? null;
+
+ if ($application instanceof Application && ! $application->destination?->server?->isTrafficAnalyticsEnabled()) {
+ $this->application = $application;
+ $this->enabled = false;
+
+ return view('livewire.project.application.analytics');
+ }
+
// Rendered instantly; the Sentinel round-trip runs in the deferred lazy-load request.
return view('livewire.project.application.analytics-placeholder');
}
diff --git a/app/Livewire/Server/Proxy.php b/app/Livewire/Server/Proxy.php
index 296fd4da5d..0454d97049 100644
--- a/app/Livewire/Server/Proxy.php
+++ b/app/Livewire/Server/Proxy.php
@@ -56,7 +56,6 @@ class Proxy extends Component
$this->redirectEnabled = data_get($this->server, 'proxy.redirect_enabled', true);
$this->redirectUrl = data_get($this->server, 'proxy.redirect_url');
$this->syncData(false);
- $this->loadProxyConfiguration();
$this->clearAppliedTraefikBranchWarning();
}
diff --git a/app/Livewire/Server/TrafficAnalyticsSettings.php b/app/Livewire/Server/TrafficAnalyticsSettings.php
index 48022d9aed..c6d96df488 100644
--- a/app/Livewire/Server/TrafficAnalyticsSettings.php
+++ b/app/Livewire/Server/TrafficAnalyticsSettings.php
@@ -3,6 +3,7 @@
namespace App\Livewire\Server;
use App\Actions\Server\ConfigureTrafficAnalytics;
+use App\Livewire\Analytics;
use App\Models\Server;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\View\View;
@@ -83,6 +84,7 @@ class TrafficAnalyticsSettings extends Component
ConfigureTrafficAnalytics::run($this->server, $enable);
$this->server->refresh();
$this->isTrafficAnalyticsEnabled = $this->server->isTrafficAnalyticsEnabled();
+ $this->dispatch('trafficAnalyticsStateChanged')->to(Analytics::class);
$this->dispatch('success', $enable
? 'Traffic analytics enabled. Restarting proxy and Sentinel.'
: 'Traffic analytics disabled. Restarting proxy and Sentinel.');
diff --git a/app/Models/ServerSetting.php b/app/Models/ServerSetting.php
index 58f28ba32d..512247b771 100644
--- a/app/Models/ServerSetting.php
+++ b/app/Models/ServerSetting.php
@@ -170,15 +170,6 @@ class ServerSetting extends Model
{
static::creating(function ($setting) {
try {
- // Enable traffic analytics by default for eligible servers, unless the
- // caller explicitly set a value. Swarm and build servers are ineligible,
- // mirroring the toggle guard so the flag never contradicts capability.
- // Runs before sentinel generation, which may throw and be swallowed below.
- if (! $setting->isDirty('is_traffic_analytics_enabled')) {
- $isSwarm = $setting->is_swarm_manager || $setting->is_swarm_worker;
- $isBuild = (bool) $setting->is_build_server;
- $setting->is_traffic_analytics_enabled = ! $isSwarm && ! $isBuild;
- }
if (str($setting->sentinel_token)->isEmpty()) {
$setting->generateSentinelToken(save: false, ignoreEvent: true);
}
diff --git a/resources/views/livewire/analytics-placeholder.blade.php b/resources/views/livewire/analytics-placeholder.blade.php
index cb6007b95b..767f0f46ac 100644
--- a/resources/views/livewire/analytics-placeholder.blade.php
+++ b/resources/views/livewire/analytics-placeholder.blade.php
@@ -1,4 +1,5 @@
+ @if (! ($hideSkeleton ?? false))
{{-- Header (real chrome; only the data below is a skeleton) --}}
@if (empty($scopedServerUuid ?? null))
@@ -54,4 +55,5 @@
+ @endif
diff --git a/resources/views/livewire/analytics.blade.php b/resources/views/livewire/analytics.blade.php
index e816d1c2a0..9b6fd3643b 100644
--- a/resources/views/livewire/analytics.blade.php
+++ b/resources/views/livewire/analytics.blade.php
@@ -123,17 +123,17 @@ $appListboxOptions = array_merge(
@endif
@if ($servers->isEmpty())
-
- @if ($scopedServerUuid === null)
+ @if ($scopedServerUuid === null)
+
View servers
- @endif
-
+
+ @endif
@elseif (! $overview)
sortBy('name', SORT_NATURAL)->take($dashboardItemLimit);
$dashboardServers = $servers->sortBy('name', SORT_NATURAL)->take($dashboardItemLimit);
+ $hasTrafficAnalytics = $servers->contains(fn ($server) => $server->isTrafficAnalyticsEnabled());
@endphp
-
+ @if ($hasTrafficAnalytics)
+
+ @endif
- {{-- Real section header; only the KPI grid below is a skeleton. --}}
-
-
-
- Traffic analytics
-
-
- Team-wide request volume across servers with traffic analytics enabled
-
-
-
-
-
-
-
-
-
-
+
diff --git a/resources/views/livewire/dashboard/traffic-analytics.blade.php b/resources/views/livewire/dashboard/traffic-analytics.blade.php
index e707689ffc..3cf7ec4b5c 100644
--- a/resources/views/livewire/dashboard/traffic-analytics.blade.php
+++ b/resources/views/livewire/dashboard/traffic-analytics.blade.php
@@ -7,6 +7,8 @@ $approxBadge = fn (string $tooltip) => '
+@if ($servers->isNotEmpty() && $overview)
- @if ($servers->isEmpty())
-
-
-
- View servers
-
-
-
- @elseif (! $overview)
-
- @else
- {{-- Sparkline KPI cards. Each links through to the full analytics page. --}}
+ {{-- Sparkline KPI cards. Each links through to the full analytics page. --}}
- @endif
+@endif
+
diff --git a/resources/views/livewire/project/application/analytics.blade.php b/resources/views/livewire/project/application/analytics.blade.php
index 1255343c8e..5a0fa32a87 100644
--- a/resources/views/livewire/project/application/analytics.blade.php
+++ b/resources/views/livewire/project/application/analytics.blade.php
@@ -18,7 +18,7 @@ $analyticsServerUuid = $application->destination?->server?->uuid;
- Server settings
+ Server analytics
diff --git a/resources/views/livewire/project/application/configuration.blade.php b/resources/views/livewire/project/application/configuration.blade.php
index 3d6add96b5..cc3bcd8027 100644
--- a/resources/views/livewire/project/application/configuration.blade.php
+++ b/resources/views/livewire/project/application/configuration.blade.php
@@ -46,7 +46,8 @@
@elseif ($currentRoute === 'project.application.metrics')
@elseif ($currentRoute === 'project.application.analytics')
-
+
@elseif ($currentRoute === 'project.application.tags')
@elseif ($currentRoute === 'project.application.danger')
diff --git a/resources/views/livewire/project/application/general.blade.php b/resources/views/livewire/project/application/general.blade.php
index e882c518d5..1150b8d998 100644
--- a/resources/views/livewire/project/application/general.blade.php
+++ b/resources/views/livewire/project/application/general.blade.php
@@ -25,9 +25,6 @@
-
-
$buildPack !== 'dockercompose',
diff --git a/resources/views/livewire/project/shared/metrics.blade.php b/resources/views/livewire/project/shared/metrics.blade.php
index 62417d42cc..f166334236 100644
--- a/resources/views/livewire/project/shared/metrics.blade.php
+++ b/resources/views/livewire/project/shared/metrics.blade.php
@@ -24,9 +24,9 @@
-
- Enable Sentinel and metrics for this server before collecting application usage data.
-
+
@elseif (!str($resource->status)->contains('running'))
@endcan
-
+
diff --git a/resources/views/livewire/server/proxy.blade.php b/resources/views/livewire/server/proxy.blade.php
index e561f412fe..1d4033ea1a 100644
--- a/resources/views/livewire/server/proxy.blade.php
+++ b/resources/views/livewire/server/proxy.blade.php
@@ -90,6 +90,7 @@
@if ($server->proxyType() === ProxyTypes::TRAEFIK->value || $server->proxyType() === 'CADDY')
@can('update', $server)
@@ -128,6 +129,11 @@
@endif
@endif
+
+
+
+
@if ($proxySettings)
diff --git a/resources/views/livewire/server/traffic-analytics-settings.blade.php b/resources/views/livewire/server/traffic-analytics-settings.blade.php
index 49cbca4495..8031654046 100644
--- a/resources/views/livewire/server/traffic-analytics-settings.blade.php
+++ b/resources/views/livewire/server/traffic-analytics-settings.blade.php
@@ -8,10 +8,22 @@
-
- {{ $isTrafficAnalyticsEnabled ? 'Disable' : 'Enable' }} traffic analytics
-
+ @if ($isTrafficAnalyticsEnabled)
+
+
+
+
+ @endif
@if ($isTrafficAnalyticsEnabled)
@@ -45,7 +57,24 @@
@else
+ icon-name="dashboard">
+
+
+
+
+
+
+
@endif
diff --git a/tests/Feature/ApplicationGeneralLayoutTest.php b/tests/Feature/ApplicationGeneralLayoutTest.php
index 41f62022af..ceff52b3e7 100644
--- a/tests/Feature/ApplicationGeneralLayoutTest.php
+++ b/tests/Feature/ApplicationGeneralLayoutTest.php
@@ -21,6 +21,12 @@ test('compose file loading waits for the user to confirm the file location', fun
->not->toContain('x-init="$wire.dispatch(\'loadCompose\', true)"');
});
+test('traffic analytics is only shown on the dedicated analytics page', function () {
+ $view = file_get_contents(resource_path('views/livewire/project/application/general.blade.php'));
+
+ expect($view)->not->toContain('
toContain('toContain('description="Enable Sentinel and metrics for this server before collecting application usage data."')
+ ->toContain('icon-name="dashboard"')
+ ->not->toContain('');
+});
diff --git a/tests/Feature/ProxyConfigurationLoadingStateTest.php b/tests/Feature/ProxyConfigurationLoadingStateTest.php
index 112f68d8cc..f5779e0ac5 100644
--- a/tests/Feature/ProxyConfigurationLoadingStateTest.php
+++ b/tests/Feature/ProxyConfigurationLoadingStateTest.php
@@ -10,3 +10,21 @@ it('disables proxy configuration controls and covers the editor while saving', f
->toContain('Updating proxy configuration')
->toContain('aria-live="polite"');
});
+
+it('loads only the compose file from the frontend and shows the shared loading indicator', function () {
+ $page = file_get_contents(resource_path('views/livewire/server/proxy/show.blade.php'));
+ $proxy = file_get_contents(resource_path('views/livewire/server/proxy.blade.php'));
+ $component = file_get_contents(app_path('Livewire/Server/Proxy.php'));
+
+ expect($page)
+ ->toContain('')
+ ->not->toContain('');
+
+ expect($proxy)
+ ->toContain('x-init="$wire.loadProxyConfiguration()"')
+ ->toContain('wire:loading.flex wire:target="loadProxyConfiguration"')
+ ->toContain('');
+
+ expect($component)
+ ->not->toContain('$this->loadProxyConfiguration();');
+});
diff --git a/tests/Feature/TrafficAnalytics/ApplicationAnalyticsTest.php b/tests/Feature/TrafficAnalytics/ApplicationAnalyticsTest.php
index ec7f9b2037..1c551c8b21 100644
--- a/tests/Feature/TrafficAnalytics/ApplicationAnalyticsTest.php
+++ b/tests/Feature/TrafficAnalytics/ApplicationAnalyticsTest.php
@@ -3,6 +3,7 @@
use App\Livewire\Project\Application\Analytics;
use App\Models\Application;
use App\Models\Environment;
+use App\Models\InstanceSettings;
use App\Models\PrivateKey;
use App\Models\Project;
use App\Models\Server;
@@ -82,6 +83,7 @@ beforeEach(function () {
// Server (from a prior enabled test) can leak into a later test and be treated as
// analytics-enabled, mounting the component against an unreachable server.
Server::flushIdentityMap();
+ InstanceSettings::forceCreate(['id' => 0]);
$this->team = Team::factory()->create();
$this->user = User::factory()->create();
@@ -114,6 +116,35 @@ function makeAnalyticsApplication(Team $team, PrivateKey $privateKey, Environmen
]);
}
+it('only lazy loads application analytics when traffic analytics is enabled', function () {
+ $configuration = file_get_contents(resource_path('views/livewire/project/application/configuration.blade.php'));
+
+ expect($configuration)
+ ->toContain(':lazy="$application->destination?->server?->isTrafficAnalyticsEnabled()"');
+});
+
+it('renders the disabled state in the initial application analytics page response', function () {
+ $application = makeAnalyticsApplication($this->team, $this->privateKey, $this->environment, false);
+
+ $this->get(route('project.application.analytics', [
+ 'project_uuid' => $this->project->uuid,
+ 'environment_uuid' => $this->environment->uuid,
+ 'application_uuid' => $application->uuid,
+ ]))
+ ->assertOk()
+ ->assertSee('Traffic analytics is not enabled')
+ ->assertDontSee('__lazyLoad', escape: false)
+ ->assertDontSee('analytics-range-section');
+});
+
+it('guards the lazy placeholder when traffic analytics is disabled', function () {
+ $application = makeAnalyticsApplication($this->team, $this->privateKey, $this->environment, false);
+
+ Livewire::test(Analytics::class, ['application' => $application, 'lazy' => true])
+ ->assertSee('Traffic analytics is not enabled')
+ ->assertDontSee('analytics-range-section');
+});
+
it('renders KPIs from a mocked traffic client when analytics is enabled', function () {
$application = makeAnalyticsApplication($this->team, $this->privateKey, $this->environment, true);
@@ -189,9 +220,13 @@ it('falls back to the donut for the per-app chart when the series endpoint is ab
it('shows an empty state when traffic analytics is disabled for the server', function () {
$application = makeAnalyticsApplication($this->team, $this->privateKey, $this->environment, false);
- loadLazy(Livewire::test(Analytics::class, ['application' => $application]))
+ Livewire::test(Analytics::class, ['application' => $application, 'lazy' => false])
->assertOk()
->assertSee('Analytics')
+ ->assertSee('Traffic analytics is not enabled')
+ ->assertSee('Server analytics')
+ ->assertSeeHtml(route('server.analytics', ['server_uuid' => $application->destination->server->uuid]))
+ ->assertDontSee('__lazyLoad', escape: false)
->assertDontSee('Unique visitors');
});
@@ -204,8 +239,10 @@ it('renders the disabled empty-state without crashing when the application has n
expect($application->destination)->toBeNull();
- loadLazy(Livewire::test(Analytics::class, ['application' => $application]))
+ Livewire::test(Analytics::class, ['application' => $application, 'lazy' => false])
->assertOk()
->assertSee('Analytics')
- ->assertDontSee('Server settings');
+ ->assertSee('Traffic analytics is not enabled')
+ ->assertDontSee('__lazyLoad', escape: false)
+ ->assertDontSee('Server analytics');
});
diff --git a/tests/Feature/TrafficAnalytics/DashboardTrafficAnalyticsTest.php b/tests/Feature/TrafficAnalytics/DashboardTrafficAnalyticsTest.php
index 58a701f6dc..76a3c5b7ed 100644
--- a/tests/Feature/TrafficAnalytics/DashboardTrafficAnalyticsTest.php
+++ b/tests/Feature/TrafficAnalytics/DashboardTrafficAnalyticsTest.php
@@ -1,5 +1,6 @@
privateKey = PrivateKey::factory()->create(['team_id' => $this->team->id]);
});
+it('hides traffic analytics from the dashboard when no server has it enabled', function () {
+ $server = Server::factory()->create([
+ 'team_id' => $this->team->id,
+ 'private_key_id' => $this->privateKey->id,
+ ]);
+ $server->settings->is_traffic_analytics_enabled = false;
+ $server->settings->save();
+
+ Livewire::test(Dashboard::class)
+ ->assertOk()
+ ->assertDontSee('Traffic analytics');
+});
+
it('renders the team traffic summary aggregated across servers with an approximate badge', function () {
$serverOne = Server::factory()->create([
'team_id' => $this->team->id,
@@ -150,6 +164,14 @@ it('shows loading states while the dashboard range refreshes', function () {
->toContain('aria-label="Loading analytics"');
});
+it('styles the open analytics link as a dashboard action button', function () {
+ $view = file_get_contents(resource_path('views/livewire/dashboard/traffic-analytics.blade.php'));
+
+ expect($view)
+ ->toContain('class="group inline-flex h-7 shrink-0 items-center gap-1.5 rounded-md border border-neutral-200 bg-white px-2.5')
+ ->toContain('group-hover:translate-x-0.5');
+});
+
it('uses the dashboard surface treatment for the analytics KPI group', function () {
$view = file_get_contents(resource_path('views/livewire/dashboard/traffic-analytics.blade.php'));
@@ -162,7 +184,7 @@ it('uses the dashboard surface treatment for the analytics KPI group', function
->not->toContain('dark:bg-base dark:hover:bg-white/[0.03]');
});
-it('shows a failure empty-state instead of an all-zero KPI panel when every server fetch fails', function () {
+it('hides dashboard analytics when every server fetch fails', function () {
$serverOne = Server::factory()->create([
'team_id' => $this->team->id,
'private_key_id' => $this->privateKey->id,
@@ -183,12 +205,14 @@ it('shows a failure empty-state instead of an all-zero KPI panel when every serv
loadLazy(Livewire::test(TrafficAnalytics::class))
->assertOk()
- ->assertSee('No analytics data yet')
+ ->assertSeeHtml('class="contents"')
+ ->assertDontSee('Traffic analytics')
+ ->assertDontSee('No analytics data yet')
->assertDontSee('Unique visitors')
->assertDontSee('Error rate');
});
-it('shows an empty state when no server in the team has traffic analytics enabled', function () {
+it('renders nothing when no server in the team has traffic analytics enabled', function () {
$server = Server::factory()->create([
'team_id' => $this->team->id,
'private_key_id' => $this->privateKey->id,
@@ -200,5 +224,13 @@ it('shows an empty state when no server in the team has traffic analytics enable
loadLazy(Livewire::test(TrafficAnalytics::class))
->assertOk()
->assertDontSee('Unique visitors')
- ->assertSee('not enabled');
+ ->assertDontSee('Traffic analytics');
+});
+
+it('uses an empty lazy placeholder so analytics only appears after data loads', function () {
+ $view = file_get_contents(resource_path('views/livewire/dashboard/traffic-analytics-placeholder.blade.php'));
+
+ expect($view)
+ ->toContain('class="contents"')
+ ->not->toContain('Traffic analytics');
});
diff --git a/tests/Feature/TrafficAnalytics/ServerAnalyticsPageTest.php b/tests/Feature/TrafficAnalytics/ServerAnalyticsPageTest.php
index 1f74a6e59a..cdf0eb4ab4 100644
--- a/tests/Feature/TrafficAnalytics/ServerAnalyticsPageTest.php
+++ b/tests/Feature/TrafficAnalytics/ServerAnalyticsPageTest.php
@@ -3,6 +3,7 @@
use App\Livewire\Analytics;
use App\Livewire\Server\Analytics\Show;
use App\Livewire\Server\TrafficAnalyticsSettings;
+use App\Models\InstanceSettings;
use App\Models\Server;
use App\Models\User;
use Illuminate\Database\Eloquent\ModelNotFoundException;
@@ -13,6 +14,7 @@ use Livewire\Livewire;
uses(RefreshDatabase::class);
beforeEach(function () {
+ InstanceSettings::forceCreate(['id' => 0]);
$this->user = User::factory()->create();
$this->team = $this->user->teams()->first();
$this->actingAs($this->user);
@@ -60,6 +62,20 @@ it('moves traffic analytics configuration out of sentinel and onto analytics', f
->not->toContain('id="trafficTopn"');
});
+it('matches the server metrics empty state when traffic analytics is disabled', function () {
+ $view = file_get_contents(resource_path('views/livewire/server/traffic-analytics-settings.blade.php'));
+ $disabledState = str($view)
+ ->after('@else')
+ ->before('@endif')
+ ->toString();
+
+ expect($disabledState)
+ ->toContain('title="Traffic analytics is disabled"')
+ ->toContain('')
+ ->toContain('isHighlightedButton')
+ ->toContain('buttonTitle="Enable traffic analytics"');
+});
+
it('renders traffic analytics settings above the server analytics dashboard', function () {
$view = file_get_contents(resource_path('views/livewire/server/analytics/show.blade.php'));
@@ -67,6 +83,13 @@ it('renders traffic analytics settings above the server analytics dashboard', fu
->toBeLessThan(strpos($view, 'toContain(':lazy="$server->isTrafficAnalyticsEnabled()"');
+});
+
it('matches other server pages without a visible page title', function () {
$view = file_get_contents(resource_path('views/livewire/server/analytics/show.blade.php'));
@@ -88,6 +111,41 @@ it('scopes the server analytics page to its route server', function () {
->assertDontSee($otherServer->name);
});
+it('does not duplicate the disabled state on a scoped server analytics dashboard', function () {
+ $server = Server::factory()->create(['team_id' => $this->team->id]);
+ $server->settings->is_traffic_analytics_enabled = false;
+ $server->settings->save();
+
+ Livewire::test(Analytics::class, ['scopedServerUuid' => $server->uuid])
+ ->assertDontSee('Traffic analytics is not enabled');
+});
+
+it('removes stale analytics content when traffic analytics is disabled', function () {
+ $server = Server::factory()->create(['team_id' => $this->team->id]);
+ $server->settings->is_traffic_analytics_enabled = true;
+ $server->settings->save();
+
+ $component = Livewire::test(Analytics::class, ['scopedServerUuid' => $server->uuid]);
+
+ $server->settings->is_traffic_analytics_enabled = false;
+ $server->settings->save();
+
+ $component
+ ->dispatch('trafficAnalyticsStateChanged')
+ ->assertSet('servers', fn ($servers) => $servers->isEmpty())
+ ->assertDontSee('No analytics data yet');
+});
+
+it('does not render a skeleton placeholder for a disabled scoped server', function () {
+ $server = Server::factory()->create(['team_id' => $this->team->id]);
+ $server->settings->is_traffic_analytics_enabled = false;
+ $server->settings->save();
+
+ Livewire::test(Analytics::class, ['scopedServerUuid' => $server->uuid, 'lazy' => true])
+ ->assertDontSee('analytics-overview-section')
+ ->assertDontSee('analytics-requests-section');
+});
+
it('saves traffic analytics settings from the server analytics page', function () {
Queue::fake();
diff --git a/tests/Feature/TrafficAnalytics/ServerSettingTrafficTest.php b/tests/Feature/TrafficAnalytics/ServerSettingTrafficTest.php
index 98710c688f..3f7d7d0b7a 100644
--- a/tests/Feature/TrafficAnalytics/ServerSettingTrafficTest.php
+++ b/tests/Feature/TrafficAnalytics/ServerSettingTrafficTest.php
@@ -14,14 +14,14 @@ beforeEach(function () {
$this->team = $user->teams()->first();
});
-it('defaults traffic analytics to enabled for a normal server and exposes a server helper', function () {
+it('defaults traffic analytics to disabled for a normal server and exposes a server helper', function () {
$server = Server::factory()->create(['team_id' => $this->team->id]);
- expect($server->settings->is_traffic_analytics_enabled)->toBeTrue();
- expect($server->isTrafficAnalyticsEnabled())->toBeTrue();
+ expect($server->settings->is_traffic_analytics_enabled)->toBeFalse();
+ expect($server->isTrafficAnalyticsEnabled())->toBeFalse();
- $server->settings->is_traffic_analytics_enabled = false;
+ $server->settings->is_traffic_analytics_enabled = true;
$server->settings->save();
- expect($server->fresh()->isTrafficAnalyticsEnabled())->toBeFalse();
+ expect($server->fresh()->isTrafficAnalyticsEnabled())->toBeTrue();
});
it('defaults traffic analytics to disabled for a swarm server', function () {
@@ -44,14 +44,14 @@ it('defaults traffic analytics to disabled for a build server', function () {
expect($setting->is_traffic_analytics_enabled)->toBeFalse();
});
-it('respects an explicit traffic analytics value on creation', function () {
+it('respects an explicit enabled traffic analytics value on creation', function () {
$server = Server::factory()->create(['team_id' => $this->team->id]);
$setting = ServerSetting::create([
'server_id' => $server->id,
- 'is_traffic_analytics_enabled' => false,
+ 'is_traffic_analytics_enabled' => true,
]);
- expect($setting->is_traffic_analytics_enabled)->toBeFalse();
+ expect($setting->is_traffic_analytics_enabled)->toBeTrue();
});
it('defaults traffic collection and geoip settings to sentinel values', function () {
diff --git a/tests/Feature/TrafficAnalytics/ToggleTrafficAnalyticsTest.php b/tests/Feature/TrafficAnalytics/ToggleTrafficAnalyticsTest.php
index d1a75ba0ff..63864aada1 100644
--- a/tests/Feature/TrafficAnalytics/ToggleTrafficAnalyticsTest.php
+++ b/tests/Feature/TrafficAnalytics/ToggleTrafficAnalyticsTest.php
@@ -1,7 +1,9 @@
0]);
$this->user = User::factory()->create();
$this->team = $this->user->teams()->first();
$this->actingAs($this->user);
@@ -32,11 +35,32 @@ it('toggles traffic analytics via the sentinel settings component', function ()
Livewire::test(TrafficAnalyticsSettings::class, ['server' => $server])
->call('toggleTrafficAnalytics')
+ ->assertDispatchedTo(Analytics::class, 'trafficAnalyticsStateChanged')
->assertHasNoErrors();
expect($server->fresh()->isTrafficAnalyticsEnabled())->toBeTrue();
});
+it('warns about the application interruption before enabling traffic analytics', function () {
+ $server = Server::factory()->create(['team_id' => $this->team->id]);
+ $server->settings->is_traffic_analytics_enabled = false;
+ $server->settings->save();
+
+ Livewire::test(TrafficAnalyticsSettings::class, ['server' => $server])
+ ->assertSee('Enable traffic analytics?')
+ ->assertDontSeeHtml('wire:confirm')
+ ->assertSeeHtml('wire:loading.flex')
+ ->assertSeeHtml('wire:target="toggleTrafficAnalytics"')
+ ->assertSee('Restarting Sentinel and proxy...')
+ ->assertSee('Enabling traffic analytics will restart Sentinel and the proxy. Your applications will experience a brief interruption.');
+});
+
+it('allows the analytics toggle modal to update after the state changes', function () {
+ $view = file_get_contents(resource_path('views/livewire/server/traffic-analytics-settings.blade.php'));
+
+ expect($view)->toContain(':ignoreWire="false"');
+});
+
it('does not enable traffic analytics on a swarm server', function () {
ConfigureTrafficAnalytics::partialMock()->shouldReceive('handle')->never();