Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions __tests__/components/brain/my-stream/MyStreamWaveChat.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,18 @@ jest.mock("@/services/api/common-api", () => ({

const wave = { id: "10", participation: {}, metrics: { muted: false } } as any;
const mockOnDropClick = jest.fn();
const setDocumentVisibility = (visibilityState: DocumentVisibilityState) => {
Object.defineProperty(document, "visibilityState", {
configurable: true,
value: visibilityState,
});
};

describe("MyStreamWaveChat", () => {
let store: any;

beforeEach(() => {
setDocumentVisibility("visible");
capturedPropsHolder.current = {};
replaceMock.mockClear();
searchParamsMock.get.mockReset();
Expand Down Expand Up @@ -244,6 +251,27 @@ describe("MyStreamWaveChat", () => {
});
});

it("does not call the read endpoint on unmount when the tab is hidden", async () => {
setDocumentVisibility("hidden");

const { unmount } = renderWithProvider(
<MyStreamWaveChat
wave={wave}
firstUnreadSerialNo={null}
viewMode="chat"
onDropClick={mockOnDropClick}
/>
);

await act(async () => {
unmount();
});

expect(commonApiPostWithoutBodyAndResponse).not.toHaveBeenCalled();
expect(invalidateNotificationsMock).not.toHaveBeenCalled();
expect(mockRemoveWaveDeliveredNotifications).not.toHaveBeenCalled();
});
Comment thread
prxt6529 marked this conversation as resolved.

it("skips notification cleanup on unmount for anonymous viewers", async () => {
mockUseAuth.mockReturnValue({
connectedProfile: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,13 @@ describe("DropAttachmentDisplay", () => {
);

expect(
screen.getByRole("link", { name: "Open attachment" })
).toHaveAttribute("href", "https://example.com/files/paper.pdf");
screen.queryByRole("link", { name: "Open attachment" })
).not.toBeInTheDocument();

await user.click(
screen.getByRole("button", { name: "Download attachment" })
screen.getByRole("button", { name: "Attachment options" })
);
await user.click(screen.getByRole("button", { name: "Download" }));

expect(createObjectURLSpy).toHaveBeenCalled();
expect(clickSpy).toHaveBeenCalled();
Expand Down Expand Up @@ -183,7 +184,7 @@ describe("DropAttachmentDisplay", () => {
).not.toBeInTheDocument();
});

it("copies CSV attachment links without showing the PDF open action", async () => {
it("copies CSV attachment links without showing the open action", async () => {
const user = userEvent.setup();
const writeText = jest.fn().mockResolvedValue(undefined);
Object.defineProperty(navigator, "clipboard", {
Expand All @@ -198,16 +199,46 @@ describe("DropAttachmentDisplay", () => {
/>
);

const copyButton = screen.getByRole("button", {
name: "Copy attachment link",
});
await user.click(
screen.getByRole("button", { name: "Attachment options" })
);
const copyButton = screen.getByRole("button", { name: "Copy link" });
await user.click(copyButton);

expect(writeText).toHaveBeenCalledWith(
"https://example.com/files/data.csv"
);
expect(copyButton).toHaveAttribute("title", "Copied");
expect(copyButton).toHaveClass("tw-border-primary-400");
expect(screen.getByRole("button", { name: "Copied" })).toBeInTheDocument();
expect(
screen.queryByRole("link", { name: "Open attachment" })
).not.toBeInTheDocument();
});

it("copies PDF attachment links without showing the open action", async () => {
const user = userEvent.setup();
const writeText = jest.fn().mockResolvedValue(undefined);
Object.defineProperty(navigator, "clipboard", {
configurable: true,
value: { writeText },
});

render(
<DropAttachmentDisplay
mimeType="application/pdf"
attachmentUrl="https://example.com/files/paper.pdf"
/>
);

await user.click(
screen.getByRole("button", { name: "Attachment options" })
);
const copyButton = screen.getByRole("button", { name: "Copy link" });
await user.click(copyButton);

expect(writeText).toHaveBeenCalledWith(
"https://example.com/files/paper.pdf"
);
expect(screen.getByRole("button", { name: "Copied" })).toBeInTheDocument();
expect(
screen.queryByRole("link", { name: "Open attachment" })
).not.toBeInTheDocument();
Expand All @@ -230,11 +261,134 @@ describe("DropAttachmentDisplay", () => {
);

await user.click(
screen.getByRole("button", { name: "Copy attachment link" })
screen.getByRole("button", { name: "Attachment options" })
);
await user.click(screen.getByRole("button", { name: "Copy link" }));

expect(writeText).toHaveBeenCalledWith(
"https://ipfs.example.com/ipfs/bafybeigateway/sample.csv"
);
});

it("opens attachment preview without loading metadata", async () => {
const user = userEvent.setup();
const fetchSpy = jest.spyOn(globalThis, "fetch").mockResolvedValue({
ok: true,
text: async () => JSON.stringify({ name: "Sample", edition: 1 }),
} as Response);

render(
<DropAttachmentDisplay
mimeType="application/pdf"
attachmentUrl="ipfs://bafybeigateway/sample.pdf"
fileName="sample.pdf"
/>
);

await user.click(
screen.getByRole("button", { name: "Render attachment preview" })
);

expect(screen.getByTitle("sample.pdf")).toBeInTheDocument();
expect(fetchSpy).not.toHaveBeenCalled();
expect(screen.queryByText(/"name": "Sample"/)).not.toBeInTheDocument();
});

it("renders IPFS attachment metadata from the root CID", async () => {
const user = userEvent.setup();
const writeText = jest.fn().mockResolvedValue(undefined);
Object.defineProperty(navigator, "clipboard", {
configurable: true,
value: { writeText },
});
jest.spyOn(globalThis, "fetch").mockResolvedValue({
ok: true,
text: async () => JSON.stringify({ name: "Sample", edition: 1 }),
} as Response);

render(
<DropAttachmentDisplay
mimeType="application/pdf"
attachmentUrl="ipfs://bafybeigateway/sample.pdf"
fileName="sample.pdf"
/>
);

await user.click(
screen.getByRole("button", { name: "Attachment options" })
);
await user.click(screen.getByRole("button", { name: "View metadata" }));

await waitFor(() => {
expect(screen.getByText(/"name": "Sample"/)).toBeInTheDocument();
});
expect(globalThis.fetch).toHaveBeenCalledWith(
"https://ipfs.example.com/ipfs/bafybeigateway/metadata.json",
expect.objectContaining({ signal: expect.any(AbortSignal) })
);

const copyMetadataButton = screen.getByRole("button", {
name: "Copy metadata link",
});
await user.click(copyMetadataButton);

expect(writeText).toHaveBeenCalledWith(
"https://ipfs.example.com/ipfs/bafybeigateway/metadata.json"
);
expect(copyMetadataButton).toHaveAttribute("title", "Copied");
await user.click(
screen.getByRole("button", { name: "Close attachment details" })
);
expect(screen.queryByText(/"name": "Sample"/)).not.toBeInTheDocument();
});

it("shows a metadata not found message when metadata cannot load", async () => {
const user = userEvent.setup();
jest.spyOn(globalThis, "fetch").mockResolvedValue({
ok: false,
text: async () => "",
} as Response);

render(
<DropAttachmentDisplay
mimeType="application/pdf"
attachmentUrl="ipfs://bafybeigateway/sample.pdf"
fileName="sample.pdf"
/>
);

await user.click(
screen.getByRole("button", { name: "Attachment options" })
);
await user.click(screen.getByRole("button", { name: "View metadata" }));

await waitFor(() => {
expect(screen.getByText("Metadata not found.")).toBeInTheDocument();
});
});

it("copies attachment links from the options menu", async () => {
const user = userEvent.setup();
const writeText = jest.fn().mockResolvedValue(undefined);
Object.defineProperty(navigator, "clipboard", {
configurable: true,
value: { writeText },
});
render(
<DropAttachmentDisplay
mimeType="text/csv"
attachmentUrl="ipfs://bafybeigateway/sample.csv"
fileName="sample.csv"
/>
);

await user.click(
screen.getByRole("button", { name: "Attachment options" })
);
await user.click(screen.getByRole("button", { name: "Copy link" }));
expect(writeText).toHaveBeenCalledWith(
"https://ipfs.example.com/ipfs/bafybeigateway/sample.csv"
);
expect(screen.getByRole("button", { name: "Copied" })).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ReactQueryWrapperContext } from "@/components/react-query-wrapper/ReactQueryWrapper";
import { useWaveDropsNotificationRead } from "@/components/waves/drops/wave-drops-all/hooks/useWaveDropsNotificationRead";
import { commonApiPostWithoutBodyAndResponse } from "@/services/api/common-api";
import { render, waitFor } from "@testing-library/react";
import { act, render, waitFor } from "@testing-library/react";
import React from "react";

jest.mock("@/services/api/common-api", () => ({
Expand Down Expand Up @@ -33,8 +33,15 @@ describe("useWaveDropsNotificationRead", () => {
const removeWaveDeliveredNotifications = jest
.fn()
.mockResolvedValue(undefined);
const setDocumentVisibility = (visibilityState: DocumentVisibilityState) => {
Object.defineProperty(document, "visibilityState", {
configurable: true,
value: visibilityState,
});
};

beforeEach(() => {
setDocumentVisibility("visible");
invalidateNotifications.mockClear();
removeWaveDeliveredNotifications.mockClear();
(
Expand Down Expand Up @@ -82,4 +89,54 @@ describe("useWaveDropsNotificationRead", () => {
expect(invalidateNotifications).toHaveBeenCalled();
});
});

it("does not call the read endpoint when the tab is hidden", () => {
setDocumentVisibility("hidden");

render(
<ReactQueryWrapperContext.Provider
value={{ invalidateNotifications } as any}
>
<TestComponent
waveId="wave-1"
removeWaveDeliveredNotifications={removeWaveDeliveredNotifications}
/>
</ReactQueryWrapperContext.Provider>
);

expect(commonApiPostWithoutBodyAndResponse).not.toHaveBeenCalled();
expect(invalidateNotifications).not.toHaveBeenCalled();
expect(removeWaveDeliveredNotifications).not.toHaveBeenCalled();
});

it("syncs read state when a hidden tab becomes visible", async () => {
setDocumentVisibility("hidden");

render(
<ReactQueryWrapperContext.Provider
value={{ invalidateNotifications } as any}
>
<TestComponent
waveId="wave-1"
removeWaveDeliveredNotifications={removeWaveDeliveredNotifications}
/>
</ReactQueryWrapperContext.Provider>
);

expect(removeWaveDeliveredNotifications).not.toHaveBeenCalled();
expect(commonApiPostWithoutBodyAndResponse).not.toHaveBeenCalled();

act(() => {
setDocumentVisibility("visible");
document.dispatchEvent(new Event("visibilitychange"));
});

await waitFor(() => {
expect(removeWaveDeliveredNotifications).toHaveBeenCalledWith("wave-1");
expect(commonApiPostWithoutBodyAndResponse).toHaveBeenCalledWith({
endpoint: "notifications/wave/wave-1/read",
});
expect(invalidateNotifications).toHaveBeenCalled();
});
});
Comment thread
prxt6529 marked this conversation as resolved.
});
Loading
Loading