Skip to content

Commit

Permalink
Merge branch 'master' into dynamic-config-tutor-mfe
Browse files Browse the repository at this point in the history
  • Loading branch information
ghassanmas authored May 15, 2023
2 parents 75c6d58 + 5ca3036 commit 8d91a12
Show file tree
Hide file tree
Showing 23 changed files with 467 additions and 259 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ import { useIntl } from '@edx/frontend-platform/i18n';

import track from 'tracking';
import { reduxHooks } from 'hooks';
import useActionDisabledState from '../hooks';
import ActionButton from './ActionButton';
import messages from './messages';

export const BeginCourseButton = ({ cardId }) => {
const { formatMessage } = useIntl();
const { homeUrl } = reduxHooks.useCardCourseRunData(cardId);
const { hasAccess, isAudit, isAuditAccessExpired } = reduxHooks.useCardEnrollmentData(cardId);
const { isMasquerading } = reduxHooks.useMasqueradeData();
const { disableBeginCourse } = useActionDisabledState(cardId);
const handleClick = reduxHooks.useTrackCourseEvent(
track.course.enterCourseClicked,
cardId,
homeUrl,
);
return (
<ActionButton
disabled={isMasquerading || !hasAccess || (isAudit && isAuditAccessExpired)}
disabled={disableBeginCourse}
as="a"
href="#"
onClick={handleClick}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { shallow } from 'enzyme';
import { htmlProps } from 'data/constants/htmlKeys';
import { reduxHooks } from 'hooks';
import track from 'tracking';
import useActionDisabledState from '../hooks';
import BeginCourseButton from './BeginCourseButton';

jest.mock('tracking', () => ({
Expand All @@ -14,14 +15,12 @@ jest.mock('tracking', () => ({
jest.mock('hooks', () => ({
reduxHooks: {
useCardCourseRunData: jest.fn(() => ({ homeUrl: 'home-url' })),
useCardEnrollmentData: jest.fn(() => ({ hasAccess: true, isAudit: false, isAuditAccessExpired: false })),
useMasqueradeData: jest.fn(() => ({ isMasquerading: false })),
useTrackCourseEvent: jest.fn(
(eventName, cardId, upgradeUrl) => ({ trackCourseEvent: { eventName, cardId, upgradeUrl } }),
),
},
}));

jest.mock('../hooks', () => jest.fn(() => ({ disableBeginCourse: false })));
jest.mock('./ActionButton', () => 'ActionButton');

let wrapper;
Expand Down Expand Up @@ -51,26 +50,10 @@ describe('BeginCourseButton', () => {
wrapper = shallow(<BeginCourseButton {...props} />);
expect(reduxHooks.useCardCourseRunData).toHaveBeenCalledWith(props.cardId);
});
it('initializes enrollment data with cardId', () => {
test('disabled states', () => {
useActionDisabledState.mockReturnValueOnce({ disableBeginCourse: true });
wrapper = shallow(<BeginCourseButton {...props} />);
expect(reduxHooks.useCardEnrollmentData).toHaveBeenCalledWith(props.cardId);
});
describe('disabled states', () => {
test('learner does not have access', () => {
reduxHooks.useCardEnrollmentData.mockReturnValueOnce({ hasAccess: false });
wrapper = shallow(<BeginCourseButton {...props} />);
expect(wrapper.prop(htmlProps.disabled)).toEqual(true);
});
test('masquerading', () => {
reduxHooks.useMasqueradeData.mockReturnValueOnce({ isMasquerading: true });
wrapper = shallow(<BeginCourseButton {...props} />);
expect(wrapper.prop(htmlProps.disabled)).toEqual(true);
});
test('audit access expired', () => {
reduxHooks.useCardEnrollmentData.mockReturnValueOnce({ isAudit: true, isAuditAccessExpired: true });
wrapper = shallow(<BeginCourseButton {...props} />);
expect(wrapper.prop(htmlProps.disabled)).toEqual(true);
});
expect(wrapper.prop(htmlProps.disabled)).toEqual(true);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ import { useIntl } from '@edx/frontend-platform/i18n';

import track from 'tracking';
import { reduxHooks } from 'hooks';
import useActionDisabledState from '../hooks';
import ActionButton from './ActionButton';
import messages from './messages';

export const ResumeButton = ({ cardId }) => {
const { resumeUrl } = reduxHooks.useCardCourseRunData(cardId);
const { hasAccess, isAudit, isAuditAccessExpired } = reduxHooks.useCardEnrollmentData(cardId);
const { isMasquerading } = reduxHooks.useMasqueradeData();
const { formatMessage } = useIntl();
const { resumeUrl } = reduxHooks.useCardCourseRunData(cardId);
const { disableResumeCourse } = useActionDisabledState(cardId);
const handleClick = reduxHooks.useTrackCourseEvent(
track.course.enterCourseClicked,
cardId,
resumeUrl,
);
return (
<ActionButton
disabled={isMasquerading || !hasAccess || (isAudit && isAuditAccessExpired)}
disabled={disableResumeCourse}
as="a"
href="#"
onClick={handleClick}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,18 @@ import { shallow } from 'enzyme';
import { htmlProps } from 'data/constants/htmlKeys';
import { reduxHooks } from 'hooks';
import track from 'tracking';
import useActionDisabledState from '../hooks';
import ResumeButton from './ResumeButton';

jest.mock('hooks', () => ({
reduxHooks: {
useCardCourseRunData: jest.fn(() => ({ resumeUrl: 'resumeUrl' })),
useCardEnrollmentData: jest.fn(() => ({
hasAccess: true,
isAudit: true,
isAuditAccessExpired: false,
})),
useMasqueradeData: jest.fn(() => ({ isMasquerading: false })),
useTrackCourseEvent: (eventName, cardId, url) => jest
.fn()
.mockName(`useTrackCourseEvent('${eventName}', '${cardId}', '${url}')`),
},
}));
jest.mock('../hooks', () => jest.fn(() => ({ disableResumeCourse: false })));
jest.mock('tracking', () => ({
course: {
enterCourseClicked: 'enterCourseClicked',
Expand Down Expand Up @@ -48,36 +44,14 @@ describe('ResumeButton', () => {
describe('behavior', () => {
it('initializes course run data based on cardId', () => {
shallow(<ResumeButton {...props} />);
expect(reduxHooks.useCardCourseRunData).toHaveBeenCalledWith(props.cardId);
});
it('initializes course enrollment data based on cardId', () => {
shallow(<ResumeButton {...props} />);
expect(reduxHooks.useCardEnrollmentData).toHaveBeenCalledWith(props.cardId);
expect(reduxHooks.useCardCourseRunData).toHaveBeenCalledWith(
props.cardId,
);
});
describe('disabled states', () => {
test('masquerading', () => {
reduxHooks.useMasqueradeData.mockReturnValueOnce({ isMasquerading: true });
const wrapper = shallow(<ResumeButton {...props} />);
expect(wrapper.prop(htmlProps.disabled)).toEqual(true);
});
test('learner does not have access', () => {
reduxHooks.useCardEnrollmentData.mockReturnValueOnce({
hasAccess: false,
isAudit: true,
isAuditAccessExpired: false,
});
const wrapper = shallow(<ResumeButton {...props} />);
expect(wrapper.prop(htmlProps.disabled)).toEqual(true);
});
test('audit access expired', () => {
reduxHooks.useCardEnrollmentData.mockReturnValueOnce({
hasAccess: true,
isAudit: true,
isAuditAccessExpired: true,
});
const wrapper = shallow(<ResumeButton {...props} />);
expect(wrapper.prop(htmlProps.disabled)).toEqual(true);
});
test('disabled states', () => {
useActionDisabledState.mockReturnValueOnce({ disableResumeCourse: true });
const wrapper = shallow(<ResumeButton {...props} />);
expect(wrapper.prop(htmlProps.disabled)).toEqual(true);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@ import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';

import { reduxHooks } from 'hooks';
import useActionDisabledState from '../hooks';
import ActionButton from './ActionButton';
import messages from './messages';

export const SelectSessionButton = ({ cardId }) => {
const { formatMessage } = useIntl();
const { isMasquerading } = reduxHooks.useMasqueradeData();
const { hasAccess } = reduxHooks.useCardEnrollmentData(cardId);
const { canChange, hasSessions } = reduxHooks.useCardEntitlementData(cardId);
const { disableSelectSession } = useActionDisabledState(cardId);
const openSessionModal = reduxHooks.useUpdateSelectSessionModalCallback(cardId);
return (
<ActionButton
disabled={isMasquerading || !hasAccess || (!canChange || !hasSessions)}
disabled={disableSelectSession}
onClick={openSessionModal}
>
{formatMessage(messages.selectSession)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,34 @@ import { shallow } from 'enzyme';

import { reduxHooks } from 'hooks';
import { htmlProps } from 'data/constants/htmlKeys';
import useActionDisabledState from '../hooks';

import SelectSessionButton from './SelectSessionButton';

jest.mock('hooks', () => ({
reduxHooks: {
useCardEnrollmentData: jest.fn(() => ({ hasAccess: true })),
useCardEntitlementData: jest.fn(() => ({ canChange: true, hasSessions: true })),
useMasqueradeData: jest.fn(() => ({ isMasquerading: false })),
useUpdateSelectSessionModalCallback: () => jest.fn().mockName('mockOpenSessionModal'),
},
}));
jest.mock('../hooks', () => jest.fn(() => ({ disableSelectSession: false })));
jest.mock('./ActionButton', () => 'ActionButton');

let wrapper;

describe('SelectSessionButton', () => {
const props = { cardId: 'cardId' };
describe('snapshot', () => {
test('renders default button', () => {
wrapper = shallow(<SelectSessionButton {...props} />);
expect(wrapper).toMatchSnapshot();
});
it('renders disabled button when user does not have access to the course', () => {
reduxHooks.useCardEnrollmentData.mockReturnValueOnce({ hasAccess: false });
wrapper = shallow(<SelectSessionButton {...props} />);
expect(wrapper).toMatchSnapshot();
});
it('renders disabled button if masquerading', () => {
reduxHooks.useMasqueradeData.mockReturnValueOnce({ isMasquerading: true });
wrapper = shallow(<SelectSessionButton {...props} />);
expect(wrapper).toMatchSnapshot();
});
it('default render', () => {
expect(wrapper).toMatchSnapshot();
wrapper = shallow(<SelectSessionButton {...props} />);
expect(wrapper.prop(htmlProps.disabled)).toEqual(false);
expect(wrapper.prop(htmlProps.onClick).getMockName()).toEqual(
reduxHooks.useUpdateSelectSessionModalCallback().getMockName(),
);
});
describe('behavior', () => {
it('default render', () => {
wrapper = shallow(<SelectSessionButton {...props} />);
expect(wrapper.prop(htmlProps.disabled)).toEqual(false);
expect(wrapper.prop(htmlProps.onClick).getMockName())
.toEqual(reduxHooks.useUpdateSelectSessionModalCallback().getMockName());
});
describe('disabled states', () => {
test('learner does not have access', () => {
reduxHooks.useCardEnrollmentData.mockReturnValueOnce({ hasAccess: false });
wrapper = shallow(<SelectSessionButton {...props} />);
expect(wrapper.prop(htmlProps.disabled)).toEqual(true);
});
test('learner cannot change sessions', () => {
reduxHooks.useCardEntitlementData.mockReturnValueOnce({ canChange: false, hasSessions: true });
wrapper = shallow(<SelectSessionButton {...props} />);
expect(wrapper.prop(htmlProps.disabled)).toEqual(true);
});
test('entitlement does not have available sessions', () => {
reduxHooks.useCardEntitlementData.mockReturnValueOnce({ canChange: true, hasSessions: false });
wrapper = shallow(<SelectSessionButton {...props} />);
expect(wrapper.prop(htmlProps.disabled)).toEqual(true);
});
test('user is masquerading', () => {
reduxHooks.useMasqueradeData.mockReturnValueOnce({ isMasquerading: true });
wrapper = shallow(<SelectSessionButton {...props} />);
expect(wrapper.prop(htmlProps.disabled)).toEqual(true);
});
});
test('disabled states', () => {
useActionDisabledState.mockReturnValueOnce({ disableSelectSession: true });
expect(wrapper).toMatchSnapshot();
wrapper = shallow(<SelectSessionButton {...props} />);
expect(wrapper.prop(htmlProps.disabled)).toEqual(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useIntl } from '@edx/frontend-platform/i18n';

import track from 'tracking';
import { reduxHooks } from 'hooks';
import useActionDisabledState from '../hooks';

import ActionButton from './ActionButton';
import messages from './messages';
Expand All @@ -14,15 +15,14 @@ export const UpgradeButton = ({ cardId }) => {
const { formatMessage } = useIntl();

const { upgradeUrl } = reduxHooks.useCardCourseRunData(cardId);
const { canUpgrade } = reduxHooks.useCardEnrollmentData(cardId);
const { isMasquerading } = reduxHooks.useMasqueradeData();
const { disableUpgradeCourse } = useActionDisabledState(cardId);

const trackUpgradeClick = reduxHooks.useTrackCourseEvent(
track.course.upgradeClicked,
cardId,
upgradeUrl,
);

const isEnabled = (!isMasquerading && canUpgrade);
const enabledProps = {
as: 'a',
href: upgradeUrl,
Expand All @@ -32,8 +32,8 @@ export const UpgradeButton = ({ cardId }) => {
<ActionButton
iconBefore={Locked}
variant="outline-primary"
disabled={!isEnabled}
{...isEnabled && enabledProps}
disabled={disableUpgradeCourse}
{...!disableUpgradeCourse && enabledProps}
>
{formatMessage(messages.upgrade)}
</ActionButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { shallow } from 'enzyme';
import track from 'tracking';
import { reduxHooks } from 'hooks';
import { htmlProps } from 'data/constants/htmlKeys';
import useActionDisabledState from '../hooks';
import UpgradeButton from './UpgradeButton';

jest.mock('tracking', () => ({
Expand All @@ -13,15 +14,13 @@ jest.mock('tracking', () => ({

jest.mock('hooks', () => ({
reduxHooks: {
useMasqueradeData: jest.fn(() => ({ isMasquerading: false })),
useCardCourseRunData: jest.fn(),
useCardEnrollmentData: jest.fn(() => ({ canUpgrade: true })),
useTrackCourseEvent: jest.fn(
(eventName, cardId, upgradeUrl) => ({ trackCourseEvent: { eventName, cardId, upgradeUrl } }),
),
},
}));

jest.mock('../hooks', () => jest.fn(() => ({ disableUpgradeCourse: false })));
jest.mock('./ActionButton', () => 'ActionButton');

describe('UpgradeButton', () => {
Expand All @@ -42,13 +41,7 @@ describe('UpgradeButton', () => {
));
});
test('cannot upgrade', () => {
reduxHooks.useCardEnrollmentData.mockReturnValueOnce({ canUpgrade: false });
const wrapper = shallow(<UpgradeButton {...props} />);
expect(wrapper).toMatchSnapshot();
expect(wrapper.prop(htmlProps.disabled)).toEqual(true);
});
test('masquerading', () => {
reduxHooks.useMasqueradeData.mockReturnValueOnce({ isMasquerading: true });
useActionDisabledState.mockReturnValueOnce({ disableUpgradeCourse: true });
const wrapper = shallow(<UpgradeButton {...props} />);
expect(wrapper).toMatchSnapshot();
expect(wrapper.prop(htmlProps.disabled)).toEqual(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@ import { useIntl } from '@edx/frontend-platform/i18n';

import track from 'tracking';
import { reduxHooks } from 'hooks';
import useActionDisabledState from '../hooks';
import ActionButton from './ActionButton';
import messages from './messages';

export const ViewCourseButton = ({ cardId }) => {
const { formatMessage } = useIntl();
const { homeUrl } = reduxHooks.useCardCourseRunData(cardId);
const { hasAccess, isAudit, isAuditAccessExpired } = reduxHooks.useCardEnrollmentData(cardId);
const { disableViewCourse } = useActionDisabledState(cardId);

const handleClick = reduxHooks.useTrackCourseEvent(
track.course.enterCourseClicked,
cardId,
homeUrl,
);
// disabled on no access or (is audit track but audit access was expired)
const disabledViewCourseButton = !hasAccess || (isAudit && isAuditAccessExpired);
return (
<ActionButton
disabled={disabledViewCourseButton}
disabled={disableViewCourse}
as="a"
href="#"
onClick={handleClick}
Expand Down
Loading

0 comments on commit 8d91a12

Please sign in to comment.