// The direct upload path, end to end: the browser presigns against the app,
// PUTs the bytes straight at MinIO, and the app finalises the version.
//
// This needs the `e2e` compose profile up (minio-test plus its bucket) and the
// R2_* variables from playwright.config.ts. Without them
// isDirectFileUploadEnabled() is false and the `Direct Upload` tab is not
// rendered at all, so the first assertion below fails loudly rather than
// silently testing nothing.
//
// Note on TESTING.md section 6: it says to upload "through the drag-drop
// uploader". The uploader on the dashboard and project pages
// (components/video-drag-drop-uploader.tsx) has no at all,
// only window-level drop listeners, so setInputFiles cannot reach it. The real
// upload form is /projects/{id}/videos/new, which does have a file input, and
// that is what this spec drives.
import path from 'node:path';
import { test, expect } from './fixtures';
import { REPO_ROOT } from '../helpers/env';
const SAMPLE_VIDEO = path.join(REPO_ROOT, 'tests', 'fixtures', 'sample.mp4');
// The upload is three network round trips plus a MinIO PUT, and `next build`
// output is cold on the first hit of each route.
test.setTimeout(120_000);
test('a video file is uploaded to object storage and a second version is added', async ({
page,
seed,
seededUser,
}) => {
const { project } = await seed.project(seededUser);
const title = `Uploaded Video ${Date.now()}`;
await page.goto(`/projects/${project.id}/videos/new`);
// `CardTitle` renders a
, so the page is identified by its tab strip.
await expect(page.getByRole('tab', { name: 'Paste URL' })).toBeVisible();
// Present only when the app resolved a direct upload provider.
const directUploadTab = page.getByRole('tab', { name: 'Direct Upload' });
await expect(directUploadTab).toBeVisible();
await directUploadTab.click();
await page.getByLabel('Video Files').setInputFiles(SAMPLE_VIDEO);
await expect(page.getByText('sample.mp4')).toBeVisible();
await page.getByLabel('Title').fill(title);
await page.getByRole('button', { name: 'Add Video', exact: true }).click();
// Back on the project page with the video listed. A failed presign or a
// rejected PUT would leave us on the form with an error instead.
await expect(page).toHaveURL(new RegExp(`/projects/${project.id}$`), { timeout: 90_000 });
const videoHeading = page.getByRole('heading', { name: title, level: 3 });
await expect(videoHeading).toBeVisible();
// --- second version ------------------------------------------------------
await videoHeading.click();
await expect(page).toHaveURL(new RegExp(`/projects/${project.id}/videos/[^/]+$`));
// One version so far, so there is nothing to compare against yet.
await expect(page.getByText('v1', { exact: true })).toBeVisible();
await expect(page.getByRole('button', { name: 'Compare' })).toHaveCount(0);
await page.getByRole('button', { name: 'New Version' }).click();
const dialog = page.getByRole('dialog');
await expect(dialog.getByRole('heading', { name: 'Add New Version' })).toBeVisible();
await dialog.getByRole('tab', { name: 'Upload File' }).click();
await dialog.getByLabel('Video File').setInputFiles(SAMPLE_VIDEO);
// Located by placeholder, not by label: the "Version Label (optional)"