mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 05:43:12 +00:00
Extra Docs advertised the 90-second limit but only checked type and size. Both dispatcher surfaces now share one screening step (type, size, count, duration), and the Completion Doc media tab and Extra Docs count the whole work order's photos and videos rather than only their own tab's share. Failed local uploads no longer count toward the limit.
82 lines
3 KiB
TypeScript
82 lines
3 KiB
TypeScript
import { fireEvent, waitFor } from "@testing-library/react";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { toast } from "react-toastify";
|
|
import { SlideOverExtrasTab } from "@/app/(protected)/workorders/_components/detail/tabs/slide-over-extras-tab";
|
|
import { probeVideoDurationSeconds } from "@/lib/probe-video-duration";
|
|
import { renderWithProviders } from "@/test/test-utils";
|
|
|
|
vi.mock("react-toastify", () => ({ toast: { error: vi.fn() } }));
|
|
vi.mock("@/lib/probe-video-duration", () => ({ probeVideoDurationSeconds: vi.fn() }));
|
|
|
|
const MB = 1_000_000;
|
|
|
|
function phoneFile(name: string, type: string, size: number): File {
|
|
return { name, type, size } as File;
|
|
}
|
|
|
|
function selectFiles(files: File[]) {
|
|
const input = document.querySelector('input[type="file"]');
|
|
fireEvent.change(input as HTMLInputElement, { target: { files } });
|
|
}
|
|
|
|
describe("SlideOverExtrasTab SH-116 media contract", () => {
|
|
beforeEach(() => {
|
|
vi.mocked(toast.error).mockClear();
|
|
vi.mocked(probeVideoDurationSeconds).mockReset();
|
|
vi.mocked(probeVideoDurationSeconds).mockResolvedValue(30);
|
|
});
|
|
|
|
it("rejects a video longer than 90 seconds before upload", async () => {
|
|
vi.mocked(probeVideoDurationSeconds).mockResolvedValue(95);
|
|
const onUpload = vi.fn();
|
|
renderWithProviders(
|
|
<SlideOverExtrasTab uploads={[]} isReadOnly={false} onUpload={onUpload} onRemove={vi.fn()} />,
|
|
);
|
|
|
|
selectFiles([phoneFile("IMG_0700.MOV", "video/quicktime", 40 * MB)]);
|
|
|
|
await waitFor(() =>
|
|
expect(toast.error).toHaveBeenCalledWith("Videos must be 90 seconds or shorter."),
|
|
);
|
|
expect(onUpload).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("rejects the 11th photo and 4th video counted across the whole work order", async () => {
|
|
const onUpload = vi.fn();
|
|
const report = phoneFile("inspection.pdf", "application/pdf", 2 * MB);
|
|
renderWithProviders(
|
|
<SlideOverExtrasTab
|
|
uploads={[]}
|
|
mediaCounts={{ photos: 10, videos: 3 }}
|
|
isReadOnly={false}
|
|
onUpload={onUpload}
|
|
onRemove={vi.fn()}
|
|
/>,
|
|
);
|
|
|
|
selectFiles([
|
|
phoneFile("IMG_0701.jpg", "image/jpeg", 2 * MB),
|
|
phoneFile("IMG_0702.MOV", "video/quicktime", 20 * MB),
|
|
report,
|
|
]);
|
|
|
|
await waitFor(() => expect(onUpload).toHaveBeenCalledTimes(1));
|
|
expect(onUpload).toHaveBeenCalledWith([report]);
|
|
expect(toast.error).toHaveBeenCalledWith("A work order can have at most 10 photos.");
|
|
expect(toast.error).toHaveBeenCalledWith("A work order can have at most 3 videos.");
|
|
});
|
|
|
|
it("accepts a ~60 MB phone video whose duration the browser cannot read", async () => {
|
|
vi.mocked(probeVideoDurationSeconds).mockResolvedValue(null);
|
|
const onUpload = vi.fn();
|
|
const video = phoneFile("IMG_0703.MOV", "", 60 * MB);
|
|
renderWithProviders(
|
|
<SlideOverExtrasTab uploads={[]} isReadOnly={false} onUpload={onUpload} onRemove={vi.fn()} />,
|
|
);
|
|
|
|
selectFiles([video]);
|
|
|
|
await waitFor(() => expect(onUpload).toHaveBeenCalledWith([video]));
|
|
expect(toast.error).not.toHaveBeenCalled();
|
|
});
|
|
});
|