Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions e2e/og-meta.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { test, expect, type Page } from '@playwright/test';

const meta = (page: Page, selector: string) => page.locator(`head > meta[${selector}]`).getAttribute('content');

test('工具頁的分享卡指向該工具、該語系的圖', async ({ page }) => {
await page.goto('/tools/url-audit');
expect(await meta(page, 'property="og:image"')).toBe('https://tiglet.vercel.app/og/zh/url-audit.png');
expect(await meta(page, 'name="twitter:image"')).toBe('https://tiglet.vercel.app/og/zh/url-audit.png');
});

test('英文頁用英文卡', async ({ page }) => {
await page.goto('/en/tools/url-audit');
expect(await meta(page, 'property="og:image"')).toBe('https://tiglet.vercel.app/og/en/url-audit.png');
});

test('首頁用首頁卡,雙語各一張', async ({ page }) => {
await page.goto('/');
expect(await meta(page, 'property="og:image"')).toBe('https://tiglet.vercel.app/og/zh/home.png');
await page.goto('/en/');
expect(await meta(page, 'property="og:image"')).toBe('https://tiglet.vercel.app/og/en/home.png');
});

test('大圖分享卡:twitter:card 與尺寸標註', async ({ page }) => {
await page.goto('/tools/gear-calculator');
expect(await meta(page, 'name="twitter:card"')).toBe('summary_large_image');
expect(await meta(page, 'property="og:image:width"')).toBe('1200');
expect(await meta(page, 'property="og:image:height"')).toBe('630');
expect(await meta(page, 'property="og:image:alt"')).toBe('齒比計算器 — Tiglet');
});

test('分享卡圖真的存在(不是 404 的連結)', async ({ page, request }) => {
await page.goto('/tools/stage-profile');
const src = await meta(page, 'property="og:image"');
const res = await request.get(new URL(src!).pathname);
expect(res.status()).toBe(200);
expect(res.headers()['content-type']).toContain('image/png');
});

test('GEO:作者標註與夠長的首頁描述', async ({ page }) => {
await page.goto('/');
expect(await meta(page, 'name="author"')).toBe('TTigger');
// 健檢工具自己的規則:description 落在 50–160 字才算 pass
const desc = await meta(page, 'name="description"');
expect(desc!.length).toBeGreaterThanOrEqual(50);
expect(desc!.length).toBeLessThanOrEqual(160);
});
14 changes: 11 additions & 3 deletions src/layouts/BaseLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,22 @@ import Footer from '../components/Footer.astro';
import CommandPalette from '../components/CommandPalette.tsx';
import TrackVisit from '../components/TrackVisit.tsx';
import { localeFromPath, switchLocalePath } from '../lib/i18n';
import { ogImagePath } from '../lib/og';

interface Props { title?: string; description?: string; toolId?: string; }
const locale = localeFromPath(Astro.url.pathname);
const {
title = locale === 'en' ? 'Tiglet — small browser tools' : 'Tiglet — 小工具站',
description = locale === 'en' ? 'A clean, no-login collection of small browser tools.' : 'TTigger 的小工具集與作品集。',
description = locale === 'en'
? 'A clean, no-login collection of small browser tools.'
: '一組乾淨、免登入的瀏覽器小工具:計算、文字處理、圖片、單車與遊戲,全部在你的瀏覽器裡完成,資料不上傳。',
toolId,
} = Astro.props;

const site = Astro.site ?? new URL('https://tiglet.vercel.app');
const canonical = new URL(Astro.url.pathname, site);
const ogImage = new URL('/pwa-512x512.png', site);
// 每個工具、每個語系一張 1200×630 分享卡;由 `npm run og` 產生
const ogImage = new URL(ogImagePath(toolId, locale), site);
// hreflang 對照連結(zh 為 x-default)
const zhUrl = new URL(switchLocalePath(Astro.url.pathname, 'zh'), site);
const enUrl = new URL(switchLocalePath(Astro.url.pathname, 'en'), site);
Expand Down Expand Up @@ -50,6 +54,7 @@ const jsonLd = {
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{title}</title>
<meta name="description" content={description} />
<meta name="author" content="TTigger" />
<link rel="canonical" href={canonical.href} />
<link rel="alternate" hreflang="zh-Hant" href={zhUrl.href} />
<link rel="alternate" hreflang="en" href={enUrl.href} />
Expand All @@ -62,10 +67,13 @@ const jsonLd = {
<meta property="og:description" content={description} />
<meta property="og:url" content={canonical.href} />
<meta property="og:image" content={ogImage.href} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content={title} />
<meta property="og:locale" content={locale === 'en' ? 'en_US' : 'zh_Hant'} />

<!-- Twitter -->
<meta name="twitter:card" content="summary" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={ogImage.href} />
Expand Down
Loading