Skip to content

test(upload): add unit tests for useVideoUpload hook #295

@DSanich

Description

@DSanich

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 00.10.251
  • 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. uploadVideouploadFile 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

  • File src/hooks/useVideoUpload.test.ts created
  • All 9 scenarios above have at least one it(...) block
  • No real network calls or DOM video decoding in tests
  • Tests pass with npx vitest run src/hooks/useVideoUpload.test.ts
  • No new TypeScript errors (npx tsc --noEmit)

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions