u1: SlideCanvas iframe sandbox += allow-scripts (allow-same-origin preserved)
→ embedded-mode script in slide_base.html now applies html.embedded
→ standalone CSS reset deactivates inside iframe; no clipping
u2: designAgentApi.loadRun merges candidate_evidence + v4_all_judgments
+ v4_candidates via Map<template_id|id|frame_id> dedup,
LABEL_PRIORITY (use_as_is<light_edit<restructure<reject) then
confidence desc, capped TOP_N_FRAMES=6
u3: Home.handleGenerate useCallback deps = [uploadedFile, slidePlan,
userSelection, pendingZones, pendingLayout] (5-tuple, stale-closure fix)
u4: tests/manual/imp47a_e2e.md — mdx03 manual e2e spec (5 axes)
Frontend-only. Backend src/ untouched. No template/catalog edits.
Determinism preserved (no LLM in frontend merge logic).
Baseline: pytest -q tests → 623 passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
91 lines
6.0 KiB
Markdown
91 lines
6.0 KiB
Markdown
# IMP-47A — mdx03 frontend stabilization manual e2e
|
||
|
||
Scope: frontend-only. Backend pipeline must NOT be modified during this test.
|
||
Path under test: `mdx=03` (default sample loaded on page open).
|
||
|
||
## Preconditions
|
||
|
||
- Backend running on `http://localhost:8001` (`uvicorn src.main:app --port 8001`).
|
||
- Frontend dev server running (`cd Front && npm run dev`).
|
||
- Working tree at IMP-47A Stage 3 HEAD (u1+u2+u3 applied to `Front/client/src/components/SlideCanvas.tsx`, `Front/client/src/services/designAgentApi.ts`, `Front/client/src/pages/Home.tsx`).
|
||
- Browser opens `http://localhost:5173/?mdx=03` (or default route, which auto-loads `mdx=03`).
|
||
|
||
## Section 1 — iframe rendering (axis 1)
|
||
|
||
Goal: verify u1 sandbox change lets `slide_base.html` script apply `html.embedded` class so the slide is not clipped inside the iframe.
|
||
|
||
Steps:
|
||
1. Open the app; wait for `mdx=03` auto-load and initial `final.html` render.
|
||
2. Click the "슬라이드 플랜 생성하기" button; wait for `run "<id>" 완료` toast.
|
||
3. Open browser DevTools → Elements; locate the iframe inside `SlideCanvas`; switch context to the iframe document.
|
||
4. Confirm `<html class="embedded">` is present (not just `<html>`).
|
||
5. Confirm the rendered slide content fills the 1280×720 frame with no top padding offset and no clipping at the bottom.
|
||
|
||
Pass: `html.embedded` class present AND no visible vertical shift/clipping.
|
||
Fail signal: iframe content pushed downward, footer cut off, or `html` lacks `embedded` class (means script never ran → sandbox regression).
|
||
|
||
## Section 2 — multi-source frame candidates (axis 2)
|
||
|
||
Goal: verify u2 3-source merge surfaces candidates from `candidate_evidence`, `v4_all_judgments`, and `v4_candidates` with deterministic dedup and cap.
|
||
|
||
Steps:
|
||
1. After Section 1 success, click any zone in the canvas; right panel switches to the "frame" tab.
|
||
2. In the frame candidate list, count visible candidates. Expect ≤ `TOP_N_FRAMES` (=6).
|
||
3. Open DevTools → Network → reload `/api/run/<id>`; inspect the JSON response and confirm at least two of `candidate_evidence`, `v4_all_judgments`, `v4_candidates` are non-empty.
|
||
4. Cross-check: union of `template_id ?? id ?? frame_id` keys from all three arrays (deduped, capped at 6) equals the UI list count and order.
|
||
5. Confirm the order respects LABEL_PRIORITY (`use_as_is` < `light_edit` < `restructure` < `reject`) then descending confidence.
|
||
|
||
Pass: union/dedup/cap/order all match.
|
||
Fail signal: only candidates from a single source visible, duplicates by template_id, more than 6 items, or order violates LABEL_PRIORITY.
|
||
|
||
## Section 3 — frame / layout override regeneration (axis 3)
|
||
|
||
Goal: verify u3 5-dep `handleGenerate` callback delivers the latest override state to backend (no stale closure).
|
||
|
||
Steps:
|
||
1. From Section 2, pick a non-default frame candidate (one whose label is not `use_as_is`); click "이 프레임 적용".
|
||
2. Confirm bottom-left button transforms to "선택대로 재생성하기" with amber pulse dot (hasPendingChanges = true).
|
||
3. Without any extra clicks, click "선택대로 재생성하기".
|
||
4. Open DevTools → Network → inspect the POST `/api/pipeline` body; confirm `overrides.frames` contains the chosen `{unit_id: frame_id}` mapping.
|
||
5. After success toast, confirm new `run_id` differs from the previous run, and the rendered iframe reflects the chosen frame (frame DOM class / id matches selection).
|
||
6. Repeat with a layout-card "적용하기" → pending overlay enters → "선택대로 재생성하기"; confirm POST body includes `overrides.layout` with the chosen preset id.
|
||
|
||
Pass: every override (frames / layout / zoneSections / zoneGeometries when applicable) reaches backend on first click; new `run_id` returned.
|
||
Fail signal: POST body lacks the override, or backend re-renders with the previous selection (stale closure regression).
|
||
|
||
## Section 4 — pending overlay enter / cancel / clear (axis 4)
|
||
|
||
Goal: verify pendingLayout overlay enters on "적용하기", exits on "취소", and auto-clears on successful regenerate.
|
||
|
||
Steps:
|
||
1. Click any non-current layout card "적용하기" button.
|
||
2. Confirm amber dashed overlay appears over `.slide-body` area with `PENDING BODY LAYOUT` label and chosen layout id.
|
||
3. Confirm "취소" button (top-right of canvas) is visible.
|
||
4. Click "취소"; confirm overlay disappears, `userSelection` resets, and `hasPendingChanges` indicator clears.
|
||
5. Re-enter pending mode (apply a layout again), this time click "선택대로 재생성하기"; confirm overlay disappears on success (Home.tsx clears `pendingLayout` + `hasPendingChanges` before pipeline call) and the new `final.html` renders inline (no overlay).
|
||
|
||
Pass: enter → cancel → re-enter → regenerate cycle leaves no overlay, no stuck pending state, no leftover `hasPendingChanges` flag.
|
||
Fail signal: overlay persists after regenerate, button stays in amber state, or cancel does not restore the canvas.
|
||
|
||
## Section 5 — mdx03 end-to-end pass (axis 5)
|
||
|
||
Goal: smoke run combining Sections 1–4 in one session to validate mdx03 demo path.
|
||
|
||
Steps:
|
||
1. Fresh reload `http://localhost:5173/?mdx=03`.
|
||
2. Click "슬라이드 플랜 생성하기" → wait for run completion → confirm iframe renders cleanly (Section 1 pass).
|
||
3. Click any zone → inspect 2+ candidates in frame list (Section 2 pass).
|
||
4. Apply a non-default frame → "선택대로 재생성하기" → new run id + iframe reflects override (Section 3 pass).
|
||
5. Apply a non-default layout → confirm overlay → "선택대로 재생성하기" → overlay clears (Section 4 pass).
|
||
6. Capture: run_id chain, final.html path under `data/runs/<run_id>/final.html`, and DOM screenshot of the rendered iframe content.
|
||
|
||
Pass: all five sections green, no console errors, no toast errors, three distinct `run_id`s produced across the session.
|
||
Fail signal: any earlier section regression OR backend pipeline failure (out-of-scope for IMP-47A — log separately).
|
||
|
||
## Out of scope (not tested here)
|
||
|
||
- AI fallback activation in `Step 12` (`light_edit` / `restructure`) — IMP-47B.
|
||
- Frame cache (#62 / IMP-46).
|
||
- mdx04 / mdx05 path-specific axes.
|
||
- Automated Playwright e2e replacement — future work.
|