diff --git a/docs/sw/sw-test-regression-baseline-2026-10-20.md b/docs/sw/sw-test-regression-baseline-2026-10-20.md new file mode 100644 index 0000000..7e23f19 --- /dev/null +++ b/docs/sw/sw-test-regression-baseline-2026-10-20.md @@ -0,0 +1,169 @@ +# SW 테스트 정합성 개선 — Storybook 회귀 기준선 유지 (2026-10-20) + +> 범위: 테스트 코드와 이 문서만 다룹니다. `@hm-design/sw` 제품 코드·공개 API·Storybook Story·디자인은 바꾸지 않았습니다. +> 브랜치 `fix/test-home-menu-integration`, 기준 main `553e4df`(Merge `feature/sw-package`). 과거 QA 증거 폴더는 수정하지 않았습니다. +> 실행 환경: Node.js 22.23.2 / npm 10.9.8, Chrome 155.0.8059.85(headless), Playwright 1.62.1(Codex 런타임, `playwright-runtime.mjs` 탐색 순서 5번). + +## 1. 결과 요약 + +같은 `storybook-static`(Node 22.23.2로 `npm run build-storybook`, 패키지 build 포함)에서 수정 전 테스트(`553e4df` 버전)와 현재 테스트를 각각 실행했습니다. + +| 테스트 | 수정 전 (`553e4df`) | 수정 후 (`20ed2f2`) | 관련 Commit | +|---|---|---|---| +| `home-menu-integration.browser.mjs` | 22/23 (FAIL 1) | **23/23** | `7fd7342` | +| `overview.browser.mjs` | 51/54 (FAIL 3) | **54/54** | `522ecbd`, `20ed2f2` | +| `overview.browser.mjs … overlays` | 17/20 (같은 FAIL 3) | **20/20** | `522ecbd`, `20ed2f2` | +| `storybook-unused-removal.browser.mjs` | 88/93 (FAIL 5) | **93/93** | `20ed2f2` | + +모든 FAIL은 과거 시점 값에 고정된 단언이었고, 기능(브라우저 동작) 실패는 없었습니다. 수정은 단언을 지우거나 건너뛰지 않고, 기준선은 그대로 둔 채 **승인된 이후 변경만** 명시적으로 허용하는 방식입니다. + +## 2. Home Menu 기존 실패 1건 (`7fd7342`) + +- 실패 항목: `without homeMenu: logo click calls onHomeClick, renders no menu, no aria-expanded` +- 실제 오류: `sw-header--overview`의 로고를 누르자 `[SWHeader Overview] homeMenuOpen(true, home)`이 기록되고 홈 메뉴가 열림. +- 원인: `170a5c2`(홈 메뉴 Header 통합, 사용자 요청)부터 Overview가 `homeMenu`를 전달합니다(`docs/sw/sw-header-usage.md` "Overview의 로고도 같은 홈 메뉴"). 테스트가 "homeMenu 없는 Header"의 대상으로 Overview를 계속 열고 있었습니다. +- 해결: 대상만 `sw-header--logo-slot`의 첫 Header(`figmaArgs` + `callbacks`, `homeMenu` 없음)로 바꾸고, Callback 판정 문자열을 그 Header의 로그(`[SWHeader] home`)로 맞췄습니다. "메뉴 없음 · `aria-expanded` 없음 · `onHomeClick`만 호출" 단언은 그대로입니다. + +## 3. Overview 기존 실패 3건 (`522ecbd`, `20ed2f2`) + +기준선은 `50bf5bd`와 그 시점의 `before-index.json`으로 유지합니다. + +| 실패 항목(수정 전) | 원인 | 해결 | +|---|---|---| +| `24 retained Story IDs, names and tags; only approved Dropdown retirements` | 이후 승인된 삭제(속성창 5개, 미사용 9개), Popup 이동, `verification` 태그, 홈 메뉴·통합 개선 Story 추가로 SW/Header가 20개가 됨 | 24개에서 승인된 삭제·이동을 빼고 승인된 태그를 적용한 뒤 승인된 추가 ID를 더한 **20개**와 ID·이름·태그가 정확히 같아야 PASS. 이동한 Story는 title이 `SW/Popup`인지 확인 | +| `retained non-Overview declarations byte-identical` | `ResponsiveMore`(170a5c2), `WindowMoveMotion`·`KGuide`(2b0e1a2) 변경, 미사용 Story 선언 삭제, `PropertyPanelCompare`의 `sw-property-panel.stories.tsx` 이동 | 변경 선언은 **승인 Commit의 내용과 byte 단위로 같을 때만** 허용, 삭제는 승인된 삭제 ID일 때만, 이동 선언은 새 파일에서 승인 Commit 내용과 비교. 그 밖의 선언은 계속 `50bf5bd`와 동일해야 함 | +| `package production source untouched except the integrated-improvement files` | `50bf5bd` 이후 승인되어 main에 병합된 `packages/sw/src` 변경 | `approvedMainCommit`(553e4df)과 비교해 통합 개선 파일 외 차이가 없어야 PASS. `50bf5bd` 이후 차이 개수는 `sinceBaseline`으로 기록만 함 | + +`20ed2f2`에서는 위 목록(태그·추가 ID·승인 선언·이동 선언)을 `storybook-inventory.mjs`로 옮겨 Overview와 미사용 검사가 같은 목록을 쓰게 했습니다. Overlays 모드는 같은 앞부분 검사를 공유하므로 같은 3건이 함께 해결됩니다. + +## 4. Storybook 미사용 검사 기존 실패 5건 (`20ed2f2`) + +기준선은 `c8e5fbe`의 `full-index-before.json`으로 유지합니다. + +| 실패 항목(수정 전) | 해결 | +|---|---| +| `19 -> 10 retained SW/Header Stories, 338 -> 329 total plus only the integrated-improvement Stories` | 338 → 329와 19 → 10은 그대로 단언. 이후 추가는 `approvedAddedStoryIds`(통합 개선·홈 메뉴·Footer·Viewer·Theme)와 **정확히 같은 집합**일 때만 PASS. 승인되지 않은 추가(`unapproved`)·누락(`missing`)을 결과에 기록 | +| `retained SW IDs, names, titles, tags and order unchanged` | 유지 10개의 ID·이름·title·tags·순서를 비교하되, 승인된 `verification` 태그와 Popup 이동만 반영 | +| `every non-removed index entry identical (docs and other packages included)` | SW 밖 항목은 값·순서 모두 동일, 유지 SW 항목은 승인 변경만 반영해 동일, SW 블록은 Center Graphic 바로 뒤에 연속(2b0e1a2 storySort) | +| `retained exports (Stories and ComposedDemo) byte-identical` | 변경·추가 Export는 승인 Commit 내용과 같을 때만, 이동 Export는 새 파일에서 비교, 나머지는 `c8e5fbe`와 동일 | +| `other packages, Overview, Dropdown CSF, assets and Storybook config untouched` | 같은 경로가 `approvedMainCommit`과 달라서는 안 됨(통합 개선 파일 제외). `c8e5fbe` 이후 차이는 `sinceBaseline`으로 기록 | + +## 5. 관련 Commit + +| Commit | 내용 | 파일 | +|---|---|---| +| `7fd7342` | test(sw): align home menu integration with current stories | `home-menu-integration.browser.mjs` | +| `522ecbd` | test(sw): align overview regression checks with approved changes | `overview.browser.mjs` | +| `20ed2f2` | test(sw): align storybook inventory checks with approved changes | `storybook-inventory.mjs`, `overview.browser.mjs`, `storybook-unused-removal.browser.mjs` | + +### 5.1 승인 목록에 반영된 Story 변경 이력 + +모두 사용자 요청으로 진행되어 main `553e4df`에 포함된 Commit입니다(`git merge-base --is-ancestor`로 확인). + +| Commit | 변경 | `storybook-inventory.mjs` 항목 | +|---|---|---| +| `50bf5bd` | 정리 1단계: Story 5개 삭제 | `retiredStage1Ids` | +| `9482d45`, `c705a1f` | Dropdown 4개 통합, `composed-controls` → Dropdown(SW/Header 바로 아래) | `retiredDropdownIds`, `retainedInventory` | +| `c8e5fbe` | 속성창 중복 5개 삭제, 4개 비교로 통합 | `retiredPropertyPanelIds` | +| `5faa1f1` | 미사용 SW Header 시연 9개 삭제 | `retiredUnusedDemoIds` | +| `55179b4`, `e8409ef` | 통합 개선 0.2.0 Story 추가, Options를 SW/Header로 통합 | `integratedStoryIds` | +| `170a5c2` 등 홈 메뉴 병합 | SW/Home Menu → SW/Header(`home-menu`, `home-menu-*`), Overview·ResponsiveMore 변경 | `homeMenuSidebarIds`, `movedHomeMenuIds`, `approvedHeaderDeclarations` | +| `6cf8854`, `7f42923`, `41e963a` | SW Footer, Viewer, Theme Story 추가 | `footerStoryIds`, `viewerStoryIds`, `themeStoryIds` | +| `7f42923` | SW/Tool Story 메뉴 삭제(`SWToolButton`·`SWToolGroup`은 공개 유지) | `retiredToolIds` | +| `126a35d` | Footer `기본 Footer`·`States` Story 삭제(전체 Footer 섹션으로 통합) | `retiredFooterIds` | +| `2b0e1a2` | 사이드바 단순화: `verification` 태그 2개, Property Panel 비교 SW/Popup 이동, 홈 메뉴 Figma 비교·중복 Variant 6개 삭제 | `verificationTaggedIds`, `movedToPopupIds`, `movedHeaderDeclarations`, `retiredHomeMenuCompareIds` | + +## 6. 변조 검증 + +수정한 단언이 "무조건 PASS"가 아닌지 확인하려고 현재 테스트(`20ed2f2`)에 일부러 승인되지 않은 변경을 넣고 실행한 뒤 원상 복구했습니다. 복구는 백업 파일로 했고 매번 `git diff`가 비어 있음(소스) 또는 `index.json` SHA-256 일치(빌드 산출물)를 확인했습니다. + +### 6.1 Story 목록 변조 (`storybook-static/index.json`, 재빌드 없음) + +| 시나리오 | Overview | Overlays | 미사용 검사 | Home Menu | +|---|---|---|---|---| +| 승인되지 않은 SW/Header Story 추가 | 53/54 — 목록 검사 FAIL | 19/20 — 목록 FAIL | 31/34 — 개수·전체 항목 FAIL, 가짜 Story 렌더 중단 | 23/23 (목록 비검사) | +| 유지 Story(`logo-slot`) 삭제 | 53/54 — 목록 FAIL | 19/20 — 목록 FAIL | 89/92 — 개수·유지 항목·전체 항목 FAIL | 중단(Logo Slot Header 대기 시간 초과) | +| 유지 Story에 `verification` 태그 추가 | 53/54 | 19/20 | 91/93 — 유지 항목·전체 항목 FAIL | 23/23 | +| 유지 Story(`document-dropdown`) title 이동 | 53/54 | 19/20 | 90/92 — 유지 항목·전체 항목 FAIL | 23/23 | +| 유지 Story(`responsive-more`) 이름 변경 | 53/54 | 19/20 | 91/93 — 유지 항목·전체 항목 FAIL | 23/23 | + +### 6.2 소스 변조 (작업 트리, 재빌드 없음, 한 번에 4건) + +| 변조 | 검출 | +|---|---| +| 유지 선언 `LogoSlot`의 섹션 제목 변경 | Overview·Overlays 선언 검사 FAIL(`LogoSlot`), 미사용 검사 Export 검사 FAIL(`changed: LogoSlot`) | +| 승인 선언 `ResponsiveMore`(170a5c2)를 승인 내용과 다르게 변경 | 같은 두 검사에서 `ResponsiveMore` FAIL — 승인 목록은 Commit 내용에 고정되며 와일드카드가 아님 | +| 통합 개선 목록 밖 패키지 파일(`sw-footer/SWFooterParts.tsx`) 변경 | Overview·Overlays 소스 검사 FAIL(`unapproved`), 미사용 검사 경로 검사 FAIL | +| `.storybook/main.ts` 변경 | 미사용 검사 경로 검사 FAIL(`unexpected`). Overview는 이 경로를 원래 검사하지 않음 | + +결과: Overview 52/54, Overlays 18/20, 미사용 검사 91/93, Home Menu 23/23. 다른 검사는 그대로 PASS였습니다. + +### 6.3 동작 변조 (Story 수정 후 재빌드) + +`sw-header--logo-slot` 첫 Header에 `homeMenu={{ sections: OVERVIEW_HOME_MENU }}`를 임시로 넣고 Storybook을 다시 build했습니다. Home Menu는 **22/23**으로 `without homeMenu: logo click calls onHomeClick, renders no menu, no aria-expanded`만 FAIL(로고 클릭 시 `onHomeClick` 기록 없음)이었습니다. 원본으로 되돌린 뒤 다시 build했고, 그 빌드에서 §7을 실행했습니다. + +## 7. 회귀 테스트 결과 (Node 22.23.2, 수정 후) + +| 테스트 | 결과 | +|---|---| +| `npm run build-storybook`(prebuild: theme defaults 확인, 5개 패키지 build) | PASS | +| `home-menu-integration` | 23/23 | +| `overview` | 54/54 | +| `overview … overlays` | 20/20 | +| `storybook-unused-removal` | 93/93 | +| `npm run check:ai-docs` | PASS (2 shared files × 4 packages, catalog·링크 일치) | +| `node packages/sw/tests/ai-docs.check.mjs` | 46/46 | + +같은 빌드에서 이번 범위 밖의 기존 테스트 4종은 아직 과거 기준으로 실패합니다(§10). + +## 8. Story 추가·삭제·이동 시 승인 목록 관리 + +승인 목록은 `packages/sw/tests/storybook-inventory.mjs` 하나에서 관리합니다. Story 변경을 사용자가 승인한 같은 변경(또는 바로 뒤 테스트 정렬 변경)에서 아래 목록을 갱신하고, 각 항목 옆 주석에 승인 Commit과 사유를 적습니다. 값은 실제 Story ID·선언 이름·Commit SHA(전체 40자)만 씁니다. + +| 변경 | 갱신할 항목 | +|---|---| +| SW Story 추가 | 해당 영역 목록(`footerStoryIds`, `viewerStoryIds`, `themeStoryIds`, `homeMenuSidebarIds`, `integratedStoryIds` 등)에 ID 추가 → `approvedAddedStoryIds`에 자동 포함. 새 영역이면 새 목록을 만들고 `approvedAddedStoryIds`에 연결 | +| SW Story 삭제 | `retired…Ids` 목록에 ID 추가(예: `retiredFooterIds`, `retiredHomeMenuCompareIds`), 추가 목록에서 제외되는지 확인. `sw-header.stories.tsx`의 Story를 지우면 Overview 선언 검사는 `retiredPropertyPanelIds`·`retiredUnusedDemoIds`의 삭제만, 미사용 검사의 Export 검사는 그 스크립트의 `removedExports` 9개만 허용하므로 새 삭제는 두 검사도 함께 갱신해야 함 | +| Story 이동(title 변경, ID 유지) | `movedToPopupIds` 같은 이동 목록과 `withApprovedEntryChanges`의 변환(title·importPath), 선언이 다른 파일로 가면 `movedHeaderDeclarations`(새 파일, 승인 Commit) | +| 태그 변경(`verification` 등) | `verificationTaggedIds`(또는 해당 변환을 `withApprovedEntryChanges`에 추가) | +| `sw-header.stories.tsx` 선언 변경·추가 | `approvedHeaderDeclarations`에 `선언 이름: 승인 Commit`. 검사는 현재 선언이 그 Commit의 내용과 byte 단위로 같은지 봄 | +| Story ID 변경 | ID 변경은 삭제 + 추가로 다룸(이전 ID는 `retired…`, 새 ID는 추가 목록). 승인 없이 ID를 바꾸지 않음 | + +원칙: + +- 승인되지 않은 Story·제품 소스 변경을 목록에 넣어 테스트를 통과시키지 않습니다. 목록은 사용자 승인 기록입니다. +- 기준선 파일(`before-index.json`, `full-index-before.json`)과 과거 QA 폴더는 고치지 않습니다. 새 실행은 항상 새 증거 폴더에 저장합니다(기본 경로로 실행하면 일부 스크립트가 과거 증거를 덮어씀). +- 목록을 바꾼 뒤에는 Storybook을 다시 build하고 Overview(두 모드)·미사용 검사를 실행해 PASS를 확인합니다. 가능하면 §6.1처럼 가짜 Story 하나를 넣어 FAIL이 나는지도 확인합니다. + +## 9. 향후 main 병합 시 기준 갱신 절차 + +`approvedMainCommit`은 "Story와 제품 소스 변경이 모두 승인된 main 병합"을 가리키며, Overview·미사용 검사는 보호 경로(Overview: `packages/sw/src`, 미사용 검사: SW ai/package 파일, 다른 패키지, Overview, Dropdown CSF, assets, `.storybook`)가 이 Commit과 같아야 PASS입니다(통합 개선 파일 제외). 역할과 갱신 조건은 같은 파일의 주석에도 적었습니다. + +> **자동 변경 금지:** main에 새 HEAD가 생겼다는 이유만으로 `approvedMainCommit`을 바꾸지 않습니다. 이 값을 자동으로 갱신하는 스크립트·Workflow는 없으며, 아래 절차를 거친 검토된 테스트 전용 Commit에서만 바뀝니다. 현재 값은 `553e4df`(`553e4df4a865aaf6514b1c2b86cb2e3e9a658e5f`)이며 이 문서 작성 시점에 변경하지 않았습니다. + +1. 병합할 브랜치의 Story·제품 소스 변경이 Commit 단위로 모두 사용자 승인을 받았는지 확인합니다. 승인되지 않은 변경이 있으면 기준을 옮기지 않습니다. +2. 병합 전 브랜치에서 §8의 승인 목록을 갱신하고, 테스트가 PASS인지 확인합니다. +3. main에 병합합니다. Squash·Rebase 병합이면 SHA가 바뀌므로 `approvedHeaderDeclarations`·`movedHeaderDeclarations`의 Commit을 main에 실제로 들어간 SHA로 다시 맞춥니다. +4. 병합 Commit의 전체 SHA로 `approvedMainCommit`을 바꾸는 **테스트 전용 Commit**을 만듭니다. 이 Commit은 보호 경로를 건드리지 않습니다. +5. Node 22.23.2에서 `npm run build-storybook` 후 Home Menu, Overview(두 모드), 미사용 검사를 새 증거 폴더로 실행합니다. 하나라도 FAIL이면 기준을 옮기지 말고 원인(미승인 변경 또는 목록 누락)을 확인합니다. +6. 실행 결과와 갱신 사유를 `docs/sw/`의 보고서에 남깁니다. 기존 보고서와 증거는 고치지 않습니다. + +## 10. 남은 문제 + +- 이번 범위 밖 기존 테스트 4종은 같은 빌드에서 과거 기준으로 실패합니다(수정 전 측정, 이 Commit에서 고치지 않음): `dropdown-consolidation` 223/228(목록·선언·경로 5건), `storybook-cleanup-stage1` 29/34(목록·선언·경로 4건 + 삭제된 `sw-header--dynamic-controls`에서 중단), `detail-buttons verify`(삭제된 `sw-header--composition`에서 중단), `property-panel-comparison verify` 50/52(목록 2건) 후 삭제된 `sw-header--property-panel`에서 예외 종료. 별도 작업으로 다룹니다. +- 통합 개선 파일 예외: Overview·미사용 검사는 `integratedChangedFiles`가 `approvedMainCommit`과 달라도 허용합니다(그 파일은 통합 개선 suite가 검증). 그 suite를 함께 실행하지 않으면 이 파일들의 미승인 변경은 두 검사로는 드러나지 않습니다. +- 과거 QA 폴더를 기본 증거 경로로 쓰는 스크립트가 있습니다. 반드시 새 증거 폴더를 지정해야 합니다. + +## 11. 재현 + +```bash +npm run build-storybook +node packages/sw/tests/home-menu-integration.browser.mjs <새 폴더>/home +node packages/sw/tests/overview.browser.mjs +node packages/sw/tests/overview.browser.mjs overlays +node packages/sw/tests/storybook-unused-removal.browser.mjs <새 폴더>/unused +npm run check:ai-docs +node packages/sw/tests/ai-docs.check.mjs +``` + +Overview는 증거 폴더의 `before-index.json`을 읽으므로 `docs/sw/storybook-overview-qa/before-index.json`을 새 폴더에 복사한 뒤 실행합니다. 이번 실행의 결과 JSON·로그는 저장소 밖 임시 폴더에 두었고(`*.log`는 Git 제외 대상), 주요 수치는 위 표에 옮겼습니다. diff --git a/packages/sw/tests/storybook-inventory.mjs b/packages/sw/tests/storybook-inventory.mjs index 2c2155c..3e69c3d 100644 --- a/packages/sw/tests/storybook-inventory.mjs +++ b/packages/sw/tests/storybook-inventory.mjs @@ -40,6 +40,17 @@ export const integratedChangedFiles = [ ] // 2026-10-20 (test alignment): approved SW Story changes after the unused-demo removal that the lists above do not name. // Each comes from a user-requested commit merged into main as 553e4df. Regressions accept exactly these changes. +// approvedMainCommit is the reviewed main merge the regressions treat as the accepted source state. overview.browser.mjs +// (both modes) and storybook-unused-removal.browser.mjs keep their old baselines (50bf5bd, c8e5fbe) for the Story +// inventory and declarations, and accept only the changes named in this file. For the source paths they guard (overview: +// packages/sw/src; unused-removal: also the SW ai/package files, the other packages, Overview, Dropdown CSF, assets and +// .storybook) they require no difference from this commit (integratedChangedFiles excepted) and report the diff from the +// old baseline as sinceBaseline only. +// Update it only after a merge into main whose Story and product source changes were all approved, in the same change +// that records that merge's approved Story additions, removals, moves and tag changes in the lists of this file. +// Never move it to make a failing check pass, and never to a commit that carries unreviewed Story or source changes. +// A new main HEAD alone is no reason to move it: no script or workflow updates it automatically; it changes only in a +// reviewed test-only commit (docs/sw/sw-test-regression-baseline-2026-10-20.md §9). export const approvedMainCommit = '553e4df4a865aaf6514b1c2b86cb2e3e9a658e5f' // 170a5c2: the SW/Header sidebar page of the home menu (the moved verification Stories are movedHomeMenuIds). export const homeMenuSidebarIds = ['sw-header--home-menu']