feat(site,skill): 모바일 앱 수준 UX — 바텀내비·백 키 히스토리·바텀 시트·safe area·PWA 와 L6 시나리오 E2E
- HIG(탭바 5~6·시트)·M3(바텀내비 3~5·48dp)·NN/g·CSS-Tricks 조사(research/mobile-app-ux-v1.md) 후 Nielsen 10 감사로 위반 도출 - 바텀 내비 5탭 + 더보기 바텀 시트(그룹 헤더), 뷰·다이얼로그 pushState/popstate — 안드로이드 백 키가 앱 내 뒤로 작동 - safe area(env+max)·입력 16px(iOS 확대 방지)·coarse 44px 타깃·tap-highlight·overscroll 위생, 인쇄에서 모바일 크롬 제거 - PWA: manifest + sharp 생성 아이콘(가온=장부 문항, 두레=시간표 블록) - tools/e2e.mjs L6 시나리오: RED 1,742 단언 318 실패 → GREEN 1,762/0, verify 체인 7계층 통과 - 실기기(안드로이드 에뮬레이터·Chrome) 텍스트 근거 검증 — 백 키·시트·탭 전환, iOS 는 WebKit(L4) 대체 - 스킬 내재화: mobile-app-ux.md, audit-gate 7계층·하니스 12~14, design-gate 터치타깃 검사, SKILL.md 규칙 7 - 도그푸드: 게이트가 죽은 선택자 8종·thresholds 병합 버그 적발 — CSS v22(양앱) 범프 후 전체 재검증
This commit is contained in:
parent
72337b7ee0
commit
14cb138e1a
133 changed files with 2139 additions and 208 deletions
|
|
@ -174,6 +174,74 @@ await jclick("[data-cancel]");
|
|||
pass("두레 내역 화면 취소", (await page.$$("#en-table tbody tr")).length === 0);
|
||||
pass("두레 1440 오버플로", (await over()) <= 0, `+${await over()}px`);
|
||||
|
||||
// ══════════ 프로덕션 마감(v19): 검증 · 실행취소 · 단축키 · 인쇄 ══════════
|
||||
await gotoFile("gaon-lms");
|
||||
|
||||
// 채점 검증: 범위 밖 점수는 이유를 말하고 저장을 막는다. 정상 복귀하면 푼다
|
||||
await jclick('[data-view="grades"]');
|
||||
await page.evaluate(() => document.querySelector('[data-assign="a2"]').click());
|
||||
await page.waitForSelector("#as-dialog[open]");
|
||||
await page.$eval("#g1", (el) => { el.value = "150"; el.dispatchEvent(new Event("input", { bubbles: true })); });
|
||||
pass(
|
||||
"가온 채점 범위 검증(150 차단)",
|
||||
(await page.$eval("#as-warn", (w) => !w.hidden && w.textContent.includes("0에서 100"))) &&
|
||||
(await page.$eval("#as-save", (b) => b.disabled)) &&
|
||||
(await page.$eval("#g1", (i) => i.getAttribute("aria-invalid") === "true"))
|
||||
);
|
||||
await page.$eval("#g1", (el) => { el.value = "92"; el.dispatchEvent(new Event("input", { bubbles: true })); });
|
||||
pass(
|
||||
"가온 채점 정상 복귀(차단 해제)",
|
||||
(await page.$eval("#as-warn", (w) => w.hidden)) && !(await page.$eval("#as-save", (b) => b.disabled))
|
||||
);
|
||||
await page.keyboard.press("Escape");
|
||||
|
||||
// 단축키: 숫자키 뷰 전환, / 검색 포커스
|
||||
await page.focus("body");
|
||||
await page.keyboard.press("3");
|
||||
pass("가온 단축키 3(출석부)", await page.$eval("#view-attendance", (el) => el.classList.contains("is-on")));
|
||||
await page.keyboard.press("/");
|
||||
pass(
|
||||
"가온 단축키 /(수강생 검색)",
|
||||
(await page.$eval("#view-students", (el) => el.classList.contains("is-on"))) &&
|
||||
(await page.evaluate(() => document.activeElement.id === "q"))
|
||||
);
|
||||
|
||||
// 알림 센터 Esc 닫힘
|
||||
await page.evaluate(() => document.getElementById("bell").click());
|
||||
await page.keyboard.press("Escape");
|
||||
pass("가온 알림 Esc 닫힘", await page.$eval("#bell-menu", (m) => !m.classList.contains("is-open")));
|
||||
pass("가온 인쇄 스타일", fs2.readFileSync(path.join(root, "public/work/gaon-lms/styles/main.css"), "utf8").includes("@media print"));
|
||||
|
||||
await gotoFile("dure-enrollment");
|
||||
|
||||
// 실행취소: 신청 → 취소 → 되돌리기 → 원상 복구
|
||||
await jclick('.tab[data-view="catalog"]');
|
||||
await jclick('[data-course="econ210"]');
|
||||
await page.waitForSelector("#course-dialog[open]");
|
||||
await jclick("#cd-apply");
|
||||
await sleep(150);
|
||||
await jclick('.tab[data-view="enrollments"]');
|
||||
await jclick("[data-cancel]");
|
||||
pass("두레 취소 안내·실행취소 버튼", await page.$eval("#en-status", (s) => !!s.querySelector(".undo-btn")));
|
||||
await page.evaluate(() => document.querySelector("#en-status .undo-btn").click());
|
||||
await sleep(150);
|
||||
pass(
|
||||
"두레 실행취소 복원",
|
||||
(await page.$$("#en-table tbody tr")).length === 1 && (await page.$eval("#credit-now", (el) => el.textContent)) === "3"
|
||||
);
|
||||
|
||||
// 단축키: 숫자키 탭 전환, / 검색 포커스
|
||||
await page.focus("body");
|
||||
await page.keyboard.press("2");
|
||||
pass("두레 단축키 2(관심 과목)", await page.$eval("#view-starred", (el) => el.classList.contains("is-on")));
|
||||
await page.keyboard.press("/");
|
||||
pass(
|
||||
"두레 단축키 /(과목 검색)",
|
||||
(await page.$eval("#view-catalog", (el) => el.classList.contains("is-on"))) &&
|
||||
(await page.evaluate(() => document.activeElement.id === "q"))
|
||||
);
|
||||
pass("두레 인쇄 스타일", fs2.readFileSync(path.join(root, "public/work/dure-enrollment/styles/main.css"), "utf8").includes("@media print"));
|
||||
|
||||
// ══════════ 좁은 폭 ══════════
|
||||
for (const w of [320, 390]) {
|
||||
await page.setViewport({ width: w, height: 800 });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue