From 5d350734c1f9884a96dcd21069396d595863dae1 Mon Sep 17 00:00:00 2001 From: Jessica McInchak Date: Mon, 8 Jul 2024 15:50:15 +0200 Subject: [PATCH] refactor files and add proj type validation --- .../modules/flows/validate/controller.ts | 2 +- .../modules/flows/validate/service.ts | 278 ------------------ .../flows/validate/service/fileTypes.ts | 76 +++++ .../modules/flows/validate/service/index.ts | 74 +++++ .../flows/validate/service/inviteToPay.ts | 88 ++++++ .../flows/validate/service/projectTypes.ts | 74 +++++ .../flows/validate/service/sections.ts | 61 ++++ .../modules/flows/validate/validate.test.ts | 54 ++++ api.planx.uk/tests/mocks/inviteToPayData.ts | 2 +- 9 files changed, 429 insertions(+), 280 deletions(-) delete mode 100644 api.planx.uk/modules/flows/validate/service.ts create mode 100644 api.planx.uk/modules/flows/validate/service/fileTypes.ts create mode 100644 api.planx.uk/modules/flows/validate/service/index.ts create mode 100644 api.planx.uk/modules/flows/validate/service/inviteToPay.ts create mode 100644 api.planx.uk/modules/flows/validate/service/projectTypes.ts create mode 100644 api.planx.uk/modules/flows/validate/service/sections.ts diff --git a/api.planx.uk/modules/flows/validate/controller.ts b/api.planx.uk/modules/flows/validate/controller.ts index 3e32ca98b8..a974a7229f 100644 --- a/api.planx.uk/modules/flows/validate/controller.ts +++ b/api.planx.uk/modules/flows/validate/controller.ts @@ -1,7 +1,7 @@ import { Node } from "@opensystemslab/planx-core/types"; import { ValidatedRequestHandler } from "../../../shared/middleware/validate"; import { z } from "zod"; -import { validateAndDiffFlow } from "./service"; +import { validateAndDiffFlow } from "./service/index"; import { ServerError } from "../../../errors"; interface ValidateAndDiffResponse { diff --git a/api.planx.uk/modules/flows/validate/service.ts b/api.planx.uk/modules/flows/validate/service.ts deleted file mode 100644 index 802f8f5c9e..0000000000 --- a/api.planx.uk/modules/flows/validate/service.ts +++ /dev/null @@ -1,278 +0,0 @@ -import { getValidSchemaValues } from "@opensystemslab/planx-core"; -import { - ComponentType, - Edges, - FlowGraph, - Node, -} from "@opensystemslab/planx-core/types"; -import * as jsondiffpatch from "jsondiffpatch"; -import intersection from "lodash/intersection"; -import countBy from "lodash/countBy"; - -import { dataMerged, getMostRecentPublishedFlow } from "../../../helpers"; -import { - hasComponentType, - isComponentType, - numberOfComponentType, -} from "./helpers"; - -type AlteredNode = { - id: string; - type?: ComponentType; - edges?: Edges; - data?: Node["data"]; -}; - -type ValidationResponse = { - title: string; - status: "Pass" | "Fail" | "Warn" | "Not applicable"; - message: string; -}; - -interface ValidateAndDiffResponse { - alteredNodes: AlteredNode[] | null; - message: string; - validationChecks?: ValidationResponse[]; -} - -const validateAndDiffFlow = async ( - flowId: string, -): Promise => { - const flattenedFlow = await dataMerged(flowId); - const mostRecent = await getMostRecentPublishedFlow(flowId); - - const delta = jsondiffpatch.diff(mostRecent, flattenedFlow); - if (!delta) - return { - alteredNodes: null, - message: "No new changes to publish", - }; - - // Only get alteredNodes and do validationChecks if there have been changes - const alteredNodes = Object.keys(delta).map((key) => ({ - id: key, - ...flattenedFlow[key], - })); - - const validationChecks = []; - const sections = validateSections(flattenedFlow); - const inviteToPay = validateInviteToPay(flattenedFlow); - const fileTypes = validateFileTypes(flattenedFlow); - validationChecks.push(sections, inviteToPay, fileTypes); - - // Arrange list of validation checks in order of status: Fail, Warn, Pass, Not applicable - const failingChecks = validationChecks.filter((v) => v.status == "Fail"); - const warningChecks = validationChecks.filter((v) => v.status === "Warn"); - const passingChecks = validationChecks.filter((v) => v.status === "Pass"); - const notApplicableChecks = validationChecks.filter( - (v) => v.status === "Not applicable", - ); - const sortedValidationChecks = failingChecks - .concat(warningChecks) - .concat(passingChecks) - .concat(notApplicableChecks); - - return { - alteredNodes, - message: "Changes queued to publish", - validationChecks: sortedValidationChecks, - }; -}; - -const validateSections = (flowGraph: FlowGraph): ValidationResponse => { - if (getSectionNodeIds(flowGraph)?.length > 0) { - if (!sectionIsInFirstPosition(flowGraph)) { - return { - title: "Sections", - status: "Fail", - message: "When using Sections, your flow must start with a Section", - }; - } - - if (!allSectionsOnRoot(flowGraph)) { - return { - title: "Sections", - status: "Fail", - message: - "Found Sections in one or more External Portals, but Sections are only allowed in main flow", - }; - } - - return { - title: "Sections", - status: "Pass", - message: "Your flow has valid Sections", - }; - } - - return { - title: "Sections", - status: "Not applicable", - message: "Your flow is not using Sections", - }; -}; - -const getSectionNodeIds = (flowGraph: FlowGraph): string[] => { - const sectionNodes = Object.entries(flowGraph).filter((entry) => - isComponentType(entry, ComponentType.Section), - ); - return sectionNodes.map(([nodeId, _nodeData]) => nodeId); -}; - -const sectionIsInFirstPosition = (flowGraph: FlowGraph): boolean => { - const firstNodeId = flowGraph["_root"].edges[0]; - return flowGraph[firstNodeId].type === ComponentType.Section; -}; - -const allSectionsOnRoot = (flowData: FlowGraph): boolean => { - const sectionTypeNodeIds = getSectionNodeIds(flowData); - const intersectingNodeIds = intersection( - flowData["_root"].edges, - sectionTypeNodeIds, - ); - return intersectingNodeIds.length === sectionTypeNodeIds.length; -}; - -const validateInviteToPay = (flowGraph: FlowGraph): ValidationResponse => { - if (inviteToPayEnabled(flowGraph)) { - if (numberOfComponentType(flowGraph, ComponentType.Pay) > 1) { - return { - title: "Invite to Pay", - status: "Fail", - message: - "When using Invite to Pay, your flow must have exactly ONE Pay", - }; - } - - if (!hasComponentType(flowGraph, ComponentType.Send)) { - return { - title: "Invite to Pay", - status: "Fail", - message: "When using Invite to Pay, your flow must have a Send", - }; - } - - if (numberOfComponentType(flowGraph, ComponentType.Send) > 1) { - return { - title: "Invite to Pay", - status: "Fail", - message: - "When using Invite to Pay, your flow must have exactly ONE Send. It can select many destinations", - }; - } - - if (!hasComponentType(flowGraph, ComponentType.FindProperty)) { - return { - title: "Invite to Pay", - status: "Fail", - message: "When using Invite to Pay, your flow must have a FindProperty", - }; - } - - if ( - !hasComponentType( - flowGraph, - ComponentType.Checklist, - "proposal.projectType", - ) - ) { - return { - title: "Invite to Pay", - status: "Fail", - message: - "When using Invite to Pay, your flow must have a Checklist that sets `proposal.projectType`", - }; - } - - return { - title: "Invite to Pay", - status: "Pass", - message: "Your flow has valid Invite to Pay", - }; - } - - return { - title: "Invite to Pay", - status: "Not applicable", - message: "Your flow is not using Invite to Pay", - }; -}; - -const inviteToPayEnabled = (flowGraph: FlowGraph): boolean => { - const payNodes = Object.entries(flowGraph).filter((entry) => - isComponentType(entry, ComponentType.Pay), - ); - const payNodeStatuses = payNodes.map( - ([_nodeId, node]) => node?.data?.allowInviteToPay, - ); - return ( - payNodeStatuses.length > 0 && - payNodeStatuses.every((status) => status === true) - ); -}; - -const validateFileTypes = (flowGraph: FlowGraph): ValidationResponse => { - // Get all passport variables set by FileUpload and/or FileUploadAndLabel - const allFileFns = [ - ...getFileUploadNodeFns(flowGraph), - ...getFileUploadAndLabelNodeFns(flowGraph), - ]; - if (allFileFns.length < 1) { - return { - title: "File types", - status: "Not applicable", - message: "Your flow is not using FileUpload or UploadAndLabel", - }; - } - - // Get all file types supported by current release of ODP Schema & compare - const validFileTypes = getValidSchemaValues("FileType"); - const invalidFileFns: string[] = []; - allFileFns.forEach((fn) => { - if (!validFileTypes?.includes(fn)) { - invalidFileFns.push(fn); - } - }); - if (invalidFileFns.length > 0) { - // Get unique fns with count of occurances - const countInvalidFileFns = countBy(invalidFileFns); - const summarisedInvalidFileFns: string[] = []; - Object.entries(countInvalidFileFns).map(([k, v]: [string, number]) => { - summarisedInvalidFileFns.push(`${k} (${v})`); - }); - return { - title: "File types", - status: "Warn", - message: `Your FileUpload or UploadAndLabel are setting data fields that are not supported by the current release of the ODP Schema: ${summarisedInvalidFileFns.join(", ")}`, - }; - } - - return { - title: "File types", - status: "Pass", - message: - "Files collected via FileUpload or UploadAndLabel are all supported by the ODP Schema", - }; -}; - -const getFileUploadNodeFns = (flowGraph: FlowGraph): string[] => { - const fileUploadNodes = Object.entries(flowGraph).filter((entry) => - isComponentType(entry, ComponentType.FileUpload), - ); - return fileUploadNodes.map(([_nodeId, node]) => node.data?.fn as string); -}; - -const getFileUploadAndLabelNodeFns = (flowGraph: FlowGraph): string[] => { - // Exclude Upload & Label nodes used in "info-only" mode with a hidden dropzone - const uploadAndLabelNodes = Object.entries(flowGraph).filter( - (entry) => - isComponentType(entry, ComponentType.FileUploadAndLabel) && - entry[1].data?.hideDropZone !== true, - ); - const uploadAndLabelFileTypes = uploadAndLabelNodes - .map(([_nodeId, node]: [string, Node]) => node.data?.fileTypes) - .flat(); - return uploadAndLabelFileTypes?.map((file: any) => file?.fn as string); -}; - -export { validateAndDiffFlow }; diff --git a/api.planx.uk/modules/flows/validate/service/fileTypes.ts b/api.planx.uk/modules/flows/validate/service/fileTypes.ts new file mode 100644 index 0000000000..cee8cfc32f --- /dev/null +++ b/api.planx.uk/modules/flows/validate/service/fileTypes.ts @@ -0,0 +1,76 @@ +import { getValidSchemaValues } from "@opensystemslab/planx-core"; +import { + ComponentType, + FlowGraph, + Node, +} from "@opensystemslab/planx-core/types"; +import countBy from "lodash/countBy"; + +import { isComponentType } from "../helpers"; +import { FlowValidationResponse } from "./index"; + +const validateFileTypes = (flowGraph: FlowGraph): FlowValidationResponse => { + // Get all passport variables set by FileUpload and/or FileUploadAndLabel + const allFileFns = [ + ...getFileUploadNodeFns(flowGraph), + ...getFileUploadAndLabelNodeFns(flowGraph), + ]; + if (allFileFns.length < 1) { + return { + title: "File types", + status: "Not applicable", + message: "Your flow is not using FileUpload or UploadAndLabel", + }; + } + + // Get all file types supported by current release of ODP Schema & compare + const validFileTypes = getValidSchemaValues("FileType"); + const invalidFileFns: string[] = []; + allFileFns.forEach((fn) => { + if (!validFileTypes?.includes(fn)) { + invalidFileFns.push(fn); + } + }); + if (invalidFileFns.length > 0) { + // Get unique fns with count of occurances + const countInvalidFileFns = countBy(invalidFileFns); + const summarisedInvalidFileFns: string[] = []; + Object.entries(countInvalidFileFns).map(([k, v]: [string, number]) => { + summarisedInvalidFileFns.push(`${k} (${v})`); + }); + return { + title: "File types", + status: "Warn", + message: `Your FileUpload or UploadAndLabel are setting data fields that are not supported by the current release of the ODP Schema: ${summarisedInvalidFileFns.join(", ")}`, + }; + } + + return { + title: "File types", + status: "Pass", + message: + "Files collected via FileUpload or UploadAndLabel are all supported by the ODP Schema", + }; +}; + +const getFileUploadNodeFns = (flowGraph: FlowGraph): string[] => { + const fileUploadNodes = Object.entries(flowGraph).filter((entry) => + isComponentType(entry, ComponentType.FileUpload), + ); + return fileUploadNodes.map(([_nodeId, node]) => node.data?.fn as string); +}; + +const getFileUploadAndLabelNodeFns = (flowGraph: FlowGraph): string[] => { + // Exclude Upload & Label nodes used in "info-only" mode with a hidden dropzone + const uploadAndLabelNodes = Object.entries(flowGraph).filter( + (entry) => + isComponentType(entry, ComponentType.FileUploadAndLabel) && + entry[1].data?.hideDropZone !== true, + ); + const uploadAndLabelFileTypes = uploadAndLabelNodes + .map(([_nodeId, node]: [string, Node]) => node.data?.fileTypes) + .flat(); + return uploadAndLabelFileTypes?.map((file: any) => file?.fn as string); +}; + +export { validateFileTypes }; diff --git a/api.planx.uk/modules/flows/validate/service/index.ts b/api.planx.uk/modules/flows/validate/service/index.ts new file mode 100644 index 0000000000..1702b3603a --- /dev/null +++ b/api.planx.uk/modules/flows/validate/service/index.ts @@ -0,0 +1,74 @@ +import { ComponentType, Edges, Node } from "@opensystemslab/planx-core/types"; +import * as jsondiffpatch from "jsondiffpatch"; + +import { dataMerged, getMostRecentPublishedFlow } from "../../../../helpers"; +import { validateFileTypes } from "./fileTypes"; +import { validateInviteToPay } from "./inviteToPay"; +import { validateSections } from "./sections"; +import { validateProjectTypes } from "./projectTypes"; + +type AlteredNode = { + id: string; + type?: ComponentType; + edges?: Edges; + data?: Node["data"]; +}; + +export type FlowValidationResponse = { + title: string; + status: "Pass" | "Fail" | "Warn" | "Not applicable"; + message: string; +}; + +interface FlowValidateAndDiffResponse { + alteredNodes: AlteredNode[] | null; + message: string; + validationChecks?: FlowValidationResponse[]; +} + +const validateAndDiffFlow = async ( + flowId: string, +): Promise => { + const flattenedFlow = await dataMerged(flowId); + const mostRecent = await getMostRecentPublishedFlow(flowId); + + const delta = jsondiffpatch.diff(mostRecent, flattenedFlow); + if (!delta) + return { + alteredNodes: null, + message: "No new changes to publish", + }; + + // Only get alteredNodes and do validationChecks if there have been changes + const alteredNodes = Object.keys(delta).map((key) => ({ + id: key, + ...flattenedFlow[key], + })); + + const validationChecks = []; + const sections = validateSections(flattenedFlow); + const inviteToPay = validateInviteToPay(flattenedFlow); + const fileTypes = validateFileTypes(flattenedFlow); + const projectTypes = validateProjectTypes(flattenedFlow); + validationChecks.push(sections, inviteToPay, fileTypes, projectTypes); + + // Arrange list of validation checks in order of status: Fail, Warn, Pass, Not applicable + const failingChecks = validationChecks.filter((v) => v.status == "Fail"); + const warningChecks = validationChecks.filter((v) => v.status === "Warn"); + const passingChecks = validationChecks.filter((v) => v.status === "Pass"); + const notApplicableChecks = validationChecks.filter( + (v) => v.status === "Not applicable", + ); + const sortedValidationChecks = failingChecks + .concat(warningChecks) + .concat(passingChecks) + .concat(notApplicableChecks); + + return { + alteredNodes, + message: "Changes queued to publish", + validationChecks: sortedValidationChecks, + }; +}; + +export { validateAndDiffFlow }; diff --git a/api.planx.uk/modules/flows/validate/service/inviteToPay.ts b/api.planx.uk/modules/flows/validate/service/inviteToPay.ts new file mode 100644 index 0000000000..61a57fe020 --- /dev/null +++ b/api.planx.uk/modules/flows/validate/service/inviteToPay.ts @@ -0,0 +1,88 @@ +import { ComponentType, FlowGraph } from "@opensystemslab/planx-core/types"; + +import { + hasComponentType, + isComponentType, + numberOfComponentType, +} from "../helpers"; +import { FlowValidationResponse } from "./index"; + +const validateInviteToPay = (flowGraph: FlowGraph): FlowValidationResponse => { + if (inviteToPayEnabled(flowGraph)) { + if (numberOfComponentType(flowGraph, ComponentType.Pay) > 1) { + return { + title: "Invite to Pay", + status: "Fail", + message: + "When using Invite to Pay, your flow must have exactly ONE Pay", + }; + } + + if (!hasComponentType(flowGraph, ComponentType.Send)) { + return { + title: "Invite to Pay", + status: "Fail", + message: "When using Invite to Pay, your flow must have a Send", + }; + } + + if (numberOfComponentType(flowGraph, ComponentType.Send) > 1) { + return { + title: "Invite to Pay", + status: "Fail", + message: + "When using Invite to Pay, your flow must have exactly ONE Send. It can select many destinations", + }; + } + + if (!hasComponentType(flowGraph, ComponentType.FindProperty)) { + return { + title: "Invite to Pay", + status: "Fail", + message: "When using Invite to Pay, your flow must have a FindProperty", + }; + } + + if ( + !hasComponentType( + flowGraph, + ComponentType.Checklist, + "proposal.projectType", + ) + ) { + return { + title: "Invite to Pay", + status: "Fail", + message: + "When using Invite to Pay, your flow must have a Checklist that sets `proposal.projectType`", + }; + } + + return { + title: "Invite to Pay", + status: "Pass", + message: "Your flow has valid Invite to Pay", + }; + } + + return { + title: "Invite to Pay", + status: "Not applicable", + message: "Your flow is not using Invite to Pay", + }; +}; + +const inviteToPayEnabled = (flowGraph: FlowGraph): boolean => { + const payNodes = Object.entries(flowGraph).filter((entry) => + isComponentType(entry, ComponentType.Pay), + ); + const payNodeStatuses = payNodes.map( + ([_nodeId, node]) => node?.data?.allowInviteToPay, + ); + return ( + payNodeStatuses.length > 0 && + payNodeStatuses.every((status) => status === true) + ); +}; + +export { validateInviteToPay }; diff --git a/api.planx.uk/modules/flows/validate/service/projectTypes.ts b/api.planx.uk/modules/flows/validate/service/projectTypes.ts new file mode 100644 index 0000000000..e111b1e7c1 --- /dev/null +++ b/api.planx.uk/modules/flows/validate/service/projectTypes.ts @@ -0,0 +1,74 @@ +import { getValidSchemaValues } from "@opensystemslab/planx-core"; +import { + ComponentType, + Edges, + FlowGraph, + Node, +} from "@opensystemslab/planx-core/types"; +import countBy from "lodash/countBy"; + +import { isComponentType } from "../helpers"; +import { FlowValidationResponse } from "./index"; + +const validateProjectTypes = (flowGraph: FlowGraph): FlowValidationResponse => { + // Get all passport values set by Answers of Questions or Checklists that set fn "proposal.projectType" + const projectTypeVals = getProjectTypeVals(flowGraph); + if (projectTypeVals.length < 1) { + return { + title: "Project types", + status: "Not applicable", + message: + 'Your flow is not using Checklists which set "proposal.projectType"', + }; + } + + // Get all project types supported by current release of ODP Schema & compare + const validProjectTypes = getValidSchemaValues("ProjectType"); + const invalidProjectVals: string[] = []; + projectTypeVals.forEach((val) => { + if (!validProjectTypes?.includes(val)) { + invalidProjectVals.push(val); + } + }); + if (invalidProjectVals.length > 0) { + // Get unique fns with count of occurances + const countInvalidProjectVals = countBy(invalidProjectVals); + const summarisedInvalidProjectVals: string[] = []; + Object.entries(countInvalidProjectVals).map(([k, v]: [string, number]) => { + summarisedInvalidProjectVals.push(`${k} (${v})`); + }); + return { + title: "Project types", + status: "Warn", + message: `Your Checklists setting "proposal.projectType" include options that are not supported by the current release of the ODP Schema: ${summarisedInvalidProjectVals.join(", ")}`, + }; + } + + return { + title: "Project types", + status: "Pass", + message: + "Project types set via Checklists are all supported by the ODP Schema", + }; +}; + +const getProjectTypeVals = (flowGraph: FlowGraph): string[] => { + const projectTypeChecklistNodes = Object.entries(flowGraph).filter( + (entry) => + isComponentType(entry, ComponentType.Checklist) && + entry[1].data?.fn === "proposal.projectType", + ); + + const answerVals: string[] = []; + projectTypeChecklistNodes.map(([_nodeId, node]) => + node.edges?.map((edgeId) => { + if (typeof flowGraph[edgeId]?.data?.val === "string") { + answerVals.push(flowGraph[edgeId]?.data?.val); + } + }), + ); + + return answerVals; +}; + +export { validateProjectTypes }; diff --git a/api.planx.uk/modules/flows/validate/service/sections.ts b/api.planx.uk/modules/flows/validate/service/sections.ts new file mode 100644 index 0000000000..a955ae11ae --- /dev/null +++ b/api.planx.uk/modules/flows/validate/service/sections.ts @@ -0,0 +1,61 @@ +import { ComponentType, FlowGraph } from "@opensystemslab/planx-core/types"; +import intersection from "lodash/intersection"; + +import { isComponentType } from "../helpers"; +import { FlowValidationResponse } from "./index"; + +const validateSections = (flowGraph: FlowGraph): FlowValidationResponse => { + if (getSectionNodeIds(flowGraph)?.length > 0) { + if (!sectionIsInFirstPosition(flowGraph)) { + return { + title: "Sections", + status: "Fail", + message: "When using Sections, your flow must start with a Section", + }; + } + + if (!allSectionsOnRoot(flowGraph)) { + return { + title: "Sections", + status: "Fail", + message: + "Found Sections in one or more External Portals, but Sections are only allowed in main flow", + }; + } + + return { + title: "Sections", + status: "Pass", + message: "Your flow has valid Sections", + }; + } + + return { + title: "Sections", + status: "Not applicable", + message: "Your flow is not using Sections", + }; +}; + +const getSectionNodeIds = (flowGraph: FlowGraph): string[] => { + const sectionNodes = Object.entries(flowGraph).filter((entry) => + isComponentType(entry, ComponentType.Section), + ); + return sectionNodes.map(([nodeId, _nodeData]) => nodeId); +}; + +const sectionIsInFirstPosition = (flowGraph: FlowGraph): boolean => { + const firstNodeId = flowGraph["_root"].edges[0]; + return flowGraph[firstNodeId].type === ComponentType.Section; +}; + +const allSectionsOnRoot = (flowData: FlowGraph): boolean => { + const sectionTypeNodeIds = getSectionNodeIds(flowData); + const intersectingNodeIds = intersection( + flowData["_root"].edges, + sectionTypeNodeIds, + ); + return intersectingNodeIds.length === sectionTypeNodeIds.length; +}; + +export { validateSections }; diff --git a/api.planx.uk/modules/flows/validate/validate.test.ts b/api.planx.uk/modules/flows/validate/validate.test.ts index dce4af082e..1c2dfd09e4 100644 --- a/api.planx.uk/modules/flows/validate/validate.test.ts +++ b/api.planx.uk/modules/flows/validate/validate.test.ts @@ -125,6 +125,12 @@ describe("sections validation on diff", () => { status: "Not applicable", message: "Your flow is not using FileUpload or UploadAndLabel", }, + { + title: "Project types", + status: "Not applicable", + message: + 'Your flow is not using Checklists which set "proposal.projectType"', + }, ]); }); }); @@ -178,6 +184,12 @@ describe("sections validation on diff", () => { status: "Not applicable", message: "Your flow is not using FileUpload or UploadAndLabel", }, + { + title: "Project types", + status: "Not applicable", + message: + 'Your flow is not using Checklists which set "proposal.projectType"', + }, ]); }); }); @@ -212,6 +224,12 @@ describe("invite to pay validation on diff", () => { status: "Fail", message: "When using Invite to Pay, your flow must have a Send", }, + { + title: "Project types", + status: "Pass", + message: + "Project types set via Checklists are all supported by the ODP Schema", + }, { title: "Sections", status: "Not applicable", @@ -266,6 +284,12 @@ describe("invite to pay validation on diff", () => { message: "When using Invite to Pay, your flow must have exactly ONE Send. It can select many destinations", }, + { + title: "Project types", + status: "Pass", + message: + "Project types set via Checklists are all supported by the ODP Schema", + }, { title: "Sections", status: "Not applicable", @@ -316,6 +340,12 @@ describe("invite to pay validation on diff", () => { message: "When using Invite to Pay, your flow must have a FindProperty", }, + { + title: "Project types", + status: "Pass", + message: + "Project types set via Checklists are all supported by the ODP Schema", + }, { title: "Sections", status: "Not applicable", @@ -368,6 +398,12 @@ describe("invite to pay validation on diff", () => { message: "When using Invite to Pay, your flow must have exactly ONE Pay", }, + { + title: "Project types", + status: "Pass", + message: + "Project types set via Checklists are all supported by the ODP Schema", + }, { title: "Sections", status: "Not applicable", @@ -432,6 +468,12 @@ describe("invite to pay validation on diff", () => { status: "Not applicable", message: "Your flow is not using FileUpload or UploadAndLabel", }, + { + title: "Project types", + status: "Not applicable", + message: + 'Your flow is not using Checklists which set "proposal.projectType"', + }, ]); }); }); @@ -513,6 +555,12 @@ describe("ODP Schema file type validation on diff", () => { status: "Not applicable", message: "Your flow is not using Invite to Pay", }, + { + title: "Project types", + status: "Not applicable", + message: + 'Your flow is not using Checklists which set "proposal.projectType"', + }, ]); }); }); @@ -585,6 +633,12 @@ describe("ODP Schema file type validation on diff", () => { status: "Not applicable", message: "Your flow is not using Invite to Pay", }, + { + title: "Project types", + status: "Not applicable", + message: + 'Your flow is not using Checklists which set "proposal.projectType"', + }, ]); }); }); diff --git a/api.planx.uk/tests/mocks/inviteToPayData.ts b/api.planx.uk/tests/mocks/inviteToPayData.ts index 9b00a4c9e1..4f4d4dfcaf 100644 --- a/api.planx.uk/tests/mocks/inviteToPayData.ts +++ b/api.planx.uk/tests/mocks/inviteToPayData.ts @@ -186,7 +186,7 @@ export const flowWithInviteToPay: Flow["data"] = { ChecklistOptionTwo: { data: { text: "Build new", - val: "build", + val: "new", }, type: 200, },