Summary
useVideoUpload is a critical hook that handles all video recording and upload logic, yet it has zero test coverage. This hook is responsible for:
- Combining multi-segment recordings into a single video
- Extracting video duration from a
Blob via a DOM <video> element
- Uploading to Blossom servers via
useUploadFile
- Tracking upload progress (0 → 0.25 → 1.0)
- Cleaning up
objectURL references after upload
- Showing toast notifications on success and error
Without tests, regressions in the upload pipeline are silent and hard to catch in CI.
Current behaviour
useVideoUpload.ts (~174 lines) has no colocated useVideoUpload.test.ts
- There is an active known limitation inside
combineSegments: when segments.length > 1, only the first segment is used and a toast is shown. This behaviour is intentional for MVP but is not verified by any assertion — so if the logic ever silently changes, nothing will catch it.
What needs to be tested
1. combineSegments — single segment (happy path)
- Returns the original
blob, blobUrl, and resolved duration
getVideoDuration is called once
- No toast is shown
2. combineSegments — multiple segments (current MVP behaviour)
- Shows a
"Multi-clip recording." toast with the correct description
- Returns only the first segment's
blob / blobUrl
getVideoDuration is called on the first segment
3. combineSegments — empty array
- Throws
'No segments to combine'
4. getVideoDuration — success
- Creates a
<video> element, sets src to an objectURL
- Resolves with
video.duration * 1000 (milliseconds)
- Calls
window.URL.revokeObjectURL after resolution
5. getVideoDuration — error
- Rejects with
'Failed to load video metadata' when video.onerror fires
- Calls
window.URL.revokeObjectURL on the error path too
6. uploadVideo — success flow
- Sets
uploadProgress to 0 → 0.1 → 0.25 → 1
- Calls
uploadFile with a File built from the combined blob
- Extracts the URL from the returned tags array (
[['url', 'https://...']])
- Returns
{ url, tags, duration }
- Revokes all blob URLs on completion
7. uploadVideo — upload returns no URL tag
- Throws
'Upload succeeded but no URL returned'
8. uploadVideo — uploadFile rejects
- Sets
uploadProgress back to 0
- Re-throws the error
- Shows a destructive toast:
'Upload snagged.'
9. isUploading flag
- Is
false initially
- Becomes
true while uploadVideoMutation is pending
- Returns to
false after completion
Mocking strategy
Follow the existing pattern from useProfileStats.test.ts. Key mocks needed:
vi.mock('@/hooks/useUploadFile', () => ({ useUploadFile: vi.fn() }));
vi.mock('@/hooks/useToast', () => ({ useToast: vi.fn(() => ({ toast: vi.fn() })) }));
Object.defineProperty(window, 'URL', {
value: { createObjectURL: vi.fn(() => 'blob:mock-url'), revokeObjectURL: vi.fn() },
});
getVideoDuration uses document.createElement('video') — mock it by dispatching loadedmetadata / error events on the created element, or by stubbing document.createElement.
Acceptance criteria
Related files
| File |
Role |
src/hooks/useVideoUpload.ts |
The hook under test |
src/hooks/useUploadFile.ts |
Blossom uploader — must be mocked |
src/hooks/useToast.ts |
Toast notifications — must be mocked |
src/hooks/useProfileStats.test.ts |
Reference for test style / mock setup |
src/test/setup.ts |
Global Vitest setup (jsdom) |
Notes
The multi-segment limitation (scenarios 2 and 3 above) is a known MVP shortcut, not a bug to fix in this issue. The goal of these tests is to document and lock in the current behaviour so that future work on FFmpeg.wasm concatenation has a clear regression baseline.
Summary
useVideoUploadis a critical hook that handles all video recording and upload logic, yet it has zero test coverage. This hook is responsible for:Blobvia a DOM<video>elementuseUploadFileobjectURLreferences after uploadWithout tests, regressions in the upload pipeline are silent and hard to catch in CI.
Current behaviour
useVideoUpload.ts(~174 lines) has no colocateduseVideoUpload.test.tscombineSegments: whensegments.length > 1, only the first segment is used and a toast is shown. This behaviour is intentional for MVP but is not verified by any assertion — so if the logic ever silently changes, nothing will catch it.What needs to be tested
1.
combineSegments— single segment (happy path)blob,blobUrl, and resolveddurationgetVideoDurationis called once2.
combineSegments— multiple segments (current MVP behaviour)"Multi-clip recording."toast with the correct descriptionblob/blobUrlgetVideoDurationis called on the first segment3.
combineSegments— empty array'No segments to combine'4.
getVideoDuration— success<video>element, setssrcto an objectURLvideo.duration * 1000(milliseconds)window.URL.revokeObjectURLafter resolution5.
getVideoDuration— error'Failed to load video metadata'whenvideo.onerrorfireswindow.URL.revokeObjectURLon the error path too6.
uploadVideo— success flowuploadProgressto0→0.1→0.25→1uploadFilewith aFilebuilt from the combined blob[['url', 'https://...']]){ url, tags, duration }7.
uploadVideo— upload returns no URL tag'Upload succeeded but no URL returned'8.
uploadVideo—uploadFilerejectsuploadProgressback to0'Upload snagged.'9.
isUploadingflagfalseinitiallytruewhileuploadVideoMutationis pendingfalseafter completionMocking strategy
Follow the existing pattern from
useProfileStats.test.ts. Key mocks needed:getVideoDurationusesdocument.createElement('video')— mock it by dispatchingloadedmetadata/errorevents on the created element, or by stubbingdocument.createElement.Acceptance criteria
src/hooks/useVideoUpload.test.tscreatedit(...)blocknpx vitest run src/hooks/useVideoUpload.test.tsnpx tsc --noEmit)Related files
src/hooks/useVideoUpload.tssrc/hooks/useUploadFile.tssrc/hooks/useToast.tssrc/hooks/useProfileStats.test.tssrc/test/setup.tsNotes
The multi-segment limitation (scenarios 2 and 3 above) is a known MVP shortcut, not a bug to fix in this issue. The goal of these tests is to document and lock in the current behaviour so that future work on FFmpeg.wasm concatenation has a clear regression baseline.