fix: satisfy SH-116 frontend gates

This commit is contained in:
Alexandre Brandizzi 2026-09-23 04:43:58 -03:00
parent 0c8a7f877f
commit d6252552ca
6 changed files with 389 additions and 219 deletions

View file

@ -1,6 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { toast } from "react-toastify";
import type { MediaUploadRequest } from "@/app/(protected)/workorders/_components/media/media-uploader";
import { useMediaUploadSession } from "@/app/(protected)/workorders/_hooks/use-media-upload-session";
import type { UploadableMediaCategory } from "@/domain/work-orders/mappers/work-order-board-detail-mapper";
import { boardMediaItemsToMediaFiles } from "@/domain/work-orders/mappers/work-order-board-detail-mapper";
@ -84,43 +83,7 @@ function saveOwnership(
}
}
export function useSlideOverMediaState({
detailForRow,
row,
uploadMediaMutation,
deleteMediaMutation,
categorizeMediaMutation,
}: UseSlideOverMediaStateArgs) {
const workOrderId = detailForRow?.info.id ?? row?.id;
const workOrderType = detailForRow?.info.type ?? row?.type;
const {
data: mediaPolicy,
isError: mediaPolicyError,
isLoading: mediaPolicyLoading,
} = useWorkOrderMediaPolicy(workOrderId, workOrderId != null);
const resumableCompletionUpload = useResumableWorkOrderMediaUpload(
workOrderId ?? "",
"CompletionMedia",
mediaPolicy ?? undefined,
);
const policyCanUpload = mediaPolicy?.canUpload === true && !mediaPolicyError;
const completionHasGallery =
hasCompletionMediaGallery(detailForRow?.info.type) ||
hasCompletionMediaGallery(mediaPolicy?.workOrderType) ||
hasCompletionMediaGallery(row?.type);
const emergencyMedia =
isEmergencyReactiveWorkOrderType(mediaPolicy?.workOrderType) ||
isEmergencyReactiveWorkOrderType(workOrderType);
const emergencyMediaUnavailable = emergencyMedia && !policyCanUpload;
const emergencyMediaUnavailableMessage =
mediaPolicy?.canUpload === false
? "Uploads are not available for this work order."
: mediaPolicyError
? "Upload permissions could not be loaded. Refresh and try again."
: mediaPolicyLoading || !mediaPolicy
? "Loading upload permissions…"
: "Uploads are not available for this work order.";
const [mediaBatchPending, setMediaBatchPending] = useState(false);
function useMediaOwnership(workOrderId: string | number | undefined) {
const [ownership, setOwnershipState] = useState<MediaSurfaceOwnership>(() =>
loadOwnership(workOrderId),
);
@ -140,7 +103,27 @@ export function useSlideOverMediaState({
[workOrderId],
);
const persistCompletionUpload = useCallback(
return { ownership, updateOwnership };
}
type OwnershipUpdater = (update: (current: MediaSurfaceOwnership) => MediaSurfaceOwnership) => void;
type CompletionUploadArgs = {
emergencyMedia: boolean;
policyCanUpload: boolean;
resumableCompletionUpload: ReturnType<typeof useResumableWorkOrderMediaUpload>;
uploadMediaMutation: ReturnType<typeof useUploadWorkOrderMedia>;
updateOwnership: OwnershipUpdater;
};
function usePersistCompletionUpload({
emergencyMedia,
policyCanUpload,
resumableCompletionUpload,
uploadMediaMutation,
updateOwnership,
}: CompletionUploadArgs) {
return useCallback(
async (input: UploadWorkOrderMediaInput): Promise<{ id: string }> => {
if (emergencyMedia) {
if (!policyCanUpload) {
@ -175,9 +158,201 @@ export function useSlideOverMediaState({
uploadMediaMutation,
],
);
}
type SlideOverActionArgs = {
detailForRow: WorkOrderBoardDetail | undefined;
isLocal: (id: string) => boolean;
removeLocal: (id: string) => void;
updateOwnership: OwnershipUpdater;
uploadMediaMutation: ReturnType<typeof useUploadWorkOrderMedia>;
deleteMediaMutation: ReturnType<typeof useDeleteWorkOrderMedia>;
categorizeMediaMutation: ReturnType<typeof useUpdateWorkOrderMediaCategory>;
};
function useSlideOverMutationActions({
detailForRow,
isLocal,
removeLocal,
updateOwnership,
uploadMediaMutation,
deleteMediaMutation,
categorizeMediaMutation,
}: SlideOverActionArgs) {
const [mediaBatchPending, setMediaBatchPending] = useState(false);
const uploadExtraFiles = useCallback(
async (files: File[]) => {
setMediaBatchPending(true);
try {
for (const file of files) {
// Extra Docs stay on the legacy route and change category only by explicit PATCH.
const item = await uploadMediaMutation.mutateAsync({ file, category: "Extra" });
updateOwnership((current) => ({
completionOwnedIds: dropOwnedId(new Set(current.completionOwnedIds), item.id),
extraOwnedIds: addOwnedId(new Set(current.extraOwnedIds), item.id),
}));
}
} finally {
setMediaBatchPending(false);
}
},
[uploadMediaMutation, updateOwnership],
);
const categorizeMedia = useCallback(
(mediaId: string, category: UploadableMediaCategory) => {
const workOrderVersion = detailForRow?.info.rowVersion;
if (!workOrderVersion) {
toast.error("Missing work order version. Refresh and try again.");
return;
}
categorizeMediaMutation.mutate(
{ mediaId, category, workOrderVersion },
{
onSuccess: () => {
updateOwnership((current) => ({
completionOwnedIds: dropOwnedId(new Set(current.completionOwnedIds), mediaId),
extraOwnedIds:
category === "Extra"
? addOwnedId(new Set(current.extraOwnedIds), mediaId)
: dropOwnedId(new Set(current.extraOwnedIds), mediaId),
}));
},
},
);
},
[detailForRow?.info.rowVersion, categorizeMediaMutation, updateOwnership],
);
const removeMedia = useCallback(
(id: string) => {
if (isLocal(id)) {
removeLocal(id);
return;
}
const workOrderVersion = detailForRow?.info.rowVersion;
if (!workOrderVersion) {
toast.error("Missing work order version. Refresh and try again.");
return;
}
deleteMediaMutation.mutate({ mediaId: id, workOrderVersion });
},
[detailForRow?.info.rowVersion, isLocal, removeLocal, deleteMediaMutation],
);
return { mediaBatchPending, uploadExtraFiles, categorizeMedia, removeMedia };
}
function hasAnyCompletionGallery(...types: Array<string | undefined>): boolean {
return types.some(hasCompletionMediaGallery);
}
function hasEmergencyMediaType(...types: Array<string | undefined>): boolean {
return types.some(isEmergencyReactiveWorkOrderType);
}
function getEmergencyUploaderOptions(
emergencyMedia: boolean,
limits: Parameters<typeof emergencyMediaSizeError>[1],
) {
if (!emergencyMedia) {
return {
emergencyMediaTypeAllowed: undefined,
emergencyMediaTypeErrorMessage: undefined,
emergencyMediaAcceptAttribute: undefined,
emergencyMediaSizeError: undefined,
emergencyMediaUploadHint: undefined,
};
}
return {
emergencyMediaTypeAllowed: (file: File) => classifyMediaKind(file) !== null,
emergencyMediaTypeErrorMessage:
"Only photos (JPG, PNG, HEIC) and videos (MP4, MOV) are supported.",
emergencyMediaAcceptAttribute: emergencyMediaAcceptAttribute(),
emergencyMediaSizeError: (file: File) => emergencyMediaSizeError(file, limits),
emergencyMediaUploadHint: "Photos up to 10 MB · Videos up to 100 MB and 90 seconds",
};
}
function isAnyMediaMutationPending(...states: boolean[]): boolean {
return states.some(Boolean);
}
function getEmergencyMediaUnavailableMessage(
canUpload: boolean | undefined,
policyError: boolean,
policyLoading: boolean,
hasPolicy: boolean,
): string {
if (canUpload === false) return "Uploads are not available for this work order.";
if (policyError) return "Upload permissions could not be loaded. Refresh and try again.";
if (policyLoading || !hasPolicy) return "Loading upload permissions…";
return "Uploads are not available for this work order.";
}
function isPolicyUploadAllowed(canUpload: boolean | undefined, policyError: boolean): boolean {
return canUpload === true && !policyError;
}
function isEmergencyUploadUnavailable(emergencyMedia: boolean, policyCanUpload: boolean): boolean {
return emergencyMedia && !policyCanUpload;
}
export function useSlideOverMediaState({
detailForRow,
row,
uploadMediaMutation,
deleteMediaMutation,
categorizeMediaMutation,
}: UseSlideOverMediaStateArgs) {
const workOrderId = detailForRow?.info.id ?? row?.id;
const workOrderType = detailForRow?.info.type ?? row?.type;
const {
data: mediaPolicy,
isError: mediaPolicyError,
isLoading: mediaPolicyLoading,
} = useWorkOrderMediaPolicy(workOrderId, workOrderId != null);
const resumableCompletionUpload = useResumableWorkOrderMediaUpload(
workOrderId ?? "",
"CompletionMedia",
mediaPolicy ?? undefined,
);
const policyCanUpload = isPolicyUploadAllowed(mediaPolicy?.canUpload, mediaPolicyError);
const completionHasGallery = hasAnyCompletionGallery(
detailForRow?.info.type,
mediaPolicy?.workOrderType,
row?.type,
);
const emergencyMedia = hasEmergencyMediaType(mediaPolicy?.workOrderType, workOrderType);
const emergencyMediaUnavailable = isEmergencyUploadUnavailable(emergencyMedia, policyCanUpload);
const emergencyMediaUnavailableMessage = getEmergencyMediaUnavailableMessage(
mediaPolicy?.canUpload,
mediaPolicyError,
mediaPolicyLoading,
Boolean(mediaPolicy),
);
const { ownership, updateOwnership } = useMediaOwnership(workOrderId);
const persistCompletionUpload = usePersistCompletionUpload({
emergencyMedia,
policyCanUpload,
resumableCompletionUpload,
uploadMediaMutation,
updateOwnership,
});
const { uploads, startUploads, retryUpload, cancelUpload, removeLocal, isLocal } =
useMediaUploadSession(detailForRow, persistCompletionUpload);
const { mediaBatchPending, uploadExtraFiles, categorizeMedia, removeMedia } =
useSlideOverMutationActions({
detailForRow,
isLocal,
removeLocal,
updateOwnership,
uploadMediaMutation,
deleteMediaMutation,
categorizeMediaMutation,
});
const completionMedia = useMemo(
() => uploads.filter((item) => isCompletionSurfaceMedia(item, ownership, completionHasGallery)),
@ -193,68 +368,15 @@ export function useSlideOverMediaState({
);
const hasLocalUploading = uploads.some((item) => item.uploadStatus === "uploading");
const mediaPending =
mediaBatchPending ||
hasLocalUploading ||
uploadMediaMutation.isPending ||
resumableCompletionUpload.isPending ||
deleteMediaMutation.isPending ||
categorizeMediaMutation.isPending;
const uploadMediaRequests = (requests: MediaUploadRequest[]) => {
startUploads(requests);
};
const uploadExtraFiles = async (files: File[]) => {
setMediaBatchPending(true);
try {
for (const file of files) {
// Upload stays Extra; Aveta is applied only by an explicit Extra Docs PATCH.
const item = await uploadMediaMutation.mutateAsync({ file, category: "Extra" });
updateOwnership((current) => ({
completionOwnedIds: dropOwnedId(new Set(current.completionOwnedIds), item.id),
extraOwnedIds: addOwnedId(new Set(current.extraOwnedIds), item.id),
}));
}
} finally {
setMediaBatchPending(false);
}
};
const categorizeMedia = (mediaId: string, category: UploadableMediaCategory) => {
const workOrderVersion = detailForRow?.info.rowVersion;
if (!workOrderVersion) {
toast.error("Missing work order version. Refresh and try again.");
return;
}
categorizeMediaMutation.mutate(
{ mediaId, category, workOrderVersion },
{
onSuccess: () => {
updateOwnership((current) => ({
completionOwnedIds: dropOwnedId(new Set(current.completionOwnedIds), mediaId),
extraOwnedIds:
category === "Extra"
? addOwnedId(new Set(current.extraOwnedIds), mediaId)
: dropOwnedId(new Set(current.extraOwnedIds), mediaId),
}));
},
},
);
};
const removeMedia = (id: string) => {
if (isLocal(id)) {
removeLocal(id);
return;
}
const workOrderVersion = detailForRow?.info.rowVersion;
if (!workOrderVersion) {
toast.error("Missing work order version. Refresh and try again.");
return;
}
deleteMediaMutation.mutate({ mediaId: id, workOrderVersion });
};
const mediaPending = isAnyMediaMutationPending(
mediaBatchPending,
hasLocalUploading,
uploadMediaMutation.isPending,
resumableCompletionUpload.isPending,
deleteMediaMutation.isPending,
categorizeMediaMutation.isPending,
);
const emergencyUploaderOptions = getEmergencyUploaderOptions(emergencyMedia, mediaPolicy?.limits);
return {
completionMedia,
@ -263,20 +385,8 @@ export function useSlideOverMediaState({
emergencyMedia,
emergencyMediaUnavailable,
emergencyMediaUnavailableMessage,
emergencyMediaTypeAllowed: emergencyMedia
? (file: File) => classifyMediaKind(file) !== null
: undefined,
emergencyMediaTypeErrorMessage: emergencyMedia
? "Only photos (JPG, PNG, HEIC) and videos (MP4, MOV) are supported."
: undefined,
emergencyMediaAcceptAttribute: emergencyMedia ? emergencyMediaAcceptAttribute() : undefined,
emergencyMediaSizeError: emergencyMedia
? (file: File) => emergencyMediaSizeError(file, mediaPolicy?.limits)
: undefined,
emergencyMediaUploadHint: emergencyMedia
? "Photos up to 10 MB · Videos up to 100 MB and 90 seconds"
: undefined,
uploadMediaRequests,
...emergencyUploaderOptions,
uploadMediaRequests: startUploads,
uploadExtraFiles,
categorizeMedia,
retryUpload,

View file

@ -39,6 +39,39 @@ function resolveWorkOrderVersion(
return version || null;
}
function isEmergencyReactive(row: WorkOrderTableRow, detailType?: string, policyType?: string) {
return (
isEmergencyReactiveWorkOrderType(detailType) ||
isEmergencyReactiveWorkOrderType(policyType) ||
isEmergencyReactiveWorkOrderType(row.type)
);
}
function emergencyPolicyMessage(
canUpload: boolean | undefined,
isError: boolean,
isLoading: boolean,
): string {
if (canUpload === false) return "Uploads are not available for this work order.";
if (isError) return "Upload permissions could not be loaded. Refresh and try again.";
if (isLoading) return "Loading upload permissions…";
return "Uploads are not available for this work order.";
}
function getEmergencyUploaderOptions(
isEmergency: boolean,
limits: Parameters<typeof emergencyMediaSizeError>[1],
) {
if (!isEmergency) return {};
return {
fileSizeError: (file: File) => emergencyMediaSizeError(file, limits),
uploadHint: "Photos up to 10 MB · Videos up to 100 MB and 90 seconds",
typeAllowed: (file: File) => classifyMediaKind(file) !== null,
typeErrorMessage: "Only photos (JPG, PNG, HEIC) and videos (MP4, MOV) are supported.",
acceptAttribute: emergencyMediaAcceptAttribute(),
};
}
export function MediaUploadModal({ row, open, onOpenChange }: MediaUploadModalProps) {
const { data: detail, isLoading } = useWorkOrderBoardDetail(row.id, open);
const {
@ -53,10 +86,7 @@ export function MediaUploadModal({ row, open, onOpenChange }: MediaUploadModalPr
policyError ? undefined : (policy ?? undefined),
);
const deleteMutation = useDeleteWorkOrderMedia(row.id);
const isEmergency =
isEmergencyReactiveWorkOrderType(detail?.info.type) ||
isEmergencyReactiveWorkOrderType(policy?.workOrderType) ||
isEmergencyReactiveWorkOrderType(row.type);
const isEmergency = isEmergencyReactive(row, detail?.info.type, policy?.workOrderType);
const uploadMutation = isEmergency ? resumableUploadMutation : legacyUploadMutation;
const emergencyUploadUnavailable = isEmergency && (policy?.canUpload !== true || policyError);
const isReadOnly = isWorkOrderDocumentsReadOnly(detail?.info.status ?? row.status);
@ -65,6 +95,7 @@ export function MediaUploadModal({ row, open, onOpenChange }: MediaUploadModalPr
const pending = uploadMutation.isPending || deleteMutation.isPending;
const handleClose = guardDialogClose(pending, () => onOpenChange(false));
const emergencyUploaderOptions = getEmergencyUploaderOptions(isEmergency, policy?.limits);
const requireVersion = (): string | null => {
const version = resolveWorkOrderVersion(detail?.info.rowVersion, row.rowVersion);
@ -130,32 +161,14 @@ export function MediaUploadModal({ row, open, onOpenChange }: MediaUploadModalPr
onCancel={cancelUpload}
readOnly={isReadOnly}
uploadDisabled={emergencyUploadUnavailable}
fileSizeError={
isEmergency ? (file) => emergencyMediaSizeError(file, policy?.limits) : undefined
}
uploadHint={
isEmergency ? "Photos up to 10 MB · Videos up to 100 MB and 90 seconds" : undefined
}
{...emergencyUploaderOptions}
pending={pending}
typeAllowed={isEmergency ? (file) => classifyMediaKind(file) !== null : undefined}
typeErrorMessage={
isEmergency
? "Only photos (JPG, PNG, HEIC) and videos (MP4, MOV) are supported."
: undefined
}
acceptAttribute={isEmergency ? emergencyMediaAcceptAttribute() : undefined}
/>
)}
{emergencyUploadUnavailable && !isReadOnly && (
<div role="status" className="mt-2">
<Text variant="description">
{policy?.canUpload === false
? "Uploads are not available for this work order."
: policyError
? "Upload permissions could not be loaded. Refresh and try again."
: policyLoading || !policy
? "Loading upload permissions…"
: "Uploads are not available for this work order."}
{emergencyPolicyMessage(policy?.canUpload, policyError, policyLoading || !policy)}
</Text>
</div>
)}

View file

@ -40,6 +40,106 @@ function viewMediaItem(item: VendorMediaItem, blob: Blob): void {
window.setTimeout(() => URL.revokeObjectURL(url), 60_000);
}
function VendorMediaItems({
items,
onView,
}: {
items: VendorMediaItem[];
onView: (item: VendorMediaItem) => void;
}) {
return items.map((item) => (
<article key={item.id} className="vp-uplift-row">
<div className="vp-row">
<strong>File</strong>
<span>
{item.originalFileName} · {item.kind} · {formatBytes(item.sizeBytes)}
</span>
</div>
<div className="vp-actions">
<button
type="button"
className="vp-btn vp-btn-ghost"
disabled={!item.canView}
onClick={() => onView(item)}
>
{item.canView ? "View" : "Unavailable"}
</button>
</div>
</article>
));
}
function VendorUploadProgress({
pending,
progress,
onCancel,
}: {
pending: boolean;
progress: number | undefined;
onCancel: () => void;
}) {
if (!pending) return null;
return (
<div role="status" aria-live="polite">
{progress !== undefined ? (
<>
<progress max={100} value={progress} style={{ width: "100%" }} />
<VendorPortalFeedback>Uploading… {progress}%</VendorPortalFeedback>
</>
) : (
<VendorPortalFeedback>Preparing upload…</VendorPortalFeedback>
)}
<button type="button" className="vp-btn vp-btn-ghost" onClick={onCancel}>
Cancel upload
</button>
</div>
);
}
function VendorUploadError({
error,
canceled,
locked,
onRetry,
}: {
error: Error | null;
canceled: boolean;
locked: boolean;
onRetry: () => void;
}) {
if (canceled) return <VendorPortalFeedback>Upload canceled.</VendorPortalFeedback>;
if (!error) return null;
return (
<div>
<VendorPortalFeedback tone="error">{safeMediaUploadErrorMessage(error)}</VendorPortalFeedback>
{!locked && error instanceof MediaUploadNetworkError && (
<button type="button" className="vp-btn vp-btn-ghost" onClick={onRetry}>
Retry upload
</button>
)}
</div>
);
}
function VendorUploadButton({
canUpload,
pending,
onClick,
}: {
canUpload: boolean;
pending: boolean;
onClick: () => void;
}) {
if (!canUpload) return null;
return (
<div className="vp-actions">
<button type="button" className="vp-btn vp-btn-primary" disabled={pending} onClick={onClick}>
Upload photo or video
</button>
</div>
);
}
export function VendorMediaSection({ token, dispatchId, locked }: VendorMediaSectionProps) {
const inputRef = useRef<HTMLInputElement>(null);
const controllerRef = useRef<AbortController | null>(null);
@ -159,82 +259,27 @@ export function VendorMediaSection({ token, dispatchId, locked }: VendorMediaSec
<VendorPortalFeedback>No photos or videos uploaded yet.</VendorPortalFeedback>
)}
{items.map((item) => (
<article key={item.id} className="vp-uplift-row">
<div className="vp-row">
<strong>File</strong>
<span>
{item.originalFileName} · {item.kind} · {formatBytes(item.sizeBytes)}
</span>
</div>
<div className="vp-actions">
<button
type="button"
className="vp-btn vp-btn-ghost"
disabled={!item.canView}
onClick={() => void handleView(item)}
>
{item.canView ? "View" : "Unavailable"}
</button>
</div>
</article>
))}
<VendorMediaItems items={items} onView={(item) => void handleView(item)} />
<VendorUploadProgress
pending={uploadMutation.isPending}
progress={progress}
onCancel={() => controllerRef.current?.abort()}
/>
<VendorUploadError
error={uploadMutation.error}
canceled={uploadCanceled}
locked={locked}
onRetry={() => startUpload(failedFileRef.current, true)}
/>
{uploadMutation.isPending && (
<div role="status" aria-live="polite">
{progress !== undefined ? (
<>
<progress max={100} value={progress} style={{ width: "100%" }} />
<VendorPortalFeedback>Uploading… {progress}%</VendorPortalFeedback>
</>
) : (
<VendorPortalFeedback>Preparing upload…</VendorPortalFeedback>
)}
<button
type="button"
className="vp-btn vp-btn-ghost"
onClick={() => controllerRef.current?.abort()}
>
Cancel upload
</button>
</div>
)}
{uploadCanceled && <VendorPortalFeedback>Upload canceled.</VendorPortalFeedback>}
{uploadMutation.isError && !uploadCanceled && (
<div>
<VendorPortalFeedback tone="error">
{safeMediaUploadErrorMessage(uploadMutation.error)}
</VendorPortalFeedback>
{!locked && uploadMutation.error instanceof MediaUploadNetworkError && (
<button
type="button"
className="vp-btn vp-btn-ghost"
disabled={uploadMutation.isPending}
onClick={() => startUpload(failedFileRef.current, true)}
>
Retry upload
</button>
)}
</div>
)}
{canUpload && (
<div className="vp-actions">
<button
type="button"
className="vp-btn vp-btn-primary"
disabled={uploadMutation.isPending}
onClick={() => {
uploadMutation.reset();
inputRef.current?.click();
}}
>
Upload photo or video
</button>
</div>
)}
<VendorUploadButton
canUpload={canUpload}
pending={uploadMutation.isPending}
onClick={() => {
uploadMutation.reset();
inputRef.current?.click();
}}
/>
{locked && (
<VendorPortalFeedback>

View file

@ -292,7 +292,7 @@ describe("resumable media upload engine", () => {
.filter((entry) => entry.method === "PUT")
.map((entry) => Number(/chunks\/(\d+)$/.exec(entry.url)?.[1]));
expect(putIndices.filter((index) => index === 0)).toEqual([0]);
expect(putIndices.at(-1)).toBe(1);
expect(putIndices[putIndices.length - 1]).toBe(1);
});
it("retries a failed create with the same idempotency key and never re-creates after chunk loss", async () => {

View file

@ -78,7 +78,7 @@ vi.mock("@/domain/work-orders/use-cases/use-resumable-work-order-media-upload",
},
}));
function baseRow(type = "Emergency"): WorkOrderTableRow {
function baseRow(type: WorkOrderTableRow["type"] = "Emergency"): WorkOrderTableRow {
return {
id: 7,
woNumber: "WO-7",

View file

@ -9,6 +9,7 @@ const policyState = vi.hoisted(() => ({
isError: false,
isLoading: true,
}));
const legacyUploadMutateAsync = vi.fn().mockResolvedValue({ id: "legacy" });
vi.mock("@/domain/work-orders/use-cases/use-work-order-media-policy", () => ({
useWorkOrderMediaPolicy: () => policyState,
@ -96,13 +97,14 @@ function detailWithMedia(
function idleMutation() {
return {
mutate: vi.fn(),
mutateAsync: vi.fn().mockResolvedValue({}),
mutateAsync: legacyUploadMutateAsync,
isPending: false,
} as never;
}
describe("useSlideOverMediaState completion filter", () => {
beforeEach(() => {
legacyUploadMutateAsync.mockClear();
sessionStorage.clear();
policyState.data = undefined;
policyState.isError = false;
@ -370,7 +372,7 @@ describe("useSlideOverMediaState completion filter", () => {
});
await waitFor(() => {
expect(legacyUpload.mutateAsync).not.toHaveBeenCalled();
expect(legacyUploadMutateAsync).not.toHaveBeenCalled();
expect(result.current.completionMedia.some((item) => item.uploadStatus === "failed")).toBe(
true,
);
@ -405,7 +407,7 @@ describe("useSlideOverMediaState completion filter", () => {
});
await waitFor(() => {
expect(legacyUpload.mutateAsync).not.toHaveBeenCalled();
expect(legacyUploadMutateAsync).not.toHaveBeenCalled();
expect(result.current.completionMedia[0]?.uploadStatus).toBe("failed");
});
});