- All 8 home page sections: Hero, Locations slider, WhyParents accordion, Birthday pricing cards, Video, Gallery, Reviews slider, News - UI components: NavLink, BtnPrimary, BtnGradient, BtnDetails, AccordionItem - Layout: sticky Header (NavLink + BtnPrimary), Footer with logo - Figma Code Connect: 5 components published (.figma.tsx + figma.config.json) - Public assets: all Figma images and SVGs exported - Pages: /kvytky, /lokatsii, /blog, /dni-narodzhennia, /grupovi-vidviduvannia - Tests: Vitest unit/api suites, Playwright e2e screenshots - Payload CMS: blocks, collections, seed data updates - Hero negative-margin to extend behind sticky header - Custom Tailwind breakpoints: lg=1440px, xl=1920px - Fix ESLint config: drop FlatCompat, use eslint-config-next flat export - Add tsconfig.tsbuildinfo, test-results/, agentdb.rvf* to .gitignore Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
71 lines
2.6 KiB
JavaScript
71 lines
2.6 KiB
JavaScript
import { chromium } from '@playwright/test'
|
|
import { existsSync, mkdirSync } from 'fs'
|
|
|
|
const PAGES = [
|
|
{ name: 'home', url: 'http://localhost:3000/' },
|
|
{ name: 'kvytky', url: 'http://localhost:3000/kvytky' },
|
|
{ name: 'checkout', url: 'http://localhost:3000/kvytky/checkout?tariff=123' },
|
|
{ name: 'lokatsii', url: 'http://localhost:3000/lokatsii' },
|
|
{ name: 'blog', url: 'http://localhost:3000/blog' },
|
|
{ name: 'birthday', url: 'http://localhost:3000/dni-narodzhennia' },
|
|
]
|
|
|
|
const SHOTS_DIR = '/tmp/shumiland-shots'
|
|
if (!existsSync(SHOTS_DIR)) mkdirSync(SHOTS_DIR, { recursive: true })
|
|
|
|
const browser = await chromium.launch({ headless: true })
|
|
const context = await browser.newContext({ viewport: { width: 1440, height: 900 } })
|
|
|
|
for (const { name, url } of PAGES) {
|
|
const page = await context.newPage()
|
|
const errors = []
|
|
const failedReqs = []
|
|
|
|
page.on('console', (msg) => {
|
|
if (msg.type() === 'error') errors.push(msg.text())
|
|
})
|
|
page.on('pageerror', (err) => errors.push(err.message))
|
|
page.on('requestfailed', (req) => {
|
|
failedReqs.push(`${req.url()}`)
|
|
})
|
|
|
|
console.log(`\n📸 ${url}`)
|
|
try {
|
|
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 15000 })
|
|
await page.waitForTimeout(2000)
|
|
|
|
const bgColor = await page.evaluate(() =>
|
|
window.getComputedStyle(document.body).backgroundColor,
|
|
)
|
|
const hasHeader = (await page.locator('header').count()) > 0
|
|
const hasFooter = (await page.locator('footer').count()) > 0
|
|
const imgStats = await page.evaluate(() => {
|
|
const imgs = Array.from(document.images)
|
|
return {
|
|
total: imgs.length,
|
|
failed: imgs
|
|
.filter((i) => !i.complete || i.naturalWidth === 0)
|
|
.map((i) => i.src)
|
|
.slice(0, 5),
|
|
}
|
|
})
|
|
|
|
console.log(` bg: ${bgColor} | header: ${hasHeader ? '✓' : '✗'} | footer: ${hasFooter ? '✓' : '✗'}`)
|
|
console.log(` images: ${imgStats.total} total, ${imgStats.failed.length} failed`)
|
|
if (imgStats.failed.length > 0) imgStats.failed.forEach((s) => console.log(` ✗ ${s.slice(0, 80)}`))
|
|
if (errors.length > 0) errors.slice(0, 5).forEach((e) => console.log(` [ERR] ${e.slice(0, 120)}`))
|
|
if (failedReqs.length > 0) {
|
|
const cssOrJs = failedReqs.filter((r) => r.includes('.css') || r.includes('.js'))
|
|
if (cssOrJs.length > 0) cssOrJs.forEach((r) => console.log(` [FAIL REQ] ${r.slice(0, 100)}`))
|
|
}
|
|
|
|
await page.screenshot({ path: `${SHOTS_DIR}/${name}.png`, fullPage: false })
|
|
console.log(` screenshot → ${SHOTS_DIR}/${name}.png`)
|
|
} catch (e) {
|
|
console.log(` ERROR: ${e.message}`)
|
|
}
|
|
await page.close()
|
|
}
|
|
|
|
await browser.close()
|
|
console.log('\nDone.')
|