import "@testing-library/jest-dom"; import { render, waitFor } from "@testing-library/react"; import { createMemoryRouter, RouterProvider } from "react-router"; import fs from "node:fs"; import path from "node:path"; import { describe, expect, it, vi } from "vitest"; import type { FeedbackWithDetails } from "../db/feedback.db.server"; import { feedbackRedactionMarkers } from "../services/FeedbackRedaction.server"; import FeedbackTable from "./FeedbackTable"; vi.mock("react-i18next", () => ({ useTranslation: () => ({ t: (key: string) => key, i18n: { language: "de", exists: () => true } }), })); vi.mock("~/components/ui/buttons/ButtonSecondary", () => ({ default: ({ children, isLoading, destructive, ...props }: React.ComponentProps<"button"> & { isLoading?: boolean; destructive?: boolean }) => ( ), })); vi.mock("~/components/ui/icons/RefreshIcon", () => ({ default: ({ className }: { className?: string }) => refresh, })); vi.mock("~/components/ui/input/InputFilters", () => ({ default: () =>
, })); vi.mock("~/components/ui/input/InputSearchWithURL", () => ({ default: () => , })); vi.mock("~/components/ui/links/FilterableValueLink", () => ({ FilterableValueLink: ({ value }: { value?: string | null }) => {value}, })); const rawEmail = "legacy.user@example.com"; const rawBearerToken = "raw-admin-token-123"; const rawHexSecret = "abcdef0123456789abcdef0123456789"; const feedbackRow: FeedbackWithDetails = { id: "feedback_1", createdAt: new Date("2026-01-01T10:00:00.000Z"), tenantId: "tenant_1", userId: "user_1", message: `Login fails for ${rawEmail} with Bearer ${rawBearerToken} and trace ${rawHexSecret}`, fromUrl: "/acme?token=raw-url-token", tenant: { id: "tenant_1", name: "Acme", slug: "acme", icon: null, logo: null, deactivatedReason: null, types: [], active: true, theme: null, themeConfig: null, }, user: { id: "user_1", email: "admin@example.com", firstName: "Ada", lastName: "Admin", phone: null, locale: "en", createdAt: new Date("2025-01-01T00:00:00.000Z"), }, }; describe("FeedbackTable", () => { it("redacts sensitive legacy feedback message values before rendering the admin table", async () => { const router = createMemoryRouter([ { path: "/feedback", element: ( ), }, ], { initialEntries: ["/feedback"] }); const { container } = render(); await waitFor(() => expect(container).toHaveTextContent(feedbackRedactionMarkers.email)); expect(container).toHaveTextContent(`Bearer ${feedbackRedactionMarkers.token}`); expect(container).toHaveTextContent(feedbackRedactionMarkers.secret); expect(container).not.toHaveTextContent(rawEmail); expect(container).not.toHaveTextContent(rawBearerToken); expect(container).not.toHaveTextContent(rawHexSecret); }); it("keeps help desk dates and counts on locale-aware formatters", () => { const repoRoot = process.cwd(); const source = [ "apps/web/app/modules/helpDesk/components/FeedbackTable.tsx", "apps/web/app/routes/admin/help-desk/index.tsx", "apps/web/app/routes/admin/help-desk/tickets.tsx", ] .map((file) => fs.readFileSync(path.join(repoRoot, file), "utf8")) .join("\n"); expect(source).toContain("useLocaleFormatters"); expect(source).toContain("formatters.dateTime"); expect(source).toContain("formatters.number"); expect(source).not.toContain("DateUtils"); expect(source).not.toContain("NumberUtils"); }); });