| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- // 探针:真实载荷(GAS 699 条同码)下的分页正确性 —— 逐页读数必须等于 domain 行读数。
- import { readFileSync } from 'node:fs'
- import { registerHooks } from 'node:module'
- import { parse, compileScript, compileTemplate } from '@vue/compiler-sfc'
- import { createRenderer, h, ref, nextTick } from '@vue/runtime-core'
- const root = new URL('../../../../../src/modules/cockpit/', import.meta.url)
- registerHooks({
- load(url, context, nextLoad) {
- if (url.endsWith('.json')) return { format: 'module', shortCircuit: true, source: `export default ${readFileSync(new URL(url), 'utf8')}` }
- if (url.endsWith('.vue')) {
- const { descriptor } = parse(readFileSync(new URL(url), 'utf8'), { filename: new URL(url).pathname })
- const script = compileScript(descriptor, { id: 'probe', genDefaultAs: '__sfc__' })
- const template = compileTemplate({
- source: descriptor.template.content, filename: new URL(url).pathname,
- id: 'probe', compilerOptions: { bindingMetadata: script.bindings },
- })
- return { format: 'module', shortCircuit: true, source: `${script.content}\n${template.code}\n__sfc__.render = render; export default __sfc__;` }
- }
- return nextLoad(url, context)
- },
- })
- const nodeOps = {
- createElement: (tag) => ({ tag, props: {}, children: [], parent: null, text: '', style: {}, addEventListener() {}, removeEventListener() {} }),
- createText: (text) => ({ tag: '#text', text, children: [], parent: null, props: {}, style: {}, addEventListener() {}, removeEventListener() {} }),
- createComment: (text) => ({ tag: '#comment', text, children: [], parent: null, props: {}, style: {}, addEventListener() {}, removeEventListener() {} }),
- setText: (n, t) => { n.text = t },
- setElementText: (n, t) => { n.text = t; n.children = [] },
- insert: (child, parent, anchor) => {
- if (child.parent) child.parent.children = child.parent.children.filter((c) => c !== child)
- child.parent = parent
- if (!parent) return
- if (anchor) { const i = parent.children.indexOf(anchor); parent.children.splice(i < 0 ? parent.children.length : i, 0, child) }
- else parent.children.push(child)
- },
- remove: (c) => { if (c.parent) c.parent.children = c.parent.children.filter((x) => x !== c); c.parent = null },
- parentNode: (n) => n.parent,
- nextSibling: (n) => (n.parent ? n.parent.children[n.parent.children.indexOf(n) + 1] || null : null),
- querySelector: () => null,
- setScopeId: () => {},
- patchProp: (el, k, _p, next) => { el.props[k] = next },
- }
- const { createApp } = createRenderer(nodeOps)
- const { default: PanelBlocks } = await import(new URL('components/PanelBlocks.vue', root))
- const { operationPanel, toOperatingInformation } = await import(new URL('domain/infrastructurePanels.js', root))
- const { section } = await import(new URL('domain/sectionState.js', root))
- function findAll(node, predicate, acc = []) {
- if (!node) return acc
- if (predicate(node)) acc.push(node)
- for (const c of node.children || []) findAll(c, predicate, acc)
- return acc
- }
- function textOf(node) {
- if (!node) return ''
- // 元素若被 setElementText 直接落文本(Vue 对纯文本元素走这条路),文本在 node.text 上。
- const own = node.tag === '#text' ? node.text : (typeof node.text === 'string' ? node.text : '') + (typeof node.props?.text === 'string' ? node.props.text : '')
- return own + (node.children || []).map(textOf).join('')
- }
- const raw = JSON.parse(readFileSync(new URL('../t51/oi-GAS.json', import.meta.url), 'utf8')).data
- const data = toOperatingInformation(raw)
- const view = operationPanel(data, section('success', { data, loaded: true }), { scope: 'all' })
- const rows = view.blocks[1].rows
- console.log('row keys distinct:', new Set(rows.map((r) => r.key)).size, '/', rows.length)
- const viewRef = ref(view)
- const app = createApp({ render: () => h(PanelBlocks, { view: viewRef.value }) })
- app.config.warnHandler = () => {}
- const container = nodeOps.createElement('#root')
- app.mount(container)
- function rendered() {
- return findAll(container, (n) => n.props && 'data-item' in n.props).map((card) => ({
- key: card.props['data-item'],
- title: textOf(findAll(card, (n) => n.props && 'class' in n.props && String(n.props['class']).includes('panel-item__title'))[0]),
- meta: textOf(findAll(card, (n) => n.props && 'class' in n.props && String(n.props['class']).includes('panel-item__meta'))[0]),
- }))
- }
- let bad = 0
- let pagesChecked = 0
- for (let page = 1; page <= 70; page += 1) {
- const btn = findAll(container, (n) => String(n.props?.['data-page-number']) === String(page))[0]
- if (!btn) { console.log('page button missing', page); bad += 1; continue }
- btn.props.onClick()
- await nextTick()
- const got = rendered()
- const want = rows.slice((page - 1) * 10, page * 10)
- pagesChecked += 1
- if (got.length !== want.length) { console.log(`page ${page}: rendered ${got.length} want ${want.length}`); bad += 1; continue }
- for (let i = 0; i < want.length; i += 1) {
- if (got[i].key !== want[i].key || got[i].meta !== want[i].meta.join(' · ') || got[i].title !== want[i].title) {
- console.log(`page ${page} row ${i} mismatch:\n got ${JSON.stringify(got[i])}\n want ${JSON.stringify({ key: want[i].key, title: want[i].title, meta: want[i].meta.join(' · ') })}`)
- bad += 1
- break
- }
- }
- }
- console.log(`pages checked: ${pagesChecked}, mismatches: ${bad}`)
- // 计数行 + 到边禁用
- const state = findAll(container, (n) => n.props && 'data-page-state' in n.props)[0]
- console.log('state on last page:', textOf(state))
- const prev = findAll(container, (n) => 'data-page-prev' in (n.props || {}))[0]
- const next = findAll(container, (n) => 'data-page-next' in (n.props || {}))[0]
- console.log('prev disabled:', prev?.props.disabled, 'next disabled:', next?.props.disabled)
|