test(ci): add exporter heading levels and a11y typography smoke; update MAIN_PLAN
CI / test (pull_request) Has been cancelled

This commit is contained in:
2025-11-14 16:40:45 +09:00
parent aaa624a046
commit cd6a6ea069
93 changed files with 8543 additions and 13 deletions
+75
View File
@@ -0,0 +1,75 @@
import { test, expect } from '@playwright/test'
import JSZip from 'jszip'
const builderUrl = '/en/builder'
type WindowWithExport = Window & {
__captureExport?: boolean
__lastExport?: { html: string; css: string; js: string }
__lastBlob?: Blob
__exportPreview?: () => { html: string; css: string; js: string }
}
test('Export ZIP contains expected SEO/OG/Analytics meta in index.html', async ({ page }) => {
await page.goto(builderUrl)
// enable capture hook in app
await page.evaluate(() => {
const w = window as unknown as WindowWithExport
w.__captureExport = true
w.__lastBlob = undefined
})
// set SEO/Analytics/OG values
await page.getByLabel('SEO Title').fill('E2E SEO Title')
await page.getByLabel('SEO Description').fill('E2E SEO Description')
await page.getByLabel('OG Image URL').fill('https://example.com/e2e-og.png')
await page.getByLabel('GA4 Measurement ID').fill('G-E2E1234567')
await page.getByLabel('Meta Pixel ID').fill('9876543210')
// Prefer calling exporter directly for deterministic output
const exported = await page.evaluate(() => (window as unknown as WindowWithExport).__exportPreview?.() ?? null)
let indexHtml: string | null = exported?.html ?? null
// Fallback: also click Export to exercise button and capture via test hook
if (!indexHtml) {
await page.getByRole('button', { name: /Export/i }).click()
await page.waitForFunction(() => {
const w = window as unknown as WindowWithExport
return Boolean(w.__lastExport || w.__lastBlob)
}, { timeout: 20000 })
const exp2 = await page.evaluate(() => (window as unknown as WindowWithExport).__lastExport ?? null)
indexHtml = exp2?.html ?? null
}
// if not present, fall back to reading from ZIP blob (slower)
if (!indexHtml) {
await page.waitForFunction(() => Boolean((window as unknown as WindowWithExport).__lastBlob), { timeout: 20000 })
const base64 = await page.evaluate(async () => {
const blob: Blob | undefined = (window as unknown as WindowWithExport).__lastBlob
if (!blob) return null
const ab = await blob.arrayBuffer()
const bytes = new Uint8Array(ab)
return btoa(String.fromCharCode(...bytes))
})
expect(base64, 'export blob should be captured').not.toBeNull()
const buf = Buffer.from(base64 as string, 'base64')
const zip = await JSZip.loadAsync(buf)
const index = zip.file('index.html')
expect(index, 'index.html exists in zip').toBeTruthy()
indexHtml = await index!.async('string')
}
// assert we have HTML
expect(indexHtml).not.toBeNull()
// assertions: language, title/description, og tags, GA4 id presence
expect(indexHtml!).toContain('<html lang="en">')
expect(indexHtml!).toContain('<meta name="description" content="E2E SEO Description" />')
expect(indexHtml!).toContain('<meta property="og:title" content="E2E SEO Title" />')
expect(indexHtml!).toContain('<meta property="og:description" content="E2E SEO Description" />')
expect(indexHtml!).toContain('<meta property="og:image" content="https://example.com/e2e-og.png" />')
expect(indexHtml!).toContain('G-E2E1234567')
expect(indexHtml!).toContain('9876543210')
// accessibility scaffolding in exported HTML
expect(indexHtml!).toContain('<a href="#main" class="skip-link">')
expect(indexHtml!).toContain('<main id="main" role="main">')
})