Files
KArtSell.Aegis/docs/Design/kbx-foundation-v60-status-canonical-contract-hardening/scripts/validate-fe-recovery-workbench-v55.mjs
T
kjh2064 3f293d8aa8
deploy / deploy (push) Successful in 1m52s
deploy / notify (push) Successful in 1s
V13-FE-006: consolidate approved UI and contract hardening
2026-08-13 02:41:00 +09:00

71 lines
7.4 KiB
JavaScript

import fs from 'node:fs'
const read = file => fs.readFileSync(file, 'utf8')
const expect = (condition, message) => {
if (!condition) {
console.error(`FAIL: ${message}`)
process.exit(1)
}
console.log(`PASS: ${message}`)
}
const excel = read('packages/kbx-ui/src/components/KbxExcelImport.vue')
for (const step of ["key: 'file'", "key: 'mapping'", "key: 'validation'", "key: 'preview'", "key: 'commit'", "key: 'result'"]) {
expect(excel.includes(step), `T08 Vue import owns ${step.replace("key: '", '').replace("'", '')} stage`)
}
expect(excel.includes("case 'validated':\n return 'preview'") && excel.includes("case 'committing':\n return 'commit'"), 'T08 maps server status to explicit preview and commit work surfaces')
expect(excel.includes('data-kbx-surface="preview"') && excel.includes('반영 미리보기'), 'T08 exposes a dedicated preview surface before mutation')
expect(excel.includes('focusNextError') && excel.includes('data-import-error-index') && excel.includes('첫 오류'), 'T08 preview supports deterministic first/next validation-error focus')
expect(excel.includes('신규/수정 구분은 서버 Preview가 제공하는 경우에만 표시합니다.'), 'T08 avoids inventing projected create/update counts when server preview does not supply them')
expect(excel.includes('같은 파일을 다시 제출하지 마세요'), 'T08 commit surface warns against duplicate user submission during background mutation')
const workQueue = read('packages/kbx-ui/src/components/KbxWorkQueuePage.vue')
for (const prop of ['queueTitle?:string','queueDescription?:string','queueCount?:number','queueCountUnit?:string']) {
expect(workQueue.includes(prop), `T06 compatibility wrapper preserves ${prop}`)
}
expect(workQueue.includes(':queue-title="queueTitle"') && workQueue.includes('#queue-actions'), 'T06 wrapper forwards work-surface title/count/actions instead of silently dropping them')
const operations = read('apps/web/src/modules/common/operations/OperationsQueuePage.vue')
expect(operations.includes("label:'표시 긴급'") && operations.includes("label:'표시 미지정'") && operations.includes("label:'표시 60분+'"), 'T06 distinguishes visible-page pressure metrics from authoritative total count')
expect(operations.includes('queue-title="예외 업무 Queue"') && operations.includes(':queue-count="query.data.value?.totalCount ?? 0"'), 'real T06 queue exposes authoritative total volume in the standard heading')
expect(operations.includes('function openNextUrgent') && operations.includes('label="다음 긴급건"'), 'real T06 lets operators jump directly to the next urgent exception without rescanning the grid')
expect(operations.includes('function showMine') && operations.includes("owner: 'mine'"), 'real T06 provides an explicit mine-work shortcut using the existing server filter contract')
const reconcile = read('apps/web/src/modules/common/reconcile/ReconcilePage.vue')
expect(reconcile.includes('KbxQuickFilterBar') && reconcile.includes("{key:'mismatch',label:'불일치'") && reconcile.includes("{key:'pending',label:'확인중'") && reconcile.includes("{key:'resolved',label:'해결'"), 'T07 exposes explicit all/mismatch/pending/resolved quick-result navigation')
expect(reconcile.includes('@row-double-clicked="detail = $event"') && reconcile.includes('KbxDrawer') && reconcile.includes('Expected') && reconcile.includes('Actual') && reconcile.includes('Difference'), 'T07 keeps row comparison context in a dedicated evidence drawer')
expect(reconcile.includes("permissionHost?.has('common.operations.create')") && reconcile.includes(':disabled="!canCreateException"'), 'T07 custom detail action respects frontend permission expression while backend remains authoritative')
expect(reconcile.includes('원천값을 화면에서 직접 덮어쓰지 않습니다.'), 'T07 resolution policy forbids client-side reconciliation truth mutation')
const pickingVm = read('apps/web/src/modules/wms/picking/useWmsPicking.ts')
expect(pickingVm.includes('lastConfirmedScan') && pickingVm.includes('command.barcode') && pickingVm.includes('item.command.barcode'), 'T09 records only server-confirmed scans, including idempotent retry confirmation')
expect(pickingVm.includes("'작업을 시작하지 못했습니다.'") && pickingVm.includes("'예외를 등록하지 못했습니다.'"), 'T09 start/exception mutation failures now produce actionable operator feedback')
const picking = read('apps/web/src/modules/wms/picking/WmsPickingPage.vue')
expect(picking.includes('data-kbx-surface="scan-truth"') && picking.includes('최근 서버 확인') && picking.includes('중복 스캔 금지'), 'T09 explicitly separates scan readiness from last authoritative server confirmation')
expect(picking.includes(':disabled="!vm.online.value || vm.pendingCommands.value > 0 || vm.scanning.value"'), 'T09 direct quantity action is fail-closed during offline/pending/server-confirmation states')
const templateManifest = read('packages/kbx-ui/src/registry/templateManifest.ts')
expect(templateManifest.includes('파일→매핑→검증→미리보기→반영→결과 6단계 고정'), 'T08 governance now matches the six-stage Business UX standard')
expect(templateManifest.includes('Queue 제목·전체건수·표시건수 구분'), 'T06 governance distinguishes authoritative total and visible pressure')
expect(templateManifest.includes('행 단위 근거 Detail로 비교 Context 유지'), 'T07 governance requires evidence-preserving row detail')
expect(templateManifest.includes('최근 서버 확인 Scan과 로컬 입력 구분'), 'T09 governance requires authoritative scan truth visibility')
const manifest = read('packages/kbx-ui/src/registry/componentManifest.ts')
expect(manifest.includes("name: 'KbxExcelImport'") && manifest.includes("version: '1.2.0'"), 'six-stage T08 business component change is versioned')
expect(manifest.includes("name: 'KbxWorkQueuePage'") && manifest.includes("version: '1.7.0'"), 'T06 wrapper contract fix is versioned')
const referenceHtml = read('apps/web/fe-reference/index.html')
const referenceJs = read('apps/web/fe-reference/kbx-fe.js')
const referenceCss = read('apps/web/fe-reference/kbx-fe.css')
expect(referenceHtml.includes('FE Reference Lab v55'), 'static HTML reference identifies the v55 recovery-workbench build')
for (const directive of ["connect-src 'self'", "frame-src 'none'", "worker-src 'none'", "object-src 'none'", "frame-ancestors 'none'"]) {
expect(referenceHtml.includes(directive), `static reference CSP preserves ${directive}`)
}
expect(referenceJs.includes("const steps=['파일','매핑','검증','미리보기','반영','결과']"), 'static HTML/JavaScript T08 retains the same six-stage import grammar')
expect(referenceJs.includes('importErrorIndex') && referenceJs.includes('data-import-error-row') && referenceJs.includes('importNextError'), 'static T08 mirrors validation error focus navigation')
expect(referenceJs.includes('lastConfirmedScanValue') && referenceJs.includes('최근 서버 확인') && referenceJs.includes('state.lastConfirmedScanValue=normalized'), 'static T09 distinguishes last confirmed scan from buffered/invalid input')
expect(referenceCss.includes('.import-error-nav') && referenceCss.includes('.scan-truth'), 'static CSS provides concrete recovery/scan-truth work surfaces')
expect(!referenceCss.includes('var(--focus)') && referenceCss.includes('@media(forced-colors:active)'), 'new static styles use defined theme variables and retain high-contrast support')
console.log('PASS: FE recovery workbench hardening v55 contract')