feat: 參考backend
This commit is contained in:
@@ -42,7 +42,7 @@ test('CLI runs doctor and scan against one prototype', async () => {
|
||||
assert.match(doctor.stdout, /ok prototype directory/)
|
||||
assert.match(contract, /Customer Portal/)
|
||||
assert.equal(spec.pageContract.title, null)
|
||||
assert.deepEqual(spec.pageContract.forms[0].fields[0], {
|
||||
assert.deepEqual(pick(spec.pageContract.forms[0].fields[0], ['name', 'label', 'type', 'required']), {
|
||||
name: 'email',
|
||||
label: 'Email',
|
||||
type: 'input',
|
||||
@@ -57,6 +57,10 @@ test('CLI runs doctor and scan against one prototype', async () => {
|
||||
assert.equal(appMap.guideSources[0].source, 'portal.md')
|
||||
})
|
||||
|
||||
function pick(object, keys) {
|
||||
return Object.fromEntries(keys.map((key) => [key, object[key]]))
|
||||
}
|
||||
|
||||
test('CLI help only exposes MVP commands', async () => {
|
||||
const result = await exec('node', [cli, 'help'])
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { buildApiCatalog, matchApiEndpoints } from '../src/lib/api-docs.js'
|
||||
import { buildMaintenanceContract } from '../src/lib/maintenance.js'
|
||||
import { buildAppMap, buildAppMapWithGuides, buildPageContract, extractRegions, inferRegionSpec, parsePrototypeGuide, summarizeHtml, validatePageContract } from '../src/lib/html.js'
|
||||
|
||||
test('summarizeHtml extracts user-visible contract evidence', () => {
|
||||
@@ -58,6 +60,37 @@ test('buildPageContract creates page-level UI contract', () => {
|
||||
assert.ok(contract.vuetifyComponents.includes('VTable'))
|
||||
})
|
||||
|
||||
test('buildPageContract infers legacy table field labels and action semantics', () => {
|
||||
const html = `
|
||||
<main>
|
||||
<form>
|
||||
<table>
|
||||
<tr><th>場地</th><td><select name="rom_id"><option>R001 大禮堂</option></select></td></tr>
|
||||
<tr><th>查詢起日</th><td><input name="startDate" maxlength="7"></td></tr>
|
||||
</table>
|
||||
<input type="button" value="查詢">
|
||||
</form>
|
||||
<table><tr><th>申請單號</th><th>操作</th></tr><tr><td>A001</td><td><input type="button" value="刪除"></td></tr></table>
|
||||
</main>
|
||||
`
|
||||
const contract = buildPageContract({
|
||||
page: 'query-room.html',
|
||||
source: '/prototype/query-room.html',
|
||||
html,
|
||||
regions: extractRegions(html),
|
||||
domSummary: summarizeHtml(html),
|
||||
screenshotPath: null
|
||||
})
|
||||
|
||||
assert.equal(contract.forms[0].fields[0].label, '場地')
|
||||
assert.deepEqual(contract.forms[0].fields[0].options, ['R001 大禮堂'])
|
||||
assert.equal(contract.forms[0].fields[1].maxLength, '7')
|
||||
assert.equal(contract.actions.find((action) => action.label === '查詢').actionType, 'search')
|
||||
assert.equal(contract.actions.find((action) => action.label === '刪除').scope, 'rowAction')
|
||||
assert.equal(contract.tables[0].role, 'searchTable')
|
||||
assert.equal(contract.tables[1].role, 'resultTable')
|
||||
})
|
||||
|
||||
test('validatePageContract reports evidence mismatches', () => {
|
||||
const report = validatePageContract({
|
||||
textSamples: ['Missing'],
|
||||
@@ -118,6 +151,110 @@ test('buildAppMap enriches routes with prototype markdown guide entries', () =>
|
||||
assert.equal(route.guide.targetView, 'RoomQueryView.vue')
|
||||
})
|
||||
|
||||
test('parsePrototypeGuide attaches checklist items to guide entries', () => {
|
||||
const guide = parsePrototypeGuide('venue.md', `
|
||||
# Venue
|
||||
|
||||
| 雛型檔 | 舊 JSP 來源 | 對應 Vue view |
|
||||
| --- | --- | --- |
|
||||
| [\`apply-room.html\`](venue/apply-room.html) | \`legacy/apply.jsp\` | \`RoomApplyView.vue\` |
|
||||
|
||||
### apply-room.html vs legacy
|
||||
|
||||
- [ ] 活動名稱必填
|
||||
- [ ] 使用節次:14 個 checkbox
|
||||
`)
|
||||
|
||||
assert.deepEqual(guide.entries[0].checklist, ['活動名稱必填', '使用節次:14 個 checkbox'])
|
||||
})
|
||||
|
||||
test('buildApiCatalog parses generic markdown API docs and matches routes', () => {
|
||||
const catalog = buildApiCatalog([
|
||||
{
|
||||
source: 'apps/backend/API.md',
|
||||
markdown: `
|
||||
## Orders Module
|
||||
|
||||
| 方法 | 路徑 | 說明 | 授權 |
|
||||
| --- | --- | --- | --- |
|
||||
| GET | \`/api/v1/orders\` | 查詢訂單 | Authorize |
|
||||
`
|
||||
},
|
||||
{
|
||||
source: 'apps/backend/API_Manual.md',
|
||||
markdown: `
|
||||
## 5. Orders API
|
||||
|
||||
### 5.1 新增訂單
|
||||
|
||||
\`\`\`text
|
||||
POST /api/v1/orders
|
||||
\`\`\`
|
||||
|
||||
Request:
|
||||
|
||||
\`\`\`json
|
||||
{ "orderName": "Demo", "items": [{ "sku": "A001", "qty": 1 }] }
|
||||
\`\`\`
|
||||
|
||||
Response:
|
||||
|
||||
\`\`\`json
|
||||
{ "orderNo": "O001" }
|
||||
\`\`\`
|
||||
|
||||
欄位規則:
|
||||
|
||||
| 欄位 | 規則 |
|
||||
| --- | --- |
|
||||
| \`orderName\` | 必填 |
|
||||
`
|
||||
}
|
||||
])
|
||||
|
||||
assert.ok(catalog.endpoints.some((endpoint) => endpoint.id === 'POST /api/v1/orders'))
|
||||
assert.equal(catalog.fieldRules[0].field, 'orderName')
|
||||
const matches = matchApiEndpoints({
|
||||
prototype: 'orders/apply.html',
|
||||
page: 'apply.html',
|
||||
module: 'orders',
|
||||
title: '訂單申請',
|
||||
guide: { targetView: 'OrderApplyView.vue' },
|
||||
evidence: { actions: ['存檔'], textSamples: ['訂單'] }
|
||||
}, catalog)
|
||||
assert.equal(matches[0].path, '/api/v1/orders')
|
||||
})
|
||||
|
||||
test('buildMaintenanceContract recommends generic templates from evidence', () => {
|
||||
const spec = buildSpec('/repo/prototype/orders/apply.html', `
|
||||
<main>
|
||||
<h1>訂單申請</h1>
|
||||
<form>
|
||||
<table>
|
||||
<tr><th>訂單名稱</th><td><input name="orderName"></td></tr>
|
||||
<tr><th>明細</th><td><select name="items"><option>A001</option></select></td></tr>
|
||||
</table>
|
||||
<input type="button" value="存檔">
|
||||
</form>
|
||||
</main>
|
||||
`)
|
||||
const route = {
|
||||
kind: 'feature-page',
|
||||
prototype: 'orders/apply.html',
|
||||
page: 'apply.html',
|
||||
guide: { targetView: 'OrderApplyView.vue' }
|
||||
}
|
||||
const contract = buildMaintenanceContract({
|
||||
route,
|
||||
spec,
|
||||
apiMatches: [{ method: 'POST', path: '/api/v1/orders', description: '新增訂單' }]
|
||||
})
|
||||
|
||||
assert.equal(contract.pageKind, 'application')
|
||||
assert.equal(contract.recommendedTemplate, 'master-detail-c')
|
||||
assert.equal(contract.dataModel.primaryEntity, 'OrderApply')
|
||||
})
|
||||
|
||||
function buildSpec(source, html) {
|
||||
const regions = extractRegions(html)
|
||||
const domSummary = summarizeHtml(html)
|
||||
|
||||
Reference in New Issue
Block a user