| | |
| | | running: true, softwareAccessible: true, listenAddress: '127.0.0.1:15020', |
| | | devices: [{ deviceId: 1, ip: '127.0.0.2', online: true, |
| | | profile: { devtype: 22, subDeviceId: 1, model: 'BTS Virtual Cabinet' }, |
| | | channels: [{ channelId: 1, online: true, workStatus: 'WORKING' }] }], |
| | | channels: [1, 2].map(channelId => ({ channelId, online: true, workStatus: 'WORKING', configuration: { |
| | | currentA: 0, voltageV: 3.7, capacityAh: 1.2, energyWh: 4.44, cycleNo: 1, stepNo: 1, |
| | | plannedCycleCount: 1, totalTimeSeconds: 0, relativeTimeSeconds: 0, |
| | | steps: [{ name: '恒流充电', stepType: 'cc', durationSeconds: 60, voltageV: 3.7 }], |
| | | } })) }], |
| | | collectors: [{ collectorId: 1, deviceCode: 'dev-sim-collector-1', mac: '02:43:00:00:00:01', |
| | | name: 'dev-sim 采集器 1', ip: '127.0.1.2', online: true, channelCount: 1, |
| | | channels: [{ channelCode: 'S-01', workStatus: 'WORKING', |
| | |
| | | } |
| | | const calls = [] |
| | | let failSoftware = false |
| | | let failConfiguration = false |
| | | await page.route('http://simulator.test/**', async route => { |
| | | const request = route.request() |
| | | const path = new URL(request.url()).pathname |
| | |
| | | if (path.endsWith('/software')) { |
| | | if (failSoftware) return route.fulfill({ status: 503, json: { message: '保存失败' } }) |
| | | state.softwareAccessible = body.accessible |
| | | } else if (path.endsWith('/devices/1/channels/1')) state.devices[0].channels[0] = { channelId: 1, ...body } |
| | | } else if (path.endsWith('/devices/1/channels/1/configuration')) { |
| | | if (failConfiguration) return route.fulfill({ status: 503, json: { message: '配置保存失败' } }) |
| | | state.devices[0].channels[0].configuration = body |
| | | } |
| | | else if (path.endsWith('/devices/1/channels/1')) Object.assign(state.devices[0].channels[0], body) |
| | | else if (path.endsWith('/collectors/1/channels/S-01/status')) state.collectors[0].channels[0].workStatus = body.workStatus |
| | | else if (path.endsWith('/collectors/1/channels/S-01')) state.collectors[0].channels[0].metrics = body.metrics |
| | | else if (path.endsWith('/devices/1')) Object.assign(state.devices[0], body) |
| | | else if (path.endsWith('/collectors/1')) Object.assign(state.collectors[0], body) |
| | | return route.fulfill({ json: state }) |
| | | }) |
| | | await page.goto('http://simulator.test/dev/bts-simulator.html') |
| | | await page.locator('#devices').getByRole('button', { name: '参数与方案' }).first().click() |
| | | const editor = page.getByRole('dialog') |
| | | await editor.getByLabel('电压 (V)', { exact: true }).fill('4.125') |
| | | await editor.getByLabel('电流 (A)').fill('-2.5') |
| | | await editor.getByRole('button', { name: '添加工步' }).click() |
| | | await editor.getByLabel('工步名称', { exact: true }).last().fill('放电测试') |
| | | await editor.getByLabel('工步类型').last().selectOption('dc') |
| | | await editor.getByLabel('时长 (s)', { exact: true }).last().fill('90') |
| | | await editor.getByLabel('当前工步').fill('2') |
| | | await editor.getByLabel('计划循环数').fill('3') |
| | | await page.clock.install() |
| | | await page.clock.fastForward(5000) |
| | | await expect(editor.getByLabel('电压 (V)', { exact: true })).toHaveValue('4.125') |
| | | await editor.screenshot({ path: output + '/channel-editor.png' }) |
| | | await editor.getByRole('button', { name: '保存通道配置' }).click() |
| | | await expect(editor).not.toBeVisible() |
| | | assert.equal(state.devices[0].channels[0].configuration.voltageV, 4.125) |
| | | assert.equal(state.devices[0].channels[0].configuration.steps[1].name, '放电测试') |
| | | assert.equal(state.devices[0].channels[1].configuration.voltageV, 3.7) |
| | | await page.reload() |
| | | await page.locator('#devices').getByRole('button', { name: '参数与方案' }).first().click() |
| | | await expect(editor.getByLabel('当前工步')).toHaveValue('2') |
| | | await expect(editor.getByLabel('工步名称', { exact: true }).last()).toHaveValue('放电测试') |
| | | await editor.getByRole('button', { name: '上移工步', exact: true }).last().click() |
| | | await expect(editor.getByLabel('工步名称', { exact: true }).first()).toHaveValue('放电测试') |
| | | await editor.getByRole('button', { name: '下移工步', exact: true }).first().click() |
| | | await editor.getByRole('button', { name: '添加工步' }).click() |
| | | await editor.getByRole('button', { name: '删除工步', exact: true }).last().click() |
| | | await expect(editor.getByLabel('工步名称', { exact: true })).toHaveCount(2) |
| | | failConfiguration = true |
| | | await editor.getByLabel('电压 (V)', { exact: true }).fill('4.2') |
| | | await editor.getByRole('button', { name: '保存通道配置' }).click() |
| | | await expect(editor.getByRole('alert')).toContainText('配置保存失败') |
| | | await expect(editor.getByLabel('电压 (V)', { exact: true })).toHaveValue('4.2') |
| | | failConfiguration = false |
| | | await page.setViewportSize({ width: 390, height: 844 }) |
| | | assert.equal(await editor.evaluate(element => element.scrollWidth > element.clientWidth), false) |
| | | await editor.screenshot({ path: output + '/channel-editor-390.png' }) |
| | | await editor.getByRole('button', { name: '取消' }).click() |
| | | await page.setViewportSize({ width: 1440, height: 1000 }) |
| | | await page.locator('#collectors').getByRole('button', { name: '编辑测量值' }).click() |
| | | await editor.getByLabel('数值', { exact: true }).first().fill('32.75') |
| | | await editor.getByRole('button', { name: '添加测量项' }).click() |
| | | await editor.getByLabel('测量项', { exact: true }).last().selectOption('negative_potential') |
| | | await editor.getByLabel('数值', { exact: true }).last().fill('-0.15') |
| | | await editor.screenshot({ path: output + '/collector-editor.png' }) |
| | | await editor.getByRole('button', { name: '保存通道配置' }).click() |
| | | await expect(editor).not.toBeVisible() |
| | | assert.equal(state.collectors[0].channels[0].metrics[0].value, 32.75) |
| | | assert.equal(state.collectors[0].channels[0].metrics[1].value, -0.15) |
| | | await page.reload() |
| | | await expect(page.locator('#collectors')).toContainText('温度 32.75 ℃') |
| | | await expect(page.locator('#collectors')).toContainText('负电位 -0.15 V') |
| | | await expect(page.getByLabel('新威软件可访问')).toBeChecked() |
| | | await page.getByLabel('新威软件可访问').uncheck() |
| | | await expect(page.getByLabel('新威软件可访问')).not.toBeChecked() |
| | |
| | | assert.deepEqual(errors, []) |
| | | assert.ok(calls.some(call => call.path.endsWith('/software') && call.method === 'PUT')) |
| | | assert.ok(calls.every(call => !/playback|sampling|history/.test(call.path))) |
| | | console.log('模拟器页面:软件、设备、通道、刷新、失败提示及 1440/768/390 布局通过。') |
| | | const consolePage = await browser.newPage({ viewport: { width: 1440, height: 1000 } }) |
| | | consolePage.on('pageerror', error => errors.push(error.message)) |
| | | await consolePage.addInitScript(() => localStorage.setItem('battery_detection_auth_token', 'browser-test')) |
| | | const config = state.devices[0].channels[0].configuration |
| | | const detail = { testId: 1, testName: '通道方案验收', testStatus: 'RUNNING', receptionStatus: 'RECEIVING', |
| | | creatorName: '若依', principalName: '若依', testTypeName: '性能测试', testPurpose: '方案观测', |
| | | thresholds: [], batteries: [], planSteps: [], logicStepMappings: [] } |
| | | const live = { testId: 1, testStatus: 'RUNNING', receptionStatus: 'RECEIVING', serverTime: Date.now(), sources: [], exceptions: [], |
| | | testPlan: { currentCycleNo: config.cycleNo, currentStepNo: config.stepNo, plannedCycleCount: config.plannedCycleCount, |
| | | rows: config.steps.map((step, index) => ({ rowType: 'STEP', stepNo: index + 1, cyclePosition: index + 1, |
| | | stepName: step.name, durationSeconds: step.durationSeconds, voltageV: step.voltageV, |
| | | executionStatus: index === 1 ? '进行中' : '已完成' })) } } |
| | | await consolePage.addInitScript(state => { |
| | | const fetch = window.fetch.bind(window) |
| | | window.fetch = (input, init) => String(input).endsWith('/test/1/live') |
| | | ? Promise.resolve(new Response(new ReadableStream({ start(controller) { |
| | | controller.enqueue(new TextEncoder().encode(`event: snapshot\ndata: ${JSON.stringify(state)}\n\n`)) |
| | | } }), { headers: { 'Content-Type': 'text/event-stream' } })) : fetch(input, init) |
| | | }, live) |
| | | await consolePage.route('http://simulator-console.test/**', async route => { |
| | | const path = new URL(route.request().url()).pathname |
| | | if (path.startsWith('/assets/')) return route.fulfill({ body: await readFile(`dist${path}`), contentType: path.endsWith('.css') ? 'text/css' : 'application/javascript' }) |
| | | if (path.endsWith('/test/1/live')) return route.fulfill({ contentType: 'text/event-stream', body: `event: snapshot\ndata: ${JSON.stringify(live)}\n\n` }) |
| | | if (path.includes('/system/test/1')) return route.fulfill({ json: { code: 200, data: path.endsWith('/live-state') ? live : detail } }) |
| | | if (path.endsWith('/getInfo')) return route.fulfill({ json: { code: 200, user: { userName: 'admin' }, roles: ['admin'] } }) |
| | | return route.fulfill({ contentType: 'text/html', body: await readFile('dist/index.html', 'utf8') }) |
| | | }) |
| | | await consolePage.goto('http://simulator-console.test/test-list/1/console') |
| | | const plan = consolePage.locator('.step-panel') |
| | | await expect(plan).toContainText('放电测试') |
| | | await expect(plan).toContainText('恒流充电') |
| | | await expect(plan).toContainText('进行中') |
| | | await expect(plan).not.toContainText('暂无后端') |
| | | await plan.screenshot({ path: output + '/console-plan.png' }) |
| | | assert.deepEqual(errors, []) |
| | | console.log('模拟器页面:逐通道参数、工步增删排序、采集器数值、刷新保留、编辑防覆盖、保存失败及 1440/768/390 布局通过。') |
| | | } finally { |
| | | await browser.close() |
| | | } |