import assert from 'node:assert/strict'
|
import { readFile, mkdir } from 'node:fs/promises'
|
import { extname, resolve } from 'node:path'
|
import { chromium, expect } from '@playwright/test'
|
|
// 构建产物浏览器验收:请求接缝用夹具覆盖成功和失败,不启动开发或业务服务。
|
const dist = resolve(import.meta.dirname, '../dist')
|
const artifacts = resolve(import.meta.dirname, 'e2e/.artifacts/refinement')
|
await mkdir(artifacts, { recursive: true })
|
const browser = await chromium.launch({ channel: 'chrome', headless: true })
|
const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } })
|
await context.addInitScript(() => {
|
if (location.protocol !== 'http:') return
|
localStorage.setItem('battery_detection_auth_token', 'fixture-owner')
|
localStorage.setItem('battery_detection_auth_user', JSON.stringify({username:'owner',displayName:'负责人'}))
|
window.print = () => {}
|
})
|
const now = Date.UTC(2026,8,8,16)
|
let todos = [{ id:1,content:'本人待办',ownerId:2,owner:'负责人',status:'待处理',creator:'分派人',createdAt:now,operatedAt:now,handleRemark:'' }]
|
const summary = { testCount:1001,batteryGroupCount:3,todayCount:2,monthCount:5 }
|
const dashboard = { summary, trends: { week:{labels:['9/8','9/9'],values:[0,2]},month:{labels:['9/1','9/9'],values:[3,2]},year:{labels:['9月'],values:[5]} }, tests:[{id:1,name:'真实聚合测试',type:'循环',status:'RUNNING',receptionStatus:'PAUSED',step:'',warning:0}], cabinets:[{id:1,name:'检测柜甲',capacity:50,channelCount:8,usedChannels:1,testCount:1,batteryCount:1,onlineStatus:'在线'}], warnings:[] }
|
const source = (id, groupId=9) => ({ selection:{testId:id,testBatteryId:id,cycleNo:1,logicalNo:1},name:`测试${id}`,type:'循环',groupId,group:`组${groupId}`,batteryGroup:`组${groupId}`,batteryNo:'1号',process:'合并工步',startedAt:1,consoleDetail:{batteryFields:[],specialFields:[],fieldValues:{}} })
|
const segments = [source(1),source(2),source(3,10)].map((source,index) => ({source,originalCycleNos:index===0?[1,3]:index===1?[10,11]:[1],cyclePositions:index===0?{'1':1,'3':2}:index===1?{'10':3,'11':4}:{'1':5},capacityFactor:2,offset:0,statistics:Object.fromEntries((index===0?[1,3]:index===1?[10,11]:[1]).map(no=>[no,{finalVoltage1:4-no/100,dischargeCapacityF:2}])),consoleDetail:{batteryFields:[],specialFields:[],fieldValues:{}} }))
|
const splice = {id:7,name:'循环拼接验收',description:'按实际循环接续',versionNo:1,creator:'负责人',operatorName:'负责人',createdAt:now,versionCreatedAt:now,changeSummary:'新建',spliceMode:'project-cycles',segments,cycleNos:[1,2,3,4,5],selections:segments.map(item=>item.source.selection),settings:{metrics:['finalVoltage1','finalVoltage2','dischargeCapacityF'],seriesColors:['#3978f6','#7d42d8'],sourceDetailOverrides:{}}}
|
const compare = {id:7,name:'工步对比验收',description:'跨测试叠加',versionNo:1,creator:'负责人',operatorName:'负责人',createdAt:now,versionCreatedAt:now,changeSummary:'新建',displayUnits:{},items:[source(1),source(2)].map(source=>({source,zeroTime:1000,records:[{sourceType:'SNAPSHOT',deviceTime:1000,voltage:3,current:0.1,temperature:25,positivePotential:1.5,negativePotential:-0.1},{sourceType:'SNAPSHOT',deviceTime:61000,voltage:4,current:0.2,temperature:26,positivePotential:1.6,negativePotential:-0.2}]})),settings:{metrics:['voltage','current','positiveVoltage','negativeVoltage','temperature'],seriesColors:['#3978f6','#7d42d8'],sourceDetailOverrides:{}}}
|
let dashboardFails = false, todoFails = false
|
const requests = [], errors = []
|
context.on('page', page => page.on('pageerror', error => errors.push(error.message)))
|
await context.route('http://refinement.test/**', async route => {
|
const url = new URL(route.request().url()), path = url.pathname, method = route.request().method()
|
if (path.startsWith('/dev-api')) {
|
requests.push({path,method})
|
let response = {code:200,msg:'成功',rows:[],total:0}
|
if (path.endsWith('/getInfo')) Object.assign(response,{user:{userId:2,userName:'owner',nickName:'负责人'},roles:['admin'],permissions:['*:*:*']})
|
else if (path.endsWith('/system/dashboard')) response = dashboardFails ? {code:500,msg:'统计查询失败'} : {...response,data:dashboard}
|
else if (path.endsWith('/system/todo/list')) response = todoFails ? {code:500,msg:'待办读取失败'} : {...response,rows:todos}
|
else if (path.endsWith('/system/todo/owners')) response.data = [{id:2,name:'负责人'}]
|
else if (path.endsWith('/system/todo') && method==='POST') { const input=route.request().postDataJSON();todos.push({...todos[0],...input,id:2});response.data=2 }
|
else if (path.endsWith('/system/todo/1/handle')) { todos[0]={...todos[0],status:'已处理',handleRemark:route.request().postDataJSON().remark}; }
|
else if (path.endsWith('/system/todo/2') && method==='DELETE') todos=todos.filter(item=>item.id!==2)
|
else if (path.includes('/system/splice-report/7')) response.data=splice
|
else if (path.includes('/system/compare-report/7')) response.data=compare
|
else if (path.endsWith('/monitor/operlog/list')) Object.assign(response,{rows:[{operId:4,operName:'负责人',title:'测试详情',businessType:2,status:0,operTime:'2026-09-09 08:00:00'}],total:1})
|
await route.fulfill({json:response});return
|
}
|
const file=path.startsWith('/assets/')?resolve(dist,'.'+path):resolve(dist,'index.html')
|
assert.ok(file.startsWith(dist))
|
await route.fulfill({body:await readFile(file),contentType:{'.html':'text/html','.js':'text/javascript','.css':'text/css'}[extname(file)]||'application/octet-stream'})
|
})
|
try {
|
const page=await context.newPage()
|
await page.goto('http://refinement.test/dashboard')
|
await expect(page.locator('.metrics')).toContainText('1001')
|
await expect(page.locator('.todo-panel')).toContainText('本人待办')
|
await expect(page.locator('.cabinet-panel')).toContainText('在线')
|
await expect(page.locator('.dashboard')).not.toContainText('P1')
|
await expect(page.locator('.notification-badge')).toHaveText('1')
|
await page.getByRole('button',{name:'消息通知',exact:true}).click()
|
await expect(page.locator('.notification-list')).toContainText('本人待办')
|
await expect(page.locator('.notification-list')).not.toContainText('测试等待执行')
|
await page.keyboard.press('Escape')
|
await expect(page.locator('.notification-drawer')).not.toBeVisible()
|
await page.screenshot({path:resolve(artifacts,'dashboard-desktop.png'),fullPage:true})
|
dashboardFails=true
|
await page.reload()
|
await expect(page.getByText('统计查询失败')).toBeVisible()
|
await expect(page.locator('.metrics')).not.toContainText('1001')
|
dashboardFails=false
|
await page.goto('http://refinement.test/todo-list')
|
await expect(page.locator('.todo-page tbody')).toContainText('本人待办')
|
await page.getByRole('button',{name:'新增待办',exact:true}).click()
|
await page.getByPlaceholder('请输入待办内容').fill('新增真实接口事项')
|
await page.locator('.el-dialog .el-select').click()
|
await page.getByRole('option',{name:'负责人',exact:true}).click()
|
await page.getByRole('button',{name:'确定新增'}).click()
|
await expect(page.locator('.todo-page tbody')).toContainText('新增真实接口事项')
|
await page.locator('tr').filter({hasText:'本人待办'}).getByRole('button',{name:'处理待办'}).click()
|
await page.locator('.el-message-box textarea').fill('已核对完成')
|
await page.getByRole('button',{name:'完成处理',exact:true}).click()
|
await expect(page.locator('tr').filter({hasText:'本人待办'})).toContainText('已处理')
|
await page.reload()
|
await expect(page.locator('tr').filter({hasText:'本人待办'})).toContainText('已处理')
|
await page.locator('tr').filter({hasText:'新增真实接口事项'}).getByRole('button',{name:'删除',exact:true}).click()
|
await page.locator('.el-message-box').getByRole('button',{name:'删除',exact:true}).click()
|
await expect(page.locator('.todo-page tbody')).not.toContainText('新增真实接口事项')
|
todoFails=true;await page.reload();await expect(page.locator('.todo-page .el-alert')).toContainText('待办读取失败');todoFails=false
|
await page.goto('http://refinement.test/report-splice/7/edit?mode=edit')
|
await expect(page.locator('.project-chart-card')).toHaveCount(3)
|
await expect(page.locator('.statistics-editor')).toHaveCount(0)
|
await expect(page.locator('.report-test-detail')).toHaveCount(1)
|
await expect(page.locator('.splice-form-page')).not.toContainText('能量效率')
|
await page.screenshot({path:resolve(artifacts,'splice-desktop.png'),fullPage:true})
|
const popupPromise=page.waitForEvent('popup')
|
await page.getByRole('button',{name:'打印 / 另存 PDF'}).click()
|
const popup=await popupPromise
|
await expect(popup.locator('body')).toContainText('2:原循环3')
|
await expect(popup.locator('body')).toContainText('3:原循环10')
|
await expect(popup.locator('svg')).toHaveCount(3)
|
// 同组同名电池跨测试仍有各自图例,打印不能合并来源段。
|
await expect(popup.locator('svg').first()).toContainText('测试1-1号')
|
await expect(popup.locator('svg').first()).toContainText('测试2-1号')
|
await popup.close()
|
await page.setViewportSize({width:390,height:844})
|
await expect.poll(() => page.locator('.app-main').evaluate(element => element.getBoundingClientRect().left)).toBe(0)
|
await page.screenshot({path:resolve(artifacts,'splice-mobile.png'),fullPage:true,animations:'disabled'})
|
await page.setViewportSize({width:1440,height:1000})
|
await page.goto('http://refinement.test/report-compare/7/edit?mode=edit')
|
await expect(page.locator('.chart-card')).toHaveCount(5)
|
await expect(page.locator('.chart-empty')).toHaveCount(0)
|
await expect(page.locator('.report-test-detail')).not.toContainText('循环')
|
await expect(page.locator('.report-test-detail')).not.toContainText('工步')
|
await expect(page.locator('.chart-legend-bar .legend-name')).toHaveText(['测试1-1号','测试2-1号'])
|
await expect(page.getByText('数据选择',{exact:true})).toHaveCount(0)
|
await page.screenshot({path:resolve(artifacts,'compare-desktop.png'),fullPage:true})
|
const comparePopupPromise=page.waitForEvent('popup')
|
await page.getByRole('button',{name:'打印 / 另存 PDF'}).click()
|
const comparePopup=await comparePopupPromise
|
await expect(comparePopup.locator('.chart-card svg')).toHaveCount(5)
|
await expect(comparePopup.locator('.legend span')).toHaveText(['测试1-1号','测试2-1号'])
|
await expect(comparePopup.locator('.chart-card svg').first()).toContainText('测试1-1号')
|
for (const chart of await comparePopup.locator('.chart-card svg').all()) {
|
await expect(chart.locator('path[fill="none"][stroke="#3978f6"][stroke-width="2"]')).toHaveCount(1)
|
await expect(chart.locator('path[fill="none"][stroke="#7d42d8"][stroke-width="2"]')).toHaveCount(1)
|
}
|
await comparePopup.close()
|
await page.goto('http://refinement.test/operation-log')
|
await expect(page.locator('.operation-log-page tbody')).toContainText('测试详情:修改(成功)')
|
const formationStart=requests.length
|
await page.goto('http://refinement.test/report-formation')
|
await expect(page.getByText('SP2化成过程分析报表')).toBeVisible()
|
await page.getByRole('button',{name:'新建报表'}).click()
|
await expect(page.locator('.formation-form-page')).toBeVisible()
|
assert.ok(!requests.slice(formationStart).some(item=>item.path.includes('/formation-report')))
|
assert.deepEqual(errors,[])
|
console.log('首页、待办增删处理/重开/失败、拼接图表/打印、对比详情、日志及化成演示隔离:通过')
|
} finally { await context.close();await browser.close() }
|