@0xsequence/marketplace-sdk 0.8.4 → 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 +8 -0
- package/dist/{chunk-25CAMYCG.js → chunk-BB2PTJHI.js} +22 -20
- package/dist/chunk-BB2PTJHI.js.map +1 -0
- package/dist/{chunk-VBRJ2OPM.js → chunk-EZFCQZHU.js} +2 -2
- package/dist/{chunk-VF3LWBQB.js → chunk-KCLMSSPS.js} +6 -6
- package/dist/{chunk-XUNDLCEH.js → chunk-LDZZUYG7.js} +2 -2
- package/dist/{chunk-HRL2TMXU.js → chunk-SFSFIGHM.js} +44 -34
- package/dist/{chunk-HRL2TMXU.js.map → chunk-SFSFIGHM.js.map} +1 -1
- package/dist/{chunk-44YGZVBS.js → chunk-ZVTG6US2.js} +2 -2
- package/dist/index.css +4 -4
- package/dist/index.css.map +1 -1
- package/dist/index.js +6 -6
- 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.js +4 -4
- package/dist/react/hooks/options/index.js +2 -2
- package/dist/react/index.js +7 -7
- package/dist/react/queries/index.js +1 -1
- 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 +2 -2
- 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/abi/index.js +5 -5
- package/dist/utils/index.js +6 -6
- package/package.json +19 -19
- 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/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/SellModal/__tests__/Modal.test.tsx +72 -135
- 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/test/test-utils.tsx +12 -22
- package/dist/chunk-25CAMYCG.js.map +0 -1
- package/src/react/ui/components/_internals/action-button/__tests__/ActionButton.test.tsx +0 -107
- /package/dist/{chunk-VBRJ2OPM.js.map → chunk-EZFCQZHU.js.map} +0 -0
- /package/dist/{chunk-VF3LWBQB.js.map → chunk-KCLMSSPS.js.map} +0 -0
- /package/dist/{chunk-XUNDLCEH.js.map → chunk-LDZZUYG7.js.map} +0 -0
- /package/dist/{chunk-44YGZVBS.js.map → chunk-ZVTG6US2.js.map} +0 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { render, screen } from '@test/test-utils';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import type { TokenMetadata } from '../../../../_internal';
|
|
4
|
+
import { CollectibleAsset } from '../CollectibleAsset';
|
|
5
|
+
|
|
6
|
+
describe('CollectibleAsset', () => {
|
|
7
|
+
it('renders image content correctly with proper loading states and fallback', async () => {
|
|
8
|
+
const originalImage = window.Image;
|
|
9
|
+
|
|
10
|
+
// We need to use a proper constructor function to match the Image interface
|
|
11
|
+
const MockImage = function (this: HTMLImageElement) {
|
|
12
|
+
this.src = '';
|
|
13
|
+
this.alt = '';
|
|
14
|
+
this.className = '';
|
|
15
|
+
this.onload = null;
|
|
16
|
+
this.onerror = null;
|
|
17
|
+
return this;
|
|
18
|
+
} as unknown as typeof Image;
|
|
19
|
+
|
|
20
|
+
window.Image = MockImage;
|
|
21
|
+
|
|
22
|
+
const mockMetadata: Partial<TokenMetadata> = {
|
|
23
|
+
tokenId: '1',
|
|
24
|
+
name: 'Test Collectible',
|
|
25
|
+
image: 'https://example.com/test-image.png',
|
|
26
|
+
attributes: [],
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Initial render should show the loading skeleton
|
|
30
|
+
const { rerender } = render(
|
|
31
|
+
<CollectibleAsset
|
|
32
|
+
name="Test Collectible"
|
|
33
|
+
collectibleMetadata={mockMetadata as TokenMetadata}
|
|
34
|
+
/>,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// check if skeleton is rendered during loading
|
|
38
|
+
const skeleton = screen.getByTestId('collectible-asset-skeleton');
|
|
39
|
+
expect(skeleton).toBeInTheDocument();
|
|
40
|
+
|
|
41
|
+
// trigger the image load event to simulate successful loading
|
|
42
|
+
const imgElement = document.querySelector('img');
|
|
43
|
+
expect(imgElement).not.toBeNull();
|
|
44
|
+
|
|
45
|
+
if (imgElement) {
|
|
46
|
+
expect(imgElement.getAttribute('src')).toBe(
|
|
47
|
+
'https://example.com/test-image.png',
|
|
48
|
+
);
|
|
49
|
+
expect(imgElement.getAttribute('alt')).toBe('Test Collectible');
|
|
50
|
+
|
|
51
|
+
// initial state should be invisible due to loading
|
|
52
|
+
expect(imgElement.className).toContain('invisible');
|
|
53
|
+
|
|
54
|
+
// successful image load
|
|
55
|
+
imgElement.dispatchEvent(new Event('load'));
|
|
56
|
+
|
|
57
|
+
// after loading, the image should be visible
|
|
58
|
+
expect(imgElement.className).toContain('visible');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// failing image that should use fallback
|
|
62
|
+
const mockMetadataWithBadImage: Partial<TokenMetadata> = {
|
|
63
|
+
tokenId: '1',
|
|
64
|
+
name: 'Test Collectible',
|
|
65
|
+
image: 'https://example.com/bad-image.png',
|
|
66
|
+
attributes: [],
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
rerender(
|
|
70
|
+
<CollectibleAsset
|
|
71
|
+
name="Test Collectible"
|
|
72
|
+
collectibleMetadata={mockMetadataWithBadImage as TokenMetadata}
|
|
73
|
+
/>,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const updatedImgElement = document.querySelector('img');
|
|
77
|
+
if (updatedImgElement) {
|
|
78
|
+
// simulate image load error
|
|
79
|
+
updatedImgElement.dispatchEvent(new Event('error'));
|
|
80
|
+
|
|
81
|
+
// after error, the src should be changed to the placeholder
|
|
82
|
+
expect(updatedImgElement.className).toContain('visible');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// restore the original Image implementation
|
|
86
|
+
window.Image = originalImage;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('handles video content with appropriate controls and loading states', () => {
|
|
90
|
+
// Create a mock for the HTMLVideoElement addEventListener
|
|
91
|
+
const originalAddEventListener =
|
|
92
|
+
HTMLVideoElement.prototype.addEventListener;
|
|
93
|
+
HTMLVideoElement.prototype.addEventListener = vi.fn(
|
|
94
|
+
(event: string, handler: EventListenerOrEventListenerObject) => {
|
|
95
|
+
// Immediately call the loadedmetadata handler to simulate video loaded
|
|
96
|
+
if (event === 'loadedmetadata' && typeof handler === 'function') {
|
|
97
|
+
handler(new Event('loadedmetadata'));
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Mock browser detection for Safari
|
|
103
|
+
const originalUserAgent = navigator.userAgent;
|
|
104
|
+
Object.defineProperty(navigator, 'userAgent', {
|
|
105
|
+
value:
|
|
106
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15',
|
|
107
|
+
configurable: true,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Mock video metadata
|
|
111
|
+
const mockVideoMetadata: Partial<TokenMetadata> = {
|
|
112
|
+
tokenId: '1',
|
|
113
|
+
name: 'Video Collectible',
|
|
114
|
+
video: 'https://example.com/video.mp4',
|
|
115
|
+
attributes: [],
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
render(
|
|
119
|
+
<CollectibleAsset
|
|
120
|
+
name="Video Collectible"
|
|
121
|
+
collectibleMetadata={mockVideoMetadata as TokenMetadata}
|
|
122
|
+
/>,
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// Check that video element is present with correct attributes
|
|
126
|
+
const videoElement = document.querySelector('video');
|
|
127
|
+
expect(videoElement).not.toBeNull();
|
|
128
|
+
|
|
129
|
+
if (videoElement) {
|
|
130
|
+
// Video source should be set correctly
|
|
131
|
+
const sourceElement = videoElement.querySelector('source');
|
|
132
|
+
expect(sourceElement).not.toBeNull();
|
|
133
|
+
expect(sourceElement?.getAttribute('src')).toBe(
|
|
134
|
+
'https://example.com/video.mp4',
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
// Video should have correct attributes for NFT display
|
|
138
|
+
expect(videoElement.autoplay).toBe(true);
|
|
139
|
+
expect(videoElement.loop).toBe(true);
|
|
140
|
+
expect(videoElement.controls).toBe(true);
|
|
141
|
+
expect(videoElement.playsInline).toBe(true);
|
|
142
|
+
expect(videoElement.muted).toBe(true);
|
|
143
|
+
|
|
144
|
+
// In Safari, pointer-events-none should be applied
|
|
145
|
+
expect(videoElement.className).toContain('pointer-events-none');
|
|
146
|
+
|
|
147
|
+
// After metadata loaded, video should be visible
|
|
148
|
+
expect(videoElement.className).toContain('visible');
|
|
149
|
+
expect(videoElement.className).not.toContain('invisible');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Clean up mocks
|
|
153
|
+
HTMLVideoElement.prototype.addEventListener = originalAddEventListener;
|
|
154
|
+
Object.defineProperty(navigator, 'userAgent', {
|
|
155
|
+
value: originalUserAgent,
|
|
156
|
+
configurable: true,
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('handles HTML content in iframes with proper sandboxing', () => {
|
|
161
|
+
// Mock HTML content metadata
|
|
162
|
+
const mockHtmlMetadata: Partial<TokenMetadata> = {
|
|
163
|
+
tokenId: '1',
|
|
164
|
+
name: 'HTML Collectible',
|
|
165
|
+
animation_url: 'https://example.com/interactive.html',
|
|
166
|
+
attributes: [],
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
render(
|
|
170
|
+
<CollectibleAsset
|
|
171
|
+
name="HTML Collectible"
|
|
172
|
+
collectibleMetadata={mockHtmlMetadata as TokenMetadata}
|
|
173
|
+
/>,
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// Check that iframe element is present with correct attributes
|
|
177
|
+
const iframeElement = document.querySelector('iframe');
|
|
178
|
+
expect(iframeElement).not.toBeNull();
|
|
179
|
+
|
|
180
|
+
if (iframeElement) {
|
|
181
|
+
// iframe source should be set correctly
|
|
182
|
+
expect(iframeElement.getAttribute('src')).toBe(
|
|
183
|
+
'https://example.com/interactive.html',
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// iframe should have appropriate attributes for security
|
|
187
|
+
expect(iframeElement.getAttribute('sandbox')).toBe('allow-scripts');
|
|
188
|
+
|
|
189
|
+
// iframe should have title for accessibility
|
|
190
|
+
expect(iframeElement.getAttribute('title')).toBe('HTML Collectible');
|
|
191
|
+
|
|
192
|
+
// iframe should have proper styling
|
|
193
|
+
expect(iframeElement.className).toContain('aspect-square');
|
|
194
|
+
expect(iframeElement.className).toContain('w-full');
|
|
195
|
+
|
|
196
|
+
// Verify border styling
|
|
197
|
+
expect(iframeElement.style.border).toBe('0px');
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
});
|
|
@@ -1,125 +1,94 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TEST_COLLECTIBLE, TEST_CURRENCY } from '@test/const';
|
|
2
|
+
import { fireEvent, render, screen } from '@test/test-utils';
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import {
|
|
5
|
+
type CollectibleOrder,
|
|
6
|
+
ContractType,
|
|
7
|
+
OrderSide,
|
|
8
|
+
OrderbookKind,
|
|
9
|
+
} from '../../../../_internal';
|
|
10
|
+
import { mockTokenMetadata } from '../../../../_internal/api/__mocks__/indexer.msw';
|
|
11
|
+
import { mockOrder } from '../../../../_internal/api/__mocks__/marketplace.msw';
|
|
12
|
+
import * as hooks from '../../../../hooks';
|
|
13
|
+
import { CollectibleCard } from '../CollectibleCard';
|
|
2
14
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// .closest('article');
|
|
83
|
-
// if (collectibleElement) {
|
|
84
|
-
// fireEvent.click(collectibleElement);
|
|
85
|
-
// }
|
|
86
|
-
// expect(onCollectibleClick).toHaveBeenCalledWith(defaultProps.collectibleId);
|
|
87
|
-
// });
|
|
88
|
-
// it('should open external url in new tab when external link is clicked', () => {
|
|
89
|
-
// render(<CollectibleCard {...defaultProps} />);
|
|
90
|
-
// const externalLink = screen.getByRole('link');
|
|
91
|
-
// expect(externalLink).toHaveAttribute('href', 'https://example.com');
|
|
92
|
-
// expect(externalLink).toHaveAttribute('target', '_blank');
|
|
93
|
-
// expect(externalLink).toHaveAttribute('rel', 'noopener noreferrer');
|
|
94
|
-
// });
|
|
95
|
-
// it('should use fallback image when image load fails', async () => {
|
|
96
|
-
// render(<CollectibleCard {...defaultProps} />);
|
|
97
|
-
// const image = screen.getByAltText('Test Collectible');
|
|
98
|
-
// // Trigger error
|
|
99
|
-
// fireEvent.error(image);
|
|
100
|
-
// // Check if image source is changed to fallback
|
|
101
|
-
// expect(image).toHaveAttribute('src', expect.stringContaining('chess-tile'));
|
|
102
|
-
// });
|
|
103
|
-
// it('should show balance for ERC-1155 tokens', () => {
|
|
104
|
-
// const erc1155Props = {
|
|
105
|
-
// ...defaultProps,
|
|
106
|
-
// collectionType: ContractType.ERC1155,
|
|
107
|
-
// balance: '5',
|
|
108
|
-
// };
|
|
109
|
-
// render(<CollectibleCard {...erc1155Props} />);
|
|
110
|
-
// expect(screen.getByText('Owned: 5')).toBeInTheDocument();
|
|
111
|
-
// });
|
|
112
|
-
// it('should call onCannotPerformAction when action cannot be performed', async () => {
|
|
113
|
-
// const onCannotPerformAction = vi.fn();
|
|
114
|
-
// render(
|
|
115
|
-
// <CollectibleCard
|
|
116
|
-
// {...defaultProps}
|
|
117
|
-
// onCannotPerformAction={onCannotPerformAction}
|
|
118
|
-
// balance="1" // Make it owned to test owner actions
|
|
119
|
-
// />,
|
|
120
|
-
// );
|
|
121
|
-
// // We can't directly test this without mocking the useActionButtonLogic hook
|
|
122
|
-
// // Just testing the prop passes through
|
|
123
|
-
// expect(onCannotPerformAction).not.toHaveBeenCalled();
|
|
124
|
-
// });
|
|
15
|
+
const defaultProps = {
|
|
16
|
+
collectibleId: '1',
|
|
17
|
+
chainId: 1,
|
|
18
|
+
collectionAddress: TEST_COLLECTIBLE.collectionAddress,
|
|
19
|
+
collectible: {
|
|
20
|
+
order: mockOrder,
|
|
21
|
+
listing: { ...mockOrder, side: OrderSide.listing },
|
|
22
|
+
offer: { ...mockOrder, side: OrderSide.offer },
|
|
23
|
+
metadata: {
|
|
24
|
+
...mockTokenMetadata,
|
|
25
|
+
tokenId: mockTokenMetadata.tokenId as string,
|
|
26
|
+
assets: mockTokenMetadata.assets
|
|
27
|
+
? mockTokenMetadata.assets.map((asset) => ({
|
|
28
|
+
...asset,
|
|
29
|
+
tokenId: asset.tokenId || '',
|
|
30
|
+
}))
|
|
31
|
+
: undefined,
|
|
32
|
+
},
|
|
33
|
+
} as CollectibleOrder,
|
|
34
|
+
balance: '100',
|
|
35
|
+
balanceIsLoading: false,
|
|
36
|
+
cardLoading: false,
|
|
37
|
+
onCannotPerformAction: vi.fn(),
|
|
38
|
+
assetSrcPrefixUrl: 'https://example.com/',
|
|
39
|
+
orderbookKind: OrderbookKind.sequence_marketplace_v2,
|
|
40
|
+
collectionType: ContractType.ERC721,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
describe('CollectibleCard', () => {
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
vi.clearAllMocks();
|
|
46
|
+
vi.restoreAllMocks();
|
|
47
|
+
|
|
48
|
+
const useCurrencySpy = vi.spyOn(hooks, 'useCurrency');
|
|
49
|
+
useCurrencySpy.mockReturnValue({
|
|
50
|
+
data: TEST_CURRENCY,
|
|
51
|
+
} as ReturnType<typeof hooks.useCurrency>);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('Renders correctly with valid props and shows proper collectible details', () => {
|
|
55
|
+
render(<CollectibleCard {...defaultProps} />);
|
|
56
|
+
|
|
57
|
+
expect(screen.getByText('Mock NFT')).toBeInTheDocument();
|
|
58
|
+
expect(screen.getByText('1 TEST')).toBeInTheDocument();
|
|
59
|
+
// there is an offer
|
|
60
|
+
expect(screen.getByTitle('Notification Bell')).toBeInTheDocument();
|
|
61
|
+
expect(screen.getByRole('img', { name: 'Mock NFT' })).toHaveAttribute(
|
|
62
|
+
'src',
|
|
63
|
+
defaultProps.assetSrcPrefixUrl + defaultProps.collectible.metadata.image,
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('Handles loading state by showing skeleton component', () => {
|
|
68
|
+
render(<CollectibleCard {...defaultProps} cardLoading={true} />);
|
|
69
|
+
expect(screen.getByTestId('collectible-card-skeleton')).toBeInTheDocument();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('Triggers appropriate callbacks when collectible or action buttons are clicked', () => {
|
|
73
|
+
const onCollectibleClick = vi.fn();
|
|
74
|
+
const onOfferClick = vi.fn();
|
|
75
|
+
|
|
76
|
+
render(
|
|
77
|
+
<CollectibleCard
|
|
78
|
+
{...defaultProps}
|
|
79
|
+
onCollectibleClick={onCollectibleClick}
|
|
80
|
+
onOfferClick={onOfferClick}
|
|
81
|
+
/>,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const notificationBell = screen.getByRole('button', {
|
|
85
|
+
name: 'Notification Bell',
|
|
86
|
+
});
|
|
87
|
+
fireEvent.click(notificationBell);
|
|
88
|
+
expect(onOfferClick).toHaveBeenCalled();
|
|
89
|
+
|
|
90
|
+
const collectibleCard = screen.getByTestId('collectible-card');
|
|
91
|
+
fireEvent.click(collectibleCard);
|
|
92
|
+
expect(onCollectibleClick).toHaveBeenCalled();
|
|
93
|
+
});
|
|
125
94
|
});
|
|
@@ -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();
|