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>
6.0 KiB
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-loadsmdx=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:
- Open the app; wait for
mdx=03auto-load and initialfinal.htmlrender. - Click the "슬라이드 플랜 생성하기" button; wait for
run "<id>" 완료toast. - Open browser DevTools → Elements; locate the iframe inside
SlideCanvas; switch context to the iframe document. - Confirm
<html class="embedded">is present (not just<html>). - 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:
- After Section 1 success, click any zone in the canvas; right panel switches to the "frame" tab.
- In the frame candidate list, count visible candidates. Expect ≤
TOP_N_FRAMES(=6). - Open DevTools → Network → reload
/api/run/<id>; inspect the JSON response and confirm at least two ofcandidate_evidence,v4_all_judgments,v4_candidatesare non-empty. - Cross-check: union of
template_id ?? id ?? frame_idkeys from all three arrays (deduped, capped at 6) equals the UI list count and order. - 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:
- From Section 2, pick a non-default frame candidate (one whose label is not
use_as_is); click "이 프레임 적용". - Confirm bottom-left button transforms to "선택대로 재생성하기" with amber pulse dot (hasPendingChanges = true).
- Without any extra clicks, click "선택대로 재생성하기".
- Open DevTools → Network → inspect the POST
/api/pipelinebody; confirmoverrides.framescontains the chosen{unit_id: frame_id}mapping. - After success toast, confirm new
run_iddiffers from the previous run, and the rendered iframe reflects the chosen frame (frame DOM class / id matches selection). - Repeat with a layout-card "적용하기" → pending overlay enters → "선택대로 재생성하기"; confirm POST body includes
overrides.layoutwith 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:
- Click any non-current layout card "적용하기" button.
- Confirm amber dashed overlay appears over
.slide-bodyarea withPENDING BODY LAYOUTlabel and chosen layout id. - Confirm "취소" button (top-right of canvas) is visible.
- Click "취소"; confirm overlay disappears,
userSelectionresets, andhasPendingChangesindicator clears. - Re-enter pending mode (apply a layout again), this time click "선택대로 재생성하기"; confirm overlay disappears on success (Home.tsx clears
pendingLayout+hasPendingChangesbefore pipeline call) and the newfinal.htmlrenders 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:
- Fresh reload
http://localhost:5173/?mdx=03. - Click "슬라이드 플랜 생성하기" → wait for run completion → confirm iframe renders cleanly (Section 1 pass).
- Click any zone → inspect 2+ candidates in frame list (Section 2 pass).
- Apply a non-default frame → "선택대로 재생성하기" → new run id + iframe reflects override (Section 3 pass).
- Apply a non-default layout → confirm overlay → "선택대로 재생성하기" → overlay clears (Section 4 pass).
- 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_ids 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.