From a4b24bb0a8a16dc37cf4057af24770be8382e949 Mon Sep 17 00:00:00 2001 From: German Date: Wed, 8 Nov 2023 18:54:44 -0300 Subject: [PATCH] fix: update tests --- .../CoursewareResultsFilter.jsx | 3 +- .../CoursewareResultsFilter.test.jsx | 94 ++-- .../courseware-search/CoursewareSearch.jsx | 5 +- .../CoursewareSearch.test.jsx | 73 ++- .../CoursewareSearchResults.jsx | 3 +- .../CoursewareSearchResults.test.jsx.snap | 513 ------------------ src/course-tabs/CourseTabsNavigation.jsx | 4 +- 7 files changed, 117 insertions(+), 578 deletions(-) delete mode 100644 src/course-home/courseware-search/__snapshots__/CoursewareSearchResults.test.jsx.snap diff --git a/src/course-home/courseware-search/CoursewareResultsFilter.jsx b/src/course-home/courseware-search/CoursewareResultsFilter.jsx index c64810d150..edf74a1a6e 100644 --- a/src/course-home/courseware-search/CoursewareResultsFilter.jsx +++ b/src/course-home/courseware-search/CoursewareResultsFilter.jsx @@ -18,7 +18,7 @@ export const CoursewareSearchResultsFilter = ({ intl }) => { const { courseId } = useParams(); const lastSearch = useModel('contentSearchResults', courseId); - if (!lastSearch || !lastSearch.results.length) { return null; } + if (!lastSearch || !lastSearch?.results?.length) { return null; } const { total, results } = lastSearch; @@ -47,7 +47,6 @@ export const CoursewareSearchResultsFilter = ({ intl }) => { {filters.map(({ key, label }) => ( diff --git a/src/course-home/courseware-search/CoursewareResultsFilter.test.jsx b/src/course-home/courseware-search/CoursewareResultsFilter.test.jsx index 8052ab6cba..0ca7faea62 100644 --- a/src/course-home/courseware-search/CoursewareResultsFilter.test.jsx +++ b/src/course-home/courseware-search/CoursewareResultsFilter.test.jsx @@ -1,4 +1,7 @@ import React from 'react'; +import { AppProvider } from '@edx/frontend-platform/react'; +import { Route, Routes } from 'react-router-dom'; +import { history } from '@edx/frontend-platform'; import { initializeMockApp, render, @@ -6,16 +9,56 @@ import { waitFor, } from '../../setupTest'; import { CoursewareSearchResultsFilter, filteredResultsBySelection } from './CoursewareResultsFilter'; +import initializeStore from '../../store'; +import { useModel } from '../../generic/model-store'; + +jest.mock('../../generic/model-store', () => ({ + useModel: jest.fn(), +})); const mockResults = [ - { type: 'video', title: 'video_title' }, - { type: 'video', title: 'video_title2' }, - { type: 'document', title: 'document_title' }, - { type: 'text', title: 'text_title1' }, - { type: 'text', title: 'text_title2' }, - { type: 'text', title: 'text_title3' }, + { + id: 'video-1', type: 'video', title: 'video_title', score: 3, contentHits: 1, url: '/video-1', location: ['path1', 'path2'], + }, + { + id: 'video-2', type: 'video', title: 'video_title2', score: 2, contentHits: 1, url: '/video-2', location: ['path1', 'path2'], + }, + { + id: 'document-1', type: 'document', title: 'document_title', score: 3, contentHits: 1, url: '/document-1', location: ['path1', 'path2'], + }, + { + id: 'text-1', type: 'text', title: 'text_title1', score: 3, contentHits: 1, url: '/text-1', location: ['path1', 'path2'], + }, + { + id: 'text-2', type: 'text', title: 'text_title2', score: 2, contentHits: 1, url: '/text-2', location: ['path1', 'path2'], + }, + { + id: 'text-3', type: 'text', title: 'text_title3', score: 1, contentHits: 1, url: '/text-3', location: ['path1', 'path2'], + }, ]; +const decodedCourseId = 'course-v1:edX+DemoX+Demo_Course'; +const decodedSequenceId = 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@edx_introduction'; +const decodedUnitId = 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical_0270f6de40fc'; +const pathname = `/course/${decodedCourseId}/${decodedSequenceId}/${decodedUnitId}`; + +const intl = { + formatMessage: (message) => message?.defaultMessage || '', +}; + +function renderComponent(props = {}) { + const store = initializeStore(); + history.push(pathname); + const { container } = render( + + + } /> + + , + ); + return container; +} + describe('CoursewareSearchResultsFilter', () => { beforeAll(initializeMockApp); @@ -32,53 +75,42 @@ describe('CoursewareSearchResultsFilter', () => { expect(results.length).toEqual(mockResults.length); }); - it('returns all values when the key value "all" is provided', () => { - const results = filteredResultsBySelection({ filterKey: 'all', results: mockResults }); - - expect(results.length).toEqual(mockResults.length); - }); - it('returns only "video"-typed elements when the key value "video" is given', () => { - const results = filteredResultsBySelection({ filterKey: 'video', results: mockResults }); + const results = filteredResultsBySelection({ key: 'video', results: mockResults }); expect(results.length).toEqual(2); }); it('returns only "course_outline"-typed elements when the key value "document" is given', () => { - const results = filteredResultsBySelection({ filterKey: 'document', results: mockResults }); + const results = filteredResultsBySelection({ key: 'document', results: mockResults }); expect(results.length).toEqual(1); }); it('returns only "text"-typed elements when the key value "text" is given', () => { - const results = filteredResultsBySelection({ filterKey: 'text', results: mockResults }); + const results = filteredResultsBySelection({ key: 'text', results: mockResults }); expect(results.length).toEqual(3); }); }); describe('', () => { - it('should render', async () => { - await render(); + beforeEach(() => { + jest.clearAllMocks(); + }); - await waitFor(() => { - expect(screen.queryByTestId('courseware-search-results-tabs')).toBeInTheDocument(); - expect(screen.queryByText(/All content/)).toBeInTheDocument(); - expect(screen.queryByText(/Course outline/)).toBeInTheDocument(); - expect(screen.queryByText(/Text/)).toBeInTheDocument(); - expect(screen.queryByText(/Video/)).toBeInTheDocument(); + it('should render', async () => { + useModel.mockReturnValue({ + total: 6, + results: mockResults, + filters: [], }); - }); - it('should not render if no results are provided', async () => { - await render(); + await renderComponent(); await waitFor(() => { - expect(screen.queryByTestId('courseware-search-results-tabs')).not.toBeInTheDocument(); - expect(screen.queryByText(/All content/)).not.toBeInTheDocument(); - expect(screen.queryByText(/Course outline/)).not.toBeInTheDocument(); - expect(screen.queryByText(/Text/)).not.toBeInTheDocument(); - expect(screen.queryByText(/Video/)).not.toBeInTheDocument(); + expect(screen.queryByTestId('courseware-search-results-tabs')).toBeInTheDocument(); + expect(screen.queryByText(/All content/)).toBeInTheDocument(); }); }); }); diff --git a/src/course-home/courseware-search/CoursewareSearch.jsx b/src/course-home/courseware-search/CoursewareSearch.jsx index b9611a4903..1aa7f9a213 100644 --- a/src/course-home/courseware-search/CoursewareSearch.jsx +++ b/src/course-home/courseware-search/CoursewareSearch.jsx @@ -18,7 +18,7 @@ import CoursewareSearchResultsFilterContainer from './CoursewareResultsFilter'; import { updateModel, useModel } from '../../generic/model-store'; import { searchCourseContent } from '../data/thunks'; -const CoursewareSearch = ({ intl, ...sectionProps }) => { +const CoursewareSearch = ({ intl }) => { const { courseId } = useParams(); const dispatch = useDispatch(); const { org } = useModel('courseHomeMeta', courseId); @@ -88,7 +88,7 @@ const CoursewareSearch = ({ intl, ...sectionProps }) => { } return ( -
+
); }; + CoursewareSearch.propTypes = { intl: intlShape.isRequired, }; diff --git a/src/course-home/courseware-search/CoursewareSearch.test.jsx b/src/course-home/courseware-search/CoursewareSearch.test.jsx index 276ff1a194..6c8ab76d02 100644 --- a/src/course-home/courseware-search/CoursewareSearch.test.jsx +++ b/src/course-home/courseware-search/CoursewareSearch.test.jsx @@ -1,36 +1,61 @@ import React from 'react'; +import { history } from '@edx/frontend-platform'; +import { AppProvider } from '@edx/frontend-platform/react'; +import { Route, Routes } from 'react-router-dom'; import { - fireEvent, initializeMockApp, render, screen, } from '../../setupTest'; import { CoursewareSearch } from './index'; -import { setShowSearch } from '../data/slice'; import { useElementBoundingBox, useLockScroll } from './hooks'; +import initializeStore from '../../store'; +import { useModel } from '../../generic/model-store'; -const mockDispatch = jest.fn(); - -jest.mock('./hooks'); -jest.mock('../data/slice'); -jest.mock('react-redux', () => ({ - ...jest.requireActual('react-redux'), - useDispatch: () => mockDispatch, +jest.mock('../../generic/model-store', () => ({ + useModel: jest.fn(), })); +const decodedCourseId = 'course-v1:edX+DemoX+Demo_Course'; +const decodedSequenceId = 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@edx_introduction'; +const decodedUnitId = 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical_0270f6de40fc'; +const pathname = `/course/${decodedCourseId}/${decodedSequenceId}/${decodedUnitId}`; + const tabsTopPosition = 128; +const defaultProps = { + org: 'edX', + loading: false, + searchKeyword: '', + errors: undefined, + total: 0, +}; + +const intl = { + formatMessage: (message) => message?.defaultMessage || '', +}; + function renderComponent(props = {}) { - const { container } = render(); + const store = initializeStore(); + history.push(pathname); + const { container } = render( + + + } /> + + , + ); return container; } +const mockModels = ((props = defaultProps) => { + useModel.mockReturnValue(props); +}); + describe('CoursewareSearch', () => { - beforeAll(async () => { - initializeMockApp(); - }); + beforeAll(initializeMockApp); - afterEach(() => { + beforeEach(() => { jest.clearAllMocks(); }); @@ -39,33 +64,28 @@ describe('CoursewareSearch', () => { useElementBoundingBox.mockImplementation(() => ({ top: tabsTopPosition })); }); - beforeEach(() => { + it('Should use useElementBoundingBox() and useLockScroll() hooks', () => { + mockModels(); renderComponent(); - }); - it('Should use useElementBoundingBox() and useLockScroll() hooks', () => { expect(useElementBoundingBox).toBeCalledTimes(1); expect(useLockScroll).toBeCalledTimes(1); }); it('Should have a "--modal-top-position" CSS variable matching the CourseTabsNavigation top position', () => { + mockModels(); + renderComponent(); + const section = screen.getByTestId('courseware-search-section'); expect(section.style.getPropertyValue('--modal-top-position')).toBe(`${tabsTopPosition}px`); }); - - it('Should dispatch setShowSearch(true) when clicking the close button', () => { - const button = screen.getByTestId('courseware-search-close-button'); - fireEvent.click(button); - - expect(mockDispatch).toHaveBeenCalledTimes(1); - expect(setShowSearch).toHaveBeenCalledTimes(1); - expect(setShowSearch).toHaveBeenCalledWith(false); - }); }); describe('when CourseTabsNavigation is not present', () => { it('Should use "--modal-top-position: 0" if nce element is not present', () => { useElementBoundingBox.mockImplementation(() => undefined); + + mockModels(); renderComponent(); const section = screen.getByTestId('courseware-search-section'); @@ -75,6 +95,7 @@ describe('CoursewareSearch', () => { describe('when passing extra props', () => { it('Should pass on extra props to section element', () => { + mockModels(); renderComponent({ foo: 'bar' }); const section = screen.getByTestId('courseware-search-section'); diff --git a/src/course-home/courseware-search/CoursewareSearchResults.jsx b/src/course-home/courseware-search/CoursewareSearchResults.jsx index 126e9cf3dc..a7be9c7735 100644 --- a/src/course-home/courseware-search/CoursewareSearchResults.jsx +++ b/src/course-home/courseware-search/CoursewareSearchResults.jsx @@ -12,6 +12,7 @@ const iconTypeMapping = { text: TextFields, video: VideoCamera, }; + const defaultIcon = Article; const CoursewareSearchResults = ({ results }) => { @@ -57,7 +58,7 @@ const CoursewareSearchResults = ({ results }) => { // The breadcrumbs are not expected to change. // eslint-disable-next-line react/no-array-index-key location.map((bc, i) => (
  • {bc}
  • )) -} + } ) : null} diff --git a/src/course-home/courseware-search/__snapshots__/CoursewareSearchResults.test.jsx.snap b/src/course-home/courseware-search/__snapshots__/CoursewareSearchResults.test.jsx.snap deleted file mode 100644 index 25f3541274..0000000000 --- a/src/course-home/courseware-search/__snapshots__/CoursewareSearchResults.test.jsx.snap +++ /dev/null @@ -1,513 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CoursewareSearchResults when list of results is provided should match the snapshot 1`] = ` -
    - -
    - - - -
    -
    -
    - - A Comprehensive Introduction to Supply Chain Analytics - -
    -
    -
    - -
    - - - -
    -
    -
    - - Basics of Data Collection for Supply Chain Analytics: Exploring Methods and Techniques for Optimal Data Gathering - -
    -
      -
    • -
      - A Comprehensive Introduction to Supply Chain Analytics -
      -
    • -
    -
    -
    - -
    - - - -
    -
    -
    - - Zero-Waste Strategies in Supply Chain Management - -
    -
      -
    • -
      - A Comprehensive Introduction to Supply Chain Analytics -
      -
    • -
    • -
      - Basics of Data Collection for Supply Chain Analytics: Exploring Methods and Techniques for Optimal Data Gathering -
      -
    • -
    -
    -
    - -
    - - - -
    -
    -
    - - Addressing Overproduction and Excess Inventory in Supply Chains - -
    -
      -
    • -
      - A Comprehensive Introduction to Supply Chain Analytics -
      -
    • -
    • -
      - Basics of Data Collection for Supply Chain Analytics: Exploring Methods and Techniques for Optimal Data Gathering -
      -
    • -
    • -
      - Zero-Waste Strategies in Supply Chain Management -
      -
    • -
    -
    -
    - -
    - - - -
    -
    -
    - - Balancing Supply and Demand - - - 9 - -
    -
      -
    • -
      - Strategic Sourcing and Its Impact on Supply-Demand Balance -
      -
    • -
    • -
      - Dealing with Over-supply and Under-supply Situations -
      -
    • -
    • -
      - Scenario Planning for Uncertain Supply-Demand Conditions -
      -
    • -
    -
    -
    - -
    - - - -
    -
    -
    - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin ornare porttitor purus, eget vehicula lorem ullamcorper in. In pellentesque vehicula diam, eget efficitur nisl aliquet id. Donec tincidunt dictum odio quis placerat. - - - 6 - -
    -
      -
    • -
      - Section name -
      -
    • -
    • -
      - Subsection name -
      -
    • -
    • -
      - Unit name -
      -
    • -
    -
    -
    - -
    - - - -
    -
    -
    - - TextSupply chain toolbox - -
    -
      -
    • -
      - Section name -
      -
    • -
    • -
      - Subsection name -
      -
    • -
    • -
      - Unit name -
      -
    • -
    -
    -
    - -
    - - - -
    -
    -
    - - Utilizing Demand-Driven Strategies - - - 20 - -
    -
      -
    • -
      - Section name -
      -
    • -
    • -
      - Subsection name -
      -
    • -
    • -
      - Unit name -
      -
    • -
    -
    -
    - -
    - - - -
    -
    -
    - - Video - - - 1 - -
    -
      -
    • -
      - Section name -
      -
    • -
    • -
      - Subsection name -
      -
    • -
    • -
      - Unit name -
      -
    • -
    -
    -
    -
    -`; diff --git a/src/course-tabs/CourseTabsNavigation.jsx b/src/course-tabs/CourseTabsNavigation.jsx index 87281a45be..014f1269a5 100644 --- a/src/course-tabs/CourseTabsNavigation.jsx +++ b/src/course-tabs/CourseTabsNavigation.jsx @@ -34,9 +34,7 @@ const CourseTabsNavigation = ({ ))} - {show ? ( - - ) : null} + {show && } ); };