Files
clinicpro/tests/Shared/LandingPageTest.php
T
hamedandClaude Opus 5 30b999872c refactor(public): unify site copy with the About page voice
The About page addresses the reader formally and speaks as "we"; the
landing pages used informal second-person singular ("تعریف می‌کنی") and
the home hero was a slogan. Both now match the About tone.

Landing copy: all informal verb forms and possessives converted to the
formal plural. Home: hero headline and lead rewritten as a plain
statement of what we built, plus small wording passes on the devices,
specialties and stats blocks.

The home-page smoke assertion follows the new headline.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 15:13:24 +03:30

161 lines
6.4 KiB
PHP

<?php
namespace App\Tests\Shared;
use App\Shared\Landing\LandingRegistry;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
/**
* صفحات فرود واقعاً رندر می‌شوند و متادیتای هرکدام مالِ خودش است.
*
* روت `/{slug}` هر مسیر تک‌بخشی را می‌گیرد، پس اینجا هم بررسی می‌شود که صفحهٔ اصلی و
* مسیرهای موجود را نخورده باشد.
*/
class LandingPageTest extends WebTestCase
{
private KernelBrowser $client;
private LandingRegistry $registry;
protected function setUp(): void
{
$this->client = static::createClient();
$this->registry = new LandingRegistry();
}
private function html(string $path): string
{
$this->client->request('GET', $path);
return $this->client->getResponse()->getContent();
}
public function testEveryLandingRenders(): void
{
foreach ($this->registry->all() as $slug => $page) {
$html = $this->html('/' . $slug);
self::assertSame(200, $this->client->getResponse()->getStatusCode(), "صفحهٔ «{$slug}» باز نشد");
self::assertStringContainsString('<h1 class="reveal">' . $page->h1 . '</h1>', $html);
self::assertStringContainsString('<title>' . $page->metaTitle . '</title>', $html);
self::assertStringContainsString('content="' . $page->metaDescription . '"', $html);
}
}
public function testCanonicalPointsAtTheSamePage(): void
{
foreach (array_keys($this->registry->all()) as $slug) {
$html = $this->html('/' . $slug);
self::assertMatchesRegularExpression(
'~<link rel="canonical" href="[^"]+/' . preg_quote($slug, '~') . '"/>~u',
$html,
"canonical صفحهٔ «{$slug}» به خودش اشاره نمی‌کند",
);
}
}
public function testUnknownSlugIsNotFound(): void
{
$this->client->request('GET', '/یک-اسلاگ-که-وجود-ندارد');
self::assertSame(404, $this->client->getResponse()->getStatusCode());
}
/** روت catch-all نباید صفحهٔ اصلی را بخورد. */
public function testHomePageStillWorks(): void
{
$html = $this->html('/');
self::assertSame(200, $this->client->getResponse()->getStatusCode());
self::assertStringContainsString('کارِ مطب،', $html);
}
public function testExistingRoutesAreNotShadowed(): void
{
foreach (['/sitemap.xml', '/health'] as $path) {
$this->client->request('GET', $path);
self::assertSame(200, $this->client->getResponse()->getStatusCode(), "{$path} گرفته شد");
}
}
public function testSitemapListsHomeAndEveryLanding(): void
{
$xml = $this->html('/sitemap.xml');
$doc = new \SimpleXMLElement($xml);
// خانه + دربارهٔ ما + هر صفحهٔ فرود. عدد از خودِ رجیستری می‌آید تا افزودن خوشهٔ
// کلیدواژهٔ تازه این تست را نشکند، ولی جاافتادن یک صفحه از sitemap هنوز قرمز شود.
self::assertCount(2 + count($this->registry->all()), $doc->url);
foreach (array_keys($this->registry->all()) as $slug) {
self::assertStringContainsString(rawurlencode($slug), $xml, {$slug}» در sitemap نیست");
}
}
/** بدون لینک داخلی، صفحات فقط از sitemap دیده می‌شوند. */
public function testFooterLinksToTheFeaturedLandingsOnEveryPage(): void
{
foreach (['/', '/' . LandingRegistry::CRM] as $path) {
$html = $this->html($path);
foreach ($this->registry->featured() as $page) {
self::assertStringContainsString('href="/' . $page->slug . '"', $html, "لینک «{$page->slug}» در {$path} نیست");
}
}
}
/**
* فوتر دیگر همهٔ صفحه‌ها را فهرست نمی‌کند، پس باید ثابت بماند که از همان چند
* لینکِ فوتر، با دنبال‌کردن «راهکارهای دیگر»، به هر صفحه می‌رسیم. صفحه‌ای که فقط
* در sitemap باشد عملاً بدون لینک داخلی است و رتبه نمی‌گیرد.
*/
public function testEveryLandingIsReachableFromTheFooterLinks(): void
{
$pages = $this->registry->all();
$queue = array_map(static fn ($page): string => $page->slug, $this->registry->featured());
$reached = array_flip($queue);
while ($queue !== []) {
$slug = array_shift($queue);
foreach ($pages[$slug]->related as $next) {
if (!isset($reached[$next])) {
$reached[$next] = true;
$queue[] = $next;
}
}
}
foreach (array_keys($pages) as $slug) {
self::assertArrayHasKey($slug, $reached, {$slug}» از هیچ صفحه‌ای لینک نمی‌گیرد");
}
}
public function testStructuredDataIsValidJson(): void
{
foreach (array_keys($this->registry->all()) as $slug) {
$html = $this->html('/' . $slug);
preg_match('~<script type="application/ld\+json">(.*?)</script>~s', $html, $m);
self::assertNotEmpty($m, "صفحهٔ «{$slug}» بلاک JSON-LD ندارد");
$data = json_decode(html_entity_decode($m[1]), true, flags: JSON_THROW_ON_ERROR);
$types = array_column($data['@graph'], '@type');
self::assertContains('SoftwareApplication', $types);
self::assertContains('BreadcrumbList', $types);
self::assertContains('FAQPage', $types);
}
}
/** تم مشترک است، پس همان CSS صفحهٔ اصلی باید بارگذاری شود. */
public function testLandingUsesTheHomepageTheme(): void
{
preg_match_all('~href="(/build/[^"]+\.css)"~', $this->html('/'), $home);
preg_match_all('~href="(/build/[^"]+\.css)"~', $this->html('/' . LandingRegistry::BEAUTY), $landing);
self::assertNotEmpty($home[1]);
self::assertSame($home[1], $landing[1], 'لندینگ باید همان CSS صفحهٔ اصلی را بارگذاری کند');
}
}