@0xsequence/marketplace-sdk 0.5.1 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/dist/builder-types-wOwfTJpd.d.ts +73 -0
  2. package/dist/{chunk-FCF57DZI.js → chunk-7FN62HOP.js} +5 -9
  3. package/dist/chunk-7FN62HOP.js.map +1 -0
  4. package/dist/{chunk-F4E3WJ2K.js → chunk-BVXIRVEC.js} +261 -249
  5. package/dist/chunk-BVXIRVEC.js.map +1 -0
  6. package/dist/{chunk-XP3WY5AX.js → chunk-BZD2LDJJ.js} +2 -2
  7. package/dist/{chunk-XP3WY5AX.js.map → chunk-BZD2LDJJ.js.map} +1 -1
  8. package/dist/chunk-DZKPDV63.js +27 -0
  9. package/dist/chunk-DZKPDV63.js.map +1 -0
  10. package/dist/{chunk-I37CRQ4S.js → chunk-H5YWG6WN.js} +128 -88
  11. package/dist/chunk-H5YWG6WN.js.map +1 -0
  12. package/dist/{chunk-ZUEQGPLO.js → chunk-J6F5QOW5.js} +2 -2
  13. package/dist/{chunk-ZUEQGPLO.js.map → chunk-J6F5QOW5.js.map} +1 -1
  14. package/dist/{chunk-LJAB3S6U.js → chunk-TFRAOS7F.js} +22 -13
  15. package/dist/chunk-TFRAOS7F.js.map +1 -0
  16. package/dist/{chunk-5NORRVPM.js → chunk-UZIAX32Y.js} +1 -1
  17. package/dist/{chunk-5NORRVPM.js.map → chunk-UZIAX32Y.js.map} +1 -1
  18. package/dist/{chunk-MKGSGTQC.js → chunk-Y7YTLAO2.js} +3 -3
  19. package/dist/{create-config-BXvwUh55.d.ts → create-config-Bltg8Enl.d.ts} +1 -1
  20. package/dist/index.css +1 -1
  21. package/dist/index.d.ts +3 -3
  22. package/dist/index.js +10 -6
  23. package/dist/react/_internal/index.d.ts +3 -3
  24. package/dist/react/_internal/index.js +1 -1
  25. package/dist/react/_internal/wagmi/index.d.ts +2 -3
  26. package/dist/react/_internal/wagmi/index.js +1 -1
  27. package/dist/react/hooks/index.d.ts +72 -20
  28. package/dist/react/hooks/index.js +5 -7
  29. package/dist/react/index.css +1 -1
  30. package/dist/react/index.css.map +1 -1
  31. package/dist/react/index.d.ts +4 -4
  32. package/dist/react/index.js +8 -10
  33. package/dist/react/ssr/index.d.ts +54 -41
  34. package/dist/react/ssr/index.js +5 -9
  35. package/dist/react/ssr/index.js.map +1 -1
  36. package/dist/react/ui/components/collectible-card/index.css +1 -1
  37. package/dist/react/ui/components/collectible-card/index.css.map +1 -1
  38. package/dist/react/ui/components/collectible-card/index.d.ts +2 -2
  39. package/dist/react/ui/components/collectible-card/index.js +8 -8
  40. package/dist/react/ui/index.css +1 -1
  41. package/dist/react/ui/index.css.map +1 -1
  42. package/dist/react/ui/index.d.ts +2 -2
  43. package/dist/react/ui/index.js +8 -8
  44. package/dist/react/ui/modals/_internal/components/actionModal/index.d.ts +2 -2
  45. package/dist/react/ui/modals/_internal/components/actionModal/index.js +6 -6
  46. package/dist/styles/index.css +1 -1
  47. package/dist/styles/index.css.map +1 -1
  48. package/dist/styles/index.js +1 -1
  49. package/dist/types/index.d.ts +1 -3
  50. package/dist/types/index.js +9 -5
  51. package/dist/{types-Yto6KrTN.d.ts → types-BY3husBh.d.ts} +1 -1
  52. package/dist/utils/index.d.ts +2 -2
  53. package/dist/utils/index.js +4 -4
  54. package/package.json +11 -10
  55. package/src/react/_internal/api/__mocks__/indexer.msw.ts +197 -0
  56. package/src/react/_internal/api/__mocks__/marketplace.msw.ts +140 -1
  57. package/src/react/_internal/api/__mocks__/metadata.msw.ts +162 -0
  58. package/src/react/_internal/test/mocks/publicClient.ts +39 -0
  59. package/src/react/_internal/test/mocks/wagmi.ts +61 -0
  60. package/src/react/_internal/test/mocks/wallet.ts +61 -0
  61. package/src/react/_internal/test/setup.ts +28 -0
  62. package/src/react/_internal/test-utils.tsx +31 -2
  63. package/src/react/_internal/wagmi/__tests__/create-config.test.ts +53 -20
  64. package/src/react/_internal/wagmi/create-config.ts +3 -4
  65. package/src/react/_internal/wagmi/embedded.ts +1 -4
  66. package/src/react/_internal/wagmi/universal.ts +1 -4
  67. package/src/react/_internal/wallet/wallet.ts +1 -0
  68. package/src/react/hooks/__tests__/useAutoSelectFeeOption.test.tsx +314 -0
  69. package/src/react/hooks/__tests__/useBalanceOfCollectible.test.tsx +148 -0
  70. package/src/react/hooks/__tests__/useCancelOrder.test.tsx +410 -0
  71. package/src/react/hooks/__tests__/useCancelTransactionSteps.test.tsx +269 -0
  72. package/src/react/hooks/__tests__/useCollectible.test.tsx +120 -0
  73. package/src/react/hooks/__tests__/useCollection.test.tsx +101 -0
  74. package/src/react/hooks/__tests__/useCollectionBalanceDetails.test.tsx +175 -0
  75. package/src/react/hooks/__tests__/useCollectionDetails.test.tsx +82 -0
  76. package/src/react/hooks/__tests__/useCollectionDetailsPolling.test.tsx +133 -0
  77. package/src/react/hooks/__tests__/useCountListingsForCollectible.test.tsx +108 -0
  78. package/src/react/hooks/__tests__/useCountOfCollectables.test.tsx +129 -0
  79. package/src/react/hooks/__tests__/useCountOffersForCollectible.test.tsx +108 -0
  80. package/src/react/hooks/__tests__/useCurrencies.test.tsx +176 -0
  81. package/src/react/hooks/__tests__/useCurrency.test.tsx +153 -0
  82. package/src/react/hooks/__tests__/useCurrencyBalance.test.tsx +111 -0
  83. package/src/react/hooks/__tests__/useFilters.test.tsx +127 -0
  84. package/src/react/hooks/__tests__/useFloorOrder.test.tsx +101 -0
  85. package/src/react/hooks/__tests__/useGenerateBuyTransaction.test.tsx +173 -0
  86. package/src/react/hooks/__tests__/useGenerateCancelTransaction.test.tsx +207 -0
  87. package/src/react/hooks/__tests__/useGenerateListingTransaction.test.tsx +207 -0
  88. package/src/react/hooks/__tests__/useGenerateOfferTransaction.test.tsx +205 -0
  89. package/src/react/hooks/__tests__/useGenerateSellTransaction.test.tsx +181 -0
  90. package/src/react/hooks/__tests__/useHighestOffer.test.tsx +118 -0
  91. package/src/react/hooks/__tests__/useListBalances.test.tsx +136 -0
  92. package/src/react/hooks/__tests__/useListCollectibleActivities.test.tsx +200 -0
  93. package/src/react/hooks/__tests__/useListCollectibles.test.tsx +232 -0
  94. package/src/react/hooks/__tests__/useListCollectionActivities.test.tsx +235 -0
  95. package/src/react/hooks/__tests__/useListCollections.test.tsx +296 -0
  96. package/src/react/hooks/__tests__/useListListingsForCollectible.test.tsx +140 -0
  97. package/src/react/hooks/__tests__/useListOffersForCollectible.test.tsx +140 -0
  98. package/src/react/hooks/__tests__/useLowestListing.test.tsx +148 -0
  99. package/src/react/hooks/__tests__/useMarketplaceConfig.test.tsx +106 -0
  100. package/src/react/hooks/__tests__/useRoyaltyPercentage.test.tsx +129 -0
  101. package/src/react/hooks/index.ts +0 -1
  102. package/src/react/hooks/options/__mocks__/marketplaceConfig.msw.ts +66 -10
  103. package/src/react/hooks/options/__tests__/marketplaceConfigOptions.test.tsx +2 -11
  104. package/src/react/hooks/options/marketplaceConfigOptions.ts +8 -3
  105. package/src/react/hooks/useAutoSelectFeeOption.tsx +4 -3
  106. package/src/react/hooks/useCancelTransactionSteps.tsx +17 -9
  107. package/src/react/hooks/useCollectionDetailsPolling.tsx +1 -1
  108. package/src/react/hooks/useCurrencies.tsx +29 -28
  109. package/src/react/hooks/useFilters.tsx +69 -2
  110. package/src/react/hooks/useGenerateBuyTransaction.tsx +13 -5
  111. package/src/react/hooks/useListCollectibleActivities.tsx +1 -0
  112. package/src/react/hooks/useListCollectibles.tsx +1 -0
  113. package/src/react/hooks/useListCollectionActivities.tsx +1 -0
  114. package/src/react/hooks/useListCollections.tsx +2 -2
  115. package/src/react/ui/components/_internals/custom-select/__tests__/CustomSelect.test.tsx +6 -2
  116. package/src/react/ui/components/collectible-card/CollectibleCard.tsx +1 -1
  117. package/src/react/ui/components/collectible-card/Footer.tsx +9 -5
  118. package/src/react/ui/modals/BuyModal/Modal.tsx +9 -4
  119. package/src/react/ui/modals/BuyModal/__tests__/Modal.test.tsx +0 -1
  120. package/src/react/ui/modals/BuyModal/__tests__/store.test.ts +4 -2
  121. package/src/react/ui/modals/BuyModal/hooks/__tests__/useBuyCollectable.test.tsx +1 -24
  122. package/src/react/ui/modals/BuyModal/hooks/__tests__/useCheckoutOptions.test.tsx +152 -210
  123. package/src/react/ui/modals/BuyModal/hooks/__tests__/useFees.test.tsx +19 -49
  124. package/src/react/ui/modals/BuyModal/hooks/useFees.ts +6 -6
  125. package/src/react/ui/modals/BuyModal/modals/Modal1155.tsx +4 -2
  126. package/src/react/ui/modals/BuyModal/modals/__tests__/Modal1155.test.tsx +161 -52
  127. package/src/react/ui/modals/BuyModal/store.ts +7 -0
  128. package/src/react/ui/modals/CreateListingModal/Modal.tsx +1 -3
  129. package/src/react/ui/modals/CreateListingModal/__tests__/Modal.test.tsx +59 -227
  130. package/src/react/ui/modals/CreateListingModal/hooks/useCreateListing.tsx +2 -1
  131. package/src/react/ui/modals/CreateListingModal/hooks/useGetTokenApproval.ts +8 -2
  132. package/src/react/ui/modals/CreateListingModal/hooks/useTransactionSteps.tsx +9 -5
  133. package/src/react/ui/modals/MakeOfferModal/Modal.tsx +1 -8
  134. package/src/react/ui/modals/MakeOfferModal/__tests__/Modal.test.tsx +41 -118
  135. package/src/react/ui/modals/MakeOfferModal/hooks/useMakeOffer.tsx +2 -1
  136. package/src/react/ui/modals/MakeOfferModal/hooks/useTransactionSteps.tsx +9 -5
  137. package/src/react/ui/modals/SellModal/__tests__/Modal.test.tsx +4 -3
  138. package/src/react/ui/modals/SellModal/hooks/useGetTokenApproval.tsx +33 -31
  139. package/src/react/ui/modals/SellModal/hooks/useSell.tsx +1 -0
  140. package/src/react/ui/modals/SellModal/hooks/useTransactionSteps.tsx +9 -5
  141. package/src/react/ui/modals/SuccessfulPurchaseModal/__tests__/Modal.test.tsx +0 -1
  142. package/src/react/ui/modals/_internal/components/actionModal/ErrorModal.tsx +4 -2
  143. package/src/react/ui/modals/_internal/components/currencyOptionsSelect/__tests__/index.test.tsx +129 -57
  144. package/src/react/ui/modals/_internal/components/currencyOptionsSelect/index.tsx +1 -3
  145. package/src/react/ui/modals/_internal/components/priceInput/__tests__/index.test.tsx +1 -3
  146. package/src/react/ui/modals/_internal/components/transactionDetails/index.tsx +2 -2
  147. package/src/react/ui/modals/_internal/components/transactionStatusModal/__tests__/TransactionStatusModal.test.tsx +8 -8
  148. package/src/react/ui/modals/_internal/components/transactionStatusModal/hooks/useTransactionStatus.ts +1 -0
  149. package/src/types/builder-types.ts +79 -0
  150. package/src/types/index.ts +1 -1
  151. package/src/utils/__tests__/get-public-rpc-client.test.ts +2 -0
  152. package/src/utils/getMarketplaceDetails.ts +2 -2
  153. package/tsconfig.tsbuildinfo +1 -1
  154. package/vitest.config.js +2 -1
  155. package/dist/chunk-F4E3WJ2K.js.map +0 -1
  156. package/dist/chunk-FCF57DZI.js.map +0 -1
  157. package/dist/chunk-I37CRQ4S.js.map +0 -1
  158. package/dist/chunk-LJAB3S6U.js.map +0 -1
  159. package/dist/chunk-RK6KYMZM.js +0 -18
  160. package/dist/chunk-RK6KYMZM.js.map +0 -1
  161. package/dist/marketplace-config-znEu4L0K.d.ts +0 -60
  162. package/src/react/hooks/useCurrencyOptions.tsx +0 -16
  163. package/src/types/marketplace-config.ts +0 -67
  164. /package/dist/{chunk-MKGSGTQC.js.map → chunk-Y7YTLAO2.js.map} +0 -0
@@ -0,0 +1,120 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { useCollectible } from '../useCollectible';
3
+ import { renderHook, waitFor } from '../../_internal/test-utils';
4
+ import { zeroAddress } from 'viem';
5
+ import type { UseCollectibleArgs } from '../useCollectible';
6
+ import { http, HttpResponse } from 'msw';
7
+ import {
8
+ mockTokenMetadata,
9
+ mockMetadataEndpoint,
10
+ } from '../../_internal/api/__mocks__/metadata.msw';
11
+ import { server } from '../../_internal/test/setup';
12
+
13
+ describe('useCollectible', () => {
14
+ const defaultArgs: UseCollectibleArgs = {
15
+ chainId: '1',
16
+ collectionAddress: zeroAddress,
17
+ collectibleId: '1',
18
+ query: {},
19
+ };
20
+
21
+ it('should fetch collectible data successfully', async () => {
22
+ const { result } = renderHook(() => useCollectible(defaultArgs));
23
+
24
+ // Initially loading
25
+ expect(result.current.isLoading).toBe(true);
26
+ expect(result.current.data).toBeUndefined();
27
+
28
+ // Wait for data to be loaded
29
+ await waitFor(() => {
30
+ expect(result.current.isLoading).toBe(false);
31
+ });
32
+
33
+ // Verify the data matches our mock
34
+ expect(result.current.data).toEqual(mockTokenMetadata);
35
+ expect(result.current.error).toBeNull();
36
+ });
37
+
38
+ it('should handle error states', async () => {
39
+ // Override the handler for this test to return an error
40
+ server.use(
41
+ http.post(mockMetadataEndpoint('GetTokenMetadata'), () => {
42
+ return HttpResponse.json(
43
+ { error: { message: 'Failed to fetch collectible' } },
44
+ { status: 500 },
45
+ );
46
+ }),
47
+ );
48
+
49
+ const { result } = renderHook(() => useCollectible(defaultArgs));
50
+
51
+ await waitFor(() => {
52
+ expect(result.current.isError).toBe(true);
53
+ });
54
+
55
+ expect(result.current.error).toBeDefined();
56
+ expect(result.current.data).toBeUndefined();
57
+ });
58
+
59
+ it('should refetch when args change', async () => {
60
+ const { result, rerender } = renderHook(() => useCollectible(defaultArgs));
61
+
62
+ // Wait for initial data
63
+ await waitFor(() => {
64
+ expect(result.current.isLoading).toBe(false);
65
+ });
66
+
67
+ // Change args and rerender
68
+ const newArgs = {
69
+ ...defaultArgs,
70
+ collectibleId: '2',
71
+ collectionAddress:
72
+ '0x1234567890123456789012345678901234567890' as `0x${string}`,
73
+ };
74
+
75
+ rerender(() => useCollectible(newArgs));
76
+
77
+ // Wait for new data
78
+ await waitFor(() => {
79
+ expect(result.current.data).toBeDefined();
80
+ });
81
+
82
+ // Verify that the query was refetched with new args
83
+ expect(result.current.data).toBeDefined();
84
+ expect(result.current.isSuccess).toBe(true);
85
+ });
86
+
87
+ it('should handle undefined query params', async () => {
88
+ const argsWithoutQuery: UseCollectibleArgs = {
89
+ chainId: '1',
90
+ collectionAddress: zeroAddress,
91
+ collectibleId: '1',
92
+ query: {},
93
+ };
94
+
95
+ const { result } = renderHook(() => useCollectible(argsWithoutQuery));
96
+
97
+ await waitFor(() => {
98
+ expect(result.current.isLoading).toBe(false);
99
+ });
100
+
101
+ expect(result.current.data).toBeDefined();
102
+ expect(result.current.error).toBeNull();
103
+ });
104
+
105
+ it('should validate input parameters', async () => {
106
+ const invalidArgs: UseCollectibleArgs = {
107
+ ...defaultArgs,
108
+ // @ts-expect-error
109
+ chainId: {}, // Using an object instead of a string/number will fail validation
110
+ };
111
+
112
+ const { result } = renderHook(() => useCollectible(invalidArgs));
113
+
114
+ await waitFor(() => {
115
+ expect(result.current.isError).toBe(true);
116
+ });
117
+
118
+ expect(result.current.error).toBeDefined();
119
+ });
120
+ });
@@ -0,0 +1,101 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { useCollection } from '../useCollection';
3
+ import { renderHook, waitFor } from '../../_internal/test-utils';
4
+ import { zeroAddress } from 'viem';
5
+ import type { UseCollectionArgs } from '../useCollection';
6
+ import { http, HttpResponse } from 'msw';
7
+ import {
8
+ mockContractInfo,
9
+ mockMetadataEndpoint,
10
+ } from '../../_internal/api/__mocks__/metadata.msw';
11
+ import { server } from '../../_internal/test/setup';
12
+
13
+ describe('useCollection', () => {
14
+ const defaultArgs: UseCollectionArgs = {
15
+ chainId: '1',
16
+ collectionAddress: zeroAddress,
17
+ query: {},
18
+ };
19
+
20
+ it('should fetch collection data successfully', async () => {
21
+ const { result } = renderHook(() => useCollection(defaultArgs));
22
+
23
+ // Initially loading
24
+ expect(result.current.isLoading).toBe(true);
25
+ expect(result.current.data).toBeUndefined();
26
+
27
+ // Wait for data to be loaded
28
+ await waitFor(() => {
29
+ expect(result.current.isLoading).toBe(false);
30
+ });
31
+
32
+ // Verify the data matches our mock
33
+ expect(result.current.data).toEqual(mockContractInfo);
34
+ expect(result.current.error).toBeNull();
35
+ });
36
+
37
+ it('should handle error states', async () => {
38
+ // Override the handler for this test to return an error
39
+ server.use(
40
+ http.post(mockMetadataEndpoint('GetContractInfo'), () => {
41
+ return HttpResponse.json(
42
+ { error: { message: 'Failed to fetch collection' } },
43
+ { status: 500 },
44
+ );
45
+ }),
46
+ );
47
+
48
+ const { result } = renderHook(() => useCollection(defaultArgs));
49
+
50
+ await waitFor(() => {
51
+ expect(result.current.isError).toBe(true);
52
+ });
53
+
54
+ expect(result.current.error).toBeDefined();
55
+ expect(result.current.data).toBeUndefined();
56
+ });
57
+
58
+ it('should refetch when args change', async () => {
59
+ const { result, rerender } = renderHook(() => useCollection(defaultArgs));
60
+
61
+ // Wait for initial data
62
+ await waitFor(() => {
63
+ expect(result.current.isLoading).toBe(false);
64
+ });
65
+
66
+ // Change args and rerender
67
+ const newArgs = {
68
+ ...defaultArgs,
69
+ collectionAddress:
70
+ '0x1234567890123456789012345678901234567890' as `0x${string}`,
71
+ };
72
+
73
+ rerender(() => useCollection(newArgs));
74
+
75
+ // Wait for new data
76
+ await waitFor(() => {
77
+ expect(result.current.data).toBeDefined();
78
+ });
79
+
80
+ // Verify that the query was refetched with new args
81
+ expect(result.current.data).toBeDefined();
82
+ expect(result.current.isSuccess).toBe(true);
83
+ });
84
+
85
+ it('should handle undefined query params', async () => {
86
+ const argsWithoutQuery: UseCollectionArgs = {
87
+ chainId: '1',
88
+ collectionAddress: zeroAddress,
89
+ query: {},
90
+ };
91
+
92
+ const { result } = renderHook(() => useCollection(argsWithoutQuery));
93
+
94
+ await waitFor(() => {
95
+ expect(result.current.isLoading).toBe(false);
96
+ });
97
+
98
+ expect(result.current.data).toBeDefined();
99
+ expect(result.current.error).toBeNull();
100
+ });
101
+ });
@@ -0,0 +1,175 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { waitFor } from '@testing-library/react';
3
+ import { useCollectionBalanceDetails } from '../useCollectionBalanceDetails';
4
+ import { mockTokenBalance } from '../../_internal/api/__mocks__/indexer.msw';
5
+ import { zeroAddress } from 'viem';
6
+ import { renderHook } from '../../_internal/test-utils';
7
+ import { http, HttpResponse } from 'msw';
8
+ import { server } from '../../_internal/test/setup';
9
+ import { mockIndexerEndpoint } from '../../_internal/api/__mocks__/indexer.msw';
10
+
11
+ describe('useCollectionBalanceDetails', () => {
12
+ beforeEach(() => {
13
+ vi.clearAllMocks();
14
+ server.resetHandlers();
15
+ });
16
+
17
+ afterEach(() => {
18
+ server.resetHandlers();
19
+ });
20
+
21
+ const defaultArgs = {
22
+ chainId: 1,
23
+ filter: {
24
+ accountAddresses: [zeroAddress],
25
+ omitNativeBalances: true,
26
+ },
27
+ };
28
+
29
+ it('should fetch balance details successfully', async () => {
30
+ const { result } = renderHook(() =>
31
+ useCollectionBalanceDetails(defaultArgs),
32
+ );
33
+
34
+ // Initially loading
35
+ expect(result.current.isLoading).toBe(true);
36
+ expect(result.current.data).toBeUndefined();
37
+
38
+ // Wait for data to be loaded
39
+ await waitFor(() => {
40
+ expect(result.current.isLoading).toBe(false);
41
+ });
42
+
43
+ // Verify the data matches our mock
44
+ expect(result.current.data).toBeDefined();
45
+ expect(result.current.data?.balances[0]).toEqual(mockTokenBalance);
46
+ expect(result.current.error).toBeNull();
47
+ });
48
+
49
+ it('should handle error states', async () => {
50
+ // Override the handler for this test to return an error
51
+ server.use(
52
+ http.post(mockIndexerEndpoint('GetTokenBalancesDetails'), () => {
53
+ return HttpResponse.json(
54
+ { error: { message: 'Failed to fetch balances' } },
55
+ { status: 500 },
56
+ );
57
+ }),
58
+ );
59
+
60
+ const { result } = renderHook(() =>
61
+ useCollectionBalanceDetails(defaultArgs),
62
+ );
63
+
64
+ // Wait for the error state
65
+ await waitFor(() => {
66
+ expect(result.current.isError).toBe(true);
67
+ });
68
+
69
+ expect(result.current.error).toBeDefined();
70
+ expect(result.current.data).toBeUndefined();
71
+ });
72
+
73
+ it('should refetch when args change', async () => {
74
+ const { result, rerender } = renderHook(() =>
75
+ useCollectionBalanceDetails(defaultArgs),
76
+ );
77
+
78
+ // Wait for initial data
79
+ await waitFor(() => {
80
+ expect(result.current.isLoading).toBe(false);
81
+ });
82
+
83
+ // Change args and rerender
84
+ const newArgs = {
85
+ ...defaultArgs,
86
+ filter: {
87
+ ...defaultArgs.filter,
88
+ accountAddresses: [
89
+ '0x1234567890123456789012345678901234567890' as `0x${string}`,
90
+ ],
91
+ },
92
+ };
93
+
94
+ rerender(() => useCollectionBalanceDetails(newArgs));
95
+
96
+ // Wait for new data
97
+ await waitFor(() => {
98
+ expect(result.current.data).toBeDefined();
99
+ });
100
+
101
+ expect(result.current.isSuccess).toBe(true);
102
+ });
103
+
104
+ it('should handle multiple accounts', async () => {
105
+ const multipleAccountsArgs = {
106
+ ...defaultArgs,
107
+ filter: {
108
+ ...defaultArgs.filter,
109
+ accountAddresses: [
110
+ zeroAddress,
111
+ '0x1234567890123456789012345678901234567890' as `0x${string}`,
112
+ ],
113
+ },
114
+ };
115
+
116
+ const { result } = renderHook(() =>
117
+ useCollectionBalanceDetails(multipleAccountsArgs),
118
+ );
119
+
120
+ await waitFor(() => {
121
+ expect(result.current.isSuccess).toBe(true);
122
+ });
123
+
124
+ expect(result.current.data?.balances).toHaveLength(2);
125
+ expect(result.current.data?.balances).toEqual(
126
+ expect.arrayContaining([mockTokenBalance, mockTokenBalance]),
127
+ );
128
+ });
129
+
130
+ it('should handle contract whitelist filter', async () => {
131
+ const whitelistArgs = {
132
+ ...defaultArgs,
133
+ filter: {
134
+ ...defaultArgs.filter,
135
+ contractWhitelist: [zeroAddress],
136
+ },
137
+ };
138
+
139
+ const { result } = renderHook(() =>
140
+ useCollectionBalanceDetails(whitelistArgs),
141
+ );
142
+
143
+ await waitFor(() => {
144
+ expect(result.current.isSuccess).toBe(true);
145
+ });
146
+
147
+ expect(result.current.data?.balances[0].contractAddress).toBe(zeroAddress);
148
+ });
149
+
150
+ it('should handle validation errors', () => {
151
+ expect(() =>
152
+ renderHook(() =>
153
+ useCollectionBalanceDetails({
154
+ chainId: 'invalid-chain-id' as unknown as number,
155
+ filter: {
156
+ accountAddresses: [zeroAddress],
157
+ omitNativeBalances: true,
158
+ },
159
+ }),
160
+ ),
161
+ ).toThrow();
162
+
163
+ expect(() =>
164
+ renderHook(() =>
165
+ useCollectionBalanceDetails({
166
+ chainId: 1,
167
+ filter: {
168
+ accountAddresses: ['invalid-address' as unknown as `0x${string}`],
169
+ omitNativeBalances: true,
170
+ },
171
+ }),
172
+ ),
173
+ ).toThrow();
174
+ });
175
+ });
@@ -0,0 +1,82 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { useCollectionDetails } from '../useCollectionDetails';
3
+ import { renderHook, waitFor } from '../../_internal/test-utils';
4
+ import { zeroAddress } from 'viem';
5
+ import { http, HttpResponse } from 'msw';
6
+ import { mockCollection } from '../../_internal/api/__mocks__/marketplace.msw';
7
+ import { server } from '../../_internal/test/setup';
8
+ import { mockMarketplaceEndpoint } from '../../_internal/api/__mocks__/marketplace.msw';
9
+
10
+ describe('useCollectionDetails', () => {
11
+ const defaultArgs = {
12
+ chainId: 1,
13
+ collectionAddress: zeroAddress,
14
+ };
15
+
16
+ it('should fetch collection details successfully', async () => {
17
+ const { result } = renderHook(() => useCollectionDetails(defaultArgs));
18
+
19
+ // Initially loading
20
+ expect(result.current.isLoading).toBe(true);
21
+ expect(result.current.data).toBeUndefined();
22
+
23
+ // Wait for data to be loaded
24
+ await waitFor(() => {
25
+ expect(result.current.isLoading).toBe(false);
26
+ });
27
+
28
+ // Verify the data matches our mock
29
+ expect(result.current.data).toEqual(mockCollection);
30
+ expect(result.current.error).toBeNull();
31
+ });
32
+
33
+ it('should handle error states', async () => {
34
+ // Override the handler for this test to return an error
35
+ server.use(
36
+ http.post(mockMarketplaceEndpoint('GetCollectionDetail'), () => {
37
+ return HttpResponse.json(
38
+ { error: { message: 'Failed to fetch collection details' } },
39
+ { status: 500 },
40
+ );
41
+ }),
42
+ );
43
+
44
+ const { result } = renderHook(() => useCollectionDetails(defaultArgs));
45
+
46
+ await waitFor(() => {
47
+ expect(result.current.isError).toBe(true);
48
+ });
49
+
50
+ expect(result.current.error).toBeDefined();
51
+ expect(result.current.data).toBeUndefined();
52
+ });
53
+
54
+ it('should refetch when args change', async () => {
55
+ const { result, rerender } = renderHook(() =>
56
+ useCollectionDetails(defaultArgs),
57
+ );
58
+
59
+ // Wait for initial data
60
+ await waitFor(() => {
61
+ expect(result.current.isLoading).toBe(false);
62
+ });
63
+
64
+ // Change args and rerender
65
+ const newArgs = {
66
+ ...defaultArgs,
67
+ collectionAddress:
68
+ '0x1234567890123456789012345678901234567890' as `0x${string}`,
69
+ };
70
+
71
+ rerender(() => useCollectionDetails(newArgs));
72
+
73
+ // Wait for new data
74
+ await waitFor(() => {
75
+ expect(result.current.data).toBeDefined();
76
+ });
77
+
78
+ // Verify that the query was refetched with new args
79
+ expect(result.current.data).toBeDefined();
80
+ expect(result.current.isSuccess).toBe(true);
81
+ });
82
+ });
@@ -0,0 +1,133 @@
1
+ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
2
+ import { useCollectionDetailsPolling } from '../useCollectionDetailsPolling';
3
+ import { renderHook, waitFor } from '../../_internal/test-utils';
4
+ import { zeroAddress } from 'viem';
5
+ import { http, HttpResponse } from 'msw';
6
+ import { mockCollection } from '../../_internal/api/__mocks__/marketplace.msw';
7
+ import { server } from '../../_internal/test/setup';
8
+ import { mockMarketplaceEndpoint } from '../../_internal/api/__mocks__/marketplace.msw';
9
+ import { CollectionStatus } from '../../_internal/api/marketplace.gen';
10
+
11
+ describe('useCollectionDetailsPolling', () => {
12
+ const defaultArgs = {
13
+ chainId: 1,
14
+ collectionAddress: zeroAddress,
15
+ };
16
+
17
+ beforeEach(() => {
18
+ vi.useFakeTimers({ shouldAdvanceTime: true });
19
+ });
20
+
21
+ afterEach(() => {
22
+ vi.useRealTimers();
23
+ server.resetHandlers();
24
+ });
25
+
26
+ it('should poll collection details until terminal state is reached', async () => {
27
+ // Mock initial syncing state
28
+ const syncingCollection = {
29
+ ...mockCollection,
30
+ status: CollectionStatus.syncing_metadata,
31
+ };
32
+ let requestCount = 0;
33
+
34
+ server.use(
35
+ http.post(mockMarketplaceEndpoint('GetCollectionDetail'), () => {
36
+ requestCount++;
37
+ // Return syncing state for first request, then active state
38
+ const response =
39
+ requestCount === 1
40
+ ? { collection: syncingCollection }
41
+ : {
42
+ collection: {
43
+ ...mockCollection,
44
+ status: CollectionStatus.active,
45
+ },
46
+ };
47
+ return HttpResponse.json(response);
48
+ }),
49
+ );
50
+
51
+ const { result } = renderHook(() =>
52
+ useCollectionDetailsPolling(defaultArgs),
53
+ );
54
+
55
+ // Initially loading
56
+ expect(result.current.isLoading).toBe(true);
57
+ expect(result.current.data).toBeUndefined();
58
+
59
+ // Wait for first data fetch
60
+ await waitFor(() => {
61
+ expect(result.current.data).toBeDefined();
62
+ });
63
+
64
+ // Should be in syncing state
65
+ expect(result.current.data?.status).toBe(CollectionStatus.syncing_metadata);
66
+
67
+ // Advance timer and run pending timers to trigger next poll
68
+ await vi.advanceTimersByTimeAsync(2000);
69
+ await vi.runOnlyPendingTimersAsync();
70
+
71
+ // Wait for second fetch to complete
72
+ await waitFor(() => {
73
+ expect(result.current.data?.status).toBe(CollectionStatus.active);
74
+ });
75
+
76
+ // Verify polling stops after reaching terminal state
77
+ const currentRequestCount = requestCount;
78
+ await vi.advanceTimersByTimeAsync(2000);
79
+ await vi.runOnlyPendingTimersAsync();
80
+ expect(requestCount).toBe(currentRequestCount); // Should not make additional requests
81
+ }, 10000);
82
+
83
+ it('should stop polling after max attempts', async () => {
84
+ const syncingCollection = {
85
+ ...mockCollection,
86
+ status: CollectionStatus.syncing_metadata,
87
+ };
88
+ let requestCount = 0;
89
+
90
+ server.use(
91
+ http.post(mockMarketplaceEndpoint('GetCollectionDetail'), () => {
92
+ requestCount++;
93
+ return HttpResponse.json({ collection: syncingCollection });
94
+ }),
95
+ );
96
+
97
+ renderHook(() => useCollectionDetailsPolling(defaultArgs));
98
+
99
+ // Fast-forward through all attempts
100
+ for (let i = 0; i < 30; i++) {
101
+ await vi.advanceTimersByTimeAsync(2000);
102
+ await vi.runOnlyPendingTimersAsync();
103
+ }
104
+
105
+ const finalCount = requestCount;
106
+ await vi.advanceTimersByTimeAsync(30000);
107
+ await vi.runOnlyPendingTimersAsync();
108
+ expect(requestCount).toBe(finalCount); // Should not increase after max attempts
109
+ expect(requestCount).toBeLessThanOrEqual(30); // Should not exceed MAX_ATTEMPTS
110
+ }, 10000);
111
+
112
+ it('should handle error states', async () => {
113
+ server.use(
114
+ http.post(mockMarketplaceEndpoint('GetCollectionDetail'), () => {
115
+ return HttpResponse.json(
116
+ { error: { message: 'Failed to fetch collection details' } },
117
+ { status: 500 },
118
+ );
119
+ }),
120
+ );
121
+
122
+ const { result } = renderHook(() =>
123
+ useCollectionDetailsPolling(defaultArgs),
124
+ );
125
+
126
+ await waitFor(() => {
127
+ expect(result.current.isError).toBe(true);
128
+ });
129
+
130
+ expect(result.current.error).toBeDefined();
131
+ expect(result.current.data).toBeUndefined();
132
+ }, 10000);
133
+ });