test: cover all screen states in T01-T10 catalogue (AEG-V16-023)
Type mandatory states and prevent READY or FORBIDDEN from drifting out of the shared screen matrix.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
export const screenTemplateCatalogue = Object.freeze([
|
||||
{ id: 'T01', name: '검색·목록형 CRUD', component: 'SearchListCrudPage', intendedUse: '상품·고객·권한·데이터 Run·추천 검토함', mandatoryStates: ['LOADING', 'EMPTY', 'WARN', 'ERROR', 'UNAUTHORIZED', 'PARTIAL'], mandatoryEvidence: ['filter-url', 'permission', 'export-auth'], antiPatterns: ['client-side-all-data', 'unbounded-export'] },
|
||||
{ id: 'T01', name: '검색·목록형 CRUD', component: 'SearchListCrudPage', intendedUse: '상품·고객·권한·데이터 Run·추천 검토함', mandatoryStates: ['READY', 'LOADING', 'EMPTY', 'WARN', 'ERROR', 'UNAUTHORIZED', 'FORBIDDEN', 'PARTIAL'], mandatoryEvidence: ['filter-url', 'permission', 'export-auth'], antiPatterns: ['client-side-all-data', 'unbounded-export'] },
|
||||
{ id: 'T02', name: '상세 조회형', component: 'DetailReadPage', intendedUse: '투자제안·상품·포트폴리오·백테스트 결과', mandatoryStates: ['LOADING', 'WARN', 'EXPIRED', 'READONLY', 'UNAUTHORIZED'], mandatoryEvidence: ['as-of', 'version-set', 'audit'], antiPatterns: ['mutable-evidence', 'hidden-version'] },
|
||||
{ id: 'T03', name: '등록·편집 Form', component: 'EditFormPage', intendedUse: '고객·IPS·비용표·권한·설정', mandatoryStates: ['DIRTY', 'CONFLICT', 'PROCESSING', 'ERROR'], mandatoryEvidence: ['zod', 'if-match', 'idempotency-key'], antiPatterns: ['silent-overwrite', 'pinia-form-cache'] },
|
||||
{ id: 'T04', name: 'Master-Detail', component: 'MasterDetailCrudPage', intendedUse: '고객-IPS·추천-항목·Watch-Stage·대사 Run-Break', mandatoryStates: ['LOADING', 'EMPTY', 'DIRTY', 'PARTIAL', 'CONFLICT'], mandatoryEvidence: ['route-selection', 'unsaved-guard', 'version'], antiPatterns: ['selection-only-local', 'detail-n-plus-one'] },
|
||||
|
||||
@@ -5,13 +5,13 @@ export interface ScreenTemplateDefinition {
|
||||
name: string
|
||||
component: string
|
||||
intendedUse: string
|
||||
mandatoryStates: readonly string[]
|
||||
mandatoryStates: readonly StandardScreenState[]
|
||||
mandatoryEvidence: readonly string[]
|
||||
antiPatterns: readonly string[]
|
||||
}
|
||||
|
||||
export const screenTemplateCatalogue: readonly ScreenTemplateDefinition[] = Object.freeze([
|
||||
{ id: 'T01', name: '검색·목록형 CRUD', component: 'SearchListCrudPage', intendedUse: '상품·고객·권한·데이터 Run·추천 검토함', mandatoryStates: ['LOADING','EMPTY','WARN','ERROR','UNAUTHORIZED','PARTIAL'], mandatoryEvidence: ['filter-url','permission','export-auth'], antiPatterns: ['client-side-all-data','unbounded-export'] },
|
||||
{ id: 'T01', name: '검색·목록형 CRUD', component: 'SearchListCrudPage', intendedUse: '상품·고객·권한·데이터 Run·추천 검토함', mandatoryStates: ['READY','LOADING','EMPTY','WARN','ERROR','UNAUTHORIZED','FORBIDDEN','PARTIAL'], mandatoryEvidence: ['filter-url','permission','export-auth'], antiPatterns: ['client-side-all-data','unbounded-export'] },
|
||||
{ id: 'T02', name: '상세 조회형', component: 'DetailReadPage', intendedUse: '투자제안·상품·포트폴리오·백테스트 결과', mandatoryStates: ['LOADING','WARN','EXPIRED','READONLY','UNAUTHORIZED'], mandatoryEvidence: ['as-of','version-set','audit'], antiPatterns: ['mutable-evidence','hidden-version'] },
|
||||
{ id: 'T03', name: '등록·편집 Form', component: 'EditFormPage', intendedUse: '고객·IPS·비용표·권한·설정', mandatoryStates: ['DIRTY','CONFLICT','PROCESSING','ERROR'], mandatoryEvidence: ['zod','if-match','idempotency-key'], antiPatterns: ['silent-overwrite','pinia-form-cache'] },
|
||||
{ id: 'T04', name: 'Master-Detail', component: 'MasterDetailCrudPage', intendedUse: '고객-IPS·추천-항목·Watch-Stage·대사 Run-Break', mandatoryStates: ['LOADING','EMPTY','DIRTY','PARTIAL','CONFLICT'], mandatoryEvidence: ['route-selection','unsaved-guard','version'], antiPatterns: ['selection-only-local','detail-n-plus-one'] },
|
||||
@@ -28,3 +28,4 @@ export function getScreenTemplate(id: ScreenTemplateId): ScreenTemplateDefinitio
|
||||
if (!template) throw new Error(`Unknown screen template: ${id}`)
|
||||
return template
|
||||
}
|
||||
import type { StandardScreenState } from '../contracts/screenContract'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getScreenTemplate, screenTemplateCatalogue } from '../catalogue'
|
||||
import type { StandardScreenState } from '../../contracts/screenContract'
|
||||
|
||||
describe('screen template catalogue', () => {
|
||||
it('defines all ten screen contracts without duplicate IDs or components', () => {
|
||||
@@ -16,6 +17,12 @@ describe('screen template catalogue', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('covers every standardized screen state across the ten templates', () => {
|
||||
const allStates: readonly StandardScreenState[] = ['READY','LOADING','EMPTY','WARN','ERROR','UNAUTHORIZED','FORBIDDEN','PARTIAL','DIRTY','CONFLICT','EXPIRED','READONLY','PROCESSING']
|
||||
const covered = new Set(screenTemplateCatalogue.flatMap(template => template.mandatoryStates))
|
||||
for (const state of allStates) expect(covered).toContain(state)
|
||||
})
|
||||
|
||||
it('resolves a template by stable ID', () => {
|
||||
expect(getScreenTemplate('T05').component).toBe('ApprovalWorkbenchPage')
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user