7.0.6 사이클에서 유입된 회귀 3건을 출시 전에 닫는다. 추가옵션 저장소의 반환 타입 선언이 실제 반환(중첩 맵)과 어긋나 추가옵션 있는 상품의 담기· 바로구매가 죽었고, 검색 엔진이 결과 소비 시점에 SELECT 를 재작성해 정렬이 참조하던 스코어 별칭을 지웠으며, 미들웨어 targets 가 늘어난 라우트를 따라가지 못해 비회원 현금영수증이 전면 불능이었다. 셋 다 정적 검사의 사각지대였으므로 룰 2건과 계약 테스트를 함께 세워 재발을 막는다. 같은 유형의 잠복 결함 3건(빈 반환의 타입 불일치 2건, 없는 메서드 호출 1건)도 호출 경로가 생기는 순간 fatal 이므로 함께 닫았다. 브라우저 검수 중 드러난 선행 결함 5건을 이어서 수정한다. 엔진이 event 키로 선언한 액션을 React prop 으로 연결하지 않아 오류 없이 조용히 무반응이었고, 연결한 뒤에는 커스텀 컴포넌트의 이벤트 payload 가 유실됐다. 비회원 주문상세 응답에 현금영수증 키가 없어 발급 결과에 도달할 수 없었으며, 입금확인과 현금영수증 발급이 base 통화 금액을 써서 결제 통화가 다른 주문에서 각각 저장 실패와 잘못된 증빙을 냈다. 마지막으로 통화 단위를 코드·문구에서 걷어내 설정으로 이관한다. 다국어 문구의 원/円, 프론트의 KRW 고정표와 리터럴 폴백 173건, 서버 포맷의 통화 코드 리터럴이 대상이며, 기본 통화가 원이 아닌 상점에서 값은 맞고 단위만 틀린 금액이 예외 없이 나가던 문제였다. 통화 판정을 설정 단일 출처로 모으고 규칙 3건을 정적 검사로 고정했다.
119 lines
4.6 KiB
TypeScript
119 lines
4.6 KiB
TypeScript
/**
|
|
* @file coupon-download-modal.test.tsx
|
|
* @description U10 — 쿠폰 다운로드 모달 무한스크롤 회귀 테스트 (MP10 §9, 재설계)
|
|
*
|
|
* 검증 항목 (최종 무한스크롤 설계 기준):
|
|
* - last_page 기반 버튼식 페이지네이션 블록(이전/다음/카운터) 미렌더
|
|
* - 스크롤 영역(max-h-96 overflow-y-auto) 유지
|
|
* - onScroll(type:scroll) + conditions 가드(하단 근접 + !downloadingMore + current_page<last_page)
|
|
* 로 다음 페이지 증분 로드
|
|
* - 더보기/스크롤 apiCall 이 per_page=8 + page 파라미터 사용(증분 페이지)
|
|
* - 하단 로딩 인디케이터(downloadingMore)
|
|
* - show.json 초기 상태에 downloadingMore/downloadingCouponId 명시, downloadableCouponsPage 제거
|
|
*
|
|
* @vitest-environment jsdom
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
|
|
const baseDir = path.resolve(__dirname, '../..');
|
|
|
|
function loadJson(relPath: string): any {
|
|
const raw = fs.readFileSync(path.resolve(baseDir, relPath), 'utf8');
|
|
return JSON.parse(raw);
|
|
}
|
|
|
|
function serialize(node: any): string {
|
|
return JSON.stringify(node);
|
|
}
|
|
|
|
const modal = loadJson('layouts/partials/shop/_modal_coupon_download.json');
|
|
const infoSummary = loadJson('layouts/partials/shop/detail/_info_summary.json');
|
|
const showLayout = loadJson('layouts/shop/show.json');
|
|
|
|
describe('U10 — 다운로드 모달 무한스크롤 (버튼식 페이지네이션 제거)', () => {
|
|
const text = serialize(modal);
|
|
|
|
it('이전/다음 페이지 i18n 키 참조가 모달에 없다', () => {
|
|
expect(text).not.toContain('shop.coupon_download.prev_page');
|
|
expect(text).not.toContain('shop.coupon_download.next_page');
|
|
});
|
|
|
|
it('스크롤 영역(max-h-96 overflow-y-auto)은 유지된다', () => {
|
|
expect(text).toContain('max-h-96');
|
|
expect(text).toContain('overflow-y-auto');
|
|
});
|
|
|
|
it('스크롤 그리드에 onScroll(type:scroll) 액션이 있다', () => {
|
|
expect(text).toContain('"type":"scroll"');
|
|
});
|
|
|
|
it('무한스크롤 가드(하단 근접 + downloadingMore + current_page<last_page)가 있다', () => {
|
|
expect(text).toContain('scrollHeight');
|
|
expect(text).toContain('downloadingMore');
|
|
expect(text).toContain('current_page');
|
|
expect(text).toContain('last_page');
|
|
});
|
|
|
|
it('스크롤 증분 로드 apiCall 이 page 증분(current_page+1)을 사용한다', () => {
|
|
expect(text).toContain('per_page=8');
|
|
expect(text).toContain('current_page ?? 1) + 1');
|
|
});
|
|
|
|
it('연속 스크롤 중복 호출 방지를 위해 debounce 가 설정되어 있다', () => {
|
|
expect(text).toContain('"debounce":200');
|
|
});
|
|
|
|
it('하단 로딩 인디케이터(downloadingMore)가 있다', () => {
|
|
expect(text).toContain('downloadingMore');
|
|
expect(text).toContain('animate-spin');
|
|
});
|
|
|
|
it('meta description 이 스크롤로 갱신되었다', () => {
|
|
expect(modal.meta.description).toContain('스크롤');
|
|
expect(modal.meta.description).not.toContain('페이지네이션');
|
|
});
|
|
});
|
|
|
|
describe('U10 — 더보기 액션(배지와 같은 목록을 연다)', () => {
|
|
const text = serialize(infoSummary);
|
|
|
|
/*
|
|
* 「+N」 배지는 이 상품에 적용되는 쿠폰(productDownloadableCoupons)으로 계산된다.
|
|
* 더보기가 전체 다운로드 쿠폰을 새로 조회하면 이 상품에 적용되지 않는 쿠폰이 섞여
|
|
* 배지 수와 모달 건수가 어긋나므로, 더보기는 배지와 같은 데이터소스를 그대로 넘긴다.
|
|
* (종전 테스트는 별도 조회 설계를 고정하고 있었다 — 그 설계가 배지 불일치의 원인이었다.)
|
|
*/
|
|
it('더보기가 별도 조회 없이 배지와 같은 데이터소스를 모달에 넘긴다', () => {
|
|
expect(text).toContain('downloadableCoupons":"{{productDownloadableCoupons.data}}');
|
|
expect(text).not.toContain('per_page=8&page=1');
|
|
});
|
|
|
|
it('더보기가 모달 로딩 상태를 초기화한다', () => {
|
|
expect(text).toContain('downloadableCouponsLoading":false');
|
|
expect(text).toContain('downloadingMore":false');
|
|
});
|
|
|
|
it('downloadableCouponsPage setState 가 제거되었다', () => {
|
|
expect(text).not.toContain('downloadableCouponsPage');
|
|
});
|
|
});
|
|
|
|
describe('U10 — show.json 초기 상태 정리', () => {
|
|
const text = serialize(showLayout);
|
|
|
|
it('downloadableCouponsPage 초기값이 제거되었다', () => {
|
|
expect(text).not.toContain('downloadableCouponsPage');
|
|
});
|
|
|
|
it('downloadingCouponId 초기값이 명시되었다', () => {
|
|
expect(text).toContain('downloadingCouponId');
|
|
});
|
|
|
|
it('downloadingMore 초기값이 명시되었다', () => {
|
|
expect(text).toContain('downloadingMore');
|
|
});
|
|
});
|