Skip to content

Commit

Permalink
feat: Map and Label Happy Path Tests (#3690)
Browse files Browse the repository at this point in the history
  • Loading branch information
RODO94 authored Sep 17, 2024
1 parent 3389060 commit f45fefd
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 82 deletions.
263 changes: 184 additions & 79 deletions editor.planx.uk/src/@planx/components/MapAndLabel/Public/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,18 @@ import { setup } from "testUtils";
import { vi } from "vitest";
import { axe } from "vitest-axe";

import { point1, point2 } from "../test/mocks/geojson";
import { point1, point2, point3 } from "../test/mocks/geojson";
import { props } from "../test/mocks/Trees";
import { addFeaturesToMap, addMultipleFeatures } from "../test/utils";
import {
addFeaturesToMap,
addMultipleFeatures,
checkErrorMessagesEmpty,
checkErrorMessagesPopulated,
clickContinue,
fillOutFirstHalfOfForm,
fillOutForm,
fillOutSecondHalfOfForm,
} from "../test/utils";

beforeAll(() => {
if (!window.customElements.get("my-map")) {
Expand Down Expand Up @@ -40,25 +49,23 @@ describe("Basic UI", () => {
it("removes the prompt once a feature is added", async () => {
const { queryByText, getByTestId } = setup(<MapAndLabel {...props} />);
const map = getByTestId("map-and-label-map");
expect(map).toBeInTheDocument();

addFeaturesToMap(map, [point1]);

await waitFor(() =>
expect(
queryByText("Plot a feature on the map to begin"),
).not.toBeInTheDocument(),
queryByText("Plot a feature on the map to begin")
).not.toBeInTheDocument()
);
});

it("renders the schema name as the tab title", async () => {
const { queryByText, getByRole, getByTestId } = setup(
<MapAndLabel {...props} />,
<MapAndLabel {...props} />
);
expect(queryByText(/Tree 1/)).not.toBeInTheDocument();

const map = getByTestId("map-and-label-map");
expect(map).toBeInTheDocument();

addFeaturesToMap(map, [point1]);

Expand All @@ -68,12 +75,11 @@ describe("Basic UI", () => {

it("should not have any accessibility violations", async () => {
const { queryByText, getByTestId, container } = setup(
<MapAndLabel {...props} />,
<MapAndLabel {...props} />
);
expect(queryByText(/Tree 1/)).not.toBeInTheDocument();

const map = getByTestId("map-and-label-map");
expect(map).toBeInTheDocument();

addFeaturesToMap(map, [point1]);

Expand All @@ -85,23 +91,26 @@ describe("Basic UI", () => {
// Schema and field validation is handled in both List and Schema folders - here we're only testing the MapAndLabel specific error handling
describe("validation and error handling", () => {
it("shows all fields are required", async () => {
const { getAllByTestId, getByTestId, getByRole, user } = setup(
<MapAndLabel {...props} />,
const { getByTestId, user, queryByRole, getAllByTestId } = setup(
<MapAndLabel {...props} />
);
const map = getByTestId("map-and-label-map");
expect(map).toBeInTheDocument();

addFeaturesToMap(map, [point1]);

expect(getByRole("tab", { name: /Tree 1/ })).toBeInTheDocument();
const tabOne = queryByRole("tab", { name: /Tree 1/ });
expect(tabOne).toBeInTheDocument();

const firstTabPanel = getByTestId("vertical-tabpanel-0");
const firstSpeciesInput = within(firstTabPanel).getByLabelText("Species");

const continueButton = getByRole("button", { name: /Continue/ });
expect(continueButton).toBeInTheDocument();
await user.click(continueButton);
// check input is empty
expect(firstSpeciesInput).toHaveDisplayValue("");

await clickContinue(user);

const errorMessages = getAllByTestId(/error-message-input/);

// Date field has been removed so only 4 inputs
expect(errorMessages).toHaveLength(4);

errorMessages.forEach((message) => {
Expand All @@ -111,119 +120,215 @@ describe("validation and error handling", () => {

// it shows all fields are required in a tab
it("should show all fields are required, for all feature tabs", async () => {
const { getByTestId, getByRole, user, debug } = setup(
<MapAndLabel {...props} />,
);
const map = getByTestId("map-and-label-map");
expect(map).toBeInTheDocument();
debug();
const { getByTestId, getByRole, user } = setup(<MapAndLabel {...props} />);

addMultipleFeatures([point1, point2]);

// vertical side tab query
const firstTab = getByRole("tab", { name: /Tree 1/ });
const secondTab = getByRole("tab", { name: /Tree 2/ });

// side tab validation
expect(firstTab).toBeInTheDocument();
expect(secondTab).toBeInTheDocument();

// form for each tab
const firstTabPanel = getByTestId("vertical-tabpanel-0");
const secondTabPanel = getByTestId("vertical-tabpanel-1");

// default is to start on seond tab panel since we add two points
// default is to start on second tab panel since we add two points
expect(firstTabPanel).not.toBeVisible();
expect(secondTabPanel).toBeVisible();

// Form is generate for secondTabPanel but not the first
expect(secondTabPanel.childElementCount).toBeGreaterThan(0);
expect(firstTabPanel.childElementCount).toBe(0);

const continueButton = getByRole("button", { name: /Continue/ });
await user.click(continueButton);
await clickContinue(user);

// error messages appear
const errorMessagesTabTwo =
within(secondTabPanel).getAllByTestId(/error-message-input/);
expect(errorMessagesTabTwo).toHaveLength(4);

// error messages are empty but visible before error state induced
// this ensures they contain the error message text
errorMessagesTabTwo.forEach((input) => {
expect(input).not.toBeEmptyDOMElement();
});
await checkErrorMessagesPopulated();

await user.click(firstTab);

expect(firstTabPanel).toBeVisible();

// error messages persist
const errorMessagesTabOne =
within(firstTabPanel).getAllByTestId(/error-message-input/);
expect(errorMessagesTabOne).toHaveLength(4);
await checkErrorMessagesPopulated();
});

// it shows all fields are required across different tabs
it("should show an error if the minimum number of items is not met", async () => {
const { getByTestId, getByRole, user } = setup(<MapAndLabel {...props} />);
const map = getByTestId("map-and-label-map");
expect(map).toBeInTheDocument();

const continueButton = getByRole("button", { name: /Continue/ });
const { getByTestId, user } = setup(<MapAndLabel {...props} />);

await user.click(continueButton);
await clickContinue(user);

const errorWrapper = getByTestId(/error-wrapper/);

const errorMessage = within(errorWrapper).getByText(/You must plot /);

expect(errorMessage).toBeInTheDocument();
expect(errorMessage).toBeVisible();
});
// ??
it("an error state is applied to a tabpanel button, when it's associated feature is invalid", async () => {
const { getByTestId, getByRole, user, getAllByTestId } = setup(
<MapAndLabel {...props} />,
const { getByTestId, user, queryByRole } = setup(
<MapAndLabel {...props} />
);
const map = getByTestId("map-and-label-map");
expect(map).toBeInTheDocument();

addFeaturesToMap(map, [point1]);

const tabOne = getByRole("tab", { name: /Tree 1/ });
const tabOne = queryByRole("tab", { name: /Tree 1/ });

expect(tabOne).toBeInTheDocument();

const continueButton = getByRole("button", { name: /Continue/ });
expect(continueButton).toBeInTheDocument();
await user.click(continueButton);
await clickContinue(user);

const errorMessages = getAllByTestId(/error-message-input/);

// check error messages are correct amount and contain info
expect(errorMessages).toHaveLength(4);

errorMessages.forEach((message) => {
expect(message).not.toBeEmptyDOMElement();
});
await checkErrorMessagesPopulated();

expect(tabOne).toHaveStyle("border-left: 5px solid #D4351C");
});
// shows the error state on a tab when it's invalid
});

it("does not trigger handleSubmit when errors exist", async () => {
const handleSubmit = vi.fn();
const { getByTestId, user } = setup(
<MapAndLabel {...props} handleSubmit={handleSubmit} />
);
const map = getByTestId("map-and-label-map");

addFeaturesToMap(map, [point1]);

await clickContinue(user);

await checkErrorMessagesPopulated();

expect(handleSubmit).not.toBeCalled();
});
test.todo("an error displays if the maximum number of items is exceeded");

describe("basic interactions - happy path", () => {
test.todo("adding an item to the map adds a feature tab");
// add feature, see a tab (one feature only)
test.todo("a user can input details on a single feature and submit");
// only one feature, fill out form, submit
test.todo("adding multiple features to the map adds multiple feature tabs");
// add more than one feature, see multiple tabs
test.todo("a user can input details on multiple features and submit");
// add details to more than one tab, submit
test.todo("a user can input details on feature tabs in any order");
// ??
it("adding an item to the map adds a feature tab", async () => {
const { getByTestId } = setup(<MapAndLabel {...props} />);
const map = getByTestId("map-and-label-map");

addFeaturesToMap(map, [point1]);

const firstTabPanel = getByTestId("vertical-tabpanel-0");

expect(firstTabPanel).toBeVisible();
});

it("a user can input details on a single feature and submit", async () => {
const { getByTestId, user } = setup(<MapAndLabel {...props} />);

const map = getByTestId("map-and-label-map");

addFeaturesToMap(map, [point1]);

const firstTabPanel = getByTestId("vertical-tabpanel-0");

expect(firstTabPanel).toBeVisible();

await fillOutForm(user);

await clickContinue(user);

await checkErrorMessagesEmpty();
});

it("adding multiple features to the map adds multiple feature tabs", async () => {
const { queryByRole } = setup(<MapAndLabel {...props} />);

addMultipleFeatures([point1, point2, point3]);

// vertical side tab query
const firstTab = queryByRole("tab", { name: /Tree 1/ });
const secondTab = queryByRole("tab", { name: /Tree 2/ });
const thirdTab = queryByRole("tab", { name: /Tree 3/ });
const fourthTab = queryByRole("tab", { name: /Tree 4/ });

// check the right amount are in the document
expect(firstTab).toBeInTheDocument();
expect(secondTab).toBeInTheDocument();
expect(thirdTab).toBeInTheDocument();
expect(fourthTab).not.toBeInTheDocument();

expect(firstTab).toHaveAttribute("aria-selected", "false");
expect(secondTab).toHaveAttribute("aria-selected", "false");
expect(thirdTab).toHaveAttribute("aria-selected", "true");
});

it("a user can input details on multiple features and submit", async () => {
const { getByTestId, getByRole, user } = setup(<MapAndLabel {...props} />);
getByTestId("map-and-label-map");

addMultipleFeatures([point1, point2]);

// vertical side tab query
const firstTab = getByRole("tab", { name: /Tree 1/ });
const firstTabPanel = getByTestId("vertical-tabpanel-0");
const secondTabPanel = getByTestId("vertical-tabpanel-1");

await fillOutForm(user);
const secondSpeciesInput = within(secondTabPanel).getByLabelText("Species");

expect(secondSpeciesInput).toHaveDisplayValue("Larch");

await user.click(firstTab);

// check form on screen is reset
const firstSpeciesInput = within(firstTabPanel).getByLabelText("Species");
expect(secondSpeciesInput).not.toBeInTheDocument();
expect(firstSpeciesInput).not.toHaveDisplayValue("Larch");

await fillOutForm(user);

await clickContinue(user);

await checkErrorMessagesEmpty();
});
it("a user can input details on feature tabs in any order", async () => {
const { getByTestId, getByRole, user } = setup(<MapAndLabel {...props} />);

addMultipleFeatures([point1, point2]);

const firstTab = getByRole("tab", { name: /Tree 1/ });
const secondTab = getByRole("tab", { name: /Tree 2/ });

const firstTabPanel = getByTestId("vertical-tabpanel-0");
const secondTabPanel = getByTestId("vertical-tabpanel-1");

await user.click(firstTab);

const firstSpeciesInput = within(firstTabPanel).getByLabelText("Species");
expect(firstSpeciesInput).not.toHaveDisplayValue("Larch");

// partially fill out firstTabPanel
await fillOutFirstHalfOfForm(user);

await user.click(secondTab);
const secondSpeciesInput = within(secondTabPanel).getByLabelText("Species");
expect(secondSpeciesInput).not.toHaveDisplayValue("Larch");

// partially fill out secondTabPanel
await fillOutFirstHalfOfForm(user);

await user.click(firstTab);

// check that the data stays within the firstTabPanel
expect(firstSpeciesInput).toHaveDisplayValue("Larch");

// Complete the filling out of the firstTabPanel
await fillOutSecondHalfOfForm(user);

await user.click(secondTab);

// check that the data stays within the secondTabPanel
expect(secondSpeciesInput).toHaveDisplayValue("Larch");

// Complete the filling out of the secondTabPanel
await fillOutSecondHalfOfForm(user);

await clickContinue(user);

await checkErrorMessagesEmpty();
});
});

describe("copy feature select", () => {
Expand All @@ -232,7 +337,7 @@ describe("copy feature select", () => {
it.todo("is enabled once multiple features are present");
// copy select enabled once you add more features
it.todo(
"lists all other features as options (the current feature is not listed)",
"lists all other features as options (the current feature is not listed)"
);
// current tree is not an option in the copy select
it.todo("copies all data from one feature to another");
Expand All @@ -254,11 +359,11 @@ describe("payload generation", () => {
test.todo("a submitted payload contains a GeoJSON feature collection");
// check payload contains GeoJSON feature collection
test.todo(
"the feature collection contains all geospatial data inputted by the user",
"the feature collection contains all geospatial data inputted by the user"
);
// feature collection matches the mocked data
test.todo(
"each feature's properties correspond with the details entered for that feature",
"each feature's properties correspond with the details entered for that feature"
);
// feature properties contain the answers to inputs
});
Loading

0 comments on commit f45fefd

Please sign in to comment.