Files
html-transform/test/html.test.js
T
2026-05-07 14:46:14 +08:00

138 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import test from 'node:test'
import assert from 'node:assert/strict'
import { buildAppMap, buildAppMapWithGuides, buildPageContract, extractRegions, inferRegionSpec, parsePrototypeGuide, summarizeHtml, validatePageContract } from '../src/lib/html.js'
test('summarizeHtml extracts user-visible contract evidence', () => {
const summary = summarizeHtml(`
<title>Orders</title>
<main>
<h1>Orders</h1>
<form><label>Email</label><input name="email" required><button>Save</button></form>
</main>
`)
assert.equal(summary.title, 'Orders')
assert.deepEqual(summary.labels, ['Email'])
assert.deepEqual(summary.buttons, ['Save'])
assert.equal(summary.inputs[0].name, 'email')
assert.equal(summary.inputs[0].required, true)
})
test('extractRegions falls back to a single page region', () => {
const regions = extractRegions('<h1>Hello</h1><p>World</p>')
assert.equal(regions.length, 1)
assert.equal(regions[0].id, 'page-1')
})
test('inferRegionSpec maps forms to VForm', () => {
const [region] = extractRegions('<main><form><input name="q"><button>Search</button></form></main>')
const spec = inferRegionSpec(region)
assert.equal(spec.vuetifyComponent, 'VForm')
assert.deepEqual(spec.uiContract.primaryActions, ['Search'])
})
test('buildPageContract creates page-level UI contract', () => {
const html = `
<main>
<h1>Orders</h1>
<form><label>Email</label><input name="email" required><button>Search</button></form>
<table><thead><tr><th>Status</th></tr></thead><tbody><tr><td>Pending</td></tr></tbody></table>
</main>
`
const regions = extractRegions(html)
const domSummary = summarizeHtml(html)
const contract = buildPageContract({
page: 'orders.html',
source: '/prototype/orders.html',
html,
regions,
domSummary,
screenshotPath: '.ht/cache/prototype/orders/desktop-default.png'
})
assert.equal(contract.page, 'orders.html')
assert.equal(contract.forms[0].fields[0].required, true)
assert.deepEqual(contract.tables[0].headers, ['Status'])
assert.ok(contract.vuetifyComponents.includes('VTable'))
})
test('validatePageContract reports evidence mismatches', () => {
const report = validatePageContract({
textSamples: ['Missing'],
actions: [{ label: 'Save' }],
forms: [],
vuetifyComponents: ['VContainer']
}, {
textSamples: ['Orders'],
buttons: ['Search'],
labels: []
})
assert.equal(report.requiresHumanReview, true)
assert.match(report.warnings.join('\n'), /Missing/)
assert.match(report.warnings.join('\n'), /Save/)
})
test('buildAppMap classifies auth, shell references, and feature pages', () => {
const prototypeDir = '/repo/prototype'
const specs = [
buildSpec('/repo/prototype/portal/login.html', '<main><input type="password" name="pwd"><button>登入</button></main>'),
buildSpec('/repo/prototype/portal/app-layout.html', '<main><button>隱藏選單</button><button>登  出</button></main>'),
buildSpec('/repo/prototype/venue/applications-list.html', '<main><h1>我的申請紀錄</h1><table><tr><th>申請單號</th></tr></table><button>查詢</button></main>')
]
const appMap = buildAppMap(specs, prototypeDir)
assert.equal(appMap.routes.find((route) => route.prototype === 'portal/login.html').kind, 'auth')
assert.equal(appMap.routes.find((route) => route.prototype === 'portal/login.html').layout, 'template-auth')
assert.equal(buildAppMap([
buildSpec('/repo/prototype/portal/forget-password.html', '<main><h1>忘記密碼</h1><input name="idno"><button>發送至信箱</button></main>')
], prototypeDir).routes[0].targetRole, 'forgot-password')
assert.equal(appMap.routes.find((route) => route.prototype === 'portal/app-layout.html').kind, 'legacy-shell-reference')
assert.equal(appMap.routes.find((route) => route.prototype === 'portal/app-layout.html').usePrototypeContent, false)
assert.equal(appMap.routes.find((route) => route.prototype === 'venue/applications-list.html').kind, 'feature-page')
assert.equal(appMap.routes.find((route) => route.prototype === 'venue/applications-list.html').layout, 'template-app')
assert.equal(appMap.modules.find((module) => module.name === 'venue').kind, 'feature-module')
})
test('buildAppMap enriches routes with prototype markdown guide entries', () => {
const prototypeDir = '/repo/prototype'
const guide = parsePrototypeGuide('venue.md', `
# Venue 雛型導覽
| 雛型檔 | 舊 JSP 來源 | 舊 PB NVO 來源 | 對應 Vue viewM9 |
| --- | --- | --- | --- |
| [\`query-room.html\`](venue/query-room.html) | \`zte_pro/zte451_02.jsp\` + \`zte451_02_1.jsp\` | \`n_zte451.of_zte451_02\` / \`of_zte451_02_1\` | \`RoomQueryView.vue\` |
`)
const appMap = buildAppMapWithGuides([
buildSpec('/repo/prototype/venue/query-room.html', '<main><h1>全校場地查詢</h1><button>查詢</button></main>')
], prototypeDir, [guide])
const route = appMap.routes[0]
assert.equal(appMap.guideSources[0].source, 'venue.md')
assert.equal(route.evidence.prototypeGuide, 'venue.md')
assert.equal(route.guide.legacyJsp, 'zte_pro/zte451_02.jsp + zte451_02_1.jsp')
assert.equal(route.guide.legacyPb, 'n_zte451.of_zte451_02 / of_zte451_02_1')
assert.equal(route.guide.targetView, 'RoomQueryView.vue')
})
function buildSpec(source, html) {
const regions = extractRegions(html)
const domSummary = summarizeHtml(html)
const page = source.split('/').at(-1)
return {
source,
page,
pageContract: buildPageContract({
page,
source,
html,
regions,
domSummary,
screenshotPath: `.ht/cache/prototype/${page.replace(/\.html$/, '')}/desktop-default.png`
})
}
}