Skip to content

Commit

Permalink
test: Basic test harness and first suite of tests
Browse files Browse the repository at this point in the history
  • Loading branch information
DafyddLlyr committed Aug 30, 2024
1 parent b7545a2 commit 8c42784
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const CopyFeature: React.FC<Props> = ({
bordered
required
title={"Copy from"}
labelId={`select-label-${destinationIndex}`}
labelId={`select-${destinationIndex}`}
value={""}
onChange={(e) => {
const label = e.target.value as string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { MyMap } from "@opensystemslab/map";
import { Presentational as MapAndLabel } from "@planx/components/MapAndLabel/Public";
import { waitFor } from "@testing-library/react";
import React from "react";
import { setup } from "testUtils";
import { vi } from "vitest";
import { axe } from "vitest-axe";

import { point1 } from "../test/mocks/geojson";
import { props } from "../test/mocks/Trees";
import { addFeaturesToMap } from "../test/utils";

beforeAll(() => {
if (!window.customElements.get("my-map")) {
window.customElements.define("my-map", MyMap);
}

const ResizeObserverMock = vi.fn(() => ({
observe: vi.fn(),
unobserve: vi.fn(),
disconnect: vi.fn(),
}));

vi.stubGlobal("ResizeObserver", ResizeObserverMock);
});

describe("Basic UI", () => {
it("renders correctly", () => {
const { getByText } = setup(<MapAndLabel {...props} />);

expect(getByText(/Mock title/)).toBeInTheDocument();
expect(getByText(/Mock description/)).toBeInTheDocument();
});

it("shows the user a prompt to begin the plotting interaction", async () => {
const { getByText } = setup(<MapAndLabel {...props} />);
expect(getByText("Plot a feature on the map to begin")).toBeInTheDocument();
});

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(),
);
});

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

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

addFeaturesToMap(map, [point1]);

expect(getByRole("tab", { name: /Tree 1/ })).toBeInTheDocument();
expect(getByRole("heading", { name: /Tree 1/ })).toBeInTheDocument();
});

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

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

addFeaturesToMap(map, [point1]);

const results = await axe(container);
expect(results).toHaveNoViolations();
});
});

// 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", () => {
test.todo("all fields are required");
test.todo("all fields are required, for all feature tabs");
test.todo("an error displays if the minimum number of items is not met");
test.todo("an error displays if the maximum number of items is exceeded");
});

describe("basic interactions - happy path", () => {
// Plot a feature on the map to begin - taken away
test.todo("adding an item to the map adds a feature tab");

test.todo("a user can input details on a single feature and submit");
test.todo("adding multiple features to the map adds multiple feature tabs");
test.todo("a user can input details on multiple features and submit");
test.todo("a user can input details on feature tabs in any order");
});

describe("copy feature select", () => {
it.todo("select does not display if only a single feature is present");
it.todo("does appear once multiple features are present");
it.todo(
"lists all other features as options (the current feature is not listed)",
);
it.todo("copies all data from one feature to another");
});

describe("remove feature button", () => {
it.todo("removes a feature from the form");
it.todo("removes a feature from the map");
});

describe("payload generation", () => {
test.todo("a submitted payload contains a GeoJSON feature collection");
test.todo(
"the feature collection contains all geospatial data inputted by the user",
);
test.todo(
"each feature's properties correspond with the details entered for that feature",
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ const VerticalFeatureTabs: React.FC<{ features: Feature[] }> = ({
onChange={(_e, newValue) => {
editFeature(parseInt(newValue, 10));
}}
// TODO!
aria-label="Vertical tabs example"
aria-label="Select a feature to enter data"
sx={{ borderRight: 1, borderColor: "divider" }}
>
{features.map((feature, i) => (
Expand All @@ -87,6 +86,8 @@ const VerticalFeatureTabs: React.FC<{ features: Feature[] }> = ({
key={`tabpanel-${i}`}
value={i.toString()}
sx={{ width: "100%" }}
aria-labelledby={`vertical-tab-${i}`}
id={`vertical-tabpanel-${i}`}
>
<Box
sx={{
Expand Down Expand Up @@ -208,6 +209,7 @@ const Root = () => {
{/* @ts-ignore */}
<my-map
id="map-and-label-map"
data-testid="map-and-label-map"
basemap={basemap}
ariaLabelOlFixedOverlay={`An interactive map for plotting and describing individual ${schemaName.toLocaleLowerCase()}`}
drawMode
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { PresentationalProps } from "../../Public";
import { Trees } from "../../schemas/Trees";

export const props: PresentationalProps = {
title: "Mock title",
description: "Mock description",
schemaName: "Trees",
fn: "MockFn",
schema: Trees,
basemap: "OSM",
drawColor: "#00FF00",
drawType: "Point",
longitude: -0.1629784,
latitude: 51.5230919,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Feature, Point } from "geojson";

export const point1: Feature<Point, { label: string }> = {
type: "Feature",
properties: {
label: "1",
},
geometry: {
type: "Point",
coordinates: [-3.685929607119201, 57.15301433687542],
},
};
18 changes: 18 additions & 0 deletions editor.planx.uk/src/@planx/components/MapAndLabel/test/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Feature, Point, Polygon } from "geojson";
import { act } from "react-dom/test-utils";

/**
* Helper to mock a user's interaction with the @opensystemslab/map element
* We aren't able to mock a user's click interaction, so instead we dispatch an event which matches the one generated by the webcomponent
*/
export const addFeaturesToMap = async (
map: HTMLElement,
features: Feature<Point | Polygon, { label: string }>[],
) => {
const mockEvent = new CustomEvent("geojsonChange", {
detail: {
"EPSG:3857": { features },
},
});
act(() => map.dispatchEvent(mockEvent));
};

0 comments on commit 8c42784

Please sign in to comment.