71 lines
7.4 KiB
JavaScript
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')
|