@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
package/src/react/ui/modals/_internal/components/currencyOptionsSelect/__tests__/index.test.tsx
CHANGED
|
@@ -1,151 +1,54 @@
|
|
|
1
1
|
import { observable } from '@legendapp/state';
|
|
2
2
|
import type { UseQueryResult } from '@tanstack/react-query';
|
|
3
3
|
import { render } from '@test';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { TEST_CURRENCIES, TEST_CURRENCY } from '@test/const';
|
|
5
|
+
import { screen } from '@testing-library/react';
|
|
6
|
+
import { zeroAddress } from 'viem';
|
|
7
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
6
8
|
import CurrencyOptionsSelect from '..';
|
|
7
9
|
import type { Currency } from '../../../../../../_internal';
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
import * as hooks from '../../../../../../hooks';
|
|
11
|
+
|
|
12
|
+
const defaultProps = {
|
|
13
|
+
collectionAddress: zeroAddress,
|
|
14
|
+
chainId: 1,
|
|
15
|
+
selectedCurrency$: observable<Currency | null | undefined>(TEST_CURRENCY),
|
|
16
|
+
secondCurrencyAsDefault: false,
|
|
17
|
+
includeNativeCurrency: false,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
describe('CurrencyOptionsSelect', () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
vi.restoreAllMocks();
|
|
18
23
|
});
|
|
19
24
|
|
|
20
25
|
it('should render loading skeleton when currencies are loading', () => {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
it('should set first currency as default when currencies load', async () => {
|
|
27
|
-
vi.mocked(useCurrencies).mockReturnValue({
|
|
28
|
-
data: mockCurrencies,
|
|
29
|
-
isLoading: false,
|
|
30
|
-
} as UseQueryResult<Currency[], Error>);
|
|
31
|
-
|
|
32
|
-
const props = createDefaultProps();
|
|
33
|
-
render(<CurrencyOptionsSelect {...props} />);
|
|
34
|
-
|
|
35
|
-
await waitFor(() => {
|
|
36
|
-
const selectedCurrency = props.selectedCurrency$.get();
|
|
37
|
-
expect(selectedCurrency).toBeDefined();
|
|
38
|
-
expect(selectedCurrency?.contractAddress).toBe(
|
|
39
|
-
mockCurrencies[0].contractAddress,
|
|
40
|
-
);
|
|
41
|
-
expect(selectedCurrency?.symbol).toBe(mockCurrencies[0].symbol);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
expect(screen.getByText(mockCurrencies[0].symbol)).toBeInTheDocument();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should set second currency as default when secondCurrencyAsDefault is true', async () => {
|
|
48
|
-
vi.mocked(useCurrencies).mockReturnValue({
|
|
49
|
-
data: mockCurrencies,
|
|
50
|
-
isLoading: false,
|
|
51
|
-
} as UseQueryResult<Currency[], Error>);
|
|
52
|
-
|
|
53
|
-
const props = createDefaultProps();
|
|
54
|
-
render(<CurrencyOptionsSelect {...props} secondCurrencyAsDefault={true} />);
|
|
55
|
-
|
|
56
|
-
await waitFor(() => {
|
|
57
|
-
const selectedCurrency = props.selectedCurrency$.get();
|
|
58
|
-
expect(selectedCurrency).toBeDefined();
|
|
59
|
-
expect(selectedCurrency?.contractAddress).toBe(
|
|
60
|
-
mockCurrencies[1].contractAddress,
|
|
61
|
-
);
|
|
62
|
-
expect(selectedCurrency?.symbol).toBe(mockCurrencies[1].symbol);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
expect(screen.getByText(mockCurrencies[1].symbol)).toBeInTheDocument();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should update selected currency when changed programmatically', async () => {
|
|
69
|
-
vi.mocked(useCurrencies).mockReturnValue({
|
|
70
|
-
data: mockCurrencies,
|
|
71
|
-
isLoading: false,
|
|
26
|
+
const useCurrenciesSpy = vi.spyOn(hooks, 'useCurrencies');
|
|
27
|
+
useCurrenciesSpy.mockReturnValue({
|
|
28
|
+
isLoading: true,
|
|
29
|
+
data: undefined,
|
|
30
|
+
error: null,
|
|
72
31
|
} as UseQueryResult<Currency[], Error>);
|
|
73
32
|
|
|
74
|
-
|
|
75
|
-
render(<CurrencyOptionsSelect {...props} />);
|
|
76
|
-
|
|
77
|
-
// Wait for initial currency to be set
|
|
78
|
-
await waitFor(() => {
|
|
79
|
-
expect(props.selectedCurrency$.get()).toBeDefined();
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// Programmatically change the selected currency
|
|
83
|
-
props.selectedCurrency$.set(mockCurrencies[1]);
|
|
33
|
+
render(<CurrencyOptionsSelect {...defaultProps} />);
|
|
84
34
|
|
|
85
|
-
|
|
86
|
-
expect(
|
|
87
|
-
|
|
88
|
-
);
|
|
89
|
-
expect(props.selectedCurrency$.get()?.symbol).toBe(
|
|
90
|
-
mockCurrencies[1].symbol,
|
|
91
|
-
);
|
|
35
|
+
const skeleton = document.querySelector('.h-7.w-20.rounded-2xl');
|
|
36
|
+
expect(skeleton).toBeInTheDocument();
|
|
37
|
+
expect(skeleton).toHaveClass('animate-skeleton');
|
|
92
38
|
});
|
|
93
39
|
|
|
94
|
-
it('should
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
// Initial load with currencies
|
|
98
|
-
useCurrenciesMock.mockReturnValue({
|
|
99
|
-
data: mockCurrencies,
|
|
40
|
+
it('should set first currency as default when currencies load', async () => {
|
|
41
|
+
const useCurrenciesSpy = vi.spyOn(hooks, 'useCurrencies');
|
|
42
|
+
useCurrenciesSpy.mockReturnValue({
|
|
100
43
|
isLoading: false,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const props = createDefaultProps();
|
|
104
|
-
render(<CurrencyOptionsSelect {...props} />);
|
|
105
|
-
|
|
106
|
-
// Wait for initial currency to be set
|
|
107
|
-
await waitFor(() => {
|
|
108
|
-
expect(props.selectedCurrency$.get()).toBeDefined();
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// Programmatically set the second currency
|
|
112
|
-
props.selectedCurrency$.set(mockCurrencies[1]);
|
|
113
|
-
|
|
114
|
-
// Verify second currency is selected
|
|
115
|
-
expect(props.selectedCurrency$.get()?.contractAddress).toBe(
|
|
116
|
-
mockCurrencies[1].contractAddress,
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
// Simulate reload by setting loading state
|
|
120
|
-
useCurrenciesMock.mockReturnValue({
|
|
121
|
-
data: undefined,
|
|
122
|
-
isLoading: true,
|
|
44
|
+
data: TEST_CURRENCIES,
|
|
123
45
|
error: null,
|
|
124
|
-
} as
|
|
125
|
-
|
|
126
|
-
// Verify the selected currency remains the same during loading
|
|
127
|
-
expect(props.selectedCurrency$.get()?.contractAddress).toBe(
|
|
128
|
-
mockCurrencies[1].contractAddress,
|
|
129
|
-
);
|
|
130
|
-
expect(props.selectedCurrency$.get()?.symbol).toBe(
|
|
131
|
-
mockCurrencies[1].symbol,
|
|
132
|
-
);
|
|
46
|
+
} as UseQueryResult<Currency[], Error>);
|
|
133
47
|
|
|
134
|
-
|
|
135
|
-
useCurrenciesMock.mockReturnValue({
|
|
136
|
-
data: mockCurrencies,
|
|
137
|
-
isLoading: false,
|
|
138
|
-
error: null,
|
|
139
|
-
} as unknown as UseQueryResult<Currency[], Error>);
|
|
48
|
+
render(<CurrencyOptionsSelect {...defaultProps} />);
|
|
140
49
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
mockCurrencies[1].contractAddress,
|
|
145
|
-
);
|
|
146
|
-
expect(props.selectedCurrency$.get()?.symbol).toBe(
|
|
147
|
-
mockCurrencies[1].symbol,
|
|
148
|
-
);
|
|
149
|
-
});
|
|
50
|
+
const trigger = screen.getByTestId('currency-select-trigger');
|
|
51
|
+
expect(trigger).toBeInTheDocument();
|
|
52
|
+
expect(trigger).toHaveTextContent(TEST_CURRENCY.symbol);
|
|
150
53
|
});
|
|
151
54
|
});
|
package/src/react/ui/modals/_internal/components/floorPriceText/__tests__/FloorPriceText.test.tsx
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { render } from '@test';
|
|
2
|
+
import type { Hex } from 'viem';
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import type { Currency } from '../../../../../../_internal/api/marketplace.gen';
|
|
5
|
+
import * as hooks from '../../../../../../hooks';
|
|
6
|
+
import FloorPriceText from '../index';
|
|
7
|
+
|
|
8
|
+
describe('FloorPriceText', () => {
|
|
9
|
+
const mockCurrency: Currency = {
|
|
10
|
+
contractAddress: '0x0000000000000000000000000000000000000000' as Hex,
|
|
11
|
+
symbol: 'ETH',
|
|
12
|
+
decimals: 18,
|
|
13
|
+
name: 'Ethereum',
|
|
14
|
+
chainId: 1,
|
|
15
|
+
status: 'VERIFIED' as Currency['status'],
|
|
16
|
+
imageUrl: '',
|
|
17
|
+
exchangeRate: 0,
|
|
18
|
+
defaultChainCurrency: false,
|
|
19
|
+
nativeCurrency: true,
|
|
20
|
+
createdAt: '',
|
|
21
|
+
updatedAt: '',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const mockProps = {
|
|
25
|
+
chainId: 1,
|
|
26
|
+
collectionAddress: '0x1234567890123456789012345678901234567890' as Hex,
|
|
27
|
+
tokenId: '1',
|
|
28
|
+
price: {
|
|
29
|
+
amountRaw: '1000000000000000000',
|
|
30
|
+
currency: mockCurrency,
|
|
31
|
+
},
|
|
32
|
+
onBuyNow: vi.fn(),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
vi.clearAllMocks();
|
|
37
|
+
vi.restoreAllMocks();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should render null when loading', () => {
|
|
41
|
+
const useLowestListingSpy = vi.spyOn(hooks, 'useLowestListing');
|
|
42
|
+
const useComparePricesSpy = vi.spyOn(hooks, 'useComparePrices');
|
|
43
|
+
|
|
44
|
+
const lowestListingMock = {
|
|
45
|
+
data: undefined,
|
|
46
|
+
isLoading: true,
|
|
47
|
+
status: 'pending',
|
|
48
|
+
fetchStatus: 'fetching',
|
|
49
|
+
} as unknown as ReturnType<typeof hooks.useLowestListing>;
|
|
50
|
+
|
|
51
|
+
const comparePricesMock = {
|
|
52
|
+
data: undefined,
|
|
53
|
+
isLoading: true,
|
|
54
|
+
status: 'pending',
|
|
55
|
+
fetchStatus: 'fetching',
|
|
56
|
+
} as unknown as ReturnType<typeof hooks.useComparePrices>;
|
|
57
|
+
|
|
58
|
+
useLowestListingSpy.mockReturnValue(lowestListingMock);
|
|
59
|
+
useComparePricesSpy.mockReturnValue(comparePricesMock);
|
|
60
|
+
|
|
61
|
+
const { container } = render(<FloorPriceText {...mockProps} />);
|
|
62
|
+
expect(container.firstChild).toBeNull();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should render null when floorPriceRaw is undefined even when not loading', () => {
|
|
66
|
+
const useLowestListingSpy = vi.spyOn(hooks, 'useLowestListing');
|
|
67
|
+
const useComparePricesSpy = vi.spyOn(hooks, 'useComparePrices');
|
|
68
|
+
|
|
69
|
+
const lowestListingMock = {
|
|
70
|
+
data: undefined,
|
|
71
|
+
isLoading: false,
|
|
72
|
+
status: 'success',
|
|
73
|
+
fetchStatus: 'idle',
|
|
74
|
+
} as ReturnType<typeof hooks.useLowestListing>;
|
|
75
|
+
|
|
76
|
+
const comparePricesMock = {
|
|
77
|
+
data: undefined,
|
|
78
|
+
isLoading: false,
|
|
79
|
+
status: 'success',
|
|
80
|
+
fetchStatus: 'idle',
|
|
81
|
+
} as unknown as ReturnType<typeof hooks.useComparePrices>;
|
|
82
|
+
|
|
83
|
+
useLowestListingSpy.mockReturnValue(lowestListingMock);
|
|
84
|
+
useComparePricesSpy.mockReturnValue(comparePricesMock);
|
|
85
|
+
|
|
86
|
+
const { container } = render(<FloorPriceText {...mockProps} />);
|
|
87
|
+
expect(container.firstChild).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should display "Same as floor price" and Buy Now button when price is same as floor price', () => {
|
|
91
|
+
const useLowestListingSpy = vi.spyOn(hooks, 'useLowestListing');
|
|
92
|
+
const useComparePricesSpy = vi.spyOn(hooks, 'useComparePrices');
|
|
93
|
+
|
|
94
|
+
const lowestListingMock = {
|
|
95
|
+
data: {
|
|
96
|
+
priceAmount: '1000000000000000000',
|
|
97
|
+
priceAmountFormatted: '1',
|
|
98
|
+
priceCurrencyAddress:
|
|
99
|
+
'0x0000000000000000000000000000000000000000' as Hex,
|
|
100
|
+
},
|
|
101
|
+
isLoading: false,
|
|
102
|
+
status: 'success',
|
|
103
|
+
fetchStatus: 'idle',
|
|
104
|
+
} as ReturnType<typeof hooks.useLowestListing>;
|
|
105
|
+
|
|
106
|
+
const comparePricesMock = {
|
|
107
|
+
data: {
|
|
108
|
+
status: 'same',
|
|
109
|
+
percentageDifferenceFormatted: '0',
|
|
110
|
+
},
|
|
111
|
+
isLoading: false,
|
|
112
|
+
status: 'success',
|
|
113
|
+
fetchStatus: 'idle',
|
|
114
|
+
} as ReturnType<typeof hooks.useComparePrices>;
|
|
115
|
+
|
|
116
|
+
useLowestListingSpy.mockReturnValue(lowestListingMock);
|
|
117
|
+
useComparePricesSpy.mockReturnValue(comparePricesMock);
|
|
118
|
+
|
|
119
|
+
const { getByText } = render(<FloorPriceText {...mockProps} />);
|
|
120
|
+
|
|
121
|
+
expect(getByText('Same as floor price')).toBeInTheDocument();
|
|
122
|
+
expect(getByText('Buy for 1 ETH')).toBeInTheDocument();
|
|
123
|
+
expect(mockProps.onBuyNow).not.toHaveBeenCalled();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should display percentage below floor price and not show Buy Now button when price is below floor price', () => {
|
|
127
|
+
const useLowestListingSpy = vi.spyOn(hooks, 'useLowestListing');
|
|
128
|
+
const useComparePricesSpy = vi.spyOn(hooks, 'useComparePrices');
|
|
129
|
+
|
|
130
|
+
const lowestListingMock = {
|
|
131
|
+
data: {
|
|
132
|
+
priceAmount: '1200000000000000000', // Floor price is higher
|
|
133
|
+
priceAmountFormatted: '1.2',
|
|
134
|
+
priceCurrencyAddress:
|
|
135
|
+
'0x0000000000000000000000000000000000000000' as Hex,
|
|
136
|
+
},
|
|
137
|
+
isLoading: false,
|
|
138
|
+
status: 'success',
|
|
139
|
+
fetchStatus: 'idle',
|
|
140
|
+
} as ReturnType<typeof hooks.useLowestListing>;
|
|
141
|
+
|
|
142
|
+
const comparePricesMock = {
|
|
143
|
+
data: {
|
|
144
|
+
status: 'below',
|
|
145
|
+
percentageDifferenceFormatted: '20',
|
|
146
|
+
},
|
|
147
|
+
isLoading: false,
|
|
148
|
+
status: 'success',
|
|
149
|
+
fetchStatus: 'idle',
|
|
150
|
+
} as ReturnType<typeof hooks.useComparePrices>;
|
|
151
|
+
|
|
152
|
+
useLowestListingSpy.mockReturnValue(lowestListingMock);
|
|
153
|
+
useComparePricesSpy.mockReturnValue(comparePricesMock);
|
|
154
|
+
|
|
155
|
+
const { getByText, queryByText } = render(
|
|
156
|
+
<FloorPriceText {...mockProps} />,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
expect(getByText('20% below floor price')).toBeInTheDocument();
|
|
160
|
+
expect(queryByText('Buy for 1.2 ETH')).not.toBeInTheDocument();
|
|
161
|
+
expect(mockProps.onBuyNow).not.toHaveBeenCalled();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should display percentage above floor price and show Buy Now button when price is above floor price', () => {
|
|
165
|
+
const useLowestListingSpy = vi.spyOn(hooks, 'useLowestListing');
|
|
166
|
+
const useComparePricesSpy = vi.spyOn(hooks, 'useComparePrices');
|
|
167
|
+
|
|
168
|
+
const lowestListingMock = {
|
|
169
|
+
data: {
|
|
170
|
+
priceAmount: '800000000000000000', // Floor price is lower
|
|
171
|
+
priceAmountFormatted: '0.8',
|
|
172
|
+
priceCurrencyAddress:
|
|
173
|
+
'0x0000000000000000000000000000000000000000' as Hex,
|
|
174
|
+
},
|
|
175
|
+
isLoading: false,
|
|
176
|
+
status: 'success',
|
|
177
|
+
fetchStatus: 'idle',
|
|
178
|
+
} as ReturnType<typeof hooks.useLowestListing>;
|
|
179
|
+
|
|
180
|
+
const comparePricesMock = {
|
|
181
|
+
data: {
|
|
182
|
+
status: 'above',
|
|
183
|
+
percentageDifferenceFormatted: '25',
|
|
184
|
+
},
|
|
185
|
+
isLoading: false,
|
|
186
|
+
status: 'success',
|
|
187
|
+
fetchStatus: 'idle',
|
|
188
|
+
} as ReturnType<typeof hooks.useComparePrices>;
|
|
189
|
+
|
|
190
|
+
useLowestListingSpy.mockReturnValue(lowestListingMock);
|
|
191
|
+
useComparePricesSpy.mockReturnValue(comparePricesMock);
|
|
192
|
+
|
|
193
|
+
const { getByText } = render(<FloorPriceText {...mockProps} />);
|
|
194
|
+
|
|
195
|
+
expect(getByText('25% above floor price')).toBeInTheDocument();
|
|
196
|
+
expect(getByText('Buy for 0.8 ETH')).toBeInTheDocument();
|
|
197
|
+
expect(mockProps.onBuyNow).not.toHaveBeenCalled();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { observable } from '@legendapp/state';
|
|
2
|
+
import { TEST_CURRENCY } from '@test/const';
|
|
3
|
+
import { fireEvent, render, screen } from '@test/test-utils';
|
|
4
|
+
import { zeroAddress } from 'viem';
|
|
5
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
6
|
+
import PriceInput from '..';
|
|
7
|
+
|
|
8
|
+
const defaultProps = {
|
|
9
|
+
chainId: 1,
|
|
10
|
+
collectionAddress: zeroAddress,
|
|
11
|
+
$price: observable({
|
|
12
|
+
amountRaw: '0',
|
|
13
|
+
currency: TEST_CURRENCY,
|
|
14
|
+
}),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
describe('PriceInput', () => {
|
|
18
|
+
it('should render with initial value of 0', () => {
|
|
19
|
+
render(<PriceInput {...defaultProps} />);
|
|
20
|
+
const input = screen.getByRole('textbox', { name: 'Enter price' });
|
|
21
|
+
expect(input).toHaveValue('0');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should update the price when the input changes', () => {
|
|
25
|
+
render(<PriceInput {...defaultProps} />);
|
|
26
|
+
const input = screen.getByRole('textbox', { name: 'Enter price' });
|
|
27
|
+
fireEvent.change(input, { target: { value: '100' } });
|
|
28
|
+
expect(input).toHaveValue('100');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should call onPriceChange when the input changes', () => {
|
|
32
|
+
const onPriceChange = vi.fn();
|
|
33
|
+
render(<PriceInput {...defaultProps} onPriceChange={onPriceChange} />);
|
|
34
|
+
const input = screen.getByRole('textbox', { name: 'Enter price' });
|
|
35
|
+
fireEvent.change(input, { target: { value: '100' } });
|
|
36
|
+
expect(onPriceChange).toHaveBeenCalledTimes(1);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should not call onPriceChange when the input is 0', () => {
|
|
40
|
+
const onPriceChange = vi.fn();
|
|
41
|
+
render(<PriceInput {...defaultProps} onPriceChange={onPriceChange} />);
|
|
42
|
+
const input = screen.getByRole('textbox', { name: 'Enter price' });
|
|
43
|
+
fireEvent.change(input, { target: { value: '0' } });
|
|
44
|
+
expect(onPriceChange).toHaveBeenCalledTimes(0);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should handle disabled prop', () => {
|
|
48
|
+
render(<PriceInput {...defaultProps} disabled />);
|
|
49
|
+
|
|
50
|
+
const priceInputWrapper = screen
|
|
51
|
+
.getByRole('textbox', { name: 'Enter price' })
|
|
52
|
+
.closest('.price-input');
|
|
53
|
+
expect(priceInputWrapper).toHaveClass('pointer-events-none opacity-50');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { fireEvent, render, screen } from '@test';
|
|
4
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import ActionButtons from '../_components/ActionButtons';
|
|
6
|
+
|
|
7
|
+
describe('ActionButtons', () => {
|
|
8
|
+
const mockOnCancel = vi.fn();
|
|
9
|
+
const mockOnConfirm = vi.fn();
|
|
10
|
+
|
|
11
|
+
const defaultProps = {
|
|
12
|
+
onCancel: mockOnCancel,
|
|
13
|
+
onConfirm: mockOnConfirm,
|
|
14
|
+
disabled: false,
|
|
15
|
+
loading: false,
|
|
16
|
+
confirmed: false,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
vi.clearAllMocks();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should render both buttons', () => {
|
|
24
|
+
render(<ActionButtons {...defaultProps} />);
|
|
25
|
+
|
|
26
|
+
expect(screen.getByText('Cancel')).toBeInTheDocument();
|
|
27
|
+
expect(screen.getByText('Continue with')).toBeInTheDocument();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should call onCancel when cancel button is clicked', () => {
|
|
31
|
+
render(<ActionButtons {...defaultProps} />);
|
|
32
|
+
|
|
33
|
+
const cancelButton = screen.getByText('Cancel');
|
|
34
|
+
fireEvent.click(cancelButton);
|
|
35
|
+
|
|
36
|
+
expect(mockOnCancel).toHaveBeenCalledTimes(1);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should call onConfirm when confirm button is clicked', () => {
|
|
40
|
+
render(<ActionButtons {...defaultProps} />);
|
|
41
|
+
|
|
42
|
+
const confirmButton = screen.getByText('Continue with');
|
|
43
|
+
fireEvent.click(confirmButton);
|
|
44
|
+
|
|
45
|
+
expect(mockOnConfirm).toHaveBeenCalledTimes(1);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should disable confirm button when disabled prop is true', () => {
|
|
49
|
+
render(<ActionButtons {...defaultProps} disabled={true} />);
|
|
50
|
+
|
|
51
|
+
const confirmButton = screen.getByText('Continue with').closest('button');
|
|
52
|
+
expect(confirmButton).toBeDisabled();
|
|
53
|
+
|
|
54
|
+
if (confirmButton) {
|
|
55
|
+
fireEvent.click(confirmButton);
|
|
56
|
+
}
|
|
57
|
+
expect(mockOnConfirm).not.toHaveBeenCalled();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should display token symbol when provided', () => {
|
|
61
|
+
render(<ActionButtons {...defaultProps} tokenSymbol="ETH" />);
|
|
62
|
+
|
|
63
|
+
expect(screen.getByText('Continue with ETH')).toBeInTheDocument();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should show skeleton loading when tokenSymbol is not provided', () => {
|
|
67
|
+
render(<ActionButtons {...defaultProps} />);
|
|
68
|
+
|
|
69
|
+
expect(screen.getByText('Continue with')).toBeInTheDocument();
|
|
70
|
+
expect(document.querySelector('.animate-shimmer')).toBeInTheDocument();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { render, screen } from '@test';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import BalanceIndicator from '../_components/BalanceIndicator';
|
|
6
|
+
|
|
7
|
+
describe('BalanceIndicator', () => {
|
|
8
|
+
it('should render warning icon and negative text when insufficient balance', () => {
|
|
9
|
+
render(
|
|
10
|
+
<BalanceIndicator
|
|
11
|
+
insufficientBalance={true}
|
|
12
|
+
currencyBalance={{ formatted: '0.5' }}
|
|
13
|
+
selectedFeeOption={{ token: { symbol: 'ETH' } }}
|
|
14
|
+
/>,
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
expect(document.querySelector('.text-negative')).toBeInTheDocument();
|
|
18
|
+
expect(screen.getByText('You have 0.5 ETH')).toBeInTheDocument();
|
|
19
|
+
expect(screen.getByText('You have 0.5 ETH').className).toContain(
|
|
20
|
+
'text-negative',
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should render checkmark icon and normal text when balance is sufficient', () => {
|
|
25
|
+
render(
|
|
26
|
+
<BalanceIndicator
|
|
27
|
+
insufficientBalance={false}
|
|
28
|
+
currencyBalance={{ formatted: '1.5' }}
|
|
29
|
+
selectedFeeOption={{ token: { symbol: 'ETH' } }}
|
|
30
|
+
/>,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
expect(document.querySelector('.text-positive')).toBeInTheDocument();
|
|
34
|
+
expect(screen.getByText('You have 1.5 ETH')).toBeInTheDocument();
|
|
35
|
+
expect(screen.getByText('You have 1.5 ETH').className).not.toContain(
|
|
36
|
+
'text-negative',
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should handle undefined currencyBalance gracefully', () => {
|
|
41
|
+
render(
|
|
42
|
+
<BalanceIndicator
|
|
43
|
+
insufficientBalance={false}
|
|
44
|
+
selectedFeeOption={{ token: { symbol: 'ETH' } }}
|
|
45
|
+
/>,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
expect(screen.getByText('You have 0 ETH')).toBeInTheDocument();
|
|
49
|
+
});
|
|
50
|
+
});
|