diff --git a/src/containers/CourseCard/__snapshots__/index.test.jsx.snap b/src/containers/CourseCard/__snapshots__/index.test.jsx.snap
deleted file mode 100644
index 08f36c4..0000000
--- a/src/containers/CourseCard/__snapshots__/index.test.jsx.snap
+++ /dev/null
@@ -1,111 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`CourseCard component snapshot: collapsed 1`] = `
-
-
-
-
-
-
-
- }
- title={
-
- }
- />
-
-
-
-
-
-
-
-
-
-
-
-
-`;
-
-exports[`CourseCard component snapshot: not collapsed 1`] = `
-
-
-
-
-
-
-
- }
- title={
-
- }
- />
-
-
-
-
-
-
-
-
-
-
-
-
-`;
diff --git a/src/containers/CourseCard/components/CourseCardImage.test.jsx b/src/containers/CourseCard/components/CourseCardImage.test.jsx
index 05d0636..eee03c0 100644
--- a/src/containers/CourseCard/components/CourseCardImage.test.jsx
+++ b/src/containers/CourseCard/components/CourseCardImage.test.jsx
@@ -1,61 +1,72 @@
-import { shallow } from '@edx/react-unit-test-utils';
-
+import { render, screen } from '@testing-library/react';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import { formatMessage } from 'testUtils';
import { reduxHooks } from 'hooks';
-import track from 'tracking';
import useActionDisabledState from './hooks';
-import CourseCardImage from './CourseCardImage';
+import { CourseCardImage } from './CourseCardImage';
+import messages from '../messages';
-const homeUrl = 'home-url';
+jest.unmock('@edx/frontend-platform/i18n');
+jest.unmock('@openedx/paragon');
+jest.unmock('react');
-jest.mock('tracking', () => ({
- course: {
- courseImageClicked: jest.fn().mockName('segment.courseImageClicked'),
- },
-}));
+const homeUrl = 'https://example.com';
+const bannerImgSrc = 'banner-img-src.jpg';
jest.mock('hooks', () => ({
reduxHooks: {
- useCardCourseData: jest.fn(() => ({ bannerImgSrc: 'banner-img-src' })),
+ useCardCourseData: jest.fn(() => ({ bannerImgSrc })),
useCardCourseRunData: jest.fn(() => ({ homeUrl })),
- useCardEnrollmentData: jest.fn(() => ({ isVerified: true })),
+ useCardEnrollmentData: jest.fn(),
useTrackCourseEvent: jest.fn((eventName, cardId, url) => ({
trackCourseEvent: { eventName, cardId, url },
})),
},
}));
-jest.mock('./hooks', () => jest.fn(() => ({ disableCourseTitle: false })));
+
+jest.mock('./hooks', () => jest.fn());
describe('CourseCardImage', () => {
const props = {
- cardId: 'cardId',
- orientation: 'orientation',
+ cardId: 'test-card-id',
+ orientation: 'horizontal',
};
- beforeEach(() => {
- jest.clearAllMocks();
+
+ it('renders course image with correct attributes', () => {
+ useActionDisabledState.mockReturnValue({ disableCourseTitle: true });
+ reduxHooks.useCardEnrollmentData.mockReturnValue({ isVerified: true });
+ render();
+
+ const image = screen.getByRole('img', { name: formatMessage(messages.bannerAlt) });
+ expect(image).toBeInTheDocument();
+ expect(image.src).toContain(bannerImgSrc);
+ expect(image.parentElement).toHaveClass('horizontal');
});
- describe('snapshot', () => {
- test('renders clickable link course Image', () => {
- const wrapper = shallow();
- expect(wrapper.snapshot).toMatchSnapshot();
- expect(wrapper.instance.type).toBe('a');
- expect(wrapper.instance.props.onClick).toEqual(
- reduxHooks.useTrackCourseEvent(
- track.course.courseImageClicked,
- props.cardId,
- homeUrl,
- ),
- );
- });
- test('renders disabled link', () => {
- useActionDisabledState.mockReturnValueOnce({ disableCourseTitle: true });
- const wrapper = shallow();
- expect(wrapper.snapshot).toMatchSnapshot();
- expect(wrapper.instance.type).toBe('div');
- });
+
+ it('isVerified, should render badge', () => {
+ useActionDisabledState.mockReturnValue({ disableCourseTitle: false });
+ reduxHooks.useCardEnrollmentData.mockReturnValue({ isVerified: true });
+ render();
+
+ const badge = screen.getByText(formatMessage(messages.verifiedBanner));
+ expect(badge).toBeInTheDocument();
+ const badgeImg = screen.getByRole('img', { name: formatMessage(messages.verifiedBannerRibbonAlt) });
+ expect(badgeImg).toBeInTheDocument();
});
- describe('behavior', () => {
+
+ it('renders link with correct href if disableCourseTitle is false', () => {
+ useActionDisabledState.mockReturnValue({ disableCourseTitle: false });
+ reduxHooks.useCardEnrollmentData.mockReturnValue({ isVerified: false });
+ render();
+
+ const link = screen.getByRole('link');
+ expect(link).toHaveAttribute('href', homeUrl);
+ });
+ describe('hooks', () => {
it('initializes', () => {
- shallow();
+ useActionDisabledState.mockReturnValue({ disableCourseTitle: false });
+ reduxHooks.useCardEnrollmentData.mockReturnValue({ isVerified: true });
+ render();
expect(reduxHooks.useCardCourseData).toHaveBeenCalledWith(props.cardId);
expect(reduxHooks.useCardCourseRunData).toHaveBeenCalledWith(
props.cardId,
diff --git a/src/containers/CourseCard/components/CourseCardTitle.test.jsx b/src/containers/CourseCard/components/CourseCardTitle.test.jsx
index aa3dcdd..306df11 100644
--- a/src/containers/CourseCard/components/CourseCardTitle.test.jsx
+++ b/src/containers/CourseCard/components/CourseCardTitle.test.jsx
@@ -1,12 +1,10 @@
-import { shallow } from '@edx/react-unit-test-utils';
-
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { reduxHooks } from 'hooks';
import track from 'tracking';
import useActionDisabledState from './hooks';
import CourseCardTitle from './CourseCardTitle';
-const homeUrl = 'home-url';
-
jest.mock('tracking', () => ({
course: {
courseTitleClicked: jest.fn().mockName('segment.courseTitleClicked'),
@@ -15,53 +13,65 @@ jest.mock('tracking', () => ({
jest.mock('hooks', () => ({
reduxHooks: {
- useCardCourseData: jest.fn(() => ({ courseName: 'course-name' })),
- useCardCourseRunData: jest.fn(() => ({ homeUrl })),
- useTrackCourseEvent: jest.fn((eventName, cardId, url) => ({
- trackCourseEvent: { eventName, cardId, url },
- })),
+ useCardCourseData: jest.fn(),
+ useCardCourseRunData: jest.fn(),
+ useTrackCourseEvent: jest.fn(),
},
}));
+
jest.mock('./hooks', () => jest.fn(() => ({ disableCourseTitle: false })));
+jest.unmock('@edx/frontend-platform/i18n');
+jest.unmock('@openedx/paragon');
+jest.unmock('react');
+
describe('CourseCardTitle', () => {
const props = {
- cardId: 'cardId',
+ cardId: 'test-card-id',
};
+
+ const courseName = 'Test Course';
+ const homeUrl = 'http://test.com';
+ const handleTitleClick = jest.fn();
+
beforeEach(() => {
jest.clearAllMocks();
+ reduxHooks.useCardCourseData.mockReturnValue({ courseName });
+ reduxHooks.useCardCourseRunData.mockReturnValue({ homeUrl });
+ reduxHooks.useTrackCourseEvent.mockReturnValue(handleTitleClick);
});
- describe('snapshot', () => {
- test('renders clickable link course title', () => {
- const wrapper = shallow();
- expect(wrapper.snapshot).toMatchSnapshot();
- const title = wrapper.instance.findByTestId('CourseCardTitle');
- expect(title[0].type).toBe('a');
- expect(title[0].props.onClick).toEqual(
- reduxHooks.useTrackCourseEvent(
- track.course.courseTitleClicked,
- props.cardId,
- homeUrl,
- ),
- );
- });
- test('renders disabled link', () => {
- useActionDisabledState.mockReturnValueOnce({ disableCourseTitle: true });
- const wrapper = shallow();
- expect(wrapper.snapshot).toMatchSnapshot();
- const title = wrapper.instance.findByTestId('CourseCardTitle');
- expect(title[0].type).toBe('span');
- expect(title[0].props.onClick).toBeUndefined();
- });
+
+ it('renders course name as link when not disabled', async () => {
+ useActionDisabledState.mockReturnValue({ disableCourseTitle: false });
+ render();
+
+ const user = userEvent.setup();
+ const link = screen.getByRole('link', { name: courseName });
+ expect(link).toHaveAttribute('href', homeUrl);
+
+ await user.click(link);
+ expect(handleTitleClick).toHaveBeenCalled();
});
- describe('behavior', () => {
- it('initializes', () => {
- shallow();
- expect(reduxHooks.useCardCourseData).toHaveBeenCalledWith(props.cardId);
- expect(reduxHooks.useCardCourseRunData).toHaveBeenCalledWith(
- props.cardId,
- );
- expect(useActionDisabledState).toHaveBeenCalledWith(props.cardId);
- });
+
+ it('renders course name as span when disabled', () => {
+ useActionDisabledState.mockReturnValue({ disableCourseTitle: true });
+ render();
+
+ const text = screen.getByText(courseName);
+ expect(text).toBeInTheDocument();
+ expect(text.tagName.toLowerCase()).toBe('span');
+ });
+
+ it('uses correct hooks with cardId', () => {
+ useActionDisabledState.mockReturnValue({ disableCourseTitle: false });
+ render();
+
+ expect(reduxHooks.useCardCourseData).toHaveBeenCalledWith(props.cardId);
+ expect(reduxHooks.useCardCourseRunData).toHaveBeenCalledWith(props.cardId);
+ expect(reduxHooks.useTrackCourseEvent).toHaveBeenCalledWith(
+ track.course.courseTitleClicked,
+ props.cardId,
+ homeUrl,
+ );
});
});
diff --git a/src/containers/CourseCard/components/__snapshots__/CourseCardImage.test.jsx.snap b/src/containers/CourseCard/components/__snapshots__/CourseCardImage.test.jsx.snap
deleted file mode 100644
index 964a146..0000000
--- a/src/containers/CourseCard/components/__snapshots__/CourseCardImage.test.jsx.snap
+++ /dev/null
@@ -1,72 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`CourseCardImage snapshot renders clickable link course Image 1`] = `
-
-
-
-
-
- Verified
-
-
-
-
-
-`;
-
-exports[`CourseCardImage snapshot renders disabled link 1`] = `
-
-
-
-
-
- Verified
-
-
-
-
-
-`;
diff --git a/src/containers/CourseCard/components/__snapshots__/CourseCardTitle.test.jsx.snap b/src/containers/CourseCard/components/__snapshots__/CourseCardTitle.test.jsx.snap
deleted file mode 100644
index 34446ad..0000000
--- a/src/containers/CourseCard/components/__snapshots__/CourseCardTitle.test.jsx.snap
+++ /dev/null
@@ -1,33 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`CourseCardTitle snapshot renders clickable link course title 1`] = `
-
-`;
-
-exports[`CourseCardTitle snapshot renders disabled link 1`] = `
-
-
- course-name
-
-
-`;
diff --git a/src/containers/CourseCard/index.test.jsx b/src/containers/CourseCard/index.test.jsx
index ffab80d..ec26e00 100644
--- a/src/containers/CourseCard/index.test.jsx
+++ b/src/containers/CourseCard/index.test.jsx
@@ -1,5 +1,5 @@
-import React from 'react';
-import { shallow } from '@edx/react-unit-test-utils';
+import { render, screen } from '@testing-library/react';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
import CourseCard from '.';
import hooks from './hooks';
@@ -8,22 +8,47 @@ jest.mock('./hooks', () => ({
useIsCollapsed: jest.fn(),
}));
-jest.mock('./components/CourseCardBanners', () => 'CourseCardBanners');
-jest.mock('./components/CourseCardImage', () => 'CourseCardImage');
-jest.mock('./components/CourseCardMenu', () => 'CourseCardMenu');
-jest.mock('./components/CourseCardActions', () => 'CourseCardActions');
-jest.mock('./components/CourseCardDetails', () => 'CourseCardDetails');
-jest.mock('./components/CourseCardTitle', () => 'CourseCardTitle');
+const namesMockComponents = [
+ 'CourseCardBanners',
+ 'CourseCardImage',
+ 'CourseCardMenu',
+ 'CourseCardActions',
+ 'CourseCardDetails',
+ 'CourseCardTitle',
+];
+
+jest.mock('./components/CourseCardBanners', () => jest.fn(() => CourseCardBanners
));
+jest.mock('./components/CourseCardImage', () => jest.fn(() => CourseCardImage
));
+jest.mock('./components/CourseCardMenu', () => jest.fn(() => CourseCardMenu
));
+jest.mock('./components/CourseCardActions', () => jest.fn(() => CourseCardActions
));
+jest.mock('./components/CourseCardDetails', () => jest.fn(() => CourseCardDetails
));
+jest.mock('./components/CourseCardTitle', () => jest.fn(() => CourseCardTitle
));
+
+jest.unmock('@edx/frontend-platform/i18n');
+jest.unmock('@openedx/paragon');
+jest.unmock('react');
const cardId = 'test-card-id';
describe('CourseCard component', () => {
- test('snapshot: collapsed', () => {
+ it('collapsed', () => {
hooks.useIsCollapsed.mockReturnValueOnce(true);
- expect(shallow().snapshot).toMatchSnapshot();
+ render();
+ const cardImage = screen.getByText('CourseCardImage');
+ expect(cardImage.parentElement).not.toHaveClass('d-flex');
});
- test('snapshot: not collapsed', () => {
+ it('not collapsed', () => {
hooks.useIsCollapsed.mockReturnValueOnce(false);
- expect(shallow().snapshot).toMatchSnapshot();
+ render();
+ const cardImage = screen.getByText('CourseCardImage');
+ expect(cardImage.parentElement).toHaveClass('d-flex');
+ });
+ it('renders courseCard child components', () => {
+ hooks.useIsCollapsed.mockReturnValueOnce(false);
+ render();
+ namesMockComponents.map((courseCardName) => {
+ const courseCardComponent = screen.getByText(courseCardName);
+ return expect(courseCardComponent).toBeInTheDocument();
+ });
});
});
diff --git a/src/containers/CourseFilterControls/components/Checkbox.test.jsx b/src/containers/CourseFilterControls/components/Checkbox.test.jsx
index de5eafa..2bce7b7 100644
--- a/src/containers/CourseFilterControls/components/Checkbox.test.jsx
+++ b/src/containers/CourseFilterControls/components/Checkbox.test.jsx
@@ -1,14 +1,21 @@
-import { shallow } from '@edx/react-unit-test-utils';
+import { render, screen } from '@testing-library/react';
+import { formatMessage } from 'testUtils';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
import { FilterKeys } from 'data/constants/app';
import Checkbox from './Checkbox';
+import messages from '../messages';
+
+jest.unmock('@edx/frontend-platform/i18n');
+jest.unmock('@openedx/paragon');
+jest.unmock('react');
describe('Checkbox', () => {
- describe('snapshot', () => {
+ describe('renders correctly', () => {
Object.keys(FilterKeys).forEach((filterKey) => {
it(`renders ${filterKey}`, () => {
- const wrapper = shallow();
- expect(wrapper.snapshot).toMatchSnapshot();
+ render();
+ expect(screen.getByText(formatMessage(messages[filterKey]))).toBeInTheDocument();
});
});
});
diff --git a/src/containers/CourseFilterControls/components/FilterForm.test.jsx b/src/containers/CourseFilterControls/components/FilterForm.test.jsx
index 2905d3d..9a18490 100644
--- a/src/containers/CourseFilterControls/components/FilterForm.test.jsx
+++ b/src/containers/CourseFilterControls/components/FilterForm.test.jsx
@@ -1,29 +1,58 @@
-import { shallow } from '@edx/react-unit-test-utils';
-
+import { render, screen, fireEvent } from '@testing-library/react';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import { formatMessage } from 'testUtils';
import { FilterKeys } from 'data/constants/app';
-import FilterForm, { filterOrder } from './FilterForm';
+import { FilterForm, filterOrder } from './FilterForm';
+import messages from '../messages';
-jest.mock('./Checkbox', () => 'Checkbox');
+jest.unmock('@edx/frontend-platform/i18n');
+jest.unmock('@openedx/paragon');
+jest.unmock('react');
+
+const mockHandleFilterChange = jest.fn();
+
+const defaultProps = {
+ filters: [FilterKeys.inProgress],
+ handleFilterChange: mockHandleFilterChange,
+};
+
+const renderComponent = (props = defaultProps) => render(
+
+
+ ,
+);
describe('FilterForm', () => {
- const props = {
- filters: ['test-filter'],
- handleFilterChange: jest.fn().mockName('handleFilterChange'),
- };
- describe('snapshot', () => {
- test('renders', () => {
- const wrapper = shallow();
- expect(wrapper.snapshot).toMatchSnapshot();
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders all filter checkboxes in correct order', () => {
+ renderComponent();
+ const checkboxes = screen.getAllByRole('checkbox');
+ expect(checkboxes).toHaveLength(filterOrder.length);
+ checkboxes.forEach((checkbox, index) => {
+ expect(checkbox).toHaveAttribute('value', filterOrder[index]);
});
});
- test('filterOrder', () => {
- expect(filterOrder).toEqual([
- FilterKeys.inProgress,
- FilterKeys.notStarted,
- FilterKeys.done,
- FilterKeys.notEnrolled,
- FilterKeys.upgraded,
- ]);
+ it('checks boxes based on filters prop', () => {
+ const filters = [FilterKeys.inProgress, FilterKeys.done];
+ renderComponent({ ...defaultProps, filters });
+ filters.forEach(filter => {
+ expect(screen.getByRole('checkbox', { name: formatMessage(messages[filter]) })).toBeChecked();
+ });
+ });
+
+ it('calls handleFilterChange when checkbox is clicked', () => {
+ renderComponent();
+ const checkbox = screen.getByRole('checkbox', { name: formatMessage(messages.notStarted) });
+ fireEvent.click(checkbox);
+ expect(mockHandleFilterChange).toHaveBeenCalled();
+ });
+
+ it('displays course status heading', () => {
+ renderComponent();
+ expect(screen.getByText(/course status/i)).toBeInTheDocument();
});
});
diff --git a/src/containers/CourseFilterControls/components/__snapshots__/Checkbox.test.jsx.snap b/src/containers/CourseFilterControls/components/__snapshots__/Checkbox.test.jsx.snap
deleted file mode 100644
index 35226ca..0000000
--- a/src/containers/CourseFilterControls/components/__snapshots__/Checkbox.test.jsx.snap
+++ /dev/null
@@ -1,46 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Checkbox snapshot renders done 1`] = `
-
- Done
-
-`;
-
-exports[`Checkbox snapshot renders inProgress 1`] = `
-
- In-Progress
-
-`;
-
-exports[`Checkbox snapshot renders notEnrolled 1`] = `
-
- Not Enrolled
-
-`;
-
-exports[`Checkbox snapshot renders notStarted 1`] = `
-
- Not Started
-
-`;
-
-exports[`Checkbox snapshot renders upgraded 1`] = `
-
- Upgraded
-
-`;
diff --git a/src/containers/CourseFilterControls/components/__snapshots__/FilterForm.test.jsx.snap b/src/containers/CourseFilterControls/components/__snapshots__/FilterForm.test.jsx.snap
deleted file mode 100644
index 01f2167..0000000
--- a/src/containers/CourseFilterControls/components/__snapshots__/FilterForm.test.jsx.snap
+++ /dev/null
@@ -1,41 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`FilterForm snapshot renders 1`] = `
-
-
- Course Status
-
-
-
-
-
-
-
-
-
-`;