@0xsequence/marketplace-sdk 0.8.3 → 0.8.5
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.
- package/CHANGELOG.md +16 -1
- package/dist/{chunk-25CAMYCG.js → chunk-BB2PTJHI.js} +22 -20
- package/dist/chunk-BB2PTJHI.js.map +1 -0
- package/dist/{chunk-5ATGT5S4.js → chunk-EZFCQZHU.js} +14 -6
- package/dist/chunk-EZFCQZHU.js.map +1 -0
- package/dist/{chunk-DFI52A2E.js → chunk-KCLMSSPS.js} +364 -242
- package/dist/chunk-KCLMSSPS.js.map +1 -0
- package/dist/{chunk-XUNDLCEH.js → chunk-LDZZUYG7.js} +2 -2
- package/dist/{chunk-QTV77W42.js → chunk-SFSFIGHM.js} +45 -35
- package/dist/chunk-SFSFIGHM.js.map +1 -0
- package/dist/{chunk-FSJKN4YN.js → chunk-ZSCZLHKX.js} +194 -2
- package/dist/chunk-ZSCZLHKX.js.map +1 -0
- package/dist/{chunk-FH4TZRDV.js → chunk-ZVTG6US2.js} +2 -2
- package/dist/index.css +4 -4
- package/dist/index.css.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{lowestListing-DUZ_nYml.d.ts → lowestListing-W7P4EkC3.d.ts} +34 -11
- package/dist/react/_internal/databeat/index.js +5 -5
- package/dist/react/_internal/index.d.ts +1 -1
- package/dist/react/_internal/index.js +3 -1
- package/dist/react/_internal/wagmi/index.d.ts +3 -2
- package/dist/react/_internal/wagmi/index.js +3 -1
- package/dist/react/hooks/index.d.ts +8 -5
- package/dist/react/hooks/index.js +6 -4
- package/dist/react/hooks/options/index.js +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +9 -7
- package/dist/react/queries/index.d.ts +1 -1
- package/dist/react/queries/index.js +6 -2
- package/dist/react/ssr/index.js +1 -1
- package/dist/react/ui/components/collectible-card/index.d.ts +3 -2
- package/dist/react/ui/components/collectible-card/index.js +7 -7
- package/dist/react/ui/icons/index.js +1 -1
- package/dist/react/ui/index.js +7 -7
- package/dist/react/ui/modals/_internal/components/actionModal/index.js +5 -5
- package/dist/types/index.js +1 -1
- package/dist/utils/index.js +1 -1
- package/package.json +19 -19
- package/src/react/_internal/api/__mocks__/marketplace.msw.ts +35 -21
- package/src/react/_internal/wagmi/__tests__/create-config.test.ts +1 -11
- package/src/react/_internal/wagmi/get-connectors.ts +27 -24
- package/src/react/hooks/__tests__/useCancelTransactionSteps.test.tsx +4 -9
- package/src/react/hooks/__tests__/useGenerateCancelTransaction.test.tsx +5 -4
- package/src/react/hooks/__tests__/useGenerateListingTransaction.test.tsx +14 -10
- package/src/react/hooks/__tests__/useGenerateOfferTransaction.test.tsx +115 -65
- package/src/react/hooks/__tests__/useGenerateSellTransaction.test.tsx +10 -7
- package/src/react/hooks/__tests__/useInventory.test.tsx +294 -0
- package/src/react/hooks/index.ts +1 -0
- package/src/react/hooks/useAutoSelectFeeOption.tsx +10 -3
- package/src/react/hooks/useCancelOrder.tsx +1 -0
- package/src/react/hooks/useCancelTransactionSteps.tsx +18 -4
- package/src/react/hooks/useGenerateOfferTransaction.tsx +11 -1
- package/src/react/hooks/useInventory.tsx +15 -0
- package/src/react/hooks/util/optimisticCancelUpdates.ts +115 -0
- package/src/react/queries/index.ts +1 -0
- package/src/react/queries/inventory.ts +303 -0
- package/src/react/queries/listBalances.ts +1 -8
- package/src/react/queries/listCollectibles.ts +12 -3
- package/src/react/ui/components/_internals/action-button/__tests__/ActionButtonBody.test.tsx +27 -94
- package/src/react/ui/components/_internals/action-button/__tests__/NonOwnerActions.test.tsx +59 -0
- package/src/react/ui/components/_internals/action-button/__tests__/OwnerActions.test.tsx +73 -0
- package/src/react/ui/components/_internals/action-button/__tests__/useActionButtonLogic.test.tsx +77 -0
- package/src/react/ui/components/_internals/action-button/components/ActionButtonBody.tsx +3 -2
- package/src/react/ui/components/_internals/action-button/hooks/useActionButtonLogic.ts +4 -3
- package/src/react/ui/components/collectible-card/CollectibleAsset.tsx +1 -0
- package/src/react/ui/components/collectible-card/CollectibleCard.tsx +18 -12
- package/src/react/ui/components/collectible-card/__tests__/CollectibleAsset.test.tsx +200 -0
- package/src/react/ui/components/collectible-card/__tests__/CollectibleCard.test.tsx +92 -123
- package/src/react/ui/components/collectible-card/__tests__/Footer.test.tsx +136 -0
- package/src/react/ui/modals/BuyModal/__tests__/Modal.test.tsx +2 -8
- package/src/react/ui/modals/CreateListingModal/__tests__/Modal.test.tsx +74 -104
- package/src/react/ui/modals/MakeOfferModal/__tests__/Modal.test.tsx +108 -78
- package/src/react/ui/modals/SellModal/__tests__/Modal.test.tsx +72 -135
- package/src/react/ui/modals/_internal/components/actionModal/ActionModal.test.tsx +286 -0
- package/src/react/ui/modals/_internal/components/actionModal/ActionModal.tsx +16 -4
- package/src/react/ui/modals/_internal/components/currencyOptionsSelect/__tests__/index.test.tsx +35 -132
- package/src/react/ui/modals/_internal/components/floorPriceText/__tests__/FloorPriceText.test.tsx +199 -0
- package/src/react/ui/modals/_internal/components/priceInput/__tests__/PriceInput.test.tsx +55 -0
- package/src/react/ui/modals/_internal/components/priceInput/index.tsx +1 -1
- package/src/react/ui/modals/_internal/components/selectWaasFeeOptions/__tests__/ActionButtons.test.tsx +72 -0
- package/src/react/ui/modals/_internal/components/selectWaasFeeOptions/__tests__/BalanceIndicator.test.tsx +50 -0
- package/src/react/ui/modals/_internal/components/selectWaasFeeOptions/__tests__/SelectWaasFeeOptions.test.tsx +193 -0
- package/src/react/ui/modals/_internal/components/switchChainModal/index.tsx +2 -2
- package/test/const.ts +24 -0
- package/test/test-utils.tsx +85 -47
- package/.changeset/flat-parks-clean.md +0 -8
- package/.changeset/red-buckets-deny.md +0 -6
- package/.changeset/seven-doors-taste.md +0 -5
- package/dist/chunk-25CAMYCG.js.map +0 -1
- package/dist/chunk-5ATGT5S4.js.map +0 -1
- package/dist/chunk-DFI52A2E.js.map +0 -1
- package/dist/chunk-FSJKN4YN.js.map +0 -1
- package/dist/chunk-QTV77W42.js.map +0 -1
- package/src/react/ui/components/_internals/action-button/__tests__/ActionButton.test.tsx +0 -107
- package/src/react/ui/modals/_internal/components/priceInput/__tests__/index.test.tsx +0 -164
- /package/dist/{chunk-XUNDLCEH.js.map → chunk-LDZZUYG7.js.map} +0 -0
- /package/dist/{chunk-FH4TZRDV.js.map → chunk-ZVTG6US2.js.map} +0 -0
|
@@ -1,166 +1,103 @@
|
|
|
1
|
-
import { cleanup,
|
|
1
|
+
import { cleanup, render, renderHook, screen, waitFor } from '@test';
|
|
2
|
+
import { TEST_COLLECTIBLE } from '@test/const';
|
|
3
|
+
import { createMockWallet } from '@test/mocks/wallet';
|
|
2
4
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import {
|
|
5
|
+
import { useSellModal } from '..';
|
|
6
|
+
import { StepType, WalletKind } from '../../../../_internal';
|
|
7
|
+
import { createMockStep } from '../../../../_internal/api/__mocks__/marketplace.msw';
|
|
8
|
+
import { mockOrder } from '../../../../_internal/api/__mocks__/marketplace.msw';
|
|
9
|
+
import * as walletModule from '../../../../_internal/wallet/useWallet';
|
|
4
10
|
import { SellModal } from '../Modal';
|
|
5
|
-
import
|
|
11
|
+
import * as useGetTokenApprovalDataModule from '../hooks/useGetTokenApproval';
|
|
6
12
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import { mockMarketplaceEndpoint } from '../../../../_internal/api/__mocks__/marketplace.msw';
|
|
12
|
-
import { useSell } from '../hooks/useSell';
|
|
13
|
-
|
|
14
|
-
// Test data
|
|
15
|
-
const mockOrder = {
|
|
16
|
-
orderId: '1',
|
|
17
|
-
priceAmount: '1000000000000000000',
|
|
18
|
-
priceCurrencyAddress: '0x0',
|
|
19
|
-
quantityRemaining: '1',
|
|
20
|
-
createdAt: new Date().toISOString(),
|
|
21
|
-
marketplace: MarketplaceKind.sequence_marketplace_v2,
|
|
22
|
-
} as Order;
|
|
23
|
-
|
|
24
|
-
const mockModalProps = {
|
|
25
|
-
collectionAddress: '0x123',
|
|
26
|
-
chainId: 1,
|
|
27
|
-
tokenId: '1',
|
|
13
|
+
const defaultArgs = {
|
|
14
|
+
collectionAddress: TEST_COLLECTIBLE.collectionAddress,
|
|
15
|
+
chainId: TEST_COLLECTIBLE.chainId,
|
|
16
|
+
tokenId: TEST_COLLECTIBLE.collectibleId,
|
|
28
17
|
order: mockOrder,
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// TODO: remove when there is mocks for more endpoints
|
|
32
|
-
vi.mock(import('../../../../hooks'), async (importOriginal) => {
|
|
33
|
-
const mod = await importOriginal();
|
|
34
|
-
return {
|
|
35
|
-
...mod,
|
|
36
|
-
useCollection: vi.fn().mockImplementation(mod.useCollection),
|
|
37
|
-
useCurrency: vi.fn().mockImplementation(mod.useCurrency),
|
|
38
|
-
};
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
vi.mock('@0xsequence/kit', () => ({
|
|
42
|
-
useWaasFeeOptions: vi.fn().mockReturnValue([]),
|
|
43
|
-
}));
|
|
44
|
-
|
|
45
|
-
beforeEach(() => {
|
|
46
|
-
cleanup();
|
|
47
|
-
vi.clearAllMocks();
|
|
48
|
-
vi.mock('../hooks/useSell', () => ({
|
|
49
|
-
useSell: vi.fn().mockReturnValue({
|
|
50
|
-
isLoading: false,
|
|
51
|
-
executeApproval: vi.fn(),
|
|
52
|
-
sell: vi.fn(),
|
|
53
|
-
}),
|
|
54
|
-
}));
|
|
55
|
-
});
|
|
18
|
+
};
|
|
56
19
|
|
|
57
|
-
describe
|
|
58
|
-
|
|
59
|
-
render(<SellModal />);
|
|
60
|
-
expect(screen.queryByText('You have an offer')).toBeNull();
|
|
61
|
-
});
|
|
20
|
+
describe('MakeOfferModal', () => {
|
|
21
|
+
const mockWallet = createMockWallet();
|
|
62
22
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
);
|
|
70
|
-
sellModal$.open(mockModalProps);
|
|
71
|
-
render(<SellModal />);
|
|
72
|
-
const errorModal = await screen.findByTestId('error-modal');
|
|
73
|
-
expect(errorModal).toBeVisible();
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
cleanup();
|
|
25
|
+
// Reset all mocks
|
|
26
|
+
vi.clearAllMocks();
|
|
27
|
+
vi.resetAllMocks();
|
|
28
|
+
vi.restoreAllMocks();
|
|
74
29
|
});
|
|
75
30
|
|
|
76
|
-
it
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
31
|
+
it('should show main button if there is no approval step', async () => {
|
|
32
|
+
// Mock sequence wallet
|
|
33
|
+
const sequenceWallet = {
|
|
34
|
+
...mockWallet,
|
|
35
|
+
walletKind: WalletKind.sequence,
|
|
36
|
+
};
|
|
37
|
+
vi.spyOn(walletModule, 'useWallet').mockReturnValue({
|
|
38
|
+
wallet: sequenceWallet,
|
|
80
39
|
isLoading: false,
|
|
81
40
|
isError: false,
|
|
82
41
|
});
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
42
|
+
vi.spyOn(
|
|
43
|
+
useGetTokenApprovalDataModule,
|
|
44
|
+
'useGetTokenApprovalData',
|
|
45
|
+
).mockReturnValue({
|
|
46
|
+
data: {
|
|
47
|
+
step: null,
|
|
48
|
+
},
|
|
87
49
|
isLoading: false,
|
|
88
|
-
|
|
50
|
+
isSuccess: true,
|
|
89
51
|
});
|
|
90
52
|
|
|
91
|
-
|
|
53
|
+
// Render the modal
|
|
54
|
+
const { result } = renderHook(() => useSellModal());
|
|
55
|
+
result.current.show(defaultArgs);
|
|
56
|
+
|
|
92
57
|
render(<SellModal />);
|
|
93
|
-
|
|
94
|
-
|
|
58
|
+
|
|
59
|
+
// Wait for the component to update
|
|
60
|
+
await waitFor(() => {
|
|
61
|
+
// The Approve TOKEN button should not exist
|
|
62
|
+
expect(screen.queryByText('Approve TOKEN')).toBeNull();
|
|
63
|
+
|
|
64
|
+
// The Accept button should exist
|
|
65
|
+
expect(screen.getByRole('button', { name: 'Accept' })).toBeDefined();
|
|
66
|
+
});
|
|
95
67
|
});
|
|
96
|
-
});
|
|
97
68
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
},
|
|
69
|
+
it('(non-sequence wallets) should show approve token button if there is an approval step, disable main button', async () => {
|
|
70
|
+
const nonSequenceWallet = {
|
|
71
|
+
...mockWallet,
|
|
72
|
+
walletKind: 'unknown' as WalletKind,
|
|
73
|
+
};
|
|
74
|
+
vi.spyOn(walletModule, 'useWallet').mockReturnValue({
|
|
75
|
+
wallet: nonSequenceWallet,
|
|
106
76
|
isLoading: false,
|
|
107
77
|
isError: false,
|
|
108
78
|
});
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
79
|
+
vi.spyOn(
|
|
80
|
+
useGetTokenApprovalDataModule,
|
|
81
|
+
'useGetTokenApprovalData',
|
|
82
|
+
).mockReturnValue({
|
|
112
83
|
data: {
|
|
113
|
-
|
|
114
|
-
imageUrl: 'test-url',
|
|
84
|
+
step: createMockStep(StepType.tokenApproval),
|
|
115
85
|
},
|
|
116
86
|
isLoading: false,
|
|
117
|
-
|
|
87
|
+
isSuccess: true,
|
|
118
88
|
});
|
|
119
89
|
|
|
120
|
-
|
|
90
|
+
// Render the modal
|
|
91
|
+
const { result } = renderHook(() => useSellModal());
|
|
92
|
+
result.current.show(defaultArgs);
|
|
121
93
|
|
|
122
|
-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
123
|
-
(useSell as any).mockReturnValue({
|
|
124
|
-
isLoading: false,
|
|
125
|
-
executeApproval: mockExecuteApproval,
|
|
126
|
-
sell: vi.fn(),
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
sellModal$.open({
|
|
130
|
-
...mockModalProps,
|
|
131
|
-
order: {
|
|
132
|
-
...mockOrder,
|
|
133
|
-
quantityRemaining: '1',
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
sellModal$.steps.approval.exist.set(true);
|
|
137
|
-
sellModal$.steps.approval.isExecuting.set(false);
|
|
138
|
-
sellModal$.steps.transaction.isExecuting.set(false);
|
|
139
94
|
render(<SellModal />);
|
|
140
95
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
fireEvent.click(approveButton);
|
|
144
|
-
expect(mockExecuteApproval).toHaveBeenCalled();
|
|
145
|
-
});
|
|
146
|
-
});
|
|
96
|
+
await waitFor(() => {
|
|
97
|
+
expect(screen.getByText('Approve TOKEN')).toBeDefined();
|
|
147
98
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
(useSell as any).mockReturnValue({
|
|
152
|
-
isLoading: false,
|
|
153
|
-
executeApproval: vi.fn(),
|
|
154
|
-
sell: mockSell,
|
|
99
|
+
expect(screen.getByRole('button', { name: 'Accept' })).toBeDefined();
|
|
100
|
+
expect(screen.getByRole('button', { name: 'Accept' })).toBeDisabled();
|
|
101
|
+
});
|
|
155
102
|
});
|
|
156
|
-
|
|
157
|
-
sellModal$.open(mockModalProps);
|
|
158
|
-
sellModal$.steps.approval.exist.set(false);
|
|
159
|
-
sellModal$.steps.approval.isExecuting.set(false);
|
|
160
|
-
sellModal$.steps.transaction.isExecuting.set(false);
|
|
161
|
-
render(<SellModal />);
|
|
162
|
-
|
|
163
|
-
const acceptButton = screen.getByText('Accept');
|
|
164
|
-
fireEvent.click(acceptButton);
|
|
165
|
-
expect(mockSell).toHaveBeenCalled();
|
|
166
103
|
});
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { fireEvent, render, screen, waitFor } from '@test';
|
|
2
|
+
import { type Address, custom, zeroAddress } from 'viem';
|
|
3
|
+
import { mainnet, polygon } from 'viem/chains';
|
|
4
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { WalletKind } from '../../../../../_internal';
|
|
6
|
+
import * as walletModule from '../../../../../_internal/wallet/useWallet';
|
|
7
|
+
import { ActionModal } from './ActionModal';
|
|
8
|
+
|
|
9
|
+
const mockShowSwitchChainModal = vi.fn();
|
|
10
|
+
vi.mock('../switchChainModal', () => ({
|
|
11
|
+
useSwitchChainModal: () => ({
|
|
12
|
+
show: mockShowSwitchChainModal,
|
|
13
|
+
close: vi.fn(),
|
|
14
|
+
isSwitching$: { get: () => false },
|
|
15
|
+
}),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
describe('ActionModal', () => {
|
|
19
|
+
const mockOnClose = vi.fn();
|
|
20
|
+
const mockOnClick = vi.fn();
|
|
21
|
+
|
|
22
|
+
const defaultProps = {
|
|
23
|
+
isOpen: true,
|
|
24
|
+
onClose: mockOnClose,
|
|
25
|
+
title: 'Test Modal',
|
|
26
|
+
children: <div>Modal Content</div>,
|
|
27
|
+
ctas: [
|
|
28
|
+
{
|
|
29
|
+
label: 'Test Button',
|
|
30
|
+
onClick: mockOnClick,
|
|
31
|
+
testid: 'test-button',
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
chainId: polygon.id,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
vi.clearAllMocks();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('Loading states', () => {
|
|
42
|
+
it('should show a loading spinner when modalLoading prop is true', async () => {
|
|
43
|
+
render(<ActionModal {...defaultProps} modalLoading={true} />);
|
|
44
|
+
|
|
45
|
+
expect(screen.getByTestId('spinner')).toBeInTheDocument();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should show a loading spinner when isLoading from useWallet is true', async () => {
|
|
49
|
+
vi.spyOn(walletModule, 'useWallet').mockReturnValue({
|
|
50
|
+
wallet: null,
|
|
51
|
+
isLoading: true,
|
|
52
|
+
isError: false,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
render(<ActionModal {...defaultProps} />);
|
|
56
|
+
|
|
57
|
+
expect(screen.getByTestId('spinner')).toBeInTheDocument();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should show error message when useWallet returns an error', () => {
|
|
61
|
+
vi.spyOn(walletModule, 'useWallet').mockReturnValue({
|
|
62
|
+
wallet: null,
|
|
63
|
+
isLoading: false,
|
|
64
|
+
isError: true,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
render(<ActionModal {...defaultProps} />);
|
|
68
|
+
|
|
69
|
+
expect(screen.getByTestId('error-loading-text')).toBeInTheDocument();
|
|
70
|
+
expect(screen.getByText('Error loading modal')).toBeInTheDocument();
|
|
71
|
+
expect(screen.queryByText('Modal Content')).not.toBeInTheDocument();
|
|
72
|
+
expect(screen.queryByTestId('test-button')).not.toBeInTheDocument();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should show modal content if loading states is false and no error', async () => {
|
|
76
|
+
vi.spyOn(walletModule, 'useWallet').mockReturnValue({
|
|
77
|
+
wallet: null,
|
|
78
|
+
isLoading: false,
|
|
79
|
+
isError: false,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
render(<ActionModal {...defaultProps} modalLoading={false} />);
|
|
83
|
+
|
|
84
|
+
expect(screen.getByText('Modal Content')).toBeInTheDocument();
|
|
85
|
+
expect(screen.getByTestId('test-button')).toBeInTheDocument();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('Chain switching', () => {
|
|
90
|
+
it('should automatically switch chain for Sequence WaaS wallets', async () => {
|
|
91
|
+
const switchChainMock = vi.fn();
|
|
92
|
+
|
|
93
|
+
vi.spyOn(walletModule, 'useWallet').mockReturnValue({
|
|
94
|
+
wallet: {
|
|
95
|
+
address: () => Promise.resolve(zeroAddress as Address),
|
|
96
|
+
getChainId: vi.fn().mockResolvedValue(mainnet.id),
|
|
97
|
+
switchChain: switchChainMock,
|
|
98
|
+
transport: custom({ request: vi.fn() }),
|
|
99
|
+
walletKind: WalletKind.sequence,
|
|
100
|
+
isWaaS: true,
|
|
101
|
+
handleConfirmTransactionStep: vi.fn(),
|
|
102
|
+
handleSendTransactionStep: vi.fn(),
|
|
103
|
+
handleSignMessageStep: vi.fn(),
|
|
104
|
+
hasTokenApproval: vi.fn(),
|
|
105
|
+
},
|
|
106
|
+
isLoading: false,
|
|
107
|
+
isError: false,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
render(<ActionModal {...defaultProps} />);
|
|
111
|
+
|
|
112
|
+
expect(switchChainMock).toHaveBeenCalledWith(polygon.id);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should show switch chain modal when CTA is clicked with chain mismatch', async () => {
|
|
116
|
+
mockShowSwitchChainModal.mockClear();
|
|
117
|
+
|
|
118
|
+
vi.spyOn(walletModule, 'useWallet').mockReturnValue({
|
|
119
|
+
wallet: {
|
|
120
|
+
address: () => Promise.resolve(zeroAddress as Address),
|
|
121
|
+
getChainId: vi.fn().mockResolvedValue(mainnet.id), // different from defaultProps.chainId
|
|
122
|
+
switchChain: vi.fn(),
|
|
123
|
+
transport: custom({ request: vi.fn() }),
|
|
124
|
+
walletKind: WalletKind.sequence,
|
|
125
|
+
isWaaS: false,
|
|
126
|
+
handleConfirmTransactionStep: vi.fn(),
|
|
127
|
+
handleSendTransactionStep: vi.fn(),
|
|
128
|
+
handleSignMessageStep: vi.fn(),
|
|
129
|
+
hasTokenApproval: vi.fn(),
|
|
130
|
+
},
|
|
131
|
+
isLoading: false,
|
|
132
|
+
isError: false,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
render(<ActionModal {...defaultProps} />);
|
|
136
|
+
|
|
137
|
+
const button = screen.getByTestId('test-button');
|
|
138
|
+
fireEvent.click(button);
|
|
139
|
+
|
|
140
|
+
await waitFor(() => {
|
|
141
|
+
expect(mockShowSwitchChainModal).toHaveBeenCalledWith({
|
|
142
|
+
chainIdToSwitchTo: polygon.id,
|
|
143
|
+
onSuccess: expect.any(Function),
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should directly execute callback when chain already matches', async () => {
|
|
149
|
+
mockOnClick.mockClear();
|
|
150
|
+
|
|
151
|
+
vi.spyOn(walletModule, 'useWallet').mockReturnValue({
|
|
152
|
+
wallet: {
|
|
153
|
+
address: () => Promise.resolve(zeroAddress as Address),
|
|
154
|
+
getChainId: vi.fn().mockResolvedValue(polygon.id), // Same as defaultProps.chainId
|
|
155
|
+
switchChain: vi.fn(),
|
|
156
|
+
transport: custom({ request: vi.fn() }),
|
|
157
|
+
walletKind: WalletKind.sequence,
|
|
158
|
+
isWaaS: false,
|
|
159
|
+
handleConfirmTransactionStep: vi.fn(),
|
|
160
|
+
handleSendTransactionStep: vi.fn(),
|
|
161
|
+
handleSignMessageStep: vi.fn(),
|
|
162
|
+
hasTokenApproval: vi.fn(),
|
|
163
|
+
},
|
|
164
|
+
isLoading: false,
|
|
165
|
+
isError: false,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
render(<ActionModal {...defaultProps} />);
|
|
169
|
+
|
|
170
|
+
const button = screen.getByTestId('test-button');
|
|
171
|
+
fireEvent.click(button);
|
|
172
|
+
|
|
173
|
+
await waitFor(() => {
|
|
174
|
+
expect(mockOnClick).toHaveBeenCalled();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('CTA buttons', () => {
|
|
180
|
+
it('should call onClick when CTA button is clicked', async () => {
|
|
181
|
+
const onClick = vi.fn();
|
|
182
|
+
render(
|
|
183
|
+
<ActionModal
|
|
184
|
+
{...defaultProps}
|
|
185
|
+
ctas={[{ label: 'Click Me', onClick, testid: 'cta-button' }]}
|
|
186
|
+
/>,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
const button = screen.getByTestId('cta-button');
|
|
190
|
+
fireEvent.click(button);
|
|
191
|
+
|
|
192
|
+
await waitFor(() => {
|
|
193
|
+
expect(onClick).toHaveBeenCalled();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should disable the button when disabled prop is true', () => {
|
|
198
|
+
render(
|
|
199
|
+
<ActionModal
|
|
200
|
+
{...defaultProps}
|
|
201
|
+
ctas={[
|
|
202
|
+
{
|
|
203
|
+
label: 'Disabled Button',
|
|
204
|
+
onClick: mockOnClick,
|
|
205
|
+
disabled: true,
|
|
206
|
+
testid: 'disabled-button',
|
|
207
|
+
},
|
|
208
|
+
]}
|
|
209
|
+
/>,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const button = screen.getByTestId('disabled-button');
|
|
213
|
+
expect(button).toBeDisabled();
|
|
214
|
+
|
|
215
|
+
fireEvent.click(button);
|
|
216
|
+
expect(mockOnClick).not.toHaveBeenCalled();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should show spinner when pending prop is true', () => {
|
|
220
|
+
render(
|
|
221
|
+
<ActionModal
|
|
222
|
+
{...defaultProps}
|
|
223
|
+
ctas={[
|
|
224
|
+
{
|
|
225
|
+
label: 'Loading Button',
|
|
226
|
+
onClick: mockOnClick,
|
|
227
|
+
pending: true,
|
|
228
|
+
testid: 'pending-button',
|
|
229
|
+
},
|
|
230
|
+
]}
|
|
231
|
+
/>,
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
expect(screen.getByTestId('pending-button')).toBeInTheDocument();
|
|
235
|
+
expect(screen.getByTestId('pending-button-spinner')).toBeInTheDocument(); // wrapper of spinner has data-testid of {testid}-spinner
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should not render hidden buttons', () => {
|
|
239
|
+
render(
|
|
240
|
+
<ActionModal
|
|
241
|
+
{...defaultProps}
|
|
242
|
+
ctas={[
|
|
243
|
+
{
|
|
244
|
+
label: 'Visible Button',
|
|
245
|
+
onClick: vi.fn(),
|
|
246
|
+
testid: 'visible-button',
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
label: 'Hidden Button',
|
|
250
|
+
onClick: vi.fn(),
|
|
251
|
+
hidden: true,
|
|
252
|
+
testid: 'hidden-button',
|
|
253
|
+
},
|
|
254
|
+
]}
|
|
255
|
+
/>,
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
expect(screen.getByTestId('visible-button')).toBeInTheDocument();
|
|
259
|
+
expect(screen.queryByTestId('hidden-button')).not.toBeInTheDocument();
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('should render multiple CTA buttons', () => {
|
|
263
|
+
render(
|
|
264
|
+
<ActionModal
|
|
265
|
+
{...defaultProps}
|
|
266
|
+
ctas={[
|
|
267
|
+
{
|
|
268
|
+
label: 'Primary CTA',
|
|
269
|
+
onClick: vi.fn(),
|
|
270
|
+
testid: 'primary-cta',
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
label: 'Secondary CTA',
|
|
274
|
+
onClick: vi.fn(),
|
|
275
|
+
variant: 'secondary',
|
|
276
|
+
testid: 'secondary-cta',
|
|
277
|
+
},
|
|
278
|
+
]}
|
|
279
|
+
/>,
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
expect(screen.getByTestId('primary-cta')).toBeInTheDocument();
|
|
283
|
+
expect(screen.getByTestId('secondary-cta')).toBeInTheDocument();
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
});
|
|
@@ -52,7 +52,7 @@ export const ActionModal = observer(
|
|
|
52
52
|
const chainMismatch = walletChainId !== Number(chainId);
|
|
53
53
|
if (chainMismatch) {
|
|
54
54
|
showSwitchChainModal({
|
|
55
|
-
chainIdToSwitchTo:
|
|
55
|
+
chainIdToSwitchTo: chainId,
|
|
56
56
|
onSuccess,
|
|
57
57
|
});
|
|
58
58
|
} else {
|
|
@@ -84,13 +84,21 @@ export const ActionModal = observer(
|
|
|
84
84
|
{modalLoading || isLoading || isError ? (
|
|
85
85
|
<div
|
|
86
86
|
className={`flex ${spinnerContainerClassname} w-full items-center justify-center`}
|
|
87
|
+
data-testid="error-loading-wrapper"
|
|
87
88
|
>
|
|
88
89
|
{isError && (
|
|
89
|
-
<Text
|
|
90
|
+
<Text
|
|
91
|
+
data-testid="error-loading-text"
|
|
92
|
+
className="text-center font-body text-error100 text-small"
|
|
93
|
+
>
|
|
90
94
|
Error loading modal
|
|
91
95
|
</Text>
|
|
92
96
|
)}
|
|
93
|
-
{isLoading
|
|
97
|
+
{(isLoading || modalLoading) && (
|
|
98
|
+
<div data-testid="spinner">
|
|
99
|
+
<Spinner size="lg" />
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
94
102
|
</div>
|
|
95
103
|
) : (
|
|
96
104
|
children
|
|
@@ -118,7 +126,11 @@ export const ActionModal = observer(
|
|
|
118
126
|
data-testid={cta.testid}
|
|
119
127
|
label={
|
|
120
128
|
<div className="flex items-center justify-center gap-2">
|
|
121
|
-
{cta.pending &&
|
|
129
|
+
{cta.pending && (
|
|
130
|
+
<div data-testid={`${cta.testid}-spinner`}>
|
|
131
|
+
<Spinner size="sm" />
|
|
132
|
+
</div>
|
|
133
|
+
)}
|
|
122
134
|
|
|
123
135
|
{cta.label}
|
|
124
136
|
</div>
|