diff --git a/apps/web/components/dashboard/quota-banner.test.tsx b/apps/web/components/dashboard/quota-banner.test.tsx
new file mode 100644
index 000000000..04c14dbe4
--- /dev/null
+++ b/apps/web/components/dashboard/quota-banner.test.tsx
@@ -0,0 +1,77 @@
+import { describe, expect, it } from "vitest";
+import { renderToStaticMarkup } from "react-dom/server";
+import { QuotaRejectionDetail } from "./quota-banner";
+
+/** The text a reader sees: everything outside `<...>` tags in rendered markup. */
+function visibleText(html: string): string {
+ let text = "";
+ let inTag = false;
+ for (const char of html) {
+ if (char === "<") inTag = true;
+ else if (char === ">") inTag = false;
+ else if (!inTag) text += char;
+ }
+ return text;
+}
+
+describe("QuotaRejectionDetail", () => {
+ it("renders nothing before any webhook was rejected", () => {
+ expect(renderToStaticMarkup()).toBe("");
+ expect(
+ renderToStaticMarkup(
+
+ )
+ ).toBe("");
+ });
+
+ it("names the total and each affected endpoint", () => {
+ const html = renderToStaticMarkup(
+
+ );
+ const text = visibleText(html);
+ expect(text).toBe(
+ "1,420 webhooks rejected this period on stripe-prod (1,000), gh-staging (420)."
+ );
+ expect(html).toContain('stripe-prod');
+ });
+
+ it("uses singular wording and summarizes unlisted endpoints", () => {
+ const one = renderToStaticMarkup(
+
+ );
+ expect(visibleText(one)).toBe("1 webhook rejected this period on solo (1).");
+
+ const many = renderToStaticMarkup(
+
+ );
+ expect(visibleText(many)).toBe(
+ "9 webhooks rejected this period on a (9) and 3 more endpoints."
+ );
+ });
+
+ it("escapes slugs", () => {
+ const html = renderToStaticMarkup(
+ x", rejected: 1 }],
+ }}
+ />
+ );
+ expect(html).not.toContain("