-
Notifications
You must be signed in to change notification settings - Fork 7
fix(sdk): unread notification count uses a placeholder, not initialData #1852
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| import { vi } from "vitest"; | ||
| import React from "react"; | ||
| import { act, screen, waitFor } from "@testing-library/react"; | ||
| import "@testing-library/jest-dom"; | ||
| import { renderWithQueryClient } from "@/specs/test-utils"; | ||
|
|
||
| // The unread count options as the SDK ships them after vision-web#1851: a placeholder 0 | ||
| // while loading, then the server's count. Mocked here because web specs load the | ||
| // committed SDK build; the SDK's own spec covers the options themselves. | ||
| const unread = vi.hoisted(() => ({ fetch: vi.fn<() => Promise<number>>() })); | ||
| vi.mock("@ecency/sdk", async (importOriginal) => ({ | ||
| ...(await importOriginal<typeof import("@ecency/sdk")>()), | ||
| getNotificationsUnreadCountQueryOptions: (username?: string) => ({ | ||
| queryKey: ["notifications", "unread", username], | ||
| queryFn: () => unread.fetch(), | ||
| placeholderData: 0 | ||
| }) | ||
| })); | ||
| vi.mock("@/core/hooks", () => ({ | ||
| useActiveAccount: () => ({ activeUser: { username: "tester" } }) | ||
| })); | ||
|
|
||
| import { NavbarNotificationsButton } from "@/features/shared/navbar/navbar-notifications-button"; | ||
|
|
||
| const UNREAD_KEY = ["notifications", "unread", "tester"]; | ||
|
|
||
| function deferred<T>() { | ||
| let resolve: (value: T) => void = () => undefined; | ||
| const promise = new Promise<T>((r) => { | ||
| resolve = r; | ||
| }); | ||
| return { promise, resolve }; | ||
| } | ||
|
|
||
| const bell = (name: string) => screen.getByRole("button", { name }); | ||
| const isRinging = (button: HTMLElement) => button.querySelector(".animate-bell-ring") !== null; | ||
|
|
||
| describe("NavbarNotificationsButton", () => { | ||
| beforeEach(() => { | ||
| unread.fetch.mockReset(); | ||
| }); | ||
|
|
||
| it("does not ring for the count loaded with the page", async () => { | ||
| const first = deferred<number>(); | ||
| unread.fetch.mockReturnValue(first.promise); | ||
|
|
||
| renderWithQueryClient(<NavbarNotificationsButton />); | ||
| // The placeholder 0 is on screen while the request runs: no badge. | ||
| expect(bell("user-nav.notifications")).toBeInTheDocument(); | ||
|
|
||
| await act(async () => first.resolve(5)); | ||
|
|
||
| const button = await waitFor(() => bell("user-nav.notifications-unread")); | ||
| expect(screen.getByText("5")).toBeInTheDocument(); | ||
| expect(isRinging(button)).toBe(false); | ||
| }); | ||
|
|
||
| it("rings when the count rises while the page is open", async () => { | ||
| unread.fetch.mockResolvedValue(5); | ||
| const { queryClient } = renderWithQueryClient(<NavbarNotificationsButton />); | ||
| const button = await waitFor(() => bell("user-nav.notifications-unread")); | ||
| expect(isRinging(button)).toBe(false); | ||
|
|
||
| act(() => { | ||
| queryClient.setQueryData(UNREAD_KEY, 6); | ||
| }); | ||
|
|
||
| expect(await screen.findByText("6")).toBeInTheDocument(); | ||
| expect(isRinging(bell("user-nav.notifications-unread"))).toBe(true); | ||
| }); | ||
|
|
||
| it("shows no badge before the first count arrives", () => { | ||
| unread.fetch.mockReturnValue(deferred<number>().promise); | ||
| renderWithQueryClient(<NavbarNotificationsButton />); | ||
|
|
||
| expect(bell("user-nav.notifications")).toBeInTheDocument(); | ||
| expect(screen.queryByText("0")).not.toBeInTheDocument(); | ||
| }); | ||
| }); |
This file was deleted.
This file was deleted.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Large diffs are not rendered by default.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: ecency/vision-web
Length of output: 15623
🏁 Script executed:
Repository: ecency/vision-web
Length of output: 13540
Reset
prevUnreadRefwhen the active account changes.setActiveUsercan replace one logged-in user with another without clearing the mobile navbar. The mobile branch rendersNavbarNotificationsButtonat the same unkeyed position, so React preserves its numericprevUnreadRef. When the first non-placeholder count for the new account arrives, the effect compares it with the previous account’s count. A higher count can start the bell animation on initial load.Store the username with the count or reset the ref when
activeUser?.usernamechanges. Add an account-switch regression test.🤖 Prompt for AI Agents