From 6ebd15edef09350e1c1bde30d60b0e9a3742b9a5 Mon Sep 17 00:00:00 2001 From: wiiiii123 Date: Sat, 9 May 2026 08:53:53 +0700 Subject: [PATCH 1/2] fix(recording): stop browser recorder before cleanup --- src/hooks/useScreenRecorder.test.ts | 30 +++++++++++++++++++++++++++++ src/hooks/useScreenRecorder.ts | 6 +++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/hooks/useScreenRecorder.test.ts b/src/hooks/useScreenRecorder.test.ts index e98554ad1..344249031 100644 --- a/src/hooks/useScreenRecorder.test.ts +++ b/src/hooks/useScreenRecorder.test.ts @@ -132,6 +132,11 @@ function stopRecording( if (webcamRecorder && webcamRecorder.state !== "inactive") { webcamRecorder.stop(); } + try { + recorder.requestData(); + } catch { + // Stopping should continue even if the browser refuses an explicit flush. + } recorder.stop(); return { stopped: true, wasNative: false }; } @@ -328,6 +333,31 @@ describe("useScreenRecorder state machine", () => { expect(callOrder).toEqual(["resume", "stop"]); }); + it("flushes the current recorder data before stopping", () => { + const callOrder: string[] = []; + recorder.requestData.mockImplementation(() => { + callOrder.push("requestData"); + }); + recorder.stop.mockImplementation(() => { + callOrder.push("stop"); + }); + + stopRecording(recorder, false); + + expect(callOrder).toEqual(["requestData", "stop"]); + }); + + it("still stops when the explicit data flush fails", () => { + recorder.requestData.mockImplementation(() => { + throw new Error("flush failed"); + }); + + const result = stopRecording(recorder, false); + + expect(result.stopped).toBe(true); + expect(recorder.stop).toHaveBeenCalled(); + }); + it("still stops when resume throws from paused state", () => { recorder.pause(); recorder.resume.mockImplementation(() => { diff --git a/src/hooks/useScreenRecorder.ts b/src/hooks/useScreenRecorder.ts index 3d7933eed..695914787 100644 --- a/src/hooks/useScreenRecorder.ts +++ b/src/hooks/useScreenRecorder.ts @@ -1092,7 +1092,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn { } } pendingWebcamPathPromise.current = stopWebcamRecorder(); - cleanupCapturedMedia(); + try { + recorder.requestData(); + } catch (error) { + console.warn("Failed to flush recorder before stopping:", error); + } recorder.stop(); setRecording(false); setFinalizing(true); From 0fce11792d39367edd5d0a6181ad453a9d72b3bf Mon Sep 17 00:00:00 2001 From: wiiiii123 Date: Sat, 9 May 2026 09:01:39 +0700 Subject: [PATCH 2/2] test(recording): cover paused recorder flush order --- src/hooks/useScreenRecorder.test.ts | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/hooks/useScreenRecorder.test.ts b/src/hooks/useScreenRecorder.test.ts index 344249031..7cf480cfa 100644 --- a/src/hooks/useScreenRecorder.test.ts +++ b/src/hooks/useScreenRecorder.test.ts @@ -347,6 +347,24 @@ describe("useScreenRecorder state machine", () => { expect(callOrder).toEqual(["requestData", "stop"]); }); + it("resumes, flushes, then stops from paused state", () => { + recorder.pause(); + const callOrder: string[] = []; + recorder.resume.mockImplementation(() => { + callOrder.push("resume"); + }); + recorder.requestData.mockImplementation(() => { + callOrder.push("requestData"); + }); + recorder.stop.mockImplementation(() => { + callOrder.push("stop"); + }); + + stopRecording(recorder, false); + + expect(callOrder).toEqual(["resume", "requestData", "stop"]); + }); + it("still stops when the explicit data flush fails", () => { recorder.requestData.mockImplementation(() => { throw new Error("flush failed"); @@ -358,6 +376,26 @@ describe("useScreenRecorder state machine", () => { expect(recorder.stop).toHaveBeenCalled(); }); + it("still stops from paused state when the explicit data flush fails", () => { + recorder.pause(); + const callOrder: string[] = []; + recorder.resume.mockImplementation(() => { + callOrder.push("resume"); + }); + recorder.requestData.mockImplementation(() => { + callOrder.push("requestData"); + throw new Error("flush failed"); + }); + recorder.stop.mockImplementation(() => { + callOrder.push("stop"); + }); + + const result = stopRecording(recorder, false); + + expect(result.stopped).toBe(true); + expect(callOrder).toEqual(["resume", "requestData", "stop"]); + }); + it("still stops when resume throws from paused state", () => { recorder.pause(); recorder.resume.mockImplementation(() => {