From 2eea66d545cac7c07999f8c42cff2233505ff8b1 Mon Sep 17 00:00:00 2001 From: azizu06 Date: Sat, 11 Jul 2026 21:52:50 -0400 Subject: [PATCH 1/2] fix: play uploaded video in results --- src/app/globals.css | 5 +++++ src/components/capcheck-app.test.tsx | 28 ++++++++++++++++++++++++++++ src/components/capcheck-app.tsx | 13 +++++++++++++ src/components/scorecard.tsx | 26 +++++++++++++++++++------- 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index 261c704..df5d8f4 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -669,6 +669,11 @@ input:disabled { aspect-ratio: 9 / 16; } +.video-preview { + width: 100%; + object-fit: contain; +} + .video-facade svg { width: 44px; height: 44px; diff --git a/src/components/capcheck-app.test.tsx b/src/components/capcheck-app.test.tsx index 0560648..526b384 100644 --- a/src/components/capcheck-app.test.tsx +++ b/src/components/capcheck-app.test.tsx @@ -218,6 +218,34 @@ describe("CapCheckApp", () => { expect((request.body as FormData).get("file")).toEqual(file); }); + it("plays the submitted upload in the completed result and releases its preview", async () => { + const createObjectURL = vi.fn(() => "blob:capcheck-upload-preview"); + const revokeObjectURL = vi.fn(); + vi.stubGlobal("URL", Object.assign(URL, { createObjectURL, revokeObjectURL })); + vi.stubGlobal( + "fetch", + vi.fn().mockResolvedValue( + sseResponse({ type: "complete", scorecard: DEMO_SCORECARDS.partialFailure }), + ), + ); + const user = userEvent.setup(); + const { unmount } = render(); + const file = new File(["video"], "earnings-demo.mp4", { type: "video/mp4" }); + + await user.upload(screen.getByLabelText(/choose a video file/i), file); + await user.click(checkItButton()); + + const preview = await screen.findByLabelText( + "Play uploaded video: demo-earnings-claims.mp4", + ); + expect(preview).toHaveAttribute("src", "blob:capcheck-upload-preview"); + expect(preview).toHaveAttribute("controls"); + expect(createObjectURL).toHaveBeenCalledWith(file); + + unmount(); + expect(revokeObjectURL).toHaveBeenCalledWith("blob:capcheck-upload-preview"); + }); + it("allows a selected upload to be removed", async () => { vi.stubGlobal("fetch", vi.fn()); const user = userEvent.setup(); diff --git a/src/components/capcheck-app.tsx b/src/components/capcheck-app.tsx index 36f32de..2c9d8c0 100644 --- a/src/components/capcheck-app.tsx +++ b/src/components/capcheck-app.tsx @@ -28,6 +28,7 @@ export function CapCheckApp() { const [progress, setProgress] = useState([]); const [scorecard, setScorecard] = useState(null); const [loading, setLoading] = useState(false); + const [uploadPreviewUrl, setUploadPreviewUrl] = useState(null); const scenario = useMemo(() => { if (typeof window === "undefined") return undefined; @@ -42,6 +43,17 @@ export function CapCheckApp() { }; }, []); + useEffect(() => { + if (!file) { + setUploadPreviewUrl(null); + return; + } + + const objectUrl = URL.createObjectURL(file); + setUploadPreviewUrl(objectUrl); + return () => URL.revokeObjectURL(objectUrl); + }, [file]); + const performAnalysis = async (submitUrl: string, submitFile: File | null) => { setValidation(""); setMiniError(""); @@ -175,6 +187,7 @@ export function CapCheckApp() { )} void performAnalysis(url, file)} onCheckAnother={reset} /> diff --git a/src/components/scorecard.tsx b/src/components/scorecard.tsx index bcd6875..9ffb0d0 100644 --- a/src/components/scorecard.tsx +++ b/src/components/scorecard.tsx @@ -32,10 +32,12 @@ const displayUrl = (url: string) => url.replace(/^https?:\/\//, ""); export function ScorecardView({ scorecard, + uploadPreviewUrl, onRunAgain, onCheckAnother, }: { scorecard: Scorecard; + uploadPreviewUrl: string | null; onRunAgain(): void; onCheckAnother(): void; }) { @@ -214,13 +216,23 @@ export function ScorecardView({