diff --git a/__tests__/components/home/NowMintingDetails.test.tsx b/__tests__/components/home/NowMintingDetails.test.tsx new file mode 100644 index 0000000000..56c808dac0 --- /dev/null +++ b/__tests__/components/home/NowMintingDetails.test.tsx @@ -0,0 +1,81 @@ +import NowMintingDetails from "@/components/home/now-minting/NowMintingDetails"; +import { render, screen } from "@testing-library/react"; + +jest.mock("next/link", () => ({ + __esModule: true, + default: ({ href, children, ...props }: any) => ( + + {children} + + ), +})); + +jest.mock("@/components/home/now-minting/NowMintingHeader", () => ({ + __esModule: true, + default: () =>
, +})); + +jest.mock("@/components/home/now-minting/NowMintingStatsGrid", () => ({ + __esModule: true, + default: () =>
, +})); + +jest.mock("@/components/home/now-minting/NowMintingCountdown", () => ({ + __esModule: true, + default: () =>
, +})); + +const baseNft = { + id: 667, + name: "All the roads lead to OM", + artist: "Lapis Light", + artist_seize_handle: "lapislight", + mint_date: new Date("2026-03-01"), + floor_price: 1.23456, + collection: "The Memes", + season: 1, +}; + +describe("NowMintingDetails", () => { + it("omits file metadata rows when media metadata is missing", () => { + render( + + ); + + expect(screen.queryByText("File type")).not.toBeInTheDocument(); + expect(screen.queryByText("Dimensions")).not.toBeInTheDocument(); + expect(screen.getByText("Collection")).toBeInTheDocument(); + expect(screen.getByText("Season")).toBeInTheDocument(); + }); + + it("renders file metadata rows when image metadata is present", () => { + render( + + ); + + expect(screen.getByText("File type")).toBeInTheDocument(); + expect(screen.getByText("PNG")).toBeInTheDocument(); + expect(screen.getByText("Dimensions")).toBeInTheDocument(); + expect(screen.getByText("1,200 x 800")).toBeInTheDocument(); + }); +}); diff --git a/__tests__/components/memelab/MemeLabPage.test.tsx b/__tests__/components/memelab/MemeLabPage.test.tsx index a8b97b373a..df34db0c50 100644 --- a/__tests__/components/memelab/MemeLabPage.test.tsx +++ b/__tests__/components/memelab/MemeLabPage.test.tsx @@ -2,11 +2,45 @@ import { SeizeConnectProvider } from "@/components/auth/SeizeConnectContext"; import MemeLabPageComponent from "@/components/memelab/MemeLabPage"; import { MEME_FOCUS } from "@/components/the-memes/MemeShared"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { act, render, screen, waitFor } from "@testing-library/react"; +import { + act, + fireEvent, + render, + screen, + waitFor, +} from "@testing-library/react"; import React from "react"; import { createConfig, http, WagmiProvider } from "wagmi"; import { mainnet } from "wagmi/chains"; +jest.mock("react-bootstrap", () => { + const actual = jest.requireActual("react-bootstrap"); + const Carousel = ({ children, onSlide, ...props }: any) => ( +
+
+ ); + + Carousel.Item = ({ children, ...props }: any) => ( +
{children}
+ ); + + return { + ...actual, + Carousel, + }; +}); + // Mock TitleContext jest.mock("@/contexts/TitleContext", () => ({ useTitle: jest.fn(() => ({ @@ -25,6 +59,13 @@ jest.mock("@/components/nft-marketplace-links/NFTMarketplaceLinks", () => ({ __esModule: true, default: () =>
, })); + +jest.mock("@/components/download/Download", () => ({ + __esModule: true, + default: ({ href }: { href: string }) => ( +
+ ), +})); // Mock Next.js components jest.mock("next/link", () => ({ __esModule: true, @@ -299,9 +340,9 @@ function createMockTransactions(balance: number) { }; } -function setupMockApiCalls(balance = 1) { +function setupMockApiCalls(balance = 1, nftOverrides: any = {}) { const meta = createMockMeta(); - const nft = createMockNft(); + const nft = createMockNft(nftOverrides); const transactions = createMockTransactions(balance); const activity = { data: [], count: 0 }; @@ -317,6 +358,15 @@ function setupMockApiCalls(balance = 1) { }); } +function getCardDetailValue(label: string): string { + const labelCell = screen.getByText(label); + const row = labelCell.closest("tr"); + const value = row?.querySelectorAll("td")[1]?.textContent; + + expect(value).toBeTruthy(); + return value ?? ""; +} + describe("MemeLabPageComponent", () => { it("renders without crashing", async () => { setupMockApiCalls(); @@ -467,6 +517,146 @@ describe("MemeLabPageComponent", () => { ); }); + it("falls back to top-level media URLs for art links", async () => { + mockUseSearchParams.mockReturnValue({ + get: jest.fn((key: string) => { + if (key === "focus") return MEME_FOCUS.THE_ART; + return null; + }), + }); + + setupMockApiCalls(1, { + image: "https://top-level.example/image.png", + animation: "https://top-level.example/animation.mp4", + metadata: { + attributes: [], + image_details: { format: "PNG" }, + animation_details: { format: "MP4" }, + }, + }); + + await act(async () => { + renderWithQueryClient(); + }); + + await waitFor(() => { + expect( + screen.getByRole("link", { + name: "https://top-level.example/image.png", + }) + ).toHaveAttribute("href", "https://top-level.example/image.png"); + expect( + screen.getByRole("link", { + name: "https://top-level.example/animation.mp4", + }) + ).toHaveAttribute("href", "https://top-level.example/animation.mp4"); + }); + }); + + it("ignores whitespace metadata.image and falls back to the top-level image", async () => { + mockUseSearchParams.mockReturnValue({ + get: jest.fn((key: string) => { + if (key === "focus") return MEME_FOCUS.THE_ART; + return null; + }), + }); + + setupMockApiCalls(1, { + image: " https://top-level.example/image.png ", + metadata: { + attributes: [], + image: " ", + image_details: { format: "PNG" }, + }, + }); + + await act(async () => { + renderWithQueryClient(); + }); + + await waitFor(() => { + expect( + screen.getByRole("link", { + name: "https://top-level.example/image.png", + }) + ).toHaveAttribute("href", "https://top-level.example/image.png"); + expect(screen.getByTestId("download")).toHaveAttribute( + "data-href", + "https://top-level.example/image.png" + ); + }); + }); + + it("treats metadata.animation_url-only NFTs as animated", async () => { + mockUseSearchParams.mockReturnValue({ + get: jest.fn((key: string) => { + if (key === "focus") return MEME_FOCUS.THE_ART; + return null; + }), + }); + + setupMockApiCalls(1, { + animation: "", + metadata: { + attributes: [], + image_details: { format: "PNG", width: 1200, height: 800 }, + animation_details: { format: "HTML", width: 1920, height: 1080 }, + animation_url: "https://metadata.example/animation.html", + image: "https://metadata.example/image.png", + }, + }); + + await act(async () => { + renderWithQueryClient(); + }); + + await waitFor(() => { + expect(screen.getAllByTestId("nft-image")).toHaveLength(2); + expect( + screen.getByRole("link", { + name: "https://metadata.example/animation.html", + }) + ).toHaveAttribute("href", "https://metadata.example/animation.html"); + expect(getCardDetailValue("File type")).toBe("HTML"); + expect(getCardDetailValue("Dimensions")).toBe("1,920 x 1,080"); + }); + }); + + it("switches card details to image metadata on the image slide", async () => { + mockUseSearchParams.mockReturnValue({ + get: jest.fn((key: string) => { + if (key === "focus") return MEME_FOCUS.THE_ART; + return null; + }), + }); + + setupMockApiCalls(1, { + animation: "", + metadata: { + attributes: [], + image_details: { format: "PNG", width: 1200, height: 800 }, + animation_details: { format: "HTML", width: 1920, height: 1080 }, + animation_url: "https://metadata.example/animation.html", + image: "https://metadata.example/image.png", + }, + }); + + await act(async () => { + renderWithQueryClient(); + }); + + await waitFor(() => { + expect(screen.getAllByTestId("nft-image")).toHaveLength(2); + }); + + fireEvent.click(screen.getByTestId("carousel-slide-1")); + + await waitFor(() => { + expect(getCardDetailValue("File type")).toBe("PNG"); + expect(getCardDetailValue("Dimensions")).toBe("1,200 x 800"); + }); + }); + it("handles empty metadata response", async () => { mockFetchUrl.mockImplementation((url: string) => { if (url.includes("lab_extended_data")) diff --git a/__tests__/components/nft-image/NFTImage.test.tsx b/__tests__/components/nft-image/NFTImage.test.tsx index eeb0c1817c..d54eac14e4 100644 --- a/__tests__/components/nft-image/NFTImage.test.tsx +++ b/__tests__/components/nft-image/NFTImage.test.tsx @@ -1,21 +1,23 @@ -import { render, screen } from '@testing-library/react'; -import React from 'react'; -import NFTImage from '@/components/nft-image/NFTImage'; +import { render, screen } from "@testing-library/react"; +import React from "react"; +import NFTImage from "@/components/nft-image/NFTImage"; // Mock next/image -jest.mock('next/image', () => { +jest.mock("next/image", () => { return function MockImage({ alt, priority, ...props }: any) { return {alt}; }; }); // Mock all renderer components -jest.mock('@/components/nft-image/renderers/NFTImageRenderer', () => { +jest.mock("@/components/nft-image/renderers/NFTImageRenderer", () => { return function MockNFTImageRenderer(props: any) { return (
{props.nft.name} - {props.balance > 0 && SEIZED{!props.showOwned ? ` x${props.balance}` : ""}} + {props.balance > 0 && ( + SEIZED{!props.showOwned ? ` x${props.balance}` : ""} + )} {props.showUnseized && props.balance === 0 && UNSEIZED} {props.showUnseized && props.balance === -1 && ...}
@@ -23,12 +25,14 @@ jest.mock('@/components/nft-image/renderers/NFTImageRenderer', () => { }; }); -jest.mock('@/components/nft-image/renderers/NFTVideoRenderer', () => { +jest.mock("@/components/nft-image/renderers/NFTVideoRenderer", () => { return function MockNFTVideoRenderer(props: any) { return (
@@ -36,12 +40,14 @@ jest.mock('@/components/nft-image/renderers/NFTVideoRenderer', () => { }; }); -jest.mock('@/components/nft-image/renderers/NFTHTMLRenderer', () => { +jest.mock("@/components/nft-image/renderers/NFTHTMLRenderer", () => { return function MockNFTHTMLRenderer(props: any) { return (