QA 시나리오 카탈로그
렌더링·인증·폼·CRUD·접근성·보안 등 14개 카테고리 100여 개 브라우저 QA 시나리오와 게이트 판정 기준
한눈에 보기
브라우저 QA 에이전트가 태스크 완료 전 실행하는 시나리오 카탈로그입니다. “빌드 성공”이 아니라 “사용자 플로우가 실제로 동작하는가”를 판정 기준으로 만듭니다.
- 핵심 질문: 변경 범위별로 어떤 시나리오를 어느 깊이까지 실행해야 PASS를 선언할 수 있는가?
- 읽는 대상: 브라우저 QA 자동화를 설계하는 운영자
- 연결 문서: QA & Operations Methods, Harness — Hooks & Gates, Evidence Dashboard
WebMCP(CDP) 기반 브라우저 QA 시나리오. QA 에이전트가 모든 태스크 완료 전 실행하는 시나리오 정의입니다. 예시 코드의 URL은 모두 로컬 개발 서버 기준의 일반 예시입니다.
QA 레벨
| 레벨 | 실행 시점 | 범위 | 소요 시간 |
|---|---|---|---|
| micro-QA | 개별 태스크 완료 시 | 변경된 기능만 타겟 테스트 | 1-2분 |
| feature-QA | 기능 단위 완료 시 | 해당 기능 전체 시나리오 | 3-5분 |
| full-QA | Phase 완료 / 최종 검증 | 전체 시나리오 | 10-15분 |
카테고리별 QA 시나리오
1. 페이지 렌더링 (RENDERING)
모든 라우트가 정상 렌더링되는지 검증.
| ID | 시나리오 | 검증 방법 (WebMCP) | 심각도 |
|---|---|---|---|
| R-01 | 메인 페이지 렌더링 | navigate("/") → qa_page_state() → bodyLength > 100 | CRITICAL |
| R-02 | 모든 등록 라우트 렌더링 | 각 라우트 순회 → bodyLength > 0, readyState === “complete” | CRITICAL |
| R-03 | 빈 페이지(White Screen) 감지 | bodyLength < 50 → FAIL | CRITICAL |
| R-04 | 에러 바운더리 표시 | ”error”, “something went wrong” 텍스트 감지 → FAIL | CRITICAL |
| R-05 | 로딩 스피너 무한 대기 | 5초 후에도 spinner/loading 요소 존재 → HIGH | HIGH |
| R-06 | 404 페이지 존재하지 않는 라우트 | /nonexistent-route 접근 → 404 페이지 또는 리다이렉트 | MEDIUM |
| R-07 | SSR/CSR 하이드레이션 불일치 | 콘솔에 “hydration” 에러 감지 | HIGH |
// WebMCP 실행 예시
navigate("http://localhost:3000/");
const state = await mcpCall('qa_page_state');
assert(state.bodyLength > 100, 'R-01: 메인 페이지 빈 화면');
assert(state.readyState === 'complete', 'R-01: 로드 미완료');
2. 인증 / 인가 (AUTH)
로그인, 로그아웃, 세션 관리, 권한 제어 검증.
| ID | 시나리오 | 검증 방법 | 심각도 |
|---|---|---|---|
| A-01 | 로그인 폼 렌더링 | /login → qa_form_state() → email/password 필드 존재 | CRITICAL |
| A-02 | 올바른 자격증명 로그인 | 폼 입력 → 제출 → 대시보드 리다이렉트 | CRITICAL |
| A-03 | 잘못된 자격증명 거부 | 잘못된 비밀번호 → 에러 메시지 표시, 페이지 유지 | CRITICAL |
| A-04 | 미인증 접근 리다이렉트 | 보호된 경로 직접 접근 → /login 리다이렉트 | CRITICAL |
| A-05 | 로그아웃 기능 | 로그아웃 버튼 클릭 → 세션 삭제, 로그인 페이지 이동 | HIGH |
| A-06 | 세션 만료 처리 | 만료된 토큰으로 API 호출 → 401 → 로그인 리다이렉트 | HIGH |
| A-07 | 회원가입 유효성 검증 | 빈 필드/짧은 비밀번호/잘못된 이메일 → 각각 에러 메시지 | HIGH |
| A-08 | 소셜 로그인 버튼 | OAuth 버튼 존재 + 클릭 시 올바른 URL로 이동 | MEDIUM |
| A-09 | 비밀번호 재설정 플로우 | 이메일 입력 → 전송 → 성공 메시지 | MEDIUM |
| A-10 | 권한별 UI 분기 | admin은 관리 메뉴 보임, 일반 사용자는 안 보임 | HIGH |
// A-01: 로그인 폼 검증
navigate("http://localhost:3000/login");
const forms = await mcpCall('qa_form_state');
assert(forms.length > 0, 'A-01: 로그인 폼 없음');
const loginForm = forms[0];
const hasEmail = loginForm.fields.some(f => f.name === 'email' || f.type === 'email');
const hasPassword = loginForm.fields.some(f => f.type === 'password');
assert(hasEmail && hasPassword, 'A-01: 이메일/비밀번호 필드 누락');
// A-04: 미인증 리다이렉트
navigate("http://localhost:3000/dashboard");
const state = await mcpCall('qa_page_state');
assert(state.url.includes('/login'), 'A-04: 미인증 접근이 차단되지 않음');
3. 폼 입력 및 유효성 검증 (FORM)
모든 폼의 입력, 제출, 유효성 검사 검증.
| ID | 시나리오 | 검증 방법 | 심각도 |
|---|---|---|---|
| F-01 | 필수 필드 빈 제출 차단 | required 필드 비우고 제출 → 에러 메시지 | HIGH |
| F-02 | 이메일 형식 검증 | ”invalid-email” 입력 → 포맷 에러 | HIGH |
| F-03 | 최소/최대 길이 검증 | 1자 입력 → 최소 길이 에러 | MEDIUM |
| F-04 | 비밀번호 강도 검증 | 약한 비밀번호 → 강도 표시기 / 에러 | MEDIUM |
| F-05 | 폼 제출 성공 | 유효한 데이터 입력 → 제출 → 성공 피드백 | CRITICAL |
| F-06 | 중복 제출 방지 | 빠른 더블 클릭 → 버튼 비활성화 또는 1회만 처리 | HIGH |
| F-07 | 폼 초기화/리셋 | 리셋 버튼 → 모든 필드 초기값 복원 | LOW |
| F-08 | 파일 업로드 | 파일 선택 → 미리보기 → 업로드 성공 | HIGH |
| F-09 | 드롭다운/셀렉트 동작 | 옵션 선택 → 값 반영 | MEDIUM |
| F-10 | 날짜/시간 입력기 | 날짜 선택 → 올바른 포맷 반영 | MEDIUM |
| F-11 | 동적 폼 필드 | 조건부 필드 표시/숨기기 → 상태 일관성 | MEDIUM |
| F-12 | 서버 사이드 유효성 에러 | 서버 422 → 필드별 에러 메시지 매핑 | HIGH |
// F-01: 필수 필드 빈 제출
navigate("http://localhost:3000/signup");
click('button[type="submit"]');
const errors = await mcpCall('qa_assert_element', {
selector: '[role="alert"], .error-message, .text-red-500',
visible: true
});
assert(errors.found, 'F-01: 필수 필드 빈 제출 시 에러 메시지 없음');
4. CRUD 작업 (CRUD)
데이터 생성, 조회, 수정, 삭제 전체 플로우 검증.
| ID | 시나리오 | 검증 방법 | 심각도 |
|---|---|---|---|
| C-01 | 리스트 조회 (Read) | 목록 페이지 → 아이템 렌더링, 빈 상태 처리 | CRITICAL |
| C-02 | 상세 조회 (Read) | 아이템 클릭 → 상세 정보 표시 | HIGH |
| C-03 | 새 항목 생성 (Create) | 폼 작성 → 제출 → 리스트에 추가됨 | CRITICAL |
| C-04 | 항목 수정 (Update) | 수정 버튼 → 폼 프리필 → 변경 → 저장 → 반영 | CRITICAL |
| C-05 | 항목 삭제 (Delete) | 삭제 버튼 → 확인 다이얼로그 → 삭제 → 리스트에서 제거 | HIGH |
| C-06 | 낙관적 업데이트 | 수정 후 즉시 UI 반영 → 서버 응답 후 확정 | MEDIUM |
| C-07 | 빈 상태 UI | 데이터 0건 → “데이터 없음” 메시지 또는 생성 유도 | MEDIUM |
| C-08 | 페이지네이션 | 다음/이전 페이지 → 데이터 변경, 현재 페이지 표시 | MEDIUM |
| C-09 | 검색/필터 | 검색어 입력 → 결과 필터링 → 초기화 시 전체 복원 | MEDIUM |
| C-10 | 정렬 | 컬럼 헤더 클릭 → 오름/내림차순 토글 | LOW |
| C-11 | 무한 스크롤 | 스크롤 하단 도달 → 추가 데이터 로드 | MEDIUM |
| C-12 | 삭제 확인 다이얼로그 | 삭제 → 확인 모달 → 취소 시 유지, 확인 시 삭제 | HIGH |
// C-01: 리스트 조회
navigate("http://localhost:3000/items");
const state = await mcpCall('qa_page_state');
// 아이템이 있거나 빈 상태 메시지가 있어야 함
const hasItems = state.bodyPreview.length > 50;
const hasEmptyState = state.bodyPreview.includes('없') || state.bodyPreview.includes('empty');
assert(hasItems || hasEmptyState, 'C-01: 리스트 미렌더링');
5. 네비게이션 (NAVIGATION)
라우팅, 링크, 브레드크럼, 뒤로가기 등 검증.
| ID | 시나리오 | 검증 방법 | 심각도 |
|---|---|---|---|
| N-01 | 메인 네비게이션 링크 | 모든 nav 링크 클릭 → 올바른 페이지 이동 | HIGH |
| N-02 | 브라우저 뒤로/앞으로 | history.back() → 이전 페이지 렌더링 | MEDIUM |
| N-03 | 딥 링크 직접 접근 | /items/123 직접 URL 입력 → 정상 렌더링 | HIGH |
| N-04 | 브레드크럼 정확성 | 현재 위치 반영, 각 레벨 클릭 가능 | LOW |
| N-05 | 활성 메뉴 하이라이트 | 현재 페이지에 해당하는 nav 아이템 활성 상태 | LOW |
| N-06 | 404 라우트 처리 | 존재하지 않는 경로 → 404 페이지 | MEDIUM |
| N-07 | 리다이렉트 체인 | 리다이렉트 시 무한 루프 없음, 최종 페이지 도달 | HIGH |
| N-08 | 쿼리 파라미터 보존 | 필터/정렬 상태가 URL에 반영, 새로고침 후 유지 | MEDIUM |
6. 반응형 / 레이아웃 (RESPONSIVE)
다양한 뷰포트에서 레이아웃 검증.
| ID | 시나리오 | 검증 방법 | 심각도 |
|---|---|---|---|
| RS-01 | 모바일 레이아웃 (375px) | viewport 375px → 수평 스크롤 없음, 터치 타겟 48px+ | HIGH |
| RS-02 | 태블릿 레이아웃 (768px) | viewport 768px → 적절한 그리드 전환 | MEDIUM |
| RS-03 | 데스크탑 레이아웃 (1280px) | viewport 1280px → 풀 레이아웃 표시 | MEDIUM |
| RS-04 | 수평 오버플로우 없음 | 모든 뷰포트에서 body scrollWidth <= viewport width | HIGH |
| RS-05 | 모바일 메뉴 토글 | 햄버거 메뉴 → 사이드바/드로어 표시 → 닫기 | HIGH |
| RS-06 | 이미지 반응형 | 이미지가 컨테이너 넘지 않음, max-width: 100% | MEDIUM |
| RS-07 | 텍스트 가독성 | font-size >= 14px (모바일), line-height 적절 | MEDIUM |
| RS-08 | 터치 타겟 크기 | 버튼/링크 최소 44x44px (모바일) | MEDIUM |
// RS-01: 모바일 레이아웃 검증
evaluate_script(`
document.documentElement.style.width = '375px';
window.innerWidth
`);
navigate("http://localhost:3000/");
evaluate_script(`
document.body.scrollWidth <= 375 ? 'PASS' : 'FAIL: 수평 오버플로우'
`);
7. 접근성 (ACCESSIBILITY)
WCAG 2.1 AA 기준 검증.
| ID | 시나리오 | 검증 방법 | 심각도 |
|---|---|---|---|
| AC-01 | 이미지 alt 속성 | qa_accessibility() → missing-alt 0건 | HIGH |
| AC-02 | 빈 인터랙티브 요소 | 텍스트/aria-label 없는 button/a 감지 | HIGH |
| AC-03 | 라벨 없는 input | label 또는 aria-label 없는 input | HIGH |
| AC-04 | 헤딩 레벨 순서 | h1 → h2 → h3 순서 (스킵 금지) | MEDIUM |
| AC-05 | lang 속성 | <html lang="ko"> 존재 | MEDIUM |
| AC-06 | 키보드 네비게이션 | Tab 키로 모든 인터랙티브 요소 접근 가능 | HIGH |
| AC-07 | 포커스 표시기 | focus 시 시각적 인디케이터(outline 등) 존재 | MEDIUM |
| AC-08 | 색상 대비 | 텍스트-배경 대비 4.5:1 이상 | MEDIUM |
| AC-09 | ARIA 역할 | 모달 → role=“dialog”, 알림 → role=“alert” | MEDIUM |
| AC-10 | Skip Navigation | 첫 Tab에서 “본문 바로가기” 링크 | LOW |
// AC-01~05: 접근성 기본 검사
const a11y = await mcpCall('qa_accessibility');
assert(a11y.issues.filter(i => i.type === 'missing-alt').length === 0, 'AC-01: alt 없는 이미지');
assert(a11y.issues.filter(i => i.type === 'empty-interactive').length === 0, 'AC-02: 빈 버튼/링크');
assert(a11y.issues.filter(i => i.type === 'unlabeled-input').length === 0, 'AC-03: 라벨 없는 input');
assert(a11y.lang, 'AC-05: html lang 속성 없음');
8. 에러 핸들링 (ERROR)
네트워크 실패, 서버 에러, 사용자 에러 시 UI 반응 검증.
| ID | 시나리오 | 검증 방법 | 심각도 |
|---|---|---|---|
| E-01 | API 500 에러 표시 | 서버 에러 시 사용자 친화적 메시지 (스택트레이스 노출 금지) | CRITICAL |
| E-02 | 네트워크 오프라인 | fetch 실패 → “연결 확인” 메시지 또는 재시도 버튼 | HIGH |
| E-03 | 404 API 응답 | 존재하지 않는 리소스 → “찾을 수 없음” 메시지 | MEDIUM |
| E-04 | 타임아웃 처리 | 10초 초과 → 타임아웃 메시지, 재시도 옵션 | MEDIUM |
| E-05 | 콘솔 에러 없음 | qa_console_errors() → 0건 | HIGH |
| E-06 | 언핸들드 프로미스 거부 | unhandledrejection 이벤트 감지 → 0건 | HIGH |
| E-07 | 에러 바운더리 작동 | 컴포넌트 에러 시 전체 앱 크래시 방지 | CRITICAL |
| E-08 | 유효성 에러 메시지 명확성 | 에러 메시지가 해결 방법을 제시 | MEDIUM |
| E-09 | 에러 후 복구 | 에러 발생 후 정상 동작으로 복구 가능 | HIGH |
// E-05: 콘솔 에러 확인
const errors = await mcpCall('qa_console_errors');
const criticalErrors = errors.filter(e =>
!e.msg.includes('favicon') && !e.msg.includes('HMR')
);
assert(criticalErrors.length === 0, `E-05: 콘솔 에러 ${criticalErrors.length}건`);
9. API 통신 (API)
네트워크 요청/응답 검증.
| ID | 시나리오 | 검증 방법 | 심각도 |
|---|---|---|---|
| AP-01 | API 호출 성공 | qa_network_log() → 모든 요청 status 2xx | HIGH |
| AP-02 | 실패한 요청 없음 | status 4xx/5xx 또는 FAILED 없음 | HIGH |
| AP-03 | 인증 토큰 전송 | Authorization 헤더 포함 (보호된 엔드포인트) | CRITICAL |
| AP-04 | CORS 에러 없음 | 콘솔에 CORS 관련 에러 0건 | HIGH |
| AP-05 | 응답 시간 적정 | 모든 API 응답 < 3000ms | MEDIUM |
| AP-06 | 중복 요청 방지 | 같은 엔드포인트 연속 호출 없음 (디바운스) | MEDIUM |
| AP-07 | 로딩 상태 표시 | API 호출 중 spinner/skeleton 표시 | MEDIUM |
| AP-08 | 에러 응답 처리 | 4xx/5xx → 사용자 피드백 (토스트/알림) | HIGH |
// AP-01, AP-02: 네트워크 요청 검증
const network = await mcpCall('qa_network_log');
const failures = network.filter(r =>
r.status === 'FAILED' || (typeof r.status === 'number' && r.status >= 400)
);
assert(failures.length === 0, `AP-01: 실패한 API 요청 ${failures.length}건`);
10. 상태 관리 (STATE)
클라이언트 상태 일관성 검증.
| ID | 시나리오 | 검증 방법 | 심각도 |
|---|---|---|---|
| S-01 | 상태 변경 → UI 반영 | 데이터 변경 후 관련 컴포넌트 모두 업데이트 | CRITICAL |
| S-02 | 새로고침 후 상태 유지 | F5 → 로그인 상태, 필터 등 유지 | HIGH |
| S-03 | 탭 간 상태 동기화 | 다른 탭에서 변경 → 현재 탭 반영 (해당 시) | MEDIUM |
| S-04 | 로딩/에러/성공 상태 전환 | 각 상태별 적절한 UI 표시 | HIGH |
| S-05 | 캐시 무효화 | 데이터 변경 후 캐시된 목록 갱신 | HIGH |
| S-06 | 동시 수정 충돌 | 같은 데이터 동시 수정 시 충돌 처리 | MEDIUM |
| S-07 | localStorage 미사용 | Supabase Auth 내부 제외 localStorage 호출 0건 | HIGH |
// S-07: localStorage 사용 감지
evaluate_script(`
const originalSetItem = Storage.prototype.setItem;
window.__lsWrites = [];
Storage.prototype.setItem = function(key, value) {
if (!key.startsWith('sb-')) window.__lsWrites.push(key);
return originalSetItem.call(this, key, value);
};
`);
// ... 앱 조작 후 ...
evaluate_script(`JSON.stringify(window.__lsWrites)`);
// __lsWrites 배열이 비어야 함
11. 성능 (PERFORMANCE)
페이지 로드 및 런타임 성능 검증.
| ID | 시나리오 | 검증 방법 | 심각도 |
|---|---|---|---|
| P-01 | 초기 로드 < 3초 | navigate 후 DOMContentLoaded 타이밍 | HIGH |
| P-02 | LCP < 2.5초 | Largest Contentful Paint 측정 | MEDIUM |
| P-03 | 번들 크기 적정 | 빌드 output에서 main chunk < 500KB | MEDIUM |
| P-04 | 이미지 최적화 | 2MB 이상 이미지 없음, WebP/AVIF 사용 | LOW |
| P-05 | 메모리 누수 없음 | 페이지 전환 반복 후 heap 증가 없음 | MEDIUM |
| P-06 | 불필요한 리렌더링 | React DevTools Profiler 기준 과도한 렌더 감지 | LOW |
| P-07 | 레이지 로딩 | 초기 번들에 모든 라우트 포함 안 됨 | LOW |
// P-01: 초기 로드 시간
evaluate_script(`
const timing = performance.timing;
const loadTime = timing.loadEventEnd - timing.navigationStart;
JSON.stringify({ loadTime, threshold: 3000, pass: loadTime < 3000 });
`);
12. 보안 (SECURITY)
클라이언트 사이드 보안 검증.
| ID | 시나리오 | 검증 방법 | 심각도 |
|---|---|---|---|
| SEC-01 | XSS 방지 | <script>alert(1)</script> 입력 → 이스케이프 처리 | CRITICAL |
| SEC-02 | CSRF 토큰 | 폼 제출 시 CSRF 토큰 포함 (해당 시) | HIGH |
| SEC-03 | 민감 정보 미노출 | 페이지 소스에 API 키, 시크릿 없음 | CRITICAL |
| SEC-04 | HTTPS 리다이렉트 | HTTP → HTTPS 자동 리다이렉트 (프로덕션) | HIGH |
| SEC-05 | 보안 헤더 존재 | X-Content-Type-Options, CSP 등 | MEDIUM |
| SEC-06 | 비밀번호 마스킹 | password 필드 type=“password” 유지 | HIGH |
| SEC-07 | 자동완성 제어 | 민감 필드 autocomplete=“off” 또는 적절한 값 | LOW |
| SEC-08 | 인증 토큰 안전 저장 | 토큰이 URL 파라미터나 로그에 노출되지 않음 | CRITICAL |
| SEC-09 | 클릭재킹 방지 | X-Frame-Options 또는 CSP frame-ancestors | MEDIUM |
// SEC-01: XSS 방지
type('input[name="search"]', '<script>alert("xss")</script>');
click('button[type="submit"]');
const state = await mcpCall('qa_page_state');
assert(!state.bodyPreview.includes('<script>'), 'SEC-01: XSS 취약점');
13. 토스트 / 알림 / 모달 (NOTIFICATION)
사용자 피드백 UI 검증.
| ID | 시나리오 | 검증 방법 | 심각도 |
|---|---|---|---|
| NT-01 | 성공 토스트 표시 | 작업 성공 → 성공 메시지 토스트 | MEDIUM |
| NT-02 | 에러 토스트 표시 | 작업 실패 → 에러 메시지 토스트 | HIGH |
| NT-03 | 토스트 자동 닫힘 | 3-5초 후 자동 사라짐 | LOW |
| NT-04 | 모달 열기/닫기 | 트리거 → 모달 표시 → 배경 클릭/ESC → 닫힘 | MEDIUM |
| NT-05 | 모달 배경 스크롤 잠금 | 모달 열림 시 body 스크롤 비활성화 | LOW |
| NT-06 | 확인 다이얼로그 | 위험한 작업 전 “정말 삭제하시겠습니까?” 확인 | HIGH |
| NT-07 | 모달 포커스 트랩 | Tab이 모달 내부에서만 순환 | MEDIUM |
14. 다국어 / i18n (I18N)
다국어 지원 시 검증.
| ID | 시나리오 | 검증 방법 | 심각도 |
|---|---|---|---|
| I-01 | 기본 언어 표시 | 설정된 기본 언어로 UI 렌더링 | HIGH |
| I-02 | 언어 전환 | 언어 선택 → 모든 UI 텍스트 변경 | MEDIUM |
| I-03 | 번역 누락 감지 | 번역 키가 그대로 표시되는 경우 감지 | MEDIUM |
| I-04 | RTL 레이아웃 | 아랍어/히브리어 → 우→좌 레이아웃 전환 | LOW |
| I-05 | 날짜/숫자 포맷 | 로케일에 맞는 날짜/통화 형식 | MEDIUM |
QA 레벨별 시나리오 매핑
micro-QA (태스크 완료 시)
변경된 파일/기능에 해당하는 시나리오만 실행:
변경 파일 분석 → 카테고리 매핑 → 해당 시나리오만 실행
| 변경 영역 | 실행 시나리오 |
|---|---|
| 인증 관련 (auth/, login, signup) | A-01~A-10 |
| 폼 컴포넌트 (form, input) | F-01~F-12 |
| 리스트/CRUD 페이지 | C-01~C-12 |
| 레이아웃/네비게이션 | N-01 |
| API 호출 수정 | AP-01~AP-08 |
| 에러 핸들링 | E-01~E-09 |
| 공통 (항상 실행) | R-01 |
feature-QA (기능 단위 완료 시)
해당 기능의 전체 시나리오 + 관련 카테고리:
기능 범위 파악 → 1차 카테고리 전체 실행 → 2차 연관 카테고리 실행
full-QA (Phase 완료 / 최종 검증)
전체 시나리오 실행:
- R-01~R-07 (렌더링)
- A-01~A-10 (인증)
- F-01~F-12 (폼)
- C-01~C-12 (CRUD)
- N-01~N-08 (네비게이션)
- RS-01~RS-08 (반응형)
- AC-01~AC-10 (접근성)
- E-01~E-09 (에러)
- AP-01~AP-08 (API)
- S-01~S-07 (상태)
- P-01~P-07 (성능)
- SEC-01~SEC-09 (보안)
- NT-01~NT-07 (알림/모달)
- I-01~I-05 (다국어, 해당 시)
QA 증거 파일 확장 스키마
.qa-evidence.json 확장:
{
"timestamp": "2026-03-11T12:00:00Z",
"qa_level": "full",
"task_id": "task-123",
"code_qa": {
"build": "pass",
"typecheck": "pass",
"lint": "pass"
},
"browser_test": {
"executed": true,
"method": "superpowers-chrome",
"routes_tested": ["/", "/login", "/dashboard", "/items", "/items/new"],
"screenshots": ["/tmp/qa-home.png", "/tmp/qa-login.png"],
"console_errors": 0,
"network_failures": 0
},
"scenarios_executed": {
"total": 95,
"passed": 93,
"failed": 0,
"skipped": 2,
"categories": {
"RENDERING": { "total": 7, "passed": 7 },
"AUTH": { "total": 10, "passed": 10 },
"FORM": { "total": 12, "passed": 12 },
"CRUD": { "total": 12, "passed": 12 },
"NAVIGATION": { "total": 8, "passed": 8 },
"RESPONSIVE": { "total": 8, "passed": 6, "skipped": 2 },
"ACCESSIBILITY": { "total": 10, "passed": 10 },
"ERROR": { "total": 9, "passed": 9 },
"API": { "total": 8, "passed": 8 },
"STATE": { "total": 7, "passed": 7 },
"PERFORMANCE": { "total": 4, "passed": 4 }
}
},
"findings_count": {
"critical": 0,
"high": 0,
"medium": 0,
"low": 0
},
"verdict": "PASS"
}
QA Gate 판정 기준
PASS 조건 (모든 조건 충족)
code_qa: build + typecheck 모두 passbrowser_test.executed: truebrowser_test.method: “superpowers-chrome” 또는 “agent-browser” (curl-fallback은 SPA에서 불충분)findings_count.critical: 0findings_count.high: 0scenarios_executed.failed: 0
FAIL 조건 (1개라도 해당)
- 빌드 실패
- 브라우저 테스트 미실행
- CRITICAL 또는 HIGH FINDING 존재
- 필수 시나리오(공통) 실패
WARNING (블로킹하지 않음)
- MEDIUM FINDING 존재
- curl-fallback 사용 (SPA 검증 한계)
- 증거 파일 1시간 이상 경과
- 일부 시나리오 SKIPPED
Source Notes
이 문서는 KeystoneHub의 브라우저 QA 시나리오 정의를 공개용으로 정리한 것입니다. 예시의 URL/포트는 일반화된 로컬 개발 서버 예시이며 특정 서비스를 가리키지 않습니다. Provenance: keystone-native.