Files
timebank-cc-public/tests/Feature/PlatformHeadTest.php
T

108 lines
4.9 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\Admin;
use App\Notifications\NonUserPasswordResetNotification;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\View;
use Illuminate\Support\HtmlString;
use Tests\TestCase;
/**
* The shared head of the layouts (layouts/partials/brand-head): title, icons and the Open Graph preview for shared
* links. Optional files are linked only when they exist (platform folder, theme, public/).
*/
class PlatformHeadTest extends TestCase
{
private string $root;
private string $public;
private string $originalPublic;
protected function setUp(): void
{
parent::setUp();
$base = sys_get_temp_dir() . '/platform-head-test-' . uniqid();
$this->root = "{$base}/platform";
$this->public = "{$base}/public";
File::makeDirectory("{$this->root}/public", 0755, true);
File::makeDirectory($this->public, 0755, true);
$this->originalPublic = public_path();
$this->app->usePublicPath($this->public);
config(['app.platform_path' => $this->root]);
// The layouts' @vite would look for the build manifest in the temporary public/
$this->withoutVite();
}
protected function tearDown(): void
{
View::flushSections();
$this->app->usePublicPath($this->originalPublic);
File::deleteDirectory(dirname($this->root));
parent::tearDown();
}
private function renderHead(): string
{
return view('layouts.guest', ['slot' => new HtmlString('')])->render();
}
public function test_open_graph_tags_use_property_and_the_title_is_escaped_once(): void
{
View::startSection('title', 'Terms & conditions');
$html = $this->renderHead();
$this->assertStringContainsString('<title>Terms &amp; conditions - ' . e(config('app.name')) . '</title>', $html);
$this->assertStringContainsString('<meta property="og:title" content="Terms &amp; conditions - ' . e(config('app.name')) . '">', $html);
$this->assertStringContainsString('<meta property="og:site_name"', $html);
$this->assertStringContainsString('<meta property="og:url" content="' . url()->current() . '">', $html);
$this->assertStringNotContainsString('&amp;amp;', $html);
$this->assertStringNotContainsString('name="og:', $html);
$this->assertStringNotContainsString('og:image', $html, 'no preview image without a file');
}
public function test_the_preview_image_comes_from_the_platform_folder_then_the_theme(): void
{
File::put("{$this->public}/theme-preview.png", 'png');
config(['themes.themes.' . theme_id() . '.logos.og_image' => 'theme-preview.png']);
$this->assertStringContainsString('<meta property="og:image" content="' . asset('theme-preview.png') . '">', $this->renderHead());
File::put("{$this->root}/public/og-image.png", 'png');
$this->artisan('platform:link')->assertExitCode(0);
$html = $this->renderHead();
$this->assertStringContainsString('<meta property="og:image" content="' . asset('platform/og-image.png') . '">', $html);
$this->assertStringContainsString('<meta name="twitter:card" content="summary_large_image">', $html);
}
public function test_optional_icons_are_linked_only_when_present(): void
{
$html = $this->renderHead();
$this->assertStringNotContainsString('favicon.svg', $html);
$this->assertStringNotContainsString('rel="manifest"', $html);
$this->assertStringContainsString('favicon-32x32.png', $html, 'the standard icons are always linked');
File::put("{$this->root}/public/favicon.svg", '<svg/>');
File::put("{$this->root}/public/site.webmanifest", '{}');
File::put("{$this->public}/favicon-96x96.png", 'png');
$this->artisan('platform:link')->assertExitCode(0);
$html = $this->renderHead();
$this->assertStringContainsString('<link rel="icon" type="image/svg+xml" href="' . asset('platform/favicon.svg') . '">', $html);
$this->assertStringContainsString('<link rel="manifest" href="' . asset('platform/site.webmanifest') . '">', $html);
$this->assertStringContainsString('<link rel="icon" type="image/png" sizes="96x96" href="' . asset('favicon-96x96.png') . '">', $html);
}
/**
* Uuro's branch added onQueue('high') to this notification (2025-12-15), which is not a queued notification:
* every admin or bank password reset then failed with "Call to undefined method ...::onQueue()".
*/
public function test_the_admin_and_bank_password_reset_mail_can_be_built(): void
{
$notification = new NonUserPasswordResetNotification('https://example.test/reset/abc', new Admin(['name' => 'admin-test']));
$this->assertStringContainsString('https://example.test/reset/abc', (string) $notification->toMail(new Admin(['name' => 'admin-test']))->render());
}
}