import { JSDOM } from 'jsdom'; import fs from 'node:fs'; const html = fs.readFileSync('index.html', 'utf8'); const dom = new JSDOM(html, { url: 'http://localhost:3000/#/workflows/sdB_cno', runScripts: 'outside-only', pretendToBeVisual: true, }); const { window } = dom; global.window = window; global.document = window.document; global.location = window.location; global.localStorage = window.localStorage; global.matchMedia = window.matchMedia?.bind(window) ?? (() => ({ matches: false, addEventListener() {} })); window.localStorage.setItem('dcts_admin_token', 'test-token'); const mkPoint = (teff, logc, status, method, attempts = 1, relc = 0.0005) => ({ name: `t${teff}_g5.0_he-2_c${logc}_n-4_o-4`, teff, logg: 5.0, loghe: -2, logc, logn: -4, logo: -4, cno_sum: logc - 8, wave: 0, status, success_method: method, attempt_count: attempts, last_max_relc: relc, last_task_type: method, seed_point_name: method === 'seed_step' ? 't20000_g5.0_he-2_c-4_n-4_o-4' : null, node_id: 'node-a1', last_completed_at: status === 'converged' || status === 'failed' ? '2026-07-30 10:00:00' : null, last_error: status === 'failed' ? 'nl stage diverged' : null, }); const POINTS = [ mkPoint(20000, -4, 'converged', 'cold_run'), mkPoint(40000, -4, 'converged', 'seed_step', 2), mkPoint(60000, -4, 'failed', null, 2, 9.5e5), mkPoint(20000, -3, 'running', null), ]; global.fetch = window.fetch = async (url) => { const u = String(url); if (u.includes('/stats')) { return new Response(JSON.stringify({ success: true, message: 'ok', data: { name: 'sdB_cno', status: 'running', total: 4, pending: 0, queued: 0, running: 1, converged: 2, failed: 1, cold_run_converged: 1, seed_step_converged: 1, imported_converged: 0, waves: [{ wave: 0, total: 4, converged: 2, failed: 1 }], avg_point_sec: 600, eta_sec: 600 }, }), { status: 200 }); } if (u.includes('/points')) { let pts = [...POINTS]; const q = new URL(u, 'http://localhost'); if (q.searchParams.get('status')) pts = pts.filter(p => p.status === q.searchParams.get('status')); if (q.searchParams.get('method')) pts = pts.filter(p => p.success_method === q.searchParams.get('method')); return new Response(JSON.stringify({ success: true, message: 'ok', data: { total: pts.length, points: pts } }), { status: 200 }); } return new Response('{}', { status: 404 }); }; const { mountWorkflowDetail } = await import('./src/views/workflowDetail.js'); const tick = () => new Promise((r) => setTimeout(r, 30)); mountWorkflowDetail('sdB_cno'); await tick(); await tick(); // Tab2:点表 document.querySelector('[data-wf-tab="points"]').click(); await tick(); await tick(); console.log('points rows:', document.querySelectorAll('#points-table-body tr[data-point-row]').length === 4 ? 'OK' : 'MISSING'); console.log('rescued marker:', document.querySelector('#points-table-body')?.textContent.includes('↻') ? 'OK' : 'MISSING'); // 状态过滤 → 仅 failed document.querySelector('[data-wf-filter="status"]').value = 'failed'; document.querySelector('[data-wf-filter="status"]').dispatchEvent(new window.Event('change', { bubbles: true })); await tick(); console.log('status filter:', document.querySelectorAll('#points-table-body tr[data-point-row]').length === 1 ? 'OK' : 'WRONG'); // Tab3:热力图 document.querySelector('[data-wf-tab="analysis"]').click(); await tick(); await tick(); // logc 非轴且多值 → 切片选择器应出现并默认固定 logc=-4(3 点成格,c-3 点被切掉) const sliceSel = document.querySelector('[data-heat-slice="logc"]'); console.log('slice selector:', sliceSel ? 'OK' : 'MISSING'); const cells = document.querySelectorAll('.heatmap-cell'); console.log('heatmap cells (sliced):', cells.length === 3 ? 'OK' : `GOT ${cells.length}`); console.log('cold cell:', document.querySelector('.heatmap-cell.hc-cold') ? 'OK' : 'MISSING'); console.log('seed cell:', document.querySelector('.heatmap-cell.hc-seed') ? 'OK' : 'MISSING'); console.log('failed cell:', document.querySelector('.heatmap-cell.hc-failed') ? 'OK' : 'MISSING'); console.log('conclusion:', document.getElementById('heat-conclusion')?.textContent.includes('冷启动成功') ? 'OK' : 'MISSING'); // 切片切到 logc=-3 → 只剩 running 点一格 sliceSel.value = '-3'; sliceSel.dispatchEvent(new window.Event('change', { bubbles: true })); await tick(); console.log('slice switch:', document.querySelectorAll('.heatmap-cell').length === 1 && document.querySelector('.heatmap-cell.hc-running') ? 'OK' : 'WRONG'); process.exit(0);