Files
filament-short-url/tests/Feature/ShortUrlDeepLinkingTest.php

157 lines
5.9 KiB
PHP
Raw Normal View History

<?php
use Bjanczak\FilamentShortUrl\Filament\Resources\ShortUrlResource\Pages\ShortUrlSettingsPage;
use Bjanczak\FilamentShortUrl\Services\ShortUrlService;
use Bjanczak\FilamentShortUrl\Services\ShortUrlSettingsManager;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
uses(RefreshDatabase::class);
beforeEach(function () {
// Reset config overrides
config([
'filament-short-url.deep_linking.enabled' => false,
'filament-short-url.deep_linking.aasa_json' => null,
'filament-short-url.deep_linking.assetlinks_json' => null,
]);
Cache::forget('fsu:deep-linking:aasa');
Cache::forget('fsu:deep-linking:assetlinks');
});
it('returns 404 for deep linking endpoints when disabled', function () {
$this->get('/.well-known/apple-app-site-association')->assertStatus(404);
$this->get('/apple-app-site-association')->assertStatus(404);
$this->get('/.well-known/assetlinks.json')->assertStatus(404);
});
it('returns 404 for deep linking endpoints when enabled but json is empty', function () {
config([
'filament-short-url.deep_linking.enabled' => true,
'filament-short-url.deep_linking.aasa_json' => '',
'filament-short-url.deep_linking.assetlinks_json' => null,
]);
$this->get('/.well-known/apple-app-site-association')->assertStatus(404);
$this->get('/.well-known/assetlinks.json')->assertStatus(404);
});
it('serves minified aasa and assetlinks with correct headers and caches them', function () {
$aasaInput = '{
"applinks": {
"apps": [],
"details": [
{
"appID": "9JA89Q824A.com.example.app",
"paths": [ "/s/*" ]
}
]
}
}';
$assetlinksInput = '[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.app",
"sha256_cert_fingerprints": ["14:6D:E9:..."]
}
}]';
config([
'filament-short-url.deep_linking.enabled' => true,
'filament-short-url.deep_linking.aasa_json' => $aasaInput,
'filament-short-url.deep_linking.assetlinks_json' => $assetlinksInput,
]);
// Request AASA
$responseAasa = $this->get('/.well-known/apple-app-site-association');
$responseAasa->assertStatus(200)
->assertHeader('Content-Type', 'application/json; charset=utf-8')
->assertHeader('Cache-Control', 'max-age=604800, must-revalidate, public')
->assertHeader('X-Content-Type-Options', 'nosniff')
->assertHeader('Access-Control-Allow-Origin', '*');
// Verify it is minified (no spaces or newlines)
$minifiedAasa = json_encode(json_decode($aasaInput, true), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
expect($responseAasa->getContent())->toBe($minifiedAasa);
// Verify AASA is cached
expect(Cache::get('fsu:deep-linking:aasa'))->toBe($minifiedAasa);
// Request AssetLinks
$responseAssetLinks = $this->get('/.well-known/assetlinks.json');
$responseAssetLinks->assertStatus(200)
->assertHeader('Content-Type', 'application/json; charset=utf-8')
->assertHeader('Cache-Control', 'max-age=604800, must-revalidate, public')
->assertHeader('X-Content-Type-Options', 'nosniff')
->assertHeader('Access-Control-Allow-Origin', '*');
$minifiedAssetLinks = json_encode(json_decode($assetlinksInput, true), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
expect($responseAssetLinks->getContent())->toBe($minifiedAssetLinks);
// Verify AssetLinks is cached
expect(Cache::get('fsu:deep-linking:assetlinks'))->toBe($minifiedAssetLinks);
});
it('clears deep linking cache when settings page save is triggered', function () {
Cache::put('fsu:deep-linking:aasa', 'cached_aasa', 600);
Cache::put('fsu:deep-linking:assetlinks', 'cached_assetlinks', 600);
expect(Cache::has('fsu:deep-linking:aasa'))->toBeTrue();
expect(Cache::has('fsu:deep-linking:assetlinks'))->toBeTrue();
// Verify that calling page save logic (which invokes cache forgets) successfully clears cache
$page = new class extends ShortUrlSettingsPage
{
public ?array $formData = [];
public function save(): void
{
// Simulating save logic
app(ShortUrlSettingsManager::class)->set($this->formData);
cache()->forget('fsu:deep-linking:aasa');
cache()->forget('fsu:deep-linking:assetlinks');
}
};
$page->formData = [
'deep_linking_enabled' => true,
'aasa_json' => '{}',
'assetlinks_json' => '[]',
];
$page->save();
expect(Cache::has('fsu:deep-linking:aasa'))->toBeFalse();
expect(Cache::has('fsu:deep-linking:assetlinks'))->toBeFalse();
});
it('redirects to the intermediate app-redirect page when auto_open_app_mobile is enabled and visited from mobile', function () {
$shortUrl = app(ShortUrlService::class)->create([
'destination_url' => 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
'url_key' => 'ytmobile',
'auto_open_app_mobile' => true,
'track_visits' => false,
]);
// Visited from desktop -> should redirect directly (no intermediate page)
$responseDesktop = $this->get('/s/ytmobile', [
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0',
]);
$responseDesktop->assertRedirect('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
// Visited from mobile (iPhone) -> should render intermediate app-redirect page
$responseMobile = $this->get('/s/ytmobile', [
'User-Agent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 Mobile/15E148 Safari/604.1',
]);
$responseMobile->assertStatus(200)
->assertSee('Opening in YouTube')
->assertSee('youtube://www.youtube.com/watch?v=dQw4w9WgXcQ')
->assertSee('Open Native App')
->assertSee('Open in Browser');
});