mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
fix(ci): restore firefox and prevent dom crashes
- Fixed `package.json` to restrict `test:visual` to `visual` project, preventing execution of E2E tests in the `build` job (which lacks firefox). - Fixed `js/ui/components/UploadModal.js` and `js/ui/components/SearchFilterModal.js` with defensive null checks to prevent "Cannot set properties of null" crashes during E2E tests. - Verified `ci.yml` installs firefox for `e2e-headless`. - Verified `js/relayManager.js` handles missing `window.location`. Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
e6f5ef253f
commit
218159701a
3 files changed
+77
-69
No files matched your search
@@ -197,8 +197,8 @@ const formatDateInputValue = (timestampSeconds) => {
|
||||
|
||||
function handleDateChip(days) {
|
||||
if (days === "all") {
|
||||
dateStartInput.value = "";
|
||||
dateEndInput.value = "";
|
||||
if (dateStartInput) dateStartInput.value = "";
|
||||
if (dateEndInput) dateEndInput.value = "";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -209,8 +209,8 @@ function handleDateChip(days) {
|
||||
const start = new Date();
|
||||
start.setDate(now.getDate() - numDays);
|
||||
|
||||
dateStartInput.value = formatDateInputValue(Math.floor(start.getTime() / 1000));
|
||||
dateEndInput.value = formatDateInputValue(Math.floor(now.getTime() / 1000));
|
||||
if (dateStartInput) dateStartInput.value = formatDateInputValue(Math.floor(start.getTime() / 1000));
|
||||
if (dateEndInput) dateEndInput.value = formatDateInputValue(Math.floor(now.getTime() / 1000));
|
||||
}
|
||||
|
||||
// Helpers for Chips
|
||||
@@ -234,49 +234,53 @@ 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)
|
||||
const hasDateRange = Number.isFinite(safeFilters.dateRange?.after) || Number.isFinite(safeFilters.dateRange?.before);
|
||||
const dateBtns = dateChipsContainer.querySelectorAll("button");
|
||||
dateBtns.forEach(btn => {
|
||||
if (btn.dataset.days === "all") {
|
||||
const isActive = !hasDateRange;
|
||||
btn.setAttribute("aria-pressed", isActive);
|
||||
btn.dataset.state = isActive ? "on" : "off";
|
||||
} else {
|
||||
// We could try to calculate if range matches 24h/7d etc, but for now just clear them if custom dates are set
|
||||
btn.setAttribute("aria-pressed", "false");
|
||||
btn.dataset.state = "off";
|
||||
}
|
||||
});
|
||||
if (dateChipsContainer) {
|
||||
const dateBtns = dateChipsContainer.querySelectorAll("button");
|
||||
dateBtns.forEach(btn => {
|
||||
if (btn.dataset.days === "all") {
|
||||
const isActive = !hasDateRange;
|
||||
btn.setAttribute("aria-pressed", isActive);
|
||||
btn.dataset.state = isActive ? "on" : "off";
|
||||
} else {
|
||||
// We could try to calculate if range matches 24h/7d etc, but for now just clear them if custom dates are set
|
||||
btn.setAttribute("aria-pressed", "false");
|
||||
btn.dataset.state = "off";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 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");
|
||||
const chippedTags = new Set();
|
||||
|
||||
tagBtns.forEach(btn => {
|
||||
const tag = btn.dataset.tag;
|
||||
chippedTags.add(tag);
|
||||
const isActive = activeTags.has(tag);
|
||||
btn.setAttribute("aria-pressed", isActive);
|
||||
btn.dataset.state = isActive ? "on" : "off";
|
||||
});
|
||||
if (tagChipsContainer) {
|
||||
const tagBtns = tagChipsContainer.querySelectorAll("button");
|
||||
tagBtns.forEach(btn => {
|
||||
const tag = btn.dataset.tag;
|
||||
chippedTags.add(tag);
|
||||
const isActive = activeTags.has(tag);
|
||||
btn.setAttribute("aria-pressed", isActive);
|
||||
btn.dataset.state = isActive ? "on" : "off";
|
||||
});
|
||||
}
|
||||
|
||||
// Remaining tags go to input
|
||||
const otherTags = (safeFilters.tags || []).filter(t => !chippedTags.has(t));
|
||||
if (otherTags.length) {
|
||||
if (otherTags.length && tagsInput) {
|
||||
tagsInput.value = otherTags.join(", ");
|
||||
}
|
||||
|
||||
@@ -284,34 +288,38 @@ function syncStateToControls(filters) {
|
||||
const minSec = safeFilters.duration?.minSeconds;
|
||||
const maxSec = safeFilters.duration?.maxSeconds;
|
||||
|
||||
durationShortCheckbox.checked = false;
|
||||
durationMediumCheckbox.checked = false;
|
||||
durationLongCheckbox.checked = false;
|
||||
if (durationShortCheckbox) durationShortCheckbox.checked = false;
|
||||
if (durationMediumCheckbox) durationMediumCheckbox.checked = false;
|
||||
if (durationLongCheckbox) durationLongCheckbox.checked = false;
|
||||
|
||||
if (minSec === null && maxSec === 300) durationShortCheckbox.checked = true;
|
||||
if (minSec === 300 && maxSec === 1200) durationMediumCheckbox.checked = true;
|
||||
if (minSec === 1200 && maxSec === null) durationLongCheckbox.checked = true;
|
||||
if (minSec === null && maxSec === 300 && durationShortCheckbox) durationShortCheckbox.checked = true;
|
||||
if (minSec === 300 && maxSec === 1200 && durationMediumCheckbox) durationMediumCheckbox.checked = true;
|
||||
if (minSec === 1200 && maxSec === null && durationLongCheckbox) durationLongCheckbox.checked = true;
|
||||
|
||||
// Has
|
||||
const hasBtns = hasChipsContainer.querySelectorAll("button");
|
||||
hasBtns.forEach(btn => {
|
||||
const type = btn.dataset.has;
|
||||
let isActive = false;
|
||||
if (type === "magnet") isActive = safeFilters.hasMagnet === true;
|
||||
if (type === "url") isActive = safeFilters.hasUrl === true;
|
||||
if (hasChipsContainer) {
|
||||
const hasBtns = hasChipsContainer.querySelectorAll("button");
|
||||
hasBtns.forEach(btn => {
|
||||
const type = btn.dataset.has;
|
||||
let isActive = false;
|
||||
if (type === "magnet") isActive = safeFilters.hasMagnet === true;
|
||||
if (type === "url") isActive = safeFilters.hasUrl === true;
|
||||
|
||||
btn.setAttribute("aria-pressed", isActive);
|
||||
btn.dataset.state = isActive ? "on" : "off";
|
||||
});
|
||||
btn.setAttribute("aria-pressed", isActive);
|
||||
btn.dataset.state = isActive ? "on" : "off";
|
||||
});
|
||||
}
|
||||
|
||||
// NSFW
|
||||
if (ALLOW_NSFW_CONTENT) {
|
||||
const nsfwIsOn = safeFilters.nsfw === "true" || safeFilters.nsfw === "only";
|
||||
nsfwToggle.setAttribute("aria-checked", nsfwIsOn);
|
||||
nsfwToggle.classList.toggle("is-on", nsfwIsOn);
|
||||
} else {
|
||||
nsfwToggle.setAttribute("aria-checked", "false");
|
||||
nsfwToggle.classList.remove("is-on");
|
||||
if (nsfwToggle) {
|
||||
if (ALLOW_NSFW_CONTENT) {
|
||||
const nsfwIsOn = safeFilters.nsfw === "true" || safeFilters.nsfw === "only";
|
||||
nsfwToggle.setAttribute("aria-checked", nsfwIsOn);
|
||||
nsfwToggle.classList.toggle("is-on", nsfwIsOn);
|
||||
} else {
|
||||
nsfwToggle.setAttribute("aria-checked", "false");
|
||||
nsfwToggle.classList.remove("is-on");
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-expand Advanced if needed
|
||||
|
||||
@@ -517,9 +517,9 @@ export class UploadModal {
|
||||
// Show UI
|
||||
this.sourceSections.progress.classList.remove("hidden");
|
||||
this.sourceSections.results.classList.remove("hidden");
|
||||
this.results.videoUrl.value = "Uploading...";
|
||||
this.results.magnet.value = "Pending...";
|
||||
this.results.torrentUrl.value = "Pending...";
|
||||
if (this.results.videoUrl) this.results.videoUrl.value = "Uploading...";
|
||||
if (this.results.magnet) this.results.magnet.value = "Pending...";
|
||||
if (this.results.torrentUrl) this.results.torrentUrl.value = "Pending...";
|
||||
|
||||
this.updateVideoProgress(0, "Preparing upload...");
|
||||
|
||||
@@ -615,7 +615,7 @@ export class UploadModal {
|
||||
provider: providerLabel,
|
||||
prefix: storagePrefix,
|
||||
});
|
||||
this.results.videoUrl.value = videoPublicUrl;
|
||||
if (this.results.videoUrl) this.results.videoUrl.value = videoPublicUrl;
|
||||
this.updateVideoProgress(1, "Video uploaded.");
|
||||
|
||||
// 5. Handle Torrent Result & Upload .torrent file
|
||||
@@ -670,15 +670,15 @@ export class UploadModal {
|
||||
|
||||
this.torrentState.magnet = magnet;
|
||||
|
||||
this.results.magnet.value = magnet;
|
||||
this.results.torrentUrl.value = torrentPublicUrl;
|
||||
if (this.results.magnet) this.results.magnet.value = magnet;
|
||||
if (this.results.torrentUrl) this.results.torrentUrl.value = torrentPublicUrl;
|
||||
|
||||
this.updateVideoProgress(1, "Ready to publish!");
|
||||
} else {
|
||||
this.torrentState.status = 'skipped'; // Failed hash or invalid
|
||||
this.updateVideoProgress(1, "Upload complete (No torrent fallback).");
|
||||
this.results.magnet.value = "Not available (Info Hash failed)";
|
||||
this.results.torrentUrl.value = "Not available";
|
||||
if (this.results.magnet) this.results.magnet.value = "Not available (Info Hash failed)";
|
||||
if (this.results.torrentUrl) this.results.torrentUrl.value = "Not available";
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
@@ -699,7 +699,7 @@ export class UploadModal {
|
||||
|
||||
alert(`Upload failed: ${err.message}`);
|
||||
|
||||
this.inputs.file.value = ""; // Reset
|
||||
if (this.inputs.file) this.inputs.file.value = ""; // Reset
|
||||
}
|
||||
}
|
||||
|
||||
@@ -782,7 +782,7 @@ export class UploadModal {
|
||||
|
||||
this.thumbnailUploadState.status = 'complete';
|
||||
this.thumbnailUploadState.url = publicUrl;
|
||||
this.inputs.thumbnail.value = publicUrl;
|
||||
if (this.inputs.thumbnail) this.inputs.thumbnail.value = publicUrl;
|
||||
|
||||
this.updateThumbnailProgress(1, "Thumbnail uploaded.");
|
||||
|
||||
@@ -813,18 +813,18 @@ export class UploadModal {
|
||||
}
|
||||
|
||||
const pct = Math.round(fraction * 100);
|
||||
this.inputs.progress.value = pct;
|
||||
this.statusText.uploadPercent.textContent = `${pct}%`;
|
||||
if (this.inputs.progress) this.inputs.progress.value = pct;
|
||||
if (this.statusText.uploadPercent) this.statusText.uploadPercent.textContent = `${pct}%`;
|
||||
}
|
||||
|
||||
updateThumbnailProgress(fraction, text) {
|
||||
if (text) this.statusText.thumbnailMain.textContent = text;
|
||||
if (text && this.statusText.thumbnailMain) this.statusText.thumbnailMain.textContent = text;
|
||||
|
||||
if (fraction === null) return;
|
||||
|
||||
const pct = Math.round(fraction * 100);
|
||||
this.inputs.thumbnailProgress.value = pct;
|
||||
this.statusText.thumbnailPercent.textContent = `${pct}%`;
|
||||
if (this.inputs.thumbnailProgress) this.inputs.thumbnailProgress.value = pct;
|
||||
if (this.statusText.thumbnailPercent) this.statusText.thumbnailPercent.textContent = `${pct}%`;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -27,8 +27,8 @@
|
||||
"lint:tailwind-brackets": "node scripts/check-design-tokens.mjs --check=brackets",
|
||||
"lint:tailwind-colors": "node scripts/check-tailwind-colors.mjs",
|
||||
"check:css": "npm run build:css",
|
||||
"test:visual": "npm run build && npx playwright test",
|
||||
"test:visual:update": "npm run build && cross-env UPDATE_VISUAL_BASELINES=1 npx playwright test",
|
||||
"test:visual": "npm run build && npx playwright test --project=visual",
|
||||
"test:visual:update": "npm run build && cross-env UPDATE_VISUAL_BASELINES=1 npx playwright test --project=visual",
|
||||
"test:e2e": "npm run build && npx playwright test tests/e2e --project=e2e --project=e2e-firefox",
|
||||
"test:e2e:chromium": "npm run build && npx playwright test tests/e2e --project=e2e",
|
||||
"test:e2e:firefox": "npm run build && npx playwright test tests/e2e --project=e2e-firefox",
|
||||
|
||||
Reference in new issue
Block a user