From 7da6de85975f708aac37316be5d587fe7d1a4b72 Mon Sep 17 00:00:00 2001 From: Navin Karkera Date: Wed, 20 Dec 2023 17:08:43 +0530 Subject: [PATCH] test: replace query calls by button clicks --- src/course-outline/CourseOutline.test.jsx | 210 ++++++++++-------- .../__mocks__/courseOutlineIndex.js | 4 +- src/course-outline/card-header/CardHeader.jsx | 26 ++- .../card-header/CardHeader.test.jsx | 6 +- .../delete-modal/DeleteModal.jsx | 1 + .../header-navigations/HeaderNavigations.jsx | 1 + .../publish-modal/PublishModal.jsx | 5 +- .../section-card/SectionCard.test.jsx | 11 +- src/course-outline/status-bar/StatusBar.jsx | 2 +- .../subsection-card/SubsectionCard.test.jsx | 8 +- 10 files changed, 162 insertions(+), 112 deletions(-) diff --git a/src/course-outline/CourseOutline.test.jsx b/src/course-outline/CourseOutline.test.jsx index 84e3382a75..0f4ee33e17 100644 --- a/src/course-outline/CourseOutline.test.jsx +++ b/src/course-outline/CourseOutline.test.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { - render, waitFor, cleanup, fireEvent, within, + act, render, waitFor, cleanup, fireEvent, within, } from '@testing-library/react'; import { IntlProvider } from '@edx/frontend-platform/i18n'; import { AppProvider } from '@edx/frontend-platform/react'; @@ -19,20 +19,10 @@ import { getXBlockBaseApiUrl, } from './data/api'; import { - addNewSectionQuery, - addNewSubsectionQuery, configureCourseSectionQuery, - deleteCourseSectionQuery, - deleteCourseSubsectionQuery, - duplicateSectionQuery, - duplicateSubsectionQuery, - editCourseItemQuery, - enableCourseHighlightsEmailsQuery, fetchCourseBestPracticesQuery, fetchCourseLaunchQuery, fetchCourseOutlineIndexQuery, - fetchCourseReindexQuery, - publishCourseItemQuery, updateCourseSectionHighlightsQuery, setSectionOrderListQuery, } from './data/thunk'; @@ -50,6 +40,7 @@ import CourseOutline from './CourseOutline'; import messages from './messages'; import headerMessages from './header-navigations/messages'; import cardHeaderMessages from './card-header/messages'; +import enableHighlightsModalMessages from './enable-highlights-modal/messages'; let axiosMock; let store; @@ -111,24 +102,39 @@ describe('', () => { }); it('check reindex and render success alert is correctly', async () => { - const { getByText } = render(); + const { findByText, findByTestId } = render(); axiosMock .onGet(getCourseReindexApiUrl(courseOutlineIndexMock.reindexLink)) .reply(200); - await executeThunk(fetchCourseReindexQuery(courseId, courseOutlineIndexMock.reindexLink), store.dispatch); + const reindexButton = await findByTestId('course-reindex'); + fireEvent.click(reindexButton); + + expect(await findByText(messages.alertSuccessDescription.defaultMessage)).toBeInTheDocument(); + }); + + it('render error alert after failed reindex correctly', async () => { + const { findByText, findByTestId } = render(); + + axiosMock + .onGet(getCourseReindexApiUrl(courseOutlineIndexMock.reindexLink)) + .reply(500); + const reindexButton = await findByTestId('course-reindex'); + await act(async () => { + fireEvent.click(reindexButton); + }); - expect(getByText(messages.alertSuccessDescription.defaultMessage)).toBeInTheDocument(); + expect(await findByText(messages.alertErrorTitle.defaultMessage)).toBeInTheDocument(); }); it('adds new section correctly', async () => { - const { findAllByTestId } = render(); - let element = await findAllByTestId('section-card'); + const { findAllByTestId, findByTestId } = render(); + let elements = await findAllByTestId('section-card'); window.HTMLElement.prototype.getBoundingClientRect = jest.fn(() => ({ top: 0, bottom: 4000, })); - expect(element.length).toBe(4); + expect(elements.length).toBe(4); axiosMock .onPost(getXBlockBaseApiUrl()) @@ -138,16 +144,18 @@ describe('', () => { axiosMock .onGet(getXBlockApiUrl(courseSectionMock.id)) .reply(200, courseSectionMock); - await executeThunk(addNewSectionQuery(courseId), store.dispatch); + const newSectionButton = await findByTestId('new-section-button'); + await act(async () => { + fireEvent.click(newSectionButton); + }); - element = await findAllByTestId('section-card'); - expect(element.length).toBe(5); + elements = await findAllByTestId('section-card'); + expect(elements.length).toBe(5); expect(window.HTMLElement.prototype.scrollIntoView).toBeCalled(); }); it('adds new subsection correctly', async () => { const { findAllByTestId } = render(); - const sectionId = courseOutlineIndexMock.courseStructure.childInfo.children[0].id; const [section] = await findAllByTestId('section-card'); let subsections = await within(section).findAllByTestId('subsection-card'); expect(subsections.length).toBe(1); @@ -164,24 +172,16 @@ describe('', () => { axiosMock .onGet(getXBlockApiUrl(courseSubsectionMock.id)) .reply(200, courseSubsectionMock); - await executeThunk(addNewSubsectionQuery(sectionId), store.dispatch); + const newSubsectionButton = await within(section).findByTestId('new-subsection-button'); + await act(async () => { + fireEvent.click(newSubsectionButton); + }); subsections = await within(section).findAllByTestId('subsection-card'); expect(subsections.length).toBe(2); expect(window.HTMLElement.prototype.scrollIntoView).toBeCalled(); }); - it('render error alert after failed reindex correctly', async () => { - const { getByText } = render(); - - axiosMock - .onGet(getCourseReindexApiUrl('some link')) - .reply(500); - await executeThunk(fetchCourseReindexQuery(courseId, 'some link'), store.dispatch); - - expect(getByText(messages.alertErrorTitle.defaultMessage)).toBeInTheDocument(); - }); - it('render checklist value correctly', async () => { const { getByText } = render(); @@ -208,15 +208,9 @@ describe('', () => { }); it('check highlights are enabled after enable highlights query is successful', async () => { - const { findByTestId } = render(); - - axiosMock - .onGet(getCourseOutlineIndexApiUrl(courseId)) - .reply(200, { - ...courseOutlineIndexMock, - highlightsEnabledForMessaging: false, - }); + const { findByTestId, findByText } = render(); + axiosMock.reset(); axiosMock .onPost(getEnableHighlightsEmailsApiUrl(courseId), { publish: 'republish', @@ -225,8 +219,22 @@ describe('', () => { }, }) .reply(200); + axiosMock + .onGet(getCourseOutlineIndexApiUrl(courseId)) + .reply(200, { + ...courseOutlineIndexMock, + courseStructure: { + ...courseOutlineIndexMock.courseStructure, + highlightsEnabledForMessaging: true, + }, + }); - await executeThunk(enableCourseHighlightsEmailsQuery(courseId), store.dispatch); + const enableButton = await findByTestId('highlights-enable-button'); + fireEvent.click(enableButton); + const saveButton = await findByText(enableHighlightsModalMessages.submitButton.defaultMessage); + await act(async () => { + fireEvent.click(saveButton); + }); expect(await findByTestId('highlights-enabled-span')).toBeInTheDocument(); }); @@ -264,10 +272,10 @@ describe('', () => { }); it('check edit section when edit query is successfully', async () => { - const { getByText } = render(); + const { findAllByTestId, findByText } = render(); const newDisplayName = 'New section name'; - const section = courseOutlineIndexMock.courseStructure.childInfo.children[0]; + const [section] = courseOutlineIndexMock.courseStructure.childInfo.children; axiosMock .onPost(getCourseItemApiUrl(section.id, { @@ -278,40 +286,67 @@ describe('', () => { .reply(200, { dummy: 'value' }); axiosMock .onGet(getXBlockApiUrl(section.id)) - .reply(200); - - await executeThunk(editCourseItemQuery(section.id, section.id, newDisplayName), store.dispatch); + .reply(200, { + ...section, + display_name: newDisplayName, + }); - await waitFor(() => { - expect(getByText(section.displayName)).toBeInTheDocument(); + const [sectionElement] = await findAllByTestId('section-card'); + const editButton = await within(sectionElement).findByTestId('section-edit-button'); + fireEvent.click(editButton); + const editField = await within(sectionElement).findByTestId('section-edit-field'); + fireEvent.change(editField, { target: { value: newDisplayName } }); + await act(async () => { + fireEvent.blur(editField); }); + + expect(await findByText(newDisplayName)).toBeInTheDocument(); }); - it('check whether section is deleted when delete query is successfully', async () => { - const { queryByText } = render(); - const section = courseOutlineIndexMock.courseStructure.childInfo.children[1]; + it('check whether section is deleted when delete button is clicked', async () => { + const { findAllByTestId, findByTestId, queryByText } = render(); + const [section] = courseOutlineIndexMock.courseStructure.childInfo.children; await waitFor(() => { expect(queryByText(section.displayName)).toBeInTheDocument(); }); axiosMock.onDelete(getCourseItemApiUrl(section.id)).reply(200); - await executeThunk(deleteCourseSectionQuery(section.id), store.dispatch); + + const [sectionElement] = await findAllByTestId('section-card'); + const menu = await within(sectionElement).findByTestId('section-card-header__menu-button'); + fireEvent.click(menu); + const deleteButton = await within(sectionElement).findByTestId('section-card-header__menu-delete-button'); + fireEvent.click(deleteButton); + const confirmButton = await findByTestId('delete-confirm-button'); + await act(async () => { + fireEvent.click(confirmButton); + }); await waitFor(() => { expect(queryByText(section.displayName)).not.toBeInTheDocument(); }); }); - it('check whether subsection is deleted when delete query is successfully', async () => { - const { queryByText } = render(); - const section = courseOutlineIndexMock.courseStructure.childInfo.children[1]; + it('check whether subsection is deleted when delete button is clicked', async () => { + const { findAllByTestId, findByTestId, queryByText } = render(); + const [section] = courseOutlineIndexMock.courseStructure.childInfo.children; const [subsection] = section.childInfo.children; await waitFor(() => { expect(queryByText(subsection.displayName)).toBeInTheDocument(); }); axiosMock.onDelete(getCourseItemApiUrl(subsection.id)).reply(200); - await executeThunk(deleteCourseSubsectionQuery(subsection.id, section.id), store.dispatch); + + const [sectionElement] = await findAllByTestId('section-card'); + const [subsectionElement] = await within(sectionElement).findAllByTestId('subsection-card'); + const menu = await within(subsectionElement).findByTestId('subsection-card-header__menu-button'); + fireEvent.click(menu); + const deleteButton = await within(subsectionElement).findByTestId('subsection-card-header__menu-delete-button'); + fireEvent.click(deleteButton); + const confirmButton = await findByTestId('delete-confirm-button'); + await act(async () => { + fireEvent.click(confirmButton); + }); await waitFor(() => { expect(queryByText(subsection.displayName)).not.toBeInTheDocument(); @@ -320,9 +355,7 @@ describe('', () => { it('check whether section is duplicated successfully', async () => { const { findAllByTestId } = render(); - const section = courseOutlineIndexMock.courseStructure.childInfo.children[0]; - const sectionId = section.id; - const courseBlockId = courseOutlineIndexMock.courseStructure.id; + const [section] = courseOutlineIndexMock.courseStructure.childInfo.children; expect(await findAllByTestId('section-card')).toHaveLength(4); axiosMock @@ -336,8 +369,14 @@ describe('', () => { .reply(200, { ...section, }); - await executeThunk(duplicateSectionQuery(sectionId, courseBlockId), store.dispatch); + const [sectionElement] = await findAllByTestId('section-card'); + const menu = await within(sectionElement).findByTestId('section-card-header__menu-button'); + fireEvent.click(menu); + const duplicateButton = await within(sectionElement).findByTestId('section-card-header__menu-duplicate-button'); + await act(async () => { + fireEvent.click(duplicateButton); + }); expect(await findAllByTestId('section-card')).toHaveLength(5); }); @@ -346,7 +385,6 @@ describe('', () => { const section = courseOutlineIndexMock.courseStructure.childInfo.children[0]; let [sectionElement] = await findAllByTestId('section-card'); const [subsection] = section.childInfo.children; - const subsectionId = subsection.id; let subsections = await within(sectionElement).findAllByTestId('subsection-card'); expect(subsections.length).toBe(1); @@ -363,33 +401,21 @@ describe('', () => { ...section, }); - await executeThunk(duplicateSubsectionQuery(subsectionId, section.id), store.dispatch); + const menu = await within(subsections[0]).findByTestId('subsection-card-header__menu-button'); + fireEvent.click(menu); + const duplicateButton = await within(subsections[0]).findByTestId('subsection-card-header__menu-duplicate-button'); + await act(async () => { + fireEvent.click(duplicateButton); + }); [sectionElement] = await findAllByTestId('section-card'); subsections = await within(sectionElement).findAllByTestId('subsection-card'); expect(subsections.length).toBe(2); }); - it('check publish section when publish query is successfully', async () => { - cleanup(); - const { getAllByTestId } = render(); - const section = courseOutlineIndexMock.courseStructure.childInfo.children[0]; - - axiosMock - .onGet(getCourseOutlineIndexApiUrl(courseId)) - .reply(200, { - courseOutlineIndexMock, - courseStructure: { - childInfo: { - children: [ - { - ...section, - published: false, - }, - ], - }, - }, - }); + it('check section is published when publish button is clicked', async () => { + const [section] = courseOutlineIndexMock.courseStructure.childInfo.children; + const { findAllByTestId, findByTestId } = render(); axiosMock .onPost(getCourseItemApiUrl(section.id), { @@ -405,16 +431,21 @@ describe('', () => { releasedToStudents: false, }); - await executeThunk(publishCourseItemQuery(section.id, section.id), store.dispatch); + const [sectionElement] = await findAllByTestId('section-card'); + const menu = await within(sectionElement).findByTestId('section-card-header__menu-button'); + fireEvent.click(menu); + const publishButton = await within(sectionElement).findByTestId('section-card-header__menu-publish-button'); + await act(async () => fireEvent.click(publishButton)); + const confirmButton = await findByTestId('publish-confirm-button'); + await act(async () => fireEvent.click(confirmButton)); - const firstSection = getAllByTestId('section-card')[0]; expect( - firstSection.querySelector('.item-card-header__badge-status'), + sectionElement.querySelector('.item-card-header__badge-status'), ).toHaveTextContent(cardHeaderMessages.statusBadgePublishedNotLive.defaultMessage); }); it('check configure section when configure query is successful', async () => { - const { findAllByTestId, findByText, findByPlaceholderText } = render(); + const { findAllByTestId, findByPlaceholderText } = render(); const section = courseOutlineIndexMock.courseStructure.childInfo.children[0]; const newReleaseDate = '2025-08-10T10:00:00Z'; axiosMock @@ -433,8 +464,7 @@ describe('', () => { const [firstSection] = await findAllByTestId('section-card'); - const sectionDropdownButton = firstSection.querySelector('#section-card-header__menu'); - expect(sectionDropdownButton).toBeInTheDocument(); + const sectionDropdownButton = await within(firstSection).findByTestId('section-card-header__menu-button'); fireEvent.click(sectionDropdownButton); axiosMock @@ -446,7 +476,7 @@ describe('', () => { await executeThunk(configureCourseSectionQuery(section.id, true, newReleaseDate), store.dispatch); fireEvent.click(sectionDropdownButton); - const configureBtn = await findByText(cardHeaderMessages.menuConfigure.defaultMessage); + const configureBtn = await within(firstSection).findByTestId('section-card-header__menu-configure-button'); fireEvent.click(configureBtn); const datePicker = await findByPlaceholderText('MM/DD/YYYY'); diff --git a/src/course-outline/__mocks__/courseOutlineIndex.js b/src/course-outline/__mocks__/courseOutlineIndex.js index b65784c9be..d7ed4ed35f 100644 --- a/src/course-outline/__mocks__/courseOutlineIndex.js +++ b/src/course-outline/__mocks__/courseOutlineIndex.js @@ -55,7 +55,7 @@ module.exports = { }, ], showCorrectness: 'always', - highlightsEnabledForMessaging: true, + highlightsEnabledForMessaging: false, highlightsEnabled: true, highlightsPreviewOnly: false, highlightsDocUrl: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/developing_course/course_sections.html#set-section-highlights-for-weekly-course-highlight-messages', @@ -72,7 +72,7 @@ module.exports = { category: 'chapter', hasChildren: true, editedOn: 'Aug 23, 2023 at 12:35 UTC', - published: true, + published: false, publishedOn: 'Aug 23, 2023 at 12:35 UTC', studioUrl: '/course/course-v1:edX+DemoX+Demo_Course?show=block-v1%3AedX%2BDemoX%2BDemo_Course%2Btype%40chapter%2Bblock%40d8a6192ade314473a78242dfeedfbf5b', releasedToStudents: true, diff --git a/src/course-outline/card-header/CardHeader.jsx b/src/course-outline/card-header/CardHeader.jsx index 150d90c269..2c06e02220 100644 --- a/src/course-outline/card-header/CardHeader.jsx +++ b/src/course-outline/card-header/CardHeader.jsx @@ -62,7 +62,7 @@ const CardHeader = ({ {isFormOpen ? ( e && e.focus()} value={titleValue} name="displayName" @@ -115,7 +115,7 @@ const CardHeader = ({
{!isFormOpen && ( {intl.formatMessage(messages.menuPublish)} - {intl.formatMessage(messages.menuConfigure)} - {intl.formatMessage(messages.menuDuplicate)} - {intl.formatMessage(messages.menuDelete)} + + {intl.formatMessage(messages.menuConfigure)} + + + {intl.formatMessage(messages.menuDuplicate)} + + + {intl.formatMessage(messages.menuDelete)} +
diff --git a/src/course-outline/card-header/CardHeader.test.jsx b/src/course-outline/card-header/CardHeader.test.jsx index e9269531c9..704d69baad 100644 --- a/src/course-outline/card-header/CardHeader.test.jsx +++ b/src/course-outline/card-header/CardHeader.test.jsx @@ -146,7 +146,7 @@ describe('', () => { it('calls onClickEdit when the button is clicked', async () => { const { findByTestId } = renderComponent(); - const editButton = await findByTestId('edit-button'); + const editButton = await findByTestId('section-edit-button'); fireEvent.click(editButton); waitFor(() => { expect(onClickEditMock).toHaveBeenCalled(); @@ -159,7 +159,7 @@ describe('', () => { isFormOpen: true, }); - expect(await findByTestId('edit field')).toBeInTheDocument(); + expect(await findByTestId('section-edit-field')).toBeInTheDocument(); waitFor(() => { expect(queryByTestId('section-card-header__expanded-btn')).not.toBeInTheDocument(); expect(queryByTestId('edit-button')).not.toBeInTheDocument(); @@ -173,7 +173,7 @@ describe('', () => { isDisabledEditField: true, }); - expect(await findByTestId('edit field')).toBeDisabled(); + expect(await findByTestId('section-edit-field')).toBeDisabled(); }); it('calls onClickDelete when item is clicked', async () => { diff --git a/src/course-outline/delete-modal/DeleteModal.jsx b/src/course-outline/delete-modal/DeleteModal.jsx index a6c08fdeeb..4569ac3248 100644 --- a/src/course-outline/delete-modal/DeleteModal.jsx +++ b/src/course-outline/delete-modal/DeleteModal.jsx @@ -28,6 +28,7 @@ const DeleteModal = ({ isOpen, close, onDeleteSubmit }) => { {intl.formatMessage(messages.cancelButton)} diff --git a/src/course-outline/section-card/SectionCard.test.jsx b/src/course-outline/section-card/SectionCard.test.jsx index 1ffda7b738..d391e48ffc 100644 --- a/src/course-outline/section-card/SectionCard.test.jsx +++ b/src/course-outline/section-card/SectionCard.test.jsx @@ -85,19 +85,18 @@ describe('', () => { }); it('title only updates if changed', async () => { - const { queryByTestId, findByTestId } = renderComponent(); + const { findByTestId } = renderComponent(); - let editButton = await findByTestId('edit-button'); + let editButton = await findByTestId('section-edit-button'); fireEvent.click(editButton); - let editField = await findByTestId('edit field'); - expect(queryByTestId('edit field')).toBeInTheDocument(); + let editField = await findByTestId('section-edit-field'); fireEvent.blur(editField); expect(onEditSectionSubmit).not.toHaveBeenCalled(); - editButton = await findByTestId('edit-button'); + editButton = await findByTestId('section-edit-button'); fireEvent.click(editButton); - editField = await findByTestId('edit field'); + editField = await findByTestId('section-edit-field'); fireEvent.change(editField, { target: { value: 'some random value' } }); fireEvent.blur(editField); expect(onEditSectionSubmit).toHaveBeenCalled(); diff --git a/src/course-outline/status-bar/StatusBar.jsx b/src/course-outline/status-bar/StatusBar.jsx index c843338ab1..3e08d5b414 100644 --- a/src/course-outline/status-bar/StatusBar.jsx +++ b/src/course-outline/status-bar/StatusBar.jsx @@ -81,7 +81,7 @@ const StatusBar = ({ {intl.formatMessage(messages.highlightEmailsEnabled)} ) : ( - )} diff --git a/src/course-outline/subsection-card/SubsectionCard.test.jsx b/src/course-outline/subsection-card/SubsectionCard.test.jsx index c0e6cd9498..080ae40da0 100644 --- a/src/course-outline/subsection-card/SubsectionCard.test.jsx +++ b/src/course-outline/subsection-card/SubsectionCard.test.jsx @@ -108,16 +108,16 @@ describe('', () => { it('title only updates if changed', async () => { const { findByTestId } = renderComponent(); - let editButton = await findByTestId('edit-button'); + let editButton = await findByTestId('subsection-edit-button'); fireEvent.click(editButton); - let editField = await findByTestId('edit field'); + let editField = await findByTestId('subsection-edit-field'); fireEvent.blur(editField); expect(onEditSubectionSubmit).not.toHaveBeenCalled(); - editButton = await findByTestId('edit-button'); + editButton = await findByTestId('subsection-edit-button'); fireEvent.click(editButton); - editField = await findByTestId('edit field'); + editField = await findByTestId('subsection-edit-field'); fireEvent.change(editField, { target: { value: 'some random value' } }); fireEvent.keyDown(editField, { key: 'Enter', keyCode: 13 }); expect(onEditSubectionSubmit).toHaveBeenCalled();