const fs = require('fs'); const path = require('path'); const { chromium } = require('playwright'); const ROOT = path.resolve(__dirname, '..'); const PROFILE = path.join(ROOT, 'diagnostics', 'erp-playwright-profile'); const RUN_ID = new Date().toISOString().replace(/[-:T.Z]/g, '').slice(0, 14); const RUN_DIR = path.join(ROOT, 'diagnostics', 'single-team-test', `run-${RUN_ID}`); const LOG_FILE = path.join(RUN_DIR, 'run.log'); const ORDER = { product: '遇见老挝', departureDate: '2026-07-12', pax: { adult: 2, childBed: 1, childNoBed: 1, infant: 0, leader: 0 }, rooms: { SGL: 0, TWN: 0, TRP: 0, DBL: 2, HNM: 0, TL: 0 }, prices: { adult: 500, childBed: 200, childNoBed: 100, infant: 0, leader: 0 }, op: '测试', sales: '琳琳', }; fs.mkdirSync(RUN_DIR, { recursive: true }); function log(message, data) { const line = `[${new Date().toISOString()}] ${message}${data === undefined ? '' : ` ${JSON.stringify(data)}`}`; fs.appendFileSync(LOG_FILE, `${line}\n`, 'utf8'); console.log(line); } async function saveShot(page, name) { const file = path.join(RUN_DIR, `${name}.png`); await page.screenshot({ path: file, fullPage: false }); log('screenshot', { file }); } async function getText(page) { return await page.locator('body').innerText({ timeout: 5000 }).catch(() => ''); } async function waitForLogin(page) { await page.goto('https://ltjt.yunzhi.run/System/Mainlt.asp', { waitUntil: 'domcontentloaded', timeout: 30000 }); for (;;) { const url = page.url().toLowerCase(); const body = await getText(page); if (!url.includes('login') && (body.includes('业务操作') || body.includes('独立团计划表') || body.includes('首页'))) { log('login-ok', { url: page.url(), title: await page.title() }); return; } log('waiting-login', { url: page.url(), title: await page.title() }); await saveShot(page, 'waiting-login'); await page.waitForTimeout(3000); } } async function countAndClick(page, selector, label) { const locator = page.locator(selector); const count = await locator.count(); log('locator-count', { label, selector, count }); if (count !== 1) throw new Error(`${label} locator count ${count}: ${selector}`); await locator.click({ timeout: 10000 }); } async function main() { log('run-start', { runDir: RUN_DIR, order: ORDER }); const context = await chromium.launchPersistentContext(PROFILE, { headless: false, viewport: { width: 1920, height: 1000 }, acceptDownloads: true, downloadsPath: path.join(RUN_DIR, 'downloads'), }); const page = context.pages()[0] || await context.newPage(); page.setDefaultTimeout(12000); try { await waitForLogin(page); await page.goto('https://ltjt.yunzhi.run/System/Business/orders.asp', { waitUntil: 'domcontentloaded', timeout: 30000, }); await page.waitForTimeout(1500); await saveShot(page, 'orders-list'); await countAndClick(page, 'input[type="button"][value="下单"]', 'single-order-button'); await page.waitForTimeout(1500); await saveShot(page, 'after-click-single-order'); const body = await getText(page); log('after-click-body-snippet', body.slice(0, 1000)); log('manual-checkpoint', { message: 'Single-order form opened. Automation paused here until field selectors are confirmed.', runDir: RUN_DIR, }); } catch (error) { log('error', { message: error.message, stack: error.stack }); await saveShot(page, 'error'); throw error; } finally { log('leaving-browser-open', { runDir: RUN_DIR }); await new Promise(() => {}); } } main().catch((error) => { console.error(error); process.exit(1); });