diff --git a/editor.planx.uk/src/@planx/components/DrawBoundary/Public/Public.test.tsx b/editor.planx.uk/src/@planx/components/DrawBoundary/Public/Public.test.tsx index b3824e8ad7..abae3f1d6e 100644 --- a/editor.planx.uk/src/@planx/components/DrawBoundary/Public/Public.test.tsx +++ b/editor.planx.uk/src/@planx/components/DrawBoundary/Public/Public.test.tsx @@ -1,13 +1,29 @@ +import { Breadcrumbs } from "@opensystemslab/planx-core/types"; +import { PASSPORT_REQUESTED_FILES_KEY } from "@planx/components/FileUploadAndLabel/model"; import { screen } from "@testing-library/react"; +import axios from "axios"; +import { useStore, vanillaStore } from "pages/FlowEditor/lib/store"; import React from "react"; +import { act } from "react-dom/test-utils"; import { axe, setup } from "testUtils"; +import { + DrawBoundaryUserAction, + PASSPORT_COMPONENT_ACTION_KEY, + PASSPORT_UPLOAD_KEY, +} from "../model"; import DrawBoundary from "./"; +jest.mock("axios"); +const mockedAxios = axios as jest.Mocked; +global.URL.createObjectURL = jest.fn(); + +const { getState, setState } = vanillaStore; + test("recovers previously submitted files when clicking the back button", async () => { const handleSubmit = jest.fn(); const previouslySubmittedData = { - "locationPlan": [ + locationPlan: [ { file: { path: "placeholder.png", @@ -147,3 +163,212 @@ test("hides the upload option and allows user to continue without drawing if edi await user.click(screen.getByTestId("continue-button")); expect(handleSubmit).toHaveBeenCalledTimes(1); }); + +test("captures output data in the correct format when uploading a file", async () => { + // Setup file mock + const mockFileName = "test.png"; + const mockFileURL = + "https://api.editor.planx.dev/file/private/gws7l5d1/test.png"; + + const file = new File(["test"], mockFileName, { type: "image/png" }); + + const mockedPost = mockedAxios.post.mockResolvedValueOnce({ + data: { + fileType: "image/png", + fileUrl: mockFileURL, + }, + }); + + const handleSubmit = jest.fn(); + + const { user } = setup( + , + ); + + // Toggle to file upload mode + await user.click(screen.getByTestId("upload-file-button")); + + // Upload file + const input = screen.getByTestId("upload-input"); + await user.upload(input, file); + expect(mockedPost).toHaveBeenCalled(); + + await user.click(screen.getByTestId("continue-button")); + const submitted = handleSubmit.mock.calls[0][0]; + + // DrawBoundary passport variable set + expect(submitted.data).toHaveProperty(PASSPORT_UPLOAD_KEY); + expect(submitted.data.locationPlan).toHaveLength(1); + expect(submitted.data.locationPlan[0].url).toEqual(mockFileURL); + expect(submitted.data.locationPlan[0].file.path).toEqual(mockFileName); + + // DrawBoundary action captured + expect(submitted.data[PASSPORT_COMPONENT_ACTION_KEY]).toEqual( + DrawBoundaryUserAction.Upload, + ); + + // File added to requested files + expect(submitted.data).toHaveProperty(PASSPORT_REQUESTED_FILES_KEY); + expect(submitted.data[PASSPORT_REQUESTED_FILES_KEY]).toMatchObject( + expect.objectContaining({ + required: [PASSPORT_UPLOAD_KEY], + recommended: [], + optional: [], + }), + ); +}); + +test("appends to existing '_requestedFiles' value", async () => { + // Setup file mock + const mockFileName = "test.png"; + const mockFileURL = + "https://api.editor.planx.dev/file/private/gws7l5d1/test.png"; + + const file = new File(["test"], mockFileName, { type: "image/png" }); + + mockedAxios.post.mockResolvedValueOnce({ + data: { + fileType: "image/png", + fileUrl: mockFileURL, + }, + }); + + const handleSubmit = jest.fn(); + + // Mimic having passed file upload / file upload and label component + const breadcrumbs: Breadcrumbs = { + previousFileUploadComponent: { + auto: false, + data: { + floorPlan: [ + { + url: "http://test.com/file1.jpg", + filename: "file1.jpg", + }, + ], + utilityBill: [ + { + url: "http://test.com/file2.jpg", + filename: "file2.jpg", + }, + ], + "elevations.existing": [ + { + url: "http://test.com/file3.jpg", + filename: "file3.jpg", + }, + ], + [PASSPORT_REQUESTED_FILES_KEY]: { + required: ["floorPlan", "utilityBill"], + recommended: ["elevations.existing"], + optional: [], + }, + }, + }, + }; + + const flow = { + _root: { + edges: ["previousFileUploadComponent", "DrawBoundary"], + }, + previousFileUploadComponent: { + type: 145, + data: { + title: "Upload and label", + fileTypes: [ + { + name: "Floor Plan", + fn: "floorPlan", + rule: { + condition: "AlwaysRequired", + }, + }, + { + name: "Utility Bill", + fn: "utilityBill", + rule: { + condition: "AlwaysRequired", + }, + }, + { + name: "Existing elevations", + fn: "elevations.existing", + rule: { + condition: "AlwaysRecommended", + }, + }, + ], + hideDropZone: false, + }, + }, + DrawBoundary: { + type: 10, + data: { + howMeasured: "", + policyRef: "", + info: "", + title: "Confirm your location plan", + description: "", + titleForUploading: "Upload a location plan", + descriptionForUploading: "", + hideFileUpload: false, + dataFieldBoundary: "property.boundary.site", + dataFieldArea: "property.boundary.area", + }, + }, + }; + + act(() => setState({ flow, breadcrumbs })); + + const { user } = setup( + , + ); + + // Check current passport setup + const passport = getState().computePassport(); + const existingRequestedFiles = passport.data?.[PASSPORT_REQUESTED_FILES_KEY]; + expect(existingRequestedFiles).toBeDefined(); + expect(existingRequestedFiles).toMatchObject({ + required: ["floorPlan", "utilityBill"], + recommended: ["elevations.existing"], + optional: [], + }); + + // Toggle to file upload mode + await user.click(screen.getByTestId("upload-file-button")); + + // Upload file and continue + const input = screen.getByTestId("upload-input"); + await user.upload(input, file); + await user.click(screen.getByTestId("continue-button")); + + const { required, recommended, optional } = + handleSubmit.mock.calls[0][0].data[PASSPORT_REQUESTED_FILES_KEY]; + + // Current file key has been added to required + expect(required).toContain(PASSPORT_UPLOAD_KEY); + + // Previous file keys have not been overwritten + expect(required).toContain("floorPlan"); + expect(required).toContain("utilityBill"); + + // Recommended and optional keys untouched + expect(recommended).toEqual(["elevations.existing"]); + expect(optional).toHaveLength(0); +}); diff --git a/editor.planx.uk/src/@planx/components/DrawBoundary/Public/index.tsx b/editor.planx.uk/src/@planx/components/DrawBoundary/Public/index.tsx index a373b6cd93..001e964c62 100644 --- a/editor.planx.uk/src/@planx/components/DrawBoundary/Public/index.tsx +++ b/editor.planx.uk/src/@planx/components/DrawBoundary/Public/index.tsx @@ -3,6 +3,7 @@ import Link from "@mui/material/Link"; import Typography from "@mui/material/Typography"; import { visuallyHidden } from "@mui/utils"; import { FileUploadSlot } from "@planx/components/FileUpload/Public"; +import { PASSPORT_REQUESTED_FILES_KEY } from "@planx/components/FileUploadAndLabel/model"; import Card from "@planx/components/shared/Preview/Card"; import { MapContainer, @@ -134,6 +135,17 @@ export default function Component(props: Props) { newPassportData[PASSPORT_UPLOAD_KEY] = slots; newPassportData[PASSPORT_COMPONENT_ACTION_KEY] = DrawBoundaryUserAction.Upload; + + // Track as requested file + const { required, recommended, optional } = passport.data?.[ + PASSPORT_REQUESTED_FILES_KEY + ] || { required: [], recommended: [], optional: [] }; + + newPassportData[PASSPORT_REQUESTED_FILES_KEY] = { + required: [...required, PASSPORT_UPLOAD_KEY], + recommended, + optional, + }; } props.handleSubmit?.({ data: { ...newPassportData } }); diff --git a/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Public.test.tsx b/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Public.test.tsx index 28f12f703f..2d4a65557b 100644 --- a/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Public.test.tsx +++ b/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Public.test.tsx @@ -8,13 +8,13 @@ import { import { UserEvent } from "@testing-library/user-event/dist/types/setup/setup"; import axios from "axios"; import { vanillaStore } from "pages/FlowEditor/lib/store"; -import { FullStore, useStore } from "pages/FlowEditor/lib/store"; +import { FullStore } from "pages/FlowEditor/lib/store"; import React from "react"; import { axe, setup } from "testUtils"; import { Breadcrumbs } from "types"; import { mockFileTypes, mockFileTypesUniqueKeys } from "./mocks"; -import { Condition, PASSPORT_REQUESTED_FILES_KEY } from "./model"; +import { PASSPORT_REQUESTED_FILES_KEY } from "./model"; import FileUploadAndLabelComponent from "./Public"; const { getState, setState } = vanillaStore; @@ -717,9 +717,6 @@ describe("Submitting data", () => { act(() => setState({ flow, breadcrumbs })); - const passport = useStore.getState().computePassport(); - console.log({ passport }); - const handleSubmit = jest.fn(); const { user } = setup( = ({ }; const DesignSettings: React.FC = () => { - const isUsingFeatureFlag = hasFeatureFlag("SHOW_TEAM_SETTINGS"); const [formikConfig, setFormikConfig] = useState< FormikConfig | undefined >(undefined); @@ -145,34 +142,19 @@ const DesignSettings: React.FC = () => { How your service appears to public users - {!isUsingFeatureFlag ? ( - - {" "} - - ) : ( + {formikConfig && ( <> - {formikConfig && ( - <> - - - - - - )} - - - Theme updated successfully - - + + + + )} + + + Theme updated successfully + + ); };