Files
kra-new/scripts/sw_alert_records_smoke.cjs
T

157 lines
10 KiB
JavaScript

// 独立 Chrome 核对预警页面;预警写入和附件上传使用受控接口,不修改真实业务数据。
const fs = require('node:fs/promises')
const path = require('node:path')
const assert = require('node:assert/strict')
const { chromium } = require(process.env.SW_PLAYWRIGHT_MODULE || 'playwright')
;(async () => {
const token = (await fs.readFile(path.join(process.env.TEMP, 'sw-kra-http-test-token'), 'utf8')).trim()
const browser = await chromium.launch({ channel: 'chrome', headless: true })
const results = []
const png = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Y9Zl1sAAAAASUVORK5CYII=', 'base64')
try {
for (const level of [0, 1, 2, 3]) {
const context = await browser.newContext({ viewport: { width: 1500, height: 1080 } })
await context.addInitScript(value => localStorage.setItem('token', value), token)
const page = await context.newPage()
const pageErrors = []
const writes = []
const queries = []
let uploads = 0
let unavailable = false
const rows = [1, 2, 3, 4, 5].map(status => ({
id: String(100 + status), platformCode: 'lx', platformName: '龙翔', ruleName: `测试规则-${status}`,
ruleType: '测试分类', targetPromoteName: `渠道-${status}`, businessName: '测试一级商务',
alertContent: '测试预警内容', status, createdAt: '2026-11-03 10:20:30', updatedAt: '2026-11-03 10:20:30'
}))
const logs = new Map(rows.map(row => [row.id, []]))
const log = (id, action, note, attachments = []) => logs.get(id).unshift({
id: String(writes.length), operatorName: '页面测试账号', logType: action ? 1 : 2,
logTypeLabel: action ? '状态变更操作' : '添加独立备注', actionDetail: action || '',
noteContent: note || '', attachments, createdAt: '2026-11-03 10:30:00'
})
page.on('pageerror', error => pageErrors.push(error.message))
await page.route('http://127.0.0.1:8080/api/**', async route => {
const request = route.request()
const url = new URL(request.url())
const api = url.pathname.slice(4)
const ok = data => route.fulfill({ json: { code: 0, msg: '成功', data } })
if (api === '/sw/alertRule/ruleTypes') return ok(['测试分类'])
if (api === '/sw/alertRecord/list') {
queries.push(url.search)
let filtered = rows.filter(row => !url.searchParams.get('status') || row.status === Number(url.searchParams.get('status')))
if (url.searchParams.get('ruleName')) filtered = filtered.filter(row => row.ruleName.includes(url.searchParams.get('ruleName')))
return ok({ pageData: { rows: unavailable ? [] : filtered, total: unavailable ? 0 : filtered.length }, statusCount: unavailable ? {} : { 1: 1, 2: 1, 3: 1, 4: 1, 5: 1 }, available: !unavailable })
}
if (api.startsWith('/sw/alertRecord/detail/')) {
const id = api.split('/').at(-1)
return ok({ alertRecord: rows.find(row => row.id === id), operationLogs: logs.get(id), canAudit: level === 1 })
}
if (api === '/fileUploadAndDownload/upload') {
uploads++
return ok({ file: { ID: 800 + uploads, url: `/files/verify-${uploads}.png`, name: 'test.png' } })
}
if (api.startsWith('/sw/alertRecord/') && request.method() === 'POST') {
const payload = request.postDataJSON()
const id = String(payload.alertId)
writes.push({ path: api, body: payload })
const action = api.split('/').at(-1)
if (action === 'addNote') log(id, '', payload.noteContent, ['/files/verify-1.png'])
else {
rows.find(row => row.id === id).status = { startProcess: 2, submitAudit: 3, reject: 4, reProcess: 2, approve: 5 }[action]
log(id, { startProcess: 'start', submitAudit: 'submit', reject: 'reject', reProcess: 'reprocess', approve: 'pass' }[action], payload.note)
}
return ok({})
}
const response = await route.fetch({ url: 'http://127.0.0.1:18000' + api + url.search, headers: { ...request.headers(), 'x-token': token } })
if (api === '/user/getUserInfo') {
const body = await response.json()
Object.assign(body.data.userInfo, { ID: 70000 + level, level, nickName: '页面测试账号', phone: '', email: '', authorityId: level === 0 ? 888 : 900 + level })
return route.fulfill({ response, json: body })
}
return route.fulfill({ response })
})
await page.route('**/files/verify-*.png', route => route.fulfill({ contentType: 'image/png', body: png }))
await page.goto('http://127.0.0.1:8080/#/layout/sw-AlertCenter/AlertList')
await page.getByText('测试规则-1', { exact: true }).waitFor()
const result = { level, stateButtons: [], passed: true }
const tableRow = status => page.locator('.el-table__body-wrapper tr').filter({ hasText: `测试规则-${status}` })
const dialog = page.getByRole('dialog', { name: '预警处理 / 详情', exact: true })
const close = async () => { await dialog.getByRole('button', { name: '关闭', exact: true }).click(); await dialog.waitFor({ state: 'hidden' }) }
for (const status of [1, 2, 3, 4, 5]) {
const expected = { 1: '处理', 2: '继续处理', 3: level === 1 ? '审核' : '查看', 4: '重新处理', 5: '查看' }[status]
await tableRow(status).getByRole('button', { name: expected, exact: true }).click()
await dialog.getByText('测试预警内容', { exact: true }).waitFor()
assert.equal(await dialog.getByRole('button', { name: '审核通过', exact: true }).count(), status === 3 && level === 1 ? 1 : 0)
assert.equal(await dialog.getByPlaceholder('填写处理备注...').count(), status === 5 || (status === 3 && level !== 1) ? 0 : 1)
result.stateButtons.push({ status, label: expected })
await close()
}
if (level === 1) {
await tableRow(1).getByRole('button', { name: '处理', exact: true }).click()
await dialog.getByPlaceholder('填写处理备注...').waitFor()
await dialog.locator('input[type="file"]').setInputFiles({ name: 'test.png', mimeType: 'image/png', buffer: png })
await dialog.locator('textarea').evaluate((element, bytes) => {
const transfer = new DataTransfer()
transfer.items.add(new File([new Uint8Array(bytes)], 'paste.png', { type: 'image/png' }))
element.dispatchEvent(new ClipboardEvent('paste', { bubbles: true, clipboardData: transfer }))
}, [...png])
await dialog.locator('.alert-upload-zone').evaluate((element, bytes) => {
const transfer = new DataTransfer()
transfer.items.add(new File([new Uint8Array(bytes)], 'drop.png', { type: 'image/png' }))
element.dispatchEvent(new DragEvent('drop', { bubbles: true, dataTransfer: transfer }))
}, [...png])
assert.equal(await dialog.locator('.pending-image').count(), 3)
await dialog.locator('.pending-image').first().getByRole('button', { name: '移除', exact: true }).click()
await dialog.locator('.pending-image').first().getByRole('button', { name: '移除', exact: true }).click()
assert.equal(await dialog.locator('.pending-image').count(), 1)
assert.equal(uploads, 0)
await dialog.getByRole('button', { name: '添加备注', exact: true }).click()
await page.getByText('请填写处理备注后再提交附件', { exact: true }).waitFor()
assert.equal(uploads, 0)
await dialog.getByPlaceholder('填写处理备注...').fill('测试备注和图片')
await dialog.getByRole('button', { name: '开始处理', exact: true }).click()
await page.getByText('请先点击「添加备注」上传图片,或移除暂存图片后再操作', { exact: true }).waitFor()
assert.equal(writes.length, 0)
await dialog.getByRole('button', { name: '添加备注', exact: true }).click()
await dialog.getByText('备注:测试备注和图片', { exact: true }).waitFor()
assert.equal(uploads, 1)
assert.deepEqual(writes[0].body.attachmentIds, ['801'])
for (const [button, next, confirmation] of [
['开始处理', '提交审核', true], ['提交审核', '审核通过', true], ['审核驳回', '重新处理', true],
['重新处理', '提交审核', true], ['提交审核', '审核通过', true], ['审核通过', null, false]
]) {
await dialog.getByRole('button', { name: button, exact: true }).click()
if (confirmation) await page.getByRole('dialog', { name: '操作确认', exact: true }).getByRole('button', { name: '确定', exact: true }).click()
if (next) await dialog.getByRole('button', { name: next, exact: true }).waitFor()
else await dialog.getByPlaceholder('填写处理备注...').waitFor({ state: 'hidden' })
}
assert.equal(writes.length, 7)
await page.screenshot({ path: path.join(process.env.TEMP, 'sw-alert-record-workflow.png'), animations: 'disabled' })
await close()
result.workflowAndAttachments = true
result.pasteDropAndRemove = true
}
if (level === 2) {
await page.getByRole('button', { name: '未处理 1', exact: true }).click()
await tableRow(2).waitFor({ state: 'hidden' })
assert.ok(queries.at(-1).includes('status=1'))
await page.getByRole('button', { name: '未处理 1', exact: true }).click()
await tableRow(2).waitFor()
assert.ok(!queries.at(-1).includes('status=1'))
unavailable = true
await page.locator('.kra-search-box').getByRole('button', { name: '搜索', exact: true }).click()
await page.getByText('渠道数据尚未接入,暂时无法核对预警可见范围。', { exact: true }).waitFor()
assert.equal(await page.getByText('未接入', { exact: true }).count(), 5)
result.statusCardsAndUnavailable = true
}
assert.deepEqual(pageErrors, [])
result.pageErrors = pageErrors
results.push(result)
console.log(JSON.stringify(result))
await context.close()
}
await fs.writeFile('docs/sw6-to-kra/alert-records-page-2026-11-03.json', JSON.stringify({ method: '真实 Chrome 渲染;受控身份、预警数据、写接口和图片上传;未修改真实业务数据', results }, null, 2) + '\n')
} finally { await browser.close() }
})().catch(error => { console.error(error); process.exitCode = 1 })