Skip to content
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

Tests for hooks (1) #177

Merged
merged 20 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
45a03c6
feat: Add marketplace mock handlers
mithatakbulut Feb 17, 2025
1528c43
feat: Add metadata mock handler for contract info
mithatakbulut Feb 17, 2025
90e26b6
feat: Add test setup and wrapper
mithatakbulut Feb 17, 2025
2369645
test: Add tests for useCollection hook
mithatakbulut Feb 17, 2025
bd44aa9
test: Add renderHookWithClient utility for testing React hooks
mithatakbulut Feb 18, 2025
b91768b
test: Export MSW server for testing
mithatakbulut Feb 18, 2025
041fb53
test: Enhance metadata mock handlers with debug and expanded mock data
mithatakbulut Feb 18, 2025
0ddce92
test: Refactor useCollection hook test suite
mithatakbulut Feb 18, 2025
380a479
test: Add comprehensive test suite for useCollectible hook
mithatakbulut Feb 18, 2025
5d2faa7
test: Add comprehensive test suite for useFilters hook
mithatakbulut Feb 18, 2025
997cc9a
feat(sdk): implement wallet mocks and cancel transaction tests
mithatakbulut Feb 18, 2025
074f8e6
refactor: use wrapper from `test-utils`
mithatakbulut Feb 18, 2025
fad450a
refactor: refactor useCheckoutOptions.test to use MSW mocks
mithatakbulut Feb 18, 2025
2ba8df4
feat(mocks): add MSW handlers for indexer API with mock data
mithatakbulut Feb 18, 2025
53f09eb
test: add unit tests useCollectionBalanceDetails hook
mithatakbulut Feb 18, 2025
a47514a
test: add tests for useCollectionDetails hook
mithatakbulut Feb 18, 2025
58d92ba
feat: add mock for useCountListingsForCollectible hook and implement …
mithatakbulut Feb 18, 2025
bc82d8b
refactor: clean up unused imports
mithatakbulut Feb 18, 2025
923cd11
Merge branch 'master' into tests-for-hooks
mithatakbulut Feb 18, 2025
3fd5ea6
fix type issues
mithatakbulut Feb 18, 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
122 changes: 121 additions & 1 deletion packages/sdk/src/react/_internal/api/__mocks__/marketplace.msw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,20 @@ import {
type Order,
type CollectibleOrder,
type Activity,
type Collection,
type Marketplace,
MarketplaceKind,
OrderSide,
OrderStatus,
ActivityAction,
type Marketplace,
CurrencyStatus,
TransactionCrypto,
CollectionStatus,
CollectionPriority,
type Step,
StepType,
ExecuteType,
ContractType,
} from '../marketplace.gen';

import { zeroAddress } from 'viem';
Expand Down Expand Up @@ -115,6 +123,33 @@ export const mockActivity: Activity = {
updatedAt: new Date().toISOString(),
};

export const mockCollection: Collection = {
status: CollectionStatus.active,
chainId: 1,
contractAddress: '0x1234567890123456789012345678901234567890',
contractType: ContractType.ERC721,
priority: CollectionPriority.normal,
tokenQuantityDecimals: 0,
config: {
lastSynced: {},
collectiblesSynced: new Date().toISOString(),
activitiesSynced: new Date().toISOString(),
activitiesSyncedContinuity: new Date().toISOString(),
},
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};

export const mockSteps: Step[] = [
{
id: StepType.tokenApproval,
data: '0x...',
to: '0x1234567890123456789012345678901234567890',
value: '0',
executeType: ExecuteType.order,
},
];

// Debug configuration
export let isDebugEnabled = false;
export const enableDebug = () => {
Expand Down Expand Up @@ -219,4 +254,89 @@ export const handlers = [
mockMarketplaceHandler('GetCountOfOffersForCollectible', {
count: 1,
}),

mockMarketplaceHandler('GetCollectionDetail', {
collection: mockCollection,
}),

mockMarketplaceHandler('GetCollectibleLowestOffer', {
order: mockOrder,
}),

mockMarketplaceHandler('GetCollectibleHighestOffer', {
order: mockOrder,
}),

mockMarketplaceHandler('GetCollectibleLowestListing', {
order: mockOrder,
}),

mockMarketplaceHandler('GetCollectibleHighestListing', {
order: mockOrder,
}),

mockMarketplaceHandler('ListCollectibleListings', {
listings: [mockOrder],
page: { page: 1, pageSize: 10, more: false },
}),

mockMarketplaceHandler('ListCollectibleOffers', {
offers: [mockOrder],
page: { page: 1, pageSize: 10, more: false },
}),

mockMarketplaceHandler('GenerateBuyTransaction', {
steps: mockSteps,
}),

mockMarketplaceHandler('GenerateSellTransaction', {
steps: mockSteps,
}),

mockMarketplaceHandler('GenerateListingTransaction', {
steps: mockSteps,
}),

mockMarketplaceHandler('GenerateOfferTransaction', {
steps: mockSteps,
}),

mockMarketplaceHandler('GenerateCancelTransaction', {
steps: mockSteps,
}),

mockMarketplaceHandler('Execute', {
orderId: '0x9876543210987654321098765432109876543210',
}),

mockMarketplaceHandler('GetCountOfAllCollectibles', {
count: 100,
}),

mockMarketplaceHandler('GetCountOfFilteredCollectibles', {
count: 50,
}),

mockMarketplaceHandler('ListCollectiblesWithLowestListing', {
collectibles: [mockCollectibleOrder],
page: { page: 1, pageSize: 10, more: false },
}),

mockMarketplaceHandler('ListCollectiblesWithHighestOffer', {
collectibles: [mockCollectibleOrder],
page: { page: 1, pageSize: 10, more: false },
}),

mockMarketplaceHandler('SyncOrder', {}),

mockMarketplaceHandler('SyncOrders', {}),

mockMarketplaceHandler('CheckoutOptionsSalesContract', {
options: {
crypto: TransactionCrypto.all,
swap: [],
nftCheckout: [],
onRamp: [],
},
}),
];
39 changes: 39 additions & 0 deletions packages/sdk/src/react/_internal/api/__mocks__/metadata.msw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { type ContractInfo, ResourceStatus } from '@0xsequence/metadata';
import { http, HttpResponse } from 'msw';

export const mockContractInfo: ContractInfo = {
address: '0x0000000000000000000000000000000000000000',
chainId: 1,
name: 'Mock Collection',
symbol: 'MOCK',
type: 'ERC721',
status: ResourceStatus.AVAILABLE,
notFound: false,
deployed: true,
queuedAt: '2021-01-01T00:00:00Z',
updatedAt: new Date().toISOString(),
bytecodeHash: '0x1234567890',
extensions: {
description: 'A mock collection for testing',
link: 'https://example.com',
ogImage: 'https://example.com/image.png',
ogName: 'Mock Collection',
originAddress: '0x0000000000000000000000000000000000000000',
originChainId: 1,
verified: true,
categories: ['test'],
blacklist: false,
verifiedBy: '0x',
featured: true,
},
source: '0x',
logoURI: 'https://example.com/logo.png',
};

export const handlers = [
http.post('*/rpc/Metadata/GetContractInfo', () => {
return HttpResponse.json({
contractInfo: mockContractInfo,
});
}),
];
27 changes: 27 additions & 0 deletions packages/sdk/src/react/_internal/test/TestWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import type { PropsWithChildren } from 'react';
import { ConfigProvider } from '../../context/ConfigContext';

Check failure on line 3 in packages/sdk/src/react/_internal/test/TestWrapper.tsx

View workflow job for this annotation

GitHub Actions / Build

Cannot find module '../../context/ConfigContext' or its corresponding type declarations.
import type { SdkConfig } from '../../../types';

const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
gcTime: 0,
},
},
});

const defaultConfig = {
projectAccessKey: '1',
baseUrl: 'http://localhost:3000',
projectId: '1',
} as SdkConfig;

export const TestWrapper = ({ children }: PropsWithChildren) => {
return (
<QueryClientProvider client={queryClient}>
<ConfigProvider value={defaultConfig}>{children}</ConfigProvider>
</QueryClientProvider>
);
};
20 changes: 20 additions & 0 deletions packages/sdk/src/react/_internal/test/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { afterAll, afterEach, beforeAll } from 'vitest';
import { cleanup } from '@testing-library/react';
import { setupServer } from 'msw/node';
import { handlers as marketplaceHandlers } from '../api/__mocks__/marketplace.msw';
import { handlers as metadataHandlers } from '../api/__mocks__/metadata.msw';

const server = setupServer(...marketplaceHandlers, ...metadataHandlers);

beforeAll(() => {
server.listen();
});

afterEach(() => {
cleanup();
server.resetHandlers();
});

afterAll(() => {
server.close();
});
126 changes: 126 additions & 0 deletions packages/sdk/src/react/hooks/__tests__/useCollection.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { renderHook, waitFor } from '@testing-library/react';
import { describe, expect, it, beforeAll, afterAll, afterEach } from 'vitest';
import { useCollection } from '../useCollection';
import { TestWrapper } from '../../_internal/test/TestWrapper';
import { zeroAddress } from 'viem';
import type { UseCollectionArgs } from '../useCollection';
import { setupServer } from 'msw/node';
import { http, HttpResponse } from 'msw';
import { mockContractInfo } from '../../_internal/api/__mocks__/metadata.msw';

const server = setupServer(
http.post('*/rpc/Metadata/GetContractInfo', () => {
return HttpResponse.json({
contractInfo: mockContractInfo,
});
}),
);

describe('useCollection', () => {
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

const defaultArgs: UseCollectionArgs = {
chainId: '1',
collectionAddress: zeroAddress,
query: {},
};

it('should fetch collection data successfully', async () => {
const { result } = renderHook(() => useCollection(defaultArgs), {
wrapper: TestWrapper,
});

// Initially loading
expect(result.current.isLoading).toBe(true);
expect(result.current.data).toBeUndefined();

// Wait for data to be loaded
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});

// Verify the data matches our mock
expect(result.current.data).toEqual(mockContractInfo);
expect(result.current.error).toBeNull();
});

it('should handle error states', async () => {
// Override the handler for this test to return an error
server.use(
http.post('*/rpc/Metadata/GetContractInfo', () => {
return new HttpResponse(null, { status: 400 });
}),
);

const invalidArgs = {
...defaultArgs,
chainId: 'invalid', // This should cause validation error
};

const { result } = renderHook(() => useCollection(invalidArgs), {
wrapper: TestWrapper,
});

await waitFor(() => {
expect(result.current.isError).toBe(true);
});

expect(result.current.error).toBeDefined();
expect(result.current.data).toBeUndefined();
});

it('should refetch when args change', async () => {
const { result, rerender } = renderHook(
(props: UseCollectionArgs) => useCollection(props),
{
wrapper: TestWrapper,
initialProps: defaultArgs,
},
);

// Wait for initial data
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});

// Change props and rerender
const newArgs: UseCollectionArgs = {
...defaultArgs,
collectionAddress:
'0x1234567890123456789012345678901234567890' as `0x${string}`,
};

rerender(newArgs);

// Should be loading again
expect(result.current.isLoading).toBe(true);

// Wait for new data
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});

expect(result.current.data).toBeDefined();
});

it('should handle undefined query params', async () => {
const argsWithoutQuery: UseCollectionArgs = {
chainId: '1',
collectionAddress: zeroAddress,
query: {},
};

const { result } = renderHook(() => useCollection(argsWithoutQuery), {
wrapper: TestWrapper,
});

await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});

expect(result.current.data).toBeDefined();
expect(result.current.error).toBeNull();
});
});
Loading