60 lines
4.7 KiB
JavaScript
60 lines
4.7 KiB
JavaScript
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
const root=process.cwd();const failures=[]
|
|
const read=p=>fs.readFileSync(path.join(root,p),'utf8')
|
|
const json=p=>JSON.parse(read(p))
|
|
const must=(text,needles,scope)=>needles.forEach(needle=>{if(!text.includes(needle))failures.push(`${scope} missing ${needle}`)})
|
|
|
|
const recipes=json('contracts/screens/kbx.screen-recipes.json')
|
|
const verification=json('generated/screen-recipe-verification-manifest.json')
|
|
const scenarios=json('contracts/testing/kbx.test-scenarios.json').scenarios
|
|
const screens=json('generated/screen-manifest.json')
|
|
const screenById=new Map(screens.map(screen=>[screen.id,screen]))
|
|
const scenarioById=new Map(scenarios.map(scenario=>[scenario.id,scenario]))
|
|
if(recipes.recipes.length!==9)failures.push(`expected 9 recipes, found ${recipes.recipes.length}`)
|
|
if(verification.recipes.length!==9)failures.push(`expected 9 recipe verification rows, found ${verification.recipes.length}`)
|
|
for(const recipe of recipes.recipes){
|
|
const profile=recipe.testProfile
|
|
if(!profile) { failures.push(`${recipe.code} missing testProfile`); continue }
|
|
for(const key of ['requiredScenarioKinds','requiredTags','requiredEvidence','requiredChecks'])if(!(profile[key]?.length>0))failures.push(`${recipe.code} testProfile missing ${key}`)
|
|
if(!profile.requiredScenarioKinds.includes('e2e'))failures.push(`${recipe.code} must require at least one E2E scenario`)
|
|
const row=verification.recipes.find(item=>item.code===recipe.code)
|
|
if(!row?.complete)failures.push(`${recipe.code} recipe verification incomplete: ${JSON.stringify(row)}`)
|
|
const canonical=recipe.canonicalScenarioIds.flatMap(id=>scenarioById.has(id)?[scenarioById.get(id)]:[])
|
|
const representativeE2e=canonical.find(scenario=>scenario.kind==='e2e'&&screenById.get(scenario.screenId)?.templateCode===recipe.code)
|
|
if(!representativeE2e)failures.push(`${recipe.code} needs an E2E scenario on a screen that actually uses ${recipe.code}`)
|
|
}
|
|
|
|
const typeContract=read('packages/kbx-contracts/src/screen.ts')
|
|
must(typeContract,['KbxScreenRecipeTestProfile','requiredScenarioKinds','requiredTags','requiredEvidence','requiredChecks'],'recipe verification type contract')
|
|
const generator=read('scripts/generate-screen-recipe-contracts.mjs')
|
|
must(generator,['screen-recipe-verification-manifest.json','missingScenarioKinds','missingTags','missingEvidence','kbxScreenRecipeVerificationCatalog'],'recipe verification generator')
|
|
|
|
for(const [file,id] of [
|
|
['tests/e2e/erp-item-master.spec.ts','scenario.erp.item-master.keyboard-recovery'],
|
|
['tests/e2e/common-operations.spec.ts','scenario.common.operations.queue-recovery'],
|
|
['tests/e2e/common-reconcile.spec.ts','scenario.common.reconcile.mismatch-recovery'],
|
|
]) must(read(file),[id,'scenarioTitle','data-kbx-surface'],file)
|
|
|
|
const scaffolder=read('scripts/create-kbx-screen.mjs')
|
|
must(scaffolder,['KbxGeneratedScreenTestPlan','.test-plan.ts','recipe.testProfile.requiredChecks','recipe.testProfile.requiredEvidence'],'recipe-driven generated test plan')
|
|
const scaffoldValidator=read('scripts/validate-scaffolder.mjs')
|
|
must(scaffoldValidator,['generated recipe test plan','test plan missing required check','test plan missing evidence'],'scaffolder test-plan governance')
|
|
|
|
const navigation=read('packages/kbx-contracts/src/navigation.ts')
|
|
must(navigation,['occurredAt: string','KbxHomeAttentionQueue','overflowCount','sourceCounts'],'Home attention queue contract')
|
|
const homeNav=read('packages/kbx-ui/src/shell/homeNavigation.ts')
|
|
must(homeNav,['buildKbxHomeAttentionQueue','b.occurredAt.localeCompare(a.occurredAt)','overflowCount','sourceCounts','Backward-compatible item-only view'],'Home attention deterministic queue')
|
|
const home=read('packages/kbx-ui/src/shell/KbxHomePage.vue')
|
|
must(home,['buildKbxHomeAttentionQueue','exactAttentionCount','attentionOverflowCount','전체 {{exactAttentionCount}}개 · 상위 {{attentionItems.length}}개 표시'],'Home exact attention count UX')
|
|
const uiIndex=read('packages/kbx-ui/src/index.ts')
|
|
if(!uiIndex.includes('buildKbxHomeAttentionQueue'))failures.push('public UI API missing buildKbxHomeAttentionQueue')
|
|
const homeTest=read('tests/unit/kbx-home-attention.contract.spec.ts')
|
|
must(homeTest,['newest-first within priority','overflowCount','sourceCounts'], 'Home attention queue unit contract')
|
|
|
|
const releaseAnalyzer=read('scripts/analyze-kbx-release.mjs')
|
|
must(releaseAnalyzer,['screen-recipe','testProfile','canonicalScenarioIds'],'release analyzer recipe verification tracking')
|
|
|
|
if(failures.length){console.error(failures.join('\n'));process.exit(1)}
|
|
console.log(`v36 recipe/home validation passed: recipes=${recipes.recipes.length}, recipe verification=9/9, canonical E2E representative coverage enforced, and Home attention exact-count/newest-first queue is active.`)
|