diff --git a/src/grading-settings/grading-sidebar/GradingSidebar.test.jsx b/src/grading-settings/grading-sidebar/GradingSidebar.test.jsx index 33da030b5b..64e8df482d 100644 --- a/src/grading-settings/grading-sidebar/GradingSidebar.test.jsx +++ b/src/grading-settings/grading-sidebar/GradingSidebar.test.jsx @@ -1,11 +1,14 @@ -import React from 'react'; import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n'; import { render } from '@testing-library/react'; +import { initializeMockApp } from '@edx/frontend-platform'; +import { AppProvider } from '@edx/frontend-platform/react'; +import initializeStore from '../../store'; import messages from './messages'; import GradingSidebar from '.'; const mockPathname = '/foo-bar'; +let store; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -16,11 +19,31 @@ jest.mock('react-router-dom', () => ({ const RootWrapper = () => ( - + + + ); describe('', () => { + beforeEach(async () => { + initializeMockApp({ + authenticatedUser: { + userId: 3, + username: 'abc123', + administrator: false, + roles: [], + }, + }); + + store = initializeStore({ + courseDetail: { + courseId: 'id', + status: 'sucessful', + }, + }); + }); + it('renders sidebar text content correctly', () => { const { getByText } = render(); expect(getByText(messages.gradingSidebarTitle.defaultMessage)).toBeInTheDocument(); diff --git a/src/group-configurations/experiment-configurations-section/ExperimentConfigurationsSection.test.jsx b/src/group-configurations/experiment-configurations-section/ExperimentConfigurationsSection.test.jsx index 580258006f..6fcd756567 100644 --- a/src/group-configurations/experiment-configurations-section/ExperimentConfigurationsSection.test.jsx +++ b/src/group-configurations/experiment-configurations-section/ExperimentConfigurationsSection.test.jsx @@ -8,12 +8,20 @@ import ExperimentConfigurationsSection from '.'; const handleCreateMock = jest.fn(); const handleDeleteMock = jest.fn(); const handleEditMock = jest.fn(); +const mockPathname = '/foo-bar'; const experimentConfigurationActions = { handleCreate: handleCreateMock, handleDelete: handleDeleteMock, handleEdit: handleEditMock, }; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: () => ({ + pathname: mockPathname, + }), +})); + const renderComponent = (props) => render( { expect(screen.queryByRole('heading', { name: 'Content permissions' })).not.toBeInTheDocument(); }); + it('show content permissions section if Learning Assistant app is enabled', async () => { const initialState = { models: { diff --git a/src/pages-and-resources/pages/PageCard.test.jsx b/src/pages-and-resources/pages/PageCard.test.jsx index 33ecf5f4b4..376ef757cf 100644 --- a/src/pages-and-resources/pages/PageCard.test.jsx +++ b/src/pages-and-resources/pages/PageCard.test.jsx @@ -3,7 +3,7 @@ import { queryAllByRole, } from '@testing-library/react'; -import { initializeMockApp } from '@edx/frontend-platform'; +import { initializeMockApp, getConfig } from '@edx/frontend-platform'; import { AppProvider } from '@edx/frontend-platform/react'; import { IntlProvider } from '@edx/frontend-platform/i18n'; @@ -14,24 +14,33 @@ import PagesAndResourcesProvider from '../PagesAndResourcesProvider'; let container; let store; +const mockPageConfig = [ + { + id: '1', + legacyLink: `${getConfig().STUDIO_BASE_URL}/tabs/course-v1:OpenedX+DemoX+DemoCourse`, + name: 'Custom pages', + }, + { + id: '2', + legacyLink: `${getConfig().STUDIO_BASE_URL}/textbooks/course-v1:OpenedX+DemoX+DemoCourse`, + name: 'Textbook', + enabled: true, + }, + { + name: 'Page', + allowedOperations: { + enable: true, + }, + id: '3', + }, +]; const renderComponent = () => { const wrapper = render( - + , @@ -61,9 +70,11 @@ describe('LiveSettings', () => { renderComponent(); expect(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', 'SomeUrl'); + expect(textbookSettingsButton).toHaveAttribute('href', textbookPagePath); }); }); diff --git a/src/pages-and-resources/pages/PageSettingButton.jsx b/src/pages-and-resources/pages/PageSettingButton.jsx index 803bcc6886..63f6f8521e 100644 --- a/src/pages-and-resources/pages/PageSettingButton.jsx +++ b/src/pages-and-resources/pages/PageSettingButton.jsx @@ -20,25 +20,25 @@ const PageSettingButton = ({ const { formatMessage } = useIntl(); const { path: pagesAndResourcesPath } = useContext(PagesAndResourcesContext); const navigate = useNavigate(); - const { waffleFlags } = useSelector(getStudioHomeData); + const studioHomeData = useSelector(getStudioHomeData); const linkTo = useMemo(() => { if (!legacyLink) { return null; } if (legacyLink.includes('textbooks')) { - return waffleFlags?.ENABLE_NEW_TEXTBOOKS_PAGE + return studioHomeData?.waffleFlags?.ENABLE_NEW_TEXTBOOKS_PAGE ? `/course/${courseId}/${id.replace('_', '-')}` : legacyLink; } if (legacyLink.includes('tabs')) { - return waffleFlags?.ENABLE_NEW_CUSTOM_PAGES + return studioHomeData?.waffleFlags?.ENABLE_NEW_CUSTOM_PAGES ? `/course/${courseId}/${id.replace('_', '-')}` : legacyLink; } return null; - }, [legacyLink, waffleFlags, id]); + }, [legacyLink, studioHomeData?.waffleFlags, id]); const canConfigureOrEnable = allowedOperations?.configure || allowedOperations?.enable; diff --git a/src/studio-home/card-item/CardItem.test.tsx b/src/studio-home/card-item/CardItem.test.tsx index e2cd393459..986cb40997 100644 --- a/src/studio-home/card-item/CardItem.test.tsx +++ b/src/studio-home/card-item/CardItem.test.tsx @@ -43,7 +43,7 @@ describe('', () => { const dropDownMenu = screen.getByTestId('toggle-dropdown'); fireEvent.click(dropDownMenu); const btnReRunCourse = screen.getByText(messages.btnReRunText.defaultMessage); - expect(btnReRunCourse).toHaveAttribute('href', trimSlashes(props.rerunLink)); + expect(btnReRunCourse).toHaveAttribute('href', `/${trimSlashes(props.rerunLink)}`); const viewLiveLink = screen.getByText(messages.viewLiveBtnText.defaultMessage); expect(viewLiveLink).toHaveAttribute('href', props.lmsLink); }); diff --git a/src/studio-home/data/api.js b/src/studio-home/data/api.js index 6602a4e370..5e80bc6182 100644 --- a/src/studio-home/data/api.js +++ b/src/studio-home/data/api.js @@ -2,6 +2,8 @@ import { camelCaseObject, snakeCaseObject, getConfig } from '@edx/frontend-platform'; import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; +import { getWaffleFlagsConfig } from './utils'; + export const getApiBaseUrl = () => getConfig().STUDIO_BASE_URL; export const getStudioHomeApiUrl = () => new URL('api/contentstore/v1/home', getApiBaseUrl()).href; export const getRequestCourseCreatorUrl = () => new URL('request_course_creator', getApiBaseUrl()).href; @@ -14,30 +16,11 @@ export const getCourseNotificationUrl = (url) => new URL(url, getApiBaseUrl()).h export async function getStudioHomeData() { const { data } = await getAuthenticatedHttpClient().get(getStudioHomeApiUrl()); - const result = camelCaseObject(data); - - const waffleFlagsConfig = { - ENABLE_NEW_SCHEDULE_AND_DETAILS_PAGE: result?.waffleFlags?.contentstoreNewStudioMfeUseNewScheduleDetailsPage, - ENABLE_NEW_GRADING_PAGE: result?.waffleFlags?.contentstoreNewStudioMfeUseNewGradingPage, - ENABLE_NEW_COURSE_TEAM_PAGE: result?.waffleFlags?.contentstoreNewStudioMfeUseNewCourseTeamPage, - ENABLE_NEW_GROUP_CONFIGURATIONS_PAGE: result?.waffleFlags?.contentstoreNewStudioMfeUseNewGroupConfigurationsPage, - ENABLE_NEW_ADVANCED_SETTINGS_PAGE: result?.waffleFlags?.contentstoreNewStudioMfeUseNewAdvancedSettingsPage, - ENABLE_NEW_COURSE_OUTLINE_PAGE: result?.waffleFlags?.contentstoreNewStudioMfeUseNewCourseOutlinePage, - ENABLE_NEW_COURSE_UPDATES_PAGE: result?.waffleFlags?.contentstoreNewStudioMfeUseNewUpdatesPage, - ENABLE_NEW_FILE_UPLOAD_PAGE: result?.waffleFlags?.contentstoreNewStudioMfeUseNewFilesUploadsPage, - ENABLE_NEW_PAGES_AND_RESOURCES_PAGE: result?.waffleFlags?.contentstoreNewStudioMfeUseNewCustomPages, - ENABLE_NEW_IMPORT_PAGE: result?.waffleFlags?.contentstoreNewStudioMfeUseNewImportPage, - ENABLE_NEW_EXPORT_PAGE: result?.waffleFlags?.contentstoreNewStudioMfeUseNewExportPage, - ENABLE_NEW_HOME_PAGE: result?.waffleFlags?.newStudioMfeUseNewHomePage, - ENABLE_NEW_TEXTBOOKS_PAGE: result?.waffleFlags?.contentstoreNewStudioMfeUseNewTextbooksPage, - ENABLE_NEW_CUSTOM_PAGES: result?.waffleFlags?.contentstoreNewStudioMfeUseNewCustomPages, - ENABLE_NEW_VIDEO_UPLOAD_PAGE: result?.waffleFlags?.contentstoreNewStudioMfeUseNewVideoUploadsPage, - ENABLE_NEW_CERTIFICATES_PAGE: result?.waffleFlags?.contentstoreNewStudioMfeUseNewCertificatesPage, - ENABLE_NEW_UNIT_PAGE: result?.waffleFlags?.contentstoreNewStudioMfeUseNewUnitPage, + const result = { + ...camelCaseObject(data), + waffleFlags: getWaffleFlagsConfig(camelCaseObject(data)), }; - result.waffleFlags = waffleFlagsConfig; - return result; } diff --git a/src/studio-home/data/selectors.js b/src/studio-home/data/selectors.js index 5487ca42b6..97e525fb5b 100644 --- a/src/studio-home/data/selectors.js +++ b/src/studio-home/data/selectors.js @@ -1,4 +1,4 @@ -export const getStudioHomeData = (state) => state.studioHome.studioHomeData; +export const getStudioHomeData = (state) => state.studioHome?.studioHomeData; export const getLoadingStatuses = (state) => state.studioHome.loadingStatuses; export const getSavingStatuses = (state) => state.studioHome.savingStatuses; export const getStudioHomeCoursesParams = (state) => state.studioHome.studioHomeCoursesRequestParams; diff --git a/src/studio-home/data/utils.js b/src/studio-home/data/utils.js new file mode 100644 index 0000000000..81d6d885f5 --- /dev/null +++ b/src/studio-home/data/utils.js @@ -0,0 +1,27 @@ +/** + * Retrieves the waffle flag configuration based on the provided result object. + * + * @param {Object} result - The result object containing waffle flags. + * @param {Object} result.waffleFlags - The waffle flags for feature toggling. + * @returns {Object} The configuration object with feature toggles. + */ +// eslint-disable-next-line import/prefer-default-export +export const getWaffleFlagsConfig = ({ waffleFlags = {} }) => ({ + ENABLE_NEW_SCHEDULE_AND_DETAILS_PAGE: waffleFlags.contentstoreNewStudioMfeUseNewScheduleDetailsPage ?? false, + ENABLE_NEW_GRADING_PAGE: waffleFlags.contentstoreNewStudioMfeUseNewGradingPage ?? false, + ENABLE_NEW_COURSE_TEAM_PAGE: waffleFlags.contentstoreNewStudioMfeUseNewCourseTeamPage ?? false, + ENABLE_NEW_GROUP_CONFIGURATIONS_PAGE: waffleFlags.contentstoreNewStudioMfeUseNewGroupConfigurationsPage ?? false, + ENABLE_NEW_ADVANCED_SETTINGS_PAGE: waffleFlags.contentstoreNewStudioMfeUseNewAdvancedSettingsPage ?? false, + ENABLE_NEW_COURSE_OUTLINE_PAGE: waffleFlags.contentstoreNewStudioMfeUseNewCourseOutlinePage ?? false, + ENABLE_NEW_COURSE_UPDATES_PAGE: waffleFlags.contentstoreNewStudioMfeUseNewUpdatesPage ?? false, + ENABLE_NEW_FILE_UPLOAD_PAGE: waffleFlags.contentstoreNewStudioMfeUseNewFilesUploadsPage ?? false, + ENABLE_NEW_PAGES_AND_RESOURCES_PAGE: waffleFlags.contentstoreNewStudioMfeUseNewCustomPages ?? false, + ENABLE_NEW_IMPORT_PAGE: waffleFlags.contentstoreNewStudioMfeUseNewImportPage ?? false, + ENABLE_NEW_EXPORT_PAGE: waffleFlags.contentstoreNewStudioMfeUseNewExportPage ?? false, + ENABLE_NEW_HOME_PAGE: waffleFlags.newStudioMfeUseNewHomePage ?? false, + ENABLE_NEW_TEXTBOOKS_PAGE: waffleFlags.contentstoreNewStudioMfeUseNewTextbooksPage ?? false, + ENABLE_NEW_CUSTOM_PAGES: waffleFlags.contentstoreNewStudioMfeUseNewCustomPages ?? false, + ENABLE_NEW_VIDEO_UPLOAD_PAGE: waffleFlags.contentstoreNewStudioMfeUseNewVideoUploadsPage ?? false, + ENABLE_NEW_CERTIFICATES_PAGE: waffleFlags.contentstoreNewStudioMfeUseNewCertificatesPage ?? false, + ENABLE_NEW_UNIT_PAGE: waffleFlags.contentstoreNewStudioMfeUseNewUnitPage ?? false, +}); diff --git a/src/studio-home/factories/mockApiResponses.jsx b/src/studio-home/factories/mockApiResponses.jsx index 8d86e22d4a..ce5fdf90e2 100644 --- a/src/studio-home/factories/mockApiResponses.jsx +++ b/src/studio-home/factories/mockApiResponses.jsx @@ -45,6 +45,25 @@ export const generateGetStudioHomeDataApiResponse = () => ({ platformName: 'Your Platform Name Here', userIsActive: true, allowToCreateNewOrg: false, + waffleFlags: { + ENABLE_NEW_ADVANCED_SETTINGS_PAGE: false, + ENABLE_NEW_CERTIFICATES_PAGE: false, + ENABLE_NEW_COURSE_OUTLINE_PAGE: false, + ENABLE_NEW_COURSE_TEAM_PAGE: false, + ENABLE_NEW_COURSE_UPDATES_PAGE: false, + ENABLE_NEW_CUSTOM_PAGES: false, + ENABLE_NEW_EXPORT_PAGE: false, + ENABLE_NEW_FILE_UPLOAD_PAGE: false, + ENABLE_NEW_GRADING_PAGE: false, + ENABLE_NEW_GROUP_CONFIGURATIONS_PAGE: false, + ENABLE_NEW_HOME_PAGE: false, + ENABLE_NEW_IMPORT_PAGE: false, + ENABLE_NEW_PAGES_AND_RESOURCES_PAGE: false, + ENABLE_NEW_SCHEDULE_AND_DETAILS_PAGE: false, + ENABLE_NEW_TEXTBOOKS_PAGE: false, + ENABLE_NEW_UNIT_PAGE: false, + ENABLE_NEW_VIDEO_UPLOAD_PAGE: false, + }, }); /** Mock for the deprecated /api/contentstore/v1/home/courses endpoint. Note this endpoint is NOT paginated. */ diff --git a/src/studio-home/verify-email-layout/VerifyEmailLayout.test.jsx b/src/studio-home/verify-email-layout/VerifyEmailLayout.test.jsx index ed5f507831..785fb484f0 100644 --- a/src/studio-home/verify-email-layout/VerifyEmailLayout.test.jsx +++ b/src/studio-home/verify-email-layout/VerifyEmailLayout.test.jsx @@ -1,11 +1,15 @@ -import React from 'react'; import { render } from '@testing-library/react'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { initializeMockApp } from '@edx/frontend-platform'; +import { AppProvider } from '@edx/frontend-platform/react'; +import initializeStore from '../../store'; import messages from './messages'; import VerifyEmailLayout from '.'; +let store; + const mockPathname = '/foo-bar'; const fakeAuthenticatedUser = { email: 'email@fake.com', @@ -18,28 +22,52 @@ jest.mock('react-router-dom', () => ({ pathname: mockPathname, }), })); + jest.mock('@edx/frontend-platform/auth'); + getAuthenticatedUser.mockImplementation(() => fakeAuthenticatedUser); const RootWrapper = () => ( - + + + ); describe('', () => { + beforeEach(async () => { + initializeMockApp({ + authenticatedUser: { + userId: 3, + username: 'abc123', + administrator: false, + roles: [], + }, + }); + + store = initializeStore({ + courseDetail: { + courseId: 'id', + status: 'sucessful', + }, + }); + }); + it('renders successfully', () => { const { getByText } = render(); + expect( getByText(`Thanks for signing up, ${fakeAuthenticatedUser.username}!`, { exact: false, }), ).toBeInTheDocument(); - expect(getByText(messages.bannerTitle.defaultMessage)).toBeInTheDocument(); + expect(getByText( `Almost there! In order to complete your sign up we need you to verify your email address (${fakeAuthenticatedUser.email}). An activation message and next steps should be waiting for you there.`, { exact: false }, )).toBeInTheDocument(); + expect(getByText(messages.sidebarTitle.defaultMessage)).toBeInTheDocument(); expect(getByText(messages.sidebarDescription.defaultMessage)).toBeInTheDocument(); }); diff --git a/src/utils.test.js b/src/utils.test.js index e4aada849f..05e07ddc1a 100644 --- a/src/utils.test.js +++ b/src/utils.test.js @@ -71,7 +71,7 @@ describe('FilesAndUploads utils', () => { getConfig.mockReturnValue({ PUBLIC_PATH: 'example.com/' }); getPath.mockReturnValue('/course-authoring/'); - const checkPath = '/some/path'; + const checkPath = '/course-authoring/some/path'; const result = createCorrectInternalRoute(checkPath); expect(result).toBe('/course-authoring/some/path');