From 38de6a33c18fc9190e4e5ad9e13b560fc2cf88d9 Mon Sep 17 00:00:00 2001 From: Alison Langston <46360176+alangsto@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:12:20 -0500 Subject: [PATCH] feat: add upgrade screen at end of trial (#76) --- .../__snapshots__/index.test.jsx.snap | 2 +- src/components/Disclosure/index.jsx | 20 +- src/components/Disclosure/index.test.jsx | 17 +- src/components/Sidebar/index.jsx | 16 +- src/components/Sidebar/index.test.jsx | 17 + .../UpgradeButton/UpgradeButton.scss | 3 + src/components/UpgradeButton/index.jsx | 40 + src/components/UpgradeButton/index.test.jsx | 42 + src/components/UpgradePanel/UpgradePanel.scss | 21 + .../UpsellBullets/UpsellBullets.jsx | 109 +++ .../UpgradePanel/UpsellBullets/index.jsx | 30 + .../__snapshots__/index.test.jsx.snap | 717 ++++++++++++++++++ src/components/UpgradePanel/index.jsx | 23 + src/components/UpgradePanel/index.test.jsx | 35 + src/hooks/use-course-upgrade.js | 10 +- src/hooks/use-course-upgrade.test.jsx | 87 ++- src/widgets/Xpert.jsx | 11 - 17 files changed, 1141 insertions(+), 59 deletions(-) create mode 100644 src/components/UpgradeButton/UpgradeButton.scss create mode 100644 src/components/UpgradeButton/index.jsx create mode 100644 src/components/UpgradeButton/index.test.jsx create mode 100644 src/components/UpgradePanel/UpgradePanel.scss create mode 100644 src/components/UpgradePanel/UpsellBullets/UpsellBullets.jsx create mode 100644 src/components/UpgradePanel/UpsellBullets/index.jsx create mode 100644 src/components/UpgradePanel/__snapshots__/index.test.jsx.snap create mode 100644 src/components/UpgradePanel/index.jsx create mode 100644 src/components/UpgradePanel/index.test.jsx diff --git a/src/components/Disclosure/__snapshots__/index.test.jsx.snap b/src/components/Disclosure/__snapshots__/index.test.jsx.snap index ee89f66e..3a33458c 100644 --- a/src/components/Disclosure/__snapshots__/index.test.jsx.snap +++ b/src/components/Disclosure/__snapshots__/index.test.jsx.snap @@ -119,7 +119,7 @@ exports[` When trial upgrade being shown should match snapsho diff --git a/src/components/Disclosure/index.jsx b/src/components/Disclosure/index.jsx index c617627a..99f3d48b 100644 --- a/src/components/Disclosure/index.jsx +++ b/src/components/Disclosure/index.jsx @@ -1,20 +1,20 @@ import PropTypes from 'prop-types'; import React from 'react'; -import { Hyperlink, Icon, Button } from '@openedx/paragon'; +import { Hyperlink, Icon } from '@openedx/paragon'; import { QuestionAnswerOutline, LightbulbCircle, AutoAwesome } from '@openedx/paragon/icons'; import { ensureConfig, getConfig } from '@edx/frontend-platform/config'; -import { useCourseUpgrade, useTrackEvent } from '../../hooks'; + +import UpgradeButton from '../UpgradeButton'; +import { useCourseUpgrade } from '../../hooks'; import './Disclosure.scss'; ensureConfig(['PRIVACY_POLICY_URL']); const Disclosure = ({ children }) => { - const { upgradeable, upgradeUrl, auditTrialLengthDays } = useCourseUpgrade(); - const { track } = useTrackEvent(); + const { upgradeable, auditTrialLengthDays } = useCourseUpgrade(); - const handleClick = () => track('edx.ui.lms.learning_assistant.disclosure_upgrade_click'); const freeDays = auditTrialLengthDays === 1 ? '1 day' : `${auditTrialLengthDays} days`; return ( @@ -49,15 +49,7 @@ const Disclosure = ({ children }) => { Free for {freeDays}, then upgrade course for full access to Xpert features. - + ) : null} diff --git a/src/components/Disclosure/index.test.jsx b/src/components/Disclosure/index.test.jsx index f40b4380..eda9a5a7 100644 --- a/src/components/Disclosure/index.test.jsx +++ b/src/components/Disclosure/index.test.jsx @@ -1,7 +1,7 @@ import React from 'react'; -import { fireEvent, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import { render } from '../../utils/utils.test'; -import { useCourseUpgrade, useTrackEvent } from '../../hooks'; +import { useCourseUpgrade } from '../../hooks'; import TrialDisclosure from '.'; @@ -53,15 +53,12 @@ describe('', () => { }); describe('When trial upgrade being shown', () => { - const mockedTrackEvent = jest.fn(); - beforeEach(() => { useCourseUpgrade.mockReturnValue({ upgradeable: true, upgradeUrl: mockedUpgradeUrl, auditTrialLengthDays: mockedAuditTrialDays, }); - useTrackEvent.mockReturnValue({ track: mockedTrackEvent }); ({ container } = render(Children)); }); @@ -78,16 +75,6 @@ describe('', () => { expect(upgradeCta).toHaveAttribute('href', mockedUpgradeUrl); }); - it('should call the track event on click', () => { - const upgradeCta = screen.queryByTestId('upgrade-cta'); - - expect(mockedTrackEvent).not.toHaveBeenCalled(); - - fireEvent.click(upgradeCta); - - expect(mockedTrackEvent).toHaveBeenCalledWith('edx.ui.lms.learning_assistant.disclosure_upgrade_click'); - }); - it('should match snapshot', () => { expect(container).toMatchSnapshot(); }); diff --git a/src/components/Sidebar/index.jsx b/src/components/Sidebar/index.jsx index bb590182..d9bc6497 100644 --- a/src/components/Sidebar/index.jsx +++ b/src/components/Sidebar/index.jsx @@ -7,11 +7,13 @@ import { } from '@openedx/paragon'; import { Close } from '@openedx/paragon/icons'; +import { useCourseUpgrade } from '../../hooks'; import showSurvey from '../../utils/surveyMonkey'; import APIError from '../APIError'; import ChatBox from '../ChatBox'; import Disclosure from '../Disclosure'; +import UpgradePanel from '../UpgradePanel'; import MessageForm from '../MessageForm'; import { ReactComponent as XpertLogo } from '../../assets/xpert-logo.svg'; import './Sidebar.scss'; @@ -27,6 +29,9 @@ const Sidebar = ({ disclosureAcknowledged, messageList, } = useSelector(state => state.learningAssistant); + + const { upgradeable, auditTrialExpired } = useCourseUpgrade(); + const chatboxContainerRef = useRef(null); // this use effect is intended to scroll to the bottom of the chat window, in the case @@ -97,6 +102,15 @@ const Sidebar = ({ ); + const getPanel = () => { + const showUpgrade = upgradeable && auditTrialExpired; + + if (showUpgrade) { + return ; + } + return (disclosureAcknowledged ? (getSidebar()) : ({getMessageForm()})); + }; + return ( isOpen && (
- {disclosureAcknowledged ? (getSidebar()) : ({getMessageForm()})} + {getPanel()}
) ); diff --git a/src/components/Sidebar/index.test.jsx b/src/components/Sidebar/index.test.jsx index 210daf40..5f40a144 100644 --- a/src/components/Sidebar/index.test.jsx +++ b/src/components/Sidebar/index.test.jsx @@ -5,6 +5,7 @@ import { usePromptExperimentDecision } from '../../experiments'; import { render as renderComponent } from '../../utils/utils.test'; import { initialState } from '../../data/slice'; import showSurvey from '../../utils/surveyMonkey'; +import { useCourseUpgrade, useTrackEvent } from '../../hooks'; import Sidebar from '.'; @@ -33,6 +34,11 @@ jest.mock('../../experiments', () => ({ usePromptExperimentDecision: jest.fn(), })); +jest.mock('../../hooks', () => ({ + useCourseUpgrade: jest.fn(), + useTrackEvent: jest.fn(), +})); + const defaultProps = { courseId: 'some-course-id', isOpen: true, @@ -63,6 +69,8 @@ const render = async (props = {}, sliceState = {}) => { describe('', () => { beforeEach(() => { jest.resetAllMocks(); + useCourseUpgrade.mockReturnValue({ upgradeable: false }); + useTrackEvent.mockReturnValue({ track: jest.fn() }); usePromptExperimentDecision.mockReturnValue([]); }); @@ -81,6 +89,15 @@ describe('', () => { render(undefined, { disclosureAcknowledged: true }); expect(screen.queryByTestId('sidebar-xpert')).toBeInTheDocument(); }); + + it('should not render xpert if audit trial is expired', () => { + useCourseUpgrade.mockReturnValue({ + upgradeable: true, + auditTrialExpired: true, + }); + render(); + expect(screen.queryByTestId('sidebar-xpert')).not.toBeInTheDocument(); + }); }); describe('when it\'s not open', () => { diff --git a/src/components/UpgradeButton/UpgradeButton.scss b/src/components/UpgradeButton/UpgradeButton.scss new file mode 100644 index 00000000..8d39f4cb --- /dev/null +++ b/src/components/UpgradeButton/UpgradeButton.scss @@ -0,0 +1,3 @@ +.trial-upgrade { + border-radius: 99rem; +} diff --git a/src/components/UpgradeButton/index.jsx b/src/components/UpgradeButton/index.jsx new file mode 100644 index 00000000..e80a81aa --- /dev/null +++ b/src/components/UpgradeButton/index.jsx @@ -0,0 +1,40 @@ +import PropTypes from 'prop-types'; +import React from 'react'; + +import { Button, Icon } from '@openedx/paragon'; +import { LockOpen } from '@openedx/paragon/icons'; +import { useCourseUpgrade, useTrackEvent } from '../../hooks'; + +import './UpgradeButton.scss'; + +const UpgradeButton = ({ includeLockIcon, trackingEventName }) => { + const { upgradeUrl } = useCourseUpgrade(); + const { track } = useTrackEvent(); + + const handleClick = () => track(trackingEventName); + + return ( + + ); +}; + +UpgradeButton.propTypes = { + includeLockIcon: PropTypes.bool, + trackingEventName: PropTypes.string.isRequired, +}; + +UpgradeButton.defaultProps = { + includeLockIcon: false, +}; + +export default UpgradeButton; diff --git a/src/components/UpgradeButton/index.test.jsx b/src/components/UpgradeButton/index.test.jsx new file mode 100644 index 00000000..951e6e75 --- /dev/null +++ b/src/components/UpgradeButton/index.test.jsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { fireEvent, screen } from '@testing-library/react'; +import { render } from '../../utils/utils.test'; +import { useCourseUpgrade, useTrackEvent } from '../../hooks'; + +import UpgradeButton from '.'; + +jest.mock('../../hooks', () => ({ + useCourseUpgrade: jest.fn(), + useTrackEvent: jest.fn(), +})); + +describe('UpgradeButton', () => { + beforeEach(() => { + useCourseUpgrade.mockReturnValue({ upgradeUrl: 'www.test.com' }); + useTrackEvent.mockReturnValue({ track: jest.fn() }); + }); + + it('should render UpgradeButton', () => { + render(); + expect(screen.queryByText('Upgrade now')).toBeInTheDocument(); + expect(screen.queryByTestId('upgrade-cta')).toHaveAttribute('href', 'www.test.com'); + expect(screen.queryByTestId('lock-icon')).not.toBeInTheDocument(); + }); + + it('should call track event on click', () => { + const mockedTrackEvent = jest.fn(); + useTrackEvent.mockReturnValue({ track: mockedTrackEvent }); + + render(); + + const upgradeCta = screen.queryByTestId('upgrade-cta'); + expect(mockedTrackEvent).not.toHaveBeenCalled(); + fireEvent.click(upgradeCta); + expect(mockedTrackEvent).toHaveBeenCalledWith('test.tracking'); + }); + + it('should render lock icon', () => { + render(); + expect(screen.queryByTestId('lock-icon')).toBeInTheDocument(); + }); +}); diff --git a/src/components/UpgradePanel/UpgradePanel.scss b/src/components/UpgradePanel/UpgradePanel.scss new file mode 100644 index 00000000..1d576223 --- /dev/null +++ b/src/components/UpgradePanel/UpgradePanel.scss @@ -0,0 +1,21 @@ +@use '../../utils/variables'; + +.upgrade-panel { + height: 100%; + + overflow-y: auto; + background-color: variables.$dark-green; + + h2 { + font-size: 1.375rem; + } + + .xpert-value-prop-check { + color: variables.$accent-yellow; + } + + .xpert-value-prop { + margin-bottom: 1rem; + font-size: 0.875rem; + } +} diff --git a/src/components/UpgradePanel/UpsellBullets/UpsellBullets.jsx b/src/components/UpgradePanel/UpsellBullets/UpsellBullets.jsx new file mode 100644 index 00000000..00b3e3f6 --- /dev/null +++ b/src/components/UpgradePanel/UpsellBullets/UpsellBullets.jsx @@ -0,0 +1,109 @@ +import React from 'react'; +import { Check } from '@openedx/paragon/icons'; +import { Icon, Stack } from '@openedx/paragon'; +import { FormattedMessage } from '@edx/frontend-platform/i18n'; +import { getConfig } from '@edx/frontend-platform'; + +const CheckmarkBullet = () => ( + +); + +export const VerifiedCertBullet = () => { + const verifiedCertLink = ( +
+ + + ); + return ( + + + + + + + ); +}; + +export const UnlockGradedBullet = () => { + const gradedAssignmentsInBoldText = ( + + + + ); + return ( + + + + + + + ); +}; + +export const FullAccessBullet = () => { + const fullAccessInBoldText = ( + + + + ); + return ( + + + + + + + ); +}; + +export const XpertAccessBullet = () => { + const xpertLearningAssistantInBoldText = ( + + + + ); + return ( + + + + + + + ); +}; diff --git a/src/components/UpgradePanel/UpsellBullets/index.jsx b/src/components/UpgradePanel/UpsellBullets/index.jsx new file mode 100644 index 00000000..08b8fcf6 --- /dev/null +++ b/src/components/UpgradePanel/UpsellBullets/index.jsx @@ -0,0 +1,30 @@ +import PropTypes from 'prop-types'; +import { + VerifiedCertBullet, + UnlockGradedBullet, + FullAccessBullet, + XpertAccessBullet, +} from './UpsellBullets'; + +const UpsellContent = ({ isFBE }) => ( +
+ + + {isFBE && ( + <> + + + + )} +
+); + +UpsellContent.defaultProps = { + isFBE: false, +}; + +UpsellContent.propTypes = { + isFBE: PropTypes.bool, +}; + +export default UpsellContent; diff --git a/src/components/UpgradePanel/__snapshots__/index.test.jsx.snap b/src/components/UpgradePanel/__snapshots__/index.test.jsx.snap new file mode 100644 index 00000000..94026f48 --- /dev/null +++ b/src/components/UpgradePanel/__snapshots__/index.test.jsx.snap @@ -0,0 +1,717 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UpgradePanel displays correct bullet points if FBE 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+
+

+ Upgrade this course +

+
+
+ + + + + Gain full access to + + Xpert Learning Assistant + + +
+
+ + + + + Earn a + + verified certificate + + of completion to showcase on your resumé + +
+
+ + + + + Unlock your access to all course activities, including + + graded assignments + + +
+
+ + + + + + Full access + + to course content and materials, even after the course ends + +
+
+ + + + + Upgrade now + +
+
+ , + "container":
+
+

+ Upgrade this course +

+
+
+ + + + + Gain full access to + + Xpert Learning Assistant + + +
+
+ + + + + Earn a + + verified certificate + + of completion to showcase on your resumé + +
+
+ + + + + Unlock your access to all course activities, including + + graded assignments + + +
+
+ + + + + + Full access + + to course content and materials, even after the course ends + +
+
+ + + + + Upgrade now + +
+
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "store": { + "dispatch": [Function], + "getState": [Function], + "replaceReducer": [Function], + "subscribe": [Function], + Symbol(Symbol.observable): [Function], + }, + "unmount": [Function], +} +`; + +exports[`UpgradePanel displays correct bullet points if not FBE 1`] = ` +{ + "asFragment": [Function], + "baseElement": +
+
+

+ Upgrade this course +

+
+
+ + + + + Gain full access to + + Xpert Learning Assistant + + +
+
+ + + + + Earn a + + verified certificate + + of completion to showcase on your resumé + +
+
+ + + + + Upgrade now + +
+
+ , + "container":
+
+

+ Upgrade this course +

+
+
+ + + + + Gain full access to + + Xpert Learning Assistant + + +
+
+ + + + + Earn a + + verified certificate + + of completion to showcase on your resumé + +
+
+ + + + + Upgrade now + +
+
, + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "store": { + "dispatch": [Function], + "getState": [Function], + "replaceReducer": [Function], + "subscribe": [Function], + Symbol(Symbol.observable): [Function], + }, + "unmount": [Function], +} +`; diff --git a/src/components/UpgradePanel/index.jsx b/src/components/UpgradePanel/index.jsx new file mode 100644 index 00000000..3280f2db --- /dev/null +++ b/src/components/UpgradePanel/index.jsx @@ -0,0 +1,23 @@ +import React from 'react'; + +import { useCourseUpgrade } from '../../hooks'; +import UpsellContent from './UpsellBullets'; +import UpgradeButton from '../UpgradeButton'; + +import './UpgradePanel.scss'; + +const UpgradePanel = () => { + const { isFBE } = useCourseUpgrade(); + + return ( +
+

+ Upgrade this course +

+ + +
+ ); +}; + +export default UpgradePanel; diff --git a/src/components/UpgradePanel/index.test.jsx b/src/components/UpgradePanel/index.test.jsx new file mode 100644 index 00000000..86d211f2 --- /dev/null +++ b/src/components/UpgradePanel/index.test.jsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { render } from '../../utils/utils.test'; +import { useCourseUpgrade, useTrackEvent } from '../../hooks'; + +import UpgradePanel from '.'; + +jest.mock('../../hooks', () => ({ + useCourseUpgrade: jest.fn(), + useTrackEvent: jest.fn(), +})); + +describe('UpgradePanel', () => { + beforeEach(() => { + useCourseUpgrade.mockReturnValue({ upgradeUrl: 'www.test.com' }); + useTrackEvent.mockReturnValue({ track: jest.fn() }); + }); + + it('displays correct bullet points if not FBE', () => { + useCourseUpgrade.mockReturnValue({ + upgradeUrl: 'www.test.com', + isFBE: false, + }); + + expect(render()).toMatchSnapshot(); + }); + + it('displays correct bullet points if FBE', () => { + useCourseUpgrade.mockReturnValue({ + upgradeUrl: 'www.test.com', + isFBE: true, + }); + + expect(render()).toMatchSnapshot(); + }); +}); diff --git a/src/hooks/use-course-upgrade.js b/src/hooks/use-course-upgrade.js index 41abfa99..9373d151 100644 --- a/src/hooks/use-course-upgrade.js +++ b/src/hooks/use-course-upgrade.js @@ -30,7 +30,11 @@ const millisecondsInOneDay = 24 * 60 * 60 * 1000; // hours*minutes*seconds*milli */ export default function useCourseUpgrade() { const { courseId, isUpgradeEligible } = useContext(CourseInfoContext); - const { offer } = useModel('coursewareMeta', courseId); + const { + offer, + accessExpiration, + datesBannerInfo, + } = useModel('coursewareMeta', courseId); const { verifiedMode } = useModel('courseHomeMeta', courseId); const { auditTrialLengthDays, @@ -46,12 +50,13 @@ export default function useCourseUpgrade() { if (auditTrial?.expirationDate) { const auditTrialExpirationDate = new Date(auditTrial.expirationDate); - auditTrialDaysRemaining = Math.ceil((auditTrialExpirationDate - Date.now()) / millisecondsInOneDay); auditTrialExpired = auditTrialDaysRemaining < 0; } + const isFBE = !!accessExpiration && !!datesBannerInfo?.contentTypeGatingEnabled; + return { upgradeable: true, auditTrialLengthDays, @@ -59,5 +64,6 @@ export default function useCourseUpgrade() { auditTrialExpired, auditTrial, upgradeUrl, + isFBE, }; } diff --git a/src/hooks/use-course-upgrade.test.jsx b/src/hooks/use-course-upgrade.test.jsx index 92895fda..c68cad3a 100644 --- a/src/hooks/use-course-upgrade.test.jsx +++ b/src/hooks/use-course-upgrade.test.jsx @@ -18,12 +18,12 @@ const contextWrapper = ({ courseInfo }) => function Wrapper({ children }) { // e }; const renderHook = ({ - courseInfo, offer = {}, verifiedMode = {}, state = { learningAssistant: {} }, + courseInfo, coursewareMeta = { offer: {} }, courseHomeMeta = { verifiedMode: {} }, state = { learningAssistant: {} }, }) => { useModel.mockImplementation((model) => { switch (model) { - case 'coursewareMeta': return { offer }; - case 'courseHomeMeta': return { verifiedMode }; + case 'coursewareMeta': return coursewareMeta; + case 'courseHomeMeta': return courseHomeMeta; default: { throw new Error('Model not mocked'); } @@ -63,12 +63,15 @@ describe('useCourseUpgrade()', () => { auditTrialExpired: false, auditTrialLengthDays: mockedAuditTrialLengthDays, upgradeUrl: mockedUpgradeUrl, + isFBE: false, }; const { result: resultOffer } = renderHook({ courseInfo: { isUpgradeEligible: true }, - offer: { - upgradeUrl: mockedUpgradeUrl, + coursewareMeta: { + offer: { + upgradeUrl: mockedUpgradeUrl, + }, }, state: { learningAssistant: { @@ -81,8 +84,10 @@ describe('useCourseUpgrade()', () => { const { result: resultVerified } = renderHook({ courseInfo: { isUpgradeEligible: true }, - verifiedMode: { - upgradeUrl: mockedUpgradeUrl, + courseHomeMeta: { + verifiedMode: { + upgradeUrl: mockedUpgradeUrl, + }, }, state: { learningAssistant: { @@ -97,11 +102,15 @@ describe('useCourseUpgrade()', () => { it('should return trial info if enabled and not expired', () => { const { result } = renderHook({ courseInfo: { isUpgradeEligible: true }, - offer: { - upgradeUrl: mockedUpgradeUrl, + coursewareMeta: { + offer: { + upgradeUrl: mockedUpgradeUrl, + }, }, - verifiedMode: { - upgradeUrl: mockedUpgradeUrl, + courseHomeMeta: { + verifiedMode: { + upgradeUrl: mockedUpgradeUrl, + }, }, state: { learningAssistant: { @@ -122,17 +131,64 @@ describe('useCourseUpgrade()', () => { auditTrialLengthDays: mockedAuditTrialLengthDays, upgradeUrl: 'https://upgrade.edx/course/test', upgradeable: true, + isFBE: false, }); }); it('should return trial info if expired', () => { const { result } = renderHook({ courseInfo: { isUpgradeEligible: true }, - offer: { - upgradeUrl: mockedUpgradeUrl, + coursewareMeta: { + offer: { + upgradeUrl: mockedUpgradeUrl, + }, + }, + courseHomeMeta: { + verifiedMode: { + upgradeUrl: mockedUpgradeUrl, + }, + }, + state: { + learningAssistant: { + auditTrialLengthDays: mockedAuditTrialLengthDays, + auditTrial: { + expirationDate: '2024-01-05 09:00:00', + }, + }, + }, + }); + + expect(result.current).toEqual({ + auditTrial: { + expirationDate: '2024-01-05 09:00:00', + }, + auditTrialDaysRemaining: -5, + auditTrialExpired: true, + auditTrialLengthDays: mockedAuditTrialLengthDays, + upgradeUrl: 'https://upgrade.edx/course/test', + upgradeable: true, + isFBE: false, + }); + }); + + it('should return isFBE true if feature based enrollment', () => { + const { result } = renderHook({ + courseInfo: { isUpgradeEligible: true }, + coursewareMeta: { + offer: { + upgradeUrl: mockedUpgradeUrl, + }, + accessExpiration: { + upgradeDeadline: '2024-01-07 09:00:00', + }, + datesBannerInfo: { + contentTypeGatingEnabled: true, + }, }, - verifiedMode: { - upgradeUrl: mockedUpgradeUrl, + courseHomeMeta: { + verifiedMode: { + upgradeUrl: mockedUpgradeUrl, + }, }, state: { learningAssistant: { @@ -153,6 +209,7 @@ describe('useCourseUpgrade()', () => { auditTrialLengthDays: mockedAuditTrialLengthDays, upgradeUrl: 'https://upgrade.edx/course/test', upgradeable: true, + isFBE: true, }); }); }); diff --git a/src/widgets/Xpert.jsx b/src/widgets/Xpert.jsx index 48cc8283..2a5b32d3 100644 --- a/src/widgets/Xpert.jsx +++ b/src/widgets/Xpert.jsx @@ -23,7 +23,6 @@ const Xpert = ({ const { isEnabled, sidebarIsOpen, - auditTrial, } = useSelector(state => state.learningAssistant); const setSidebarIsOpen = (isOpen) => { @@ -34,16 +33,6 @@ const Xpert = ({ dispatch(getLearningAssistantChatSummary(courseId)); }, [dispatch, courseId]); - // NOTE: This value can be used later on if/when we pass the enrollment mode to this component - const isAuditTrialNotExpired = () => { // eslint-disable-line no-unused-vars - const auditTrialExpirationDate = new Date(auditTrial.expirationDate); - - if ((Date.now() - auditTrialExpirationDate) > 0) { - return true; - } - return false; - }; - return isEnabled ? (