fix(recording): stop browser recorder before cleanup (#464)

* fix(recording): stop browser recorder before cleanup

* test(recording): cover paused recorder flush order
This commit is contained in:
Phạm Thị Minh Hồng
2026-05-09 09:04:08 +07:00
committed by GitHub
2 changed files with 73 additions and 1 deletions
+68
View File
@@ -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,69 @@ 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("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");
});
const result = stopRecording(recorder, false);
expect(result.stopped).toBe(true);
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(() => {
+5 -1
View File
@@ -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);