yeg
2026-09-09 d67f933e50c7b5bf8c90e7d7db0000721ce796cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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() }