178 lines
7.3 KiB
JavaScript
178 lines
7.3 KiB
JavaScript
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', 'split-child-execute', `run-${RUN_ID}`);
|
|
|
|
const ORDER = {
|
|
parentGroup: 'LW-260704A-A',
|
|
date: '2026-07-04',
|
|
customerName: 'LW云南七彩金桥国旅(昆明景腾)',
|
|
customerId: '778',
|
|
pax: { adult: 5, childBed: 0, childNoBed: 0, infant: 0, leader: 0 },
|
|
prices: { adult: 500, childBed: 0, childNoBed: 0, infant: 0, leader: 0 },
|
|
op: '测试',
|
|
sales: '琳琳',
|
|
origin: '云南',
|
|
note: '测试散拼子单,自动化测试后可删除。',
|
|
};
|
|
|
|
fs.mkdirSync(RUN_DIR, { recursive: true });
|
|
|
|
function log(message, data) {
|
|
console.log(`[${new Date().toISOString()}] ${message}${data === undefined ? '' : ` ${JSON.stringify(data)}`}`);
|
|
}
|
|
|
|
async function setField(scope, selector, value) {
|
|
const locator = scope.locator(selector).first();
|
|
await locator.waitFor({ state: 'attached', timeout: 15000 });
|
|
await locator.fill(String(value));
|
|
await locator.evaluate((el) => {
|
|
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
});
|
|
}
|
|
|
|
async function setDomValue(frame, id, value) {
|
|
await frame.evaluate(([fieldId, fieldValue]) => {
|
|
const el = document.getElementById(fieldId) || document.querySelector(`[name="${fieldId}"]`);
|
|
if (!el) throw new Error(`field not found: ${fieldId}`);
|
|
el.value = String(fieldValue);
|
|
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
el.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
}, [id, value]);
|
|
}
|
|
|
|
async function queryParent(page) {
|
|
await page.goto('https://ltjt.yunzhi.run/System/Business/plan.asp', { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
await page.waitForTimeout(900);
|
|
await setField(page, '#S_chufariqi', ORDER.date);
|
|
await setField(page, '#S_chufarizhi', ORDER.date);
|
|
await setField(page, '#S_tuanxuhao', ORDER.parentGroup);
|
|
await page.locator('#SearchButton').click();
|
|
await page.waitForTimeout(2500);
|
|
}
|
|
|
|
async function getChildFrame(page) {
|
|
for (let i = 0; i < 60; i += 1) {
|
|
const frame = page.frames().find((f) => f.url().includes('/Business/plan_order.asp'));
|
|
if (frame) return frame;
|
|
await page.waitForTimeout(500);
|
|
}
|
|
throw new Error('plan_order frame not found');
|
|
}
|
|
|
|
async function readAllText(page) {
|
|
const texts = [];
|
|
for (const frame of page.frames()) {
|
|
const text = await frame.evaluate(() => document.body.innerText.replace(/\s+/g, ' ').trim()).catch(() => '');
|
|
if (text) texts.push(text);
|
|
}
|
|
return texts.join('\n');
|
|
}
|
|
|
|
function extractChildOrderNumber(text) {
|
|
const matches = text.match(/D\d{4,}/g) || [];
|
|
return matches.length ? matches[matches.length - 1] : null;
|
|
}
|
|
|
|
async function clickDialogOk(page) {
|
|
const selectors = ['button:has-text("确定")', 'input[value="确定"]', 'a:has-text("确定")', '.aui_state_highlight'];
|
|
for (const selector of selectors) {
|
|
const locator = page.locator(selector).first();
|
|
if (await locator.count().catch(() => 0)) {
|
|
await locator.click().catch(() => {});
|
|
await page.waitForTimeout(500);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
async function main() {
|
|
log('run-start', { RUN_DIR, ORDER });
|
|
const context = await chromium.launchPersistentContext(PROFILE, {
|
|
headless: false,
|
|
viewport: { width: 1920, height: 1000 },
|
|
acceptDownloads: true,
|
|
});
|
|
const page = context.pages()[0] || await context.newPage();
|
|
page.setDefaultTimeout(15000);
|
|
|
|
try {
|
|
await queryParent(page);
|
|
await page.screenshot({ path: path.join(RUN_DIR, '01-parent-before.png'), fullPage: false });
|
|
const beforeListText = await readAllText(page);
|
|
fs.writeFileSync(path.join(RUN_DIR, '01-parent-before.txt'), beforeListText, 'utf8');
|
|
await page.locator('a:has-text("拼单")').first().click();
|
|
const frame = await getChildFrame(page);
|
|
await frame.waitForSelector('#zutuanshe', { timeout: 30000 });
|
|
|
|
await setDomValue(frame, 'zutuanshe', ORDER.customerName);
|
|
await setDomValue(frame, 'zutuansheid', ORDER.customerId);
|
|
await setDomValue(frame, 'gendanren', ORDER.op);
|
|
await setDomValue(frame, 'xiaoshouren', ORDER.sales);
|
|
await setDomValue(frame, 'keyuandi', ORDER.origin);
|
|
await setDomValue(frame, 'xiadanbeizhu', ORDER.note);
|
|
await setDomValue(frame, 'darenshu', ORDER.pax.adult);
|
|
await setDomValue(frame, 'xiaorenshu', ORDER.pax.childBed);
|
|
await setDomValue(frame, 'ertrenshu', ORDER.pax.childNoBed);
|
|
await setDomValue(frame, 'yingrenshu', ORDER.pax.infant);
|
|
await setDomValue(frame, 'quanrenshu', ORDER.pax.leader);
|
|
|
|
const rows = [
|
|
{ row: 0, qty: ORDER.pax.adult, price: ORDER.prices.adult },
|
|
{ row: 1, qty: ORDER.pax.childBed, price: ORDER.prices.childBed },
|
|
{ row: 2, qty: ORDER.pax.childNoBed, price: ORDER.prices.childNoBed },
|
|
{ row: 3, qty: ORDER.pax.infant, price: ORDER.prices.infant },
|
|
];
|
|
for (const item of rows) {
|
|
await setDomValue(frame, `ys_danwei${item.row}`, ORDER.customerName).catch(() => {});
|
|
await setDomValue(frame, `ys_danweiid${item.row}`, ORDER.customerId).catch(() => {});
|
|
await setDomValue(frame, `ys_shuliang${item.row}`, item.qty).catch(() => {});
|
|
await setDomValue(frame, `ys_danjia${item.row}`, item.price).catch(() => {});
|
|
await setDomValue(frame, `ys_jine${item.row}`, item.qty * item.price).catch(() => {});
|
|
}
|
|
|
|
await page.waitForTimeout(1000);
|
|
await page.screenshot({ path: path.join(RUN_DIR, '02-before-save.png'), fullPage: false });
|
|
const beforeSave = await readAllText(page);
|
|
fs.writeFileSync(path.join(RUN_DIR, '02-before-save.txt'), beforeSave, 'utf8');
|
|
|
|
await frame.locator('#SubmitButton, [name="SubmitButton"]').first().click();
|
|
await page.waitForTimeout(4000);
|
|
await page.screenshot({ path: path.join(RUN_DIR, '03-after-save.png'), fullPage: false });
|
|
const afterSave = await readAllText(page);
|
|
fs.writeFileSync(path.join(RUN_DIR, '03-after-save.txt'), afterSave, 'utf8');
|
|
const childOrder = extractChildOrderNumber(afterSave);
|
|
if (!childOrder) throw new Error(`could not capture child order number: ${afterSave.slice(0, 800)}`);
|
|
await clickDialogOk(page);
|
|
|
|
await queryParent(page);
|
|
await page.screenshot({ path: path.join(RUN_DIR, '04-parent-after.png'), fullPage: false });
|
|
const afterListText = await readAllText(page);
|
|
fs.writeFileSync(path.join(RUN_DIR, '04-parent-after.txt'), afterListText, 'utf8');
|
|
const receivedOk = afterListText.includes(ORDER.parentGroup) && afterListText.includes('成人5');
|
|
|
|
const result = { runDir: RUN_DIR, parentGroup: ORDER.parentGroup, date: ORDER.date, childOrder, receivedOk };
|
|
fs.writeFileSync(path.join(RUN_DIR, 'result.json'), JSON.stringify(result, null, 2), 'utf8');
|
|
log('run-complete', result);
|
|
} catch (error) {
|
|
log('error', { message: error.message, stack: error.stack });
|
|
await page.screenshot({ path: path.join(RUN_DIR, 'error.png'), fullPage: false }).catch(() => {});
|
|
throw error;
|
|
} finally {
|
|
await context.close();
|
|
}
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error(error);
|
|
process.exit(1);
|
|
});
|