Skip to content

Commit

Permalink
feat: Add _requestedFiles key to Passport (#2763)
Browse files Browse the repository at this point in the history
  • Loading branch information
DafyddLlyr authored Feb 8, 2024
1 parent b291c83 commit 4547e86
Show file tree
Hide file tree
Showing 8 changed files with 292 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function Component(props: any) {
</InputRow>
<InputRow>
<Input
// required
required
format="data"
name="fn"
value={formik.values.fn}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Story = StoryObj<typeof meta>;

export const Basic = {
args: {
fn: "roofPlan",
title: "Upload roof plan",
description:
"The plan should show the roof of the building as it looks today.",
Expand Down
31 changes: 8 additions & 23 deletions editor.planx.uk/src/@planx/components/FileUpload/Public.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,31 @@ import { uniqueId } from "lodash";
import React from "react";
import { axe, setup } from "testUtils";

import { PASSPORT_REQUESTED_FILES_KEY } from "../FileUploadAndLabel/model";
import FileUpload from "./Public";

test("renders correctly and blocks submit if there are no files added", async () => {
const handleSubmit = jest.fn();

setup(<FileUpload handleSubmit={handleSubmit} />);
setup(<FileUpload fn="someKey" handleSubmit={handleSubmit} />);

expect(screen.getByRole("button", { name: "Continue" })).toBeDisabled();

expect(handleSubmit).toHaveBeenCalledTimes(0);
});

test("recovers previously submitted files when clicking the back button", async () => {
const handleSubmit = jest.fn();
const componentId = uniqueId();
const uploadedFile = {
data: {
[componentId]: [dummyFile],
},
};

const { user } = setup(
<FileUpload
id={componentId}
handleSubmit={handleSubmit}
previouslySubmittedData={uploadedFile}
/>,
);

await user.click(screen.getByTestId("continue-button"));

expect(handleSubmit).toHaveBeenCalledWith(uploadedFile);
});

test("recovers previously submitted files when clicking the back button even if a data field is set", async () => {
const handleSubmit = jest.fn();
const componentId = uniqueId();
const dataField = "data-field";
const uploadedFile = {
data: {
[dataField]: [dummyFile],
[PASSPORT_REQUESTED_FILES_KEY]: {
required: [dataField],
recommended: [],
optional: [],
},
},
};

Expand Down Expand Up @@ -85,6 +69,7 @@ it("should not have any accessibility violations", async () => {

const { container } = setup(
<FileUpload
fn="someKey"
id={componentId}
handleSubmit={handleSubmit}
description="description"
Expand Down
67 changes: 48 additions & 19 deletions editor.planx.uk/src/@planx/components/FileUpload/Public.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { MoreInformation } from "@planx/components/shared";
import Card from "@planx/components/shared/Preview/Card";
import QuestionHeader from "@planx/components/shared/Preview/QuestionHeader";
import { Store } from "pages/FlowEditor/lib/store";
import { Store, useStore } from "pages/FlowEditor/lib/store";
import type { handleSubmit } from "pages/Preview/Node";
import React, { useEffect, useRef, useState } from "react";
import { FileWithPath } from "react-dropzone";
import ErrorWrapper from "ui/shared/ErrorWrapper";
import { array } from "yup";

import { PASSPORT_REQUESTED_FILES_KEY } from "../FileUploadAndLabel/model";
import { PrivateFileUpload } from "../shared/PrivateFileUpload/PrivateFileUpload";
import { getPreviouslySubmittedData, makeData } from "../shared/utils";

interface Props extends MoreInformation {
id?: string;
title?: string;
fn?: string;
fn: string;
description?: string;
handleSubmit: handleSubmit;
previouslySubmittedData?: Store.userData;
Expand Down Expand Up @@ -50,27 +51,55 @@ const FileUpload: React.FC<Props> = (props) => {
const [slots, setSlots] = useState<FileUploadSlot[]>(recoveredSlots ?? []);
const [validationError, setValidationError] = useState<string>();

const uploadedFiles = (slots: FileUploadSlot[]) =>
makeData(
props,
slots.map((slot) => ({
url: slot.url,
filename: slot.file.path,
cachedSlot: {
...slot,
file: {
path: slot.file.path,
type: slot.file.type,
size: slot.file.size,
},
},
})),
);

const updatedRequestedFiles = () => {
// const { required, recommended, optional } = useStore
// .getState()
// .requestedFiles();

const { required, recommended, optional } = useStore
.getState()
.computePassport().data?.[PASSPORT_REQUESTED_FILES_KEY] || {
required: [],
recommended: [],
optional: [],
};

return {
[PASSPORT_REQUESTED_FILES_KEY]: {
required: [...required, props.fn],
recommended,
optional,
},
};
};

const handleSubmit = () => {
slotsSchema
.validate(slots)
.then(() => {
props.handleSubmit(
makeData(
props,
slots.map((slot) => ({
url: slot.url,
filename: slot.file.path,
cachedSlot: {
...slot,
file: {
path: slot.file.path,
type: slot.file.type,
size: slot.file.size,
},
},
})),
),
);
props.handleSubmit({
data: {
...uploadedFiles(slots).data,
...updatedRequestedFiles(),
},
});
})
.catch((err) => {
setValidationError(err.message);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import { fireEvent, screen, waitFor, within } from "@testing-library/react";
import {
act,
fireEvent,
screen,
waitFor,
within,
} from "@testing-library/react";
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 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 FileUploadAndLabelComponent from "./Public";

const { getState, setState } = vanillaStore;
let initialState: FullStore;

jest.mock("axios");
const mockedAxios = axios as jest.Mocked<typeof axios>;

Expand Down Expand Up @@ -595,3 +609,165 @@ describe("Error handling", () => {
expect(handleSubmit).not.toHaveBeenCalled();
});
});

describe("Submitting data", () => {
beforeAll(() => (initialState = getState()));

afterEach(() => waitFor(() => setState(initialState)));

it("records the user uploaded files", async () => {
const handleSubmit = jest.fn();
const { user } = setup(
<FileUploadAndLabelComponent
title="Test title"
handleSubmit={handleSubmit}
fileTypes={mockFileTypesUniqueKeys}
/>,
);

await uploadAndTagSingleFile(user);

await user.click(screen.getByText("Continue"));
expect(handleSubmit).toHaveBeenCalledTimes(1);

const submitted = handleSubmit.mock.calls[0][0];
const uploadedFile = submitted?.data?.roofPlan;

expect(uploadedFile).toBeDefined();
expect(uploadedFile).toHaveLength(1);
expect(uploadedFile[0]).toEqual(
expect.objectContaining({
filename: "test1.png",
url: "https://api.editor.planx.dev/file/private/gws7l5d1/test1.jpg",
}),
);
});

it("records the full file type list presented to the user", async () => {
const handleSubmit = jest.fn();
const { user } = setup(
<FileUploadAndLabelComponent
title="Test title"
handleSubmit={handleSubmit}
fileTypes={mockFileTypesUniqueKeys}
/>,
);

await uploadAndTagSingleFile(user);
await user.click(screen.getByText("Continue"));

const submitted = handleSubmit.mock.calls[0][0];
const requestedFiles = submitted?.data?._requestedFiles;

expect(requestedFiles).toBeDefined();
expect(requestedFiles.required).toContain("roofPlan");
expect(requestedFiles.recommended).toContain("heritage");
expect(requestedFiles.optional).toContain("utilityBill");
});

it("appends to the list of existing requested files", async () => {
// Mimic having passed file upload / file upload and label component
const breadcrumbs: Breadcrumbs = {
previousFileUploadComponent: {
auto: false,
data: {
anotherFileType: [
{
url: "http://test.com/file.jpg",
filename: "file.jpg",
},
],
[PASSPORT_REQUESTED_FILES_KEY]: {
required: ["anotherFileType"],
recommended: [],
optional: [],
},
},
},
};

const flow = {
_root: {
edges: ["previousFileUploadComponent", "currentComponent"],
},
previousFileUploadComponent: {
data: {
fn: "anotherFileType",
color: "#EFEFEF",
},
type: 140,
},
currentComponent: {
type: 145,
data: {
title: "Upload and label",
fileTypes: [
{
name: "Floor Plan",
fn: "floorPlan",
rule: {
condition: "AlwaysRequired",
},
},
],
hideDropZone: false,
},
},
};

act(() => setState({ flow, breadcrumbs }));

const passport = useStore.getState().computePassport();
console.log({ passport });

const handleSubmit = jest.fn();
const { user } = setup(
<FileUploadAndLabelComponent
title="Test title"
handleSubmit={handleSubmit}
fileTypes={mockFileTypesUniqueKeys}
/>,
);

await uploadAndTagSingleFile(user);
await user.click(screen.getByText("Continue"));

const submitted = handleSubmit.mock.calls[0][0];
const requestedFiles = submitted?.data?._requestedFiles;

// Existing files from previous components
expect(requestedFiles.required).toContain("anotherFileType");

// Requested files from this component
expect(requestedFiles.required).toContain("roofPlan");
expect(requestedFiles.recommended).toContain("heritage");
expect(requestedFiles.optional).toContain("utilityBill");
});
});

/**
* Test helper which steps through the process of uploading and labelling a file
* Does not contain assertations - relies on these happening in other, more granular, passing tests
*/
const uploadAndTagSingleFile = async (user: UserEvent) => {
mockedAxios.post.mockResolvedValueOnce({
data: {
fileType: "image/png",
fileUrl: "https://api.editor.planx.dev/file/private/gws7l5d1/test1.jpg",
},
});

const file1 = new File(["test1"], "test1.png", { type: "image/png" });
const input = screen.getByTestId("upload-input");
await user.upload(input, [file1]);

const fileTaggingModal = await within(document.body).findByTestId(
"file-tagging-dialog",
);

const selects = await within(document.body).findAllByTestId("select");
fireEvent.change(selects[0], { target: { value: "Roof plan" } });

const submitModalButton = await within(fileTaggingModal).findByText("Done");
user.click(submitModalButton);
};
Loading

0 comments on commit 4547e86

Please sign in to comment.