diff --git a/src/course-rerun/CourseRerun.test.jsx b/src/course-rerun/CourseRerun.test.jsx
index 6f5cc97d4..cc1373bfa 100644
--- a/src/course-rerun/CourseRerun.test.jsx
+++ b/src/course-rerun/CourseRerun.test.jsx
@@ -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';
diff --git a/src/course-rerun/course-rerun-form/CourseRerunForm.test.jsx b/src/course-rerun/course-rerun-form/CourseRerunForm.test.jsx
index 5b73f1db3..f26b1943f 100644
--- a/src/course-rerun/course-rerun-form/CourseRerunForm.test.jsx
+++ b/src/course-rerun/course-rerun-form/CourseRerunForm.test.jsx
@@ -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 '.';
diff --git a/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.test.jsx b/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.test.jsx
index 682e8e636..e79b38fb6 100644
--- a/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.test.jsx
+++ b/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.test.jsx
@@ -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';
diff --git a/src/generic/create-or-rerun-course/factories/mockApiResponses.jsx b/src/generic/create-or-rerun-course/factories/mockApiResponses.jsx
index ca3e0b347..db008301b 100644
--- a/src/generic/create-or-rerun-course/factories/mockApiResponses.jsx
+++ b/src/generic/create-or-rerun-course/factories/mockApiResponses.jsx
@@ -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';
diff --git a/src/library-authoring/LibraryAuthoringPage.test.tsx b/src/library-authoring/LibraryAuthoringPage.test.tsx
index dd3ed50a9..d84ff75d7 100644
--- a/src/library-authoring/LibraryAuthoringPage.test.tsx
+++ b/src/library-authoring/LibraryAuthoringPage.test.tsx
@@ -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';
diff --git a/src/library-authoring/add-content/AddContentWorkflow.test.tsx b/src/library-authoring/add-content/AddContentWorkflow.test.tsx
index 8244fc716..dab4dee98 100644
--- a/src/library-authoring/add-content/AddContentWorkflow.test.tsx
+++ b/src/library-authoring/add-content/AddContentWorkflow.test.tsx
@@ -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';
diff --git a/src/library-authoring/add-content/PickLibraryContentModal.test.tsx b/src/library-authoring/add-content/PickLibraryContentModal.test.tsx
index 03126f610..0fed30629 100644
--- a/src/library-authoring/add-content/PickLibraryContentModal.test.tsx
+++ b/src/library-authoring/add-content/PickLibraryContentModal.test.tsx
@@ -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';
diff --git a/src/library-authoring/create-library/CreateLibrary.test.tsx b/src/library-authoring/create-library/CreateLibrary.test.tsx
index f279f13a1..eb45d8007 100644
--- a/src/library-authoring/create-library/CreateLibrary.test.tsx
+++ b/src/library-authoring/create-library/CreateLibrary.test.tsx
@@ -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';
diff --git a/src/library-authoring/routes.test.tsx b/src/library-authoring/routes.test.tsx
index cc819602d..c07de78c6 100644
--- a/src/library-authoring/routes.test.tsx
+++ b/src/library-authoring/routes.test.tsx
@@ -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();
diff --git a/src/studio-home/StudioHome.test.jsx b/src/studio-home/StudioHome.test.jsx
deleted file mode 100644
index 8e6eca953..000000000
--- a/src/studio-home/StudioHome.test.jsx
+++ /dev/null
@@ -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 = () => (
-
-
- }
- />
- }
- />
- }
- />
- ,
-
-);
-
-describe('', () => {
- 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();
- waitFor(() => {
- expect(getByText(messages.homePageLoadFailedMessage.defaultMessage)).toBeInTheDocument();
- });
- });
-
- it('should render Studio home title', () => {
- const { getByText } = render();
- 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();
- 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();
- 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();
- 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();
- 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();
- 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();
- 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();
- 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();
- 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();
- 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();
- 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();
-
- 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();
- 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();
- 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();
- 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/StudioHome.test.tsx b/src/studio-home/StudioHome.test.tsx
new file mode 100644
index 000000000..40b104d9f
--- /dev/null
+++ b/src/studio-home/StudioHome.test.tsx
@@ -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('', () => {
+ 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(, { path: '/home' });
+ expect(screen.getByText('Failed to load Studio home. Please try again later.')).toBeInTheDocument();
+ });
+
+ it('should render Studio home title', async () => {
+ render(, { 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(, { 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(, { 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(, { 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(, { 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(, { 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(, { 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(, { 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(, { 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(, { 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(, { 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(, { 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(, { 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(, { 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(, { path: '/home' });
+ expect(screen.getByText('Looking for help with Studio?')).toBeInTheDocument();
+ expect(screen.getByText('LMS')).toHaveAttribute('href', process.env.LMS_BASE_URL);
+ });
+ });
+});
diff --git a/src/studio-home/StudioHome.tsx b/src/studio-home/StudioHome.tsx
index 57c6ad90d..32915bf25 100644
--- a/src/studio-home/StudioHome.tsx
+++ b/src/studio-home/StudioHome.tsx
@@ -102,7 +102,6 @@ const StudioHome = () => {
iconBefore={AddIcon}
size="sm"
onClick={newLibraryClick}
- data-testid="new-library-button"
>
{intl.formatMessage(messages.addNewLibraryBtnText)}
,
diff --git a/src/studio-home/__mocks__/index.js b/src/studio-home/__mocks__/index.js
deleted file mode 100644
index 76d31279a..000000000
--- a/src/studio-home/__mocks__/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default as studioHomeMock } from './studioHomeMock';
diff --git a/src/studio-home/__mocks__/studioHomeMock.js b/src/studio-home/__mocks__/studioHomeMock.ts
similarity index 99%
rename from src/studio-home/__mocks__/studioHomeMock.js
rename to src/studio-home/__mocks__/studioHomeMock.ts
index 25e210eee..28a70ab3a 100644
--- a/src/studio-home/__mocks__/studioHomeMock.js
+++ b/src/studio-home/__mocks__/studioHomeMock.ts
@@ -1,4 +1,4 @@
-module.exports = {
+export default {
activeTab: 'courses',
allowCourseReruns: true,
allowedOrganizations: ['edx', 'org'],
diff --git a/src/studio-home/card-item/CardItem.test.tsx b/src/studio-home/card-item/CardItem.test.tsx
index 189b112f1..b033f789d 100644
--- a/src/studio-home/card-item/CardItem.test.tsx
+++ b/src/studio-home/card-item/CardItem.test.tsx
@@ -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);
diff --git a/src/studio-home/collapsible-state-with-action/CollapsibleStateWithAction.test.jsx b/src/studio-home/collapsible-state-with-action/CollapsibleStateWithAction.test.jsx
index 96cc477bc..382ba7568 100644
--- a/src/studio-home/collapsible-state-with-action/CollapsibleStateWithAction.test.jsx
+++ b/src/studio-home/collapsible-state-with-action/CollapsibleStateWithAction.test.jsx
@@ -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 '.';
diff --git a/src/studio-home/create-new-course-form/CourseNewCourseForm.test.jsx b/src/studio-home/create-new-course-form/CourseNewCourseForm.test.jsx
index 26105c39a..f9837f163 100644
--- a/src/studio-home/create-new-course-form/CourseNewCourseForm.test.jsx
+++ b/src/studio-home/create-new-course-form/CourseNewCourseForm.test.jsx
@@ -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 '.';
diff --git a/src/studio-home/home-sidebar/HomeSidebar.test.jsx b/src/studio-home/home-sidebar/HomeSidebar.test.jsx
index a6dca1281..a751a2b71 100644
--- a/src/studio-home/home-sidebar/HomeSidebar.test.jsx
+++ b/src/studio-home/home-sidebar/HomeSidebar.test.jsx
@@ -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', () => ({
diff --git a/src/studio-home/processing-courses/ProcessingCourses.test.jsx b/src/studio-home/processing-courses/ProcessingCourses.test.jsx
index c9f259568..cee84b160 100644
--- a/src/studio-home/processing-courses/ProcessingCourses.test.jsx
+++ b/src/studio-home/processing-courses/ProcessingCourses.test.jsx
@@ -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 '.';
diff --git a/src/studio-home/tabs-section/TabsSection.test.tsx b/src/studio-home/tabs-section/TabsSection.test.tsx
index 0537af17a..67450dc7f 100644
--- a/src/studio-home/tabs-section/TabsSection.test.tsx
+++ b/src/studio-home/tabs-section/TabsSection.test.tsx
@@ -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 '.';
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 4adb7663c..050c94c8a 100644
--- a/src/studio-home/tabs-section/courses-tab/index.test.tsx
+++ b/src/studio-home/tabs-section/courses-tab/index.test.tsx
@@ -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 '.';