Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Production deploy #4037

Merged
merged 5 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions e2e/tests/ui-driven/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"packageManager": "[email protected]",
"devDependencies": {
"@playwright/test": "^1.49.0",
"@types/geojson": "^7946.0.14",
"@types/node": "18.16.1",
"eslint-plugin-playwright": "^0.20.0"
}
Expand Down
7 changes: 7 additions & 0 deletions e2e/tests/ui-driven/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 62 additions & 4 deletions e2e/tests/ui-driven/src/create-flow-with-geospatial.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import {
} from "./helpers/context";
import { getTeamPage } from "./helpers/getPage";
import { createAuthenticatedSession } from "./helpers/globalHelpers";
import { answerFindProperty, clickContinue } from "./helpers/userActions";
import {
answerFindProperty,
answerQuestion,
clickContinue,
} from "./helpers/userActions";
import { PlaywrightEditor } from "./pages/Editor";
import {
navigateToService,
Expand All @@ -15,6 +19,11 @@ import {
} from "./helpers/navigateAndPublish";
import { TestContext } from "./helpers/types";
import { serviceProps } from "./helpers/serviceData";
import { checkGeoJsonContent } from "./helpers/geospatialChecks";
import {
mockMapGeoJson,
mockPropertyTypeOptions,
} from "./mocks/geospatialMocks";

test.describe("Flow creation, publish and preview", () => {
let context: TestContext = {
Expand Down Expand Up @@ -48,10 +57,21 @@ test.describe("Flow creation, publish and preview", () => {

await editor.createFindProperty();
await expect(editor.nodeList).toContainText(["Find property"]);
// Find property will automate past this question at first
await editor.createQuestionWithDataFieldOptions(
"What type of property is it?",
mockPropertyTypeOptions,
"property.type",
);
await expect(editor.nodeList).toContainText([
"What type of property is it?",
]);
// but property info "change" button will navigate back to it
await editor.createPropertyInformation();
await expect(editor.nodeList).toContainText(["About the property"]);
await editor.createInternalPortal();
await editor.populateInternalPortal();
await page.getByRole("link", { name: "start" }).click(); // return to main flow
await editor.createFilter();
await editor.createUploadAndLabel();
// TODO: editor.createPropertyInfo()
await editor.createDrawBoundary();
Expand All @@ -61,7 +81,6 @@ test.describe("Flow creation, publish and preview", () => {
await expect(editor.nodeList).toContainText([
"Find property",
"an internal portalEdit Portal",
"Filter - Planning permissionImmuneMissing informationPermission neededPrior approvalNoticePermitted developmentNot developmentNo flag result",
"Upload and label",
"Confirm your location plan",
"Planning constraints",
Expand Down Expand Up @@ -99,18 +118,57 @@ test.describe("Flow creation, publish and preview", () => {
await page.goto(
`/${context.team.slug}/${serviceProps.slug}/published?analytics=false`,
);

await expect(
page.locator("h1", { hasText: "Find the property" }),
).toBeVisible();
await answerFindProperty(page);
await clickContinue({ page });

await expect(
page.getByRole("heading", { name: "About the property" }),
).toBeVisible();

// Check map component has geoJson content
await checkGeoJsonContent(page, mockMapGeoJson);

// Check property info is being shown
await expect(page.getByText("Test Street, Testville")).toBeVisible();
await expect(page.getByText("Residential - Semi Detached")).toBeVisible();
const changeButton = page.getByRole("button", {
name: "Change your Property type",
});

await changeButton.click();

// ensure residential is selected on back nav to test previouslySubmittedData is working
await expect(
page.getByRole("radio", { name: "Residential", checked: true }),
).toBeVisible();

await answerQuestion({
page: page,
title: "What type of property is it?",
answer: "Commercial",
});

// navigate back to Property Info page
await clickContinue({ page });
await expect(
page.getByRole("heading", { name: "About the property" }),
).toBeVisible();

// Ensure we've successfully changed property type
await expect(page.getByText("Residential - Semi Detached")).toBeHidden();
await expect(page.getByText("Commercial")).toBeVisible();

await clickContinue({ page });

await expect(
page.locator("h1", { hasText: "A notice inside a portal!" }),
).toBeVisible();
await clickContinue({ page });

// TODO: answer filter?
// TODO: answer uploadAndLabel
// TODO: answerPropertyInfo, answerDrawBoundary, answerPlanningConstraints
});
Expand Down
43 changes: 43 additions & 0 deletions e2e/tests/ui-driven/src/helpers/addComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ComponentType } from "@opensystemslab/planx-core/types";
import { expect, Locator, Page } from "@playwright/test";
import { contextDefaults } from "./context";
import { externalPortalServiceProps } from "./serviceData";
import { OptionWithDataValues } from "./types";

const createBaseComponent = async (
page: Page,
Expand Down Expand Up @@ -81,6 +82,9 @@ const createBaseComponent = async (
break;
case ComponentType.FindProperty:
break;
case ComponentType.PropertyInformation:
await page.getByLabel("Show users a 'change' link to").click();
break;
case ComponentType.PlanningConstraints:
break;
case ComponentType.DrawBoundary:
Expand Down Expand Up @@ -153,6 +157,21 @@ export const createQuestionWithOptions = async (
);
};

export const createQuestionWithDataFieldOptions = async (
page: Page,
locatingNode: Locator,
questionText: string,
options: OptionWithDataValues[],
dataField: string,
) => {
await locatingNode.click();
await page.getByRole("dialog").waitFor();
await page.getByPlaceholder("Text").fill(questionText);
await page.getByPlaceholder("Data Field").fill(dataField);
await createComponentOptionsWithDataValues(page, options);
await page.locator('button[form="modal"][type="submit"]').click();
};

export const createNotice = async (
page: Page,
locatingNode: Locator,
Expand Down Expand Up @@ -275,6 +294,17 @@ export const createFindProperty = async (page: Page, locatingNode: Locator) => {
await createBaseComponent(page, locatingNode, ComponentType.FindProperty);
};

export const createPropertyInformation = async (
page: Page,
locatingNode: Locator,
) => {
await createBaseComponent(
page,
locatingNode,
ComponentType.PropertyInformation,
);
};

export const createPlanningConstraints = async (
page: Page,
locatingNode: Locator,
Expand Down Expand Up @@ -331,6 +361,19 @@ async function createComponentOptions(
}
}

async function createComponentOptionsWithDataValues(
page: Page,
options: OptionWithDataValues[],
) {
let index = 0;
for (const option of options) {
await page.locator("button").filter({ hasText: "add new" }).click();
await page.getByPlaceholder("Option").nth(index).fill(option.optionText);
await page.getByPlaceholder("Data Value").nth(index).fill(option.dataValue);
index++;
}
}

export const createList = async (
page: Page,
locatingNode: Locator,
Expand Down
12 changes: 12 additions & 0 deletions e2e/tests/ui-driven/src/helpers/geospatialChecks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { expect, Page } from "@playwright/test";
import { Feature } from "geojson";

export const checkGeoJsonContent = async (page: Page, geoJson: Feature) => {
// Wait for the map component to be present
const mapComponent = await page.waitForSelector("my-map");

// Get the geojsonData attribute
const geojsonData = await mapComponent.getAttribute("geojsondata");

expect(JSON.parse(geojsonData!)).toEqual(geoJson);
};
2 changes: 2 additions & 0 deletions e2e/tests/ui-driven/src/helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ export interface TestContext {
externalPortalFlow?: Flow;
sessionIds?: string[];
}

export type OptionWithDataValues = { optionText: string; dataValue: string };
39 changes: 39 additions & 0 deletions e2e/tests/ui-driven/src/mocks/geospatialMocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { OptionWithDataValues } from "../helpers/types";

export const mockPropertyTypeOptions: OptionWithDataValues[] = [
{ optionText: "Residential", dataValue: "residential" },
{ optionText: "Commercial", dataValue: "commercial" },
];

import { Feature } from "geojson";

export const mockMapGeoJson: Feature = {
geometry: {
type: "MultiPolygon",
coordinates: [
[
[
[-0.633498, 51.605485],
[-0.633455, 51.605606],
[-0.633788, 51.605643],
[-0.634429, 51.605799],
[-0.634429, 51.605767],
[-0.633498, 51.605485],
],
],
],
},
type: "Feature",
properties: {
"entry-date": "2024-05-06",
"start-date": "2010-05-12",
"end-date": "",
entity: 12000041468,
name: "",
dataset: "title-boundary",
typology: "geography",
reference: "45211072",
prefix: "title-boundary",
"organisation-entity": "13",
},
};
24 changes: 24 additions & 0 deletions e2e/tests/ui-driven/src/pages/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ import {
createNotice,
createNumberInput,
createPlanningConstraints,
createPropertyInformation,
createQuestionWithDataFieldOptions,
createQuestionWithOptions,
createResult,
createReview,
createTaskList,
createTextInput,
createUploadAndLabel,
} from "../helpers/addComponent";
import { OptionWithDataValues } from "../helpers/types";

export class PlaywrightEditor {
readonly page: Page;
Expand Down Expand Up @@ -81,6 +84,23 @@ export class PlaywrightEditor {
).toBeVisible();
}

async createQuestionWithDataFieldOptions(
title: string,
answers: OptionWithDataValues[],
dataField: string,
) {
await createQuestionWithDataFieldOptions(
this.page,
this.getNextNode(),
title,
answers,
dataField,
);
await expect(
this.page.locator("a").filter({ hasText: title }),
).toBeVisible();
}

async createNoticeOnEachBranch() {
// Add a notice to the "Yes" path
await createNotice(
Expand Down Expand Up @@ -168,6 +188,10 @@ export class PlaywrightEditor {
await createFindProperty(this.page, this.getNextNode());
}

async createPropertyInformation() {
await createPropertyInformation(this.page, this.getNextNode());
}

async createDrawBoundary() {
await createDrawBoundary(this.page, this.getNextNode());
}
Expand Down
4 changes: 2 additions & 2 deletions editor.planx.uk/src/@planx/components/NextSteps/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const TaskEditor: React.FC<ListManagerEditorProps<Step>> = (props) => {
<InputRow>
<Input
name="description"
value={props.value.description}
value={props.value.description || ""}
multiline
onChange={(ev: ChangeEvent<HTMLInputElement>) => {
props.onChange({
Expand All @@ -61,7 +61,7 @@ const TaskEditor: React.FC<ListManagerEditorProps<Step>> = (props) => {
<Input
name="url"
type="url"
value={props.value.url}
value={props.value.url || ""}
onChange={(ev: ChangeEvent<HTMLInputElement>) => {
props.onChange({
...props.value,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Badge from "@mui/material/Badge";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Dialog from "@mui/material/Dialog";
Expand Down Expand Up @@ -98,35 +97,21 @@ export const PublishFlowButton: React.FC<{ previewURL: string }> = ({
setLastPublishedTitle(formatLastPublishMessage(date, user));
}, [flowId]);

const _validateAndDiffRequest = useAsync(async () => {
const newChanges = await validateAndDiffFlow(flowId);
setAlteredNodes(
newChanges?.data.alteredNodes ? newChanges.data.alteredNodes : [],
);
}, [flowId]);

// useStore.getState().getTeam().slug undefined here, use window instead
const teamSlug = window.location.pathname.split("/")[1];

return (
<Box width="100%" mt={2}>
<Box display="flex" flexDirection="column" alignItems="flex-end">
<Badge
<Button
sx={{ width: "100%" }}
badgeContent={alteredNodes && alteredNodes.length}
max={999}
color="warning"
variant="contained"
color="primary"
disabled={!useStore.getState().canUserEditTeam(teamSlug)}
onClick={handleCheckForChangesToPublish}
>
<Button
sx={{ width: "100%" }}
variant="contained"
color="primary"
disabled={!useStore.getState().canUserEditTeam(teamSlug)}
onClick={handleCheckForChangesToPublish}
>
CHECK FOR CHANGES TO PUBLISH
</Button>
</Badge>
CHECK FOR CHANGES TO PUBLISH
</Button>
<Dialog
open={dialogOpen}
onClose={() => setDialogOpen(false)}
Expand Down
Loading
Loading