Fix broken StudioHome tests (#2291)
There were a ton of problems with these tests, but the main one was the use of `waitFor` without `await`, causing all of the code inside each `waitFor` block to essentially be ignored. Other problems fixed: * Rendering a router inside a router was causing most of the render() calls to fail (our custom `render()` already provides a router so there's no need to provide one in the test case) * Use of `testid` instead of queries based on what users see * Tests could match on content in the body when trying to make assertions about the header * Mock imported via `index.js` was causing `jest-haste-map` to print warnings about duplicate mock names (this is still happening for other mocks) * Passing `courses: null` instead of `courses: []` was causing a broken render on two of the tests. I also made other cleanups to follow best practices.
This commit is contained in:
@@ -5,8 +5,8 @@ import {
|
||||
fireEvent,
|
||||
render,
|
||||
waitFor,
|
||||
} from '../testUtils';
|
||||
import { studioHomeMock } from '../studio-home/__mocks__';
|
||||
} from '@src/testUtils';
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
import { getStudioHomeApiUrl } from '../studio-home/data/api';
|
||||
import { RequestStatus } from '../data/constants';
|
||||
import messages from './messages';
|
||||
|
||||
@@ -5,7 +5,7 @@ import { initializeMockApp } from '@edx/frontend-platform';
|
||||
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { studioHomeMock } from '../../studio-home/__mocks__';
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
import initializeStore from '../../store';
|
||||
import CourseRerunForm from '.';
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
|
||||
import { studioHomeMock } from '../../studio-home/__mocks__';
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
import { getStudioHomeApiUrl } from '../../studio-home/data/api';
|
||||
import { fetchStudioHomeData } from '../../studio-home/data/thunks';
|
||||
import { RequestStatus } from '../../data/constants';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { RequestStatus } from '../../../data/constants';
|
||||
import { studioHomeMock } from '../../../studio-home/__mocks__';
|
||||
import { RequestStatus } from '@src/data/constants';
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
|
||||
export const courseId = 'course-v1:edX+DemoX+Demo_Course';
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
screen,
|
||||
waitFor,
|
||||
within,
|
||||
} from '../testUtils';
|
||||
} from '@src/testUtils';
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
import mockResult from './__mocks__/library-search.json';
|
||||
import mockEmptyResult from '../search-modal/__mocks__/empty-search-result.json';
|
||||
import {
|
||||
@@ -19,7 +20,6 @@ import {
|
||||
mockXBlockFields,
|
||||
} from './data/api.mocks';
|
||||
import { mockContentSearchConfig } from '../search-manager/data/api.mock';
|
||||
import { studioHomeMock } from '../studio-home/__mocks__';
|
||||
import { getStudioHomeApiUrl } from '../studio-home/data/api';
|
||||
import { LibraryLayout } from '.';
|
||||
import { getLibraryCollectionsApiUrl, getLibraryContainersApiUrl } from './data/api';
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
waitFor,
|
||||
screen,
|
||||
initializeMocks,
|
||||
} from '../../testUtils';
|
||||
} from '@src/testUtils';
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
import mockResult from '../__mocks__/library-search.json';
|
||||
import editorCmsApi from '../../editors/data/services/cms/api';
|
||||
import * as textEditorHooks from '../../editors/containers/TextEditor/hooks';
|
||||
@@ -19,7 +20,6 @@ import {
|
||||
} from '../data/api.mocks';
|
||||
import { mockClipboardEmpty } from '../../generic/data/api.mock';
|
||||
import { mockContentSearchConfig, mockSearchResult } from '../../search-manager/data/api.mock';
|
||||
import { studioHomeMock } from '../../studio-home/__mocks__';
|
||||
import { getStudioHomeApiUrl } from '../../studio-home/data/api';
|
||||
import LibraryLayout from '../LibraryLayout';
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { mockContentSearchConfig, mockSearchResult } from '../../search-manager/data/api.mock';
|
||||
import { mockContentSearchConfig, mockSearchResult } from '@src/search-manager/data/api.mock';
|
||||
import {
|
||||
fireEvent,
|
||||
render as baseRender,
|
||||
waitFor,
|
||||
screen,
|
||||
initializeMocks,
|
||||
} from '../../testUtils';
|
||||
import { studioHomeMock } from '../../studio-home/__mocks__';
|
||||
} from '@src/testUtils';
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
import { getStudioHomeApiUrl } from '../../studio-home/data/api';
|
||||
import mockResult from '../__mocks__/library-search.json';
|
||||
import { LibraryProvider } from '../common/context/LibraryContext';
|
||||
|
||||
@@ -8,10 +8,10 @@ import {
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
} from '../../testUtils';
|
||||
import { studioHomeMock } from '../../studio-home/__mocks__';
|
||||
import { getStudioHomeApiUrl } from '../../studio-home/data/api';
|
||||
import { getApiWaffleFlagsUrl } from '../../data/api';
|
||||
} from '@src/testUtils';
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
import { getStudioHomeApiUrl } from '@src/studio-home/data/api';
|
||||
import { getApiWaffleFlagsUrl } from '@src/data/api';
|
||||
import { CreateLibrary } from '.';
|
||||
import { getContentLibraryV2CreateApiUrl } from './data/api';
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { useEffect } from 'react';
|
||||
import fetchMock from 'fetch-mock-jest';
|
||||
import { initializeMocks, render } from '@src/testUtils';
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
import {
|
||||
mockContentLibrary,
|
||||
} from './data/api.mocks';
|
||||
import { LibraryLayout } from '.';
|
||||
import { ContentType, useLibraryRoutes } from './routes';
|
||||
import mockResult from './__mocks__/library-search.json';
|
||||
import { initializeMocks, render } from '../testUtils';
|
||||
import { studioHomeMock } from '../studio-home/__mocks__';
|
||||
import { getStudioHomeApiUrl } from '../studio-home/data/api';
|
||||
|
||||
const mockNavigate = jest.fn();
|
||||
|
||||
@@ -1,289 +0,0 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
import { MemoryRouter, Routes, Route } from 'react-router-dom';
|
||||
|
||||
import { RequestStatus } from '../data/constants';
|
||||
import { COURSE_CREATOR_STATES } from '../constants';
|
||||
import { studioHomeMock } from './__mocks__';
|
||||
import { getStudioHomeApiUrl } from './data/api';
|
||||
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 '.';
|
||||
|
||||
const {
|
||||
studioShortName,
|
||||
studioRequestEmail,
|
||||
} = studioHomeMock;
|
||||
|
||||
jest.mock('react-redux', () => ({
|
||||
...jest.requireActual('react-redux'),
|
||||
useSelector: jest.fn(),
|
||||
}));
|
||||
|
||||
const mockNavigate = jest.fn();
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'), // use actual for all non-hook parts
|
||||
useNavigate: () => mockNavigate,
|
||||
}));
|
||||
|
||||
const RootWrapper = () => (
|
||||
<MemoryRouter initialEntries={['/home']}>
|
||||
<Routes>
|
||||
<Route
|
||||
path="/home"
|
||||
element={<StudioHome />}
|
||||
/>
|
||||
<Route
|
||||
path="/libraries"
|
||||
element={<StudioHome />}
|
||||
/>
|
||||
<Route
|
||||
path="/libraries-v1"
|
||||
element={<StudioHome />}
|
||||
/>
|
||||
</Routes>,
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
describe('<StudioHome />', () => {
|
||||
describe('api fetch fails', () => {
|
||||
beforeEach(async () => {
|
||||
const mocks = initializeMocks();
|
||||
mocks.axiosMock.onGet(getStudioHomeApiUrl()).reply(404);
|
||||
useSelector.mockReturnValue({ studioHomeLoadingStatus: RequestStatus.FAILED });
|
||||
});
|
||||
|
||||
it('should render fetch error', () => {
|
||||
const { getByText } = render(<RootWrapper />);
|
||||
waitFor(() => {
|
||||
expect(getByText(messages.homePageLoadFailedMessage.defaultMessage)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render Studio home title', () => {
|
||||
const { getByText } = render(<RootWrapper />);
|
||||
waitFor(() => {
|
||||
expect(getByText('Studio home')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('api fetch succeeds', () => {
|
||||
beforeEach(async () => {
|
||||
const mocks = initializeMocks();
|
||||
mocks.axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
|
||||
useSelector.mockReturnValue(studioHomeMock);
|
||||
});
|
||||
|
||||
it('should render page and page title correctly', () => {
|
||||
const { getByText } = render(<RootWrapper />);
|
||||
waitFor(() => {
|
||||
expect(getByText(`${studioShortName} home`)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render email staff header button', async () => {
|
||||
useSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
courseCreatorStatus: COURSE_CREATOR_STATES.disallowedForThisSite,
|
||||
});
|
||||
|
||||
const { getByRole } = render(<RootWrapper />);
|
||||
waitFor(() => {
|
||||
expect(getByRole('link', { name: messages.emailStaffBtnText.defaultMessage }))
|
||||
.toHaveAttribute('href', `mailto:${studioRequestEmail}`);
|
||||
});
|
||||
});
|
||||
|
||||
it('should render create new course button', async () => {
|
||||
useSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
courseCreatorStatus: COURSE_CREATOR_STATES.granted,
|
||||
});
|
||||
|
||||
const { getByRole } = render(<RootWrapper />);
|
||||
waitFor(() => {
|
||||
expect(getByRole('button', { name: messages.addNewCourseBtnText.defaultMessage })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should show verify email layout if user inactive', () => {
|
||||
useSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
userIsActive: false,
|
||||
});
|
||||
|
||||
const { getByText } = render(<RootWrapper />);
|
||||
waitFor(() => {
|
||||
expect(getByText('Thanks for signing up, abc123!', { exact: false })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows the spinner before the query is complete', async () => {
|
||||
useSelector.mockReturnValue({
|
||||
studioHomeLoadingStatus: RequestStatus.IN_PROGRESS,
|
||||
userIsActive: true,
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
const { getByRole } = render(<RootWrapper />);
|
||||
waitFor(() => {
|
||||
const spinner = getByRole('status');
|
||||
expect(spinner.textContent).toEqual('Loading...');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('render new library button', () => {
|
||||
it('should navigate to home_library when libraries-v2 disabled', () => {
|
||||
useSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
courseCreatorStatus: COURSE_CREATOR_STATES.granted,
|
||||
librariesV2Enabled: false,
|
||||
});
|
||||
const studioBaseUrl = 'http://localhost:18010';
|
||||
|
||||
const { getByTestId } = render(<RootWrapper />);
|
||||
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', () => {
|
||||
useSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
librariesV1Enabled: false,
|
||||
});
|
||||
const { getByTestId } = render(<RootWrapper />);
|
||||
waitFor(() => {
|
||||
const createNewLibraryButton = getByTestId('new-library-button');
|
||||
fireEvent.click(createNewLibraryButton);
|
||||
expect(mockNavigate).toHaveBeenCalledWith('/library/create');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('do not render new library button for "v1 only" mode if showNewLibraryButton is False', () => {
|
||||
useSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
showNewLibraryButton: false,
|
||||
librariesV2Enabled: false,
|
||||
});
|
||||
const { queryByTestId } = render(<RootWrapper />);
|
||||
expect(queryByTestId('new-library-button')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('render new library button for "v2 only" mode even if showNewLibraryButton is False', () => {
|
||||
useSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
showNewLibraryButton: false,
|
||||
librariesV1Enabled: false,
|
||||
});
|
||||
const { queryByTestId } = render(<RootWrapper />);
|
||||
waitFor(() => {
|
||||
expect(queryByTestId('new-library-button')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render create new course container', async () => {
|
||||
useSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
courseCreatorStatus: COURSE_CREATOR_STATES.granted,
|
||||
});
|
||||
|
||||
const { getByRole, getByText } = render(<RootWrapper />);
|
||||
waitFor(() => {
|
||||
const createNewCourseButton = getByRole('button', { name: messages.addNewCourseBtnText.defaultMessage });
|
||||
|
||||
act(() => fireEvent.click(createNewCourseButton));
|
||||
expect(getByText(createNewCourseMessages.createNewCourse.defaultMessage)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should hide create new course container', async () => {
|
||||
useSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
courseCreatorStatus: COURSE_CREATOR_STATES.granted,
|
||||
});
|
||||
|
||||
const { getByRole, queryByText, getByText } = render(<RootWrapper />);
|
||||
|
||||
waitFor(() => {
|
||||
const createNewCourseButton = getByRole('button', { name: messages.addNewCourseBtnText.defaultMessage });
|
||||
fireEvent.click(createNewCourseButton);
|
||||
expect(getByText(createNewCourseMessages.createNewCourse.defaultMessage)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
waitFor(() => {
|
||||
const cancelButton = getByRole('button', { name: createOrRerunCourseMessages.cancelButton.defaultMessage });
|
||||
fireEvent.click(cancelButton);
|
||||
expect(queryByText(createNewCourseMessages.createNewCourse.defaultMessage)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('contact administrator card', () => {
|
||||
it('should show contact administrator card with no add course buttons', () => {
|
||||
useSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
courses: null,
|
||||
courseCreatorStatus: COURSE_CREATOR_STATES.pending,
|
||||
});
|
||||
const { getByText, queryByText } = render(<RootWrapper />);
|
||||
const defaultTitleMessage = messages.defaultSection_1_Title.defaultMessage;
|
||||
waitFor(() => {
|
||||
const titleWithStudioName = defaultTitleMessage.replace('{studioShortName}', 'Studio');
|
||||
const administratorCardTitle = getByText(titleWithStudioName);
|
||||
|
||||
expect(administratorCardTitle).toBeVisible();
|
||||
|
||||
const addCourseButton = queryByText(messages.btnAddNewCourseText.defaultMessage);
|
||||
expect(addCourseButton).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('should show contact administrator card with add course buttons', () => {
|
||||
useSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
courses: null,
|
||||
courseCreatorStatus: COURSE_CREATOR_STATES.granted,
|
||||
});
|
||||
const { getByText, getByTestId } = render(<RootWrapper />);
|
||||
const defaultTitleMessage = messages.defaultSection_1_Title.defaultMessage;
|
||||
waitFor(() => {
|
||||
const titleWithStudioName = defaultTitleMessage.replace('{studioShortName}', 'Studio');
|
||||
const administratorCardTitle = getByText(titleWithStudioName);
|
||||
|
||||
expect(administratorCardTitle).toBeVisible();
|
||||
|
||||
const addCourseButton = getByTestId('contact-admin-create-course');
|
||||
expect(addCourseButton).toBeVisible();
|
||||
|
||||
fireEvent.click(addCourseButton);
|
||||
expect(getByTestId('create-course-form')).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should show footer', () => {
|
||||
const { getByText } = render(<RootWrapper />);
|
||||
waitFor(() => {
|
||||
expect(getByText('Looking for help with Studio?')).toBeInTheDocument();
|
||||
expect(getByText('LMS')).toHaveAttribute('href', process.env.LMS_BASE_URL);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
237
src/studio-home/StudioHome.test.tsx
Normal file
237
src/studio-home/StudioHome.test.tsx
Normal file
@@ -0,0 +1,237 @@
|
||||
import * as reactRedux from 'react-redux';
|
||||
|
||||
import {
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
initializeMocks,
|
||||
within,
|
||||
} from '@src/testUtils';
|
||||
import { RequestStatus } from '../data/constants';
|
||||
import { COURSE_CREATOR_STATES } from '../constants';
|
||||
import studioHomeMock from './__mocks__/studioHomeMock';
|
||||
import { getStudioHomeApiUrl } from './data/api';
|
||||
import { StudioHome } from '.';
|
||||
|
||||
const {
|
||||
studioShortName,
|
||||
studioRequestEmail,
|
||||
} = studioHomeMock;
|
||||
|
||||
const mockUseSelector = jest.fn();
|
||||
jest.spyOn(reactRedux, 'useSelector').mockImplementation(mockUseSelector);
|
||||
const mockNavigate = jest.fn();
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useNavigate: () => mockNavigate,
|
||||
}));
|
||||
|
||||
/** Helper function to get the Studio header in the rendered HTML */
|
||||
function getHeaderElement(): HTMLElement {
|
||||
const header = screen.getByRole('banner');
|
||||
expect(header.tagName).toEqual('HEADER');
|
||||
return header;
|
||||
}
|
||||
|
||||
describe('<StudioHome />', () => {
|
||||
describe('api fetch fails', () => {
|
||||
beforeEach(async () => {
|
||||
const mocks = initializeMocks();
|
||||
mocks.axiosMock.onGet(getStudioHomeApiUrl()).reply(404);
|
||||
mockUseSelector.mockReturnValue({ studioHomeLoadingStatus: RequestStatus.FAILED });
|
||||
});
|
||||
|
||||
it('should render fetch error', async () => {
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
expect(screen.getByText('Failed to load Studio home. Please try again later.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render Studio home title', async () => {
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
// Search only within the header; don't match on the similar text in the body's error message.
|
||||
const header = getHeaderElement();
|
||||
expect(within(header).getByText('Studio home')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('api fetch succeeds', () => {
|
||||
beforeEach(async () => {
|
||||
const mocks = initializeMocks();
|
||||
mocks.axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock);
|
||||
mockUseSelector.mockReturnValue(studioHomeMock);
|
||||
});
|
||||
|
||||
it('should render page and page title correctly', async () => {
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
const header = getHeaderElement();
|
||||
expect(within(header).getByText(`${studioShortName} home`)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render "email staff" header button for users without create permission', async () => {
|
||||
mockUseSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
courseCreatorStatus: COURSE_CREATOR_STATES.disallowedForThisSite,
|
||||
});
|
||||
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
const header = getHeaderElement();
|
||||
const link = within(header).getByRole('link', { name: 'Email staff to create course' });
|
||||
expect(link).toHaveAttribute('href', `mailto:${studioRequestEmail}`);
|
||||
});
|
||||
|
||||
it('should render create new course button for users with create permission', async () => {
|
||||
mockUseSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
courseCreatorStatus: COURSE_CREATOR_STATES.granted,
|
||||
});
|
||||
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
const header = getHeaderElement();
|
||||
within(header).getByRole('button', { name: 'New course' }); // will error if not found
|
||||
});
|
||||
|
||||
it('should show verify email layout if user inactive', async () => {
|
||||
mockUseSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
userIsActive: false,
|
||||
});
|
||||
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
screen.getByText('Thanks for signing up, abc123!', { exact: false }); // will error if not found
|
||||
});
|
||||
|
||||
it('shows the spinner before the query is complete', async () => {
|
||||
mockUseSelector.mockReturnValue({
|
||||
studioHomeLoadingStatus: RequestStatus.IN_PROGRESS,
|
||||
userIsActive: true,
|
||||
});
|
||||
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
const spinner = screen.getByRole('status');
|
||||
expect(spinner.textContent).toEqual('Loading...');
|
||||
});
|
||||
|
||||
describe('render new library button', () => {
|
||||
it('should navigate to home_library when libraries-v2 disabled', async () => {
|
||||
mockUseSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
courseCreatorStatus: COURSE_CREATOR_STATES.granted,
|
||||
librariesV2Enabled: false,
|
||||
});
|
||||
const studioBaseUrl = 'http://localhost:18010';
|
||||
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
await waitFor(() => {
|
||||
const createNewLibraryButton = screen.getByRole('button', { name: 'New library' });
|
||||
|
||||
const mockWindowOpen = jest.spyOn(window, 'open');
|
||||
fireEvent.click(createNewLibraryButton);
|
||||
expect(mockWindowOpen).toHaveBeenCalledWith(`${studioBaseUrl}/home_library`);
|
||||
mockWindowOpen.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
it('should navigate to the library authoring page in course authoring', async () => {
|
||||
mockUseSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
librariesV1Enabled: false,
|
||||
});
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
const createNewLibraryButton = screen.getByRole('button', { name: 'New library' });
|
||||
fireEvent.click(createNewLibraryButton);
|
||||
expect(mockNavigate).toHaveBeenCalledWith('/library/create');
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render new library button for "v1 only" mode if showNewLibraryButton is False', () => {
|
||||
mockUseSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
showNewLibraryButton: false,
|
||||
librariesV2Enabled: false,
|
||||
});
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
expect(screen.queryByRole('button', { name: 'New library' })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('render new library button for "v2 only" mode even if showNewLibraryButton is False', () => {
|
||||
mockUseSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
showNewLibraryButton: false,
|
||||
librariesV1Enabled: false,
|
||||
});
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
expect(screen.queryByRole('button', { name: 'New library' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render "create new course" container', async () => {
|
||||
mockUseSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
courseCreatorStatus: COURSE_CREATOR_STATES.granted,
|
||||
});
|
||||
|
||||
const newCourseContainerText = 'Create a new course';
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
|
||||
expect(screen.queryByText(newCourseContainerText)).not.toBeInTheDocument();
|
||||
const createNewCourseButton = screen.getByRole('button', { name: 'New course' });
|
||||
fireEvent.click(createNewCourseButton);
|
||||
expect(screen.queryByText(newCourseContainerText)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should hide "create new course" container', async () => {
|
||||
mockUseSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
courseCreatorStatus: COURSE_CREATOR_STATES.granted,
|
||||
});
|
||||
|
||||
const newCourseContainerText = 'Create a new course';
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
|
||||
const createNewCourseButton = screen.getByRole('button', { name: 'New course' });
|
||||
fireEvent.click(createNewCourseButton);
|
||||
expect(screen.queryByText(newCourseContainerText)).toBeInTheDocument();
|
||||
|
||||
const cancelButton = screen.getByRole('button', { name: 'Cancel' });
|
||||
fireEvent.click(cancelButton);
|
||||
expect(screen.queryByText(newCourseContainerText)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('contact administrator card', () => {
|
||||
const adminCardTitleText = 'Are you staff on an existing Studio course?';
|
||||
|
||||
it('should show the "contact administrator" card with no "add course" buttons', () => {
|
||||
mockUseSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
courses: [],
|
||||
courseCreatorStatus: COURSE_CREATOR_STATES.pending,
|
||||
});
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
const administratorCardTitle = screen.getByText(adminCardTitleText);
|
||||
expect(administratorCardTitle).toBeVisible();
|
||||
expect(screen.queryByText('Create your first course')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show contact administrator card with add course buttons', () => {
|
||||
mockUseSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
courses: [],
|
||||
courseCreatorStatus: COURSE_CREATOR_STATES.granted,
|
||||
});
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
const administratorCardTitle = screen.getByText(adminCardTitleText);
|
||||
expect(administratorCardTitle).toBeVisible();
|
||||
const addCourseButton = screen.getByTestId('contact-admin-create-course');
|
||||
expect(addCourseButton).toBeVisible();
|
||||
fireEvent.click(addCourseButton);
|
||||
expect(screen.getByTestId('create-course-form')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
it('should show footer', () => {
|
||||
render(<StudioHome />, { path: '/home' });
|
||||
expect(screen.getByText('Looking for help with Studio?')).toBeInTheDocument();
|
||||
expect(screen.getByText('LMS')).toHaveAttribute('href', process.env.LMS_BASE_URL);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -102,7 +102,6 @@ const StudioHome = () => {
|
||||
iconBefore={AddIcon}
|
||||
size="sm"
|
||||
onClick={newLibraryClick}
|
||||
data-testid="new-library-button"
|
||||
>
|
||||
{intl.formatMessage(messages.addNewLibraryBtnText)}
|
||||
</Button>,
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { default as studioHomeMock } from './studioHomeMock';
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
activeTab: 'courses',
|
||||
allowCourseReruns: true,
|
||||
allowedOrganizations: ['edx', 'org'],
|
||||
@@ -1,15 +1,15 @@
|
||||
import * as reactRedux from 'react-redux';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
|
||||
import { studioHomeMock } from '../__mocks__';
|
||||
import messages from '../messages';
|
||||
import { trimSlashes } from './utils';
|
||||
import {
|
||||
fireEvent,
|
||||
initializeMocks,
|
||||
render,
|
||||
screen,
|
||||
} from '../../testUtils';
|
||||
} from '@src/testUtils';
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
import messages from '../messages';
|
||||
import { trimSlashes } from './utils';
|
||||
import CardItem from '.';
|
||||
|
||||
jest.spyOn(reactRedux, 'useSelector').mockImplementation(() => studioHomeMock);
|
||||
|
||||
@@ -9,12 +9,12 @@ import { AppProvider } from '@edx/frontend-platform/react';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
import { COURSE_CREATOR_STATES } from '../../constants';
|
||||
import initializeStore from '../../store';
|
||||
import { executeThunk } from '../../utils';
|
||||
import { requestCourseCreatorQuery } from '../data/thunks';
|
||||
import { getRequestCourseCreatorUrl } from '../data/api';
|
||||
import { studioHomeMock } from '../__mocks__';
|
||||
import messages from './messages';
|
||||
import CollapsibleStateWithAction from '.';
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { initializeMockApp } from '@edx/frontend-platform';
|
||||
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { studioHomeMock } from '../__mocks__';
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
import initializeStore from '../../store';
|
||||
import messages from './messages';
|
||||
import CourseNewCourseForm from '.';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { initializeMocks, render } from '../../testUtils';
|
||||
import { COURSE_CREATOR_STATES } from '../../constants';
|
||||
import { studioHomeMock } from '../__mocks__';
|
||||
import { initializeMocks, render } from '@src/testUtils';
|
||||
import { COURSE_CREATOR_STATES } from '@src/constants';
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
import HomeSidebar from '.';
|
||||
|
||||
jest.mock('react-redux', () => ({
|
||||
|
||||
@@ -5,8 +5,8 @@ import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import { initializeMockApp } from '@edx/frontend-platform';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
import initializeStore from '../../store';
|
||||
import { studioHomeMock } from '../__mocks__';
|
||||
import messages from './messages';
|
||||
import ProcessingCourses from '.';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Routes, Route, useLocation } from 'react-router-dom';
|
||||
import { getConfig, setConfig } from '@edx/frontend-platform';
|
||||
|
||||
import { studioHomeMock } from '../__mocks__';
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
import messages from '../messages';
|
||||
import tabMessages from './messages';
|
||||
import TabsSection from '.';
|
||||
|
||||
@@ -3,9 +3,9 @@ import {
|
||||
initializeMocks,
|
||||
render,
|
||||
screen,
|
||||
} from '../../../testUtils';
|
||||
import { COURSE_CREATOR_STATES } from '../../../constants';
|
||||
import { studioHomeMock } from '../../__mocks__';
|
||||
} from '@src/testUtils';
|
||||
import { COURSE_CREATOR_STATES } from '@src/constants';
|
||||
import studioHomeMock from '@src/studio-home/__mocks__/studioHomeMock';
|
||||
import { initialState } from '../../factories/mockApiResponses';
|
||||
|
||||
import CoursesTab from '.';
|
||||
|
||||
Reference in New Issue
Block a user