Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
31a8ad5
Transfer NFTs
prxt6529 Oct 9, 2025
cb8b9e0
WIP
prxt6529 Oct 10, 2025
974c050
Merge branch 'main' into transfer-nfts
prxt6529 Oct 10, 2025
4e81eb0
WIP
prxt6529 Oct 13, 2025
18b5256
WIP
prxt6529 Oct 13, 2025
e9f3511
WIP
prxt6529 Oct 13, 2025
2de200f
WIP - Test fixes
prxt6529 Oct 13, 2025
24f10ff
Merge branch 'main' into transfer-nfts
prxt6529 Oct 13, 2025
cb4578a
WIP
prxt6529 Oct 13, 2025
2e08782
Merge branch 'main' into transfer-nfts
prxt6529 Oct 14, 2025
cb94d0d
WIP
prxt6529 Oct 14, 2025
146c509
WIP
prxt6529 Oct 14, 2025
7fc39e1
WIP
prxt6529 Oct 14, 2025
69f5cc6
WIP
prxt6529 Oct 14, 2025
b626c11
WIP
prxt6529 Oct 14, 2025
489c9ac
WIP
prxt6529 Oct 15, 2025
92b743c
WIP
prxt6529 Oct 15, 2025
24fc557
Merge branch 'main' into transfer-nfts
prxt6529 Oct 15, 2025
520c4b2
WIP
prxt6529 Oct 15, 2025
1178c0a
WIP
prxt6529 Oct 15, 2025
8059d58
WIP
prxt6529 Oct 15, 2025
90cfe3f
WIP
prxt6529 Oct 15, 2025
af736a7
WIP
prxt6529 Oct 15, 2025
4cc12ba
WIP
prxt6529 Oct 15, 2025
5f23d7a
WIP
prxt6529 Oct 15, 2025
6e4a8d7
WIP
prxt6529 Oct 15, 2025
3eb7784
WIP
prxt6529 Oct 15, 2025
acebea1
WIP
prxt6529 Oct 15, 2025
fa62c5e
WIP
prxt6529 Oct 15, 2025
e7a574c
Wip
prxt6529 Oct 15, 2025
f1a2c75
Merge branch 'main' into transfer-nfts
prxt6529 Oct 15, 2025
5b13d29
WIP
prxt6529 Oct 15, 2025
f6c0a2d
WIP
prxt6529 Oct 15, 2025
7201a5a
WIP
prxt6529 Oct 15, 2025
0eb3142
WIP
prxt6529 Oct 15, 2025
08575d9
WIP
prxt6529 Oct 15, 2025
da6dedf
Merge branch 'main' into transfer-nfts
prxt6529 Oct 15, 2025
35109f9
Merge branch 'main' into transfer-nfts
prxt6529 Oct 16, 2025
ecd1e25
Merge branch 'main' into transfer-nfts
prxt6529 Oct 21, 2025
a23343a
Merge branch 'main' into transfer-nfts
prxt6529 Oct 24, 2025
397a385
Merge branch 'main' into transfer-nfts
prxt6529 Oct 27, 2025
bb79b29
WIP
prxt6529 Oct 27, 2025
e676846
Merge branch 'main' into transfer-nfts
prxt6529 Oct 29, 2025
4333b9f
Merge branch 'main' into transfer-nfts
prxt6529 Oct 29, 2025
04f91d0
WIP
prxt6529 Oct 29, 2025
8d8492f
WIP
prxt6529 Oct 29, 2025
eb10583
WIP
prxt6529 Oct 29, 2025
e7e2af5
Merge branch 'main' into transfer-nfts
prxt6529 Oct 29, 2025
0af0de5
WIP
prxt6529 Oct 29, 2025
f8cff86
WIP
prxt6529 Oct 29, 2025
39bf208
WIP
prxt6529 Oct 29, 2025
f2c3e7b
WIP
prxt6529 Oct 29, 2025
309c397
Merge branch 'main' into transfer-nfts
prxt6529 Oct 30, 2025
7a690be
WIP
prxt6529 Oct 31, 2025
5cd0d78
Merge branch 'main' into transfer-nfts
prxt6529 Oct 31, 2025
6059a82
Merge branch 'main' into transfer-nfts
prxt6529 Oct 31, 2025
6ec7db2
Merge branch 'main' into transfer-nfts
prxt6529 Nov 3, 2025
ee23408
WIP
prxt6529 Nov 3, 2025
c3c12fe
WIP
prxt6529 Nov 3, 2025
020442e
WIP
prxt6529 Nov 3, 2025
a4abc8f
WIP
prxt6529 Nov 3, 2025
2ede5ce
WIP
prxt6529 Nov 3, 2025
6848e9b
WIP
prxt6529 Nov 3, 2025
326750e
WIP
prxt6529 Nov 3, 2025
2b85e9e
WIP
prxt6529 Nov 3, 2025
61ff776
WIP
prxt6529 Nov 3, 2025
88af818
WIP
prxt6529 Nov 3, 2025
d1a239b
WIP - tests fix
prxt6529 Nov 3, 2025
7be8465
WIP
prxt6529 Nov 3, 2025
93d8bba
WIP
prxt6529 Nov 3, 2025
6fcc6fe
WIP
prxt6529 Nov 3, 2025
43b4925
WIP
prxt6529 Nov 3, 2025
7925a28
WIP - responsiveness
prxt6529 Nov 3, 2025
e53cee1
WIP
prxt6529 Nov 4, 2025
9a235a9
WIP
prxt6529 Nov 4, 2025
3e70503
Merge branch 'main' into transfer-nfts
prxt6529 Nov 4, 2025
fe697a7
WIP
prxt6529 Nov 4, 2025
8c47559
WIP
prxt6529 Nov 4, 2025
f712786
WIP
prxt6529 Nov 4, 2025
2d3caed
WIP
prxt6529 Nov 4, 2025
f5f9cfd
WIP
prxt6529 Nov 5, 2025
632d0d4
WIP
prxt6529 Nov 5, 2025
4bf4b42
WIP
prxt6529 Nov 5, 2025
6e612da
WIP
prxt6529 Nov 5, 2025
d5df619
WIP
prxt6529 Nov 5, 2025
85d33c8
WIP
prxt6529 Nov 5, 2025
9af4009
WIP - Warning banner
prxt6529 Nov 5, 2025
900e0b2
WIP
prxt6529 Nov 5, 2025
28fed79
WIP
prxt6529 Nov 5, 2025
708295d
WIP
prxt6529 Nov 5, 2025
bbcaa59
WIP
prxt6529 Nov 5, 2025
ca067ef
WIP
prxt6529 Nov 5, 2025
fb356f0
WIP
prxt6529 Nov 5, 2025
f58a862
WIP
prxt6529 Nov 5, 2025
192ec2b
WIP
prxt6529 Nov 5, 2025
3003141
WIP
prxt6529 Nov 5, 2025
5b35d62
WIP
prxt6529 Nov 5, 2025
e228844
WIP
prxt6529 Nov 5, 2025
8263cd3
Merge branch 'main' into transfer-nfts
prxt6529 Nov 5, 2025
078d2e0
WIP
prxt6529 Nov 5, 2025
054bdc8
WIP
prxt6529 Nov 5, 2025
128b1dd
WIP
prxt6529 Nov 5, 2025
021bec0
WIP
prxt6529 Nov 5, 2025
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
19 changes: 18 additions & 1 deletion __tests__/components/6529Gradient/GradientPage.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { mockGradientCollection } from "@/__tests__/fixtures/gradientFixtures";
import GradientPageComponent from "@/components/6529Gradient/GradientPage";
import { AuthContext } from "@/components/auth/Auth";
import { SeizeConnectProvider } from "@/components/auth/SeizeConnectContext";
import { CookieConsentProvider } from "@/components/cookies/CookieConsentContext";
import { GRADIENT_CONTRACT } from "@/constants";
import { TitleProvider } from "@/contexts/TitleContext";
Expand Down Expand Up @@ -58,6 +59,20 @@ jest.mock("@/components/latest-activity/LatestActivityRow", () => ({
}));
jest.mock("@/hooks/useCapacitor", () => () => ({ isIos: false }));

jest.mock("@/components/auth/SeizeConnectContext", () => ({
useSeizeConnectContext: jest.fn(() => ({
isAuthenticated: false,
seizeConnect: jest.fn(),
seizeAcceptConnection: jest.fn(),
address: undefined,
hasInitializationError: false,
initializationError: null,
})),
SeizeConnectProvider: ({ children }: { children: React.ReactNode }) => (
<>{children}</>
),
}));

const routerReplace = jest.fn();
(useRouter as jest.Mock).mockReturnValue({
replace: routerReplace,
Expand Down Expand Up @@ -89,7 +104,9 @@ function renderPage(wallet: string = "0x1") {
<AuthContext.Provider
value={{ connectedProfile: { wallets: [{ wallet }] } } as any}>
<CookieConsentProvider>
<GradientPageComponent id="1" />
<SeizeConnectProvider>
<GradientPageComponent id="1" />
</SeizeConnectProvider>
</CookieConsentProvider>
</AuthContext.Provider>
</TitleProvider>
Expand Down
72 changes: 57 additions & 15 deletions __tests__/components/NftNavigation.test.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,54 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import NftNavigation from '@/components/nft-navigation/NftNavigation';
import { enterArtFullScreen, fullScreenSupported } from '@/helpers/Helpers';
import NftNavigation from "@/components/nft-navigation/NftNavigation";
import { enterArtFullScreen, fullScreenSupported } from "@/helpers/Helpers";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { ReadonlyURLSearchParams } from "next/navigation";

jest.mock('next/link', () => ({ __esModule: true, default: ({ href, children, className, ...props }: any) => <a href={href} className={className} {...props}>{children}</a> }));
jest.mock('@fortawesome/react-fontawesome', () => ({ FontAwesomeIcon: (props: any) => <svg data-testid="icon" onClick={props.onClick} /> }));
const makeParams = (query: string = "") =>
new URLSearchParams(query) as unknown as ReadonlyURLSearchParams;

jest.mock('@/helpers/Helpers', () => ({
jest.mock("next/link", () => ({
__esModule: true,
default: ({ href, children, className, ...props }: any) => (
<a href={href} className={className} {...props}>
{children}
</a>
),
}));
jest.mock("@fortawesome/react-fontawesome", () => ({
FontAwesomeIcon: (props: any) => (
<svg data-testid="icon" onClick={props.onClick} />
),
}));

jest.mock("@/helpers/Helpers", () => ({
enterArtFullScreen: jest.fn(),
fullScreenSupported: jest.fn(),
}));

const enterArtFullScreenMock = enterArtFullScreen as jest.Mock;
const fullScreenSupportedMock = fullScreenSupported as jest.Mock;

describe('NftNavigation', () => {
describe("NftNavigation", () => {
beforeEach(() => jest.clearAllMocks());

it('disables previous link when at first item', () => {
it("disables previous link when at first item", () => {
fullScreenSupportedMock.mockReturnValue(false);
render(<NftNavigation nftId={1} path="/art" startIndex={1} endIndex={3} />);
const links = screen.getAllByRole('link');
render(
<NftNavigation
nftId={1}
path="/art"
startIndex={1}
endIndex={3}
params={makeParams()}
/>
);
const links = screen.getAllByRole("link");
expect(links[0].className).toMatch(/tw-pointer-events-none/);
expect(links[1].className).not.toMatch(/tw-pointer-events-none/);
});

it('shows fullscreen icon and triggers fullscreen', async () => {
it("shows fullscreen icon and triggers fullscreen", async () => {
fullScreenSupportedMock.mockReturnValue(true);
render(
<NftNavigation
Expand All @@ -35,11 +57,31 @@ describe('NftNavigation', () => {
startIndex={1}
endIndex={3}
fullscreenElementId="art-1"
params={makeParams()}
/>
);
const icons = screen.getAllByTestId('icon');
const icons = screen.getAllByTestId("icon");
expect(icons).toHaveLength(3);
await userEvent.click(icons[2]);
expect(enterArtFullScreenMock).toHaveBeenCalledWith('art-1');
expect(enterArtFullScreenMock).toHaveBeenCalledWith("art-1");
});

it("preserves query params in navigation links", () => {
fullScreenSupportedMock.mockReturnValue(false);
const params = makeParams("foo=bar&mode=light");
render(
<NftNavigation
nftId={2}
path="/art"
startIndex={1}
endIndex={3}
params={params}
/>
);
const links = screen.getAllByRole("link");
expect(links.length).toBeGreaterThan(0);
for (const a of links) {
expect(a.getAttribute("href") || "").toContain("foo=bar");
}
});
});
124 changes: 74 additions & 50 deletions __tests__/components/common/TabToggleWithOverflow.test.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { TabToggleWithOverflow } from '@/components/common/TabToggleWithOverflow';
import { TabToggleWithOverflow } from "@/components/common/TabToggleWithOverflow";
import { act, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

describe('TabToggleWithOverflow', () => {
describe("TabToggleWithOverflow", () => {
const options = [
{ key: 'a', label: 'A' },
{ key: 'b', label: 'B' },
{ key: 'c', label: 'C' },
{ key: 'd', label: 'D' },
{ key: "a", label: "A" },
{ key: "b", label: "B" },
{ key: "c", label: "C" },
{ key: "d", label: "D" },
];

it('shows overflow items and handles selection', async () => {
const delay = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms));

const ensureButtonFocused = async (button: HTMLElement) => {
await act(async () => {
button.focus();
await delay(50);
if (document.activeElement !== button) {
button.focus();
}
});
};

it("shows overflow items and handles selection", async () => {
const onSelect = jest.fn();
const user = userEvent.setup();
render(
Expand All @@ -24,22 +36,24 @@ describe('TabToggleWithOverflow', () => {
);

// visible tabs
expect(screen.getByText('A')).toBeInTheDocument();
expect(screen.getByText('B')).toBeInTheDocument();
expect(screen.getByText("A")).toBeInTheDocument();
expect(screen.getByText("B")).toBeInTheDocument();

// open overflow dropdown
await user.click(screen.getByRole('button', { name: 'More tabs' }));
const optionC = screen.getByRole('menuitem', { name: 'C' });
await user.click(screen.getByRole("button", { name: "More tabs" }));
const optionC = screen.getByRole("menuitem", { name: "C" });
expect(optionC).toBeInTheDocument();

await user.click(optionC);
expect(onSelect).toHaveBeenCalledWith('c');
expect(onSelect).toHaveBeenCalledWith("c");
await waitFor(() =>
expect(screen.queryByRole('menuitem', { name: 'C' })).not.toBeInTheDocument()
expect(
screen.queryByRole("menuitem", { name: "C" })
).not.toBeInTheDocument()
);
});

it('shows active label when active tab is in overflow', () => {
it("shows active label when active tab is in overflow", () => {
render(
<TabToggleWithOverflow
options={options}
Expand All @@ -50,10 +64,10 @@ describe('TabToggleWithOverflow', () => {
);

// Button label should show active label
expect(screen.getByText('D')).toBeInTheDocument();
expect(screen.getByText("D")).toBeInTheDocument();
});

it('applies ARIA roles to visible tabs', () => {
it("applies ARIA roles to visible tabs", () => {
render(
<TabToggleWithOverflow
options={options}
Expand All @@ -63,18 +77,18 @@ describe('TabToggleWithOverflow', () => {
/>
);

expect(screen.getByRole('tablist')).toBeInTheDocument();
expect(screen.getByRole('tab', { name: 'A' })).toHaveAttribute(
'aria-selected',
'true'
expect(screen.getByRole("tablist")).toBeInTheDocument();
expect(screen.getByRole("tab", { name: "A" })).toHaveAttribute(
"aria-selected",
"true"
);
expect(screen.getByRole('tab', { name: 'B' })).toHaveAttribute(
'aria-selected',
'false'
expect(screen.getByRole("tab", { name: "B" })).toHaveAttribute(
"aria-selected",
"false"
);
});

it('toggles the overflow menu with keyboard interactions', async () => {
it("toggles the overflow menu with keyboard interactions", async () => {
const user = userEvent.setup();
render(
<TabToggleWithOverflow
Expand All @@ -85,36 +99,46 @@ describe('TabToggleWithOverflow', () => {
/>
);

const moreButton = screen.getByRole('button', { name: 'More tabs' });
expect(moreButton).toHaveAttribute('aria-expanded', 'false');
const moreButton = screen.getByRole("button", { name: "More tabs" });
expect(moreButton).toHaveAttribute("aria-expanded", "false");

await user.tab(); // focus first tab
await user.tab(); // focus second tab
moreButton.focus();
expect(moreButton).toHaveFocus();
await user.keyboard('{Enter}');
await waitFor(() => {
const activeTab = screen.getByRole("tab", { name: "A" });
expect(activeTab).toHaveFocus();
});

await ensureButtonFocused(moreButton);

await waitFor(() => expect(moreButton).toHaveFocus(), { timeout: 2000 });

await user.keyboard("{Enter}");
await waitFor(() =>
expect(moreButton).toHaveAttribute('aria-expanded', 'true')
expect(moreButton).toHaveAttribute("aria-expanded", "true")
);
const optionC = await screen.findByRole('menuitem', { name: 'C' });
const optionC = await screen.findByRole("menuitem", { name: "C" });
expect(optionC).toBeInTheDocument();

await user.keyboard('{Escape}');
await user.keyboard("{Escape}");
await waitFor(() =>
expect(moreButton).toHaveAttribute('aria-expanded', 'false')
expect(moreButton).toHaveAttribute("aria-expanded", "false")
);
await waitFor(() => {
expect(screen.queryByRole('menuitem', { name: 'C' })).not.toBeInTheDocument();
expect(
screen.queryByRole("menuitem", { name: "C" })
).not.toBeInTheDocument();
});

await user.keyboard(' ');
await ensureButtonFocused(moreButton);

await waitFor(() => expect(moreButton).toHaveFocus(), { timeout: 2000 });
await user.keyboard(" ");
await waitFor(() =>
expect(moreButton).toHaveAttribute('aria-expanded', 'true')
expect(moreButton).toHaveAttribute("aria-expanded", "true")
);
await screen.findByRole('menuitem', { name: 'C' });
await screen.findByRole("menuitem", { name: "C" });
});

it('indicates overflow active state via data attribute when opened', async () => {
it("indicates overflow active state via data attribute when opened", async () => {
const user = userEvent.setup();
render(
<TabToggleWithOverflow
Expand All @@ -125,16 +149,16 @@ describe('TabToggleWithOverflow', () => {
/>
);

const moreButton = screen.getByRole('button', { name: 'More tabs' });
const moreButton = screen.getByRole("button", { name: "More tabs" });
await user.click(moreButton);

expect(screen.getByRole('menuitem', { name: 'D' })).toHaveAttribute(
'data-active',
'true'
expect(screen.getByRole("menuitem", { name: "D" })).toHaveAttribute(
"data-active",
"true"
);
expect(screen.getByRole('menuitem', { name: 'C' })).toHaveAttribute(
'data-active',
'false'
expect(screen.getByRole("menuitem", { name: "C" })).toHaveAttribute(
"data-active",
"false"
);
});
});
Loading