import assert from 'node:assert/strict' import { chromium } from 'playwright' const origin = process.env.FUEL_CARD_TEST_ORIGIN || 'http://localhost:3006' const screenshotPath = process.env.FUEL_CARD_SCREENSHOT || '/tmp/shanxia-vehicle-fuel-card.png' const browser = await chromium.launch({ headless: true, channel: 'chrome' }) const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } }) const page = await context.newPage() page.setDefaultTimeout(20000) const errors = [] page.on('pageerror', (error) => errors.push(error.message)) page.on('console', (message) => { if (message.type() === 'error') console.error('Browser console:', message.text()) }) page.on('requestfailed', (request) => console.error('Request failed:', request.url(), request.failure()) ) const longCard = '000100001234567890123456789012345' const base = '/transport/vehicle-fuel-card' const calls = [] let rows = [ { id: 'fixture-1', licensePlate: '鄂A12345', cardNo: longCard, remark: '初始备注', createTime: '2026-10-25 10:00:00', updateTime: null } ] await context.addInitScript(() => { localStorage.setItem( 'sys-v3.0.2-user', JSON.stringify({ isLogin: true, accessToken: 'fuel-card-fixture', info: { id: 'fixture-user', userId: 'fixture-user', userName: '回归测试用户', role: 'admin', roles: ['admin'] } }) ) localStorage.setItem('sys-version', '3.0.2') }) await context.route('**/api/**', async (route) => { const request = route.request() const url = new URL(request.url()) if (!url.pathname.startsWith('/api/')) return route.continue() const path = url.pathname.replace(/^\/api/, '') const method = request.method() let body = {} try { body = request.postDataJSON() || {} } catch { /* GET 无请求体 */ } let data = {} if (path.startsWith(base)) { calls.push({ path, method, body, params: Object.fromEntries(url.searchParams) }) if (path === base && method === 'GET') { const q = url.searchParams.get('q') || '' const filtered = rows.filter( (row) => row.licensePlate.includes(q) || row.cardNo.includes(q) || (row.remark || '').includes(q) ) const current = Number(url.searchParams.get('page') || 1) const size = Number(url.searchParams.get('pageSize') || 20) data = { list: filtered.slice((current - 1) * size, current * size), total: filtered.length } } else if (path === base && method === 'POST') { assert.deepEqual(Object.keys(body).sort(), ['cardNo', 'licensePlate', 'remark']) data = { ...body, id: 'fixture-2', createTime: '2026-10-25 11:00:00', updateTime: null } rows.push(data) } else if (path === `${base}/batch-delete`) { const previous = rows.length rows = rows.filter((row) => !body.ids.includes(row.id)) data = { deleted: previous - rows.length } } else if (method === 'PUT') { assert.deepEqual(Object.keys(body).sort(), ['cardNo', 'licensePlate', 'remark']) const id = path.split('/').pop() const index = rows.findIndex((row) => row.id === id) data = rows[index] = { ...rows[index], ...body, updateTime: '2026-10-25 12:00:00' } } else if (method === 'GET') { data = rows.find((row) => row.id === path.split('/').pop()) } else { throw new Error(`Unexpected fixture request: ${method} ${path}`) } return route.fulfill({ json: data }) } if (path === '/web/auth/current-user') { data = { id: 'fixture-user', userId: 'fixture-user', userName: '回归测试用户', role: 'admin', roles: ['admin'] } } else if (path === '/web/auth/menu-codes') { data = { all: false, menuCodes: ['pl:vehicle-fuel-card:list', 'pl:vehicle-recharge-workflow:list'] } } else if (path.includes('favorites')) { data = [] } else if (path.includes('dict-data')) { data = {} } else if (path.includes('upgrade-logs')) { data = [] } await route.fulfill({ json: { code: path.startsWith('/transport/') ? 0 : 200, msg: 'ok', data } }) }) try { await page.goto(`${origin}/#/vehicle-mgmt/etc-recharge/vehicle-fuel-card`) await page.getByText('车油卡信息', { exact: true }).first().click() const panel = page .locator('.panel') .filter({ has: page.locator('h2.title-bar-heading', { hasText: '车油卡信息' }) }) await panel.locator('.el-link').filter({ hasText: '鄂A12345' }).waitFor() assert.equal(await panel.locator('thead').getByText('操作', { exact: true }).count(), 0) await panel.locator('.el-link').filter({ hasText: '鄂A12345' }).click() const view = page.getByRole('dialog').filter({ has: page.locator('.view-hero') }) await view.getByText(longCard, { exact: true }).waitFor() await view.getByRole('button', { name: '编辑', exact: true }).click() const edit = page.getByRole('dialog').filter({ hasText: '编辑车油卡信息' }) await edit.getByPlaceholder('请输入油卡卡号').waitFor() assert.equal(await edit.getByPlaceholder('请输入油卡卡号').inputValue(), longCard) assert.equal(await edit.getByPlaceholder('请输入备注(选填)').inputValue(), '初始备注') await edit.getByPlaceholder('请输入备注(选填)').fill('') await edit.getByRole('button', { name: '确定', exact: true }).click() await edit.waitFor({ state: 'hidden' }) assert.equal(calls.find((call) => call.method === 'PUT').body.cardNo, longCard) assert.equal(calls.find((call) => call.method === 'PUT').body.remark, null) await panel.getByRole('button', { name: '新增', exact: true }).click() const add = page.getByRole('dialog').filter({ hasText: '新增车油卡信息' }) await add.getByPlaceholder('请输入车牌号').fill('鄂B23456') await add.getByPlaceholder('请输入油卡卡号').fill('00012345678901234567890') await add.getByPlaceholder('请输入备注(选填)').fill('新增备注') await add.getByRole('button', { name: '确定', exact: true }).click() await add.waitFor({ state: 'hidden' }) await panel.locator('.el-link').filter({ hasText: '鄂B23456' }).waitFor() await page.locator('.el-message').first().waitFor({ state: 'hidden' }) await page.screenshot({ path: screenshotPath, fullPage: true }) await panel.getByPlaceholder('车牌号/油卡卡号/备注').fill('鄂B') await panel.getByPlaceholder('车牌号/油卡卡号/备注').press('Enter') await panel.locator('.el-link').filter({ hasText: '鄂A12345' }).waitFor({ state: 'hidden' }) assert.equal( calls.filter((call) => call.path === base && call.method === 'GET').at(-1).params.q, '鄂B' ) await panel.getByPlaceholder('车牌号/油卡卡号/备注').hover() await panel.locator('.quick-search-input .el-input__clear').click() await panel.locator('.el-link').filter({ hasText: '鄂A12345' }).waitFor() await panel.locator('thead .el-checkbox').first().click() await panel.getByRole('button', { name: '删除', exact: true }).click() await page.locator('.el-message-box').getByRole('button', { name: '删除', exact: true }).click() await panel.locator('.el-link').filter({ hasText: '鄂A12345' }).waitFor({ state: 'hidden' }) assert.equal(rows.length, 0) assert.deepEqual(calls.find((call) => call.path.endsWith('batch-delete')).body.ids.sort(), [ 'fixture-1', 'fixture-2' ]) assert.deepEqual(errors, []) console.log( `PASS: 真实路由、列表、查看、编辑、长卡号、清空备注、新增、搜索、批量删除;截图 ${screenshotPath}` ) } catch (error) { console.error('Browser state:', page.url(), await page.locator('body').innerText(), errors, calls) await page.screenshot({ path: '/tmp/shanxia-fuel-card-failure.png', fullPage: true }) throw error } finally { await browser.close() }