diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 2cd0cc9..102afeb 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -14,7 +14,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest] - php: [8.4] + php: [8.4, 8.5] laravel: [11.*, 12.*] include: - laravel: 11.* @@ -27,6 +27,9 @@ jobs: carbon: ^3.5 contracts: ^12.0 collision: ^8.0 + exclude: + - laravel: 11.* + php: 8.5 name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.os }} @@ -54,5 +57,8 @@ jobs: - name: List Installed Dependencies run: composer show -D + - name: Run Security Audit + run: composer audit + - name: Execute tests - run: vendor/bin/pest + run: vendor/bin/pest --ci --no-coverage diff --git a/composer.json b/composer.json index d547c2a..2f79e37 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": "^8.4", + "php": "^8.4|^8.5", "code16/sharp": "^9.0", "illuminate/contracts": "^11.0||^12.0", "spatie/laravel-package-tools": "^1.16" @@ -66,6 +66,6 @@ } } }, - "minimum-stability": "dev", + "minimum-stability": "stable", "prefer-stable": true } diff --git a/src/Client/FathomClient.php b/src/Client/FathomClient.php index bee6f78..ef02078 100644 --- a/src/Client/FathomClient.php +++ b/src/Client/FathomClient.php @@ -65,8 +65,17 @@ public function getEndDate(): Carbon public function getSite(): ?Site { if (config()->boolean('sharp-fathom-dashboard.cache')) { - $data = Cache::get($this->siteId); - return Cache::remember($this->siteId, $this->getCacheDuration(), fn () => $this->executeGetSite()); + $site = Cache::get($this->siteId); + if($site === null){ + try{ + $site = $this->executeGetSite(); + Cache::put($this->siteId, $site, $this->getCacheDuration()); + }catch (\Throwable $e) { + report($e); + return null; + } + } + return $site; } else { return $this->executeGetSite(); } diff --git a/tests/ArchTest.php b/tests/ArchTest.php index 7d5d1f2..04b7d07 100644 --- a/tests/ArchTest.php +++ b/tests/ArchTest.php @@ -1,5 +1,5 @@ expect(['dd', 'dump', 'ray', 'die']) + ->expect(['dd', 'dump', 'ray', 'die', 'vardump']) ->each->not->toBeUsed(); diff --git a/tests/FathomAnalyticsDateFilterTest.php b/tests/FathomAnalyticsDateFilterTest.php index 3950d5d..c75ebc9 100644 --- a/tests/FathomAnalyticsDateFilterTest.php +++ b/tests/FathomAnalyticsDateFilterTest.php @@ -12,7 +12,7 @@ expect($default) ->toBeArray() - ->and($default['start'])->toBe('2025-09-23') + ->and($default['start'])->toBe('2025-09-24') ->and($default['end'])->toBe('2025-10-23'); Carbon::setTestNow(); // clear diff --git a/tests/FathomClientTest.php b/tests/FathomClientTest.php index 30c20aa..ccf852d 100644 --- a/tests/FathomClientTest.php +++ b/tests/FathomClientTest.php @@ -77,6 +77,81 @@ ->and($second->id)->toBe('SITE_123'); }); +it('getStats returns day-keyed array and handles 200', function () { + $startDate = \Illuminate\Support\Carbon::parse('2025-01-01'); + $endDate = \Illuminate\Support\Carbon::parse('2025-01-02'); + + Http::fake([ + '*/aggregations*' => Http::response([ + ['date' => '2025-01-01', 'visits' => 10, 'pageviews' => 20], + ['date' => '2025-01-02', 'visits' => 15, 'pageviews' => 25], + ], 200), + ]); + + $client = new FathomClient($startDate, $endDate); + $stats = $client->getStats(); + + expect($stats)->toBeArray()->toHaveCount(2) + ->and($stats)->toHaveKeys(['2025-01-01', '2025-01-02']) + ->and($stats['2025-01-01']['visits'])->toBe(10); +}); + +it('getStats uses cache when enabled', function () { + config()->set('sharp-fathom-dashboard.cache', true); + config()->set('sharp-fathom-dashboard.cache_ttl', 60); + + Http::fakeSequence() + ->push([ + ['date' => '2025-01-01', 'visits' => 10], + ], 200) + ->push([], 500); + + $startDate = \Illuminate\Support\Carbon::parse('2025-01-01'); + $client = new FathomClient($startDate, $startDate); + + $first = $client->getStats(); + $second = $client->getStats(); + + expect($first)->toBeArray() + ->and($second)->toBe($first); +}); + +it('getMostViewedPages uses cache when enabled', function () { + config()->set('sharp-fathom-dashboard.cache', true); + config()->set('sharp-fathom-dashboard.cache_ttl', 60); + + Http::fakeSequence() + ->push([ + ['pathname' => '/', 'pageviews' => 10], + ], 200) + ->push([], 500); + + $client = new FathomClient(); + $first = $client->getMostViewedPages(); + $second = $client->getMostViewedPages(); + + expect($first)->toBeArray() + ->and($second)->toBe($first); +}); + +it('getTopReferrers uses cache when enabled', function () { + config()->set('sharp-fathom-dashboard.cache', true); + config()->set('sharp-fathom-dashboard.cache_ttl', 60); + + Http::fakeSequence() + ->push([ + ['referrer_hostname' => 'google.com', 'pageviews' => 10], + ], 200) + ->push([], 500); + + $client = new FathomClient(); + $first = $client->getTopReferrers(); + $second = $client->getTopReferrers(); + + expect($first)->toBeArray() + ->and($second)->toBe($first); +}); + it('executeGetMostViewedPages returns array on 200 and throws on error', function () { Http::fakeSequence() ->push([ diff --git a/tests/OpenFathomSharpCommandTest.php b/tests/OpenFathomSharpCommandTest.php new file mode 100644 index 0000000..8af121b --- /dev/null +++ b/tests/OpenFathomSharpCommandTest.php @@ -0,0 +1,32 @@ +label())->toBe('Open Fathom dashboard'); +}); + +it('authorizes if fathom_access_url is set', function () { + $command = new OpenFathomSharpCommand(); + + config(['sharp-fathom-dashboard.fathom_access_url' => null]); + expect($command->authorize())->toBeFalse(); + + config(['sharp-fathom-dashboard.fathom_access_url' => 'https://app.usefathom.com/share/SITE_123/site']); + expect($command->authorize())->toBeTrue(); +}); + +it('returns a link on execute', function () { + $url = 'https://app.usefathom.com/share/SITE_123/site'; + config(['sharp-fathom-dashboard.fathom_access_url' => $url]); + + $command = new OpenFathomSharpCommand(); + $result = $command->execute(); + + expect($result)->toBe([ + 'action' => 'link', + 'link' => $url, + 'openInNewTab' => false, + ]); +}); diff --git a/tests/SharpFathomDashboardTest.php b/tests/SharpFathomDashboardTest.php new file mode 100644 index 0000000..4d2dcab --- /dev/null +++ b/tests/SharpFathomDashboardTest.php @@ -0,0 +1,77 @@ +set('sharp-fathom-dashboard.fathom_api_key', 'test-key'); + config()->set('sharp-fathom-dashboard.fathom_site_id', 'SITE_123'); + config()->set('sharp-fathom-dashboard.chart.datasets', ['pageviews', 'unique_visitors']); +}); + +it('can build widgets', function () { + $dashboard = new SharpFathomDashboard(); + + expect($dashboard->widgets())->toHaveCount(7) + ->and($dashboard->widgets())->toHaveKeys([ + 'unique_visitors', + 'pageviews', + 'avg_time_on_site', + 'bounce_rate', + 'daily_analytics', + 'most_viewed_pages', + 'top_referrers' + ]); +}); + +it('can build layout', function () { + $dashboard = new SharpFathomDashboard(); + + $layout = $dashboard->widgetsLayout(); + + expect($layout['sections'])->toHaveCount(3); +}); + +it('can build widgets data', function () { + Http::fake([ + '*/sites/*' => Http::response(['id' => 'SITE_123', 'name' => 'Test Site'], 200), + '*/aggregations*' => Http::response([ + [ + 'hostname' => 'ex.test', + 'pathname' => '/', + 'pageviews' => 10, + 'date' => now()->subDay()->format('Y-m-d'), + 'visits' => 10, + 'uniques' => 5, + 'avg_duration' => 60, + 'bounce_rate' => 0.5, + 'referrer_hostname' => 'google.com', + 'referrer_pathname' => '/' + ], + ], 200), + ]); + + $dashboard = new SharpFathomDashboard(); + $dashboard->initQueryParams([ + FathomAnalyticsDateFilter::class => '2025-01-01 - 2025-01-31' + ]); + + $data = $dashboard->data(); + + expect($data)->toHaveKeys([ + 'unique_visitors', + 'pageviews', + 'avg_time_on_site', + 'bounce_rate', + 'daily_analytics', + 'most_viewed_pages', + 'top_referrers' + ]); + + expect($data['unique_visitors']['data']['figure'])->toBe('10') + ->and($data['pageviews']['data']['figure'])->toBe('10'); +});