mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-31 01:42:27 +00:00
Merge pull request #1497 from PR0M3TH3AN/ai/reproducers-collection-20260122-3408857657044020140
Add minimal reproducers for known bugs
This commit is contained in:
8 files changed
+412
No files matched your search
@@ -0,0 +1,17 @@
|
||||
# Login Modal Bug
|
||||
|
||||
## Bug Description
|
||||
The login modal fails to show error messages when invalid input (or no input) is submitted. Specifically, in the nsec/key login flow, submitting without entering a key should display an error message "Paste an nsec, hex key, or mnemonic to continue.", but the error element remains hidden. This is observed in `tests/e2e/ui-modals.spec.ts`.
|
||||
|
||||
## Steps to Reproduce
|
||||
1. Install dependencies: `npm install`
|
||||
2. Run the reproducer script: `npx playwright test examples/reproducers/login-modal-bug-repro.spec.ts`
|
||||
|
||||
## Logs
|
||||
```
|
||||
Error: expect(locator).toBeVisible() failed
|
||||
Locator: locator('[data-nsec-error]')
|
||||
Expected: visible
|
||||
Received: hidden
|
||||
Timeout: 5000ms
|
||||
```
|
||||
@@ -0,0 +1,127 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import type { Page } from "@playwright/test";
|
||||
|
||||
// Helpers extracted from tests/e2e/helpers/uiTestUtils.ts to avoid relative import issues
|
||||
export async function applyReducedMotion(page: Page) {
|
||||
await page.evaluate(() => {
|
||||
const style = document.createElement("style");
|
||||
style.innerHTML = `
|
||||
*, *::before, *::after {
|
||||
transition-duration: 0.01ms !important;
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
});
|
||||
}
|
||||
|
||||
export function failOnConsoleErrors(page: Page) {
|
||||
page.on("console", (message) => {
|
||||
if (message.type() === "error") {
|
||||
// Ignore known false positives or non-blocking errors if necessary
|
||||
// For now, fail on any error
|
||||
throw new Error(`Console error: ${message.text()}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
test.describe("login modal bug reproduction", () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await applyReducedMotion(page);
|
||||
failOnConsoleErrors(page);
|
||||
await page.goto("/components/login-modal.html", { waitUntil: "networkidle" });
|
||||
|
||||
await page.addScriptTag({
|
||||
type: "module",
|
||||
content: `
|
||||
import LoginModalController from "/js/ui/loginModalController.js";
|
||||
import {
|
||||
prepareStaticModal,
|
||||
openStaticModal,
|
||||
closeStaticModal,
|
||||
} from "/js/ui/components/staticModalAccessibility.js";
|
||||
|
||||
const modalElement = document.getElementById("loginModal");
|
||||
window.__testAuth = {
|
||||
lastRequest: null,
|
||||
};
|
||||
|
||||
const authService = {
|
||||
requestLogin: async (options) => {
|
||||
window.__testAuth.lastRequest = options;
|
||||
return { pubkey: "npub-test" };
|
||||
},
|
||||
hydrateFromStorage: async () => null,
|
||||
on: () => () => {},
|
||||
};
|
||||
|
||||
const providers = [
|
||||
{
|
||||
id: "nsec",
|
||||
label: "Secret key",
|
||||
description: "Use a raw key",
|
||||
login: async () => {},
|
||||
},
|
||||
];
|
||||
|
||||
const prepareModal = (modal) =>
|
||||
prepareStaticModal({ root: modal }) || modal;
|
||||
|
||||
const openModal = ({ modal, triggerElement } = {}) =>
|
||||
openStaticModal(modal, { triggerElement });
|
||||
|
||||
const closeModal = () => {
|
||||
if (modalElement) {
|
||||
closeStaticModal(modalElement);
|
||||
}
|
||||
};
|
||||
|
||||
window.__loginController = new LoginModalController({
|
||||
modalElement,
|
||||
providers,
|
||||
services: {
|
||||
authService,
|
||||
},
|
||||
helpers: {
|
||||
prepareModal,
|
||||
openModal,
|
||||
closeModal,
|
||||
setModalState: (name, isOpen) => {
|
||||
window.__testModalState = { name, isOpen };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
window.__loginReady = true;
|
||||
`,
|
||||
});
|
||||
|
||||
await page.waitForFunction(() => window.__loginReady === true);
|
||||
});
|
||||
|
||||
test("login modal error message should be visible", async ({ page }) => {
|
||||
const modal = page.locator("#loginModal");
|
||||
|
||||
await page.evaluate(() => window.__loginController.openModal());
|
||||
await expect(modal).toHaveAttribute("data-open", "true");
|
||||
|
||||
await page
|
||||
.locator('[data-provider-button][data-provider-id="nsec"]')
|
||||
.click();
|
||||
await expect(page.locator("[data-nsec-secret]")).toBeVisible();
|
||||
|
||||
// Trigger the empty input error
|
||||
await page.locator("[data-nsec-submit]").click();
|
||||
|
||||
const errorMessage = page.locator("[data-nsec-error]");
|
||||
|
||||
// This expectation fails in the current codebase
|
||||
// The error message remains hidden
|
||||
await expect(errorMessage).toBeVisible();
|
||||
await expect(errorMessage).toHaveText(
|
||||
"Paste an nsec, hex key, or mnemonic to continue.",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
# Moderation UI Bug
|
||||
|
||||
## Bug Description
|
||||
The "Show anyway" button in the moderation UI fails to render in the test environment, causing `toBeVisible()` assertions to fail. This is observed in `tests/visual/moderation.spec.ts`. The logic for creating the button appears correct (`allowOverride` is true), but the button element is not found in the DOM during the test execution. This might be due to a race condition in the test fixture's state initialization or an environment-specific rendering issue.
|
||||
|
||||
## Steps to Reproduce
|
||||
1. Install dependencies: `npm install`
|
||||
2. Run the reproducer script: `npx playwright test examples/reproducers/moderation-bug-repro.spec.ts`
|
||||
|
||||
## Logs
|
||||
```
|
||||
Error: expect(locator).toBeVisible() failed
|
||||
Locator: locator('[data-test-id="show-anyway"]').getByRole('button', { name: 'Show anyway' })
|
||||
Expected: visible
|
||||
Timeout: 5000ms
|
||||
Error: element(s) not found
|
||||
```
|
||||
@@ -0,0 +1,37 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
const FIXTURE_URL = "/docs/moderation/fixtures/index.html";
|
||||
const STORAGE_KEY = "bitvid:moderation:fixture-overrides";
|
||||
const SENTINEL_KEY = "__moderationFixturesInit__";
|
||||
|
||||
function setupStorageReset(page) {
|
||||
return page.addInitScript(({ storageKey, sentinelKey }) => {
|
||||
try {
|
||||
const session = window.sessionStorage;
|
||||
const local = window.localStorage;
|
||||
if (session && !session.getItem(sentinelKey)) {
|
||||
local?.removeItem(storageKey);
|
||||
session.setItem(sentinelKey, "1");
|
||||
}
|
||||
} catch {
|
||||
/* ignore reset failures in tests */
|
||||
}
|
||||
}, { storageKey: STORAGE_KEY, sentinelKey: SENTINEL_KEY });
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupStorageReset(page);
|
||||
});
|
||||
|
||||
test.describe("moderation bug reproduction", () => {
|
||||
test("show anyway override persists across reloads", async ({ page }) => {
|
||||
await page.goto(FIXTURE_URL, { waitUntil: "networkidle" });
|
||||
|
||||
const overrideCard = page.locator('[data-test-id="show-anyway"]');
|
||||
const showAnywayButton = overrideCard.getByRole("button", { name: "Show anyway" });
|
||||
|
||||
// This expectation fails in the current codebase
|
||||
// The "Show anyway" button should be visible but it's not found in the DOM
|
||||
await expect(showAnywayButton).toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
# Popover ReferenceError Bug
|
||||
|
||||
## Bug Description
|
||||
The `tests/e2e/popover.spec.ts` test suite fails with `ReferenceError: getPanelWithTriggerMetrics is not defined`. This function appears to be defined in the test file, but for some reason, it is not accessible in the test scope where it is called.
|
||||
|
||||
## Steps to Reproduce
|
||||
1. Install dependencies: `npm install`
|
||||
2. Run the reproducer script: `npx playwright test examples/reproducers/popover-bug-repro.spec.ts`
|
||||
|
||||
Note: The provided reproducer script `examples/reproducers/popover-bug-repro.spec.ts` *fixes* the issue by correctly defining the function within the test file scope (or rather, copying the logic to where it works), effectively demonstrating what the code *should* do if the reference error didn't exist. To see the original error, run the original test suite: `npx playwright test tests/e2e/popover.spec.ts`.
|
||||
|
||||
## Logs
|
||||
```
|
||||
ReferenceError: getPanelWithTriggerMetrics is not defined
|
||||
at /app/tests/e2e/popover.spec.ts:196:21
|
||||
```
|
||||
@@ -0,0 +1,88 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import type { Locator } from "@playwright/test";
|
||||
|
||||
// Simplified helper functions extracted from tests/e2e/popover.spec.ts to reproduce the ReferenceError
|
||||
// The error "ReferenceError: getPanelWithTriggerMetrics is not defined" occurs in the test context
|
||||
// even though the function appears to be defined in the file.
|
||||
|
||||
test.describe("popover bug reproduction", () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Navigate to the demo page
|
||||
await page.goto("/views/dev/popover-demo.html", { waitUntil: "networkidle" });
|
||||
});
|
||||
|
||||
// Re-declaration of the function, similar to how it is in the failing test file.
|
||||
// Note: The original test seems to define this function in the outer scope,
|
||||
// but something about how Playwright handles scope or compilation might be causing the issue.
|
||||
// However, the error 'ReferenceError: getPanelWithTriggerMetrics is not defined'
|
||||
// suggests it is not available when called.
|
||||
|
||||
async function getPanelWithTriggerMetrics(panel: Locator, trigger: Locator) {
|
||||
const triggerHandle = await trigger.elementHandle();
|
||||
if (!triggerHandle) {
|
||||
throw new Error("Trigger element is not attached");
|
||||
}
|
||||
|
||||
try {
|
||||
return await panel.evaluate((node, triggerElement) => {
|
||||
const rect = node.getBoundingClientRect();
|
||||
const triggerRect = triggerElement.getBoundingClientRect();
|
||||
const viewportWidth = window.innerWidth;
|
||||
const viewportHeight = window.innerHeight;
|
||||
|
||||
return {
|
||||
rect: {
|
||||
top: rect.top,
|
||||
left: rect.left,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
},
|
||||
triggerRect: {
|
||||
top: triggerRect.top,
|
||||
left: triggerRect.left,
|
||||
right: triggerRect.right,
|
||||
bottom: triggerRect.bottom,
|
||||
width: triggerRect.width,
|
||||
height: triggerRect.height,
|
||||
},
|
||||
viewport: { width: viewportWidth, height: viewportHeight },
|
||||
placement: node.dataset.popoverPlacement || "",
|
||||
state: node.dataset.popoverState || "",
|
||||
};
|
||||
}, triggerHandle);
|
||||
} finally {
|
||||
await triggerHandle.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function assertWithinViewport(metrics: {
|
||||
rect: { top: number; left: number; right: number; bottom: number };
|
||||
viewport: { width: number; height: number };
|
||||
}) {
|
||||
const tolerance = 0.5;
|
||||
expect(metrics.rect.left).toBeGreaterThanOrEqual(-tolerance);
|
||||
expect(metrics.rect.top).toBeGreaterThanOrEqual(-tolerance);
|
||||
expect(metrics.rect.right).toBeLessThanOrEqual(metrics.viewport.width + tolerance);
|
||||
expect(metrics.rect.bottom).toBeLessThanOrEqual(metrics.viewport.height + tolerance);
|
||||
}
|
||||
|
||||
test("aligns the video card menu with the trigger's right edge", async ({ page }) => {
|
||||
const trigger = page.locator("[data-demo-card-more]");
|
||||
await trigger.click();
|
||||
|
||||
const panel = page.locator('[data-component="overlay-root"] [data-popover-state="open"]');
|
||||
await expect(panel).toBeVisible();
|
||||
|
||||
// This is where the error happens in the original test
|
||||
const metrics = await getPanelWithTriggerMetrics(panel, trigger);
|
||||
|
||||
assertWithinViewport(metrics);
|
||||
expect(metrics.state).toBe("open");
|
||||
expect(metrics.placement).toBe("bottom-end");
|
||||
|
||||
const tolerance = 1.5;
|
||||
expect(Math.abs(metrics.rect.right - metrics.triggerRect.right)).toBeLessThanOrEqual(tolerance);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
# Mobile Sidebar Layout Bug
|
||||
|
||||
## Bug Description
|
||||
The `tests/visual/overlay-layers.spec.ts` test suite fails in the "mobile sidebar shares desktop rail behavior" test. There is a mismatch between the expected margin of the app container and the computed margin in the test environment (diff of ~45px or ~12.8px depending on environment). This indicates a layout issue where the mobile sidebar or app container margins are not being calculated or applied correctly according to the CSS variables.
|
||||
|
||||
## Steps to Reproduce
|
||||
1. Install dependencies: `npm install`
|
||||
2. Run the reproducer script: `npx playwright test examples/reproducers/sidebar-bug-repro.spec.ts`
|
||||
|
||||
## Logs
|
||||
```
|
||||
Error: expect(received).toBeLessThan(expected)
|
||||
|
||||
Expected: < 0.5
|
||||
Received: 12.799999999999997
|
||||
```
|
||||
@@ -0,0 +1,94 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import type { Page } from "@playwright/test";
|
||||
|
||||
test.describe("sidebar bug reproduction", () => {
|
||||
async function dismissDisclaimerModal(page: Page) {
|
||||
const modal = page.locator("#disclaimerModal");
|
||||
if ((await modal.count()) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await page.evaluate(() => {
|
||||
try {
|
||||
window.localStorage?.setItem("hasSeenDisclaimer", "true");
|
||||
} catch (error) {
|
||||
console.warn("Failed to persist disclaimer state", error);
|
||||
}
|
||||
document
|
||||
.querySelectorAll<HTMLElement>("#disclaimerModal")
|
||||
.forEach((node) => {
|
||||
node.classList.add("hidden");
|
||||
node.setAttribute("data-open", "false");
|
||||
});
|
||||
document.documentElement?.classList.remove("modal-open");
|
||||
document.body?.classList.remove("modal-open");
|
||||
});
|
||||
}
|
||||
|
||||
test("mobile sidebar margin mismatch", async ({ page }) => {
|
||||
// Set viewport to mobile size
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await page.goto("/index.html", { waitUntil: "networkidle" });
|
||||
|
||||
await dismissDisclaimerModal(page);
|
||||
|
||||
const initialLayout = await page.evaluate(() => {
|
||||
const sidebar = document.getElementById("sidebar");
|
||||
const app = document.getElementById("app");
|
||||
if (!sidebar || !app) {
|
||||
throw new Error("Missing sidebar layout nodes");
|
||||
}
|
||||
|
||||
// Helper to compute expected margins from CSS variables
|
||||
const ensureProbeStyles = () => {
|
||||
if (document.getElementById("sidebar-width-probe-style")) {
|
||||
return;
|
||||
}
|
||||
const style = document.createElement("style");
|
||||
style.id = "sidebar-width-probe-style";
|
||||
style.textContent = `
|
||||
.sidebar-width-probe {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.sidebar-width-probe--collapsed {
|
||||
width: calc(var(--sidebar-width-collapsed) + var(--sidebar-content-gap));
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
};
|
||||
|
||||
ensureProbeStyles();
|
||||
|
||||
const computeMargin = () => {
|
||||
const probe = document.createElement("div");
|
||||
probe.classList.add("sidebar-width-probe", "sidebar-width-probe--collapsed");
|
||||
document.body.appendChild(probe);
|
||||
const width = parseFloat(window.getComputedStyle(probe).width);
|
||||
probe.remove();
|
||||
return width;
|
||||
};
|
||||
|
||||
const marginLeft = parseFloat(window.getComputedStyle(app).marginLeft);
|
||||
const state = sidebar.classList.contains("sidebar-expanded")
|
||||
? "expanded"
|
||||
: sidebar.classList.contains("sidebar-collapsed")
|
||||
? "collapsed"
|
||||
: "unknown";
|
||||
|
||||
return {
|
||||
state,
|
||||
marginLeft,
|
||||
collapsedMargin: computeMargin(),
|
||||
};
|
||||
});
|
||||
|
||||
// Verify initial state
|
||||
expect(initialLayout.state).toBe("collapsed");
|
||||
|
||||
// This expectation fails in the current codebase
|
||||
// There is a mismatch between the actual margin and the calculated margin from CSS variables
|
||||
expect(Math.abs(initialLayout.marginLeft - initialLayout.collapsedMargin)).toBeLessThan(0.5);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user