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]>
283 lines
8.7 KiB
TypeScript
283 lines
8.7 KiB
TypeScript
/**
|
|
* End-to-end video upload, discovery, and feed verification tests.
|
|
*
|
|
* Scenarios covered:
|
|
* - SCN-upload-url: Authenticated user publishes a video with HTTPS URL
|
|
* - SCN-upload-magnet: Authenticated user publishes a video with magnet link
|
|
* - SCN-upload-both: Authenticated user publishes with both URL and magnet
|
|
* - SCN-upload-validation: Upload form rejects invalid/empty submissions
|
|
* - SCN-feed-discovery: Seeded videos appear in feed with correct attributes
|
|
* - SCN-feed-multiple: Multiple videos render in correct order
|
|
* - SCN-upload-gated: Upload button is hidden when logged out
|
|
*/
|
|
|
|
import { test, expect } from "./helpers/bitvidTestFixture";
|
|
|
|
test.describe("Video upload and discovery", () => {
|
|
test.describe("Upload form validation", () => {
|
|
test("upload modal opens when logged-in user clicks upload button", async ({
|
|
page,
|
|
gotoApp,
|
|
loginAs,
|
|
}) => {
|
|
// Given: a logged-in user on the app
|
|
await gotoApp();
|
|
await loginAs(page);
|
|
await page.waitForFunction(
|
|
() => (window as any).__bitvidTest__?.getAppState?.().isLoggedIn === true,
|
|
);
|
|
|
|
// When: user opens upload modal
|
|
const uploadBtn = page.locator('[data-testid="upload-button"]');
|
|
await expect(uploadBtn).toBeVisible();
|
|
const openResult = await page.evaluate(async () => {
|
|
const harness = (window as any).__bitvidTest__;
|
|
if (!harness || typeof harness.openUploadModal !== "function") {
|
|
return { ok: false, reason: "harness-unavailable" };
|
|
}
|
|
return harness.openUploadModal();
|
|
});
|
|
if (!openResult?.ok) {
|
|
await uploadBtn.click();
|
|
}
|
|
|
|
// Then: upload modal becomes visible with expected form fields
|
|
const modal = page.locator('[data-testid="upload-modal"]');
|
|
await expect(modal).toBeVisible({ timeout: 10000 });
|
|
|
|
await expect(
|
|
page.locator('[data-testid="upload-title"]'),
|
|
).toBeAttached();
|
|
await expect(page.locator('[data-testid="upload-url"]')).toBeAttached();
|
|
await expect(
|
|
page.locator('[data-testid="upload-magnet"]'),
|
|
).toBeAttached();
|
|
await expect(
|
|
page.locator('[data-testid="upload-submit"]'),
|
|
).toBeAttached();
|
|
});
|
|
|
|
test("upload button is hidden when user is not logged in", async ({
|
|
page,
|
|
gotoApp,
|
|
}) => {
|
|
// Given: an unauthenticated user
|
|
await gotoApp();
|
|
|
|
// Then: upload button should be in the DOM but not visible
|
|
const uploadBtn = page.locator('[data-testid="upload-button"]');
|
|
await expect(uploadBtn).toBeAttached();
|
|
await expect(uploadBtn).not.toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe("Feed discovery from seeded events", () => {
|
|
test("seeded video with URL appears in feed with correct attributes", async ({
|
|
page,
|
|
gotoApp,
|
|
loginAs,
|
|
seedEvent,
|
|
}) => {
|
|
// Given: a video event seeded in the relay
|
|
await seedEvent({
|
|
title: "Alpha Video",
|
|
url: "https://example.com/alpha.mp4",
|
|
description: "First test video",
|
|
dTag: "alpha-vid-001",
|
|
});
|
|
|
|
// When: user loads the app and logs in
|
|
await gotoApp();
|
|
await loginAs(page);
|
|
|
|
// Then: feed items should contain the seeded video
|
|
const items = await page.evaluate(() => {
|
|
return (window as any).__bitvidTest__.waitForFeedItems(1, 60000);
|
|
});
|
|
|
|
expect(items.length).toBeGreaterThanOrEqual(1);
|
|
const alphaItem = items.find(
|
|
(item: any) => item.title === "Alpha Video",
|
|
);
|
|
expect(alphaItem).toBeDefined();
|
|
expect(alphaItem.hasUrl).toBe(true);
|
|
});
|
|
|
|
test("seeded video with magnet appears in feed", async ({
|
|
page,
|
|
gotoApp,
|
|
loginAs,
|
|
seedEvent,
|
|
}) => {
|
|
// Given: a video event with only a magnet link
|
|
await seedEvent({
|
|
title: "Magnet Only Video",
|
|
magnet:
|
|
"magnet:?xt=urn:btih:0000000000000000000000000000000000000000",
|
|
dTag: "magnet-vid-001",
|
|
});
|
|
|
|
// When: user loads and logs in
|
|
await gotoApp();
|
|
await loginAs(page);
|
|
|
|
// Then: the video appears with magnet attribute
|
|
const items = await page.evaluate(() => {
|
|
return (window as any).__bitvidTest__.waitForFeedItems(1, 60000);
|
|
});
|
|
|
|
expect(items.length).toBeGreaterThanOrEqual(1);
|
|
const magnetItem = items.find(
|
|
(item: any) => item.title === "Magnet Only Video",
|
|
);
|
|
expect(magnetItem).toBeDefined();
|
|
expect(magnetItem.hasMagnet).toBe(true);
|
|
});
|
|
|
|
test("multiple seeded videos all appear in feed", async ({
|
|
page,
|
|
gotoApp,
|
|
loginAs,
|
|
seedEvent,
|
|
}) => {
|
|
// Given: three videos seeded into the relay
|
|
await seedEvent({
|
|
title: "Video One",
|
|
url: "https://example.com/one.mp4",
|
|
dTag: "multi-001",
|
|
});
|
|
await seedEvent({
|
|
title: "Video Two",
|
|
url: "https://example.com/two.mp4",
|
|
dTag: "multi-002",
|
|
});
|
|
await seedEvent({
|
|
title: "Video Three",
|
|
url: "https://example.com/three.mp4",
|
|
magnet:
|
|
"magnet:?xt=urn:btih:1111111111111111111111111111111111111111",
|
|
dTag: "multi-003",
|
|
});
|
|
|
|
// When: user loads and logs in
|
|
await gotoApp();
|
|
await loginAs(page);
|
|
|
|
// Then: all three videos appear
|
|
const items = await page.evaluate(() => {
|
|
return (window as any).__bitvidTest__.waitForFeedItems(3, 60000);
|
|
});
|
|
|
|
expect(items.length).toBeGreaterThanOrEqual(3);
|
|
|
|
const titles = items.map((i: any) => i.title);
|
|
expect(titles).toContain("Video One");
|
|
expect(titles).toContain("Video Two");
|
|
expect(titles).toContain("Video Three");
|
|
|
|
// Video Three should have both URL and magnet
|
|
const videoThree = items.find(
|
|
(i: any) => i.title === "Video Three",
|
|
);
|
|
expect(videoThree.hasUrl).toBe(true);
|
|
expect(videoThree.hasMagnet).toBe(true);
|
|
});
|
|
|
|
test("video cards have correct data attributes", async ({
|
|
page,
|
|
gotoApp,
|
|
loginAs,
|
|
seedEvent,
|
|
testPubkey,
|
|
}) => {
|
|
// Given: a seeded video
|
|
await seedEvent({
|
|
title: "Attributed Video",
|
|
url: "https://example.com/attributed.mp4",
|
|
dTag: "attr-vid-001",
|
|
});
|
|
|
|
// When: page loads and feed populates
|
|
await gotoApp();
|
|
await loginAs(page);
|
|
|
|
await page.evaluate(() => {
|
|
return (window as any).__bitvidTest__.waitForFeedItems(1, 60000);
|
|
});
|
|
|
|
// Then: the video card DOM elements have correct data attributes
|
|
const card = page.locator("[data-video-card]").first();
|
|
await expect(card).toBeVisible();
|
|
|
|
const titleEl = card.locator("[data-video-title]");
|
|
const title = await titleEl.getAttribute("data-video-title");
|
|
expect(title).toBe("Attributed Video");
|
|
|
|
const pubkey = await card.getAttribute("data-video-pubkey");
|
|
expect(pubkey).toBe(testPubkey);
|
|
|
|
const dTag = await card.getAttribute("data-video-dtag");
|
|
expect(dTag).toBe("attr-vid-001");
|
|
});
|
|
});
|
|
|
|
test.describe("Relay event seeding integrity", () => {
|
|
test("clearing relay removes all events", async ({
|
|
seedEvent,
|
|
clearRelay,
|
|
relay,
|
|
}) => {
|
|
// Given: events seeded in the relay
|
|
await seedEvent({
|
|
title: "Ephemeral Video",
|
|
url: "https://example.com/ephemeral.mp4",
|
|
});
|
|
|
|
const httpUrl = (relay as any).httpUrl;
|
|
let resp = await fetch(`${httpUrl}/events`);
|
|
let events = await resp.json();
|
|
expect(events.length).toBeGreaterThan(0);
|
|
|
|
// When: relay is cleared
|
|
await clearRelay();
|
|
|
|
// Then: no events remain
|
|
resp = await fetch(`${httpUrl}/events`);
|
|
events = await resp.json();
|
|
expect(events.length).toBe(0);
|
|
});
|
|
|
|
test("seeded events have valid Nostr signatures", async ({
|
|
relay,
|
|
seedEvent,
|
|
}) => {
|
|
// Given: a seeded event
|
|
await seedEvent({
|
|
title: "Signed Video",
|
|
url: "https://example.com/signed.mp4",
|
|
dTag: "signed-001",
|
|
});
|
|
|
|
// When: we retrieve events from the relay
|
|
const httpUrl = (relay as any).httpUrl;
|
|
const resp = await fetch(`${httpUrl}/events`);
|
|
const events = await resp.json();
|
|
|
|
// Then: each event has required Nostr fields
|
|
expect(events.length).toBe(1);
|
|
const event = events[0];
|
|
expect(event.id).toBeTruthy();
|
|
expect(event.sig).toBeTruthy();
|
|
expect(event.pubkey).toBeTruthy();
|
|
expect(event.kind).toBe(30078);
|
|
expect(event.created_at).toBeGreaterThan(0);
|
|
|
|
// Content should be valid JSON with expected schema
|
|
const content = JSON.parse(event.content);
|
|
expect(content.version).toBe(3);
|
|
expect(content.title).toBe("Signed Video");
|
|
expect(content.url).toBe("https://example.com/signed.mp4");
|
|
});
|
|
});
|
|
});
|