mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 03:41:58 +00:00
运行对比页对齐服务端的实际输出:变化百分比按百分数写,归因、遮罩、差异说明照服务端给的显示
对照运行对比后端的实现核过一遍字段的写法: - delta_pct 服务端已经乘过 100(12.5 是 +12.5%),占比 share 仍是小数(0.4 是 40%),两处分开格式化;原先把 delta_pct 也当小数,会写成「+1250.0%」 - 维度归因服务端只给前 10 项、另有几项写在 note 里,照写;归不了因(查询结果截断、度量列遮罩、快照读不出来)时 给空的 rows 和一句说明,只写说明、不画空表;万一多给了仍只列 10 项并写明另有几项 - 约定之外服务端顺带给的几项按可选字段用上:输入的 masked(两边写「已遮罩」,不标变了)、查询的 differences (结构变了差在哪,列在并排的 SQL 上面)和 problem(快照读不出来、与哈希不一致)、模板配置项的 labels(按节点 类型翻好的叫法,键名放进悬停提示;没给或对不上时照写键名) - 找默认基准时的 404 分两种:本次这条运行不存在(机读码 run_not_found)写「运行记录不存在」,其余写「未找到 可作为基准的运行」 - check-compare 的夹具照服务端的写法改,补上述几种情形;菜单入口另查「问到基准之后才跳」(先跳到只带本次的地址、 再让对比页去找,原先查不出来);check-all 登记实测耗时
This commit is contained in:
@@ -96,7 +96,7 @@ const ESTIMATE = {
|
||||
stream: 173, chat: 160, studio: 149, manage: 145, runs: 112, publish: 92, shell: 87,
|
||||
evidence: 91, versions: 60, catalog: 45, 'ui-kit': 70, ui: 61, guards: 8, 'canvas-layout': 2, copy: 2, tokens: 1, trace: 1, decode: 1,
|
||||
// 占位,换成真检查时按实测改
|
||||
schedules: 1, compare: 1, verify: 1, inquiry: 1, bundle: 16, whatif: 33,
|
||||
schedules: 1, compare: 30, verify: 1, inquiry: 1, bundle: 16, whatif: 33,
|
||||
}
|
||||
// 最慢的两项按段拆成几份。用的是它们自己的段过滤:最后一份用 *_SKIP 兜住「其余全部」,以后新加的段
|
||||
// 不用改这里也跑得到(拆出去的那几份要是一段都没对上,那一份会报「一项都没跑」)。
|
||||
|
||||
+74
-23
@@ -70,12 +70,14 @@ const refOf = (id) => {
|
||||
}
|
||||
|
||||
const REGIONS = ['青禾区', '白石区', '云杉区', '赤松区', '蓝湾区', '金穗区', '银杏区', '翠竹区', '月湖区', '星港区', '石桥区', '柳岸区']
|
||||
// 各区的变化之和正好是总变化 2,531.5;占比按服务端的约定给小数(0.4 是 40%)
|
||||
// 各区的变化之和正好是总变化 2,531.5;占比照服务端给小数(0.4 是 40%)。服务端按变化的绝对值只给前 10 项,
|
||||
// 另有几项写在 note 里(ALL_ROWS 给「服务端多给了」那一节用)
|
||||
const DELTAS = [1012.6, 506.3, 379.7, 253.2, 126.6, 101.3, 63.3, 38.0, 25.3, 12.6, 7.6, 5.0]
|
||||
const ATTR_ROWS = REGIONS.map((name, i) => ({
|
||||
const ALL_ROWS = REGIONS.map((name, i) => ({
|
||||
key: [name], base: 3000 + i * 100, target: 3000 + i * 100 + DELTAS[i], delta: DELTAS[i],
|
||||
share: Math.round(DELTAS[i] / 2531.5 * 10000) / 10000,
|
||||
}))
|
||||
const MORE_KEYS = '共 12 个维度值,列出变化最大的 10 个'
|
||||
|
||||
const metric = (over) => ({
|
||||
card_id: 'caliber', card_title: '周报口径', unit: '', decimals: 1, reasons: [], decomposition: null,
|
||||
@@ -83,34 +85,39 @@ const metric = (over) => ({
|
||||
})
|
||||
const METRICS = [
|
||||
metric({ metric_id: 'gmv', name: '销售额', unit: '元', base: 45678.5, target: 48210, base_rendered: '45,678.5元',
|
||||
target_rendered: '48,210.0元', delta: 2531.5, delta_rendered: '+2,531.5元', delta_pct: 0.0554, classification: 'data_only',
|
||||
reasons: ['口径与 SQL 均未改动,变化来自数据'],
|
||||
target_rendered: '48,210.0元', delta: 2531.5, delta_rendered: '+2,531.5元', delta_pct: 5.54, classification: 'data_only',
|
||||
reasons: ['取数的 SQL 没有变化,查询结果有变化'],
|
||||
inputs: [{ path: 'vars.kpi.gmv', base: 45678.5, target: 48210 }],
|
||||
attribution: { columns: ['区域'], measure: 'amount', rows: ATTR_ROWS, total_delta: 2531.5, note: null } }),
|
||||
attribution: { columns: ['区域'], measure: 'amount', rows: ALL_ROWS.slice(0, 10), total_delta: 2531.5, note: MORE_KEYS } }),
|
||||
metric({ metric_id: 'orders', name: '订单数', unit: '单', decimals: 0, base: 1234, target: 1301, base_rendered: '1,234单',
|
||||
target_rendered: '1,301单', delta: 67, delta_rendered: '+67单', delta_pct: 0.0543, classification: 'data_only' }),
|
||||
target_rendered: '1,301单', delta: 67, delta_rendered: '+67单', delta_pct: 5.43, classification: 'data_only' }),
|
||||
metric({ metric_id: 'aov', name: '客单价', unit: '元', base: 37.0, target: 41.5, base_rendered: '37.0元', target_rendered: '41.5元',
|
||||
delta: 4.5, delta_rendered: '+4.5元', delta_pct: 0.1216, classification: 'caliber_and_data',
|
||||
reasons: ['口径卡「周报口径」中客单价的计算式有改动'],
|
||||
delta: 4.5, delta_rendered: '+4.5元', delta_pct: 12.16, classification: 'caliber_and_data',
|
||||
reasons: ['表达式由「vars.kpi.gmv / vars.kpi.order_cnt」改为「vars.kpi.gmv / vars.kpi.paid_cnt」'],
|
||||
decomposition: { caliber_effect: 5.8, data_effect: -1.3, caliber_rendered: '+5.8元', data_rendered: '-1.3元' },
|
||||
inputs: [{ path: 'vars.kpi.gmv', base: 45678.5, target: 48210 }, { path: 'vars.kpi.order_cnt', base: 1234, target: 1234 }],
|
||||
// 遮罩列的输入:服务端两边都不给值,另带 masked
|
||||
inputs: [{ path: 'vars.kpi.gmv', base: 45678.5, target: 48210 }, { path: 'vars.kpi.order_cnt', base: 1234, target: 1234 },
|
||||
{ path: 'vars.kpi.cost', base: null, target: null, masked: true }],
|
||||
attribution: { columns: ['区域', '渠道'], measure: 'order_cnt', total_delta: 67,
|
||||
note: '各区域订单数之和与指标的输入对不上,不给占比',
|
||||
rows: [{ key: ['青禾区', '门店'], base: 400, target: 440, delta: 40, share: null },
|
||||
{ key: ['白石区', '线上'], base: 300, target: 327, delta: 27, share: null }] } }),
|
||||
metric({ metric_id: 'refund_rate', name: '退货率', unit: '%', decimals: 2, base: 0.0235, target: 0.0198, base_rendered: '2.35%',
|
||||
target_rendered: '1.98%', delta: -0.0037, delta_rendered: '-0.37 个百分点', delta_pct: -0.1574, classification: 'sql_changed',
|
||||
target_rendered: '1.98%', delta: -0.0037, delta_rendered: '-0.37 个百分点', delta_pct: -15.74, classification: 'sql_changed',
|
||||
reasons: ['所依据的查询「退货明细」的筛选条件有改动'],
|
||||
decomposition_note: '所依据的查询「退货明细」SQL 结构有改动,口径不同,不做拆分' }),
|
||||
metric({ metric_id: 'visitors', name: '日均访客', unit: '人', decimals: 0, base: 5200, target: 5480, base_rendered: '5,200人',
|
||||
target_rendered: '5,480人', delta: 280, delta_rendered: '+280人', delta_pct: 0.0538, classification: 'params_only',
|
||||
reasons: ['「拉取销量」只有日期参数不同'] }),
|
||||
target_rendered: '5,480人', delta: 280, delta_rendered: '+280人', delta_pct: 5.38, classification: 'params_only',
|
||||
reasons: ['「拉取销量」只有日期参数不同'],
|
||||
// 归不了因:服务端给空的 rows 和一句说明
|
||||
attribution: { columns: [], measure: 'visits', rows: [], total_delta: null,
|
||||
note: '查询结果被截断(基准运行只取回了前 500 行),不做维度归因' } }),
|
||||
metric({ metric_id: 'new_users', name: '新客数', unit: '人', decimals: 0, base: null, target: 320, base_rendered: '—',
|
||||
target_rendered: '320人', delta: null, delta_rendered: null, classification: 'added', reasons: ['本次新增的指标'] }),
|
||||
metric({ metric_id: 'members', name: '会员数', unit: '人', decimals: 0, base: 8800, target: null, base_rendered: '8,800人',
|
||||
target_rendered: '—', delta: null, delta_rendered: null, classification: 'removed', reasons: ['本次没有这个指标'] }),
|
||||
metric({ metric_id: 'margin', name: '毛利率', unit: '%', decimals: 1, base: 0.31, target: 0.33, base_rendered: '31.0%',
|
||||
target_rendered: '33.0%', delta: 0.02, delta_rendered: '+2.0 个百分点', delta_pct: 0.0645, classification: 'incomparable',
|
||||
target_rendered: '33.0%', delta: 0.02, delta_rendered: '+2.0 个百分点', delta_pct: 6.45, classification: 'incomparable',
|
||||
reasons: ['基准运行的口径卡没有记录输入'], decomposition_note: '基准运行的口径卡没有记录输入,无法拆分' }),
|
||||
...[['stores', '门店数', '家', 56], ['complaints', '客诉数', '件', 12], ['sku', 'SKU 数', '个', 1500]].map(([id, name, unit, v]) =>
|
||||
metric({ metric_id: id, name, unit, decimals: 0, base: v, target: v, base_rendered: `${v.toLocaleString('en-US')}${unit}`,
|
||||
@@ -126,7 +133,8 @@ const QUERIES = [
|
||||
{ node_id: 'refund', node_title: '退货明细', ordinal: 0, structure: 'changed',
|
||||
base_sql: "SELECT COUNT(*) AS n\nFROM refunds r\nWHERE r.created_at >= '2026-09-21'",
|
||||
target_sql: "SELECT COUNT(*) AS n\nFROM refunds r\nWHERE r.paid_at >= '2026-09-28' AND r.status = 'done'",
|
||||
literals: [], base_rows: 500, target_rows: 488, base_truncated: true, target_truncated: false },
|
||||
literals: [], base_rows: 500, target_rows: 488, base_truncated: true, target_truncated: false,
|
||||
differences: ['时间条件的列不同:基准按 refunds.created_at,这次按 refunds.paid_at', "这次多了筛选条件 refunds.status = 'done'"] },
|
||||
{ node_id: 'stores', node_title: '门店清单', ordinal: 0, structure: 'same', base_sql: 'SELECT COUNT(*) AS n FROM stores',
|
||||
target_sql: 'SELECT COUNT(*) AS n FROM stores', literals: [], base_rows: 1, target_rows: 1, base_truncated: false, target_truncated: false },
|
||||
{ node_id: 'pull', node_title: '拉取销量', ordinal: 1, structure: 'same', base_sql: 'SELECT COUNT(*) AS n FROM visits',
|
||||
@@ -135,11 +143,12 @@ const QUERIES = [
|
||||
target_sql: "SELECT COUNT(*) AS n FROM users WHERE first_order_on >= '2026-09-28'", literals: [], base_rows: null, target_rows: 1,
|
||||
base_truncated: null, target_truncated: false },
|
||||
{ node_id: 'member', node_title: '会员查询', ordinal: 0, structure: 'removed', base_sql: 'SELECT COUNT(*) AS n FROM members',
|
||||
target_sql: null, literals: [], base_rows: 1, target_rows: null, base_truncated: false, target_truncated: null },
|
||||
target_sql: null, literals: [], base_rows: 1, target_rows: null, base_truncated: false, target_truncated: null,
|
||||
problem: '基准运行的查询快照无法读取' },
|
||||
]
|
||||
const TEMPLATE = { base_version: 3, target_version: 4, same_graph: false, nodes: [
|
||||
{ node_id: 'caliber', title: '周报口径', change: 'config', keys: ['metrics'] },
|
||||
{ node_id: 'refund', title: '退货明细', change: 'config', keys: ['sql', 'timeout'] },
|
||||
{ node_id: 'refund', title: '退货明细', change: 'config', keys: ['sql', 'timeout'], labels: ['SQL', '超时(秒)'] },
|
||||
{ node_id: 'newbie', title: '新客查询', change: 'added', keys: [] },
|
||||
{ node_id: 'member', title: '会员查询', change: 'removed', keys: [] },
|
||||
] }
|
||||
@@ -285,6 +294,9 @@ const shot = async (page, name) => {
|
||||
await section('从运行详情进入', async () => {
|
||||
const { page, log, blocked, errors } = await open(`/runs/${T}`)
|
||||
await page.locator(`[data-run-detail="${T}"]`).waitFor({ timeout: 10000 })
|
||||
// 走过的地址(含 pushState / replaceState):菜单要先问到基准再跳,不能先跳到只带本次的地址、再让对比页去找
|
||||
const visited = []
|
||||
page.on('framenavigated', (f) => { if (f === page.mainFrame()) visited.push(f.url()) })
|
||||
await page.getByRole('button', { name: '更多操作' }).click()
|
||||
const item = page.locator('[data-menu=compare]')
|
||||
check('「更多」菜单里有「与上一次对比」,可点', await item.count() === 1 && (await item.innerText()).includes('与上一次对比')
|
||||
@@ -298,6 +310,9 @@ await section('从运行详情进入', async () => {
|
||||
const iCmp = log.indexOf(`GET /api/runs/${T}/compare?base=${B}`)
|
||||
check('先请求 compare/base,再请求 compare', iBase >= 0 && iCmp > iBase, `${iBase} → ${iCmp}`)
|
||||
check('compare/base 只请求一次(页面不再自己找一遍)', log.filter((k) => k.startsWith(`GET /api/runs/${T}/compare/base`)).length === 1)
|
||||
const compareUrls = visited.map((u) => new URL(u)).filter((u) => u.pathname === '/runs/compare')
|
||||
check('菜单问到基准之后才跳,一步到带基准的地址', compareUrls.length >= 1 && compareUrls.every((u) => u.searchParams.get('base') === B),
|
||||
compareUrls.map((u) => u.search).join(' → '))
|
||||
check('写明基准是怎么选的', (await text(page, '[data-compare-reason]')).trim() === REASON, await text(page, '[data-compare-reason]').catch(() => ''))
|
||||
check('没有发出写请求', blocked.length === 0, blocked.join(';'))
|
||||
check('页面没有报错', errors.length === 0, errors.join(';'))
|
||||
@@ -352,7 +367,7 @@ await section('指标表', async () => {
|
||||
check('基准 → 本次、变化量用服务端渲染的写法', (await gmv.locator('[data-compare-base]').innerText()) === '45,678.5元'
|
||||
&& (await gmv.locator('[data-compare-target]').innerText()) === '48,210.0元'
|
||||
&& (await gmv.locator('[data-compare-delta]').innerText()) === '+2,531.5元')
|
||||
check('变化百分比带正负号(0.0554 → +5.5%)', (await gmv.locator('[data-compare-pct]').innerText()) === '+5.5%',
|
||||
check('变化百分比照服务端的百分数写、带正负号(5.54 → +5.5%)', (await gmv.locator('[data-compare-pct]').innerText()) === '+5.5%',
|
||||
await gmv.locator('[data-compare-pct]').innerText().catch(() => ''))
|
||||
const refund = page.locator('tr[data-compare-metric="caliber:refund_rate"]')
|
||||
check('下降的百分比写负号', (await refund.locator('[data-compare-pct]').innerText()) === '-15.7%')
|
||||
@@ -382,18 +397,20 @@ await section('展开:输入对比与维度归因', async () => {
|
||||
await detail.waitFor()
|
||||
check('展开后标为展开、指向明细', (await gmvBtn.getAttribute('aria-expanded')) === 'true'
|
||||
&& (await gmvBtn.getAttribute('aria-controls')) === await detail.getAttribute('id'))
|
||||
check('列出变化说明', (await detail.locator('[data-compare-reasons]').innerText()).includes('变化来自数据'))
|
||||
check('列出变化说明', (await detail.locator('[data-compare-reasons]').innerText()).includes('查询结果有变化'))
|
||||
const input = detail.locator('[data-compare-inputs] tr[data-compare-input="vars.kpi.gmv"]')
|
||||
check('输入对比:路径、两边的值,变了的标出来', (await input.innerText()).includes('45,678.5') && (await input.innerText()).includes('48,210')
|
||||
&& (await input.getAttribute('data-changed')) !== null, (await input.innerText().catch(() => '')).replace(/\s+/g, ' '))
|
||||
const attrRows = detail.locator('[data-compare-attr] tr[data-compare-attr-row]')
|
||||
check('维度归因只列前 10 项', await attrRows.count() === 10, String(await attrRows.count()))
|
||||
check('写明另有几项没列', (await detail.locator('[data-compare-attr-more]').innerText()).includes('2'))
|
||||
check('服务端说另有几项没列:照写', (await detail.locator('[data-compare-attr-note]').innerText()) === MORE_KEYS
|
||||
&& await detail.locator('[data-compare-attr-more]').count() === 0)
|
||||
const first = attrRows.first()
|
||||
check('归因一行:键、两边的值、变化、占比', (await first.innerText()).includes('青禾区') && (await first.innerText()).includes('+1,012.6')
|
||||
&& (await first.locator('[data-compare-share]').innerText()) === '40.0%', (await first.innerText()).replace(/\s+/g, ' '))
|
||||
check('写明按哪一列归因', (await detail.locator('[data-compare-attr]').innerText()).includes('amount'))
|
||||
check('有占比时不写缺占比的原因', await detail.locator('[data-compare-attr-note]').count() === 0)
|
||||
check('有占比时不写缺占比的原因', !(await detail.locator('[data-compare-attr-note]').innerText()).includes('占比'))
|
||||
check('写出合计变化', (await detail.locator('[data-compare-attr-total]').innerText()).includes('+2,531.5'))
|
||||
|
||||
// 键盘:Tab 到客单价的展开按钮,回车展开
|
||||
const aovBtn = page.locator('tr[data-compare-metric="caliber:aov"] button[data-compare-expand]')
|
||||
@@ -406,6 +423,9 @@ await section('展开:输入对比与维度归因', async () => {
|
||||
(await aov.locator('[data-compare-decomp-sum]').innerText()).trim() === '口径效应 +5.8元,数据效应 -1.3元,两者之和等于总变化 +4.5元',
|
||||
await aov.locator('[data-compare-decomp-sum]').innerText().catch(() => ''))
|
||||
check('没有变的输入不标', (await aov.locator('tr[data-compare-input="vars.kpi.order_cnt"]').getAttribute('data-changed')) === null)
|
||||
const masked = aov.locator('tr[data-compare-input="vars.kpi.cost"]')
|
||||
check('遮罩列的输入:两边都写「已遮罩」,不标变了', (await masked.locator('td').allInnerTexts()).every((t) => t.trim() === '已遮罩')
|
||||
&& (await masked.getAttribute('data-changed')) === null, (await masked.innerText().catch(() => '')).replace(/\s+/g, ' '))
|
||||
const shares = await aov.locator('[data-compare-attr] [data-compare-share]').allInnerTexts()
|
||||
check('没有占比:占比一栏写「—」', shares.length === 2 && shares.every((s) => s.trim() === '—'), shares.join(','))
|
||||
check('没有占比:写出原因', (await aov.locator('[data-compare-attr-note]').innerText()).includes('各区域订单数之和与指标的输入对不上'))
|
||||
@@ -420,9 +440,27 @@ await section('展开:输入对比与维度归因', async () => {
|
||||
const orders = page.locator('tr[data-compare-detail="caliber:orders"]')
|
||||
check('没有维度归因、没有输入时照实说', (await orders.locator('[data-compare-attr-none]').count()) === 1
|
||||
&& (await orders.locator('[data-compare-inputs-none]').count()) === 1)
|
||||
// 归不了因(查询结果截断):服务端给空的 rows 和一句说明,只写说明,不画空表
|
||||
await page.locator('tr[data-compare-metric="caliber:visitors"] button[data-compare-expand]').click()
|
||||
const visitors = page.locator('tr[data-compare-detail="caliber:visitors"]')
|
||||
check('归不了因:写服务端的说明,不画空表', (await visitors.locator('[data-compare-attr-note]').innerText()).includes('被截断')
|
||||
&& await visitors.locator('[data-compare-attr] table').count() === 0 && await visitors.locator('[data-compare-attr-total]').count() === 0)
|
||||
check('页面没有报错', errors.length === 0, errors.join(';'))
|
||||
})
|
||||
|
||||
await section('归因超过 10 项时只列前 10 项', async () => {
|
||||
const { page } = await open(comparePath(T, B), { compare: (t, b) => {
|
||||
const data = comparison(t, b)
|
||||
return { json: { ...data, metrics: data.metrics.map((m) => (m.metric_id === 'gmv'
|
||||
? { ...m, attribution: { ...m.attribution, rows: ALL_ROWS, note: null } } : m)) } }
|
||||
} })
|
||||
await settled(page)
|
||||
await page.locator('tr[data-compare-metric="caliber:gmv"] button[data-compare-expand]').click()
|
||||
const detail = page.locator('tr[data-compare-detail="caliber:gmv"]')
|
||||
check('只列 10 项', await detail.locator('[data-compare-attr] tr[data-compare-attr-row]').count() === 10)
|
||||
check('写明另有几项没列', (await detail.locator('[data-compare-attr-more]').innerText()).includes('另有 2 项'))
|
||||
})
|
||||
|
||||
await section('摘要筛选', async () => {
|
||||
const { page } = await open(comparePath(T, B))
|
||||
await settled(page)
|
||||
@@ -460,6 +498,10 @@ await section('查询', async () => {
|
||||
check('高亮本次独有的部分', add.includes('paid_at') && add.includes("r.status = 'done'") && !add.includes('FROM'), add)
|
||||
check('相同的部分不高亮', !(await baseSql.locator('mark').allInnerTexts()).join(' ').includes('refunds'))
|
||||
check('截断照实标', (await refund.locator('[data-compare-rows]').innerText()).includes('已截断'))
|
||||
check('结构变:列出服务端说的差在哪', JSON.stringify(await refund.locator('[data-compare-differences] li').allInnerTexts())
|
||||
=== JSON.stringify(QUERIES[1].differences))
|
||||
check('快照读不出来:照服务端的说明写在那一条上', (await page.locator('[data-compare-query="member:0"] [data-compare-query-problem]').innerText())
|
||||
=== '基准运行的查询快照无法读取')
|
||||
const pull = page.locator('[data-compare-query="pull:0"]')
|
||||
const lits = await pull.locator('[data-compare-literals] li').allInnerTexts()
|
||||
check('只参数变:列出变化的参数', lits.length === 2 && lits[0].includes("'2026-09-21'") && lits[0].includes("'2026-09-28'")
|
||||
@@ -486,9 +528,13 @@ await section('模板与说明', async () => {
|
||||
await settled(page)
|
||||
check('模板:版本 v3 → v4', (await text(page, '[data-compare-versions]')).includes('v3') && (await text(page, '[data-compare-versions]')).includes('v4'))
|
||||
const node = page.locator('[data-compare-node="refund"]')
|
||||
check('变化的节点:名字、变化类型、配置项', (await node.getAttribute('data-change')) === 'config' && (await node.innerText()).includes('退货明细')
|
||||
&& (await node.innerText()).includes('配置变更')
|
||||
&& JSON.stringify(await node.locator('[data-compare-key]').allInnerTexts()) === JSON.stringify(['sql', 'timeout']))
|
||||
check('变化的节点:名字、变化类型、配置项(用服务端给的叫法,键名在悬停提示里)', (await node.getAttribute('data-change')) === 'config'
|
||||
&& (await node.innerText()).includes('退货明细') && (await node.innerText()).includes('配置变更')
|
||||
&& JSON.stringify(await node.locator('[data-compare-key]').allInnerTexts()) === JSON.stringify(['SQL', '超时(秒)'])
|
||||
&& JSON.stringify(await node.locator('[data-compare-key]').evaluateAll((els) => els.map((e) => e.getAttribute('title'))))
|
||||
=== JSON.stringify(['sql', 'timeout']))
|
||||
check('没给叫法的配置项照写键名', JSON.stringify(await page.locator('[data-compare-node="caliber"] [data-compare-key]').allInnerTexts())
|
||||
=== JSON.stringify(['metrics']))
|
||||
check('新增、移除的节点写明', (await text(page, '[data-compare-node="newbie"]')).includes('新增节点')
|
||||
&& (await text(page, '[data-compare-node="member"]')).includes('移除节点'))
|
||||
check('说明照实列出', JSON.stringify(await texts(page, '[data-compare-notes] li')) === JSON.stringify([NOTE]))
|
||||
@@ -584,6 +630,11 @@ await section('出错与找不到基准', async () => {
|
||||
await settled(lone.page)
|
||||
check('手动选了之后照常对比', new URL(lone.page.url()).searchParams.get('base') === B2 && compareHits(lone.log, T, B2) === 1)
|
||||
|
||||
const missing = await open(comparePath(T), { base: () => ({ status: 404, json: { detail: '运行记录不存在', code: 'run_not_found' } }) })
|
||||
await settled(missing.page, 'error')
|
||||
check('找基准时本次就不存在:写运行记录不存在', await missing.page.locator('[data-compare-error="404"]').count() === 1
|
||||
&& (await missing.page.locator('[data-compare-error-title]').innerText()) === '运行记录不存在')
|
||||
|
||||
// 只带本次、找得到:自己补上基准,写明怎么选的
|
||||
const auto = await open(comparePath(T))
|
||||
await settled(auto.page)
|
||||
|
||||
Reference in New Issue
Block a user