130 lines
4.4 KiB
JavaScript
130 lines
4.4 KiB
JavaScript
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();
|