const fs = require('fs') const path = require('path') const WebSocket = require('ws') const outputDir = path.resolve(__dirname, 'screenshots') fs.mkdirSync(outputDir, { recursive: true }) const loginUser = process.env.MES_MANUAL_USER const loginPassword = process.env.MES_MANUAL_PASSWORD const wait = ms => new Promise(resolve => setTimeout(resolve, ms)) async function connect() { const targets = await fetch('http://127.0.0.1:9222/json').then(res => res.json()) const target = targets.find(item => item.type === 'page') if (!target) throw new Error('No Chrome page target found') const ws = new WebSocket(target.webSocketDebuggerUrl) let id = 0 const pending = new Map() ws.on('message', raw => { const message = JSON.parse(raw.toString()) if (message.id && pending.has(message.id)) { const { resolve, reject } = pending.get(message.id) pending.delete(message.id) message.error ? reject(new Error(message.error.message)) : resolve(message.result) } }) await new Promise((resolve, reject) => { ws.once('open', resolve) ws.once('error', reject) }) const send = (method, params = {}) => new Promise((resolve, reject) => { const requestId = ++id pending.set(requestId, { resolve, reject }) ws.send(JSON.stringify({ id: requestId, method, params })) }) return { ws, send } } async function main() { const { ws, send } = await connect() await send('Page.enable') await send('Runtime.enable') await send('Emulation.setDeviceMetricsOverride', { width: 1920, height: 1080, deviceScaleFactor: 1, mobile: false }) const evaluate = expression => send('Runtime.evaluate', { expression, awaitPromise: true, returnByValue: true }).then(result => result.result.value) async function waitFor(expression, timeout = 30000) { const started = Date.now() while (Date.now() - started < timeout) { if (await evaluate(`Boolean(${expression})`)) return await wait(300) } throw new Error(`Timed out waiting for: ${expression}`) } async function screenshot(name) { await wait(700) const result = await send('Page.captureScreenshot', { format: 'png', captureBeyondViewport: false, fromSurface: true }) fs.writeFileSync(path.join(outputDir, `${name}.png`), Buffer.from(result.data, 'base64')) console.log(`Captured ${name}`) } async function navigate(hashPath) { await send('Page.navigate', { url: `https://127.0.0.1:1997/#${hashPath}` }) await waitFor("document.readyState === 'complete'") await waitFor("document.querySelector('.app-main') || document.querySelector('.el-card')", 40000) await wait(1800) } async function clickByText(text, selector = 'button') { return evaluate(`(() => { const nodes = [...document.querySelectorAll(${JSON.stringify(selector)})] const node = nodes.find(item => item.innerText && item.innerText.replace(/\\s+/g, '').includes(${JSON.stringify(text.replace(/\s+/g, ''))})) if (!node) return false node.click() return true })()`) } await send('Page.navigate', { url: 'https://127.0.0.1:1997/#/login' }) await waitFor("document.querySelector('input[name=username]') || document.querySelector('.sidebar-container')", 40000) if (await evaluate("Boolean(document.querySelector('input[name=username]'))")) { if (!loginUser || !loginPassword) { throw new Error('Set MES_MANUAL_USER and MES_MANUAL_PASSWORD before capturing pages') } await evaluate(`(() => { const setValue = (el, value) => { const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set setter.call(el, value) el.dispatchEvent(new Event('input', { bubbles: true })) el.dispatchEvent(new Event('change', { bubbles: true })) } setValue(document.querySelector('input[name=username]'), ${JSON.stringify(loginUser)}) setValue(document.querySelector('input[name=password]'), ${JSON.stringify(loginPassword)}) })()`) await clickByText('登录') } await waitFor("document.querySelector('.sidebar-container')", 40000) await wait(1500) const pages = [ ['01-personnel-list', '/SystemMaintenance/PersonnelManagement/index'], ['02-role-list', '/SystemMaintenance/SystemRrolemaintenance/index'], ['03-menu-list', '/SystemMaintenance/MenuManagement/index'], ['04-workstation-list', '/SystemMaintenance/WorkstationManagement/index'], ['05-operation-log', '/SystemMaintenance/OperationLog/index'] ] for (const [name, route] of pages) { await navigate(route) await screenshot(name) if (name === '01-personnel-list') { if (await clickByText('新增')) { await waitFor("document.querySelector('.el-dialog__wrapper:not([style*=\"display: none\"])')") await screenshot('01-personnel-add-dialog') await clickByText('取消', '.el-dialog__wrapper:not([style*=\"display: none\"]) button') } } if (name === '02-role-list') { if (await clickByText('新增')) { await waitFor("document.querySelector('.el-dialog__wrapper:not([style*=\"display: none\"])')") await screenshot('02-role-add-dialog') await clickByText('取消', '.el-dialog__wrapper:not([style*=\"display: none\"]) button') } } if (name === '03-menu-list') { await evaluate(`document.querySelector('#selectRoleName').click()`) await waitFor("[...document.querySelectorAll('.el-select-dropdown__item')].some(item => item.innerText.trim() === '管理员')") await evaluate(`(() => { const option = [...document.querySelectorAll('.el-select-dropdown__item')] .find(item => item.innerText.trim() === '管理员') option.click() })()`) await waitFor("document.querySelectorAll('.moduleTree .el-tree-node').length > 0") await screenshot('03-menu-permission-panel') } if (name === '04-workstation-list') { if (await clickByText('新增')) { await waitFor("document.querySelector('.el-dialog__wrapper:not([style*=\"display: none\"])')") await screenshot('04-workstation-add-dialog') await clickByText('取消', '.el-dialog__wrapper:not([style*=\"display: none\"]) button') } } } ws.close() } main().catch(error => { console.error(error) process.exitCode = 1 })