feat!: remove UpgradeButton (#536)

This commit is contained in:
Maxwell Frank
2025-01-13 13:50:50 -05:00
committed by GitHub
parent ce595d0e62
commit 777d3aa45c
24 changed files with 26 additions and 244 deletions

View File

@@ -27,7 +27,7 @@ reduxHooks.useCardCourseRunData.mockReturnValue({ homeUrl });
const execEdPath = (cardId) => `exec-ed-tracking-path=${cardId}`;
reduxHooks.useCardExecEdTrackingParam.mockImplementation(execEdPath);
reduxHooks.useTrackCourseEvent.mockImplementation(
(eventName, cardId, upgradeUrl) => ({ trackCourseEvent: { eventName, cardId, upgradeUrl } }),
(eventName, cardId, url) => ({ trackCourseEvent: { eventName, cardId, url } }),
);
describe('BeginCourseButton', () => {

View File

@@ -26,7 +26,7 @@ reduxHooks.useCardCourseRunData.mockReturnValue({ resumeUrl });
const execEdPath = (cardId) => `exec-ed-tracking-path=${cardId}`;
reduxHooks.useCardExecEdTrackingParam.mockImplementation(execEdPath);
reduxHooks.useTrackCourseEvent.mockImplementation(
(eventName, cardId, upgradeUrl) => ({ trackCourseEvent: { eventName, cardId, upgradeUrl } }),
(eventName, cardId, url) => ({ trackCourseEvent: { eventName, cardId, url } }),
);
let wrapper;

View File

@@ -1,49 +0,0 @@
import { shallow } from '@edx/react-unit-test-utils';
import track from 'tracking';
import { reduxHooks } from 'hooks';
import useActionDisabledState from '../hooks';
import UpgradeButton from './UpgradeButton';
jest.mock('tracking', () => ({
course: {
upgradeClicked: jest.fn().mockName('segment.trackUpgradeClicked'),
},
}));
jest.mock('hooks', () => ({
reduxHooks: {
useCardCourseRunData: jest.fn(),
useTrackCourseEvent: jest.fn(
(eventName, cardId, upgradeUrl) => ({ trackCourseEvent: { eventName, cardId, upgradeUrl } }),
),
},
}));
jest.mock('../hooks', () => jest.fn(() => ({ disableUpgradeCourse: false })));
jest.mock('./ActionButton', () => 'ActionButton');
describe('UpgradeButton', () => {
const props = {
cardId: 'cardId',
};
const upgradeUrl = 'upgradeUrl';
reduxHooks.useCardCourseRunData.mockReturnValue({ upgradeUrl });
describe('snapshot', () => {
test('can upgrade', () => {
const wrapper = shallow(<UpgradeButton {...props} />);
expect(wrapper.snapshot).toMatchSnapshot();
expect(wrapper.instance.props.disabled).toEqual(false);
expect(wrapper.instance.props.onClick).toEqual(reduxHooks.useTrackCourseEvent(
track.course.upgradeClicked,
props.cardId,
upgradeUrl,
));
});
test('cannot upgrade', () => {
useActionDisabledState.mockReturnValueOnce({ disableUpgradeCourse: true });
const wrapper = shallow(<UpgradeButton {...props} />);
expect(wrapper.snapshot).toMatchSnapshot();
expect(wrapper.instance.props.disabled).toEqual(true);
});
});
});

View File

@@ -15,7 +15,7 @@ jest.mock('hooks', () => ({
reduxHooks: {
useCardCourseRunData: jest.fn(() => ({ homeUrl: 'homeUrl' })),
useTrackCourseEvent: jest.fn(
(eventName, cardId, upgradeUrl) => ({ trackCourseEvent: { eventName, cardId, upgradeUrl } }),
(eventName, cardId, url) => ({ trackCourseEvent: { eventName, cardId, url } }),
),
},
}));

View File

@@ -10,7 +10,7 @@ exports[`BeginCourseButton snapshot disabled snapshot 1`] = `
"trackCourseEvent": {
"cardId": "cardId",
"eventName": [MockFunction segment.enterCourseClicked],
"upgradeUrl": "home-urlexec-ed-tracking-path=cardId",
"url": "home-urlexec-ed-tracking-path=cardId",
},
}
}
@@ -29,7 +29,7 @@ exports[`BeginCourseButton snapshot enabled snapshot 1`] = `
"trackCourseEvent": {
"cardId": "cardId",
"eventName": [MockFunction segment.enterCourseClicked],
"upgradeUrl": "home-urlexec-ed-tracking-path=cardId",
"url": "home-urlexec-ed-tracking-path=cardId",
},
}
}

View File

@@ -10,7 +10,7 @@ exports[`ResumeButton snapshot disabled snapshot 1`] = `
"trackCourseEvent": {
"cardId": "cardId",
"eventName": [MockFunction segment.enterCourseClicked],
"upgradeUrl": "resume-urlexec-ed-tracking-path=cardId",
"url": "resume-urlexec-ed-tracking-path=cardId",
},
}
}
@@ -29,7 +29,7 @@ exports[`ResumeButton snapshot enabled snapshot 1`] = `
"trackCourseEvent": {
"cardId": "cardId",
"eventName": [MockFunction segment.enterCourseClicked],
"upgradeUrl": "resume-urlexec-ed-tracking-path=cardId",
"url": "resume-urlexec-ed-tracking-path=cardId",
},
}
}

View File

@@ -1,32 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`UpgradeButton snapshot can upgrade 1`] = `
<ActionButton
as="a"
disabled={false}
href="upgradeUrl"
iconBefore={[MockFunction icons.Locked]}
onClick={
{
"trackCourseEvent": {
"cardId": "cardId",
"eventName": [MockFunction segment.trackUpgradeClicked],
"upgradeUrl": "upgradeUrl",
},
}
}
variant="outline-primary"
>
Upgrade
</ActionButton>
`;
exports[`UpgradeButton snapshot cannot upgrade 1`] = `
<ActionButton
disabled={true}
iconBefore={[MockFunction icons.Locked]}
variant="outline-primary"
>
Upgrade
</ActionButton>
`;

View File

@@ -10,7 +10,7 @@ exports[`ViewCourseButton learner can view course 1`] = `
"trackCourseEvent": {
"cardId": "cardId",
"eventName": [MockFunction segment.enterCourseClicked],
"upgradeUrl": "homeUrl",
"url": "homeUrl",
},
}
}
@@ -29,7 +29,7 @@ exports[`ViewCourseButton learner cannot view course 1`] = `
"trackCourseEvent": {
"cardId": "cardId",
"eventName": [MockFunction segment.enterCourseClicked],
"upgradeUrl": "homeUrl",
"url": "homeUrl",
},
}
}

View File

@@ -1,11 +1,6 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
upgrade: {
id: 'learner-dash.courseCard.actions.upgrade',
description: 'Course card upgrade button text',
defaultMessage: 'Upgrade',
},
beginCourse: {
id: 'learner-dash.courseCard.actions.beginCourse',
description: 'Course card begin-course button text',

View File

@@ -18,8 +18,8 @@ jest.mock('hooks', () => ({
useCardCourseData: jest.fn(() => ({ bannerImgSrc: 'banner-img-src' })),
useCardCourseRunData: jest.fn(() => ({ homeUrl })),
useCardEnrollmentData: jest.fn(() => ({ isVerified: true })),
useTrackCourseEvent: jest.fn((eventName, cardId, upgradeUrl) => ({
trackCourseEvent: { eventName, cardId, upgradeUrl },
useTrackCourseEvent: jest.fn((eventName, cardId, url) => ({
trackCourseEvent: { eventName, cardId, url },
})),
},
}));

View File

@@ -17,8 +17,8 @@ jest.mock('hooks', () => ({
reduxHooks: {
useCardCourseData: jest.fn(() => ({ courseName: 'course-name' })),
useCardCourseRunData: jest.fn(() => ({ homeUrl })),
useTrackCourseEvent: jest.fn((eventName, cardId, upgradeUrl) => ({
trackCourseEvent: { eventName, cardId, upgradeUrl },
useTrackCourseEvent: jest.fn((eventName, cardId, url) => ({
trackCourseEvent: { eventName, cardId, url },
})),
},
}));

View File

@@ -9,7 +9,7 @@ exports[`CourseCardImage snapshot renders clickable link course Image 1`] = `
"trackCourseEvent": {
"cardId": "cardId",
"eventName": [MockFunction segment.courseImageClicked],
"upgradeUrl": "home-url",
"url": "home-url",
},
}
}

View File

@@ -11,7 +11,7 @@ exports[`CourseCardTitle snapshot renders clickable link course title 1`] = `
"trackCourseEvent": {
"cardId": "cardId",
"eventName": [MockFunction segment.courseTitleClicked],
"upgradeUrl": "home-url",
"url": "home-url",
},
}
}

View File

@@ -3,19 +3,18 @@ import { reduxHooks } from 'hooks';
export const useActionDisabledState = (cardId) => {
const { isMasquerading } = reduxHooks.useMasqueradeData();
const {
canUpgrade, hasAccess, isAudit, isAuditAccessExpired,
hasAccess, isAudit, isAuditAccessExpired,
} = reduxHooks.useCardEnrollmentData(cardId);
const {
isEntitlement, isFulfilled, canChange, hasSessions,
} = reduxHooks.useCardEntitlementData(cardId);
const { resumeUrl, homeUrl, upgradeUrl } = reduxHooks.useCardCourseRunData(cardId);
const { resumeUrl, homeUrl } = reduxHooks.useCardCourseRunData(cardId);
const disableBeginCourse = !homeUrl || (isMasquerading || !hasAccess || (isAudit && isAuditAccessExpired));
const disableResumeCourse = !resumeUrl || (isMasquerading || !hasAccess || (isAudit && isAuditAccessExpired));
const disableViewCourse = !hasAccess || (isAudit && isAuditAccessExpired);
const disableSelectSession = !isEntitlement || isMasquerading || !hasAccess || (!canChange || !hasSessions);
const disableUpgradeCourse = !upgradeUrl || (isMasquerading && !canUpgrade);
const disableCourseTitle = (isEntitlement && !isFulfilled) || disableViewCourse;
@@ -23,7 +22,6 @@ export const useActionDisabledState = (cardId) => {
disableBeginCourse,
disableResumeCourse,
disableViewCourse,
disableUpgradeCourse,
disableSelectSession,
disableCourseTitle,
};

View File

@@ -16,7 +16,6 @@ const cardId = 'my-test-course-number';
describe('useActionDisabledState', () => {
const defaultData = {
isMasquerading: false,
canUpgrade: false,
isEntitlement: false,
isFulfilled: false,
canChange: false,
@@ -26,12 +25,10 @@ describe('useActionDisabledState', () => {
isAuditAccessExpired: false,
resumeUrl: 'resume.url',
homeUrl: 'home.url',
upgradeUrl: 'upgrade.url',
};
const mockHooksData = (args) => {
const {
isMasquerading,
canUpgrade,
isEntitlement,
isFulfilled,
canChange,
@@ -41,11 +38,9 @@ describe('useActionDisabledState', () => {
isAuditAccessExpired,
resumeUrl,
homeUrl,
upgradeUrl,
} = { ...defaultData, ...args };
reduxHooks.useMasqueradeData.mockReturnValueOnce({ isMasquerading });
reduxHooks.useCardEnrollmentData.mockReturnValueOnce({
canUpgrade,
hasAccess,
isAudit,
isAuditAccessExpired,
@@ -59,7 +54,6 @@ describe('useActionDisabledState', () => {
reduxHooks.useCardCourseRunData.mockReturnValueOnce({
resumeUrl,
homeUrl,
upgradeUrl,
});
};
@@ -121,21 +115,6 @@ describe('useActionDisabledState', () => {
testDisabled({ hasAccess: true }, false);
});
});
describe('disableUpgradeCourse', () => {
const testDisabled = (data, expected) => {
mockHooksData(data);
expect(runHook().disableUpgradeCourse).toBe(expected);
};
it('disable when upgradeUrl is invalid', () => {
testDisabled({ upgradeUrl: null }, true);
});
it('disable when isMasquerading is true and canUpgrade is false', () => {
testDisabled({ isMasquerading: true, canUpgrade: false }, true);
});
it('enable when all conditions are met', () => {
testDisabled({ canUpgrade: true }, false);
});
});
describe('disableSelectSession', () => {
const testDisabled = (data, expected) => {
mockHooksData(data);

View File

@@ -52,7 +52,6 @@ export const courseCard = StrictDict({
homeUrl: courseRun.homeUrl,
marketingUrl: courseRun.marketingUrl,
upgradeUrl: courseRun.upgradeUrl,
progressUrl: baseAppUrl(courseRun.progressUrl),
resumeUrl: baseAppUrl(courseRun.resumeUrl), // resume will route this to learning mfe.

View File

@@ -156,7 +156,6 @@ describe('courseCard selectors module', () => {
homeUrl: 'test-home-url',
marketingUrl: 'test-marketing-url',
upgradeUrl: 'test-upgrade-url',
progressUrl: 'test-progress-url',
resumeUrl: 'test-resume-url',
@@ -181,10 +180,9 @@ describe('courseCard selectors module', () => {
it('passes minPassingGrade floored from float to a percentage value', () => {
expect(selected.minPassingGrade).toEqual(93);
});
it('passes [homeUrl, marketingUrl, upgradeUrl]', () => {
it('passes [homeUrl, marketingUrl]', () => {
expect(selected.homeUrl).toEqual(testData.homeUrl);
expect(selected.marketingUrl).toEqual(testData.marketingUrl);
expect(selected.upgradeUrl).toEqual(testData.upgradeUrl);
});
it('passes [progressUrl, unenrollUrl, resumeUrl], converted to baseAppUrl', () => {
expect(selected.progressUrl).toEqual(baseAppUrl(testData.progressUrl));

View File

@@ -50,12 +50,6 @@ export const logEvent = ({ eventName, data, courseId }) => post(urls.event(), {
event: JSON.stringify(data),
});
export const logUpgrade = ({ courseId }) => module.logEvent({
eventName: eventNames.upgradeButtonClickedEnrollment,
courseId,
data: { location: 'learner-dashboard' },
});
export const logShare = ({ courseId, site }) => module.logEvent({
eventName: eventNames.shareClicked,
courseId,
@@ -78,7 +72,6 @@ export default {
updateEntitlementEnrollment,
deleteEntitlementEnrollment,
logEvent,
logUpgrade,
logShare,
createCreditRequest,
};

View File

@@ -130,13 +130,6 @@ describe('lms api methods', () => {
beforeEach(() => {
jest.spyOn(api, moduleKeys.logEvent).mockImplementation(logEvent);
});
test('logUpgrade sends enrollment upgrade click event with learner dashboard location', () => {
expect(api.logUpgrade({ courseId })).toEqual(logEvent({
eventName: eventNames.upgradeButtonClickedEnrollment,
courseId,
data: { location: 'learner-dashboard' },
}));
});
test('logShare sends share clicke vent with course id, side and location', () => {
const site = 'test-site';
expect(api.logShare({ courseId, site })).toEqual(logEvent({

View File

@@ -779,9 +779,6 @@ export const compileCourseRunData = ({ courseName, ...data }, index) => {
courseProvider: getOption(providerOptions, index),
programs: getOption(programsOptions, index),
};
if (out.enrollment.canUpgrade) {
out.courseRun.upgradeUrl = 'test-upgrade-url';
}
return out;
};

View File

@@ -2,27 +2,14 @@ import React from 'react';
import PropTypes from 'prop-types';
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import { reduxHooks } from 'hooks';
import UpgradeButton from 'containers/CourseCard/components/CourseCardActions/UpgradeButton';
const CourseCardActionSlot = ({ cardId }) => {
const { isEntitlement } = reduxHooks.useCardEntitlementData(cardId);
const {
isVerified,
isExecEd2UCourse,
} = reduxHooks.useCardEnrollmentData(cardId);
return (
<PluginSlot
id="course_card_action_slot"
pluginProps={{
cardId,
}}
>
{!(isEntitlement || isVerified || isExecEd2UCourse) && <UpgradeButton cardId={cardId} />}
</PluginSlot>
);
};
const CourseCardActionSlot = ({ cardId }) => (
<PluginSlot
id="course_card_action_slot"
pluginProps={{
cardId,
}}
/>
);
CourseCardActionSlot.propTypes = {
cardId: PropTypes.string.isRequired,

View File

@@ -2,7 +2,6 @@ import { StrictDict } from 'utils';
export const categories = StrictDict({
dashboard: 'dashboard',
upgrade: 'upgrade',
userEngagement: 'user-engagement',
searchButton: 'search_button',
credit: 'credit',
@@ -14,9 +13,6 @@ export const events = StrictDict({
courseImageClicked: 'courseImageClicked',
courseTitleClicked: 'courseTitleClicked',
courseOptionsDropdownClicked: 'courseOptionsDropdownClicked',
upgradeButtonClicked: 'upgradeButtonClicked',
upgradeButtonClickedEnrollment: 'upgradeButtonClickedEnrollment',
upgradeButtonClickedUpsell: 'upgradeButtonClickedUpsell',
shareClicked: 'shareClicked',
userSettingsChanged: 'userSettingsChanged',
newSession: 'newSession',
@@ -36,9 +32,6 @@ export const eventNames = StrictDict({
courseImageClicked: 'edx.bi.dashboard.course_image.clicked',
courseTitleClicked: 'edx.bi.dashboard.course_title.clicked',
courseOptionsDropdownClicked: 'edx.bi.dashboard.course_options_dropdown.clicked',
upgradeButtonClicked: 'edx.bi.dashboard.upgrade_button.clicked',
upgradeButtonClickedEnrollment: 'edx.course.enrollment.upgrade.clicked',
upgradeButtonClickedUpsell: 'edx.bi.ecommerce.upsell_links_clicked',
shareClicked: 'edx.course.share_clicked',
userSettingsChanged: 'edx.user.settings.changed',
newSession: 'course-dashboard.new-session',

View File

@@ -1,4 +1,3 @@
import api from 'data/services/lms/api';
import { createEventTracker, createLinkTracker } from 'data/services/segment/utils';
import { categories, eventNames } from '../constants';
import * as module from './course';
@@ -31,20 +30,6 @@ export const courseLinkTracker = (eventName) => (courseId, href) => (
createLinkTracker(module.courseEventTracker(eventName, courseId), href)
);
// Upgrade Events
/**
* There are currently multiple tracked api events for the upgrade event, with different targets.
* Goal here is to split out the tracked events for easier testing.
*/
export const upgradeButtonClicked = (courseId) => createEventTracker(
eventNames.upgradeButtonClicked,
{ category: categories.upgrade, label: courseId },
);
export const upgradeButtonClickedUpsell = (courseId) => createEventTracker(
eventNames.upgradeButtonClickedUpsell,
{ ...upsellOptions, courseId },
);
// Non-Link events
export const courseOptionsDropdownClicked = (courseId) => (
module.courseEventTracker(eventNames.courseOptionsDropdownClicked, courseId)
@@ -57,19 +42,10 @@ export const courseTitleClicked = (...args) => (
module.courseLinkTracker(eventNames.courseTitleClicked)(...args));
export const enterCourseClicked = (...args) => (
module.courseLinkTracker(eventNames.enterCourseClicked)(...args));
export const upgradeClicked = (courseId, href) => createLinkTracker(
() => {
module.upgradeButtonClicked(courseId)();
module.upgradeButtonClickedUpsell(courseId)();
api.logUpgrade({ courseId });
},
href,
);
export default {
courseImageClicked,
courseOptionsDropdownClicked,
courseTitleClicked,
enterCourseClicked,
upgradeClicked,
};

View File

@@ -1,13 +1,8 @@
import { keyStore } from 'utils';
import api from 'data/services/lms/api';
import { createEventTracker, createLinkTracker } from 'data/services/segment/utils';
import { categories, eventNames } from '../constants';
import * as trackers from './course';
jest.mock('data/services/lms/api', () => ({
logUpgrade: jest.fn(),
}));
jest.mock('data/services/segment/utils', () => ({
createEventTracker: jest.fn(args => ({ createEventTracker: args })),
createLinkTracker: jest.fn((cb, href) => ({ createLinkTracker: { cb, href } })),
@@ -44,26 +39,6 @@ describe('course trackers', () => {
});
});
});
describe('Upgrade Events', () => {
describe('upgradeButtonClicked', () => {
it('creates an event tracker for upgradeButtonClicked event with category and label', () => {
expect(trackers.upgradeButtonClicked(courseId)).toEqual(createEventTracker(
eventNames.upgradeButtonClicked,
{ category: categories.upgrade, label: courseId },
));
});
});
describe('upgradeButtonClickedUpsell', () => {
it('creates an event tracker for upgradeButtonClickedUpsell eventwith upsellOptions', () => {
expect(trackers.upgradeButtonClickedUpsell(courseId)).toEqual(
createEventTracker(
eventNames.upgradeButtonClickedUpsell,
{ ...trackers.upsellOptions, courseId },
),
);
});
});
});
describe('Non-link events', () => {
describe('courseOptionsDropdownClicked', () => {
it('creates course event tracker for courseOptionsDropdownClicked event', () => {
@@ -101,25 +76,5 @@ describe('course trackers', () => {
);
});
});
describe('upgradeClicked', () => {
it('triggers upgrade actions and api.logUpgrade with courseId', () => {
const upgradeButtonClicked = jest.fn();
const upgradeButtonClickedUpsell = jest.fn();
const trackUpgradeButtonClicked = jest.fn(() => upgradeButtonClicked);
const trackUpgradeButtonClickedUpsell = jest.fn(() => upgradeButtonClickedUpsell);
jest.spyOn(trackers, moduleKeys.upgradeButtonClicked)
.mockImplementationOnce(trackUpgradeButtonClicked);
jest.spyOn(trackers, moduleKeys.upgradeButtonClickedUpsell)
.mockImplementationOnce(trackUpgradeButtonClickedUpsell);
const out = trackers.upgradeClicked(courseId, href).createLinkTracker;
expect(out.href).toEqual(href);
out.cb();
expect(trackUpgradeButtonClicked).toHaveBeenCalledWith(courseId);
expect(trackUpgradeButtonClickedUpsell).toHaveBeenCalledWith(courseId);
expect(upgradeButtonClicked).toHaveBeenCalledWith();
expect(upgradeButtonClickedUpsell).toHaveBeenCalledWith();
expect(api.logUpgrade).toHaveBeenCalledWith({ courseId });
});
});
});
});