Files
KArtSell.Aegis/docs/Design/kbx-foundation-v60-status-canonical-contract-hardening/scripts/validate-fe-interaction-closure-v56.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

59 lines
6.3 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 dialog=read('packages/kbx-ui/src/components/KbxDialog.vue')
const drawer=read('packages/kbx-ui/src/components/KbxDrawer.vue')
for(const [name,source] of [['Dialog',dialog],['Drawer',drawer]]){
expect(source.includes('restoreTarget')&&source.includes("@hide=\"restoreFocus\"")&&source.includes('target.isConnected'),`${name} restores focus only to a still-valid invoking control after overlay close`)
expect(source.includes("target.focus({preventScroll:true})"),`${name} restores focus without unexpected viewport jumps`)
}
expect(dialog.includes('restoreFocus?:boolean')&&dialog.includes("!props.restoreFocus"),'Dialog supports an explicit owner-managed focus lifecycle escape hatch')
const lookupDialog=read('packages/kbx-ui/src/components/KbxLookupDialog.vue')
expect(lookupDialog.includes(':restore-focus="false"'),'Lookup disables generic Dialog restore because successful selection intentionally advances to the next field')
const excel=read('packages/kbx-ui/src/components/KbxExcelImport.vue')
expect(excel.includes('watch(stage, async (current, previous)')&&excel.includes('data-kbx-stage-heading')&&excel.includes("heading?.focus({ preventScroll: true })"),'T08 transfers keyboard focus to the new six-stage work surface after DOM replacement')
expect((excel.match(/data-kbx-stage-heading/g)??[]).length>=6,'T08 gives every import stage an explicit focus landing')
const operationsApi=read('apps/web/src/modules/common/operations/operationsApi.ts')
expect(operationsApi.includes('ClaimWorkItemsResponse')&&operationsApi.includes('claimedCount')&&operationsApi.includes('skippedCount'),'T06 FE consumes the authoritative bulk claim result instead of treating HTTP 200 as all-success')
const operations=read('apps/web/src/modules/common/operations/OperationsQueuePage.vue')
expect((operations.includes('bulkReceipt')||operations.includes('actionReceipt'))&&(operations.includes('data-kbx-surface="bulk-receipt"')||operations.includes('data-kbx-surface="authoritative-action-receipt"'))&&operations.includes('담당 지정이 일부 처리되었습니다.'),'T06 renders partial bulk outcome as a persistent inline receipt')
expect(operations.includes('내 업무 보기')&&operations.includes('최신 상태 조회'),'T06 partial bulk receipt exposes concrete recovery actions')
const retryQueue=read('apps/web/src/modules/wms/picking/wmsRetryQueue.ts')
expect(retryQueue.includes("STORAGE_PREFIX = 'kbx:wms:safe-retry:v2:'")&&retryQueue.includes('scopeHash(scope')&&retryQueue.includes('scope&&scope!==ANONYMOUS_SCOPE?localStorage:sessionStorage'),'T09 safe-retry storage is tenant/user-scope partitioned and anonymous contexts remain session-only')
expect(retryQueue.includes("localStorage.removeItem(LEGACY_STORAGE_KEY)")&&retryQueue.includes('Never replay it'),'T09 retires the old unscoped shared-PDA retry queue instead of replaying it')
expect(retryQueue.includes('MAX_QUEUE_ITEMS = 32')&&retryQueue.includes('.filter(validItem)'),'T09 bounds and validates browser-persisted retry commands before replay')
const pickingVm=read('apps/web/src/modules/wms/picking/useWmsPicking.ts')
expect(pickingVm.includes('KbxScreenPreferenceScopeKey')&&pickingVm.includes('retryScope.value'),'T09 binds retry persistence to the same tenant/user UI scope used by the application shell')
const app=read('apps/web/src/App.vue')
expect(/app-version="v\d+-fe-[^"]+"/.test(app) && !app.includes('v51-fe-presentation-workbench'),'Runtime feedback/telemetry metadata carries a current versioned FE build identity instead of stale v51')
const demo=read('apps/web/src/http/demo/installFrontendDemoApi.ts')
expect(demo.includes('claimedCount')&&demo.includes('skippedCount'),'Demo API mirrors the real partial bulk claim response contract')
expect(demo.includes("targetField:'orderNo'")&&demo.includes("targetField:'warehouseCode'")&&demo.includes("targetField:'orderQty'"),'Demo T08 supplies a complete required-field mapping so browser Golden Flow can actually reach validation')
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(/KBX FE Reference Lab v\d+ ·/.test(referenceHtml) && !referenceHtml.includes('KBX FE Reference Lab v51'),'Static HTML reference carries a current versioned FE build identity')
expect(referenceJs.includes('importFocusStep')&&referenceJs.includes('data-import-stage-heading')&&referenceJs.includes('target?.focus({preventScroll:true})'),'Static HTML/JavaScript T08 mirrors stage-focus continuity without stealing focus on progress polling rerenders')
expect(!referenceJs.includes("row.actual=row.expected")&&referenceJs.includes('원천 OMS/WMS 값은 이 화면에서 직접 덮어쓰지 않습니다.'),'Static T07 no longer fabricates reconciliation success by mutating reference truth client-side')
expect(referenceCss.includes('.import-stage-focus:focus-visible')&&referenceCss.includes('@media(forced-colors:active)'),'Static import stage focus remains visible in normal and forced-color themes')
const importE2e=read('tests/e2e/oms-order-import.spec.ts')
expect(importE2e.includes("Buffer.from('KBX E2E demo workbook')")&&!importE2e.includes('orders-valid-and-invalid.xlsx\'\)'), 'T08 E2E no longer depends on a missing binary workbook fixture')
expect(importE2e.includes('toBeFocused()')&&importE2e.includes("name:'반영 결과'"),'T08 E2E asserts stage-focus continuity through terminal result')
const keyboardE2e=read('tests/e2e/kbx-keyboard-regression.spec.ts')
expect(keyboardE2e.includes('lookup Escape returns focus')&&keyboardE2e.includes('await expect(customerCode).toBeFocused()'),'Keyboard regression explicitly covers Lookup Esc focus restoration')
const manifest=read('packages/kbx-ui/src/registry/componentManifest.ts')
expect(manifest.includes("name: 'KbxExcelImport'")&&manifest.includes("version: '1.3.0'"),'T08 focus lifecycle change is component-versioned')
expect(manifest.includes("name: 'KbxDialog'")&&manifest.includes("version:'1.1.0'")&&manifest.includes("name: 'KbxDrawer'")&&manifest.includes("version:'1.1.0'"),'Overlay focus lifecycle changes are component-versioned')
console.log('PASS: FE interaction closure hardening v56 contract')