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:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-02-22 16:09:22 +00:00
1 parent d0f10d3ab7
commit 98ecc678a4
6 files changed
+24 -95

No files matched your search

+1 -1
View File
@@ -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
+5 -5
View File
@@ -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");
+6 -6
View File
@@ -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";
+7 -47
View File
@@ -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"]');
+3 -24
View File
@@ -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(
+2 -12
View File
@@ -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