diff --git a/editor.planx.uk/src/components/Feedback/FeedbackForm.tsx b/editor.planx.uk/src/components/Feedback/FeedbackForm.tsx index b500d143ed..55a5e85d1d 100644 --- a/editor.planx.uk/src/components/Feedback/FeedbackForm.tsx +++ b/editor.planx.uk/src/components/Feedback/FeedbackForm.tsx @@ -35,6 +35,7 @@ function FormInputs({ inputs }: { inputs: FeedbackFormInput[] }): FCReturn { name={input.name} value={values?.[input.name]} onChange={handleChange} + data-testid={`${input.name}Textarea`} /> ) : ( @@ -43,9 +44,11 @@ function FormInputs({ inputs }: { inputs: FeedbackFormInput[] }): FCReturn { required multiline bordered + aria-label={"Leave your feedback"} aria-describedby={input.ariaDescribedBy} value={values?.[input.name]} onChange={handleChange} + data-testid={`${input.name}Textarea`} /> )} diff --git a/editor.planx.uk/src/components/Feedback/MoreInfoFeedback.test.tsx b/editor.planx.uk/src/components/Feedback/MoreInfoFeedback.test.tsx new file mode 100644 index 0000000000..60232e83d5 --- /dev/null +++ b/editor.planx.uk/src/components/Feedback/MoreInfoFeedback.test.tsx @@ -0,0 +1,155 @@ +import "@testing-library/jest-dom/extend-expect"; + +import { fireEvent, waitFor } from "@testing-library/react"; +// eslint-disable-next-line no-restricted-imports +import userEvent from "@testing-library/user-event"; +import { + getInternalFeedbackMetadata, + insertFeedbackMutation, +} from "lib/feedback"; +import React from "react"; +import { axe, setup } from "testUtils"; + +import MoreInfoFeedbackComponent from "./MoreInfoFeedback"; + +jest.mock("lib/feedback", () => { + return { + getInternalFeedbackMetadata: jest.fn(), + insertFeedbackMutation: jest.fn(), + }; +}); + +const scrollIntoView = jest.fn(); +window.Element.prototype.scrollIntoView = scrollIntoView; + +describe("MoreInfoFeedbackComponent presentation and functionality", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + // Initial load + test('Initial loads renders "Yes" and "No" buttons initially', () => { + const { getByText } = setup(); + expect(getByText("Yes")).toBeInTheDocument(); + expect(getByText("No")).toBeInTheDocument(); + }); + + test("Does not scroll into view on initial render", () => { + setup(); + expect(scrollIntoView).not.toBeCalled(); + }); + + // Sentiment selection + test("Clicking Yes input form scrolls into view", async () => { + const { getByText } = setup(); + fireEvent.click(getByText("Yes")); + expect(scrollIntoView).toBeCalled(); + await waitFor(() => { + expect( + getByText("Please help us to improve this service by sharing feedback"), + ).toBeInTheDocument(); + }); + }); + + test("Clicking No input form scrolls into view", async () => { + const { getByText } = setup(); + fireEvent.click(getByText("No")); + expect(scrollIntoView).toBeCalled(); + await waitFor(() => { + expect( + getByText("Please help us to improve this service by sharing feedback"), + ).toBeInTheDocument(); + }); + }); + + // Form submission + test("Submitting feedback changes view to thank you message", async () => { + const { getByText, getByTestId } = setup(); + + fireEvent.click(getByText("Yes")); + await waitFor(() => { + expect(getByTestId("userCommentTextarea")).toBeInTheDocument(); + }); + + await userEvent.type( + getByTestId("userCommentTextarea"), + "Great help, thanks!", + ); + + fireEvent.click(getByText("Send feedback")); + await waitFor(() => { + expect(getInternalFeedbackMetadata).toBeCalled(); + expect(insertFeedbackMutation).toBeCalled(); + }); + + await waitFor(() => { + expect(getByText("Thank you for your feedback.")).toBeInTheDocument(); + }); + }); + + /* + We use the `required` property to validate that a user can't submit an empty + comment. + + It doesn't seem to be possible to test that the Browser stops form + submit in the Jest environment. + + Checking for `required` property currently but we could add explicit + validation. + */ + test("Feedback form requires a comment before submitting", async () => { + const { getByTestId, getByText } = setup(); + + fireEvent.click(getByText("Yes")); + await waitFor(() => { + expect(getByTestId("userCommentTextarea")).toBeInTheDocument(); + }); + + expect(getByTestId("userCommentTextarea")).toHaveAttribute("required"); + }); +}); + +describe("MoreInfoFeedbackComponent accessibility", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test("Initial load should have no accessibility violations", async () => { + const { container } = setup(); + const results = await axe(container); + expect(results).toHaveNoViolations(); + }); + + test("Form view should have no accessability violations", async () => { + const { container, getByText } = setup(); + fireEvent.click(getByText("Yes")); + + const results = await axe(container); + expect(results).toHaveNoViolations(); + }); + + test("Thank you view should have no accessibility violations", async () => { + const { container, getByText, getByTestId } = setup( + , + ); + + fireEvent.click(getByText("Yes")); + await waitFor(() => { + expect(getByTestId("userCommentTextarea")).toBeInTheDocument(); + }); + + await userEvent.type( + getByTestId("userCommentTextarea"), + "Great help, thanks!", + ); + + fireEvent.click(getByText("Send feedback")); + + await waitFor(() => { + expect(getByText("Thank you for your feedback.")).toBeInTheDocument(); + }); + + const results = await axe(container); + expect(results).toHaveNoViolations(); + }); +}); diff --git a/editor.planx.uk/src/components/Feedback/MoreInfoFeedback.tsx b/editor.planx.uk/src/components/Feedback/MoreInfoFeedback.tsx index d69500a462..804d9333ea 100644 --- a/editor.planx.uk/src/components/Feedback/MoreInfoFeedback.tsx +++ b/editor.planx.uk/src/components/Feedback/MoreInfoFeedback.tsx @@ -107,7 +107,12 @@ const MoreInfoFeedbackComponent: React.FC = () => { return ( - + Please help us to improve this service by sharing feedback