diff --git a/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx b/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx index b5853281d5..f6d1bf1381 100644 --- a/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx +++ b/src/components/EnterpriseApp/EnterpriseAppRoutes.jsx @@ -14,6 +14,7 @@ import { PlotlyAnalyticsPage } from '../PlotlyAnalytics'; import { ROUTE_NAMES } from './data/constants'; import BulkEnrollmentResultsDownloadPage from '../BulkEnrollmentResultsDownloadPage'; import LearnerCreditManagement from '../learner-credit-management'; +import BudgetDetailPage from '../learner-credit-management/BudgetDetailPage'; import { EnterpriseSubsidiesContext } from '../EnterpriseSubsidiesContext'; import ContentHighlights from '../ContentHighlights'; @@ -105,6 +106,12 @@ const EnterpriseAppRoutes = ({ /> )} + + {enableContentHighlightsPage && ( mockStore(store); +const enterpriseId = 'test-enterprise'; +const enterpriseUUID = '1234'; +const initialStore = { + portalConfiguration: { + enterpriseId, + enterpriseSlug: enterpriseId, + + }, +}; +const store = getMockStore({ ...initialStore }); + +const mockEnterpriseOfferId = '123'; + +const mockOfferDisplayName = 'Test Enterprise Offer'; +const mockOfferSummary = { + totalFunds: 5000, + redeemedFunds: 200, + remainingFunds: 4800, + percentUtilized: 0.04, + offerType: EXEC_ED_OFFER_TYPE, +}; + +const defaultEnterpriseSubsidiesContextValue = { + isLoading: false, +}; + +const BudgetDetailPageWrapper = ({ + enterpriseSubsidiesContextValue = defaultEnterpriseSubsidiesContextValue, + ...rest +}) => ( + + + + + + + + + + +); + +describe('', () => { + describe('with enterprise offer', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('displays table on clicking view budget', async () => { + const mockOffer = { + id: mockEnterpriseOfferId, + name: mockOfferDisplayName, + start: '2022-01-01', + end: '2023-01-01', + }; + useOfferSummary.mockReturnValue({ + isLoading: false, + offerSummary: mockOfferSummary, + }); + useOfferRedemptions.mockReturnValue({ + isLoading: false, + offerRedemptions: { + itemCount: 0, + pageCount: 0, + results: [], + }, + fetchOfferRedemptions: jest.fn(), + }); + render(); + expect(screen.getByText('Learner Credit Budget Detail')); + expect(screen.getByText('Overview')); + expect(screen.getByText('No results found')); + }); + }); +}); diff --git a/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js b/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js index adf8580b52..2a4fe3496c 100644 --- a/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js +++ b/src/components/EnterpriseSubsidiesContext/data/tests/hooks.test.js @@ -51,6 +51,7 @@ describe('useEnterpriseOffers', () => { start: '2021-05-15T19:56:09Z', end: '2100-05-15T19:56:09Z', isCurrent: true, + source: 'ecommerceApi', }]; SubsidyApiService.getSubsidyByCustomerUUID.mockResolvedValueOnce({ @@ -80,25 +81,35 @@ describe('useEnterpriseOffers', () => { }); it('should fetch enterprise offers for the enterprise when data available in enterprise-subsidy', async () => { - const mockOffers = [ + const mockEnterpriseSubsidyResponse = [ { - id: 'offer-id', - name: 'offer-name', - start: '2021-05-15T19:56:09Z', - end: '2100-05-15T19:56:09Z', - isCurrent: true, + uuid: 'offer-id', + title: 'offer-name', + activeDatetime: '2021-05-15T19:56:09Z', + expirationDatetime: '2100-05-15T19:56:09Z', + isActive: true, }, ]; - const mockSubsidyServiceResponse = [{ - uuid: 'offer-id', - title: 'offer-name', - active_datetime: '2021-05-15T19:56:09Z', - expiration_datetime: '2100-05-15T19:56:09Z', - is_active: true, - }]; + + const mockEcommerceResponse = [ + { + id: 'uuid', + display_name: 'offer-name', + start_datetime: '2021-05-15T19:56:09Z', + end_datetime: '2100-05-15T19:56:09Z', + is_current: true, + }, + ]; + SubsidyApiService.getSubsidyByCustomerUUID.mockResolvedValueOnce({ data: { - results: mockSubsidyServiceResponse, + results: mockEnterpriseSubsidyResponse, + }, + }); + + EcommerceApiService.fetchEnterpriseOffers.mockResolvedValueOnce({ + data: { + results: mockEcommerceResponse, }, }); @@ -113,36 +124,39 @@ describe('useEnterpriseOffers', () => { TEST_ENTERPRISE_UUID, { subsidyType: 'learner_credit' }, ); + + const expectedOffers = [ + { + id: 'offer-id', + name: 'offer-name', + start: '2021-05-15T19:56:09Z', + end: '2100-05-15T19:56:09Z', + isCurrent: true, + source: 'subsidyApi', + }, + { + id: 'uuid', + name: 'offer-name', + start: '2021-05-15T19:56:09Z', + end: '2100-05-15T19:56:09Z', + isCurrent: true, + source: 'ecommerceApi', + }, + ]; + expect(result.current).toEqual({ - offers: mockOffers, + offers: expectedOffers, isLoading: false, canManageLearnerCredit: true, }); }); it('should set canManageLearnerCredit to false if active enterprise offer or subsidy not found', async () => { - const mockOffers = [{ subsidyUuid: 'offer-1' }, { subsidyUuid: 'offer-2' }]; - const mockSubsidyServiceResponse = [ - { - uuid: 'offer-1', - title: 'offer-name', - active_datetime: '2005-05-15T19:56:09Z', - expiration_datetime: '2006-05-15T19:56:09Z', - is_active: false, - }, - { - uuid: 'offer-2', - title: 'offer-name-2', - active_datetime: '2006-05-15T19:56:09Z', - expiration_datetime: '2007-05-15T19:56:09Z', - is_active: false, - }, - ]; - const mockOfferData = []; + const mockSubsidyServiceResponse = []; EcommerceApiService.fetchEnterpriseOffers.mockResolvedValueOnce({ data: { - results: mockOffers, + results: [], }, }); SubsidyApiService.getSubsidyByCustomerUUID.mockResolvedValueOnce({ @@ -162,15 +176,22 @@ describe('useEnterpriseOffers', () => { TEST_ENTERPRISE_UUID, { subsidyType: 'learner_credit' }, ); + + const hasActiveOffersOrSubsidies = mockSubsidyServiceResponse.some(offer => offer.is_active); + let canManageLearnerCredit = false; + + if (hasActiveOffersOrSubsidies) { + canManageLearnerCredit = true; + } + expect(result.current).toEqual({ - offers: mockOfferData, + offers: [], isLoading: false, - canManageLearnerCredit: false, + canManageLearnerCredit, }); }); it('should return the active enterprise offer or subsidy when multiple available', async () => { - const mockOffers = [{ subsidyUuid: 'offer-1' }, { subsidyUuid: 'offer-2' }]; const mockSubsidyServiceResponse = [ { uuid: 'offer-1', @@ -188,18 +209,27 @@ describe('useEnterpriseOffers', () => { }, ]; const mockOfferData = [ + { + id: 'offer-1', + name: 'offer-name', + start: '2005-05-15T19:56:09Z', + end: '2006-05-15T19:56:09Z', + isCurrent: false, + source: 'subsidyApi', + }, { id: 'offer-2', name: 'offer-name-2', start: '2006-05-15T19:56:09Z', end: '2099-05-15T19:56:09Z', isCurrent: true, + source: 'subsidyApi', }, ]; EcommerceApiService.fetchEnterpriseOffers.mockResolvedValueOnce({ data: { - results: mockOffers, + results: [], }, }); SubsidyApiService.getSubsidyByCustomerUUID.mockResolvedValueOnce({ diff --git a/src/components/learner-credit-management/BudgetCard-V2.jsx b/src/components/learner-credit-management/BudgetCard-V2.jsx index b39b9297d9..09a5c8cdf5 100644 --- a/src/components/learner-credit-management/BudgetCard-V2.jsx +++ b/src/components/learner-credit-management/BudgetCard-V2.jsx @@ -1,25 +1,18 @@ -import React, { useState } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import dayjs from 'dayjs'; import { - Card, - Button, Stack, - Row, - Col, - Breadcrumb, } from '@edx/paragon'; -import { useOfferRedemptions, useOfferSummary } from './data/hooks'; -import LearnerCreditAggregateCards from './LearnerCreditAggregateCards'; -import LearnerCreditAllocationTable from './LearnerCreditAllocationTable'; -import { ROUTE_NAMES } from '../EnterpriseApp/data/constants'; +import { useOfferSummary } from './data/hooks'; +import SubBudgetCard from './Budgetcard-V3'; const BudgetCard = ({ offer, enterpriseUUID, enterpriseSlug, - enableLearnerPortal, + offerType, }) => { const { start, @@ -31,123 +24,37 @@ const BudgetCard = ({ offerSummary, } = useOfferSummary(enterpriseUUID, offer); - const { - isLoading: isLoadingOfferRedemptions, - offerRedemptions, - fetchOfferRedemptions, - } = useOfferRedemptions(enterpriseUUID, offer?.id); - const [detailPage, setDetailPage] = useState(false); - const [activeLabel, setActiveLabel] = useState(''); - const links = [ - { label: 'Budgets', url: `/${enterpriseSlug}/admin/${ROUTE_NAMES.learnerCredit}` }, - ]; const formattedStartDate = dayjs(start).format('MMMM D, YYYY'); const formattedExpirationDate = dayjs(end).format('MMMM D, YYYY'); - const navigateToBudgetRedemptions = (budgetType) => { - setDetailPage(true); - links.push({ label: budgetType, url: `/${enterpriseSlug}/admin/learner-credit` }); - setActiveLabel(budgetType); - }; - - const renderActions = (budgetType) => ( - - ); - - const renderCardHeader = (budgetType) => { - const subtitle = ( -
- - {formattedStartDate} - {formattedExpirationDate} - -
- ); - - return ( - - {renderActions(budgetType)} - - )} - /> - ); - }; - - const renderCardSection = (available, spent) => ( - - - - Available - {available} - - - Spent - {spent} - - - - ); - - const renderCardAggregate = () => ( -
- -
- ); return ( - - - - - - - {!detailPage - ? ( - <> - {renderCardAggregate()} -

Budgets

- - - - {renderCardHeader('Overview')} - {renderCardSection(offerSummary?.remainingFunds, offerSummary?.redeemedFunds)} - - - - - ) - : ( - - )} + + <> +

Budgets

+ {!isLoadingOfferSummary + && offerType === 'ecommerceApi' + ? ( + + ) + : offerSummary?.budgetsSumary.map((budget) => ( + + ))} +
); }; @@ -161,7 +68,7 @@ BudgetCard.propTypes = { }).isRequired, enterpriseUUID: PropTypes.string.isRequired, enterpriseSlug: PropTypes.string.isRequired, - enableLearnerPortal: PropTypes.bool.isRequired, + offerType: PropTypes.string.isRequired, }; export default BudgetCard; diff --git a/src/components/learner-credit-management/BudgetDetailPage.jsx b/src/components/learner-credit-management/BudgetDetailPage.jsx new file mode 100644 index 0000000000..1a97c72363 --- /dev/null +++ b/src/components/learner-credit-management/BudgetDetailPage.jsx @@ -0,0 +1,79 @@ +import React, { useContext } from 'react'; +import PropTypes from 'prop-types'; +import { + Row, + Col, + Breadcrumb, +} from '@edx/paragon'; +import { connect } from 'react-redux'; +import { Helmet } from 'react-helmet'; +import { useParams, Link } from 'react-router-dom'; +import Hero from '../Hero'; + +import LoadingMessage from '../LoadingMessage'; +import { EnterpriseSubsidiesContext } from '../EnterpriseSubsidiesContext'; + +import LearnerCreditAllocationTable from './LearnerCreditAllocationTable'; +import { useOfferRedemptions } from './data/hooks'; +import { isUUID } from './data/utils'; +import { ROUTE_NAMES } from '../EnterpriseApp/data/constants'; + +const PAGE_TITLE = 'Learner Credit Budget Detail'; + +const BudgetDetailPage = ({ + enterpriseUUID, + enterpriseSlug, +}) => { + const { id } = useParams(); + const offerId = isUUID(id) ? null : id; + const budgetId = isUUID(id) ? id : null; + + const { isLoading } = useContext(EnterpriseSubsidiesContext); + const { + isLoading: isLoadingOfferRedemptions, + offerRedemptions, + fetchOfferRedemptions, + } = useOfferRedemptions(enterpriseUUID, offerId, budgetId); + if (isLoading) { + return ; + } + const links = [ + { label: 'Budgets', to: `/${enterpriseSlug}/admin/${ROUTE_NAMES.learnerCredit}` }, + ]; + return ( + <> + + + + + + + + + + ); +}; + +const mapStateToProps = state => ({ + enterpriseUUID: state.portalConfiguration.enterpriseId, + enterpriseSlug: state.portalConfiguration.enterpriseSlug, +}); + +BudgetDetailPage.propTypes = { + enterpriseUUID: PropTypes.string.isRequired, + enterpriseSlug: PropTypes.string.isRequired, +}; + +export default connect(mapStateToProps)(BudgetDetailPage); diff --git a/src/components/learner-credit-management/Budgetcard-V3.jsx b/src/components/learner-credit-management/Budgetcard-V3.jsx new file mode 100644 index 0000000000..97c275b4e7 --- /dev/null +++ b/src/components/learner-credit-management/Budgetcard-V3.jsx @@ -0,0 +1,99 @@ +import { Link } from 'react-router-dom'; +import PropTypes from 'prop-types'; +import dayjs from 'dayjs'; +import { + Card, + Button, + Stack, + Row, + Col, +} from '@edx/paragon'; + +import { ROUTE_NAMES } from '../EnterpriseApp/data/constants'; + +const SubBudgetCard = ({ + id, + start, + end, + available, + spent, + enterpriseSlug, +}) => { + const formattedStartDate = dayjs(start).format('MMMM D, YYYY'); + const formattedExpirationDate = dayjs(end).format('MMMM D, YYYY'); + + const renderActions = (id) => ( + + ); + + const renderCardHeader = (budgetType, id) => { + const subtitle = ( +
+ + {formattedStartDate} - {formattedExpirationDate} + +
+ ); + + return ( + + {renderActions(id)} + + )} + /> + ); + }; + + const renderCardSection = (available, spent) => ( + + + + Available + {available} + + + Spent + {spent} + + + + ); + + return ( + + + + {renderCardHeader('Overview', id)} + {renderCardSection(available, spent)} + + + + ); +}; + +SubBudgetCard.propTypes = { + enterpriseSlug: PropTypes.string.isRequired, + id: PropTypes.string, + start: PropTypes.string, + end: PropTypes.string, + spent: PropTypes.number, + available: PropTypes.number, + +}; + +export default SubBudgetCard; diff --git a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx index 4c3da2d0ce..9dc6a227c1 100644 --- a/src/components/learner-credit-management/MultipleBudgetsPicker.jsx +++ b/src/components/learner-credit-management/MultipleBudgetsPicker.jsx @@ -24,6 +24,7 @@ const MultipleBudgetsPicker = ({ enterpriseUUID={enterpriseUUID} enterpriseSlug={enterpriseSlug} enableLearnerPortal={enableLearnerPortal} + offerType={offer.source} /> ))} diff --git a/src/components/learner-credit-management/data/hooks.js b/src/components/learner-credit-management/data/hooks.js index 585970c35e..5e107e9428 100644 --- a/src/components/learner-credit-management/data/hooks.js +++ b/src/components/learner-credit-management/data/hooks.js @@ -74,7 +74,7 @@ const applyFiltersToOptions = (filters, options) => { } }; -export const useOfferRedemptions = (enterpriseUUID, offerId) => { +export const useOfferRedemptions = (enterpriseUUID, offerId= null, budgetId = null) => { const shouldTrackFetchEvents = useRef(false); const [isLoading, setIsLoading] = useState(true); const [offerRedemptions, setOfferRedemptions] = useState({ @@ -90,9 +90,14 @@ export const useOfferRedemptions = (enterpriseUUID, offerId) => { const options = { page: args.pageIndex + 1, // `DataTable` uses zero-indexed array pageSize: args.pageSize, - offerId, ignoreNullCourseListPrice: true, }; + if (budgetId !== null) { + options.budgetId = budgetId; + } + if (offerId !== null) { + options.offerId = offerId; + } if (args.sortBy?.length > 0) { applySortByToOptions(args.sortBy, options); } @@ -129,10 +134,10 @@ export const useOfferRedemptions = (enterpriseUUID, offerId) => { setIsLoading(false); } }; - if (offerId) { + if (offerId || budgetId) { fetch(); } - }, [enterpriseUUID, offerId, shouldTrackFetchEvents]); + }, [enterpriseUUID, offerId, budgetId, shouldTrackFetchEvents]); const debouncedFetchOfferRedemptions = useMemo(() => debounce(fetchOfferRedemptions, 300), [fetchOfferRedemptions]); diff --git a/src/components/learner-credit-management/data/tests/hooks.test.js b/src/components/learner-credit-management/data/tests/hooks.test.js index 8ab61bce2f..f70f533d4d 100644 --- a/src/components/learner-credit-management/data/tests/hooks.test.js +++ b/src/components/learner-credit-management/data/tests/hooks.test.js @@ -72,6 +72,9 @@ describe('useOfferSummary', () => { redeemedFundsOcm: NaN, remainingFunds: 4800, percentUtilized: 0.04, + offerId: 1, + budgetsSumary: [], + offerType: undefined, }; expect(result.current).toEqual({ offerSummary: expectedResult, diff --git a/src/components/learner-credit-management/data/tests/utils.test.js b/src/components/learner-credit-management/data/tests/utils.test.js index 33902d40fe..b24abe27d4 100644 --- a/src/components/learner-credit-management/data/tests/utils.test.js +++ b/src/components/learner-credit-management/data/tests/utils.test.js @@ -23,6 +23,8 @@ describe('transformOfferSummary', () => { remainingFunds: 0.0, percentUtilized: 1.0, offerType: EXEC_ED_OFFER_TYPE, + budgetsSumary: [], + offerId: undefined, }); }); @@ -33,6 +35,8 @@ describe('transformOfferSummary', () => { remainingBalance: null, percentOfOfferSpent: null, offerType: 'Site', + offerId: '123', + budgetsSumary: [], }; expect(transformOfferSummary(offerSummary)).toEqual({ @@ -41,6 +45,10 @@ describe('transformOfferSummary', () => { remainingFunds: null, percentUtilized: null, offerType: 'Site', + redeemedFundsExecEd: undefined, + redeemedFundsOcm: undefined, + offerId: '123', + budgetsSumary: [], }); }); }); diff --git a/src/components/learner-credit-management/data/utils.js b/src/components/learner-credit-management/data/utils.js index 65524c1346..1c7a52c459 100644 --- a/src/components/learner-credit-management/data/utils.js +++ b/src/components/learner-credit-management/data/utils.js @@ -12,7 +12,23 @@ import { */ export const transformOfferSummary = (offerSummary) => { if (!offerSummary) { return null; } + let budgetsSumary = [] + if (offerSummary?.budgets) { + const budgets = offerSummary?.budgets + for (let i = 0; i < budgets.length; i++) { + let redeemedFunds = budgets[i].amountOfPolicySpent && parseFloat(budgets[i].amountOfPolicySpent); + let remainingFunds = budgets[i].remainingBalance && parseFloat(budgets[i].remainingBalance); + // Create an object with key-value pairs + const budgetEntry = { + redeemedFunds, + remainingFunds, + ...budgets[i] + }; + budgetsSumary.push(budgetEntry); + } + } + const totalFunds = offerSummary.maxDiscount && parseFloat(offerSummary.maxDiscount); let redeemedFunds = offerSummary.amountOfOfferSpent && parseFloat(offerSummary.amountOfOfferSpent); let redeemedFundsOcm = offerSummary.amountOfferSpentOcm && parseFloat(offerSummary.amountOfferSpentOcm); @@ -38,7 +54,7 @@ export const transformOfferSummary = (offerSummary) => { percentUtilized = Math.min(percentUtilized, 1.0); } const { offerType } = offerSummary; - + const { offerId } = offerSummary; return { totalFunds, redeemedFunds, @@ -47,6 +63,8 @@ export const transformOfferSummary = (offerSummary) => { remainingFunds, percentUtilized, offerType, + offerId, + budgetsSumary, }; }; @@ -91,3 +109,8 @@ export const getProgressBarVariant = ({ percentUtilized, remainingFunds }) => { } return variant; }; + +// Utility function to check if the ID is a UUID +export const isUUID = (id) => { + return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(id); +} diff --git a/src/components/learner-credit-management/tests/BudgetCard.test.jsx b/src/components/learner-credit-management/tests/BudgetCard.test.jsx index 7d8f349bda..dcff64ce3e 100644 --- a/src/components/learner-credit-management/tests/BudgetCard.test.jsx +++ b/src/components/learner-credit-management/tests/BudgetCard.test.jsx @@ -1,21 +1,19 @@ /* eslint-disable react/prop-types */ import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; import { Provider } from 'react-redux'; import thunk from 'redux-thunk'; -import userEvent from '@testing-library/user-event'; import configureMockStore from 'redux-mock-store'; import dayjs from 'dayjs'; import { screen, render, - waitFor, } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; import { IntlProvider } from '@edx/frontend-platform/i18n'; import BudgetCard from '../BudgetCard-V2'; import { useOfferSummary, useOfferRedemptions } from '../data/hooks'; -import { EXEC_ED_OFFER_TYPE } from '../data/constants'; jest.mock('../data/hooks'); useOfferSummary.mockReturnValue({ @@ -47,20 +45,15 @@ const mockEnterpriseOfferId = '123'; const mockEnterpriseOfferEnrollmentId = 456; const mockOfferDisplayName = 'Test Enterprise Offer'; -const mockOfferSummary = { - totalFunds: 5000, - redeemedFunds: 200, - remainingFunds: 4800, - percentUtilized: 0.04, - offerType: EXEC_ED_OFFER_TYPE, -}; const BudgetCardWrapper = ({ ...rest }) => ( - - - - - + + + + + + + ); describe('', () => { @@ -88,6 +81,16 @@ describe('', () => { remainingFunds: 4800, percentUtilized: 0.04, offerType: 'Site', + budgetsSumary: [ + { + id: 123, + start: '2022-01-01', + end: '2022-01-01', + available: 200, + spent: 100, + enterpriseSlug: enterpriseId, + }, + ], }, }); useOfferRedemptions.mockReturnValue({ @@ -106,42 +109,10 @@ describe('', () => { />); expect(screen.getByText('Overview')); expect(screen.queryByText('Executive Education')).not.toBeInTheDocument(); - expect(screen.getByText(`$${mockOfferSummary.redeemedFunds.toLocaleString()}`)); const formattedString = `${dayjs(mockOffer.start).format('MMMM D, YYYY')} - ${dayjs(mockOffer.end).format('MMMM D, YYYY')}`; const elementsWithTestId = screen.getAllByTestId('offer-date'); const firstElementWithTestId = elementsWithTestId[0]; expect(firstElementWithTestId).toHaveTextContent(formattedString); }); - - it('displays table on clicking view budget', async () => { - const mockOffer = { - id: mockEnterpriseOfferId, - name: mockOfferDisplayName, - start: '2022-01-01', - end: '2023-01-01', - }; - useOfferSummary.mockReturnValue({ - isLoading: false, - offerSummary: mockOfferSummary, - }); - useOfferRedemptions.mockReturnValue({ - isLoading: false, - offerRedemptions: { - itemCount: 0, - pageCount: 0, - results: [], - }, - fetchOfferRedemptions: jest.fn(), - }); - render(); - const elementsWithTestId = screen.getAllByTestId('view-budget'); - const firstElementWithTestId = elementsWithTestId[0]; - await waitFor(() => userEvent.click(firstElementWithTestId)); - expect(screen.getByText('No results found')); - }); }); });