275 lines
8.0 KiB
TypeScript
275 lines
8.0 KiB
TypeScript
import { test, expect } from "#tests/e2e/fixtures/upload"
|
|
|
|
test.describe("File Upload (Integration)", () => {
|
|
test.describe("Initial State", () => {
|
|
test("should display the upload drop zone with correct instructions", async ({
|
|
uploadPage,
|
|
}) => {
|
|
const { dropZone } = uploadPage
|
|
|
|
await expect(
|
|
dropZone.getByText("Перетащите видеофайл сюда"),
|
|
).toBeVisible()
|
|
await expect(
|
|
dropZone.getByText("или нажмите для выбора файла"),
|
|
).toBeVisible()
|
|
await expect(
|
|
dropZone.locator("button", { hasText: "Выбрать файл" }),
|
|
).toBeVisible()
|
|
})
|
|
|
|
test("should have a file input that accepts only video types", async ({
|
|
uploadPage,
|
|
}) => {
|
|
const { fileInput } = uploadPage
|
|
|
|
await expect(fileInput).toHaveAttribute("accept", "video/*")
|
|
await expect(fileInput).not.toBeDisabled()
|
|
})
|
|
|
|
test("should not show progress bar or error in initial state", async ({
|
|
uploadPage,
|
|
}) => {
|
|
const { dropZone } = uploadPage
|
|
|
|
await expect(
|
|
dropZone.getByText("Загрузка файла..."),
|
|
).not.toBeVisible()
|
|
await expect(
|
|
dropZone.getByText("Не удалось загрузить файл"),
|
|
).not.toBeVisible()
|
|
})
|
|
})
|
|
|
|
test.describe("Successful Upload", () => {
|
|
test("should upload a valid video file and advance to the Verify step", async ({
|
|
uploadPage,
|
|
}) => {
|
|
const { page, testVideoPath } = uploadPage
|
|
|
|
await uploadPage.uploadFile(testVideoPath)
|
|
|
|
// Wait for wizard to advance to Verify step
|
|
await expect(
|
|
page.locator("[data-testid='VerifyStep']"),
|
|
).toBeVisible({ timeout: 30_000 })
|
|
})
|
|
|
|
test("should show upload progress during file upload", async ({
|
|
uploadPage,
|
|
}) => {
|
|
const { dropZone, testVideoPath } = uploadPage
|
|
|
|
await uploadPage.uploadFile(testVideoPath)
|
|
|
|
// Progress UI should appear (may be brief for small files)
|
|
// We check that either progress appeared or the step already advanced
|
|
const progressOrVerify = await Promise.race([
|
|
dropZone
|
|
.getByText("Загрузка файла...")
|
|
.waitFor({ timeout: 5_000 })
|
|
.then(() => "progress" as const)
|
|
.catch(() => null),
|
|
uploadPage.page
|
|
.locator("[data-testid='VerifyStep']")
|
|
.waitFor({ timeout: 30_000 })
|
|
.then(() => "verify" as const),
|
|
])
|
|
|
|
expect(["progress", "verify"]).toContain(progressOrVerify)
|
|
})
|
|
|
|
test("should show media info on Verify step after upload", async ({
|
|
uploadPage,
|
|
}) => {
|
|
const { page, testVideoPath } = uploadPage
|
|
|
|
await uploadPage.uploadFile(testVideoPath)
|
|
|
|
const verifyStep = page.locator("[data-testid='VerifyStep']")
|
|
await expect(verifyStep).toBeVisible({ timeout: 30_000 })
|
|
|
|
// Badge should show "Готово к обработке" for MP4
|
|
await expect(
|
|
verifyStep.getByText("Готово к обработке"),
|
|
).toBeVisible({ timeout: 10_000 })
|
|
|
|
// File info card should show the filename
|
|
await expect(verifyStep.getByText("Файл")).toBeVisible()
|
|
await expect(verifyStep.getByText("Размер и формат")).toBeVisible()
|
|
})
|
|
|
|
test("should persist wizard state after upload completes", async ({
|
|
uploadPage,
|
|
}) => {
|
|
const { page, testVideoPath } = uploadPage
|
|
|
|
await uploadPage.uploadFile(testVideoPath)
|
|
|
|
await expect(
|
|
page.locator("[data-testid='VerifyStep']"),
|
|
).toBeVisible({ timeout: 30_000 })
|
|
|
|
// Wait for debounced state save (1000ms debounce + network)
|
|
await page.waitForTimeout(2500)
|
|
|
|
await page.reload()
|
|
await page.locator("[data-testid='ProjectWizard']").waitFor()
|
|
|
|
// Should remain on Verify step after reload
|
|
await expect(
|
|
page.locator("[data-testid='VerifyStep']"),
|
|
).toBeVisible({ timeout: 10_000 })
|
|
})
|
|
})
|
|
|
|
test.describe("Error States", () => {
|
|
test("should show error message when upload fails due to network error", async ({
|
|
uploadPage,
|
|
}) => {
|
|
const { page, dropZone, testVideoPath } = uploadPage
|
|
|
|
// Intercept the upload XHR endpoint to abort
|
|
await page.route("**/api/files/upload/**", (route) => route.abort())
|
|
await page.route("**/api/files/upload/", (route) => route.abort())
|
|
|
|
await uploadPage.uploadFile(testVideoPath)
|
|
|
|
await expect(
|
|
dropZone.getByText("Не удалось загрузить файл"),
|
|
).toBeVisible({ timeout: 10_000 })
|
|
|
|
// Wizard stays on upload step
|
|
await expect(dropZone).toBeVisible()
|
|
await expect(
|
|
page.locator("[data-testid='VerifyStep']"),
|
|
).not.toBeVisible()
|
|
})
|
|
|
|
test("should show error message when server returns 500", async ({
|
|
uploadPage,
|
|
}) => {
|
|
const { page, dropZone, testVideoPath } = uploadPage
|
|
|
|
await page.route("**/api/files/upload/**", (route) =>
|
|
route.fulfill({
|
|
status: 500,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ detail: "Internal Server Error" }),
|
|
}),
|
|
)
|
|
await page.route("**/api/files/upload/", (route) =>
|
|
route.fulfill({
|
|
status: 500,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ detail: "Internal Server Error" }),
|
|
}),
|
|
)
|
|
|
|
await uploadPage.uploadFile(testVideoPath)
|
|
|
|
await expect(
|
|
dropZone.getByText("Не удалось загрузить файл"),
|
|
).toBeVisible({ timeout: 10_000 })
|
|
|
|
// Stays on upload step
|
|
await expect(dropZone).toBeVisible()
|
|
})
|
|
|
|
test("should allow retrying upload after a failure", async ({
|
|
uploadPage,
|
|
}) => {
|
|
const { page, dropZone, testVideoPath } = uploadPage
|
|
|
|
// First attempt: network error
|
|
await page.route("**/api/files/upload/**", (route) => route.abort())
|
|
await page.route("**/api/files/upload/", (route) => route.abort())
|
|
|
|
await uploadPage.uploadFile(testVideoPath)
|
|
|
|
await expect(
|
|
dropZone.getByText("Не удалось загрузить файл"),
|
|
).toBeVisible({ timeout: 10_000 })
|
|
|
|
// Remove intercepts and retry
|
|
await page.unrouteAll({ behavior: "ignoreErrors" })
|
|
|
|
await uploadPage.uploadFile(testVideoPath)
|
|
|
|
// Should succeed now and advance to Verify
|
|
await expect(
|
|
page.locator("[data-testid='VerifyStep']"),
|
|
).toBeVisible({ timeout: 30_000 })
|
|
})
|
|
|
|
test("should show error message when server returns 413", async ({
|
|
uploadPage,
|
|
}) => {
|
|
const { page, dropZone, testVideoPath } = uploadPage
|
|
|
|
await page.route("**/api/files/upload/**", (route) =>
|
|
route.fulfill({
|
|
status: 413,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ detail: "File too large" }),
|
|
}),
|
|
)
|
|
await page.route("**/api/files/upload/", (route) =>
|
|
route.fulfill({
|
|
status: 413,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ detail: "File too large" }),
|
|
}),
|
|
)
|
|
|
|
await uploadPage.uploadFile(testVideoPath)
|
|
|
|
await expect(
|
|
dropZone.getByText("Не удалось загрузить файл"),
|
|
).toBeVisible({ timeout: 10_000 })
|
|
})
|
|
})
|
|
|
|
test.describe("Edge Cases", () => {
|
|
test("should disable file input during active upload", async ({
|
|
uploadPage,
|
|
}) => {
|
|
const { page, dropZone, fileInput, testVideoPath } = uploadPage
|
|
|
|
// Delay the upload response to observe the uploading state
|
|
await page.route("**/api/files/upload/**", async (route) => {
|
|
await new Promise((r) => setTimeout(r, 3000))
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
file_id: "00000000-0000-0000-0000-000000000101",
|
|
file_path: "projects/test/video.mp4",
|
|
file_url: "http://localhost:9000/projects/test/video.mp4",
|
|
}),
|
|
})
|
|
})
|
|
await page.route("**/api/files/upload/", async (route) => {
|
|
await new Promise((r) => setTimeout(r, 3000))
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
file_id: "00000000-0000-0000-0000-000000000101",
|
|
file_path: "projects/test/video.mp4",
|
|
file_url: "http://localhost:9000/projects/test/video.mp4",
|
|
}),
|
|
})
|
|
})
|
|
|
|
await uploadPage.uploadFile(testVideoPath)
|
|
|
|
// During upload, the file input should be disabled
|
|
await expect(
|
|
dropZone.getByText("Загрузка файла..."),
|
|
).toBeVisible({ timeout: 5_000 })
|
|
await expect(fileInput).toBeDisabled()
|
|
})
|
|
})
|
|
})
|