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
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import React from "react";
import { fireEvent, render, screen } from "@testing-library/react";
import { act, fireEvent, render, screen } from "@testing-library/react";

const mockGetArweaveGatewayFallbackUrls = jest.fn();
const mockShouldUseIframeFallbackTimeout = jest.fn();
const mockSandboxedExternalIframe = jest.fn();

jest.mock(
"@/components/drops/view/item/content/media/MediaDisplayImage",
Expand All @@ -26,15 +30,18 @@ jest.mock(
/>
)
);
jest.mock("@/components/common/SandboxedExternalIframe", () => (props: any) => (
<div
data-testid="iframe"
data-src={props.src}
data-title={props.title}
data-class-name={props.className}
data-container-class-name={props.containerClassName}
/>
));
jest.mock("@/components/common/SandboxedExternalIframe", () => (props: any) => {
mockSandboxedExternalIframe(props);
return (
<div
data-testid="iframe"
data-src={props.src}
data-title={props.title}
data-class-name={props.className}
data-container-class-name={props.containerClassName}
/>
);
});
jest.mock(
"@/components/drops/media/InteractiveMediaLoadGate",
() => (props: any) => (
Expand All @@ -52,9 +59,28 @@ jest.mock(
: () => <div data-testid="glb" />
);

jest.mock("@/components/nft-image/utils/gateway-fallback", () => ({
getArweaveGatewayFallbackUrls: (...args: unknown[]) =>
mockGetArweaveGatewayFallbackUrls(...args),
shouldUseIframeFallbackTimeout: (...args: unknown[]) =>
mockShouldUseIframeFallbackTimeout(...args),
}));

import MediaDisplay from "@/components/drops/view/item/content/media/MediaDisplay";

describe("MediaDisplay", () => {
beforeEach(() => {
jest.useRealTimers();
mockSandboxedExternalIframe.mockClear();
mockGetArweaveGatewayFallbackUrls.mockImplementation((url: string) => {
if (url === "ipfs://hash") {
return ["https://ipfs.io/ipfs/hash"];
}
return [url];
});
mockShouldUseIframeFallbackTimeout.mockReturnValue(true);
});

it("renders image", () => {
render(<MediaDisplay media_mime_type="image/png" media_url="img.png" />);
expect(screen.getByTestId("image")).toHaveAttribute("data-src", "img.png");
Expand Down Expand Up @@ -182,4 +208,54 @@ describe("MediaDisplay", () => {
);
expect(container).toBeEmptyDOMElement();
});

it("does not auto-advance ipfs html if the timeout fallback is disabled", () => {
jest.useFakeTimers();
mockGetArweaveGatewayFallbackUrls.mockReturnValue([
"https://ipfs.6529.io/ipfs/hash",
"https://ipfs.io/ipfs/hash",
]);
mockShouldUseIframeFallbackTimeout.mockReturnValue(false);

render(
<MediaDisplay
media_mime_type="text/html"
media_url="https://ipfs.6529.io/ipfs/hash"
/>
);

act(() => {
jest.advanceTimersByTime(8000);
});

expect(screen.getByTestId("iframe")).toHaveAttribute(
"data-src",
"https://ipfs.6529.io/ipfs/hash"
);
});

it("auto-advances html when timeout fallback is enabled", () => {
jest.useFakeTimers();
mockGetArweaveGatewayFallbackUrls.mockReturnValue([
"https://arweave.net/tx",
"https://ardrive.net/tx",
]);
mockShouldUseIframeFallbackTimeout.mockReturnValue(true);

render(
<MediaDisplay
media_mime_type="text/html"
media_url="https://arweave.net/tx"
/>
);

act(() => {
jest.advanceTimersByTime(8000);
});

expect(screen.getByTestId("iframe")).toHaveAttribute(
"data-src",
"https://ardrive.net/tx"
);
});
});
33 changes: 31 additions & 2 deletions __tests__/components/memelab/MemeLabPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,35 @@ describe("MemeLabPageComponent", () => {
});
});

it("does not crash on live tab when API media fields are null", async () => {
mockUseSearchParams.mockReturnValue({
get: jest.fn((key: string) => {
if (key === "focus") return MEME_FOCUS.LIVE;
return null;
}),
});

setupMockApiCalls(1, {
uri: null,
image: null,
animation: null,
metadata: {
attributes: [],
image: null,
animation: null,
animation_url: null,
},
});

await act(async () => {
renderWithQueryClient(<MemeLabPageComponent nftId="1" />);
});

await waitFor(() => {
expect(screen.getByRole("heading", { name: "NFT" })).toBeInTheDocument();
});
});

it("treats metadata.animation_url-only NFTs as animated", async () => {
mockUseSearchParams.mockReturnValue({
get: jest.fn((key: string) => {
Expand Down Expand Up @@ -674,7 +703,7 @@ describe("MemeLabPageComponent", () => {
expect(
screen.getByRole("link", { name: "Open animation in new tab" })
).toHaveAttribute("href", "https://metadata.example/animation.html");
expect(getCardDetailValue("File Type")).toBe("HTML");
expect(getCardDetailValue("File Type")).toBe("Interactive - HTML");
expect(getCardDetailValue("Dimensions")).toBe("1,920 x 1,080");
});
});
Expand Down Expand Up @@ -709,7 +738,7 @@ describe("MemeLabPageComponent", () => {
fireEvent.click(screen.getByTestId("carousel-slide-1"));

await waitFor(() => {
expect(getCardDetailValue("File Type")).toBe("PNG");
expect(getCardDetailValue("File Type")).toBe("Image - PNG");
expect(getCardDetailValue("Dimensions")).toBe("1,200 x 800");
});
});
Expand Down
73 changes: 73 additions & 0 deletions __tests__/components/nft-image/utils/gateway-fallback.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
jest.mock("@/components/ipfs/IPFSContext", () => ({
resolveIpfsUrlSync: (url: string) =>
url.startsWith("ipfs://")
? `https://ipfs.6529.io/ipfs/${url.slice("ipfs://".length)}`
: url,
}));

jest.mock("@/helpers/Helpers", () => ({
parseIpfsUrl: (url: string) =>
url.startsWith("ipfs://")
? `https://ipfs.io/ipfs/${url.slice("ipfs://".length)}`
: url,
}));

jest.mock("@/lib/media/ipfs-gateways", () => ({
getConfiguredIpfsGatewayHost: () => "ipfs.6529.io",
}));

jest.mock("@/lib/media/arweave-gateways", () => ({
ARWEAVE_FALLBACK_HOSTS: ["arweave.net", "ardrive.net", "gateway.arweave.net"],
canonicalizeArweaveGatewayHostname: (hostname: string) =>
hostname.toLowerCase(),
isArweaveGatewayRuntimeHost: (hostname: string) =>
["arweave.net", "ardrive.net", "gateway.arweave.net"].includes(
hostname.toLowerCase()
),
}));

import {
getArweaveGatewayFallbackUrls,
shouldUseIframeFallbackTimeout,
} from "@/components/nft-image/utils/gateway-fallback";

describe("gateway fallback helpers", () => {
it("prefers the configured gateway for ipfs protocol urls", () => {
expect(getArweaveGatewayFallbackUrls("ipfs://bafy-test")).toEqual([
"https://ipfs.6529.io/ipfs/bafy-test",
"https://ipfs.io/ipfs/bafy-test",
]);
});

it("normalizes ipfs gateway urls back to configured gateway first", () => {
expect(
getArweaveGatewayFallbackUrls("https://ipfs.io/ipfs/bafy-test")
).toEqual([
"https://ipfs.6529.io/ipfs/bafy-test",
"https://ipfs.io/ipfs/bafy-test",
]);
});

it("uses timeout fallback for approved arweave gateways", () => {
expect(
shouldUseIframeFallbackTimeout("https://arweave.net/transaction-id")
).toBe(true);
});

it("does not use timeout fallback for empty urls", () => {
expect(shouldUseIframeFallbackTimeout("")).toBe(false);
});

it("does not use timeout fallback for ipfs protocol urls", () => {
expect(shouldUseIframeFallbackTimeout("ipfs://bafy-test")).toBe(false);
});

it("does not use timeout fallback for ipfs gateways", () => {
expect(
shouldUseIframeFallbackTimeout("https://ipfs.6529.io/ipfs/bafy-test")
).toBe(false);
expect(
shouldUseIframeFallbackTimeout("https://ipfs.io/ipfs/bafy-test")
).toBe(false);
});
});
58 changes: 50 additions & 8 deletions __tests__/components/the-memes/MemePageArt.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ jest.mock("@/helpers/Helpers", () => ({
jest.mock("@/helpers/nft.helpers", () => ({
getAnimationDimensionsFromMetadata: jest.fn(),
getAnimationFileTypeFromMetadata: jest.fn(),
getAnimationMimeTypeFromMetadata: jest.fn(),
getImageDimensionsFromMetadata: jest.fn(),
getImageFileTypeFromMetadata: jest.fn(),
getImageMimeTypeFromMetadata: jest.fn(),
}));
jest.mock("@/components/nft-attributes/NFTAttributes", () => ({
__esModule: true,
Expand All @@ -86,14 +88,17 @@ const mockHelpers = jest.requireMock("@/helpers/Helpers") as {
const mockNftHelpers = jest.requireMock("@/helpers/nft.helpers") as {
getAnimationDimensionsFromMetadata: jest.Mock;
getAnimationFileTypeFromMetadata: jest.Mock;
getAnimationMimeTypeFromMetadata: jest.Mock;
getImageDimensionsFromMetadata: jest.Mock;
getImageFileTypeFromMetadata: jest.Mock;
getImageMimeTypeFromMetadata: jest.Mock;
};

const nft = {
id: 5,
has_distribution: false,
mint_price: 1,
hodl_rate: 24.48,
supply: 10,
collection: "c",
artist: "a",
Expand All @@ -116,9 +121,9 @@ const nft = {
const nftMeta = { season: 1, meme_name: "meme" };

const getCardDetailValue = (label: string): string => {
const labelCell = screen.getByText((_, element) => {
const labelCell = screen.getAllByText((_, element) => {
return element?.textContent?.toLowerCase() === label.toLowerCase();
});
})[0];
const row = labelCell.closest("tr");
const value = row?.querySelectorAll("td")[1]?.textContent;

Expand All @@ -129,7 +134,9 @@ const getCardDetailValue = (label: string): string => {
beforeEach(() => {
jest.clearAllMocks();
mockNftHelpers.getAnimationFileTypeFromMetadata.mockReturnValue("gif");
mockNftHelpers.getAnimationMimeTypeFromMetadata.mockReturnValue("image/gif");
mockNftHelpers.getImageFileTypeFromMetadata.mockReturnValue("png");
mockNftHelpers.getImageMimeTypeFromMetadata.mockReturnValue("image/png");
mockNftHelpers.getAnimationDimensionsFromMetadata.mockReturnValue("200x300");
mockNftHelpers.getImageDimensionsFromMetadata.mockReturnValue("100x100");
});
Expand Down Expand Up @@ -159,12 +166,13 @@ describe("MemePageArt", () => {
screen.getByRole("link", { name: "Open image in new tab" })
).toHaveAttribute("href", "img");
expect(screen.getByText("Card Details")).toBeInTheDocument();
expect(screen.getByText("Minting Approach")).toBeInTheDocument();
expect(screen.getAllByText("Minting Approach").length).toBeGreaterThan(0);
expect(screen.getByText("Card Description")).toBeInTheDocument();
expect(screen.getByText("Properties")).toBeInTheDocument();
expect(screen.getByText("Stats")).toBeInTheDocument();
expect(screen.getByText("Boosts")).toBeInTheDocument();
expect(getCardDetailValue("File type")).toBe("png");
expect(getCardDetailValue("File type")).toBe("Image - PNG");
expect(getCardDetailValue("TDH Rate")).toBe("24.48");
expect(getCardDetailValue("Dimensions")).toBe("100x100");
});

Expand Down Expand Up @@ -347,7 +355,7 @@ describe("MemePageArt", () => {
expect(
screen.getByRole("link", { name: "Open animation in new tab" })
).toHaveAttribute("href", "https://metadata.example/animation.gif");
expect(getCardDetailValue("File type")).toBe("gif");
expect(getCardDetailValue("File type")).toBe("Image - GIF");
expect(getCardDetailValue("Dimensions")).toBe("200x300");
});

Expand All @@ -374,7 +382,7 @@ describe("MemePageArt", () => {

fireEvent.click(screen.getByTestId("carousel-slide-1"));

expect(getCardDetailValue("File type")).toBe("png");
expect(getCardDetailValue("File type")).toBe("Image - PNG");
expect(getCardDetailValue("Dimensions")).toBe("100x100");
});

Expand All @@ -401,13 +409,13 @@ describe("MemePageArt", () => {
);

expect(screen.getAllByTestId("nft")).toHaveLength(1);
expect(getCardDetailValue("File type")).toBe("gif");
expect(getCardDetailValue("File type")).toBe("Image - GIF");
expect(getCardDetailValue("Dimensions")).toBe("200x300");

fireEvent.click(screen.getByTestId("carousel-slide-1"));

expect(screen.getAllByTestId("nft")).toHaveLength(1);
expect(getCardDetailValue("File type")).toBe("gif");
expect(getCardDetailValue("File type")).toBe("Image - GIF");
expect(getCardDetailValue("Dimensions")).toBe("200x300");

fireEvent.click(screen.getAllByTestId("fullscreen-icon")[0]);
Expand All @@ -416,4 +424,38 @@ describe("MemePageArt", () => {
"the-art-fullscreen-animation"
);
});

it("shows N/A dimensions for html art without dimension metadata", () => {
mockNftHelpers.getAnimationFileTypeFromMetadata.mockReturnValue("html");
mockNftHelpers.getAnimationMimeTypeFromMetadata.mockReturnValue(
"text/html"
);
mockNftHelpers.getAnimationDimensionsFromMetadata.mockReturnValue(
undefined
);

const nftWithHtmlAnimation = {
...nft,
image: "",
animation: "",
metadata: {
image_details: undefined,
animation_details: { format: "html" },
animation_url: "https://metadata.example/animation.html",
attributes: nft.metadata.attributes,
image: "",
},
};

render(
<MemePageArt
show={true}
nft={nftWithHtmlAnimation as any}
nftMeta={nftMeta as any}
/>
);

expect(getCardDetailValue("File type")).toBe("Interactive - HTML");
expect(getCardDetailValue("Dimensions")).toBe("N/A");
});
});
Loading
Loading