av-kit
Version:
AVFoundation Recorder kit for Node.js
192 lines (158 loc) • 5 kB
text/typescript
// Required by Jest: hoisting mocks to the top
jest.mock("../src/ffmpeg-manager", () => ({
FFmpegProcessManager: jest.fn(),
}));
jest.mock("../src/utils");
import { Recorder } from "../src/recorder";
import {
Camera,
Display,
DisplayRecordingItem,
Microphone,
RecorderOptions,
WebcamRecordingItem,
} from "../src/types";
// Mock devices
const devicesMock = {
displays: [
{ id: "display1", name: "Main Display" } as Display,
{ id: "display2", name: "Secondary Display" } as Display,
],
cameras: [
{ id: "camera1", name: "FaceTime HD Camera" } as Camera,
{ id: "camera2", name: "External Camera" } as Camera,
],
microphones: [
{ id: "mic1", name: "Built-in Microphone" } as Microphone,
{ id: "mic2", name: "External Microphone" } as Microphone,
],
};
// Mock process manager
const mockProcessManager = {
startProcess: jest.fn().mockResolvedValue(undefined),
stopProcess: jest.fn().mockResolvedValue(undefined),
stopAllProcesses: jest.fn().mockResolvedValue(undefined),
};
describe("Recorder", () => {
let recorder: Recorder;
// Mock utilities that will be spied on
const utils = require("../src/utils");
const FFmpegProcessManager =
require("../src/ffmpeg-manager").FFmpegProcessManager;
// Ensure we have valid Display and Camera objects with non-null assertion
const display = devicesMock.displays[0]!;
const camera = devicesMock.cameras[0]!;
const microphone = devicesMock.microphones[0]!;
const displayItem: DisplayRecordingItem = {
type: "display",
display,
};
const webcamItem: WebcamRecordingItem = {
type: "webcam",
camera,
microphone,
};
const options: RecorderOptions = {
output_directory: "/test/output",
items: [displayItem, webcamItem],
};
beforeEach(() => {
// Clear all mocks before each test
jest.clearAllMocks();
// Configure the mocks for this test
utils.createOutputDirectory.mockImplementation((dir: string) => dir);
utils.getAbsolutePath.mockImplementation((path: string) => path);
utils.getDeviceIndex.mockResolvedValue(0);
utils.getVideoMetadata.mockResolvedValue({
dimensions: {
width: 1920,
height: 1080,
},
durationInSeconds: 60,
});
utils.waitForFile.mockResolvedValue(true);
// Make FFmpegProcessManager return our mock
FFmpegProcessManager.mockImplementation(() => mockProcessManager);
// Create a fresh recorder for each test
recorder = new Recorder(options);
});
describe("prepare", () => {
it("should prepare recorder for recording", async () => {
// Act
await recorder.prepare();
// Assert
expect(utils.createOutputDirectory).toHaveBeenCalledWith(
options.output_directory
);
});
it("should throw error if already prepared", async () => {
// Arrange
await recorder.prepare();
// Act & Assert
await expect(recorder.prepare()).rejects.toThrow();
});
it("should throw error if no recording items provided", async () => {
// Arrange
const emptyRecorder = new Recorder({
output_directory: "/test/output",
items: [],
});
// Act & Assert
await expect(emptyRecorder.prepare()).rejects.toThrow();
});
});
describe("start", () => {
it("should start recording", async () => {
// Arrange
await recorder.prepare();
// Act
await recorder.start();
// Assert - each item should spawn a process
expect(mockProcessManager.startProcess).toHaveBeenCalledTimes(
options.items.length
);
});
it("should throw error if already recording", async () => {
// Arrange
await recorder.prepare();
await recorder.start();
// Set the recording flag manually
Object.defineProperty(recorder, "recording", {
value: true,
writable: true,
});
// Act & Assert
await expect(recorder.start()).rejects.toThrow();
});
it("should auto-prepare if not prepared", async () => {
// Act
await recorder.start();
// Assert
expect(mockProcessManager.startProcess).toHaveBeenCalledTimes(
options.items.length
);
});
});
describe("stop", () => {
it("should stop recording and return results", async () => {
// Arrange
await recorder.prepare();
await recorder.start();
// Set the recording flag manually
Object.defineProperty(recorder, "recording", {
value: true,
writable: true,
});
// Act
const result = await recorder.stop();
// Assert
expect(mockProcessManager.stopAllProcesses).toHaveBeenCalled();
expect(result).toHaveProperty("files");
expect(result.files.length).toBe(options.items.length);
});
it("should throw error if no recording in progress", async () => {
// Act & Assert
await expect(recorder.stop()).rejects.toThrow();
});
});
});