@0xsequence/marketplace-sdk 0.4.7 → 0.4.9

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 (114) hide show
  1. package/dist/{chunk-6R4G7J6Q.js → chunk-7HWJ4DUX.js} +29 -5
  2. package/dist/chunk-7HWJ4DUX.js.map +1 -0
  3. package/dist/{chunk-5UCKYAMR.js → chunk-BJLOO4NP.js} +647 -376
  4. package/dist/chunk-BJLOO4NP.js.map +1 -0
  5. package/dist/{chunk-2OJB35FS.js → chunk-BMWCIHCB.js} +5 -5
  6. package/dist/{chunk-2OJB35FS.js.map → chunk-BMWCIHCB.js.map} +1 -1
  7. package/dist/{chunk-R7GVMKMM.js → chunk-CUA4SGWT.js} +540 -18
  8. package/dist/chunk-CUA4SGWT.js.map +1 -0
  9. package/dist/{chunk-ZEH4JI2U.js → chunk-FCF57DZI.js} +7 -2
  10. package/dist/chunk-FCF57DZI.js.map +1 -0
  11. package/dist/{chunk-JEOUQFT3.js → chunk-TFCSNRD5.js} +4 -3
  12. package/dist/chunk-TFCSNRD5.js.map +1 -0
  13. package/dist/{chunk-AQT3BQ67.js → chunk-Y3KINNAU.js} +12 -5
  14. package/dist/chunk-Y3KINNAU.js.map +1 -0
  15. package/dist/{create-config-D5WqfUft.d.ts → create-config-CgYQDyMD.d.ts} +2 -2
  16. package/dist/index.css +31 -17
  17. package/dist/index.d.ts +3 -3
  18. package/dist/index.js +3 -3
  19. package/dist/{marketplace-config-C_fDWzz0.d.ts → marketplace-config-BP5-XnFG.d.ts} +1 -1
  20. package/dist/{marketplace.gen-B8S8fflj.d.ts → marketplace.gen-BzmWLP9L.d.ts} +14 -4
  21. package/dist/react/_internal/api/index.d.ts +2 -2
  22. package/dist/react/_internal/api/index.js +3 -1
  23. package/dist/react/_internal/index.d.ts +5 -5
  24. package/dist/react/_internal/index.js +4 -2
  25. package/dist/react/_internal/wagmi/index.d.ts +3 -3
  26. package/dist/react/_internal/wagmi/index.js +1 -1
  27. package/dist/react/hooks/index.d.ts +46 -5
  28. package/dist/react/hooks/index.js +12 -5
  29. package/dist/react/index.css +37 -17
  30. package/dist/react/index.css.map +1 -1
  31. package/dist/react/index.d.ts +6 -6
  32. package/dist/react/index.js +15 -8
  33. package/dist/react/ssr/index.js +6 -1
  34. package/dist/react/ssr/index.js.map +1 -1
  35. package/dist/react/ui/components/collectible-card/index.css +37 -17
  36. package/dist/react/ui/components/collectible-card/index.css.map +1 -1
  37. package/dist/react/ui/components/collectible-card/index.d.ts +27 -4
  38. package/dist/react/ui/components/collectible-card/index.js +7 -8
  39. package/dist/react/ui/index.css +37 -17
  40. package/dist/react/ui/index.css.map +1 -1
  41. package/dist/react/ui/index.d.ts +3 -3
  42. package/dist/react/ui/index.js +7 -8
  43. package/dist/react/ui/modals/_internal/components/actionModal/index.d.ts +3 -3
  44. package/dist/react/ui/modals/_internal/components/actionModal/index.js +5 -5
  45. package/dist/{sdk-config-BXVH8PS2.d.ts → sdk-config-LbbmA85k.d.ts} +27 -7
  46. package/dist/{services-CdXAIjt1.d.ts → services-DW26ougH.d.ts} +1 -1
  47. package/dist/styles/index.css +31 -17
  48. package/dist/styles/index.css.map +1 -1
  49. package/dist/styles/index.d.ts +2 -2
  50. package/dist/styles/index.js +1 -1
  51. package/dist/types/index.d.ts +3 -3
  52. package/dist/types/index.js +2 -2
  53. package/dist/{types-eX4P9xju.d.ts → types-DhTZWw1U.d.ts} +2 -2
  54. package/dist/utils/index.d.ts +3 -3
  55. package/dist/utils/index.js +3 -3
  56. package/package.json +3 -2
  57. package/src/react/__tests__/provider.test.tsx +75 -0
  58. package/src/react/_internal/api/__mocks__/marketplace.msw.ts +4 -0
  59. package/src/react/_internal/api/marketplace.gen.ts +42 -7
  60. package/src/react/_internal/wagmi/__tests__/create-config.test.ts +196 -0
  61. package/src/react/_internal/wagmi/create-config.ts +9 -1
  62. package/src/react/_internal/wallet/useWallet.ts +30 -46
  63. package/src/react/_internal/wallet/wallet.ts +52 -6
  64. package/src/react/hooks/index.ts +2 -0
  65. package/src/react/hooks/options/__mocks__/marketplaceConfig.msw.ts +10 -1
  66. package/src/react/hooks/useCollectionDetails.tsx +35 -0
  67. package/src/react/hooks/useCollectionDetailsPolling.tsx +60 -0
  68. package/src/react/provider.tsx +5 -0
  69. package/src/react/ui/components/_internals/action-button/ActionButton.tsx +36 -118
  70. package/src/react/ui/components/_internals/action-button/components/ActionButtonBody.tsx +52 -0
  71. package/src/react/ui/components/_internals/action-button/components/NonOwnerActions.tsx +72 -0
  72. package/src/react/ui/components/_internals/action-button/components/OwnerActions.tsx +81 -0
  73. package/src/react/ui/components/_internals/action-button/hooks/useActionButtonLogic.ts +93 -0
  74. package/src/react/ui/components/_internals/action-button/store.ts +47 -0
  75. package/src/react/ui/components/_internals/action-button/styles.css.ts +8 -0
  76. package/src/react/ui/components/collectible-card/CollectibleCard.tsx +34 -14
  77. package/src/react/ui/components/collectible-card/Footer.tsx +9 -9
  78. package/src/react/ui/components/collectible-card/styles.css.ts +44 -31
  79. package/src/react/ui/icons/CartIcon.tsx +46 -0
  80. package/src/react/ui/modals/BuyModal/Modal.tsx +11 -4
  81. package/src/react/ui/modals/BuyModal/__tests__/Modal.test.tsx +253 -0
  82. package/src/react/ui/modals/BuyModal/__tests__/store.test.ts +100 -0
  83. package/src/react/ui/modals/BuyModal/hooks/__tests__/useBuyCollectable.test.tsx +402 -0
  84. package/src/react/ui/modals/BuyModal/hooks/__tests__/useCheckoutOptions.test.tsx +267 -0
  85. package/src/react/ui/modals/BuyModal/hooks/__tests__/useFees.test.tsx +166 -0
  86. package/src/react/ui/modals/BuyModal/hooks/__tests__/useLoadData.test.tsx +209 -0
  87. package/src/react/ui/modals/BuyModal/hooks/useCheckoutOptions.ts +19 -17
  88. package/src/react/ui/modals/BuyModal/hooks/useLoadData.ts +9 -7
  89. package/src/react/ui/modals/BuyModal/modals/CheckoutModal.tsx +7 -0
  90. package/src/react/ui/modals/BuyModal/modals/Modal1155.tsx +36 -18
  91. package/src/react/ui/modals/BuyModal/modals/__tests__/CheckoutModal.test.tsx +162 -0
  92. package/src/react/ui/modals/BuyModal/modals/__tests__/Modal1155.test.tsx +243 -0
  93. package/src/react/ui/modals/BuyModal/store.ts +11 -10
  94. package/src/react/ui/modals/CreateListingModal/Modal.tsx +26 -3
  95. package/src/react/ui/modals/CreateListingModal/__tests__/Modal.test.tsx +141 -29
  96. package/src/react/ui/modals/SuccessfulPurchaseModal/__tests__/Modal.test.tsx +145 -0
  97. package/src/react/ui/modals/_internal/components/actionModal/ActionModal.tsx +20 -11
  98. package/src/react/ui/modals/_internal/components/currencyOptionsSelect/__tests__/index.test.tsx +13 -58
  99. package/src/react/ui/modals/_internal/components/priceInput/__tests__/index.test.tsx +2 -0
  100. package/src/react/ui/modals/_internal/components/transactionStatusModal/__tests__/TransactionStatusModal.test.tsx +18 -19
  101. package/src/react/ui/modals/_internal/components/transactionStatusModal/__tests__/utils.test.ts +2 -0
  102. package/src/react/ui/modals/_internal/components/transactionStatusModal/hooks/useTransactionStatus.ts +62 -0
  103. package/src/react/ui/modals/_internal/components/transactionStatusModal/index.tsx +53 -100
  104. package/src/react/ui/modals/_internal/components/transactionStatusModal/store.ts +2 -10
  105. package/src/utils/_internal/error/config.ts +16 -0
  106. package/tsconfig.tsbuildinfo +1 -1
  107. package/dist/chunk-5UCKYAMR.js.map +0 -1
  108. package/dist/chunk-6R4G7J6Q.js.map +0 -1
  109. package/dist/chunk-AQT3BQ67.js.map +0 -1
  110. package/dist/chunk-FWN2MCLI.js +0 -425
  111. package/dist/chunk-FWN2MCLI.js.map +0 -1
  112. package/dist/chunk-JEOUQFT3.js.map +0 -1
  113. package/dist/chunk-R7GVMKMM.js.map +0 -1
  114. package/dist/chunk-ZEH4JI2U.js.map +0 -1
@@ -13,6 +13,8 @@ import {
13
13
  useCollectible,
14
14
  useCollection,
15
15
  useBalanceOfCollectible,
16
+ useCurrencies,
17
+ useCurrencyOptions,
16
18
  } from '../../../../hooks';
17
19
  import { useCreateListing } from '../hooks/useCreateListing';
18
20
  import { useWallet } from '../../../../_internal/wallet/useWallet';
@@ -27,6 +29,11 @@ vi.mock('../../../../hooks', () => ({
27
29
  isLoading: false,
28
30
  isError: false,
29
31
  }),
32
+ useCurrencyOptions: vi.fn().mockReturnValue({
33
+ data: ['0x123'],
34
+ isLoading: false,
35
+ isError: false,
36
+ }),
30
37
  useMarketplaceConfig: vi.fn().mockReturnValue({
31
38
  data: {
32
39
  collections: [
@@ -107,39 +114,29 @@ describe('CreateListingModal', () => {
107
114
  expect(screen.queryByText('List item for sale')).toBeNull();
108
115
  });
109
116
 
110
- it('should render loading state', () => {
117
+ it('should render main form when data is loaded', () => {
118
+ // Mock successful states for all required hooks
111
119
  (useCollectible as any).mockReturnValue({
112
- isLoading: true,
120
+ data: { decimals: 18, name: 'Test NFT' },
121
+ isLoading: false,
122
+ isError: false,
113
123
  });
114
-
115
- createListingModal$.open({
116
- collectionAddress: '0x123',
117
- chainId: '1',
118
- collectibleId: '1',
124
+ (useCollection as any).mockReturnValue({
125
+ data: { type: 'ERC721', name: 'Test Collection' },
126
+ isLoading: false,
127
+ isError: false,
119
128
  });
120
-
121
- render(<CreateListingModal />);
122
- const loadingModal = screen.getByTestId('loading-modal');
123
- expect(loadingModal).toBeVisible();
124
- });
125
-
126
- it('should render error state', () => {
127
- (useCollectible as any).mockReturnValue({
128
- isError: true,
129
+ (useCurrencyOptions as any).mockReturnValue({
130
+ data: ['0x123'],
131
+ isLoading: false,
132
+ isError: false,
129
133
  });
130
-
131
- createListingModal$.open({
132
- collectionAddress: '0x123',
133
- chainId: '1',
134
- collectibleId: '1',
134
+ (useCurrencies as any).mockReturnValue({
135
+ data: [{ address: '0x123', symbol: 'TEST', decimals: 18 }],
136
+ isLoading: false,
137
+ isError: false,
135
138
  });
136
139
 
137
- render(<CreateListingModal />);
138
- const errorModal = screen.getByTestId('error-modal');
139
- expect(errorModal).toBeVisible();
140
- });
141
-
142
- it('should render main form when data is loaded', () => {
143
140
  createListingModal$.open({
144
141
  collectionAddress: '0x123',
145
142
  chainId: '1',
@@ -148,6 +145,9 @@ describe('CreateListingModal', () => {
148
145
 
149
146
  render(<CreateListingModal />);
150
147
 
148
+ // Check for the modal title using a more specific selector
149
+ expect(screen.getByRole('dialog')).toBeInTheDocument();
150
+ // Check for the collection name in the token preview
151
151
  expect(screen.getByText('Test Collection')).toBeInTheDocument();
152
152
  });
153
153
 
@@ -183,6 +183,28 @@ describe('CreateListingModal', () => {
183
183
  });
184
184
 
185
185
  it('should update state based on price input', async () => {
186
+ // Mock successful states for all required hooks
187
+ (useCollectible as any).mockReturnValue({
188
+ data: { decimals: 18, name: 'Test NFT' },
189
+ isLoading: false,
190
+ isError: false,
191
+ });
192
+ (useCollection as any).mockReturnValue({
193
+ data: { type: 'ERC721', name: 'Test Collection' },
194
+ isLoading: false,
195
+ isError: false,
196
+ });
197
+ (useCurrencyOptions as any).mockReturnValue({
198
+ data: ['0x123'],
199
+ isLoading: false,
200
+ isError: false,
201
+ });
202
+ (useCurrencies as any).mockReturnValue({
203
+ data: [{ address: '0x123', symbol: 'TEST', decimals: 18 }],
204
+ isLoading: false,
205
+ isError: false,
206
+ });
207
+
186
208
  createListingModal$.open({
187
209
  collectionAddress: '0x123',
188
210
  chainId: '1',
@@ -194,8 +216,8 @@ describe('CreateListingModal', () => {
194
216
  // Initial price should be 0
195
217
  expect(createListingModal$.listingPrice.amountRaw.get()).toBe('0');
196
218
 
197
- // Find and interact with price input
198
- const priceInput = screen.getByRole('textbox', { name: 'Enter price' });
219
+ // Find and interact with price input using id
220
+ const priceInput = screen.getByRole('textbox', { name: /enter price/i });
199
221
  expect(priceInput).toBeInTheDocument();
200
222
 
201
223
  fireEvent.change(priceInput, { target: { value: '1.5' } });
@@ -205,4 +227,94 @@ describe('CreateListingModal', () => {
205
227
  expect(createListingModal$.listingPrice.amountRaw.get()).not.toBe('0');
206
228
  });
207
229
  });
230
+
231
+ it('should show loading modal when data is being fetched', () => {
232
+ // Mock loading states for all required hooks
233
+ (useCollectible as any).mockReturnValue({
234
+ isLoading: true,
235
+ isError: false,
236
+ });
237
+ (useCollection as any).mockReturnValue({
238
+ isLoading: true,
239
+ isError: false,
240
+ });
241
+ (useCurrencyOptions as any).mockReturnValue({
242
+ isLoading: true,
243
+ isError: false,
244
+ });
245
+
246
+ createListingModal$.open({
247
+ collectionAddress: '0x123',
248
+ chainId: '1',
249
+ collectibleId: '1',
250
+ });
251
+
252
+ render(<CreateListingModal />);
253
+
254
+ expect(screen.getByTestId('loading-modal')).toBeInTheDocument();
255
+ });
256
+
257
+ it('should show error modal when there is an error fetching data', () => {
258
+ // Mock error states for required hooks
259
+ (useCollectible as any).mockReturnValue({
260
+ isLoading: false,
261
+ isError: true,
262
+ });
263
+ (useCollection as any).mockReturnValue({
264
+ isLoading: false,
265
+ isError: false,
266
+ });
267
+ (useCurrencyOptions as any).mockReturnValue({
268
+ isLoading: false,
269
+ isError: false,
270
+ });
271
+
272
+ createListingModal$.open({
273
+ collectionAddress: '0x123',
274
+ chainId: '1',
275
+ collectibleId: '1',
276
+ });
277
+
278
+ render(<CreateListingModal />);
279
+
280
+ expect(screen.getByText('Error loading item details')).toBeInTheDocument();
281
+ });
282
+
283
+ it('should show no ERC20 configured modal when currencies array is empty', () => {
284
+ // Reset all hooks to success state
285
+ (useCollectible as any).mockReturnValue({
286
+ data: { decimals: 18, name: 'Test NFT' },
287
+ isLoading: false,
288
+ isError: false,
289
+ });
290
+ (useCollection as any).mockReturnValue({
291
+ data: { type: 'ERC721', name: 'Test Collection' },
292
+ isLoading: false,
293
+ isError: false,
294
+ });
295
+ (useCurrencyOptions as any).mockReturnValue({
296
+ data: [],
297
+ isLoading: false,
298
+ isError: false,
299
+ });
300
+ (useCurrencies as any).mockReturnValue({
301
+ data: [],
302
+ isLoading: false,
303
+ isError: false,
304
+ });
305
+
306
+ createListingModal$.open({
307
+ collectionAddress: '0x123',
308
+ chainId: '1',
309
+ collectibleId: '1',
310
+ });
311
+
312
+ render(<CreateListingModal />);
313
+
314
+ expect(
315
+ screen.getByText(
316
+ 'No currencies are configured for the marketplace, contact the marketplace owners',
317
+ ),
318
+ ).toBeInTheDocument();
319
+ });
208
320
  });
@@ -0,0 +1,145 @@
1
+ import '@testing-library/jest-dom/vitest';
2
+ import { render, screen, cleanup } from '../../../../_internal/test-utils';
3
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
4
+ import SuccessfulPurchaseModal, { useSuccessfulPurchaseModal } from '..';
5
+ import { successfulPurchaseModal$ } from '../_store';
6
+ import type { ModalCallbacks } from '../../_internal/types';
7
+
8
+ describe('SuccessfulPurchaseModal', () => {
9
+ beforeEach(() => {
10
+ cleanup();
11
+ vi.clearAllMocks();
12
+ });
13
+
14
+ it('should not render when modal is closed', () => {
15
+ render(<SuccessfulPurchaseModal />);
16
+ expect(screen.queryByText('Successful purchase!')).toBeNull();
17
+ });
18
+
19
+ it('should render with purchase details when opened', () => {
20
+ const mockPurchaseData = {
21
+ collectibles: [
22
+ {
23
+ tokenId: '1',
24
+ name: 'Test NFT',
25
+ image: 'https://test.com/image.png',
26
+ attributes: [],
27
+ },
28
+ ],
29
+ totalPrice: '1.5 ETH',
30
+ explorerUrl: 'https://etherscan.io/tx/123',
31
+ explorerName: 'Etherscan',
32
+ };
33
+
34
+ successfulPurchaseModal$.open(mockPurchaseData);
35
+ render(<SuccessfulPurchaseModal />);
36
+
37
+ expect(screen.getByText('Successful purchase!')).toBeInTheDocument();
38
+ expect(screen.getByText('1.5 ETH')).toBeInTheDocument();
39
+ expect(screen.getByText('1')).toBeInTheDocument(); // Number of items
40
+ expect(screen.getByText('View on Etherscan')).toBeInTheDocument();
41
+ });
42
+
43
+ it('should render CTA button when ctaOptions are provided', () => {
44
+ const mockPurchaseData = {
45
+ collectibles: [
46
+ {
47
+ tokenId: '1',
48
+ name: 'Test NFT',
49
+ image: 'https://test.com/image.png',
50
+ attributes: [],
51
+ },
52
+ ],
53
+ totalPrice: '1.5 ETH',
54
+ explorerUrl: 'https://etherscan.io/tx/123',
55
+ explorerName: 'Etherscan',
56
+ ctaOptions: {
57
+ ctaLabel: 'View Collection',
58
+ ctaOnClick: vi.fn(),
59
+ },
60
+ };
61
+
62
+ successfulPurchaseModal$.open(mockPurchaseData);
63
+ render(<SuccessfulPurchaseModal />);
64
+
65
+ expect(screen.getByText('View Collection')).toBeInTheDocument();
66
+ });
67
+
68
+ it('should handle multiple collectibles correctly', () => {
69
+ const mockPurchaseData = {
70
+ collectibles: Array(5)
71
+ .fill(null)
72
+ .map((_, index) => ({
73
+ tokenId: String(index + 1),
74
+ name: `Test NFT ${index + 1}`,
75
+ image: `https://test.com/image${index + 1}.png`,
76
+ attributes: [],
77
+ })),
78
+ totalPrice: '5.0 ETH',
79
+ explorerUrl: 'https://etherscan.io/tx/123',
80
+ explorerName: 'Etherscan',
81
+ };
82
+
83
+ successfulPurchaseModal$.open(mockPurchaseData);
84
+ render(<SuccessfulPurchaseModal />);
85
+
86
+ expect(screen.getByText('5 TOTAL')).toBeInTheDocument();
87
+ });
88
+
89
+ it('should close modal when close button is clicked', () => {
90
+ const mockPurchaseData = {
91
+ collectibles: [
92
+ {
93
+ tokenId: '1',
94
+ name: 'Test NFT',
95
+ image: 'https://test.com/image.png',
96
+ attributes: [],
97
+ },
98
+ ],
99
+ totalPrice: '1.5 ETH',
100
+ explorerUrl: 'https://etherscan.io/tx/123',
101
+ explorerName: 'Etherscan',
102
+ };
103
+
104
+ successfulPurchaseModal$.open(mockPurchaseData);
105
+ render(<SuccessfulPurchaseModal />);
106
+
107
+ const closeButton = screen.getByLabelText('Close modal');
108
+ closeButton.click();
109
+
110
+ expect(successfulPurchaseModal$.isOpen.get()).toBe(false);
111
+ });
112
+
113
+ describe('useSuccessfulPurchaseModal', () => {
114
+ it('should provide show and close functions', () => {
115
+ const modal = useSuccessfulPurchaseModal();
116
+
117
+ expect(modal.show).toBeDefined();
118
+ expect(modal.close).toBeDefined();
119
+ });
120
+
121
+ it('should pass callbacks to modal state', () => {
122
+ const callbacks = {
123
+ onSuccess: vi.fn(),
124
+ } satisfies ModalCallbacks;
125
+
126
+ const modal = useSuccessfulPurchaseModal(callbacks);
127
+ const mockPurchaseData = {
128
+ collectibles: [
129
+ {
130
+ tokenId: '1',
131
+ name: 'Test NFT',
132
+ image: 'https://test.com/image.png',
133
+ attributes: [],
134
+ },
135
+ ],
136
+ totalPrice: '1.5 ETH',
137
+ explorerUrl: 'https://etherscan.io/tx/123',
138
+ explorerName: 'Etherscan',
139
+ };
140
+
141
+ modal.show(mockPurchaseData);
142
+ expect(successfulPurchaseModal$.callbacks.get()).toBe(callbacks);
143
+ });
144
+ });
145
+ });
@@ -12,7 +12,14 @@ import {
12
12
  Text,
13
13
  } from '@0xsequence/design-system';
14
14
  import { observer } from '@legendapp/state/react';
15
- import { Close, Content, Overlay, Portal, Root } from '@radix-ui/react-dialog';
15
+ import {
16
+ Close,
17
+ Content,
18
+ Overlay,
19
+ Portal,
20
+ Root,
21
+ Title,
22
+ } from '@radix-ui/react-dialog';
16
23
  import { getProviderEl } from '../../../../../_internal';
17
24
  import {
18
25
  closeButton,
@@ -77,16 +84,18 @@ export const ActionModal = observer(
77
84
  gap="4"
78
85
  position={'relative'}
79
86
  >
80
- <Text
81
- fontSize="medium"
82
- fontWeight="bold"
83
- textAlign="center"
84
- width="full"
85
- color="text100"
86
- fontFamily="body"
87
- >
88
- {title}
89
- </Text>
87
+ <Title asChild>
88
+ <Text
89
+ fontSize="medium"
90
+ fontWeight="bold"
91
+ textAlign="center"
92
+ width="full"
93
+ color="text100"
94
+ fontFamily="body"
95
+ >
96
+ {title}
97
+ </Text>
98
+ </Title>
90
99
 
91
100
  {children}
92
101
 
@@ -1,54 +1,12 @@
1
1
  import '@testing-library/jest-dom/vitest';
2
- import { render, screen, cleanup, fireEvent } from '@testing-library/react';
2
+ import { screen, cleanup, fireEvent } from '@testing-library/react';
3
3
  import { describe, it, expect, vi, beforeEach } from 'vitest';
4
4
  import { observable } from '@legendapp/state';
5
5
  import CurrencyOptionsSelect from '..';
6
6
  import type { Currency } from '../../../../../../_internal';
7
7
  import { useCurrencies } from '../../../../../../hooks';
8
-
9
- // Mock currencies for testing
10
- const MOCK_USDC: Currency = {
11
- symbol: 'USDC',
12
- contractAddress: '0x1234',
13
- chainId: 1,
14
- name: 'USD Coin',
15
- decimals: 6,
16
- imageUrl: 'https://example.com/usdc.png',
17
- exchangeRate: 1,
18
- defaultChainCurrency: false,
19
- nativeCurrency: false,
20
- createdAt: new Date().toISOString(),
21
- updatedAt: new Date().toISOString(),
22
- };
23
-
24
- const MOCK_WETH: Currency = {
25
- symbol: 'WETH',
26
- contractAddress: '0x5678',
27
- chainId: 1,
28
- name: 'Wrapped Ether',
29
- decimals: 18,
30
- imageUrl: 'https://example.com/weth.png',
31
- exchangeRate: 1,
32
- defaultChainCurrency: false,
33
- nativeCurrency: false,
34
- createdAt: new Date().toISOString(),
35
- updatedAt: new Date().toISOString(),
36
- };
37
-
38
- // Mock the required hooks
39
- vi.mock('../../../../../../hooks/useCurrencyOptions', () => ({
40
- useCurrencyOptions: vi.fn(() => ({
41
- USDC: MOCK_USDC.contractAddress,
42
- WETH: MOCK_WETH.contractAddress,
43
- })),
44
- }));
45
-
46
- vi.mock('../../../../../../hooks', () => ({
47
- useCurrencies: vi.fn(() => ({
48
- data: [MOCK_USDC, MOCK_WETH],
49
- isLoading: false,
50
- })),
51
- }));
8
+ import { mockCurrencies } from '../../../../../../_internal/api/__mocks__/marketplace.msw';
9
+ import { render } from '../../../../../../_internal/test-utils';
52
10
 
53
11
  // Mock the Skeleton component
54
12
  vi.mock('@0xsequence/design-system', async (importOriginal) => {
@@ -73,12 +31,6 @@ describe('CurrencyOptionsSelect', () => {
73
31
  cleanup();
74
32
  // Reset all mocks
75
33
  vi.clearAllMocks();
76
-
77
- // Setup default mock values
78
- vi.mocked(useCurrencies as any).mockReturnValue({
79
- data: [MOCK_USDC, MOCK_WETH],
80
- isLoading: false,
81
- });
82
34
  });
83
35
 
84
36
  it('should render loading skeleton when currencies are loading', () => {
@@ -100,7 +52,7 @@ describe('CurrencyOptionsSelect', () => {
100
52
  />,
101
53
  );
102
54
 
103
- expect(selectedCurrency$.get()).toEqual(MOCK_USDC);
55
+ expect(selectedCurrency$.get()).toEqual(mockCurrencies[0]);
104
56
  });
105
57
 
106
58
  it('should set second currency as default when secondCurrencyAsDefault is true', () => {
@@ -113,11 +65,14 @@ describe('CurrencyOptionsSelect', () => {
113
65
  />,
114
66
  );
115
67
 
116
- expect(selectedCurrency$.get()).toEqual(MOCK_WETH);
68
+ expect(selectedCurrency$.get()).toEqual(mockCurrencies[1]);
117
69
  });
118
70
 
119
71
  it('should update selected currency when user selects a different option', () => {
120
- const selectedCurrency$ = observable(MOCK_USDC);
72
+ const firstCurrency = mockCurrencies[0];
73
+ const secondCurrency = mockCurrencies[1];
74
+
75
+ const selectedCurrency$ = observable(firstCurrency);
121
76
 
122
77
  const { getByRole, getByText } = render(
123
78
  <CurrencyOptionsSelect
@@ -131,14 +86,14 @@ describe('CurrencyOptionsSelect', () => {
131
86
  fireEvent.click(selectButton);
132
87
 
133
88
  // Find and click the WETH option in the dropdown
134
- const wethOption = getByText('WETH');
89
+ const wethOption = getByText(secondCurrency.symbol);
135
90
  fireEvent.click(wethOption);
136
91
 
137
- expect(selectedCurrency$.get()).toEqual(MOCK_WETH);
92
+ expect(selectedCurrency$.get()).toEqual(secondCurrency);
138
93
  });
139
94
 
140
95
  it('should maintain selected currency when currencies reload', () => {
141
- const selectedCurrency$ = observable(MOCK_WETH);
96
+ const selectedCurrency$ = observable(mockCurrencies[0]);
142
97
 
143
98
  const { rerender } = render(
144
99
  <CurrencyOptionsSelect
@@ -154,6 +109,6 @@ describe('CurrencyOptionsSelect', () => {
154
109
  />,
155
110
  );
156
111
 
157
- expect(selectedCurrency$.get()).toEqual(MOCK_WETH);
112
+ expect(selectedCurrency$.get()).toEqual(mockCurrencies[0]);
158
113
  });
159
114
  });
@@ -10,6 +10,7 @@ import {
10
10
  cleanup,
11
11
  fireEvent,
12
12
  } from '../../../../../../_internal/test-utils';
13
+ import { CurrencyStatus } from '../../../../../../_internal';
13
14
 
14
15
  vi.mock('../hooks/usePriceInput', () => ({
15
16
  usePriceInput: vi.fn(({ onPriceChange }) => ({
@@ -43,6 +44,7 @@ const MOCK_CURRENCY: Currency = {
43
44
  name: 'USD Coin',
44
45
  decimals: 6,
45
46
  imageUrl: 'https://example.com/usdc.png',
47
+ status: CurrencyStatus.active,
46
48
  exchangeRate: 1,
47
49
  defaultChainCurrency: false,
48
50
  nativeCurrency: false,
@@ -10,7 +10,6 @@ import { transactionStatusModal$ } from '../store';
10
10
  import type { ShowTransactionStatusModalArgs } from '../index';
11
11
  import { TransactionType } from '../../../../../../_internal/types';
12
12
  import { beforeEach, describe, expect, it, vi, afterEach } from 'vitest';
13
- import { WaitForTransactionReceiptTimeoutError } from 'viem';
14
13
 
15
14
  const mockTransactionArgs: ShowTransactionStatusModalArgs = {
16
15
  hash: '0x123' as `0x${string}`,
@@ -20,14 +19,12 @@ const mockTransactionArgs: ShowTransactionStatusModalArgs = {
20
19
  type: TransactionType.BUY,
21
20
  };
22
21
 
23
- const mockPublicClient = {
24
- waitForTransactionReceipt: vi.fn(),
25
- };
26
-
27
- vi.mock('../../../../../../../utils/get-public-rpc-client', () => ({
28
- getPublicRpcClient: vi.fn().mockImplementation(() => mockPublicClient),
22
+ vi.mock('../hooks/useTransactionStatus', () => ({
23
+ default: vi.fn(),
29
24
  }));
30
25
 
26
+ import useTransactionStatus from '../hooks/useTransactionStatus';
27
+
31
28
  describe('TransactionStatusModal', () => {
32
29
  afterEach(() => {
33
30
  cleanup();
@@ -36,6 +33,7 @@ describe('TransactionStatusModal', () => {
36
33
  beforeEach(() => {
37
34
  transactionStatusModal$.close();
38
35
  vi.clearAllMocks();
36
+ (useTransactionStatus as any).mockReturnValue('PENDING');
39
37
  });
40
38
 
41
39
  it('should not render when closed', () => {
@@ -46,6 +44,7 @@ describe('TransactionStatusModal', () => {
46
44
  });
47
45
 
48
46
  it('should show processing state when transaction is processing', async () => {
47
+ (useTransactionStatus as any).mockReturnValue('PENDING');
49
48
  transactionStatusModal$.open(mockTransactionArgs);
50
49
  render(<TransactionStatusModal />);
51
50
 
@@ -61,9 +60,7 @@ describe('TransactionStatusModal', () => {
61
60
  });
62
61
 
63
62
  it('should show success state when transaction is successful', async () => {
64
- mockPublicClient.waitForTransactionReceipt.mockResolvedValue({
65
- status: 'success',
66
- });
63
+ (useTransactionStatus as any).mockReturnValue('SUCCESS');
67
64
  transactionStatusModal$.open(mockTransactionArgs);
68
65
  render(<TransactionStatusModal />);
69
66
 
@@ -79,9 +76,7 @@ describe('TransactionStatusModal', () => {
79
76
  });
80
77
 
81
78
  it('should show failed state when transaction fails', async () => {
82
- mockPublicClient.waitForTransactionReceipt.mockRejectedValue(
83
- new Error('Transaction failed'),
84
- );
79
+ (useTransactionStatus as any).mockReturnValue('FAILED');
85
80
  transactionStatusModal$.open(mockTransactionArgs);
86
81
  render(<TransactionStatusModal />);
87
82
 
@@ -95,9 +90,7 @@ describe('TransactionStatusModal', () => {
95
90
  });
96
91
 
97
92
  it('should show timeout state when transaction times out', async () => {
98
- mockPublicClient.waitForTransactionReceipt.mockRejectedValue(
99
- new WaitForTransactionReceiptTimeoutError({ hash: '0x123' }),
100
- );
93
+ (useTransactionStatus as any).mockReturnValue('TIMEOUT');
101
94
  transactionStatusModal$.open(mockTransactionArgs);
102
95
  render(<TransactionStatusModal />);
103
96
 
@@ -112,8 +105,11 @@ describe('TransactionStatusModal', () => {
112
105
 
113
106
  it('should call onSuccess callback when transaction succeeds', async () => {
114
107
  const onSuccess = vi.fn();
115
- mockPublicClient.waitForTransactionReceipt.mockResolvedValue({
116
- status: 'success',
108
+ (useTransactionStatus as any).mockImplementation(() => {
109
+ onSuccess({
110
+ hash: mockTransactionArgs.hash,
111
+ });
112
+ return 'SUCCESS';
117
113
  });
118
114
 
119
115
  transactionStatusModal$.open({
@@ -132,7 +128,10 @@ describe('TransactionStatusModal', () => {
132
128
  it('should call onError callback when transaction fails', async () => {
133
129
  const onError = vi.fn();
134
130
  const error = new Error('Transaction failed');
135
- mockPublicClient.waitForTransactionReceipt.mockRejectedValue(error);
131
+ (useTransactionStatus as any).mockImplementation(() => {
132
+ onError(error);
133
+ return 'FAILED';
134
+ });
136
135
 
137
136
  transactionStatusModal$.open({
138
137
  ...mockTransactionArgs,
@@ -7,6 +7,7 @@ import {
7
7
  getTransactionStatusModalSpinnerTitle,
8
8
  } from '../util/getTitle';
9
9
  import type { TransactionStatus } from '../store';
10
+ import { CurrencyStatus } from '../../../../../../_internal';
10
11
 
11
12
  describe('Transaction Status Modal Utils', () => {
12
13
  describe('getFormattedType', () => {
@@ -111,6 +112,7 @@ describe('Transaction Status Modal Utils', () => {
111
112
  nativeCurrency: true,
112
113
  createdAt: '2024-01-21T00:00:00Z',
113
114
  updatedAt: '2024-01-21T00:00:00Z',
115
+ status: CurrencyStatus.active,
114
116
  },
115
117
  },
116
118
  }),