mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Double default timeouts and extend the explicit waitForFeedItems calls across the e2e test suite to accommodate slower CI runners. This allows the docs-code-investigator scheduler run to pass successfully without being artificially blocked by environmental flakiness. Co-authored-by: PR0M3TH3AN <[email protected]>
295 lines
10 KiB
TypeScript
295 lines
10 KiB
TypeScript
/**
|
|
* Video CRUD flow scenarios: settings menu, edit modal, delete modal.
|
|
*
|
|
* Scenarios covered:
|
|
* - SCN-crud-settings-menu: Video card owned by logged-in user shows settings gear
|
|
* - SCN-crud-settings-actions: Settings menu contains edit and delete actions
|
|
* - SCN-crud-edit-open: Clicking edit action opens the edit modal
|
|
* - SCN-crud-edit-form: Edit modal contains the expected form fields
|
|
* - SCN-crud-edit-close: Edit modal can be closed without submitting
|
|
* - SCN-crud-delete-open: Clicking delete action opens the delete confirmation modal
|
|
* - SCN-crud-delete-elements: Delete modal has confirm and cancel buttons
|
|
* - SCN-crud-delete-cancel: Cancelling delete closes the modal without removing the video
|
|
*/
|
|
|
|
import { test, expect } from "./helpers/bitvidTestFixture";
|
|
|
|
/**
|
|
* Helper: seed a video, navigate, log in, and wait for the feed to populate.
|
|
*/
|
|
async function setupFeedWithVideo(
|
|
page: any,
|
|
{ gotoApp, loginAs, seedEvent }: any,
|
|
overrides: Record<string, string> = {},
|
|
) {
|
|
await seedEvent({
|
|
title: overrides.title || "CRUD Test Video",
|
|
url: overrides.url || "https://example.com/crud.mp4",
|
|
dTag: overrides.dTag || `crud-${Date.now()}`,
|
|
});
|
|
|
|
await gotoApp();
|
|
await loginAs(page);
|
|
|
|
await page.evaluate(() => {
|
|
return (window as any).__bitvidTest__.waitForFeedItems(1, 60000);
|
|
});
|
|
}
|
|
|
|
test.describe("Video CRUD — settings menu", () => {
|
|
test("video card shows settings gear button for the owner", async ({
|
|
page,
|
|
gotoApp,
|
|
loginAs,
|
|
seedEvent,
|
|
testPubkey,
|
|
}) => {
|
|
// Given: a video seeded by the test user (same key used for login)
|
|
await setupFeedWithVideo(page, { gotoApp, loginAs, seedEvent }, {
|
|
dTag: "crud-gear-001",
|
|
});
|
|
|
|
// Then: the video card should have a settings gear button
|
|
const card = page.locator("[data-video-card]").first();
|
|
await expect(card).toBeVisible();
|
|
|
|
// The card's pubkey should match the test user
|
|
const cardPubkey = await card.getAttribute("data-video-pubkey");
|
|
expect(cardPubkey).toBe(testPubkey);
|
|
|
|
// And: the settings gear button should exist on the card
|
|
const gearBtn = card.locator('[aria-label="Video settings"]');
|
|
await expect(gearBtn).toBeAttached();
|
|
});
|
|
|
|
test("clicking settings gear opens a menu with edit and delete actions", async ({
|
|
page,
|
|
gotoApp,
|
|
loginAs,
|
|
seedEvent,
|
|
}) => {
|
|
// Given: a video card with settings gear
|
|
await setupFeedWithVideo(page, { gotoApp, loginAs, seedEvent }, {
|
|
dTag: "crud-menu-001",
|
|
});
|
|
|
|
const card = page.locator("[data-video-card]").first();
|
|
const gearBtn = card.locator('[aria-label="Video settings"]');
|
|
|
|
// When: user clicks the gear button
|
|
await gearBtn.click();
|
|
|
|
// Then: a settings menu panel should appear
|
|
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
|
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
|
|
|
// And: it should contain edit and delete actions
|
|
const editAction = settingsPanel.locator('[data-action="edit"]');
|
|
const deleteAction = settingsPanel.locator('[data-action="delete"]');
|
|
await expect(editAction).toBeVisible();
|
|
await expect(deleteAction).toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe("Video CRUD — edit modal", () => {
|
|
test("clicking edit action opens the edit modal", async ({
|
|
page,
|
|
gotoApp,
|
|
loginAs,
|
|
seedEvent,
|
|
}) => {
|
|
// Given: the settings menu is open
|
|
await setupFeedWithVideo(page, { gotoApp, loginAs, seedEvent }, {
|
|
dTag: "crud-edit-open-001",
|
|
});
|
|
|
|
const card = page.locator("[data-video-card]").first();
|
|
await card.locator('[aria-label="Video settings"]').click();
|
|
|
|
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
|
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
|
|
|
// When: user clicks the edit action
|
|
await settingsPanel.locator('[data-action="edit"]').click();
|
|
|
|
// Then: the edit modal should open
|
|
const editModal = page.locator("#editVideoModal");
|
|
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
|
});
|
|
|
|
test("edit modal contains the title input field", async ({
|
|
page,
|
|
gotoApp,
|
|
loginAs,
|
|
seedEvent,
|
|
}) => {
|
|
// Given: the edit modal is open
|
|
await setupFeedWithVideo(page, { gotoApp, loginAs, seedEvent }, {
|
|
title: "Editable Video",
|
|
dTag: "crud-edit-form-001",
|
|
});
|
|
|
|
const card = page.locator("[data-video-card]").first();
|
|
await card.locator('[aria-label="Video settings"]').click();
|
|
|
|
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
|
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
|
await settingsPanel.locator('[data-action="edit"]').click();
|
|
|
|
const editModal = page.locator("#editVideoModal");
|
|
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
|
|
|
// Then: the title input should be present
|
|
const titleInput = page.locator("#editVideoTitle");
|
|
await expect(titleInput).toBeAttached();
|
|
});
|
|
|
|
test("edit modal can be closed via the close button", async ({
|
|
page,
|
|
gotoApp,
|
|
loginAs,
|
|
seedEvent,
|
|
}) => {
|
|
// Given: the edit modal is open
|
|
await setupFeedWithVideo(page, { gotoApp, loginAs, seedEvent }, {
|
|
dTag: "crud-edit-close-001",
|
|
});
|
|
|
|
const card = page.locator("[data-video-card]").first();
|
|
await card.locator('[aria-label="Video settings"]').click();
|
|
|
|
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
|
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
|
await settingsPanel.locator('[data-action="edit"]').click();
|
|
|
|
const editModal = page.locator("#editVideoModal");
|
|
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
|
|
|
// When: user clicks the close button
|
|
const closeBtn = page.locator("#closeEditVideoModal");
|
|
await closeBtn.click();
|
|
|
|
// Then: the edit modal should be hidden
|
|
await expect(editModal).toHaveClass(/hidden/, { timeout: 60000 });
|
|
});
|
|
});
|
|
|
|
test.describe("Video CRUD — delete modal", () => {
|
|
test("clicking delete action opens the delete confirmation modal", async ({
|
|
page,
|
|
gotoApp,
|
|
loginAs,
|
|
seedEvent,
|
|
}) => {
|
|
// Given: the settings menu is open
|
|
await setupFeedWithVideo(page, { gotoApp, loginAs, seedEvent }, {
|
|
dTag: "crud-delete-open-001",
|
|
});
|
|
|
|
const card = page.locator("[data-video-card]").first();
|
|
await card.locator('[aria-label="Video settings"]').click();
|
|
|
|
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
|
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
|
|
|
// When: user clicks the delete action
|
|
await settingsPanel.locator('[data-action="delete"]').click();
|
|
|
|
// Then: the delete confirmation modal should open
|
|
const deleteModal = page.locator("#deleteVideoModal");
|
|
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
|
});
|
|
|
|
test("delete modal has confirm and cancel buttons", async ({
|
|
page,
|
|
gotoApp,
|
|
loginAs,
|
|
seedEvent,
|
|
}) => {
|
|
// Given: the delete modal is open
|
|
await setupFeedWithVideo(page, { gotoApp, loginAs, seedEvent }, {
|
|
dTag: "crud-delete-btns-001",
|
|
});
|
|
|
|
const card = page.locator("[data-video-card]").first();
|
|
await card.locator('[aria-label="Video settings"]').click();
|
|
|
|
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
|
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
|
await settingsPanel.locator('[data-action="delete"]').click();
|
|
|
|
const deleteModal = page.locator("#deleteVideoModal");
|
|
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
|
|
|
// Then: confirm and cancel buttons should be present
|
|
const confirmBtn = page.locator("#confirmDeleteVideo");
|
|
const cancelBtn = page.locator("#cancelDeleteVideo");
|
|
await expect(confirmBtn).toBeVisible();
|
|
await expect(cancelBtn).toBeVisible();
|
|
|
|
// And: confirm button should indicate destructive action
|
|
await expect(confirmBtn).toHaveAttribute("data-variant", "critical");
|
|
});
|
|
|
|
test("cancelling delete closes the modal and preserves the video", async ({
|
|
page,
|
|
gotoApp,
|
|
loginAs,
|
|
seedEvent,
|
|
}) => {
|
|
// Given: the delete modal is open
|
|
await setupFeedWithVideo(page, { gotoApp, loginAs, seedEvent }, {
|
|
title: "Video To Keep",
|
|
dTag: "crud-delete-cancel-001",
|
|
});
|
|
|
|
const card = page.locator("[data-video-card]").first();
|
|
await card.locator('[aria-label="Video settings"]').click();
|
|
|
|
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
|
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
|
await settingsPanel.locator('[data-action="delete"]').click();
|
|
|
|
const deleteModal = page.locator("#deleteVideoModal");
|
|
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
|
|
|
// When: user clicks cancel
|
|
await page.locator("#cancelDeleteVideo").click();
|
|
|
|
// Then: the delete modal should close
|
|
await expect(deleteModal).toHaveClass(/hidden/, { timeout: 60000 });
|
|
|
|
// And: the video should still be in the feed
|
|
const feedItems = await page.evaluate(() => {
|
|
return (window as any).__bitvidTest__.getFeedItems();
|
|
});
|
|
expect(feedItems.length).toBeGreaterThanOrEqual(1);
|
|
});
|
|
|
|
test("delete modal shows warning about irreversible action", async ({
|
|
page,
|
|
gotoApp,
|
|
loginAs,
|
|
seedEvent,
|
|
}) => {
|
|
// Given: the delete modal is open
|
|
await setupFeedWithVideo(page, { gotoApp, loginAs, seedEvent }, {
|
|
dTag: "crud-delete-warn-001",
|
|
});
|
|
|
|
const card = page.locator("[data-video-card]").first();
|
|
await card.locator('[aria-label="Video settings"]').click();
|
|
|
|
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
|
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
|
await settingsPanel.locator('[data-action="delete"]').click();
|
|
|
|
const deleteModal = page.locator("#deleteVideoModal");
|
|
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
|
|
|
// Then: the modal should contain a warning about irreversibility
|
|
const modalText = await deleteModal.textContent();
|
|
expect(modalText).toContain("cannot be undone");
|
|
});
|
|
});
|