diff --git a/src/taxonomy/TaxonomyLayout.test.jsx b/src/taxonomy/TaxonomyLayout.test.jsx new file mode 100644 index 0000000000..924e7465e9 --- /dev/null +++ b/src/taxonomy/TaxonomyLayout.test.jsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { initializeMockApp } from '@edx/frontend-platform'; +import { AppProvider } from '@edx/frontend-platform/react'; +import { render } from '@testing-library/react'; + +import initializeStore from '../store'; +import TaxonomyLayout from './TaxonomyLayout'; + +let store; + +jest.mock('../header', () => jest.fn(() => <div data-testid="mock-header" />)); +jest.mock('@edx/frontend-component-footer', () => ({ + StudioFooter: jest.fn(() => <div data-testid="mock-footer" />), +})); +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + Outlet: jest.fn(() => <div data-testid="mock-content" />), +})); + +const RootWrapper = () => ( + <AppProvider store={store}> + <IntlProvider locale="en" messages={{}}> + <TaxonomyLayout /> + </IntlProvider> + </AppProvider> +); + +describe('<TaxonomyLayout />', async () => { + beforeEach(async () => { + initializeMockApp({ + authenticatedUser: { + userId: 3, + username: 'abc123', + administrator: true, + roles: [], + }, + }); + store = initializeStore(); + }); + + it('should render page correctly', async () => { + const { getByTestId } = render(<RootWrapper />); + expect(getByTestId('mock-header')).toBeInTheDocument(); + expect(getByTestId('mock-content')).toBeInTheDocument(); + expect(getByTestId('mock-footer')).toBeInTheDocument(); + }); +}); diff --git a/src/taxonomy/taxonomy-detail/TagListTable.test.jsx b/src/taxonomy/taxonomy-detail/TagListTable.test.jsx new file mode 100644 index 0000000000..1128deced9 --- /dev/null +++ b/src/taxonomy/taxonomy-detail/TagListTable.test.jsx @@ -0,0 +1,67 @@ +import React from 'react'; +import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n'; +import { initializeMockApp } from '@edx/frontend-platform'; +import { AppProvider } from '@edx/frontend-platform/react'; +import { render } from '@testing-library/react'; + +import { useTagListData } from '../api/hooks/api'; +import initializeStore from '../../store'; +import TagListTable from './TagListTable'; + +let store; + +jest.mock('../api/hooks/api', () => ({ + useTagListData: jest.fn(), +})); + +const RootWrapper = () => ( + <AppProvider store={store}> + <IntlProvider locale="en" messages={{}}> + <TagListTable intl={injectIntl} taxonomyId="1" /> + </IntlProvider> + </AppProvider> +); + +describe('<TagListPage />', async () => { + beforeEach(async () => { + initializeMockApp({ + authenticatedUser: { + userId: 3, + username: 'abc123', + administrator: true, + roles: [], + }, + }); + store = initializeStore(); + }); + + it('shows the spinner before the query is complete', async () => { + useTagListData.mockReturnValue({ + isLoading: true, + isFetched: false, + }); + const { getByRole } = render(<RootWrapper />); + const spinner = getByRole('status'); + expect(spinner.textContent).toEqual('loading'); + }); + + it('should render page correctly', async () => { + useTagListData.mockReturnValue({ + isSuccess: true, + isFetched: true, + isError: false, + data: { + count: 3, + numPages: 1, + results: [ + { value: 'Tag 1' }, + { value: 'Tag 2' }, + { value: 'Tag 3' }, + ], + }, + }); + const { getAllByRole } = render(<RootWrapper />); + const rows = getAllByRole('row'); + expect(rows.length).toBe(3 + 1); // 3 items plus header + }); +}); diff --git a/src/taxonomy/taxonomy-detail/TaxonomyDetailPage.jsx b/src/taxonomy/taxonomy-detail/TaxonomyDetailPage.jsx index d1a981f292..385a3f797b 100644 --- a/src/taxonomy/taxonomy-detail/TaxonomyDetailPage.jsx +++ b/src/taxonomy/taxonomy-detail/TaxonomyDetailPage.jsx @@ -28,15 +28,15 @@ const TaxonomyDetailPage = () => { const [isExportModalOpen, setIsExportModalOpen] = useState(false); - if (isError) { + if (!isFetched) { return ( - <ConnectionErrorAlert /> + <Loading /> ); } - if (!isFetched) { + if (isError || !taxonomy) { return ( - <Loading /> + <ConnectionErrorAlert /> ); } @@ -47,7 +47,7 @@ const TaxonomyDetailPage = () => { <ExportModal isOpen={isExportModalOpen} onClose={() => setIsExportModalOpen(false)} - taxonomyId={taxonomyId} + taxonomyId={taxonomy.id} taxonomyName={taxonomy.name} /> )} @@ -80,49 +80,45 @@ const TaxonomyDetailPage = () => { /> ); - if (taxonomy) { - return ( - <> - <div className="pt-4.5 pr-4.5 pl-4.5 pb-2 bg-light-100 box-shadow-down-2"> - <Container size="xl"> - <Breadcrumb - links={[ - { label: 'Taxonomies', to: '/taxonomy-list/' }, - ]} - activeLabel={taxonomy.name} - linkAs={Link} - /> - <SubHeader - title={taxonomy.name} - hideBorder - headerActions={getHeaderActions()} - /> - </Container> - </div> - <div className="bg-light-400 m-4"> - <Container size="xl"> - <Layout - lg={[{ span: 9 }, { span: 3 }]} - md={[{ span: 9 }, { span: 3 }]} - sm={[{ span: 9 }, { span: 3 }]} - xs={[{ span: 9 }, { span: 3 }]} - xl={[{ span: 9 }, { span: 3 }]} - > - <Layout.Element> - <TagListTable taxonomyId={taxonomyId} /> - </Layout.Element> - <Layout.Element> - <TaxonomyDetailSideCard taxonomy={taxonomy} /> - </Layout.Element> - </Layout> - </Container> - </div> - {renderModals()} - </> - ); - } - - return undefined; + return ( + <> + <div className="pt-4.5 pr-4.5 pl-4.5 pb-2 bg-light-100 box-shadow-down-2"> + <Container size="xl"> + <Breadcrumb + links={[ + { label: 'Taxonomies', to: '/taxonomy-list/' }, + ]} + activeLabel={taxonomy.name} + linkAs={Link} + /> + <SubHeader + title={taxonomy.name} + hideBorder + headerActions={getHeaderActions()} + /> + </Container> + </div> + <div className="bg-light-400 m-4"> + <Container size="xl"> + <Layout + lg={[{ span: 9 }, { span: 3 }]} + md={[{ span: 9 }, { span: 3 }]} + sm={[{ span: 9 }, { span: 3 }]} + xs={[{ span: 9 }, { span: 3 }]} + xl={[{ span: 9 }, { span: 3 }]} + > + <Layout.Element> + <TagListTable taxonomyId={taxonomyId} /> + </Layout.Element> + <Layout.Element> + <TaxonomyDetailSideCard taxonomy={taxonomy} /> + </Layout.Element> + </Layout> + </Container> + </div> + {renderModals()} + </> + ); }; export default TaxonomyDetailPage; diff --git a/src/taxonomy/taxonomy-detail/TaxonomyDetailPage.test.jsx b/src/taxonomy/taxonomy-detail/TaxonomyDetailPage.test.jsx new file mode 100644 index 0000000000..ab832bc9c8 --- /dev/null +++ b/src/taxonomy/taxonomy-detail/TaxonomyDetailPage.test.jsx @@ -0,0 +1,111 @@ +import React from 'react'; +import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n'; +import { initializeMockApp } from '@edx/frontend-platform'; +import { AppProvider } from '@edx/frontend-platform/react'; +import { fireEvent, render } from '@testing-library/react'; + +import { useTaxonomyDetailData } from '../api/hooks/api'; +import initializeStore from '../../store'; +import TaxonomyDetailPage from './TaxonomyDetailPage'; + +let store; + +jest.mock('../api/hooks/api', () => ({ + useTaxonomyDetailData: jest.fn(), +})); +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), // use actual for all non-hook parts + useParams: () => ({ + taxonomyId: '1', + }), +})); + +jest.mock('./TaxonomyDetailSideCard', () => jest.fn(() => <>Mock TaxonomyDetailSideCard</>)); +jest.mock('./TagListTable', () => jest.fn(() => <>Mock TagListTable</>)); + +const RootWrapper = () => ( + <AppProvider store={store}> + <IntlProvider locale="en" messages={{}}> + <TaxonomyDetailPage intl={injectIntl} /> + </IntlProvider> + </AppProvider> +); + +describe('<TaxonomyDetailPage />', async () => { + beforeEach(async () => { + initializeMockApp({ + authenticatedUser: { + userId: 3, + username: 'abc123', + administrator: true, + roles: [], + }, + }); + store = initializeStore(); + }); + + it('shows the spinner before the query is complete', async () => { + useTaxonomyDetailData.mockReturnValue({ + isFetched: false, + }); + const { getByRole } = render(<RootWrapper />); + const spinner = getByRole('status'); + expect(spinner.textContent).toEqual('Loading...'); + }); + + it('shows the connector error component if got some error', async () => { + useTaxonomyDetailData.mockReturnValue({ + isFetched: true, + isError: true, + }); + const { getByTestId } = render(<RootWrapper />); + expect(getByTestId('connectionErrorAlert')).toBeInTheDocument(); + }); + + it('should render page and page title correctly', async () => { + useTaxonomyDetailData.mockReturnValue({ + isSuccess: true, + isFetched: true, + isError: false, + data: { + id: 1, + name: 'Test taxonomy', + description: 'This is a description', + systemDefined: false, + }, + }); + const { getByRole } = render(<RootWrapper />); + expect(getByRole('heading')).toHaveTextContent('Test taxonomy'); + }); + + it('should open export modal on export menu click', () => { + useTaxonomyDetailData.mockReturnValue({ + isSuccess: true, + isFetched: true, + isError: false, + data: { + id: 1, + name: 'Test taxonomy', + description: 'This is a description', + }, + }); + + const { getByRole, getByText } = render(<RootWrapper />); + + // Modal closed + expect(() => getByText('Select format to export')).toThrow(); + + // Click on export menu + fireEvent.click(getByRole('button')); + fireEvent.click(getByText('Export')); + + // Modal opened + expect(getByText('Select format to export')).toBeInTheDocument(); + + // Click on cancel button + fireEvent.click(getByText('Cancel')); + + // Modal closed + expect(() => getByText('Select format to export')).toThrow(); + }); +});