@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
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { TEST_CURRENCY } from '@test/const';
|
|
2
|
+
import { render, screen } from '@test/test-utils';
|
|
3
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { ContractType } from '../../../../_internal';
|
|
5
|
+
import { mockOrder } from '../../../../_internal/api/__mocks__/marketplace.msw';
|
|
6
|
+
import { Footer } from '../Footer';
|
|
7
|
+
|
|
8
|
+
const defaultProps = {
|
|
9
|
+
name: 'Test',
|
|
10
|
+
type: ContractType.ERC721,
|
|
11
|
+
decimals: 18,
|
|
12
|
+
onOfferClick: vi.fn(),
|
|
13
|
+
highestOffer: mockOrder,
|
|
14
|
+
lowestOffer: mockOrder,
|
|
15
|
+
lowestListingPriceAmount: '100',
|
|
16
|
+
lowestListingCurrency: TEST_CURRENCY,
|
|
17
|
+
balance: '100',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
describe('Footer', () => {
|
|
21
|
+
it('Renders with basic props (name, type) correctly', () => {
|
|
22
|
+
render(<Footer {...defaultProps} />);
|
|
23
|
+
|
|
24
|
+
expect(screen.getByText('Test')).toBeInTheDocument();
|
|
25
|
+
expect(screen.getByText('0.0001 TEST')).toBeInTheDocument();
|
|
26
|
+
|
|
27
|
+
const notificationBell = screen.getByRole('button', {
|
|
28
|
+
name: 'Notification Bell',
|
|
29
|
+
});
|
|
30
|
+
expect(notificationBell).toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('Truncates long names appropriately based on presence of offers', () => {
|
|
34
|
+
const longName =
|
|
35
|
+
'This is a very long collectible name that needs truncation';
|
|
36
|
+
|
|
37
|
+
// Test truncation with offer present (truncates to 13 chars + "...")
|
|
38
|
+
render(
|
|
39
|
+
<Footer {...defaultProps} name={longName} highestOffer={mockOrder} />,
|
|
40
|
+
);
|
|
41
|
+
expect(screen.getByText('This is a ver...')).toBeInTheDocument();
|
|
42
|
+
|
|
43
|
+
// Test truncation without offer present (truncates to 17 chars + "...")
|
|
44
|
+
render(
|
|
45
|
+
<Footer {...defaultProps} name={longName} highestOffer={undefined} />,
|
|
46
|
+
);
|
|
47
|
+
expect(screen.getByText('This is a very lo...')).toBeInTheDocument();
|
|
48
|
+
|
|
49
|
+
// Test short name with offer (no truncation needed)
|
|
50
|
+
render(
|
|
51
|
+
<Footer {...defaultProps} name="Short Name" highestOffer={mockOrder} />,
|
|
52
|
+
);
|
|
53
|
+
expect(screen.getByText('Short Name')).toBeInTheDocument();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('Formats prices correctly for different scenarios (normal, overflow, underflow)', () => {
|
|
57
|
+
// Test normal price formatting
|
|
58
|
+
render(
|
|
59
|
+
<Footer
|
|
60
|
+
{...defaultProps}
|
|
61
|
+
lowestListingPriceAmount="1000000000000000000" // 1 TOKEN in wei
|
|
62
|
+
/>,
|
|
63
|
+
);
|
|
64
|
+
expect(screen.getByText('1 TEST')).toBeInTheDocument();
|
|
65
|
+
|
|
66
|
+
// Test small number formatting (shows more decimals)
|
|
67
|
+
render(
|
|
68
|
+
<Footer
|
|
69
|
+
{...defaultProps}
|
|
70
|
+
lowestListingPriceAmount="5000000000000000" // 0.005 TOKEN in wei
|
|
71
|
+
/>,
|
|
72
|
+
);
|
|
73
|
+
expect(screen.getByText('0.005 TEST')).toBeInTheDocument();
|
|
74
|
+
|
|
75
|
+
// Test underflow price (< 0.0001)
|
|
76
|
+
render(
|
|
77
|
+
<Footer
|
|
78
|
+
{...defaultProps}
|
|
79
|
+
lowestListingPriceAmount="10000000000000" // 0.00001 TOKEN in wei
|
|
80
|
+
/>,
|
|
81
|
+
);
|
|
82
|
+
// Should display minimum price with chevron icon
|
|
83
|
+
expect(screen.getByText('0.0001 TEST')).toBeInTheDocument();
|
|
84
|
+
|
|
85
|
+
// Test overflow price (> 100,000,000)
|
|
86
|
+
render(
|
|
87
|
+
<Footer
|
|
88
|
+
{...defaultProps}
|
|
89
|
+
lowestListingPriceAmount="100000000000000000000000000" // 100M+ TOKEN in wei
|
|
90
|
+
/>,
|
|
91
|
+
);
|
|
92
|
+
// Should display maximum price with chevron icon
|
|
93
|
+
expect(screen.getByText('100,000,000 TEST')).toBeInTheDocument();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("Displays 'Not listed yet' when no listing price is provided", () => {
|
|
97
|
+
// Create props without listing price information
|
|
98
|
+
const propsWithoutListing = {
|
|
99
|
+
...defaultProps,
|
|
100
|
+
lowestListingPriceAmount: undefined,
|
|
101
|
+
lowestListingCurrency: undefined,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
render(<Footer {...propsWithoutListing} />);
|
|
105
|
+
|
|
106
|
+
// Verify "Not listed yet" text is displayed
|
|
107
|
+
expect(screen.getByText('Not listed yet')).toBeInTheDocument();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('Shows proper token balance information for ERC721 vs ERC1155 tokens', () => {
|
|
111
|
+
// Test ERC721 token display
|
|
112
|
+
render(<Footer {...defaultProps} type={ContractType.ERC721} />);
|
|
113
|
+
expect(screen.getByText('ERC-721')).toBeInTheDocument();
|
|
114
|
+
|
|
115
|
+
// Test ERC1155 token display without balance
|
|
116
|
+
render(
|
|
117
|
+
<Footer
|
|
118
|
+
{...defaultProps}
|
|
119
|
+
type={ContractType.ERC1155}
|
|
120
|
+
balance={undefined}
|
|
121
|
+
/>,
|
|
122
|
+
);
|
|
123
|
+
expect(screen.getByText('ERC-1155')).toBeInTheDocument();
|
|
124
|
+
|
|
125
|
+
// Test ERC1155 token display with balance
|
|
126
|
+
render(
|
|
127
|
+
<Footer
|
|
128
|
+
{...defaultProps}
|
|
129
|
+
type={ContractType.ERC1155}
|
|
130
|
+
balance="1000000000000000000"
|
|
131
|
+
decimals={18}
|
|
132
|
+
/>,
|
|
133
|
+
);
|
|
134
|
+
expect(screen.getByText('Owned: 1')).toBeInTheDocument();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cleanup, render, screen } from '@test';
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
3
3
|
|
|
4
4
|
import { server } from '@test';
|
|
@@ -46,9 +46,7 @@ describe('BuyModal', () => {
|
|
|
46
46
|
// },
|
|
47
47
|
// });
|
|
48
48
|
// render(
|
|
49
|
-
// <WebSdkWrapper>
|
|
50
49
|
// <BuyModal />
|
|
51
|
-
// </WebSdkWrapper>
|
|
52
50
|
// );
|
|
53
51
|
// // Should show error modal
|
|
54
52
|
// await waitFor(() => {
|
|
@@ -98,11 +96,7 @@ describe('BuyModal', () => {
|
|
|
98
96
|
},
|
|
99
97
|
});
|
|
100
98
|
|
|
101
|
-
render(
|
|
102
|
-
<WebSdkWrapper>
|
|
103
|
-
<BuyModal />
|
|
104
|
-
</WebSdkWrapper>,
|
|
105
|
-
);
|
|
99
|
+
render(<BuyModal />);
|
|
106
100
|
|
|
107
101
|
// Should show loading modal
|
|
108
102
|
expect(screen.getByText('Loading Sequence Pay')).toBeInTheDocument();
|
|
@@ -1,137 +1,107 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { cleanup, render, renderHook, screen, waitFor } from '@test';
|
|
2
|
+
import { TEST_COLLECTIBLE } from '@test/const';
|
|
3
|
+
import { createMockWallet } from '@test/mocks/wallet';
|
|
4
4
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
-
import
|
|
5
|
+
import { useCreateListingModal } from '..';
|
|
6
|
+
import { StepType, WalletKind } from '../../../../_internal';
|
|
7
|
+
import { createMockStep } from '../../../../_internal/api/__mocks__/marketplace.msw';
|
|
8
|
+
import * as walletModule from '../../../../_internal/wallet/useWallet';
|
|
6
9
|
import { CreateListingModal } from '../Modal';
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
vi.mock(import('../../../../hooks'), async (importOriginal) => {
|
|
10
|
-
const actual = await importOriginal();
|
|
11
|
-
return {
|
|
12
|
-
...actual,
|
|
13
|
-
useCollectible: vi.fn(actual.useCollectible),
|
|
14
|
-
useCollection: vi.fn(actual.useCollection),
|
|
15
|
-
useCurrencies: vi.fn(actual.useCurrencies),
|
|
16
|
-
useMarketplaceConfig: vi.fn(actual.useMarketplaceConfig),
|
|
17
|
-
useLowestListing: vi.fn(actual.useLowestListing),
|
|
18
|
-
};
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
vi.mock('@0xsequence/kit', () => ({
|
|
22
|
-
useWaasFeeOptions: vi.fn(),
|
|
23
|
-
}));
|
|
10
|
+
import * as useGetTokenApprovalDataModule from '../hooks/useGetTokenApproval';
|
|
24
11
|
|
|
25
12
|
const defaultArgs = {
|
|
26
|
-
collectionAddress:
|
|
27
|
-
chainId:
|
|
28
|
-
collectibleId:
|
|
13
|
+
collectionAddress: TEST_COLLECTIBLE.collectionAddress,
|
|
14
|
+
chainId: TEST_COLLECTIBLE.chainId,
|
|
15
|
+
collectibleId: TEST_COLLECTIBLE.collectibleId,
|
|
29
16
|
};
|
|
30
17
|
|
|
31
|
-
describe
|
|
18
|
+
describe('MakeOfferModal', () => {
|
|
19
|
+
const mockWallet = createMockWallet();
|
|
20
|
+
|
|
32
21
|
beforeEach(() => {
|
|
33
22
|
cleanup();
|
|
34
23
|
// Reset all mocks
|
|
35
24
|
vi.clearAllMocks();
|
|
36
25
|
vi.resetAllMocks();
|
|
37
|
-
vi.
|
|
26
|
+
vi.restoreAllMocks();
|
|
38
27
|
});
|
|
39
28
|
|
|
40
|
-
it('should
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
data: undefined,
|
|
29
|
+
it('should show main button if there is no approval step', async () => {
|
|
30
|
+
// Mock sequence wallet
|
|
31
|
+
const sequenceWallet = {
|
|
32
|
+
...mockWallet,
|
|
33
|
+
walletKind: WalletKind.sequence,
|
|
34
|
+
};
|
|
35
|
+
vi.spyOn(walletModule, 'useWallet').mockReturnValue({
|
|
36
|
+
wallet: sequenceWallet,
|
|
49
37
|
isLoading: false,
|
|
50
|
-
isError:
|
|
38
|
+
isError: false,
|
|
51
39
|
});
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
it('should render main form when data is loaded', async () => {
|
|
63
|
-
createListingModal$.open(defaultArgs);
|
|
64
|
-
render(<CreateListingModal />);
|
|
65
|
-
|
|
66
|
-
// Check for the collection name in the token preview
|
|
67
|
-
expect(await screen.findByText('Mock Collection')).toBeInTheDocument();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should reset store values when modal is closed and reopened', () => {
|
|
71
|
-
// Open modal first time
|
|
72
|
-
createListingModal$.open(defaultArgs);
|
|
73
|
-
|
|
74
|
-
// Set some values in the store
|
|
75
|
-
createListingModal$.listingPrice.amountRaw.set('1000000000000000000');
|
|
76
|
-
createListingModal$.quantity.set('5');
|
|
77
|
-
|
|
78
|
-
// Close modal
|
|
79
|
-
createListingModal$.close();
|
|
80
|
-
|
|
81
|
-
// Verify store is reset
|
|
82
|
-
expect(createListingModal$.listingPrice.amountRaw.get()).toBe('0');
|
|
83
|
-
expect(createListingModal$.quantity.get()).toBe('1');
|
|
84
|
-
|
|
85
|
-
// Reopen modal
|
|
86
|
-
createListingModal$.open({
|
|
87
|
-
collectionAddress: '0x456',
|
|
88
|
-
chainId: 1,
|
|
89
|
-
collectibleId: '2',
|
|
40
|
+
vi.spyOn(
|
|
41
|
+
useGetTokenApprovalDataModule,
|
|
42
|
+
'useGetTokenApprovalData',
|
|
43
|
+
).mockReturnValue({
|
|
44
|
+
data: {
|
|
45
|
+
step: null,
|
|
46
|
+
},
|
|
47
|
+
isLoading: false,
|
|
48
|
+
isSuccess: true,
|
|
90
49
|
});
|
|
91
50
|
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should update state based on price input', async () => {
|
|
98
|
-
createListingModal$.open(defaultArgs);
|
|
51
|
+
// Render the modal
|
|
52
|
+
const { result } = renderHook(() => useCreateListingModal());
|
|
53
|
+
result.current.show(defaultArgs);
|
|
99
54
|
|
|
100
55
|
render(<CreateListingModal />);
|
|
101
56
|
|
|
102
|
-
//
|
|
103
|
-
expect(createListingModal$.listingPrice.amountRaw.get()).toBe('0');
|
|
104
|
-
|
|
105
|
-
// Find and interact with price input using id
|
|
106
|
-
const priceInput = await screen.findByRole('textbox', {
|
|
107
|
-
name: /enter price/i,
|
|
108
|
-
});
|
|
109
|
-
expect(priceInput).toBeInTheDocument();
|
|
110
|
-
|
|
111
|
-
fireEvent.change(priceInput, { target: { value: '1.5' } });
|
|
112
|
-
|
|
113
|
-
// Wait for the state to update and verify it's not 0 anymore
|
|
57
|
+
// Wait for the component to update
|
|
114
58
|
await waitFor(() => {
|
|
115
|
-
|
|
59
|
+
// The Approve TOKEN button should not exist
|
|
60
|
+
expect(screen.queryByText('Approve TOKEN')).toBeNull();
|
|
61
|
+
|
|
62
|
+
// The List item for sale button should exist
|
|
63
|
+
expect(
|
|
64
|
+
screen.getByRole('button', { name: 'List item for sale' }),
|
|
65
|
+
).toBeDefined();
|
|
116
66
|
});
|
|
117
67
|
});
|
|
118
68
|
|
|
119
|
-
it('should show
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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,
|
|
123
76
|
isLoading: false,
|
|
124
77
|
isError: false,
|
|
125
78
|
});
|
|
79
|
+
vi.spyOn(
|
|
80
|
+
useGetTokenApprovalDataModule,
|
|
81
|
+
'useGetTokenApprovalData',
|
|
82
|
+
).mockReturnValue({
|
|
83
|
+
data: {
|
|
84
|
+
step: createMockStep(StepType.tokenApproval),
|
|
85
|
+
},
|
|
86
|
+
isLoading: false,
|
|
87
|
+
isSuccess: true,
|
|
88
|
+
});
|
|
126
89
|
|
|
127
|
-
|
|
90
|
+
// Render the modal
|
|
91
|
+
const { result } = renderHook(() => useCreateListingModal());
|
|
92
|
+
result.current.show(defaultArgs);
|
|
128
93
|
|
|
129
94
|
render(<CreateListingModal />);
|
|
130
95
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
96
|
+
await waitFor(() => {
|
|
97
|
+
expect(screen.getByText('Approve TOKEN')).toBeDefined();
|
|
98
|
+
|
|
99
|
+
expect(
|
|
100
|
+
screen.getByRole('button', { name: 'List item for sale' }),
|
|
101
|
+
).toBeDefined();
|
|
102
|
+
expect(
|
|
103
|
+
screen.getByRole('button', { name: 'List item for sale' }),
|
|
104
|
+
).toBeDisabled();
|
|
105
|
+
});
|
|
136
106
|
});
|
|
137
107
|
});
|
|
@@ -1,119 +1,149 @@
|
|
|
1
|
-
import { cleanup,
|
|
2
|
-
import {
|
|
1
|
+
import { cleanup, render, renderHook, waitFor } from '@test';
|
|
2
|
+
import { TEST_COLLECTIBLE } from '@test/const';
|
|
3
|
+
import { createMockWallet } from '@test/mocks/wallet';
|
|
3
4
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { useMakeOfferModal } from '..';
|
|
6
|
+
import { CurrencyStatus, WalletKind } from '../../../../_internal';
|
|
7
|
+
import * as walletModule from '../../../../_internal/wallet/useWallet';
|
|
4
8
|
import { MakeOfferModal } from '../Modal';
|
|
5
9
|
import { makeOfferModal$ } from '../store';
|
|
6
10
|
|
|
7
|
-
// TODO: This should be moved to a shared test file
|
|
8
|
-
vi.mock(import('../../../../hooks'), async (importOriginal) => {
|
|
9
|
-
const actual = await importOriginal();
|
|
10
|
-
return {
|
|
11
|
-
...actual,
|
|
12
|
-
useCollectible: vi.fn(actual.useCollectible),
|
|
13
|
-
useCollection: vi.fn(actual.useCollection),
|
|
14
|
-
useCurrencies: vi.fn(actual.useCurrencies),
|
|
15
|
-
useMarketplaceConfig: vi.fn(actual.useMarketplaceConfig),
|
|
16
|
-
useLowestListing: vi.fn(actual.useLowestListing),
|
|
17
|
-
};
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
vi.mock('@0xsequence/kit', () => ({
|
|
21
|
-
useWaasFeeOptions: vi.fn().mockReturnValue([]),
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
11
|
const defaultArgs = {
|
|
25
|
-
collectionAddress:
|
|
12
|
+
collectionAddress: TEST_COLLECTIBLE.collectionAddress,
|
|
13
|
+
chainId: TEST_COLLECTIBLE.chainId,
|
|
14
|
+
collectibleId: TEST_COLLECTIBLE.collectibleId,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Mock currency object with all required properties
|
|
18
|
+
const mockCurrency = {
|
|
26
19
|
chainId: 1,
|
|
27
|
-
|
|
20
|
+
contractAddress: '0x123',
|
|
21
|
+
status: CurrencyStatus.active,
|
|
22
|
+
name: 'Test Token',
|
|
23
|
+
symbol: 'TEST',
|
|
24
|
+
decimals: 18,
|
|
25
|
+
imageUrl: 'https://example.com/test.png',
|
|
26
|
+
exchangeRate: 1,
|
|
27
|
+
defaultChainCurrency: false,
|
|
28
|
+
nativeCurrency: false,
|
|
29
|
+
createdAt: new Date().toISOString(),
|
|
30
|
+
updatedAt: new Date().toISOString(),
|
|
28
31
|
};
|
|
29
32
|
|
|
30
|
-
describe
|
|
33
|
+
describe('MakeOfferModal', () => {
|
|
34
|
+
const mockWallet = createMockWallet();
|
|
35
|
+
|
|
31
36
|
beforeEach(() => {
|
|
32
37
|
cleanup();
|
|
33
38
|
// Reset all mocks
|
|
34
39
|
vi.clearAllMocks();
|
|
35
40
|
vi.resetAllMocks();
|
|
41
|
+
vi.restoreAllMocks();
|
|
36
42
|
});
|
|
37
43
|
|
|
38
|
-
it('should
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
render(<MakeOfferModal />);
|
|
47
|
-
const loadingModal = screen.getByTestId('loading-modal');
|
|
48
|
-
expect(loadingModal).toBeVisible();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it.skip('should render error state', async () => {
|
|
52
|
-
// @ts-expect-error - TODO: Add a common mock object with the correct shape
|
|
53
|
-
vi.mocked(hooks.useCollection).mockReturnValue({
|
|
54
|
-
data: undefined,
|
|
44
|
+
it('should show main button if there is no approval step', async () => {
|
|
45
|
+
// Mock sequence wallet
|
|
46
|
+
const sequenceWallet = {
|
|
47
|
+
...mockWallet,
|
|
48
|
+
walletKind: WalletKind.sequence,
|
|
49
|
+
};
|
|
50
|
+
vi.spyOn(walletModule, 'useWallet').mockReturnValue({
|
|
51
|
+
wallet: sequenceWallet,
|
|
55
52
|
isLoading: false,
|
|
56
|
-
isError:
|
|
53
|
+
isError: false,
|
|
57
54
|
});
|
|
58
55
|
|
|
59
|
-
|
|
56
|
+
// Render the modal
|
|
57
|
+
const { result } = renderHook(() => useMakeOfferModal());
|
|
58
|
+
result.current.show(defaultArgs);
|
|
60
59
|
|
|
61
|
-
render(<MakeOfferModal />);
|
|
62
|
-
const errorModal = await screen.findByTestId('error-modal');
|
|
63
|
-
expect(errorModal).toBeVisible();
|
|
64
|
-
});
|
|
60
|
+
const { queryByText } = render(<MakeOfferModal />);
|
|
65
61
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
62
|
+
// Wait for the component to update
|
|
63
|
+
await waitFor(() => {
|
|
64
|
+
// The Approve TOKEN button should not exist
|
|
65
|
+
const approveButton = queryByText('Approve TOKEN');
|
|
66
|
+
expect(approveButton).toBeNull();
|
|
70
67
|
|
|
71
|
-
|
|
68
|
+
const makeOfferButton = queryByText('Make offer');
|
|
69
|
+
expect(makeOfferButton).toBeDefined();
|
|
70
|
+
});
|
|
72
71
|
});
|
|
73
72
|
|
|
74
|
-
it
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
it('(non-sequence wallets) should show approve token button if there is an approval step, disable main button', async () => {
|
|
74
|
+
const nonSequenceWallet = {
|
|
75
|
+
...mockWallet,
|
|
76
|
+
walletKind: 'unknown' as WalletKind,
|
|
77
|
+
};
|
|
78
|
+
vi.spyOn(walletModule, 'useWallet').mockReturnValue({
|
|
79
|
+
wallet: nonSequenceWallet,
|
|
80
|
+
isLoading: false,
|
|
81
|
+
isError: false,
|
|
82
|
+
});
|
|
81
83
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
+
// Render the modal
|
|
85
|
+
const { result } = renderHook(() => useMakeOfferModal());
|
|
86
|
+
result.current.show(defaultArgs);
|
|
84
87
|
|
|
85
|
-
|
|
86
|
-
expect(makeOfferModal$.offerPrice.amountRaw.get()).toBe('0');
|
|
87
|
-
expect(makeOfferModal$.expiry.get()).toBeDefined();
|
|
88
|
+
const { getByText } = render(<MakeOfferModal />);
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
await waitFor(() => {
|
|
91
|
+
// find the Approve TOKEN button
|
|
92
|
+
const approveButton = getByText('Approve TOKEN');
|
|
93
|
+
expect(approveButton).toBeDefined();
|
|
91
94
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
// main button is disabled when approval step exists
|
|
96
|
+
const makeOfferButton = getByText('Make offer');
|
|
97
|
+
expect(makeOfferButton.closest('button')).toHaveAttribute('disabled');
|
|
98
|
+
});
|
|
95
99
|
});
|
|
96
100
|
|
|
97
|
-
it
|
|
101
|
+
it('should show/hide CTAs based on approval step existence in store', async () => {
|
|
102
|
+
// test case 1: approval.exist = true
|
|
98
103
|
makeOfferModal$.open(defaultArgs);
|
|
104
|
+
makeOfferModal$.offerPrice.set({
|
|
105
|
+
amountRaw: '1000000000000000000',
|
|
106
|
+
currency: mockCurrency,
|
|
107
|
+
});
|
|
108
|
+
makeOfferModal$.offerPriceChanged.set(true);
|
|
109
|
+
makeOfferModal$.steps.approval.exist.set(true);
|
|
99
110
|
|
|
100
|
-
render(<MakeOfferModal />);
|
|
111
|
+
const { getByText, unmount } = render(<MakeOfferModal />);
|
|
101
112
|
|
|
102
|
-
|
|
103
|
-
|
|
113
|
+
await waitFor(() => {
|
|
114
|
+
// Approve TOKEN button should be visible
|
|
115
|
+
const approveButton = getByText('Approve TOKEN');
|
|
116
|
+
expect(approveButton).toBeDefined();
|
|
117
|
+
|
|
118
|
+
// Make offer button should be disabled
|
|
119
|
+
const makeOfferButton = getByText('Make offer');
|
|
120
|
+
expect(makeOfferButton.closest('button')).toHaveAttribute('disabled');
|
|
121
|
+
});
|
|
104
122
|
|
|
105
|
-
|
|
123
|
+
unmount();
|
|
124
|
+
cleanup();
|
|
106
125
|
|
|
107
|
-
|
|
108
|
-
|
|
126
|
+
// test case 2: approval.exist = false
|
|
127
|
+
makeOfferModal$.open(defaultArgs);
|
|
128
|
+
makeOfferModal$.offerPrice.set({
|
|
129
|
+
amountRaw: '1000000000000000000',
|
|
130
|
+
currency: mockCurrency,
|
|
109
131
|
});
|
|
110
|
-
|
|
132
|
+
makeOfferModal$.offerPriceChanged.set(true);
|
|
133
|
+
makeOfferModal$.steps.approval.exist.set(false);
|
|
111
134
|
|
|
112
|
-
|
|
135
|
+
const { queryByText, getByText: getByText2 } = render(<MakeOfferModal />);
|
|
113
136
|
|
|
114
|
-
// Wait for the state to update and verify it's not 0 anymore
|
|
115
137
|
await waitFor(() => {
|
|
116
|
-
|
|
138
|
+
// Approve TOKEN button should not exist or be hidden
|
|
139
|
+
const approveButton = queryByText('Approve TOKEN');
|
|
140
|
+
expect(approveButton).toBeNull();
|
|
141
|
+
|
|
142
|
+
// Make offer button should be enabled
|
|
143
|
+
const makeOfferButton = getByText2('Make offer');
|
|
144
|
+
expect(makeOfferButton.closest('button')).not.toHaveAttribute('disabled');
|
|
117
145
|
});
|
|
146
|
+
|
|
147
|
+
makeOfferModal$.close();
|
|
118
148
|
});
|
|
119
149
|
});
|