import { HashRouter, Route } from "@solidjs/router"; import { fireEvent, render, screen, waitFor } from "@solidjs/testing-library"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { PostEngagementPanel } from "../PostEngagementPanel"; const getRecordBacklinksMock = vi.hoisted(() => vi.fn()); const moderateContentMock = vi.hoisted(() => vi.fn()); const postNavigationMock = vi.hoisted(() => ({ backFromPost: vi.fn(), buildPostHref: vi.fn(), openPost: vi.fn(), openPostEngagement: vi.fn(), openPostScreen: vi.fn(), })); vi.mock("$/lib/api/diagnostics", () => ({ DiagnosticsController: { getRecordBacklinks: getRecordBacklinksMock } })); vi.mock("$/components/posts/hooks/usePostNavigation", () => ({ usePostNavigation: () => postNavigationMock })); vi.mock("$/lib/api/moderation", () => ({ ModerationController: { moderateContent: moderateContentMock } })); const POST_URI = "at://did:plc:alice/app.bsky.feed.post/123"; function renderPanel(hash = `#/post/${encodeURIComponent(POST_URI)}/engagement`) { globalThis.location.hash = hash; return render(() => ( } /> )); } describe("PostEngagementPanel", () => { beforeEach(() => { vi.resetAllMocks(); moderateContentMock.mockResolvedValue({ alert: false, blur: "none", filter: false, inform: false, noOverride: false, }); getRecordBacklinksMock.mockResolvedValue({ likes: { cursor: null, records: [{ did: "did:plc:bob", profile: { handle: "bob.test", displayName: "Bob" }, uri: "at://did:plc:bob/app.bsky.feed.like/1", }], total: 1, }, quotes: { cursor: null, records: [{ did: "did:plc:carol", profile: { handle: "carol.test", displayName: "Carol" }, uri: "at://did:plc:carol/app.bsky.feed.post/9", value: { text: "This is a quoted post body." }, }], total: 1, }, replies: { cursor: null, records: [], total: 0 }, reposts: { cursor: null, records: [{ did: "did:plc:dana", profile: { handle: "dana.test", displayName: "Dana" }, uri: "at://did:plc:dana/app.bsky.feed.repost/3", }], total: 1, }, }); }); it("loads engagement and defaults to likes tab", async () => { renderPanel(); expect(await screen.findByText("Post Engagement")).toBeInTheDocument(); expect(await screen.findByText("Bob")).toBeInTheDocument(); expect(getRecordBacklinksMock).toHaveBeenCalledWith(POST_URI); }); it("opens quote posts from the quotes tab", async () => { renderPanel(`#/post/${encodeURIComponent(POST_URI)}/engagement?tab=quotes`); expect(await screen.findByText("This is a quoted post body.")).toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: /carol/i })); expect(postNavigationMock.openPostScreen).toHaveBeenCalledWith("at://did:plc:carol/app.bsky.feed.post/9"); }); it("switches engagement tabs via query-state routing", async () => { renderPanel(); await screen.findByText("Bob"); fireEvent.click(screen.getByRole("button", { name: /Reposts/i })); await waitFor(() => expect(globalThis.location.hash).toContain("tab=reposts")); expect(await screen.findByText("Dana")).toBeInTheDocument(); }); it("renders profile moderation badges for labeled engagement actors", async () => { getRecordBacklinksMock.mockResolvedValueOnce({ likes: { cursor: null, records: [{ did: "did:plc:bob", profile: { handle: "bob.test", displayName: "Bob", labels: [{ src: "did:plc:labeler", val: "sexual" }] }, uri: "at://did:plc:bob/app.bsky.feed.like/1", }], total: 1, }, quotes: { cursor: null, records: [], total: 0 }, replies: { cursor: null, records: [], total: 0 }, reposts: { cursor: null, records: [], total: 0 }, }); moderateContentMock.mockImplementation(async (_labels, context: string) => { if (context === "profileList") { return { alert: true, blur: "none", filter: false, inform: false, noOverride: false }; } return { alert: false, blur: "none", filter: false, inform: false, noOverride: false }; }); renderPanel(); expect(await screen.findByText("Bob")).toBeInTheDocument(); expect(await screen.findByText("Alert")).toBeInTheDocument(); }); });