修改完成

This commit is contained in:
2026-07-21 07:53:03 +08:00
parent 01d246c47b
commit ff60119277
172 changed files with 7982 additions and 2999 deletions
+94 -2
View File
@@ -1,5 +1,12 @@
const assert = require('assert')
const sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds))
const origin = process.argv[2] || 'http://localhost:5173'
const sizes = [
{ width: 320, height: 568 },
{ width: 360, height: 640 },
{ width: 360, height: 800 },
{ width: 412, height: 915 }
]
const connect = async () => {
const pages = await (await fetch('http://127.0.0.1:9222/json/list')).json()
@@ -27,7 +34,13 @@ const connect = async () => {
return { socket, send }
}
const valueOf = async (send, expression) => (await send('Runtime.evaluate', { expression, returnByValue: true })).result?.value
const valueOf = async (send, expression) => {
const evaluation = await send('Runtime.evaluate', { expression, returnByValue: true })
if (evaluation.exceptionDetails) {
throw new Error(evaluation.exceptionDetails.exception?.description || evaluation.exceptionDetails.text)
}
return evaluation.result?.value
}
const waitFor = async (send, expression, message) => {
for (let attempt = 0; attempt < 50; attempt += 1) {
if (await valueOf(send, expression)) return
@@ -39,7 +52,9 @@ const open = async (send, query, selector) => {
const url = `${origin}/#/pages/tree/t01-tree-overview${query}`
await send('Page.navigate', { url })
await waitFor(send, `location.href === ${JSON.stringify(url)}`, `T01 navigation failed: ${query}`)
const previousTimeOrigin = await valueOf(send, 'performance.timeOrigin')
await send('Page.reload')
await waitFor(send, `performance.timeOrigin !== ${JSON.stringify(previousTimeOrigin)}`, `T01 reload did not create a fresh document: ${query}`)
await waitFor(send, `Boolean(document.querySelector(${JSON.stringify(selector)}))`, `T01 state did not render: ${selector}`)
}
@@ -51,12 +66,89 @@ const run = async () => {
await open(send, '?state=landscape&genealogyId=1001', '.tree-state--landscape')
await open(send, '?state=empty&genealogyId=1001', '.tree-state--empty')
await open(send, '?state=error&genealogyId=1001', '.tree-state--error')
for (const size of sizes) {
await send('Emulation.setDeviceMetricsOverride', { ...size, deviceScaleFactor: 1, mobile: true, screenWidth: size.width, screenHeight: size.height })
await open(send, '?genealogyId=1001', '.tree-state--tree .member-node')
const metrics = await valueOf(send, `(() => ({
documentWidth: document.documentElement.scrollWidth,
stageDisplay: getComputedStyle(document.querySelector('.tree-stage')).display,
railPosition: getComputedStyle(document.querySelector('.generation-rail')).position,
nodePosition: getComputedStyle(document.querySelector('.member-node')).position,
nodeCount: document.querySelectorAll('.member-node').length,
firstNodeRect: (() => {
const rect = document.querySelector('.member-node').getBoundingClientRect()
return { left: rect.left, top: rect.top, width: rect.width, height: rect.height }
})(),
canvasRect: (() => {
const rect = document.querySelector('.tree-canvas').getBoundingClientRect()
return { left: rect.left, top: rect.top, width: rect.width, height: rect.height }
})()
}))()`)
assert(metrics.documentWidth <= size.width + 1, `T01 document overflows at ${size.width}x${size.height}`)
assert.strictEqual(metrics.stageDisplay, 'grid', `T01 stage is not a grid at ${size.width}x${size.height}`)
assert.strictEqual(metrics.railPosition, 'static', `T01 generation rail is positioned at ${size.width}x${size.height}`)
assert.strictEqual(metrics.nodePosition, 'static', `T01 member node is positioned at ${size.width}x${size.height}`)
assert.strictEqual(metrics.nodeCount, 6, `T01 member nodes are incomplete at ${size.width}x${size.height}`)
assert(metrics.firstNodeRect.width > 80 && metrics.firstNodeRect.height > 30, `T01 member node collapsed at ${size.width}x${size.height}: ${JSON.stringify(metrics)}`)
assert(metrics.firstNodeRect.top >= metrics.canvasRect.top && metrics.firstNodeRect.top < metrics.canvasRect.top + metrics.canvasRect.height, `T01 member node escaped the data-driven canvas at ${size.width}x${size.height}: ${JSON.stringify(metrics)}`)
assert(metrics.firstNodeRect.left + metrics.firstNodeRect.width > 0 && metrics.firstNodeRect.left < size.width, `T01 initial member node is outside the visible horizontal tree viewport at ${size.width}x${size.height}: ${JSON.stringify(metrics)}`)
assert(metrics.firstNodeRect.top < size.height * 0.5, `T01 generation rail and canvas were stacked instead of sharing the tree-stage row at ${size.width}x${size.height}: ${JSON.stringify(metrics)}`)
}
const stressMembers = []
for (let generation = 1; generation <= 10; generation += 1) {
for (let index = 0; index < 12; index += 1) {
stressMembers.push({
id: generation * 100 + index,
parentId: generation === 1 ? undefined : (generation - 1) * 100 + Math.floor(index / 2),
name: `压力成员${generation}-${index + 1}`,
relation: generation === 1 ? '始祖' : '后代',
years: '1900—',
generation,
branch: `${index + 1}支长名称`,
x: 150 + index * 250,
y: 135 + (generation - 1) * 220
})
}
}
const injected = await valueOf(send, `(() => {
let instance = document.querySelector('.tree-page')?.__vueParentComponent
while (instance && !Object.prototype.hasOwnProperty.call(instance.setupState || {}, 'members')) instance = instance.parent
const state = instance?.setupState
if (!state) return false
const nextMembers = ${JSON.stringify(stressMembers)}
if (Array.isArray(state.members)) state.members = nextMembers
else if (state.members && 'value' in state.members) state.members.value = nextMembers
else return false
return true
})()`)
assert.strictEqual(injected, true, 'T01 stress data could not be injected into the active component')
await waitFor(send, "document.querySelectorAll('.member-node').length === 120", 'T01 did not render 10 generations with 12 members each')
const stressMetrics = await valueOf(send, `(() => {
const canvas = document.querySelector('.tree-canvas')
const rail = document.querySelector('.generation-rail')
return {
canvasWidth: parseFloat(getComputedStyle(canvas).width),
canvasHeight: parseFloat(getComputedStyle(canvas).height),
railHeight: parseFloat(getComputedStyle(rail).height),
generationCount: document.querySelectorAll('.generation-band').length,
connectorCount: document.querySelectorAll('.lineage-connector').length,
documentWidth: document.documentElement.scrollWidth
}
})()`)
assert(stressMetrics.canvasWidth > 1400, 'T01 canvas width did not grow from 12 members per generation')
assert(stressMetrics.canvasHeight > 1000, 'T01 canvas height did not grow from 10 generations')
assert(Math.abs(stressMetrics.canvasHeight - stressMetrics.railHeight) <= 1, 'T01 generation rail did not follow the data-driven canvas height')
assert.strictEqual(stressMetrics.generationCount, 10, 'T01 generation rail did not derive all generations from data')
assert(stressMetrics.connectorCount > 30, 'T01 relationship connectors did not derive from parent data')
assert(stressMetrics.documentWidth <= 413, 'T01 stress tree escaped its scroll owner')
await open(send, '?genealogyId=1001', '.tree-state--tree .member-node')
await valueOf(send, "document.querySelector('.member-node')?.click()")
await valueOf(send, "document.querySelector('.sheet-member-action')?.click()")
await valueOf(send, "document.querySelector('.member-sheet .app-button')?.click()")
await waitFor(send, "location.href.includes('/pages/tree/t03-member-profile?genealogyId=1001&personId=')", 'T01 selected member did not open T03')
process.stdout.write('T01-TREE-STATE-RUNTIME-SMOKE PASS\n')
} finally {
try { await send('Emulation.clearDeviceMetricsOverride') } catch (_) {}
socket.close()
}
}