import fs from "node:fs/promises"; import path from "node:path"; import { chromium, devices } from "playwright"; const targetUrl = "https://m.ctrip.com/tangram/OTQ1Mg==?ctm_ref=vactang_page_9452&isHideNavBar=YES&ctm_ref=vactang_page_159930"; const outDir = path.join(process.cwd(), "docs", "research", "secondary"); const shotDir = path.join(process.cwd(), "docs", "design-references", "secondary"); await fs.mkdir(outDir, { recursive: true }); await fs.mkdir(shotDir, { recursive: true }); const browser = await chromium.launch({ headless: true }); async function newPage() { const context = await browser.newContext({ ...devices["iPhone 13 Pro"], viewport: { width: 390, height: 844 }, locale: "zh-CN", timezoneId: "Asia/Shanghai", extraHTTPHeaders: { "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8" }, }); const page = await context.newPage(); const events = []; page.on("console", (msg) => { if (msg.type() === "error") events.push({ type: "console-error", text: msg.text().slice(0, 300) }); }); page.on("pageerror", (err) => events.push({ type: "page-error", text: err.message.slice(0, 300) })); await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 60000 }); await page.waitForTimeout(6000); return { context, page, events }; } async function snap(name, page, events, extra = {}) { await page.screenshot({ path: path.join(shotDir, `${name}.png`), fullPage: false }); const data = await page.evaluate(() => ({ title: document.title, url: location.href, scrollY, bodyText: document.body.innerText.slice(0, 2500), buttons: [...document.querySelectorAll('button, a, [role="button"], .nav_bottom_item')].slice(0, 100).map((el) => ({ tag: el.tagName.toLowerCase(), text: el.textContent?.trim().replace(/\s+/g, " ").slice(0, 160) ?? "", className: String(el.className ?? "").slice(0, 160), href: el instanceof HTMLAnchorElement ? el.href : "", rect: (() => { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; })(), })), })); await fs.writeFile(path.join(outDir, `${name}.json`), JSON.stringify({ ...data, events, extra }, null, 2)); return data; } async function probe(name, action) { const { context, page, events } = await newPage(); let error = null; try { await action(page); await page.waitForTimeout(5000); } catch (err) { error = err instanceof Error ? err.message : String(err); } const data = await snap(name, page, events, { error }); await context.close(); return { name, error, title: data.title, url: data.url, text: data.bodyText.slice(0, 200).replace(/\n/g, "|"), }; } const results = []; results.push( await probe("search-click", async (page) => { await page.locator(".icon-wrap").filter({ hasText: "搜索" }).first().click({ timeout: 10000 }); }), ); results.push( await probe("support-click", async (page) => { await page.locator(".icon-wrap").filter({ hasText: "客服" }).first().click({ timeout: 10000 }); }), ); results.push( await probe("product-first-click", async (page) => { for (let y = 0; y < 2400; y += 600) { await page.evaluate((scrollY) => scrollTo(0, scrollY), y); await page.waitForTimeout(500); } const item = page.locator(".diy_product_flex_item.expose_dom").filter({ hasText: "¥" }).first(); await item.scrollIntoViewIfNeeded(); await page.waitForTimeout(500); await item.click({ timeout: 10000 }); }), ); results.push( await probe("bottom-destination-click", async (page) => { await page.locator(".nav_bottom_item").filter({ hasText: "目的地" }).first().click({ timeout: 10000 }); }), ); results.push( await probe("bottom-manager-click", async (page) => { await page.locator(".nav_bottom_item").filter({ hasText: "客户经理" }).first().click({ timeout: 10000 }); }), ); results.push( await probe("bottom-message-click", async (page) => { await page.locator(".nav_bottom_item").filter({ hasText: "消息" }).first().click({ timeout: 10000 }); }), ); results.push( await probe("bottom-order-click", async (page) => { await page.locator(".nav_bottom_item").filter({ hasText: "订单" }).first().click({ timeout: 10000 }); }), ); await fs.writeFile(path.join(outDir, "probe-summary.json"), JSON.stringify(results, null, 2)); console.log(JSON.stringify(results, null, 2)); await browser.close();