diff --git a/apps/web/src/features/i18n/locales/en-US.json b/apps/web/src/features/i18n/locales/en-US.json index 02b3275e72..d7aab3949a 100644 --- a/apps/web/src/features/i18n/locales/en-US.json +++ b/apps/web/src/features/i18n/locales/en-US.json @@ -1263,6 +1263,7 @@ "reveal-muted": "Muted author - Reveal comment", "reveal-muted-long-description": "Show additional replies, including those that may contain offensive content", "reveal": "Reveal comment", + "hide": "Hide comment", "reveal-comments": "Show {{n}} comments", "no-conversation": "No conversation yet, be first to reply", "start-conversation": "Start conversation", diff --git a/apps/web/src/features/shared/discussion/discussion-item.tsx b/apps/web/src/features/shared/discussion/discussion-item.tsx index f77fd4d25e..01407c8f5b 100644 --- a/apps/web/src/features/shared/discussion/discussion-item.tsx +++ b/apps/web/src/features/shared/discussion/discussion-item.tsx @@ -309,7 +309,7 @@ export const DiscussionItem = memo(function DiscussionItem({ aria-expanded={!isContentCollapsed} onClick={() => setIsContentCollapsed((value) => !value)} > - {i18next.t(isContentCollapsed ? toggleLabelKey : "chat.hide-message")} + {i18next.t(isContentCollapsed ? toggleLabelKey : "discussion.hide")} )} diff --git a/apps/web/src/specs/features/shared/discussion-item.spec.tsx b/apps/web/src/specs/features/shared/discussion-item.spec.tsx index e103c9e7b6..4f752e9ba2 100644 --- a/apps/web/src/specs/features/shared/discussion-item.spec.tsx +++ b/apps/web/src/specs/features/shared/discussion-item.spec.tsx @@ -84,6 +84,17 @@ vi.mock("@/features/shared/discussion/discussion-list", () => ({ DiscussionList: vi.mock("@/features/shared/comment", () => ({ Comment: () => null })); import { DiscussionItem } from "@/features/shared/discussion/discussion-item"; +import enUS from "@/features/i18n/locales/en-US.json"; + +// Resolve a dotted i18n key against the shipped English locale. +function getLocaleValue(key: string): unknown { + return key.split(".").reduce((acc, part) => { + if (typeof acc !== "object" || acc === null) { + return undefined; + } + return (acc as Record)[part]; + }, enUS as Record); +} function renderItem(entry: Entry, root: Entry) { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); @@ -148,6 +159,33 @@ describe("DiscussionItem", () => { expect(screen.queryByTestId("entry-tip-btn")).not.toBeInTheDocument(); }); + it("labels the collapse toggle with keys that exist in en-US.json", () => { + // i18next is globally mocked to echo the key, so the rendered text IS the + // key the component asked for. The hide side used to ask for + // `chat.hide-message`, which no locale defines, and shipped the raw key. + const lowRepComment = mockEntry({ + author: "spammer", + permlink: "re-the-post-spam", + parent_author: "bob", + parent_permlink: "the-post", + depth: 1, + author_reputation: -8, + net_rshares: 0, + stats: { flag_weight: 0, gray: true, hide: false, total_votes: 0 } + }); + + renderItem(lowRepComment, root); + + const revealKey = screen.getByRole("button", { name: /^discussion\./ }).textContent!; + expect(getLocaleValue(revealKey)).toBeTypeOf("string"); + + fireEvent.click(screen.getByText(revealKey)); + + const hideKey = screen.getByRole("button", { name: /^discussion\./ }).textContent!; + expect(hideKey).not.toBe(revealKey); + expect(getLocaleValue(hideKey)).toBeTypeOf("string"); + }); + it("animates the reply composer entrance only after clicking Reply, never on initial render", () => { activeUserRef.current = "demo"; const { container } = renderItem(comment, root);