[ROLES-47] Permissions definitions for Schedule & Details (#854)

This commit is contained in:
Lucas Calviño
2024-03-08 15:07:56 -03:00
committed by GitHub
parent c754a5e519
commit a2bfb1fb7b
21 changed files with 153 additions and 18 deletions

2
package-lock.json generated
View File

@@ -65,7 +65,7 @@
"@testing-library/user-event": "^13.2.1",
"axios-mock-adapter": "1.22.0",
"glob": "7.2.3",
"husky": "7.0.4",
"husky": "^7.0.4",
"jest-canvas-mock": "^2.5.2",
"jest-expect-message": "^1.1.3",
"react-test-renderer": "17.0.2",

View File

@@ -21,10 +21,13 @@ import scheduleMessages from './schedule-section/messages';
import genericMessages from '../generic/help-sidebar/messages';
import messages from './messages';
import ScheduleAndDetails from '.';
import { getUserPermissionsUrl, getUserPermissionsEnabledFlagUrl } from '../generic/data/api';
import { fetchUserPermissionsQuery, fetchUserPermissionsEnabledFlag } from '../generic/data/thunks';
let axiosMock;
let store;
const courseId = '123';
const userId = 3;
// Mock the tinymce lib
jest.mock('@tinymce/tinymce-react', () => {
@@ -50,6 +53,18 @@ jest.mock('react-textarea-autosize', () => jest.fn((props) => (
<textarea {...props} onFocus={() => {}} onBlur={() => {}} />
)));
const permissionsMockStore = async (permissions) => {
axiosMock.onGet(getUserPermissionsUrl(courseId, userId)).reply(200, permissions);
axiosMock.onGet(getUserPermissionsEnabledFlagUrl).reply(200, { enabled: true });
await executeThunk(fetchUserPermissionsQuery(courseId), store.dispatch);
await executeThunk(fetchUserPermissionsEnabledFlag(), store.dispatch);
};
const permissionDisabledMockStore = async () => {
axiosMock.onGet(getUserPermissionsEnabledFlagUrl).reply(200, { enabled: false });
await executeThunk(fetchUserPermissionsEnabledFlag(), store.dispatch);
};
const RootWrapper = () => (
<AppProvider store={store}>
<IntlProvider locale="en" messages={{}}>
@@ -62,7 +77,7 @@ describe('<ScheduleAndDetails />', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
userId,
username: 'abc123',
administrator: true,
roles: [],
@@ -111,6 +126,30 @@ describe('<ScheduleAndDetails />', () => {
});
});
it('should shows the PermissionDeniedAlert when there are not the right user permissions', async () => {
const permissionsData = { permissions: ['wrong_permission'] };
await permissionsMockStore(permissionsData);
const { queryByText } = render(<RootWrapper />);
await waitFor(() => {
const permissionDeniedAlert = queryByText('You are not authorized to view this page. If you feel you should have access, please reach out to your course team admin to be given access.');
expect(permissionDeniedAlert).toBeInTheDocument();
});
});
it('should not show the PermissionDeniedAlert when the User Permissions Flag is not enabled', async () => {
await permissionDisabledMockStore();
const { queryByText, getAllByText } = render(<RootWrapper />);
await waitFor(() => {
const permissionDeniedAlert = queryByText('You are not authorized to view this page. If you feel you should have access, please reach out to your course team admin to be given access.');
const scheduleAndDetailElements = getAllByText(messages.headingTitle.defaultMessage);
const scheduleAndDetailTitle = scheduleAndDetailElements[0];
expect(permissionDeniedAlert).not.toBeInTheDocument();
expect(scheduleAndDetailTitle).toBeInTheDocument();
});
});
it('should hide credit section with condition', async () => {
const updatedResponse = {
...courseSettingsMock,

View File

@@ -19,6 +19,7 @@ module.exports = {
enrollmentEndEditable: true,
isCreditCourse: true,
isEntranceExamsEnabled: true,
isEditable: true,
isPrerequisiteCoursesEnabled: true,
languageOptions: [
['en', 'English'],

View File

@@ -18,6 +18,7 @@ describe('<DetailsSection />', () => {
language: courseSettingsMock.languageOptions[1][0],
languageOptions: courseSettingsMock.languageOptions,
onChange: onChangeMock,
isEditable: courseSettingsMock.isEditable,
};
it('renders details section successfully', () => {
@@ -57,4 +58,10 @@ describe('<DetailsSection />', () => {
getByRole('button', { name: messages.dropdownEmpty.defaultMessage }),
).toBeInTheDocument();
});
it('should disable the dropdown if isEditable is false', () => {
const initialProps = { ...props, isEditable: false };
const { getByRole } = render(<RootWrapper {...initialProps} />);
expect(getByRole('button').disabled).toEqual(true);
});
});

View File

@@ -7,7 +7,7 @@ import SectionSubHeader from '../../generic/section-sub-header';
import messages from './messages';
const DetailsSection = ({
language, languageOptions, onChange,
language, languageOptions, onChange, isEditable,
}) => {
const intl = useIntl();
const formattedLanguage = () => {
@@ -24,7 +24,7 @@ const DetailsSection = ({
<Form.Group className="form-group-custom dropdown-language">
<Form.Label>{intl.formatMessage(messages.dropdownLabel)}</Form.Label>
<Dropdown className="bg-white">
<Dropdown.Toggle variant="outline-primary" id="languageDropdown">
<Dropdown.Toggle variant="outline-primary" id="languageDropdown" disabled={!isEditable}>
{formattedLanguage()}
</Dropdown.Toggle>
<Dropdown.Menu>
@@ -56,6 +56,7 @@ DetailsSection.propTypes = {
PropTypes.arrayOf(PropTypes.string.isRequired).isRequired,
).isRequired,
onChange: PropTypes.func.isRequired,
isEditable: PropTypes.bool.isRequired,
};
export default DetailsSection;

View File

@@ -43,6 +43,9 @@ import LicenseSection from './license-section';
import ScheduleSidebar from './schedule-sidebar';
import messages from './messages';
import { useLoadValuesPrompt, useSaveValuesPrompt } from './hooks';
import { useUserPermissions } from '../generic/hooks';
import { getUserPermissionsEnabled } from '../generic/data/selectors';
import PermissionDeniedAlert from '../generic/PermissionDeniedAlert';
const ScheduleAndDetails = ({ intl, courseId }) => {
const courseSettings = useSelector(getCourseSettings);
@@ -53,6 +56,12 @@ const ScheduleAndDetails = ({ intl, courseId }) => {
|| loadingSettingsStatus === RequestStatus.IN_PROGRESS;
const course = useModel('courseDetails', courseId);
const { checkPermission } = useUserPermissions();
const userPermissionsEnabled = useSelector(getUserPermissionsEnabled);
const showPermissionDeniedAlert = userPermissionsEnabled && (
!checkPermission('manage_course_settings') && !checkPermission('view_course_settings')
);
const canEdit = (!userPermissionsEnabled) ? true : checkPermission('manage_course_settings');
document.title = getPageHeadTitle(course?.name, intl.formatMessage(messages.headingTitle));
const {
@@ -145,6 +154,12 @@ const ScheduleAndDetails = ({ intl, courseId }) => {
return <></>;
}
if (showPermissionDeniedAlert) {
return (
<PermissionDeniedAlert />
);
}
if (loadingDetailsStatus === RequestStatus.DENIED || loadingSettingsStatus === RequestStatus.DENIED) {
return (
<div className="row justify-content-center m-6">
@@ -266,12 +281,14 @@ const ScheduleAndDetails = ({ intl, courseId }) => {
certificatesDisplayBehavior={certificatesDisplayBehavior}
canShowCertificateAvailableDateField={canShowCertificateAvailableDateField}
onChange={handleValuesChange}
isEditable={canEdit}
/>
{aboutPageEditable && (
<DetailsSection
language={language}
languageOptions={languageOptions}
onChange={handleValuesChange}
isEditable={canEdit}
/>
)}
<IntroducingSection
@@ -292,6 +309,7 @@ const ScheduleAndDetails = ({ intl, courseId }) => {
enableExtendedCourseDetails={enableExtendedCourseDetails}
videoThumbnailImageAssetPath={videoThumbnailImageAssetPath}
onChange={handleValuesChange}
isEditable={canEdit}
/>
{enableExtendedCourseDetails && (
<>
@@ -319,12 +337,14 @@ const ScheduleAndDetails = ({ intl, courseId }) => {
isPrerequisiteCoursesEnabled
}
onChange={handleValuesChange}
isEditable={canEdit}
/>
)}
{licensingEnabled && (
<LicenseSection
license={license}
onChange={handleValuesChange}
isEditable={canEdit}
/>
)}
</div>

View File

@@ -47,7 +47,7 @@ const {
} = courseDetailsMock;
const {
aboutPageEditable, sidebarHtmlEnabled, shortDescriptionEditable, lmsLinkForAboutPage,
aboutPageEditable, sidebarHtmlEnabled, shortDescriptionEditable, lmsLinkForAboutPage, isEditable,
} = courseSettingsMock;
const props = {
@@ -63,6 +63,7 @@ const props = {
courseImageAssetPath,
shortDescriptionEditable,
onChange: onChangeMock,
isEditable,
};
describe('<IntroducingSection />', () => {
@@ -98,4 +99,13 @@ describe('<IntroducingSection />', () => {
expect(queryAllByText(messages.courseOverviewLabel.defaultMessage).length).toBe(0);
expect(queryAllByText(messages.courseAboutSidebarLabel.defaultMessage).length).toBe(0);
});
it('should hide components if isEditable is false', () => {
const initialProps = { ...props, isEditable: false };
const { queryAllByText } = render(<RootWrapper {...initialProps} />);
expect(queryAllByText(messages.introducingTitle.defaultMessage).length).toBe(0);
expect(queryAllByText(messages.introducingDescription.defaultMessage).length).toBe(0);
expect(queryAllByText(messages.courseOverviewLabel.defaultMessage).length).toBe(0);
expect(queryAllByText(messages.courseAboutSidebarLabel.defaultMessage).length).toBe(0);
});
});

View File

@@ -33,6 +33,7 @@ const IntroducingSection = ({
enableExtendedCourseDetails,
videoThumbnailImageAssetPath,
onChange,
isEditable,
}) => {
const overviewHelpText = (
<FormattedMessage
@@ -72,7 +73,7 @@ const IntroducingSection = ({
return (
<section className="section-container introducing-section">
{aboutPageEditable && (
{aboutPageEditable && isEditable && (
<SectionSubHeader
title={intl.formatMessage(messages.introducingTitle)}
description={intl.formatMessage(messages.introducingDescription)}
@@ -87,7 +88,7 @@ const IntroducingSection = ({
onChange={onChange}
/>
)}
{shortDescriptionEditable && (
{shortDescriptionEditable && isEditable && (
<Form.Group className="form-group-custom">
<Form.Label>
{intl.formatMessage(messages.courseShortDescriptionLabel)}
@@ -107,7 +108,7 @@ const IntroducingSection = ({
</Form.Control.Feedback>
</Form.Group>
)}
{aboutPageEditable && (
{aboutPageEditable && isEditable && (
<>
<Form.Group className="form-group-custom">
<Form.Label>{intl.formatMessage(messages.courseOverviewLabel)}</Form.Label>
@@ -160,7 +161,7 @@ const IntroducingSection = ({
/>
</>
)}
{aboutPageEditable && (
{aboutPageEditable && isEditable && (
<IntroductionVideo introVideo={introVideo} onChange={onChange} />
)}
</section>
@@ -200,6 +201,7 @@ IntroducingSection.propTypes = {
enableExtendedCourseDetails: PropTypes.bool.isRequired,
videoThumbnailImageAssetPath: PropTypes.string,
onChange: PropTypes.func.isRequired,
isEditable: PropTypes.bool.isRequired,
};
export default injectIntl(IntroducingSection);

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { render } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { courseDetailsMock } from '../__mocks__';
import { courseDetailsMock, courseSettingsMock } from '../__mocks__';
import messages from './messages';
import LicenseSection from '.';
@@ -17,6 +17,7 @@ const RootWrapper = (props) => (
const props = {
license: courseDetailsMock.license,
onChange: onChangeMock,
isEditable: courseSettingsMock.isEditable,
};
describe('<LicenseSection />', () => {

View File

@@ -10,7 +10,7 @@ import { LICENSE_TYPE } from './constants';
import messages from './messages';
import { useLicenseDetails } from './hooks';
const LicenseSection = ({ license, onChange }) => {
const LicenseSection = ({ license, onChange, isEditable }) => {
const intl = useIntl();
const {
licenseURL,
@@ -29,6 +29,7 @@ const LicenseSection = ({ license, onChange }) => {
<LicenseSelector
licenseType={licenseType}
onChangeLicenseType={handleChangeLicenseType}
isEditable={isEditable}
/>
{licenseType === LICENSE_TYPE.creativeCommons && (
<LicenseCommonsOptions
@@ -52,6 +53,7 @@ LicenseSection.defaultProps = {
LicenseSection.propTypes = {
license: PropTypes.string,
onChange: PropTypes.func.isRequired,
isEditable: PropTypes.bool.isRequired,
};
export default LicenseSection;

View File

@@ -18,6 +18,7 @@ const RootWrapper = (props) => (
const props = {
licenseType: LICENSE_TYPE.allRightsReserved,
onChangeLicenseType: onChangeLicenseTypeMock,
isEditable: true,
};
describe('<LicenseSelector />', () => {
@@ -60,4 +61,13 @@ describe('<LicenseSelector />', () => {
expect(buttonFirst).toHaveClass('btn btn-outline-primary');
expect(buttonSecond).toHaveClass('btn btn-outline-primary');
});
it('should show disabled buttons if isEditable is false', () => {
const initialProps = { ...props, isEditable: false };
const { getByRole } = render(<RootWrapper {...initialProps} />);
const buttonFirst = getByRole('button', { name: messages.licenseChoice1.defaultMessage });
const buttonSecond = getByRole('button', { name: messages.licenseChoice2.defaultMessage });
expect(buttonFirst.disabled).toEqual(true);
expect(buttonSecond.disabled).toEqual(true);
});
});

View File

@@ -12,7 +12,7 @@ import {
import { LICENSE_TYPE } from '../constants';
import messages from './messages';
const LicenseSelector = ({ licenseType, onChangeLicenseType }) => {
const LicenseSelector = ({ licenseType, onChangeLicenseType, isEditable }) => {
const LICENSE_BUTTON_GROUP_LABELS = {
[LICENSE_TYPE.allRightsReserved]: {
label: <FormattedMessage {...messages.licenseChoice1} />,
@@ -37,6 +37,7 @@ const LicenseSelector = ({ licenseType, onChangeLicenseType }) => {
<Button
variant={isActive ? 'primary' : 'outline-primary'}
onClick={() => onChangeLicenseType(type, 'license')}
disabled={!isEditable}
>
{LICENSE_BUTTON_GROUP_LABELS[type].label}
</Button>
@@ -64,6 +65,7 @@ LicenseSelector.defaultProps = {
LicenseSelector.propTypes = {
licenseType: PropTypes.oneOf(Object.values(LICENSE_TYPE)),
onChangeLicenseType: PropTypes.func.isRequired,
isEditable: PropTypes.bool.isRequired,
};
export default LicenseSelector;

View File

@@ -36,6 +36,7 @@ const {
isEntranceExamsEnabled,
possiblePreRequisiteCourses,
isPrerequisiteCoursesEnabled,
isEditable,
} = courseSettingsMock;
const props = {
@@ -49,6 +50,7 @@ const props = {
entranceExamMinimumScorePct,
isPrerequisiteCoursesEnabled,
onChange: onChangeMock,
isEditable,
};
describe('<RequirementsSection />', () => {
@@ -90,4 +92,10 @@ describe('<RequirementsSection />', () => {
expect(queryAllByLabelText(messages.dropdownLabel.defaultMessage).length).toBe(0);
expect(queryAllByLabelText(entranceExamMessages.requirementsEntrance.defaultMessage).length).toBe(0);
});
it('should disable the dropdown if isEditable is false', () => {
const initialProps = { ...props, isEditable: false };
const { queryByTestId } = render(<RootWrapper {...initialProps} />);
expect(queryByTestId('prerequisite_dropdown').disabled).toEqual(true);
});
});

View File

@@ -4,7 +4,7 @@ import {
} from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { courseDetailsMock } from '../../__mocks__';
import { courseDetailsMock, courseSettingsMock } from '../../__mocks__';
import gradeRequirementsMessages from '../grade-requirements/messages';
import messages from './messages';
import EntranceExam from '.';
@@ -30,6 +30,7 @@ const props = {
isCheckedString: courseDetailsMock.entranceExamEnabled,
entranceExamMinimumScorePct: courseDetailsMock.entranceExamMinimumScorePct,
onChange: onChangeMock,
isEditable: courseSettingsMock.isEditable,
};
describe('<EntranceExam />', () => {
@@ -58,4 +59,11 @@ describe('<EntranceExam />', () => {
).toBe(0);
});
});
it('should disable the checkbox if isEditable is false', () => {
const initialProps = { ...props, isEditable: false };
const { getAllByRole } = render(<RootWrapper {...initialProps} />);
const checkbox = getAllByRole('checkbox')[0];
expect(checkbox.disabled).toEqual(true);
});
});

View File

@@ -13,6 +13,7 @@ const EntranceExam = ({
isCheckedString,
entranceExamMinimumScorePct,
onChange,
isEditable,
}) => {
const { courseId } = useParams();
const showEntranceExam = isCheckedString === 'true';
@@ -33,6 +34,7 @@ const EntranceExam = ({
<Form.Checkbox
checked={showEntranceExam}
onChange={toggleEntranceExam}
disabled={!isEditable}
>
<FormattedMessage {...messages.requirementsEntranceCollapseTitle} />
</Form.Checkbox>
@@ -63,6 +65,7 @@ const EntranceExam = ({
errorEffort={errorEffort}
entranceExamMinimumScorePct={entranceExamMinimumScorePct}
onChange={onChange}
isEditable={isEditable}
/>
</Card.Body>
</>
@@ -83,6 +86,7 @@ EntranceExam.propTypes = {
isCheckedString: PropTypes.string,
entranceExamMinimumScorePct: PropTypes.string,
onChange: PropTypes.func.isRequired,
isEditable: PropTypes.bool.isRequired,
};
export default EntranceExam;

View File

@@ -4,7 +4,7 @@ import {
} from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { courseDetailsMock } from '../../__mocks__';
import { courseDetailsMock, courseSettingsMock } from '../../__mocks__';
import scheduleMessage from '../../messages';
import messages from './messages';
import GradeRequirements from '.';
@@ -21,6 +21,7 @@ const props = {
errorEffort: '',
entranceExamMinimumScorePct: courseDetailsMock.entranceExamMinimumScorePct,
onChange: onChangeMock,
isEditable: courseSettingsMock.isEditable,
};
describe('<GradeRequirements />', () => {
@@ -31,6 +32,13 @@ describe('<GradeRequirements />', () => {
expect(getByDisplayValue(props.entranceExamMinimumScorePct)).toBeInTheDocument();
});
it('disable the input if isEditable is false', () => {
const initialProps = { ...props, isEditable: false };
const { getByDisplayValue } = render(<RootWrapper {...initialProps} />);
const input = getByDisplayValue(props.entranceExamMinimumScorePct);
expect(input.disabled).toEqual(true);
});
it('should call onChange on input change', () => {
const { getByDisplayValue } = render(<RootWrapper {...props} />);
const input = getByDisplayValue(props.entranceExamMinimumScorePct);

View File

@@ -10,6 +10,7 @@ const GradeRequirements = ({
errorEffort,
entranceExamMinimumScorePct,
onChange,
isEditable,
}) => (
<Form.Group
className={classNames('form-group-custom', {
@@ -27,6 +28,7 @@ const GradeRequirements = ({
value={entranceExamMinimumScorePct}
onChange={(e) => onChange(e.target.value, 'entranceExamMinimumScorePct')}
trailingElement="%"
disabled={!isEditable}
/>
</Stack>
{errorEffort && (
@@ -49,6 +51,7 @@ GradeRequirements.propTypes = {
errorEffort: PropTypes.string,
entranceExamMinimumScorePct: PropTypes.string,
onChange: PropTypes.func.isRequired,
isEditable: PropTypes.bool.isRequired,
};
export default GradeRequirements;

View File

@@ -19,6 +19,7 @@ const RequirementsSection = ({
entranceExamMinimumScorePct,
isPrerequisiteCoursesEnabled,
onChange,
isEditable,
}) => {
const intl = useIntl();
const selectedItem = possiblePreRequisiteCourses?.find(
@@ -33,7 +34,7 @@ const RequirementsSection = ({
>
<Form.Label>{intl.formatMessage(messages.dropdownLabel)}</Form.Label>
<Dropdown className="bg-white">
<Dropdown.Toggle id="prerequisiteDropdown" variant="outline-primary">
<Dropdown.Toggle id="prerequisiteDropdown" variant="outline-primary" disabled={!isEditable} data-testid="prerequisite_dropdown">
{formattedSelectedItem}
</Dropdown.Toggle>
<Dropdown.Menu>
@@ -74,6 +75,7 @@ const RequirementsSection = ({
value={effort || ''}
placeholder={TIME_FORMAT.toUpperCase()}
onChange={(e) => onChange(e.target.value, 'effort')}
disabled={!isEditable}
/>
<Form.Control.Feedback>
{intl.formatMessage(messages.timepickerHelpText)}
@@ -87,6 +89,7 @@ const RequirementsSection = ({
isCheckedString={entranceExamEnabled}
entranceExamMinimumScorePct={entranceExamMinimumScorePct}
onChange={onChange}
isEditable={isEditable}
/>
)}
</section>
@@ -125,6 +128,7 @@ RequirementsSection.propTypes = {
entranceExamMinimumScorePct: PropTypes.string,
isPrerequisiteCoursesEnabled: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired,
isEditable: PropTypes.bool.isRequired,
};
export default RequirementsSection;

View File

@@ -125,7 +125,7 @@ const CertificateDisplayRow = ({
<Form.Label>
{intl.formatMessage(messages.certificateBehaviorLabel)}
</Form.Label>
<Dropdown claswsName="bg-white">
<Dropdown className="bg-white">
<Dropdown.Toggle id="certificate-behavior-dropdown" variant="outline-primary">
{certificateDisplayValue}
</Dropdown.Toggle>

View File

@@ -20,6 +20,7 @@ const ScheduleSection = ({
certificatesDisplayBehavior,
canShowCertificateAvailableDateField,
onChange,
isEditable,
}) => {
const intl = useIntl();
const enrollmentEndHelpText = intl.formatMessage(
@@ -42,6 +43,7 @@ const ScheduleSection = ({
],
rowType: SCHEDULE_ROW_TYPES.datetime,
helpText: intl.formatMessage(messages.scheduleCourseStartDateHelpText),
readonly: !isEditable,
controlName: 'startDate',
errorFeedback: errorFields?.startDate,
},
@@ -53,6 +55,7 @@ const ScheduleSection = ({
value: endDate,
rowType: SCHEDULE_ROW_TYPES.datetime,
helpText: intl.formatMessage(messages.scheduleCourseEndDateHelpText),
readonly: !isEditable,
controlName: 'endDate',
errorFeedback: errorFields?.endDate,
},
@@ -73,6 +76,7 @@ const ScheduleSection = ({
value: enrollmentStart,
rowType: SCHEDULE_ROW_TYPES.datetime,
helpText: intl.formatMessage(messages.scheduleEnrollmentStartDateHelpText),
readonly: !isEditable,
controlName: 'enrollmentStart',
errorFeedback: errorFields?.enrollmentStart,
},
@@ -84,7 +88,7 @@ const ScheduleSection = ({
value: enrollmentEnd,
rowType: SCHEDULE_ROW_TYPES.datetime,
helpText: computedEnrollmentEndHelpText,
readonly: !enrollmentEndEditable,
readonly: !enrollmentEndEditable || !isEditable,
controlName: 'enrollmentEnd',
errorFeedback: errorFields?.enrollmentEnd,
},
@@ -165,6 +169,7 @@ ScheduleSection.propTypes = {
certificatesDisplayBehavior: PropTypes.string.isRequired,
canShowCertificateAvailableDateField: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired,
isEditable: PropTypes.bool.isRequired,
};
export default ScheduleSection;

View File

@@ -43,7 +43,7 @@ describe('studio-home api calls', () => {
expect(result).toEqual(expected);
});
fit('should get studio courses data', async () => {
it('should get studio courses data', async () => {
const apiLink = `${getApiBaseUrl()}/api/contentstore/v1/home/courses`;
axiosMock.onGet(apiLink).reply(200, generateGetStudioCoursesApiResponse());
const result = await getStudioHomeCourses('');