Changeset View
Changeset View
Standalone View
Standalone View
cashtab/src/components/Agora/__tests__/index.test.tsx
| Show First 20 Lines • Show All 171 Lines • ▼ Show 20 Lines | it('Screen loads as expected if there are no agora partial listings', async () => { | ||||
| expect( | expect( | ||||
| screen.queryByTitle('Loading active offers'), | screen.queryByTitle('Loading active offers'), | ||||
| ).not.toBeInTheDocument(), | ).not.toBeInTheDocument(), | ||||
| ); | ); | ||||
| // Wait for element to get token info and load | // Wait for element to get token info and load | ||||
| expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); | expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); | ||||
| // We see sort switches from "Manage my offers" | // User listings load in the background and should resolve to empty state | ||||
| expect(screen.getByTitle('Sort by TokenId')).toBeInTheDocument(); | |||||
| expect(screen.getByTitle('Sort by Offer Count')).toBeInTheDocument(); | |||||
| // But we have no offers | |||||
| expect( | expect( | ||||
| await screen.findByText( | await screen.findByText('You have no active listings.'), | ||||
| 'No whitelisted tokens are currently listed for sale. Try loading all offers.', | |||||
| ), | |||||
| ).toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
| // We try to load all the offers | // We try to load all the offers | ||||
| await userEvent.click( | await userEvent.click( | ||||
| screen.getByRole('button', { name: 'Load all offers' }), | screen.getByRole('button', { name: 'Load all offers' }), | ||||
| ); | ); | ||||
| // We see a confirmation modal | // We see a confirmation modal | ||||
| // Note this is a silly msg, but we do not expect to ever have 0 offers in prod, so we do not handle it in the app | expect(screen.getByText('Load all offers?')).toBeInTheDocument(); | ||||
| expect( | expect( | ||||
| screen.getByText( | screen.getByText( | ||||
| 'We have 0 listings. This will take a long time and the screen will be slow.', | 'This will query and render a large marketplace list and may be slow. Continue?', | ||||
| ), | ), | ||||
| ).toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
| // Loading 0 offers does not sound scary to us. Let's do it. | // Loading 0 offers does not sound scary to us. Let's do it. | ||||
| await userEvent.click(screen.getByText('OK')); | await userEvent.click(screen.getByText('OK')); | ||||
| // No offers. We were warned. | // No offers. We were warned. | ||||
| expect( | expect( | ||||
| await screen.findByText('No tokens are currently listed for sale.'), | await screen.findByText('No tokens are currently listed for sale.'), | ||||
| ).toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
| // We switch to see our created offers | |||||
| await userEvent.click(screen.getByTitle('Toggle Active Offers')); | |||||
| // We have made no offers | |||||
| // This is always empty if active offers is empty, as for partials, active offers will render both | |||||
| // public offers and offers created by your wallet | |||||
| // Your offers you can only cancel, not buy | |||||
| expect( | |||||
| screen.getByText('You do not have any listed tokens'), | |||||
| ).toBeInTheDocument(); | |||||
| // We do not see sort switches from "Manage my offers" | |||||
| expect(screen.queryByTitle('Sort by TokenId')).not.toBeInTheDocument(); | |||||
| expect( | |||||
| screen.queryByTitle('Sort by Offer Count'), | |||||
| ).not.toBeInTheDocument(); | |||||
| }); | }); | ||||
| it('A chronik error notice is rendered if there is some error in querying listings', async () => { | it('A chronik error notice is rendered if there is some error in querying listings', async () => { | ||||
| // Need to mock agora API endpoints | // Need to mock agora API endpoints | ||||
| const mockedAgora = new MockAgora(); | const mockedAgora = new MockAgora(); | ||||
| // mock await agora.offeredFungibleTokenIds(); | // mock await agora.offeredFungibleTokenIds(); | ||||
| mockedAgora.setOfferedFungibleTokenIds(new Error('some chronik error')); | mockedAgora.setOfferedFungibleTokenIds(new Error('some chronik error')); | ||||
| Show All 40 Lines | it('A chronik error notice is rendered if there is some error in querying listings', async () => { | ||||
| // Wait for agora offers to load | // Wait for agora offers to load | ||||
| await waitFor(() => | await waitFor(() => | ||||
| expect( | expect( | ||||
| screen.queryByTitle('Loading active offers'), | screen.queryByTitle('Loading active offers'), | ||||
| ).not.toBeInTheDocument(), | ).not.toBeInTheDocument(), | ||||
| ); | ); | ||||
| // Error is only surfaced after user opts into loading all offers | |||||
| await userEvent.click( | |||||
| screen.getByRole('button', { name: 'Load all offers' }), | |||||
| ); | |||||
| await userEvent.click(screen.getByText('OK')); | |||||
| // A chronik error notice is rendered | // A chronik error notice is rendered | ||||
| expect( | expect( | ||||
| await screen.findByText( | await screen.findByText( | ||||
| 'Error querying listed tokens. Please try again later.', | 'Error querying listed tokens. Please try again later.', | ||||
| ), | ), | ||||
| ).toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
| }); | }); | ||||
| ▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | it('A whitelisted offer is rendered immediately', async () => { | ||||
| ( | ( | ||||
| await screen.findAllByText((content, _element) => | await screen.findAllByText((content, _element) => | ||||
| content.includes('Staked XEC'), | content.includes('Staked XEC'), | ||||
| ) | ) | ||||
| ).length, | ).length, | ||||
| ).toBeGreaterThan(0); | ).toBeGreaterThan(0); | ||||
| expect(await screen.findAllByText('XECX')).toHaveLength(2); | expect(await screen.findAllByText('XECX')).toHaveLength(2); | ||||
| // In the new design, offer details live on the token page. Click XECX to open OrderBook. | |||||
| await userEvent.click( | |||||
| screen.getByRole('link', { name: /icon for.*Staked XEC.*XECX/ }), | |||||
| ); | |||||
| // Because this offer was created by this wallet, we have the option to cancel it | // Because this offer was created by this wallet, we have the option to cancel it | ||||
| expect( | expect( | ||||
| await screen.findByRole('button', { name: 'Cancel your offer' }), | await screen.findByRole('button', { name: 'Cancel your offer' }), | ||||
| ).toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
| }); | }); | ||||
| it('We need to load all to see a non-whitelisted offer', async () => { | it('We need to load all to see a non-whitelisted offer', async () => { | ||||
| const mockedAgora = new MockAgora(); | const mockedAgora = new MockAgora(); | ||||
| ▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | it('We need to load all to see a non-whitelisted offer', async () => { | ||||
| expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); | expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); | ||||
| expect(screen.getByText('Token Offers')).toBeInTheDocument(); | expect(screen.getByText('Token Offers')).toBeInTheDocument(); | ||||
| // Wait for the token to be rendered (either as a whitelisted token or after loading all offers) | // Wait for the token to be rendered (either as a whitelisted token or after loading all offers) | ||||
| expect(await screen.findByText('Token Offers')).toBeInTheDocument(); | expect(await screen.findByText('Token Offers')).toBeInTheDocument(); | ||||
| // We should see "No whitelisted tokens are currently listed for sale" since Cachet is not whitelisted | // Whitelisted list is rendered by default | ||||
| expect( | expect( | ||||
| screen.getByText( | await screen.findByText((content, _element) => | ||||
| 'No whitelisted tokens are currently listed for sale. Try loading all offers.', | content.includes('Staked XEC'), | ||||
| ), | ), | ||||
| ).toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
| // We try to load all the offers to see the non-whitelisted Cachet token | // We try to load all the offers to see the non-whitelisted Cachet token | ||||
| await userEvent.click( | await userEvent.click( | ||||
| screen.getByRole('button', { name: 'Load all offers' }), | screen.getByRole('button', { name: 'Load all offers' }), | ||||
| ); | ); | ||||
| // We see a confirmation modal | // We see a confirmation modal | ||||
| expect( | expect(screen.getByText('Load all offers?')).toBeInTheDocument(); | ||||
| screen.getByText( | |||||
| 'We have 1 listings. This will take a long time and the screen will be slow.', | |||||
| ), | |||||
| ).toBeInTheDocument(); | |||||
| // Loading 1 offer sounds reasonable | // Loading 1 offer sounds reasonable | ||||
| await userEvent.click(screen.getByText('OK')); | await userEvent.click(screen.getByText('OK')); | ||||
| // We see the token name and ticker above its PartialOffer after OrderBooks load | // We see the token name and ticker in Your Listings after OrderBooks load | ||||
| expect( | expect( | ||||
| ( | ( | ||||
| await screen.findAllByText((content, _element) => | await screen.findAllByText((content, _element) => | ||||
| content.includes('Cachet'), | content.includes('Cachet'), | ||||
| ) | ) | ||||
| ).length, | ).length, | ||||
| ).toBeGreaterThan(0); | ).toBeGreaterThan(0); | ||||
| // In the new design, offer details live on the token page. Click Cachet to open OrderBook. | |||||
| await userEvent.click( | |||||
| screen.getByRole('link', { name: /icon for.*Cachet.*CACHET/ }), | |||||
| ); | |||||
| // Because this offer was created by this wallet, we have the option to cancel it | // Because this offer was created by this wallet, we have the option to cancel it | ||||
| expect( | expect( | ||||
| await screen.findByRole('button', { name: 'Cancel your offer' }), | await screen.findByRole('button', { name: 'Cancel your offer' }), | ||||
| ).toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
| }); | }); | ||||
| it('We can fetch and use the blacklist from token server', async () => { | it('We can fetch and use the blacklist from token server', async () => { | ||||
| when(fetch) | when(fetch) | ||||
| ▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | it('We can fetch and use the blacklist from token server', async () => { | ||||
| chronik={mockedChronik} | chronik={mockedChronik} | ||||
| ecc={ecc} | ecc={ecc} | ||||
| agora={mockedAgora} | agora={mockedAgora} | ||||
| theme={theme} | theme={theme} | ||||
| route={`/agora/`} | route={`/agora/`} | ||||
| />, | />, | ||||
| ); | ); | ||||
| // No whitelisted offers | // Whitelisted list is rendered by default | ||||
| expect( | expect(await screen.findByText('Token Offers')).toBeInTheDocument(); | ||||
| await screen.findByText( | |||||
| 'No whitelisted tokens are currently listed for sale. Try loading all offers.', | |||||
| ), | |||||
| ).toBeInTheDocument(); | |||||
| // We try to load all the offers | // We try to load all the offers | ||||
| await userEvent.click( | await userEvent.click( | ||||
| screen.getByRole('button', { name: 'Load all offers' }), | screen.getByRole('button', { name: 'Load all offers' }), | ||||
| ); | ); | ||||
| // We see a confirmation modal showing 0 offers, as expected | // We see a confirmation modal | ||||
| expect( | expect(screen.getByText('Load all offers?')).toBeInTheDocument(); | ||||
| screen.getByText( | |||||
| 'We have 0 listings. This will take a long time and the screen will be slow.', | |||||
| ), | |||||
| ).toBeInTheDocument(); | |||||
| // Loading 1 offer sounds reasonable | // Loading 1 offer sounds reasonable | ||||
| await userEvent.click(screen.getByText('OK')); | await userEvent.click(screen.getByText('OK')); | ||||
| // Wait for token info to load (wait for loading to disappear and token name to appear) | // Wait for token info to load (wait for loading to disappear and token name to appear) | ||||
| // Wait for the token to be rendered (either as a whitelisted token or after loading all offers) | // Wait for the token to be rendered (either as a whitelisted token or after loading all offers) | ||||
| ▲ Show 20 Lines • Show All 115 Lines • ▼ Show 20 Lines | it('On token server API fail, we fall back to locally maintained blacklist. A blacklisted offer does not render in all offers, but will render in My offers', async () => { | ||||
| // Wait for the Agora component to finish its initialization and load the whitelisted tokens | // Wait for the Agora component to finish its initialization and load the whitelisted tokens | ||||
| await waitFor(() => | await waitFor(() => | ||||
| expect( | expect( | ||||
| screen.queryByTitle('Loading active offers'), | screen.queryByTitle('Loading active offers'), | ||||
| ).not.toBeInTheDocument(), | ).not.toBeInTheDocument(), | ||||
| ); | ); | ||||
| // No whitelisted offers | // Whitelisted list is rendered by default | ||||
| expect( | expect(await screen.findByText('Token Offers')).toBeInTheDocument(); | ||||
| await screen.findByText( | |||||
| 'No whitelisted tokens are currently listed for sale. Try loading all offers.', | |||||
| ), | |||||
| ).toBeInTheDocument(); | |||||
| // We try to load all the offers | // We try to load all the offers | ||||
| await userEvent.click( | await userEvent.click( | ||||
| screen.getByRole('button', { name: 'Load all offers' }), | screen.getByRole('button', { name: 'Load all offers' }), | ||||
| ); | ); | ||||
| // We see a confirmation modal showing 0 offers, as expected | // We see a confirmation modal | ||||
| expect( | expect(screen.getByText('Load all offers?')).toBeInTheDocument(); | ||||
| screen.getByText( | |||||
| 'We have 0 listings. This will take a long time and the screen will be slow.', | |||||
| ), | |||||
| ).toBeInTheDocument(); | |||||
| // Close the modal | |||||
| await userEvent.click(screen.getByText('X')); | |||||
| // We switch to see our created offers | |||||
| await userEvent.click(screen.getByTitle('Toggle Active Offers')); | |||||
| expect(screen.getByText('Manage your listings')).toBeInTheDocument(); | |||||
| // Wait for token info to load (wait for loading to disappear and token name to appear) | |||||
| // Wait for the token to be rendered (either as a whitelisted token or after loading all offers) | // Close the modal (new Modal uses SVG with title="Close", not text "X") | ||||
| await userEvent.click(screen.getByTitle('Close')); | |||||
| // In the new design, Your Listings and Token Offers are always shown together. | |||||
| // The blacklisted BUX offer appears in Your Listings (our offers). | |||||
| expect(screen.getByText('Your Listings')).toBeInTheDocument(); | |||||
| expect(await screen.findByText('Token Offers')).toBeInTheDocument(); | expect(await screen.findByText('Token Offers')).toBeInTheDocument(); | ||||
| // Wait for the Agora component to finish its initialization and load the whitelisted tokens | |||||
| await waitFor(() => | await waitFor(() => | ||||
| expect( | expect( | ||||
| screen.queryByTitle('Loading active offers'), | screen.queryByTitle('Loading active offers'), | ||||
| ).not.toBeInTheDocument(), | ).not.toBeInTheDocument(), | ||||
| ); | ); | ||||
| // We see the token name and ticker above its PartialOffer | // We see the token name and ticker in Your Listings | ||||
| expect( | expect( | ||||
| ( | ( | ||||
| await screen.findAllByText((content, _element) => | await screen.findAllByText((content, _element) => | ||||
| content.includes('Badger Universal Token'), | content.includes('Badger Universal Token'), | ||||
| ) | ) | ||||
| ).length, | ).length, | ||||
| ).toBeGreaterThan(0); | ).toBeGreaterThan(0); | ||||
| expect(screen.getByText('BUX')).toBeInTheDocument(); | expect(screen.getByText('BUX')).toBeInTheDocument(); | ||||
| // Because this offer was created by this wallet, we have the option to cancel it | // Click BUX to open OrderBook; our offer shows cancel button | ||||
| await userEvent.click( | |||||
| screen.getByRole('link', { | |||||
| name: /icon for.*Badger Universal Token.*BUX/, | |||||
| }), | |||||
| ); | |||||
| expect( | expect( | ||||
| screen.getByRole('button', { name: 'Cancel your offer' }), | await screen.findByRole('button', { name: 'Cancel your offer' }), | ||||
| ).toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
| }); | }); | ||||
| it('We can see multiple offers, some we made, others we did not, and we can cancel an offer', async () => { | it('We can see multiple offers, some we made, others we did not, and we can cancel an offer', async () => { | ||||
| // We do not want the date mocked here, it interferes with changing wallets somehow | // We do not want the date mocked here, it interferes with changing wallets somehow | ||||
| jest.spyOn(global, 'Date').mockRestore(); | jest.spyOn(global, 'Date').mockRestore(); | ||||
| // Need to mock agora API endpoints | // Need to mock agora API endpoints | ||||
| ▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | it('We can see multiple offers, some we made, others we did not, and we can cancel an offer', async () => { | ||||
| // Wait for agora offers to load | // Wait for agora offers to load | ||||
| await waitFor(() => | await waitFor(() => | ||||
| expect( | expect( | ||||
| screen.queryByTitle('Loading active offers'), | screen.queryByTitle('Loading active offers'), | ||||
| ).not.toBeInTheDocument(), | ).not.toBeInTheDocument(), | ||||
| ); | ); | ||||
| // We have no whitelisted tokens, so we see expected msg | // Whitelisted list is rendered by default | ||||
| expect(await screen.findByText('Token Offers')).toBeInTheDocument(); | |||||
| expect( | |||||
| await screen.findByText( | |||||
| 'No whitelisted tokens are currently listed for sale. Try loading all offers.', | |||||
| ), | |||||
| ).toBeInTheDocument(); | |||||
| // We try to load all the offers | // We try to load all the offers | ||||
| await userEvent.click( | await userEvent.click( | ||||
| screen.getByRole('button', { name: 'Load all offers' }), | screen.getByRole('button', { name: 'Load all offers' }), | ||||
| ); | ); | ||||
| // We see a confirmation modal | // We see a confirmation modal | ||||
| expect( | expect(screen.getByText('Load all offers?')).toBeInTheDocument(); | ||||
| screen.getByText( | |||||
| 'We have 2 listings. This will take a long time and the screen will be slow.', | |||||
| ), | |||||
| ).toBeInTheDocument(); | |||||
| // Load them | // Load them | ||||
| await userEvent.click(screen.getByText('OK')); | await userEvent.click(screen.getByText('OK')); | ||||
| // Wait for Agora to load all orderBookInfo | // Wait for all tokens to be loaded | ||||
| await waitFor( | |||||
| () => | |||||
| expect( | |||||
| screen.queryByTitle('Loading OrderBook info...'), | |||||
| ).not.toBeInTheDocument(), | |||||
| // This can take some time | |||||
| // May need to adjust if experience flakiness | |||||
| { timeout: 10000 }, | |||||
| ); | |||||
| // When orderbook info has loaded, we see a switch to sort by offer count | |||||
| expect( | expect( | ||||
| await screen.findByTitle('Sort by Offer Count'), | await screen.findByText('All 2 tokens with offers loaded'), | ||||
| ).toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
| // On load, a switch indicates that the OrderBooks are sorted by tokenId | // Token rows are links with href="/token/{tokenId}". Extract tokenId from href. | ||||
| expect( | const getTokenOrder = () => | ||||
| window.getComputedStyle(screen.getByTitle('Sort by TokenId')) | screen | ||||
| .backgroundColor, | .getAllByRole('link', { name: /icon for/ }) | ||||
| ).toBe('rgb(42, 46, 46)'); | .map( | ||||
| el => | |||||
| el | |||||
| .getAttribute('href') | |||||
| ?.match(/\/token\/([^/#]+)/)?.[1] ?? '', | |||||
| ); | |||||
| // On load, OrderBooks are sorted by token id | // On load, Your Listings shows tokens sorted by token id | ||||
| // Bull tokenId starts with 01d...; Cachet with aed...; so we expect Bull to be first | // Bull tokenId starts with 01d...; Cachet with aed...; so we expect Bull to be first | ||||
| const initialOrder = screen | const initialOrder = getTokenOrder(); | ||||
| .getAllByRole('button', { name: /View larger icon for/ }) | |||||
| .map(el => el.getAttribute('title')); | |||||
| expect(initialOrder).toEqual([BULL_TOKEN_ID, CACHET_TOKEN_ID]); | expect(initialOrder).toEqual([BULL_TOKEN_ID, CACHET_TOKEN_ID]); | ||||
| // Let's sort by offer count | |||||
| await userEvent.click(screen.getByTitle('Sort by Offer Count')); | |||||
| // Now we expect to see CACHET first, since there are 2 CACHET offers and 1 Bull offer | |||||
| const sortedOrder = screen | |||||
| .getAllByRole('button', { name: /View larger icon for/ }) | |||||
| .map(el => el.getAttribute('title')); | |||||
| expect(sortedOrder).toEqual([CACHET_TOKEN_ID, BULL_TOKEN_ID]); | |||||
| // We can revert to sorting by tokenId | |||||
| await userEvent.click(screen.getByTitle('Sort by TokenId')); | |||||
| // Now we are back to the initial ordering | |||||
| const initialOrderAgain = screen | |||||
| .getAllByRole('button', { name: /View larger icon for/ }) | |||||
| .map(el => el.getAttribute('title')); | |||||
| expect(initialOrderAgain).toEqual([BULL_TOKEN_ID, CACHET_TOKEN_ID]); | |||||
| // Wait for element to get token info and load | // Wait for element to get token info and load | ||||
| expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); | expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); | ||||
| // We have an offer | |||||
| expect(screen.getByText('Token Offers')).toBeInTheDocument(); | expect(screen.getByText('Token Offers')).toBeInTheDocument(); | ||||
| // Wait for the token to be rendered (either as a whitelisted token or after loading all offers) | // In the new design, offer details live on the token page. Click Bull to open OrderBook. | ||||
| expect(await screen.findByText('Token Offers')).toBeInTheDocument(); | await userEvent.click( | ||||
| screen.getByRole('link', { name: /icon for.*Bull.*BULL/ }), | |||||
| // Wait for the Agora component to finish its initialization and load the whitelisted tokens | |||||
| await waitFor(() => | |||||
| expect( | |||||
| screen.queryByTitle('Loading active offers'), | |||||
| ).not.toBeInTheDocument(), | |||||
| ); | ); | ||||
| // We see all token names and tickers above their PartialOffers | |||||
| expect( | |||||
| ( | |||||
| await screen.findAllByText((content, _element) => | |||||
| content.includes('Cachet'), | |||||
| ) | |||||
| ).length, | |||||
| ).toBeGreaterThan(0); | |||||
| expect( | |||||
| ( | |||||
| await screen.findAllByText((content, _element) => | |||||
| content.includes('CACHET'), | |||||
| ) | |||||
| ).length, | |||||
| ).toBeGreaterThan(0); | |||||
| expect( | |||||
| ( | |||||
| await screen.findAllByText((content, _element) => | |||||
| content.includes('Bull'), | |||||
| ) | |||||
| ).length, | |||||
| ).toBeGreaterThan(0); | |||||
| expect( | |||||
| ( | |||||
| await screen.findAllByText((content, _element) => | |||||
| content.includes('BULL'), | |||||
| ) | |||||
| ).length, | |||||
| ).toBeGreaterThan(0); | |||||
| // For BULL, there is only one offer, so that offer is the spot price | // For BULL, there is only one offer, so that offer is the spot price | ||||
| const BULL_SPOT_MIN_QTY = '8'; | const BULL_SPOT_MIN_QTY = '8'; | ||||
| const BULL_SPOT_PRICE_MIN_BUY = '400.42k XEC'; | const BULL_SPOT_PRICE_MIN_BUY = '400.42k XEC'; | ||||
| const BULL_SPOT_PRICE_FIAT_MIN_BUY = '$12.01 USD'; | const BULL_SPOT_PRICE_FIAT_MIN_BUY = '$12.01 USD'; | ||||
| // We await this as the component will load and render token info before | // Wait for OrderBook to load on the token page | ||||
| // the offers have finished loading | |||||
| expect( | expect( | ||||
| ( | ( | ||||
| await screen.findAllByText((content, _element) => | await screen.findAllByText((content, _element) => | ||||
| content.includes(`${BULL_SPOT_MIN_QTY} BULL`), | content.includes(`${BULL_SPOT_MIN_QTY} BULL`), | ||||
| ) | ) | ||||
| ).length, | ).length, | ||||
| ).toBeGreaterThan(0); | ).toBeGreaterThan(0); | ||||
| expect(screen.getByText(BULL_SPOT_PRICE_MIN_BUY)).toBeInTheDocument(); | |||||
| expect( | expect( | ||||
| screen.queryByText(BULL_SPOT_PRICE_FIAT_MIN_BUY), | await screen.findByText(BULL_SPOT_PRICE_MIN_BUY), | ||||
| ).not.toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
| // For tokens with multiple partial offers available, the lowest-priced | |||||
| // offer is selected by default ("spot price") | |||||
| const CACHET_SPOT_MIN_QTY = '.20'; | |||||
| const CACHET_SPOT_PRICE_MIN_BUY = '240.64 XEC'; | |||||
| const CACHET_SPOT_PRICE_FIAT_MIN_BUY = '$0.0072 USD'; | |||||
| // Quantities are not displayed until they load, so we await | |||||
| expect( | |||||
| ( | |||||
| await screen.findAllByText((content, _element) => | |||||
| content.includes(`${CACHET_SPOT_MIN_QTY} CACHET`), | |||||
| ) | |||||
| ).length, | |||||
| ).toBeGreaterThan(0); | |||||
| expect(screen.getByText(CACHET_SPOT_PRICE_MIN_BUY)).toBeInTheDocument(); | |||||
| expect( | expect( | ||||
| screen.queryByText(CACHET_SPOT_PRICE_FIAT_MIN_BUY), | screen.queryByText(BULL_SPOT_PRICE_FIAT_MIN_BUY), | ||||
| ).not.toBeInTheDocument(); | ).not.toBeInTheDocument(); | ||||
| // Because both spot offers were created by the active Alpha wallet, | // Bull has one offer, created by Alpha, so we see one cancel button | ||||
| // we see two cancel buttons | |||||
| expect( | expect( | ||||
| screen.getAllByRole('button', { name: 'Cancel your offer' })[1], | screen.getByRole('button', { name: 'Cancel your offer' }), | ||||
| ).toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
| // Navigate back to Agora | |||||
| await userEvent.click(screen.getByRole('button', { name: 'Agora' })); | |||||
| // Change wallets using the dropdown menu at the top of the screen | // Change wallets using the dropdown menu at the top of the screen | ||||
| // NB you cannot have the Date() function mocked if you want to test changing wallets | // NB you cannot have the Date() function mocked if you want to test changing wallets | ||||
| await userEvent.selectOptions( | await userEvent.selectOptions( | ||||
| screen.getByTestId('wallet-select'), | screen.getByTestId('wallet-select'), | ||||
| screen.getByText('Agora Partial Beta'), | screen.getByText('Agora Partial Beta'), | ||||
| ); | ); | ||||
| await waitFor( | await waitFor( | ||||
| () => | () => | ||||
| expect(screen.queryByTitle('Loading')).not.toBeInTheDocument(), | expect(screen.queryByTitle('Loading')).not.toBeInTheDocument(), | ||||
| // This can take some time | // This can take some time | ||||
| // May need to adjust if experience flakiness | // May need to adjust if experience flakiness | ||||
| { timeout: 3000 }, | { timeout: 3000 }, | ||||
| ); | ); | ||||
| expect(await screen.findByText('100,000.00 XEC')).toBeInTheDocument(); | expect(await screen.findByText('100,000.00 XEC')).toBeInTheDocument(); | ||||
| // Wait for tokens to re-load (triggered by wallet change) | // Wait for tokens to re-load (triggered by wallet change) | ||||
| await waitFor(() => | await waitFor(() => | ||||
| expect( | expect( | ||||
| screen.queryByTitle('Loading active offers'), | screen.queryByTitle('Loading active offers'), | ||||
| ).not.toBeInTheDocument(), | ).not.toBeInTheDocument(), | ||||
| ); | ); | ||||
| // Wait for active offers to load | |||||
| expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); | expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); | ||||
| // Switching wallets triggers a refresh of the offers | // With Beta: Your Listings has Cachet only, Token Offers has Bull. | ||||
| expect( | // Click Cachet to open its OrderBook. | ||||
| await screen.findByRole('button', { name: 'Buy CACHET' }), | await userEvent.click( | ||||
| ).toBeInTheDocument(); | screen.getByRole('link', { name: /icon for.*Cachet.*CACHET/ }), | ||||
| // Hit the switch to show listings created by the active wallet (now Beta) | |||||
| const toggleAllVsMyOffersSwitch = screen.getByTitle( | |||||
| 'Toggle Active Offers', | |||||
| ); | ); | ||||
| await userEvent.click(toggleAllVsMyOffersSwitch); | |||||
| // we see only the beta-created Cachet offer | |||||
| expect( | |||||
| ( | |||||
| await screen.findAllByText((content, _element) => | |||||
| content.includes('Cachet'), | |||||
| ) | |||||
| ).length, | |||||
| ).toBeGreaterThan(0); | |||||
| expect( | |||||
| ( | |||||
| await screen.findAllByText((content, _element) => | |||||
| content.includes('CACHET'), | |||||
| ) | |||||
| ).length, | |||||
| ).toBeGreaterThan(0); | |||||
| // We do not see any offers for Bull, this was created by alpha | const CACHET_SPOT_MIN_QTY = '.20'; | ||||
| expect( | |||||
| screen.queryAllByText((content, _element) => | |||||
| content.includes('Bull'), | |||||
| ), | |||||
| ).toHaveLength(0); | |||||
| expect( | |||||
| screen.queryAllByText((content, _element) => | |||||
| content.includes('BULL'), | |||||
| ), | |||||
| ).toHaveLength(0); | |||||
| // Note that we only see orderbooks that we have offers for | |||||
| // But we see all offers for these orderbooks | |||||
| // The spot price offer is rendered by default | // We see all offers for Cachet. The spot price (Alpha's offer) is default. | ||||
| // This happens to not be our offer | |||||
| expect( | expect( | ||||
| ( | ( | ||||
| await screen.findAllByText((content, _element) => | await screen.findAllByText((content, _element) => | ||||
| content.includes(`${CACHET_SPOT_MIN_QTY} CACHET`), | content.includes(`${CACHET_SPOT_MIN_QTY} CACHET`), | ||||
| ) | ) | ||||
| ).length, | ).length, | ||||
| ).toBeGreaterThan(0); | ).toBeGreaterThan(0); | ||||
| // We can buy this offer from the Manage screen | |||||
| expect( | expect( | ||||
| screen.getByRole('button', { name: 'Buy CACHET' }), | screen.getByRole('button', { name: 'Buy CACHET' }), | ||||
| ).toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
| // Select our offer | // Select our offer (Beta's) | ||||
| await userEvent.click(screen.getByText('12,000.66 XEC')); | await userEvent.click(screen.getByText('12,000.66 XEC')); | ||||
| // Now we can only cancel our offer | // Now we can only cancel our offer | ||||
| expect( | expect( | ||||
| screen.getByRole('button', { name: 'Cancel your offer' }), | screen.getByRole('button', { name: 'Cancel your offer' }), | ||||
| ).toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
| expect( | expect( | ||||
| screen.queryByRole('button', { name: 'Buy CACHET' }), | screen.queryByRole('button', { name: 'Buy CACHET' }), | ||||
| ).not.toBeInTheDocument(); | ).not.toBeInTheDocument(); | ||||
| // OK go back to all offers | // Navigate back to Agora, then switch to first wallet | ||||
| await userEvent.click(toggleAllVsMyOffersSwitch); | await userEvent.click(screen.getByRole('button', { name: 'Agora' })); | ||||
| // Nice but let's go back to the first wallet | |||||
| // Change wallets using the dropdown menu at the top of the screen | |||||
| // NB you cannot have the Date() function mocked if you want to test changing wallets | |||||
| await userEvent.selectOptions( | await userEvent.selectOptions( | ||||
| screen.getByTestId('wallet-select'), | screen.getByTestId('wallet-select'), | ||||
| screen.getByText('Agora Partial Alpha'), | screen.getByText('Agora Partial Alpha'), | ||||
| ); | ); | ||||
| await waitFor( | await waitFor( | ||||
| () => | () => | ||||
| expect(screen.queryByTitle('Loading')).not.toBeInTheDocument(), | expect(screen.queryByTitle('Loading')).not.toBeInTheDocument(), | ||||
| // This can take some time | |||||
| // May need to adjust if experience flakiness | |||||
| { timeout: 3000 }, | { timeout: 3000 }, | ||||
| ); | ); | ||||
| expect(await screen.findByText('4,200.00 XEC')).toBeInTheDocument(); | expect(await screen.findByText('4,200.00 XEC')).toBeInTheDocument(); | ||||
| // Wait for tokens to re-load (triggered by wallet change) | |||||
| await waitFor(() => | await waitFor(() => | ||||
| expect( | expect( | ||||
| screen.queryByTitle('Loading active offers'), | screen.queryByTitle('Loading active offers'), | ||||
| ).not.toBeInTheDocument(), | ).not.toBeInTheDocument(), | ||||
| ); | ); | ||||
| expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); | expect(await screen.findByTitle('Active Offers')).toBeInTheDocument(); | ||||
| // Now we see cancel buttons again | // Open Bull's OrderBook to cancel our offer | ||||
| expect( | await userEvent.click( | ||||
| ( | screen.getByRole('link', { name: /icon for.*Bull.*BULL/ }), | ||||
| await screen.findAllByRole('button', { | ); | ||||
| name: 'Cancel your offer', | |||||
| }) | |||||
| )[1], | |||||
| ).toBeInTheDocument(); | |||||
| // If we select the offer created by the Beta wallet, we see a buy button | |||||
| await userEvent.click(screen.getByText('12,000.66 XEC')); | |||||
| // We also see updates to the rendered spot details | |||||
| const UPDATED_CACHET_SPOT_MIN_QTY = '.30'; | |||||
| const UPDATED_CACHET_SPOT_PRICE_MIN_BUY = '3.6k XEC'; | |||||
| const UPDATED_CACHET_SPOT_PRICE_FIAT_MIN_BUY = '$0.1081 USD'; | |||||
| expect( | |||||
| ( | |||||
| await screen.findAllByText((content, _element) => | |||||
| content.includes(`${UPDATED_CACHET_SPOT_MIN_QTY} CACHET`), | |||||
| ) | |||||
| ).length, | |||||
| ).toBeGreaterThan(0); | |||||
| expect( | |||||
| ( | |||||
| await screen.findAllByText((content, _element) => | |||||
| content.includes(UPDATED_CACHET_SPOT_PRICE_MIN_BUY), | |||||
| ) | |||||
| ).length, | |||||
| ).toBeGreaterThan(0); | |||||
| expect( | |||||
| screen.queryAllByText((content, _element) => | |||||
| content.includes(UPDATED_CACHET_SPOT_PRICE_FIAT_MIN_BUY), | |||||
| ), | |||||
| ).toHaveLength(0); | |||||
| // We see our Bull offer with cancel button (wait for OrderBook to load) | |||||
| expect( | expect( | ||||
| screen.getByRole('button', { name: 'Buy CACHET' }), | await screen.findByRole('button', { name: 'Cancel your offer' }), | ||||
| ).toBeInTheDocument(); | ).toBeInTheDocument(); | ||||
| // Let's cancel the BULL offer | // Let's cancel the BULL offer | ||||
| await userEvent.click( | await userEvent.click( | ||||
| screen.getByRole('button', { name: 'Cancel your offer' }), | screen.getByRole('button', { name: 'Cancel your offer' }), | ||||
| ); | ); | ||||
| // We see a confirmation modal | // We see a confirmation modal | ||||
| Show All 16 Lines | |||||