mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
test(e2e): smoke the real upload -> publish -> feed -> delete pipeline
Existing upload/feed specs seed events straight into the relay, bypassing the app's own publish path. This spec drives the actual UI: open the upload modal, switch to External Link mode, fill title + URL, submit -> the app signs and publishes to the mock relay -> the video appears in the feed; then the owner deletes it via the gear menu and it leaves the feed. Closes the launch-readiness coverage gap on the publish lifecycle (the S3/R2 file upload + cleanup are covered by the unit suites). Verified passing on chromium (2/2). Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
7f5dcd7e50
commit
a7803c810b
1 file changed
+125
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Launch-readiness smoke for the REAL publish pipeline.
|
||||
*
|
||||
* Every other upload/feed spec seeds events straight into the relay, which
|
||||
* bypasses the app's own publish path. This spec drives the actual UI:
|
||||
* open upload modal -> fill title + URL -> submit -> the app signs & publishes
|
||||
* to the (mock) relay -> the video shows up in the feed -> the owner deletes it.
|
||||
*
|
||||
* Scenarios:
|
||||
* - SCN-publish-url-first: a URL-first upload submitted through the form is
|
||||
* signed, published to the relay, and appears in the feed with its URL.
|
||||
* - SCN-publish-then-delete: the owner can delete a self-published video and it
|
||||
* leaves the feed.
|
||||
*
|
||||
* This covers the upload->publish->feed->delete orchestration end to end. The
|
||||
* S3/R2 *file* upload (multipart, key derivation, cleanup) is exercised by the
|
||||
* unit suites; this smoke validates the note lifecycle that ties it together.
|
||||
*/
|
||||
|
||||
import { test, expect } from "./helpers/bitvidTestFixture";
|
||||
|
||||
async function openUploadAndPublish(
|
||||
page: any,
|
||||
{ title, url }: { title: string; url: string },
|
||||
) {
|
||||
const opened = await page.evaluate(() =>
|
||||
(window as any).__bitvidTest__.openUploadModal(),
|
||||
);
|
||||
expect(opened?.ok, `openUploadModal: ${JSON.stringify(opened)}`).toBe(true);
|
||||
|
||||
const modal = page.locator('[data-testid="upload-modal"]');
|
||||
await expect(modal).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// The modal defaults to "Upload File" (S3) mode; switch to "External Link" so
|
||||
// the URL field is shown (URL-first publish, no storage needed).
|
||||
await page.locator("#btn-mode-external").click();
|
||||
const urlInput = page.locator('[data-testid="upload-url"]');
|
||||
await expect(urlInput).toBeVisible({ timeout: 10000 });
|
||||
|
||||
await page.locator('[data-testid="upload-title"]').fill(title);
|
||||
await urlInput.fill(url);
|
||||
await page.locator('[data-testid="upload-submit"]').click();
|
||||
|
||||
// The publish path closes the modal on success.
|
||||
await expect(modal).not.toBeVisible({ timeout: 60000 });
|
||||
}
|
||||
|
||||
test.describe("Upload → publish → feed lifecycle (real pipeline)", () => {
|
||||
test("a URL-first upload submitted through the form publishes and appears in the feed", async ({
|
||||
page,
|
||||
gotoApp,
|
||||
loginAs,
|
||||
}) => {
|
||||
await gotoApp();
|
||||
await loginAs(page);
|
||||
|
||||
const title = `Smoke Publish ${Date.now()}`;
|
||||
const url = "https://example.com/smoke-publish.mp4";
|
||||
|
||||
await openUploadAndPublish(page, { title, url });
|
||||
|
||||
// The app signs the note, publishes to the mock relay, and the feed updates.
|
||||
const item = await page.evaluate(async (t: string) => {
|
||||
const harness = (window as any).__bitvidTest__;
|
||||
const deadline = Date.now() + 60000;
|
||||
while (Date.now() < deadline) {
|
||||
const items = harness.getFeedItems();
|
||||
const found = items.find((i: any) => i.title === t);
|
||||
if (found) return found;
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
}
|
||||
return null;
|
||||
}, title);
|
||||
|
||||
expect(item, "published video should appear in the feed").not.toBeNull();
|
||||
expect(item.hasUrl).toBe(true);
|
||||
});
|
||||
|
||||
test("the owner can delete a self-published video and it leaves the feed", async ({
|
||||
page,
|
||||
gotoApp,
|
||||
loginAs,
|
||||
}) => {
|
||||
await gotoApp();
|
||||
await loginAs(page);
|
||||
|
||||
const title = `Smoke Delete ${Date.now()}`;
|
||||
await openUploadAndPublish(page, {
|
||||
title,
|
||||
url: "https://example.com/smoke-delete.mp4",
|
||||
});
|
||||
|
||||
// Wait for the published card to render.
|
||||
const card = page
|
||||
.locator("[data-video-card]")
|
||||
.filter({ has: page.locator(`[data-video-title="${title}"]`) })
|
||||
.first();
|
||||
await expect(card).toBeVisible({ timeout: 60000 });
|
||||
|
||||
// Owner gear menu -> delete -> confirm.
|
||||
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 });
|
||||
await page.locator("#confirmDeleteVideo").click();
|
||||
await expect(deleteModal).toHaveClass(/hidden/, { timeout: 60000 });
|
||||
|
||||
// The video should no longer be in the feed.
|
||||
const stillThere = await page.evaluate(async (t: string) => {
|
||||
const harness = (window as any).__bitvidTest__;
|
||||
const deadline = Date.now() + 30000;
|
||||
while (Date.now() < deadline) {
|
||||
const items = harness.getFeedItems();
|
||||
if (!items.some((i: any) => i.title === t)) return false;
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
}
|
||||
return true;
|
||||
}, title);
|
||||
|
||||
expect(stillThere, "deleted video should leave the feed").toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user