// 无头 E2E: 非标档位在线生成 (二期) — 按钮升级/立即命中下载/排队轮询下载 // 前置: 站点 config.js 的 api 已指向本地 GenServer (匿名模式); GenServer 已启动 // 用法: node e2e-gen.js const fs = require('fs'); const path = require('path'); let chromium; try { chromium = require('playwright-core').chromium; } catch (e) { chromium = require('C:/Users/ruigu/AppData/Local/Temp/nds3dtest/node_modules/playwright-core').chromium; } const edge = 'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe'; (async () => { const url = process.argv[2] || 'http://localhost:8080/'; const jobsDir = process.argv[3]; const outputDir = process.argv[4]; const browser = await chromium.launch({ executablePath: edge, headless: true }); const context = await browser.newContext({ acceptDownloads: true, viewport: { width: 1600, height: 900 } }); const page = await context.newPage(); const errs = []; page.on('pageerror', e => errs.push(e.message)); await page.goto(url, { waitUntil: 'domcontentloaded' }); await page.waitForTimeout(2500); let pass = 0, fail = 0; const out = []; const check = (name, ok) => { out.push((ok ? 'PASS ' : 'FAIL ') + name); if (ok) pass++; else fail++; }; // KC 系列 const names = await page.$$eval('.tree .series', els => els.map(e => e.textContent.trim())); await (await page.$$('.tree .series'))[names.findIndex(n => n.indexOf('KC') === 0)].click(); await page.waitForTimeout(500); const setSel = async (code, val) => { await page.selectOption('#sel-' + code, val); await page.waitForTimeout(250); }; const setStroke = async (v) => { await page.fill('#sel-stroke', String(v)); await page.dispatchEvent('#sel-stroke', 'change'); await page.waitForTimeout(400); }; const btn = async () => page.$eval('#btnDownload', el => ({ text: el.textContent, disabled: el.disabled, hidden: el.classList.contains('hidden') })); // 1) 非标 87 (输出目录已预置) → 按钮=在线生成数模; 点击 → immediate → 触发下载 await setSel('type', '00'); await setSel('bore', '32'); await setStroke(87); await setSel('magnet', '__EMPTY__'); await setSel('mount', '__EMPTY__'); await page.waitForTimeout(400); var b = await btn(); check('非标按钮 = 在线生成数模 且可用 (实际: ' + b.text + (b.disabled ? '/disabled' : '/enabled') + ')', b.text.indexOf('在线生成') >= 0 && !b.disabled); var dlEvent = null; page.waitForEvent('download', { timeout: 10000 }).then(d => { dlEvent = d; }).catch(() => { }); await page.click('#btnDownload'); await page.waitForTimeout(2500); check('立即命中 → 触发全格式 zip 下载', dlEvent !== null); var st = await page.$eval('#status', el => el.textContent); check('状态含 就绪/下载 (实际: ' + st.trim() + ')', st.indexOf('就绪') >= 0 || st.indexOf('下载') >= 0); // 2) 非标 88 (无输出) → 点击 → 排队/生成中 → 外部模拟 worker 完成 → 触发下载 await setStroke(88); await page.waitForTimeout(400); b = await btn(); check('非标 88 按钮可用', b.text.indexOf('在线生成') >= 0 && !b.disabled); dlEvent = null; page.waitForEvent('download', { timeout: 30000 }).then(d => { dlEvent = d; }).catch(() => { }); await page.click('#btnDownload'); await page.waitForTimeout(4000); b = await btn(); check('点击后进入生成/排队态 (实际: ' + b.text + ')', b.text.indexOf('生成') >= 0 || b.text.indexOf('队列') >= 0); // 模拟 worker: 认领 .json → .running → 造输出 → 写 .done const jobs = fs.readdirSync(jobsDir).filter(f => f.startsWith('job_') && f.endsWith('.json')); if (jobs.length) { const jf = path.join(jobsDir, jobs[0]); const rf = jf.replace(/\.json$/, '.running'); fs.renameSync(jf, rf); const o = path.join(outputDir, 'KC', 'KC0032-88'); fs.mkdirSync(o, { recursive: true }); ['KC0032-88.stp', 'KC0032-88.x_t', 'KC0032-88.igs', 'KC0032-88.stl'].forEach(f => fs.writeFileSync(path.join(o, f), 'FAKE ' + f)); fs.writeFileSync(jf.replace(/\.json$/, '.done'), JSON.stringify({ ok: true, error: '', items: [{ series: 'KC', code: 'KC0032-88', ok: true, hit: false, files: ['KC0032-88.stp', 'KC0032-88.x_t', 'KC0032-88.igs', 'KC0032-88.stl'] }] })); fs.unlinkSync(rf); check('模拟 worker 完成写入', true); } else { check('任务已入队 (jobs 目录可见)', jobs.length > 0); } await page.waitForTimeout(12000); // 轮询间隔 3s, 等状态刷新并触发下载 check('排队轮询 → 完成后触发下载', dlEvent !== null); // 3) 标准档位回归: stroke=10 → 按钮回 下载 STEP 可用 await setStroke(10); await page.waitForTimeout(400); b = await btn(); check('标准档位按钮回 下载 STEP (实际: ' + b.text + ')', b.text.indexOf('下载 STEP') >= 0 && !b.disabled && !b.hidden); console.log(out.join('\n')); console.log('结果: ' + pass + ' 通过, ' + fail + ' 失败 | 页面错误: ' + (errs.length ? errs.join(' | ') : '无')); await browser.close(); process.exit(fail ? 1 : 0); })().catch(e => { console.error('FAIL:', e.message); process.exit(1); });