diff --git a/api.planx.uk/package.json b/api.planx.uk/package.json index cf59ad7d0f..33042b0c16 100644 --- a/api.planx.uk/package.json +++ b/api.planx.uk/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@airbrake/node": "^2.1.8", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#bea2192", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#d47553e", "@types/isomorphic-fetch": "^0.0.36", "adm-zip": "^0.5.10", "aws-sdk": "^2.1467.0", diff --git a/api.planx.uk/pnpm-lock.yaml b/api.planx.uk/pnpm-lock.yaml index 71459ce07a..4322bf9d5f 100644 --- a/api.planx.uk/pnpm-lock.yaml +++ b/api.planx.uk/pnpm-lock.yaml @@ -14,8 +14,8 @@ dependencies: specifier: ^2.1.8 version: 2.1.8 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#bea2192 - version: github.com/theopensystemslab/planx-core/bea2192 + specifier: git+https://github.com/theopensystemslab/planx-core#d47553e + version: github.com/theopensystemslab/planx-core/d47553e '@types/isomorphic-fetch': specifier: ^0.0.36 version: 0.0.36 @@ -6137,8 +6137,8 @@ packages: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} dev: false - github.com/theopensystemslab/planx-core/bea2192: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/bea2192} + github.com/theopensystemslab/planx-core/d47553e: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/d47553e} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true diff --git a/e2e/tests/api-driven/package.json b/e2e/tests/api-driven/package.json index 73118cf07b..d5e27fd240 100644 --- a/e2e/tests/api-driven/package.json +++ b/e2e/tests/api-driven/package.json @@ -7,7 +7,7 @@ "packageManager": "pnpm@8.6.6", "dependencies": { "@cucumber/cucumber": "^9.3.0", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#bea2192", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#d47553e", "axios": "^1.7.4", "dotenv": "^16.3.1", "dotenv-expand": "^10.0.0", diff --git a/e2e/tests/api-driven/pnpm-lock.yaml b/e2e/tests/api-driven/pnpm-lock.yaml index 2a64be1400..31f0a8f970 100644 --- a/e2e/tests/api-driven/pnpm-lock.yaml +++ b/e2e/tests/api-driven/pnpm-lock.yaml @@ -9,8 +9,8 @@ dependencies: specifier: ^9.3.0 version: 9.3.0 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#bea2192 - version: github.com/theopensystemslab/planx-core/bea2192 + specifier: git+https://github.com/theopensystemslab/planx-core#d47553e + version: github.com/theopensystemslab/planx-core/d47553e axios: specifier: ^1.7.4 version: 1.7.4 @@ -2932,8 +2932,8 @@ packages: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} dev: false - github.com/theopensystemslab/planx-core/bea2192: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/bea2192} + github.com/theopensystemslab/planx-core/d47553e: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/d47553e} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true diff --git a/e2e/tests/ui-driven/package.json b/e2e/tests/ui-driven/package.json index a337e13060..ed53dde709 100644 --- a/e2e/tests/ui-driven/package.json +++ b/e2e/tests/ui-driven/package.json @@ -8,7 +8,7 @@ "postinstall": "./install-dependencies.sh" }, "dependencies": { - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#bea2192", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#d47553e", "axios": "^1.7.4", "dotenv": "^16.3.1", "eslint": "^8.56.0", diff --git a/e2e/tests/ui-driven/pnpm-lock.yaml b/e2e/tests/ui-driven/pnpm-lock.yaml index 17fbb73582..aef5a7a65b 100644 --- a/e2e/tests/ui-driven/pnpm-lock.yaml +++ b/e2e/tests/ui-driven/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: dependencies: '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#bea2192 - version: github.com/theopensystemslab/planx-core/bea2192 + specifier: git+https://github.com/theopensystemslab/planx-core#d47553e + version: github.com/theopensystemslab/planx-core/d47553e axios: specifier: ^1.7.4 version: 1.7.4 @@ -2674,8 +2674,8 @@ packages: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} dev: false - github.com/theopensystemslab/planx-core/bea2192: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/bea2192} + github.com/theopensystemslab/planx-core/d47553e: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/d47553e} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true diff --git a/editor.planx.uk/package.json b/editor.planx.uk/package.json index 622801495b..2cd00f4b21 100644 --- a/editor.planx.uk/package.json +++ b/editor.planx.uk/package.json @@ -15,7 +15,7 @@ "@mui/material": "^5.15.10", "@mui/utils": "^5.15.11", "@opensystemslab/map": "1.0.0-alpha.3", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#e6040d9", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#d47553e", "@tiptap/core": "^2.4.0", "@tiptap/extension-bold": "^2.0.3", "@tiptap/extension-bubble-menu": "^2.1.13", diff --git a/editor.planx.uk/pnpm-lock.yaml b/editor.planx.uk/pnpm-lock.yaml index 4d4a8b1c43..a0ee2beed6 100644 --- a/editor.planx.uk/pnpm-lock.yaml +++ b/editor.planx.uk/pnpm-lock.yaml @@ -47,8 +47,8 @@ dependencies: specifier: 1.0.0-alpha.3 version: 1.0.0-alpha.3 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#e6040d9 - version: github.com/theopensystemslab/planx-core/e6040d9(@types/react@18.2.45) + specifier: git+https://github.com/theopensystemslab/planx-core#d47553e + version: github.com/theopensystemslab/planx-core/d47553e(@types/react@18.2.45) '@tiptap/core': specifier: ^2.4.0 version: 2.4.0(@tiptap/pm@2.0.3) @@ -21360,9 +21360,9 @@ packages: use-sync-external-store: 1.2.0(react@18.2.0) dev: false - github.com/theopensystemslab/planx-core/e6040d9(@types/react@18.2.45): - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/e6040d9} - id: github.com/theopensystemslab/planx-core/e6040d9 + github.com/theopensystemslab/planx-core/d47553e(@types/react@18.2.45): + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/d47553e} + id: github.com/theopensystemslab/planx-core/d47553e name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true diff --git a/editor.planx.uk/src/@planx/components/Page/Public.test.tsx b/editor.planx.uk/src/@planx/components/Page/Public.test.tsx index 27a23409ba..fca2c24dd8 100644 --- a/editor.planx.uk/src/@planx/components/Page/Public.test.tsx +++ b/editor.planx.uk/src/@planx/components/Page/Public.test.tsx @@ -1 +1,191 @@ -test.todo("coming soon!"); +import React from "react"; +import { setup } from "testUtils"; +import { vi } from "vitest"; +import { axe } from "vitest-axe"; + +import PageComponent from "./Public"; +import { ProposedAdvertisements } from "./schema/AdvertConsent"; + +it("renders correctly", async () => { + const { getByRole } = setup( + , + ); + + const title = getByRole("heading", { + level: 1, + name: "Tell us about your proposed advertisements", + }); + expect(title).toBeVisible(); +}); + +it("has no accessibility violations", async () => { + const { container } = setup( + , + ); + + const results = await axe(container); + expect(results).toHaveNoViolations(); +}); + +it("displays the supplied Page schema", () => { + const { getByLabelText } = setup( + , + ); + + // Each PageSchema field is displayed + ProposedAdvertisements.fields.forEach(({ data }) => { + const input = getByLabelText(data.title); + expect(input).toBeVisible(); + }); +}); + +it("handles PageSchema errors", async () => { + const handleSubmit = vi.fn(); + const { + user, + queryAllByTestId, + getAllByTestId, + getByTestId, + queryByText, + getAllByText, + } = setup( + , + ); + + // No error messages present initially + let errorMessages = queryAllByTestId(/error-message-input-number/); + expect(queryByText("Enter your answer before continuing")).toBeNull(); + errorMessages.forEach((message) => expect(message).toBeEmptyDOMElement()); + + // User hits "Continue" without populating fields + const continueButton = getByTestId("continue-button"); + await user.click(continueButton); + + // Error messages present, per field + errorMessages = getAllByTestId(/error-message-input-number/); + errorMessages.forEach((message) => expect(message).not.toBeEmptyDOMElement()); + expect(getAllByText("Enter your answer before continuing")).toHaveLength( + ProposedAdvertisements.fields.length, + ); + + // Due to errors, component has not submitted data + expect(handleSubmit).not.toHaveBeenCalled(); +}); + +it("submits a valid payload", async () => { + const handleSubmit = vi.fn(); + const { user, getAllByLabelText, getByTestId } = setup( + , + ); + + const inputs = getAllByLabelText(/How many/); + expect(inputs).toHaveLength(ProposedAdvertisements.fields.length); + + // Populate each field + for (const input of inputs) { + await user.type(input, "1"); + } + + const continueButton = getByTestId("continue-button"); + await user.click(continueButton); + + expect(handleSubmit).toHaveBeenCalled(); + + const expectedFlattenedData = expect.objectContaining({ + data: expect.objectContaining({ + "testFn.fascia": 1, + "testFn.hoarding": 1, + "testFn.other": 1, + "testFn.projecting": 1, + }), + }); + expect(handleSubmit).toHaveBeenCalledWith(expectedFlattenedData); + + const expectedDefaultData = expect.objectContaining({ + data: expect.objectContaining({ + testFn: [ + { + fascia: 1, + hoarding: 1, + other: 1, + projecting: 1, + }, + ], + }), + }); + expect(handleSubmit).toHaveBeenCalledWith(expectedDefaultData); +}); + +it("handles back navigation", () => { + const previousData = { + data: { + testFn: [ + { + fascia: 1, + hoarding: 2, + other: 3, + projecting: 4, + }, + ], + "testFn.fascia": 1, + "testFn.hoarding": 2, + "testFn.other": 3, + "testFn.projecting": 4, + }, + }; + + const handleSubmit = vi.fn(); + const { getByLabelText } = setup( + , + ); + + // Each input is correctly pre-populated + const fasciaInput = getByLabelText(/fascia/); + expect(fasciaInput).toHaveValue(1); + + const hoardingInput = getByLabelText(/hoarding/); + expect(hoardingInput).toHaveValue(2); + + const otherInput = getByLabelText(/other/); + expect(otherInput).toHaveValue(3); + + const projectingInput = getByLabelText(/projecting/); + expect(projectingInput).toHaveValue(4); +}); diff --git a/editor.planx.uk/src/@planx/components/Page/Public.tsx b/editor.planx.uk/src/@planx/components/Page/Public.tsx index 81c18cc423..d96e3dfcf1 100644 --- a/editor.planx.uk/src/@planx/components/Page/Public.tsx +++ b/editor.planx.uk/src/@planx/components/Page/Public.tsx @@ -1,10 +1,12 @@ import { PublicProps } from "@planx/components/ui"; -import { useFormik } from "formik"; +import { FormikConfig, useFormik } from "formik"; import React from "react"; +import { flatten } from "../List/utils"; import Card from "../shared/Preview/Card"; import CardHeader from "../shared/Preview/CardHeader"; import { useSchema } from "../shared/Schema/hook"; +import { SchemaUserData } from "../shared/Schema/model"; import { SchemaFields } from "../shared/Schema/SchemaFields"; import { getPreviouslySubmittedData } from "../shared/utils"; import { Page } from "./model"; @@ -17,9 +19,28 @@ function PageComponent(props: Props) { previousValues: getPreviouslySubmittedData(props), }); + const onSubmit: FormikConfig["onSubmit"] = (data) => { + if (!props.handleSubmit) return; + + // Default data - used for "back", to maintain a copy of the data as generated by the component + const defaultPassportData = { [props.fn]: data.schemaData }; + + // Flattened data - used for access via automation, SetValue etc + const flattenedData = flatten({ [props.fn]: data.schemaData[0] }); + + const userData = { + data: { + ...defaultPassportData, + ...flattenedData, + }, + }; + + props.handleSubmit(userData); + }; + const formik = useFormik({ ...formikConfig, - onSubmit: (data) => console.log({ data }), + onSubmit, }); return (