CI Testing with Playwright
BugDrop can be checked in CI without submitting a real report. The stable browser contract is the bugdrop:ready window event, a #bugdrop-host element, and controls inside its open Shadow DOM. The current trigger, dialog, and close selectors are .bd-trigger, .bd-modal, and .bd-close.
Source reviewed: official BugDrop README and repository on 2026-08-14. The example below was exercised against the hosted widget and the checksum-pinned copy used by this site.
Copy this smoke test
Register the event observer before navigation so a fast synchronous load cannot race the test:
import { test, expect } from "@playwright/test";
test("BugDrop loads and opens", async ({ page }) => {
await page.addInitScript(() => {
window.addEventListener(
"bugdrop:ready",
() => document.documentElement.setAttribute("data-bugdrop-ready", "true"),
{ once: true },
);
});
await page.goto(process.env.PREVIEW_URL ?? "http://127.0.0.1:3000");
await expect(page.locator("html")).toHaveAttribute("data-bugdrop-ready", "true");
await expect(page.locator("#bugdrop-host")).toBeAttached();
const trigger = page.locator("#bugdrop-host .bd-trigger");
await expect(trigger).toBeVisible();
await trigger.click();
await expect(page.locator("#bugdrop-host .bd-modal")).toBeVisible();
await expect(page.locator("#bugdrop-host .bd-close")).toBeVisible();
});
Playwright locators pierce an open Shadow DOM, so the descendant selectors above reach the widget without a custom helper. If your environment blocks third-party scripts, assert the request policy explicitly instead of adding a timeout.
Verify the loading element
The widget must execute from a normal script tag with its repository configuration attached. This assertion catches an accidental async, defer, duplicate, or wrong repository value:
test("uses one synchronous configured script", async ({ page }) => {
await page.goto(process.env.PREVIEW_URL!);
const scripts = page.locator(
'script[src="https://bugdrop.neonwatty.workers.dev/widget.js"]',
);
await expect(scripts).toHaveCount(1);
await expect(scripts).toHaveAttribute("data-repo", "owner/repo");
await expect(scripts).not.toHaveAttribute("async", /.*/);
await expect(scripts).not.toHaveAttribute("defer", /.*/);
});
Check preview-only loading
For the Vercel preview workflow, run the smoke test against a Preview Deployment. Run this negative check against the production URL:
test("production omits preview feedback", async ({ page }) => {
await page.goto(process.env.PRODUCTION_URL!);
await expect(
page.locator('script[data-label="Preview feedback"]'),
).toHaveCount(0);
await expect(page.locator("#bugdrop-host")).toHaveCount(0);
});
The negative assertion applies to the preview-only integration. If production intentionally has a different BugDrop installation, distinguish it with a different label and assert the preview script itself is absent.
Run it in CI
npm install --save-dev @playwright/test
npx playwright install --with-deps chromium
npx playwright test e2e/bugdrop.spec.ts
Pin the widget for deterministic CI if your release process requires it, and review installation, configuration, and security before enabling screenshots on sensitive routes. A smoke test proves loading and interaction; only a deliberate end-to-end canary should create and then verify a real GitHub Issue.