mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-02 10:52:27 +00:00
fix(ci): install firefox for e2e-headless job and harden ui tests\n\n- Updated .github/workflows/ci.yml to install firefox in the e2e-headless job, resolving \"Executable doesn't exist\" errors.\n- Added defensive null checks in UploadModal.js to prevent runtime crashes during reset.\n- Hardened login-flows.spec.ts to prevent flaky logout assertions.
Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
d0f10d3ab7
commit
98ecc678a4
6 files changed
+24
-95
No files matched your search
@@ -478,7 +478,7 @@ jobs:
|
||||
run: npm ci
|
||||
|
||||
- name: Install Playwright browsers
|
||||
run: npx playwright install --with-deps chromium
|
||||
run: npx playwright install --with-deps chromium firefox
|
||||
|
||||
- name: Run headless E2E tests
|
||||
run: npm run test:e2e
|
||||
@@ -234,8 +234,8 @@ function syncStateToControls(filters) {
|
||||
const safeFilters = filters || DEFAULT_FILTERS;
|
||||
|
||||
// Date
|
||||
dateStartInput.value = formatDateInputValue(safeFilters.dateRange?.after);
|
||||
dateEndInput.value = formatDateInputValue(safeFilters.dateRange?.before);
|
||||
if (dateStartInput) dateStartInput.value = formatDateInputValue(safeFilters.dateRange?.after);
|
||||
if (dateEndInput) dateEndInput.value = formatDateInputValue(safeFilters.dateRange?.before);
|
||||
|
||||
// Logic to highlight correct date chip if it matches
|
||||
// (Simplified: just highlight 'All Time' if no dates, otherwise clear chips)
|
||||
@@ -254,13 +254,13 @@ function syncStateToControls(filters) {
|
||||
});
|
||||
|
||||
// Sort
|
||||
sortSelect.value = safeFilters.sort || "relevance";
|
||||
if (sortSelect) sortSelect.value = safeFilters.sort || "relevance";
|
||||
|
||||
// Author
|
||||
authorInput.value = safeFilters.authorPubkeys?.join(", ") || "";
|
||||
if (authorInput) authorInput.value = safeFilters.authorPubkeys?.join(", ") || "";
|
||||
|
||||
// Tags
|
||||
tagsInput.value = "";
|
||||
if (tagsInput) tagsInput.value = "";
|
||||
// We separate tags that are in chips vs typed
|
||||
const activeTags = new Set(safeFilters.tags || []);
|
||||
const tagBtns = tagChipsContainer.querySelectorAll("button");
|
||||
|
||||
@@ -1223,19 +1223,19 @@ export class UploadModal {
|
||||
}
|
||||
|
||||
// Clear Result Inputs
|
||||
if (this.results.videoUrl) this.results.videoUrl.value = "";
|
||||
if (this.results.magnet) this.results.magnet.value = "";
|
||||
if (this.results.torrentUrl) this.results.torrentUrl.value = "";
|
||||
if (this.results?.videoUrl) this.results.videoUrl.value = "";
|
||||
if (this.results?.magnet) this.results.magnet.value = "";
|
||||
if (this.results?.torrentUrl) this.results.torrentUrl.value = "";
|
||||
|
||||
// Reset Inputs
|
||||
if (this.inputs.file) {
|
||||
if (this.inputs?.file) {
|
||||
this.inputs.file.value = "";
|
||||
}
|
||||
if (this.inputs.thumbnailFile) {
|
||||
if (this.inputs?.thumbnailFile) {
|
||||
this.inputs.thumbnailFile.value = "";
|
||||
}
|
||||
// Re-enable thumbnail input if it was disabled by an upload
|
||||
if (this.inputs.thumbnail) {
|
||||
if (this.inputs?.thumbnail) {
|
||||
this.inputs.thumbnail.disabled = false;
|
||||
if (this.inputs.thumbnail.placeholder && this.inputs.thumbnail.placeholder.startsWith("Selected:")) {
|
||||
this.inputs.thumbnail.placeholder = "https://example.com/thumbnail.jpg";
|
||||
|
||||
@@ -87,18 +87,8 @@ test.describe("Accessibility and keyboard navigation", () => {
|
||||
await page.keyboard.press("Enter");
|
||||
|
||||
// Then: login modal opens
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const modal = document.querySelector(
|
||||
'[data-testid="login-modal"]',
|
||||
);
|
||||
if (!(modal instanceof HTMLElement)) return false;
|
||||
return modal.getAttribute("data-open") === "true";
|
||||
},
|
||||
{ timeout: 15000 },
|
||||
);
|
||||
|
||||
const modal = page.locator('[data-testid="login-modal"]');
|
||||
await expect(modal).toBeVisible({ timeout: 15000 });
|
||||
await expect(modal).toHaveAttribute("data-open", "true");
|
||||
});
|
||||
});
|
||||
@@ -127,19 +117,8 @@ test.describe("Accessibility and keyboard navigation", () => {
|
||||
await page.keyboard.press("Escape");
|
||||
|
||||
// Then: modal closes (data-open becomes false or modal is hidden)
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const modal = document.querySelector(
|
||||
'[data-testid="login-modal"]',
|
||||
);
|
||||
if (!(modal instanceof HTMLElement)) return true;
|
||||
return (
|
||||
modal.getAttribute("data-open") === "false" ||
|
||||
modal.classList.contains("hidden")
|
||||
);
|
||||
},
|
||||
{ timeout: 10000 },
|
||||
);
|
||||
const modal = page.locator('[data-testid="login-modal"]');
|
||||
await expect(modal).toBeHidden({ timeout: 10000 });
|
||||
});
|
||||
|
||||
test("login modal contains focusable elements", async ({
|
||||
@@ -150,16 +129,8 @@ test.describe("Accessibility and keyboard navigation", () => {
|
||||
await gotoApp();
|
||||
await page.locator('[data-testid="login-button"]').click();
|
||||
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const modal = document.querySelector(
|
||||
'[data-testid="login-modal"]',
|
||||
);
|
||||
if (!(modal instanceof HTMLElement)) return false;
|
||||
return modal.getAttribute("data-open") === "true";
|
||||
},
|
||||
{ timeout: 15000 },
|
||||
);
|
||||
const modal = page.locator('[data-testid="login-modal"]');
|
||||
await expect(modal).toBeVisible({ timeout: 15000 });
|
||||
|
||||
// Then: modal has focusable interactive elements
|
||||
const focusableCount = await page.evaluate(() => {
|
||||
@@ -267,19 +238,8 @@ test.describe("Accessibility and keyboard navigation", () => {
|
||||
await expect(uploadBtn).toBeVisible();
|
||||
await uploadBtn.click();
|
||||
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const modal = document.querySelector(
|
||||
'[data-testid="upload-modal"]',
|
||||
);
|
||||
if (!(modal instanceof HTMLElement)) return false;
|
||||
return (
|
||||
modal.getAttribute("data-open") === "true" &&
|
||||
!modal.classList.contains("hidden")
|
||||
);
|
||||
},
|
||||
{ timeout: 10000 },
|
||||
);
|
||||
const modal = page.locator('[data-testid="upload-modal"]');
|
||||
await expect(modal).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Then: form fields are focusable
|
||||
const titleInput = page.locator('[data-testid="upload-title"]');
|
||||
|
||||
@@ -179,21 +179,8 @@ test.describe("Login and authentication flows", () => {
|
||||
await page.locator('[data-testid="login-button"]').click();
|
||||
|
||||
// Then: login modal becomes visible
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const modal = document.querySelector(
|
||||
'[data-testid="login-modal"]',
|
||||
);
|
||||
if (!(modal instanceof HTMLElement)) return false;
|
||||
return (
|
||||
modal.getAttribute("data-open") === "true" &&
|
||||
!modal.classList.contains("hidden")
|
||||
);
|
||||
},
|
||||
{ timeout: 15000 },
|
||||
);
|
||||
|
||||
const modal = page.locator('[data-testid="login-modal"]');
|
||||
await expect(modal).toBeVisible({ timeout: 15000 });
|
||||
await expect(modal).toHaveAttribute("data-open", "true");
|
||||
});
|
||||
|
||||
@@ -205,16 +192,8 @@ test.describe("Login and authentication flows", () => {
|
||||
await gotoApp();
|
||||
await page.locator('[data-testid="login-button"]').click();
|
||||
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const modal = document.querySelector(
|
||||
'[data-testid="login-modal"]',
|
||||
);
|
||||
if (!(modal instanceof HTMLElement)) return false;
|
||||
return modal.getAttribute("data-open") === "true";
|
||||
},
|
||||
{ timeout: 15000 },
|
||||
);
|
||||
const modal = page.locator('[data-testid="login-modal"]');
|
||||
await expect(modal).toBeVisible({ timeout: 15000 });
|
||||
|
||||
// Then: at least one provider button is visible
|
||||
const providers = page.locator(
|
||||
|
||||
@@ -209,18 +209,8 @@ test.describe("Video playback scenarios", () => {
|
||||
await closeBtn.first().click({ force: true });
|
||||
|
||||
// Then: modal is hidden again
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const modal = document.querySelector("#playerModal");
|
||||
if (!modal) return true;
|
||||
return (
|
||||
modal.classList.contains("hidden") ||
|
||||
modal.style.display === "none" ||
|
||||
modal.getAttribute("data-open") === "false"
|
||||
);
|
||||
},
|
||||
{ timeout: 10000 },
|
||||
);
|
||||
const modal = page.locator("#playerModal");
|
||||
await expect(modal).toBeHidden({ timeout: 10000 });
|
||||
}
|
||||
|
||||
// App state should still be valid
|
||||
|
||||
Reference in new issue
Block a user