From bc8d59b0eb8d1dafa24116770ef2763656b4ae1c Mon Sep 17 00:00:00 2001
From: Peter Kulko <93188219+PKulkoRaccoonGang@users.noreply.github.com>
Date: Thu, 21 Nov 2024 20:24:31 +0200
Subject: [PATCH] chore: [FC-0070] Some tests refactoring (#1518)
---
src/CourseAuthoringPage.test.jsx | 63 ++----
src/CourseAuthoringRoutes.test.jsx | 100 ++++-----
.../ChecklistSection.test.jsx | 6 +-
src/custom-pages/CustomPages.test.jsx | 40 +---
.../grading-scale/react-ranger.test.js | 55 +++++
.../LibraryAuthoringPage.test.tsx | 3 -
.../add-content/AddContentWorkflow.test.tsx | 2 -
.../PickLibraryContentModal.test.tsx | 2 -
.../pages/PageCard.test.jsx | 52 ++---
src/studio-home/StudioHome.test.jsx | 212 ++++++++----------
.../collapsible-state-with-action/index.jsx | 1 +
.../tabs-section/courses-tab/index.test.tsx | 14 ++
src/testUtils.tsx | 5 +
13 files changed, 258 insertions(+), 297 deletions(-)
create mode 100644 src/grading-settings/grading-scale/react-ranger.test.js
diff --git a/src/CourseAuthoringPage.test.jsx b/src/CourseAuthoringPage.test.jsx
index 4f342f826b..7640dc772d 100644
--- a/src/CourseAuthoringPage.test.jsx
+++ b/src/CourseAuthoringPage.test.jsx
@@ -1,19 +1,12 @@
-import React from 'react';
+import { getConfig } from '@edx/frontend-platform';
-import { render } from '@testing-library/react';
-
-import { getConfig, initializeMockApp } from '@edx/frontend-platform';
-import MockAdapter from 'axios-mock-adapter';
-import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
-import { AppProvider } from '@edx/frontend-platform/react';
-import { IntlProvider } from '@edx/frontend-platform/i18n';
-import initializeStore from './store';
import CourseAuthoringPage from './CourseAuthoringPage';
import PagesAndResources from './pages-and-resources/PagesAndResources';
import { executeThunk } from './utils';
import { fetchCourseApps } from './pages-and-resources/data/thunks';
import { fetchCourseDetail, fetchWaffleFlags } from './data/thunks';
import { getApiWaffleFlagsUrl } from './data/api';
+import { initializeMocks, render } from './testUtils';
const courseId = 'course-v1:edX+TestX+Test_Course';
let mockPathname = '/evilguy/';
@@ -27,16 +20,9 @@ let axiosMock;
let store;
beforeEach(async () => {
- initializeMockApp({
- authenticatedUser: {
- userId: 3,
- username: 'abc123',
- administrator: true,
- roles: [],
- },
- });
- store = initializeStore();
- axiosMock = new MockAdapter(getAuthenticatedHttpClient());
+ const mocks = initializeMocks();
+ store = mocks.reduxStore;
+ axiosMock = mocks.axiosMock;
axiosMock
.onGet(getApiWaffleFlagsUrl(courseId))
.reply(200, {});
@@ -56,13 +42,9 @@ describe('Editor Pages Load no header', () => {
mockPathname = '/editor/';
await mockStoreSuccess();
const wrapper = render(
-
-
-
-
-
-
-
+
+
+
,
);
expect(wrapper.queryByRole('status')).not.toBeInTheDocument();
@@ -71,13 +53,9 @@ describe('Editor Pages Load no header', () => {
mockPathname = '/evilguy/';
await mockStoreSuccess();
const wrapper = render(
-
-
-
-
-
-
-
+
+
+
,
);
expect(wrapper.queryByRole('status')).toBeInTheDocument();
@@ -105,14 +83,7 @@ describe('Course authoring page', () => {
};
test('renders not found page on non-existent course key', async () => {
await mockStoreNotFound();
- const wrapper = render(
-
-
-
-
-
- ,
- );
+ const wrapper = render();
expect(await wrapper.findByTestId('notFoundAlert')).toBeInTheDocument();
});
test('does not render not found page on other kinds of error', async () => {
@@ -123,13 +94,9 @@ describe('Course authoring page', () => {
// found alert is not present.
const contentTestId = 'courseAuthoringPageContent';
const wrapper = render(
-
-
-
-
-
-
-
+
+
+
,
);
expect(await wrapper.findByTestId(contentTestId)).toBeInTheDocument();
diff --git a/src/CourseAuthoringRoutes.test.jsx b/src/CourseAuthoringRoutes.test.jsx
index 790c68326c..e2233559f7 100644
--- a/src/CourseAuthoringRoutes.test.jsx
+++ b/src/CourseAuthoringRoutes.test.jsx
@@ -1,15 +1,10 @@
-import React from 'react';
-import { AppProvider } from '@edx/frontend-platform/react';
-import { initializeMockApp } from '@edx/frontend-platform';
-import { render, screen } from '@testing-library/react';
-import { MemoryRouter } from 'react-router-dom';
-import MockAdapter from 'axios-mock-adapter';
-import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import CourseAuthoringRoutes from './CourseAuthoringRoutes';
-import initializeStore from './store';
import { executeThunk } from './utils';
import { getApiWaffleFlagsUrl } from './data/api';
import { fetchWaffleFlags } from './data/thunks';
+import {
+ screen, initializeMocks, render, waitFor,
+} from './testUtils';
const courseId = 'course-v1:edX+TestX+Test_Course';
const pagesAndResourcesMockText = 'Pages And Resources';
@@ -17,7 +12,6 @@ const editorContainerMockText = 'Editor Container';
const videoSelectorContainerMockText = 'Video Selector Container';
const customPagesMockText = 'Custom Pages';
let store;
-let axiosMock;
const mockComponentFn = jest.fn();
jest.mock('react-router-dom', () => ({
@@ -57,72 +51,58 @@ jest.mock('./custom-pages/CustomPages', () => (props) => {
describe('', () => {
beforeEach(async () => {
- initializeMockApp({
- authenticatedUser: {
- userId: 3,
- username: 'abc123',
- administrator: true,
- roles: [],
- },
- });
- store = initializeStore();
- axiosMock = new MockAdapter(getAuthenticatedHttpClient());
+ const { axiosMock, reduxStore } = initializeMocks();
+ store = reduxStore;
axiosMock
.onGet(getApiWaffleFlagsUrl(courseId))
.reply(200, {});
await executeThunk(fetchWaffleFlags(courseId), store.dispatch);
});
- fit('renders the PagesAndResources component when the pages and resources route is active', () => {
+ it('renders the PagesAndResources component when the pages and resources route is active', async () => {
render(
-
-
-
-
- ,
- );
-
- expect(screen.getByText(pagesAndResourcesMockText)).toBeVisible();
- expect(mockComponentFn).toHaveBeenCalledWith(
- expect.objectContaining({
- courseId,
- }),
+ ,
+ { routerProps: { initialEntries: ['/pages-and-resources'] } },
);
+ await waitFor(() => {
+ expect(screen.getByText(pagesAndResourcesMockText)).toBeVisible();
+ expect(mockComponentFn).toHaveBeenCalledWith(
+ expect.objectContaining({
+ courseId,
+ }),
+ );
+ });
});
- it('renders the EditorContainer component when the course editor route is active', () => {
+ it('renders the EditorContainer component when the course editor route is active', async () => {
render(
-
-
-
-
- ,
- );
-
- expect(screen.queryByText(editorContainerMockText)).toBeInTheDocument();
- expect(screen.queryByText(pagesAndResourcesMockText)).not.toBeInTheDocument();
- expect(mockComponentFn).toHaveBeenCalledWith(
- expect.objectContaining({
- courseId,
- }),
+ ,
+ { routerProps: { initialEntries: ['/editor/video/block-id'] } },
);
+ await waitFor(() => {
+ expect(screen.queryByText(editorContainerMockText)).toBeInTheDocument();
+ expect(screen.queryByText(pagesAndResourcesMockText)).not.toBeInTheDocument();
+ expect(mockComponentFn).toHaveBeenCalledWith(
+ expect.objectContaining({
+ learningContextId: courseId,
+ }),
+ );
+ });
});
- it('renders the VideoSelectorContainer component when the course videos route is active', () => {
+ it('renders the VideoSelectorContainer component when the course videos route is active', async () => {
render(
-
-
-
-
- ,
- );
-
- expect(screen.queryByText(videoSelectorContainerMockText)).toBeInTheDocument();
- expect(screen.queryByText(pagesAndResourcesMockText)).not.toBeInTheDocument();
- expect(mockComponentFn).toHaveBeenCalledWith(
- expect.objectContaining({
- courseId,
- }),
+ ,
+ { routerProps: { initialEntries: ['/editor/course-videos/block-id'] } },
);
+ await waitFor(() => {
+ expect(screen.queryByText(videoSelectorContainerMockText)).toBeInTheDocument();
+ expect(screen.queryByText(pagesAndResourcesMockText)).not.toBeInTheDocument();
+ expect(mockComponentFn).toHaveBeenCalledWith(
+ expect.objectContaining({
+ courseId,
+ }),
+ );
+ });
});
});
diff --git a/src/course-checklist/ChecklistSection/ChecklistSection.test.jsx b/src/course-checklist/ChecklistSection/ChecklistSection.test.jsx
index c4ad7f3262..09aa81d338 100644
--- a/src/course-checklist/ChecklistSection/ChecklistSection.test.jsx
+++ b/src/course-checklist/ChecklistSection/ChecklistSection.test.jsx
@@ -1,8 +1,8 @@
-import { within, screen } from '@testing-library/react';
-import '@testing-library/jest-dom';
import { camelCaseObject } from '@edx/frontend-platform';
-import { initializeMocks, render } from '../../testUtils';
+import {
+ initializeMocks, render, screen, within,
+} from '../../testUtils';
import { getApiWaffleFlagsUrl } from '../../data/api';
import { fetchWaffleFlags } from '../../data/thunks';
import { generateCourseLaunchData } from '../factories/mockApiResponses';
diff --git a/src/custom-pages/CustomPages.test.jsx b/src/custom-pages/CustomPages.test.jsx
index 8e8a4122c0..de57e25b80 100644
--- a/src/custom-pages/CustomPages.test.jsx
+++ b/src/custom-pages/CustomPages.test.jsx
@@ -1,18 +1,12 @@
+import ReactDOM from 'react-dom';
+
import {
- render,
- act,
+ initializeMocks,
fireEvent,
screen,
-} from '@testing-library/react';
-import ReactDOM from 'react-dom';
-
-import { initializeMockApp } from '@edx/frontend-platform';
-import MockAdapter from 'axios-mock-adapter';
-import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
-import { AppProvider } from '@edx/frontend-platform/react';
-import { IntlProvider } from '@edx/frontend-platform/i18n';
-
-import initializeStore from '../store';
+ act,
+ render,
+} from '../testUtils';
import { executeThunk } from '../utils';
import { RequestStatus } from '../data/constants';
import { getApiWaffleFlagsUrl } from '../data/api';
@@ -23,7 +17,6 @@ import {
generateNewPageApiResponse,
getStatusValue,
courseId,
- initialState,
} from './factories/mockApiResponses';
import {
@@ -39,13 +32,7 @@ let store;
ReactDOM.createPortal = jest.fn(node => node);
const renderComponent = () => {
- render(
-
-
-
-
- ,
- );
+ render();
};
const mockStore = async (status) => {
@@ -64,16 +51,9 @@ const mockStore = async (status) => {
describe('CustomPages', () => {
beforeEach(async () => {
- initializeMockApp({
- authenticatedUser: {
- userId: 3,
- username: 'abc123',
- administrator: false,
- roles: [],
- },
- });
- store = initializeStore(initialState);
- axiosMock = new MockAdapter(getAuthenticatedHttpClient());
+ const mocks = initializeMocks();
+ store = mocks.reduxStore;
+ axiosMock = mocks.axiosMock;
axiosMock
.onGet(getApiWaffleFlagsUrl(courseId))
.reply(200, {
diff --git a/src/grading-settings/grading-scale/react-ranger.test.js b/src/grading-settings/grading-scale/react-ranger.test.js
new file mode 100644
index 0000000000..76692970df
--- /dev/null
+++ b/src/grading-settings/grading-scale/react-ranger.test.js
@@ -0,0 +1,55 @@
+import { renderHook, act } from '@testing-library/react-hooks';
+
+import { useRanger } from './react-ranger';
+
+describe('useRanger Hook', () => {
+ const mockOnChange = jest.fn();
+ const mockOnDrag = jest.fn();
+
+ const defaultProps = {
+ values: [20, 80],
+ min: 0,
+ max: 100,
+ stepSize: 10,
+ onChange: mockOnChange,
+ onDrag: mockOnDrag,
+ };
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('initializes with correct default properties', () => {
+ const { result } = renderHook(() => useRanger(defaultProps));
+
+ expect(result.current.ticks).toBeDefined();
+ expect(result.current.segments).toBeDefined();
+ expect(result.current.handles).toHaveLength(2); // Two handles for two values
+ expect(result.current.activeHandleIndex).toBeNull();
+ });
+
+ it('calculates ticks based on min, max, and stepSize', () => {
+ const { result } = renderHook(() => useRanger(defaultProps));
+
+ const tickValues = result.current.ticks.map((tick) => tick.value);
+ expect(tickValues).toEqual([0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]);
+ });
+
+ it('resets active handle index after interaction', () => {
+ const { result } = renderHook(() => useRanger(defaultProps));
+
+ act(() => {
+ result.current.handles[0].getHandleProps().onMouseDown({ persist: jest.fn() }, 0);
+ document.dispatchEvent(new MouseEvent('mouseup'));
+ });
+
+ expect(result.current.activeHandleIndex).toBeNull();
+ });
+
+ it('computes segments based on values', () => {
+ const { result } = renderHook(() => useRanger(defaultProps));
+
+ const segmentValues = result.current.segments.map((segment) => segment.value);
+ expect(segmentValues).toEqual([20, 80, 100]);
+ });
+});
diff --git a/src/library-authoring/LibraryAuthoringPage.test.tsx b/src/library-authoring/LibraryAuthoringPage.test.tsx
index 990cbcb4b5..d352b0abb6 100644
--- a/src/library-authoring/LibraryAuthoringPage.test.tsx
+++ b/src/library-authoring/LibraryAuthoringPage.test.tsx
@@ -23,7 +23,6 @@ import { getStudioHomeApiUrl } from '../studio-home/data/api';
import { mockBroadcastChannel } from '../generic/data/api.mock';
import { LibraryLayout } from '.';
import { getLibraryCollectionsApiUrl } from './data/api';
-import { getApiWaffleFlagsUrl } from '../data/api';
mockGetCollectionMetadata.applyMock();
mockContentSearchConfig.applyMock();
@@ -56,7 +55,6 @@ describe('', () => {
beforeEach(async () => {
const { axiosMock } = initializeMocks();
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
- axiosMock.onGet(getApiWaffleFlagsUrl()).reply(200, {});
// The Meilisearch client-side API uses fetch, not Axios.
fetchMock.mockReset();
@@ -681,7 +679,6 @@ describe('', () => {
it('Shows an error if libraries V2 is disabled', async () => {
const { axiosMock } = initializeMocks();
- axiosMock.onGet(getApiWaffleFlagsUrl()).reply(200, {});
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, {
...studioHomeMock,
libraries_v2_enabled: false,
diff --git a/src/library-authoring/add-content/AddContentWorkflow.test.tsx b/src/library-authoring/add-content/AddContentWorkflow.test.tsx
index 2e0818172b..b019700a56 100644
--- a/src/library-authoring/add-content/AddContentWorkflow.test.tsx
+++ b/src/library-authoring/add-content/AddContentWorkflow.test.tsx
@@ -21,7 +21,6 @@ import { mockBroadcastChannel, mockClipboardEmpty } from '../../generic/data/api
import { mockContentSearchConfig, mockSearchResult } from '../../search-manager/data/api.mock';
import { studioHomeMock } from '../../studio-home/__mocks__';
import { getStudioHomeApiUrl } from '../../studio-home/data/api';
-import { getApiWaffleFlagsUrl } from '../../data/api';
import LibraryLayout from '../LibraryLayout';
mockContentSearchConfig.applyMock();
@@ -52,7 +51,6 @@ describe('AddContentWorkflow test', () => {
beforeEach(async () => {
const { axiosMock } = initializeMocks();
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
- axiosMock.onGet(getApiWaffleFlagsUrl()).reply(200, {});
});
it('can create an HTML component', async () => {
diff --git a/src/library-authoring/add-content/PickLibraryContentModal.test.tsx b/src/library-authoring/add-content/PickLibraryContentModal.test.tsx
index 0f327fd09d..a179dff4b6 100644
--- a/src/library-authoring/add-content/PickLibraryContentModal.test.tsx
+++ b/src/library-authoring/add-content/PickLibraryContentModal.test.tsx
@@ -17,7 +17,6 @@ import {
mockGetCollectionMetadata,
} from '../data/api.mocks';
import { PickLibraryContentModal } from './PickLibraryContentModal';
-import { getApiWaffleFlagsUrl } from '../../data/api';
mockContentSearchConfig.applyMock();
mockContentLibrary.applyMock();
@@ -48,7 +47,6 @@ describe('', () => {
const mocks = initializeMocks();
mockShowToast = mocks.mockShowToast;
mocks.axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
- mocks.axiosMock.onGet(getApiWaffleFlagsUrl()).reply(200, {});
});
it('can pick components from the modal', async () => {
diff --git a/src/pages-and-resources/pages/PageCard.test.jsx b/src/pages-and-resources/pages/PageCard.test.jsx
index 4c8a80a9f3..6a9c193fa9 100644
--- a/src/pages-and-resources/pages/PageCard.test.jsx
+++ b/src/pages-and-resources/pages/PageCard.test.jsx
@@ -1,13 +1,11 @@
+import { getConfig } from '@edx/frontend-platform';
+
import {
+ initializeMocks,
+ screen,
render,
- queryAllByRole,
-} from '@testing-library/react';
-import MockAdapter from 'axios-mock-adapter';
-import { initializeMockApp, getConfig } from '@edx/frontend-platform';
-import { AppProvider } from '@edx/frontend-platform/react';
-import { IntlProvider } from '@edx/frontend-platform/i18n';
-import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
-import initializeStore from '../../store';
+ waitFor,
+} from '../../testUtils';
import PageGrid from './PageGrid';
import { executeThunk } from '../../utils';
import { getApiWaffleFlagsUrl } from '../../data/api';
@@ -42,34 +40,18 @@ const mockPageConfig = [
const renderComponent = () => {
const wrapper = render(
-
-
-
-
-
-
- ,
+
+
+ ,
);
container = wrapper.container;
};
describe('LiveSettings', () => {
beforeEach(async () => {
- initializeMockApp({
- authenticatedUser: {
- userId: 3,
- username: 'abc123',
- administrator: false,
- roles: [],
- },
- });
- store = initializeStore({
- courseDetail: {
- courseId: 'id',
- status: 'sucessful',
- },
- });
- axiosMock = new MockAdapter(getAuthenticatedHttpClient());
+ const mocks = initializeMocks();
+ store = mocks.reduxStore;
+ axiosMock = mocks.axiosMock;
axiosMock
.onGet(getApiWaffleFlagsUrl(courseId))
.reply(200, {
@@ -83,13 +65,17 @@ describe('LiveSettings', () => {
it('should render three cards', async () => {
renderComponent();
- expect(queryAllByRole(container, 'button')).toHaveLength(3);
+ waitFor(() => {
+ expect(screen.queryAllByRole(container, 'button')).toHaveLength(3);
+ });
});
it('should navigate to legacyLink', async () => {
renderComponent();
const textbookPagePath = mockPageConfig[0][1];
- const textbookSettingsButton = queryAllByRole(container, 'link')[1];
- expect(textbookSettingsButton).toHaveAttribute('href', textbookPagePath);
+ waitFor(() => {
+ const textbookSettingsButton = screen.queryAllByRole(container, 'link')[1];
+ expect(textbookSettingsButton).toHaveAttribute('href', textbookPagePath);
+ });
});
});
diff --git a/src/studio-home/StudioHome.test.jsx b/src/studio-home/StudioHome.test.jsx
index b2a8fd871b..8e6eca953a 100644
--- a/src/studio-home/StudioHome.test.jsx
+++ b/src/studio-home/StudioHome.test.jsx
@@ -1,32 +1,22 @@
-import React from 'react';
import { useSelector } from 'react-redux';
import { MemoryRouter, Routes, Route } from 'react-router-dom';
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { initializeMockApp } from '@edx/frontend-platform';
-import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
-import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n';
-import { AppProvider } from '@edx/frontend-platform/react';
-import {
- act, fireEvent, render, waitFor,
-} from '@testing-library/react';
-import MockAdapter from 'axios-mock-adapter';
-import initializeStore from '../store';
import { RequestStatus } from '../data/constants';
import { COURSE_CREATOR_STATES } from '../constants';
-import { executeThunk } from '../utils';
import { studioHomeMock } from './__mocks__';
import { getStudioHomeApiUrl } from './data/api';
-import { fetchStudioHomeData } from './data/thunks';
-import { getApiWaffleFlagsUrl } from '../data/api';
-import { fetchWaffleFlags } from '../data/thunks';
+import {
+ act,
+ fireEvent,
+ render,
+ waitFor,
+ initializeMocks,
+} from '../testUtils';
import messages from './messages';
import createNewCourseMessages from './create-new-course-form/messages';
import createOrRerunCourseMessages from '../generic/create-or-rerun-course/messages';
import { StudioHome } from '.';
-let axiosMock;
-let store;
const {
studioShortName,
studioRequestEmail,
@@ -44,90 +34,60 @@ jest.mock('react-router-dom', () => ({
useNavigate: () => mockNavigate,
}));
-const queryClient = new QueryClient();
-
const RootWrapper = () => (
-
-
-
-
-
- }
- />
- }
- />
- }
- />
-
-
-
-
-
+
+
+ }
+ />
+ }
+ />
+ }
+ />
+ ,
+
);
describe('', () => {
describe('api fetch fails', () => {
beforeEach(async () => {
- initializeMockApp({
- authenticatedUser: {
- userId: 3,
- username: 'abc123',
- administrator: true,
- roles: [],
- },
- });
- store = initializeStore();
- axiosMock = new MockAdapter(getAuthenticatedHttpClient());
- axiosMock.onGet(getStudioHomeApiUrl()).reply(404);
- await executeThunk(fetchStudioHomeData(), store.dispatch);
- axiosMock
- .onGet(getApiWaffleFlagsUrl())
- .reply(200, {});
- await executeThunk(fetchWaffleFlags(), store.dispatch);
+ const mocks = initializeMocks();
+ mocks.axiosMock.onGet(getStudioHomeApiUrl()).reply(404);
useSelector.mockReturnValue({ studioHomeLoadingStatus: RequestStatus.FAILED });
});
it('should render fetch error', () => {
const { getByText } = render();
- expect(getByText(messages.homePageLoadFailedMessage.defaultMessage)).toBeInTheDocument();
+ waitFor(() => {
+ expect(getByText(messages.homePageLoadFailedMessage.defaultMessage)).toBeInTheDocument();
+ });
});
it('should render Studio home title', () => {
const { getByText } = render();
- expect(getByText('Studio home')).toBeInTheDocument();
+ waitFor(() => {
+ expect(getByText('Studio home')).toBeInTheDocument();
+ });
});
});
describe('api fetch succeeds', () => {
beforeEach(async () => {
- initializeMockApp({
- authenticatedUser: {
- userId: 3,
- username: 'abc123',
- administrator: true,
- roles: [],
- },
- });
- store = initializeStore();
- axiosMock = new MockAdapter(getAuthenticatedHttpClient());
- axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
- await executeThunk(fetchStudioHomeData(), store.dispatch);
+ const mocks = initializeMocks();
+ mocks.axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
useSelector.mockReturnValue(studioHomeMock);
- axiosMock
- .onGet(getApiWaffleFlagsUrl())
- .reply(200, {});
- await executeThunk(fetchWaffleFlags(), store.dispatch);
});
it('should render page and page title correctly', () => {
const { getByText } = render();
- expect(getByText(`${studioShortName} home`)).toBeInTheDocument();
+ waitFor(() => {
+ expect(getByText(`${studioShortName} home`)).toBeInTheDocument();
+ });
});
it('should render email staff header button', async () => {
@@ -137,8 +97,10 @@ describe('', () => {
});
const { getByRole } = render();
- expect(getByRole('link', { name: messages.emailStaffBtnText.defaultMessage }))
- .toHaveAttribute('href', `mailto:${studioRequestEmail}`);
+ waitFor(() => {
+ expect(getByRole('link', { name: messages.emailStaffBtnText.defaultMessage }))
+ .toHaveAttribute('href', `mailto:${studioRequestEmail}`);
+ });
});
it('should render create new course button', async () => {
@@ -148,7 +110,9 @@ describe('', () => {
});
const { getByRole } = render();
- expect(getByRole('button', { name: messages.addNewCourseBtnText.defaultMessage })).toBeInTheDocument();
+ waitFor(() => {
+ expect(getByRole('button', { name: messages.addNewCourseBtnText.defaultMessage })).toBeInTheDocument();
+ });
});
it('should show verify email layout if user inactive', () => {
@@ -158,7 +122,9 @@ describe('', () => {
});
const { getByText } = render();
- expect(getByText('Thanks for signing up, abc123!', { exact: false })).toBeInTheDocument();
+ waitFor(() => {
+ expect(getByText('Thanks for signing up, abc123!', { exact: false })).toBeInTheDocument();
+ });
});
it('shows the spinner before the query is complete', async () => {
@@ -169,8 +135,10 @@ describe('', () => {
await act(async () => {
const { getByRole } = render();
- const spinner = getByRole('status');
- expect(spinner.textContent).toEqual('Loading...');
+ waitFor(() => {
+ const spinner = getByRole('status');
+ expect(spinner.textContent).toEqual('Loading...');
+ });
});
});
@@ -184,13 +152,15 @@ describe('', () => {
const studioBaseUrl = 'http://localhost:18010';
const { getByTestId } = render();
- const createNewLibraryButton = getByTestId('new-library-button');
-
- const { open } = window;
- window.open = jest.fn();
- fireEvent.click(createNewLibraryButton);
- expect(window.open).toHaveBeenCalledWith(`${studioBaseUrl}/home_library`);
- window.open = open;
+ waitFor(() => {
+ const createNewLibraryButton = getByTestId('new-library-button');
+
+ const { open } = window;
+ window.open = jest.fn();
+ fireEvent.click(createNewLibraryButton);
+ expect(window.open).toHaveBeenCalledWith(`${studioBaseUrl}/home_library`);
+ window.open = open;
+ });
});
it('should navigate to the library authoring page in course authoring', () => {
@@ -199,11 +169,11 @@ describe('', () => {
librariesV1Enabled: false,
});
const { getByTestId } = render();
- const createNewLibraryButton = getByTestId('new-library-button');
-
- fireEvent.click(createNewLibraryButton);
-
- expect(mockNavigate).toHaveBeenCalledWith('/library/create');
+ waitFor(() => {
+ const createNewLibraryButton = getByTestId('new-library-button');
+ fireEvent.click(createNewLibraryButton);
+ expect(mockNavigate).toHaveBeenCalledWith('/library/create');
+ });
});
});
@@ -224,7 +194,9 @@ describe('', () => {
librariesV1Enabled: false,
});
const { queryByTestId } = render();
- expect(queryByTestId('new-library-button')).toBeInTheDocument();
+ waitFor(() => {
+ expect(queryByTestId('new-library-button')).toBeInTheDocument();
+ });
});
it('should render create new course container', async () => {
@@ -234,10 +206,12 @@ describe('', () => {
});
const { getByRole, getByText } = render();
- const createNewCourseButton = getByRole('button', { name: messages.addNewCourseBtnText.defaultMessage });
+ waitFor(() => {
+ const createNewCourseButton = getByRole('button', { name: messages.addNewCourseBtnText.defaultMessage });
- await act(() => fireEvent.click(createNewCourseButton));
- expect(getByText(createNewCourseMessages.createNewCourse.defaultMessage)).toBeInTheDocument();
+ act(() => fireEvent.click(createNewCourseButton));
+ expect(getByText(createNewCourseMessages.createNewCourse.defaultMessage)).toBeInTheDocument();
+ });
});
it('should hide create new course container', async () => {
@@ -247,16 +221,16 @@ describe('', () => {
});
const { getByRole, queryByText, getByText } = render();
- const createNewCourseButton = getByRole('button', { name: messages.addNewCourseBtnText.defaultMessage });
- fireEvent.click(createNewCourseButton);
waitFor(() => {
+ const createNewCourseButton = getByRole('button', { name: messages.addNewCourseBtnText.defaultMessage });
+ fireEvent.click(createNewCourseButton);
expect(getByText(createNewCourseMessages.createNewCourse.defaultMessage)).toBeInTheDocument();
});
- const cancelButton = getByRole('button', { name: createOrRerunCourseMessages.cancelButton.defaultMessage });
- fireEvent.click(cancelButton);
waitFor(() => {
+ const cancelButton = getByRole('button', { name: createOrRerunCourseMessages.cancelButton.defaultMessage });
+ fireEvent.click(cancelButton);
expect(queryByText(createNewCourseMessages.createNewCourse.defaultMessage)).not.toBeInTheDocument();
});
});
@@ -270,13 +244,15 @@ describe('', () => {
});
const { getByText, queryByText } = render();
const defaultTitleMessage = messages.defaultSection_1_Title.defaultMessage;
- const titleWithStudioName = defaultTitleMessage.replace('{studioShortName}', 'Studio');
- const administratorCardTitle = getByText(titleWithStudioName);
+ waitFor(() => {
+ const titleWithStudioName = defaultTitleMessage.replace('{studioShortName}', 'Studio');
+ const administratorCardTitle = getByText(titleWithStudioName);
- expect(administratorCardTitle).toBeVisible();
+ expect(administratorCardTitle).toBeVisible();
- const addCourseButton = queryByText(messages.btnAddNewCourseText.defaultMessage);
- expect(addCourseButton).toBeNull();
+ const addCourseButton = queryByText(messages.btnAddNewCourseText.defaultMessage);
+ expect(addCourseButton).toBeNull();
+ });
});
it('should show contact administrator card with add course buttons', () => {
@@ -287,23 +263,27 @@ describe('', () => {
});
const { getByText, getByTestId } = render();
const defaultTitleMessage = messages.defaultSection_1_Title.defaultMessage;
- const titleWithStudioName = defaultTitleMessage.replace('{studioShortName}', 'Studio');
- const administratorCardTitle = getByText(titleWithStudioName);
+ waitFor(() => {
+ const titleWithStudioName = defaultTitleMessage.replace('{studioShortName}', 'Studio');
+ const administratorCardTitle = getByText(titleWithStudioName);
- expect(administratorCardTitle).toBeVisible();
+ expect(administratorCardTitle).toBeVisible();
- const addCourseButton = getByTestId('contact-admin-create-course');
- expect(addCourseButton).toBeVisible();
+ const addCourseButton = getByTestId('contact-admin-create-course');
+ expect(addCourseButton).toBeVisible();
- fireEvent.click(addCourseButton);
- expect(getByTestId('create-course-form')).toBeVisible();
+ fireEvent.click(addCourseButton);
+ expect(getByTestId('create-course-form')).toBeVisible();
+ });
});
});
it('should show footer', () => {
const { getByText } = render();
- expect(getByText('Looking for help with Studio?')).toBeInTheDocument();
- expect(getByText('LMS')).toHaveAttribute('href', process.env.LMS_BASE_URL);
+ waitFor(() => {
+ expect(getByText('Looking for help with Studio?')).toBeInTheDocument();
+ expect(getByText('LMS')).toHaveAttribute('href', process.env.LMS_BASE_URL);
+ });
});
});
});
diff --git a/src/studio-home/collapsible-state-with-action/index.jsx b/src/studio-home/collapsible-state-with-action/index.jsx
index 196fa4a0b9..ee99398a8e 100644
--- a/src/studio-home/collapsible-state-with-action/index.jsx
+++ b/src/studio-home/collapsible-state-with-action/index.jsx
@@ -98,6 +98,7 @@ const CollapsibleStateWithAction = ({ state, className }) => {
{title}
diff --git a/src/studio-home/tabs-section/courses-tab/index.test.tsx b/src/studio-home/tabs-section/courses-tab/index.test.tsx
index 81c72307e9..8c9f1d8dd4 100644
--- a/src/studio-home/tabs-section/courses-tab/index.test.tsx
+++ b/src/studio-home/tabs-section/courses-tab/index.test.tsx
@@ -4,6 +4,7 @@ import { render, screen } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppProvider } from '@edx/frontend-platform/react';
+import { COURSE_CREATOR_STATES } from '../../../constants';
import initializeStore from '../../../store';
import { studioHomeMock } from '../../__mocks__';
import { initialState } from '../../factories/mockApiResponses';
@@ -127,4 +128,17 @@ describe('', () => {
const alertCoursesNotFound = screen.queryByTestId('processing-courses-title');
expect(alertCoursesNotFound).toBeInTheDocument();
});
+
+ it('should render CollapsibleStateWithAction when courseCreatorStatus is true', () => {
+ const props = { isShowProcessing: true, isEnabledPagination: false };
+ const customStoreData = {
+ studioHomeData: {
+ inProcessCourseActions: [],
+ courseCreatorStatus: COURSE_CREATOR_STATES.denied,
+ },
+ };
+ renderComponent(props, customStoreData);
+ const collapsibleStateWithAction = screen.queryByTestId('collapsible-state-with-action');
+ expect(collapsibleStateWithAction).toBeInTheDocument();
+ });
});
diff --git a/src/testUtils.tsx b/src/testUtils.tsx
index dc84448bde..6cfda2c0f8 100644
--- a/src/testUtils.tsx
+++ b/src/testUtils.tsx
@@ -24,6 +24,7 @@ import {
import { ToastContext, type ToastContextData } from './generic/toast-context';
import initializeReduxStore from './store';
+import { getApiWaffleFlagsUrl } from './data/api';
/** @deprecated Use React Query and/or regular React Context instead of redux */
let reduxStore: Store;
@@ -172,6 +173,10 @@ export function initializeMocks({ user = defaultUser, initialState = undefined }
});
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
+ axiosMock
+ .onGet(getApiWaffleFlagsUrl())
+ .reply(200, {});
+
// Reset `mockToastContext` for this current test
mockToastContext = {
showToast: jest.fn(),