Skip to content

Commit

Permalink
provide unit tests for new form components
Browse files Browse the repository at this point in the history
  • Loading branch information
aswallace committed Mar 21, 2024
1 parent 0a7f82d commit 4150aff
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 2 deletions.
2 changes: 1 addition & 1 deletion packages/core/components/AnnotationFilterForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ interface AnnotationFilterFormProps {
* of annotation: if the annotation is of type string, it will render a list for the user to choose
* amongst its items; if the annotation is of type date, it will render a date input; etc.
*/
export default function AnnotationFilterFormWrapper(props: AnnotationFilterFormProps) {
export default function AnnotationFilterForm(props: AnnotationFilterFormProps) {
const { annotationName } = props;
const dispatch = useDispatch();
const annotations = useSelector(metadata.selectors.getSupportedAnnotations);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { render, fireEvent } from "@testing-library/react";
import { expect } from "chai";
import { noop } from "lodash";
import * as React from "react";
import sinon from "sinon";

import DateRangePicker from "..";
import FileFilter from "../../../entity/FileFilter";

describe("<RangePicker />", () => {
it("renders inputs for start and end dates with selectable date pickers", () => {
// Arrange
const onSearch = sinon.spy();
const { getAllByLabelText, getAllByRole, getByLabelText, getByRole } = render(
<DateRangePicker onSearch={onSearch} onReset={noop} currentRange={undefined} />
);

// Should render both input fields
expect(getAllByRole("combobox").length).to.equal(2);
expect(getAllByLabelText(/start/).length).to.equal(1);
expect(getAllByLabelText(/end/).length).to.equal(1);

// Select a start date
expect(onSearch.called).to.equal(false);
fireEvent.click(getByLabelText(/start/));
fireEvent.click(getByRole("button", { name: /^18,\s/ }));
expect(onSearch.called).to.equal(true);

// Reset spy to isolate assertions)
onSearch.resetHistory();

// Select an end date
expect(onSearch.called).to.equal(false);
fireEvent.click(getByLabelText(/end/));
fireEvent.click(getByRole("button", { name: /^20,\s/ }));
expect(onSearch.called).to.equal(true);
});

it("initializes to values passed through props if provided", () => {
// Arrange
const currentRange = new FileFilter(
"date",
`RANGE(2024-02-21T00:00:00.000Z,2024-03-21T00:00:00.000Z)`
);
const { getByText } = render(
<DateRangePicker onSearch={noop} onReset={noop} currentRange={currentRange} />
);

expect(getByText("Wed Feb 21 2024")).to.exist;
// We currently subtract a day to account for exclusive upper date range
expect(getByText("Wed Mar 20 2024")).to.exist;
});

it("renders a 'Clear' button if given a callback", () => {
// Arrange
const onSearch = noop;
const onReset = sinon.spy();

// Act / Assert
const { getByTitle } = render(
<DateRangePicker onSearch={onSearch} onReset={onReset} currentRange={undefined} />
);

// Hit reset
expect(onReset.called).to.equal(false);
fireEvent.click(getByTitle("Clear"));
expect(onReset.called).to.equal(true);
});
});
135 changes: 135 additions & 0 deletions packages/core/components/RangePicker/test/RangePicker.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { render, fireEvent, screen } from "@testing-library/react";
import { expect } from "chai";
import { noop } from "lodash";
import * as React from "react";
import sinon from "sinon";

import RangePicker, { ListItem } from "..";
import FileFilter from "../../../entity/FileFilter";

describe("<RangePicker />", () => {
it("renders input fields for min and max values initialized to overall min/max", () => {
// Arrange
const items: ListItem[] = ["0", "20"].map((val) => ({
displayValue: val,
value: val,
}));

const { getAllByTitle } = render(
<RangePicker items={items} onSearch={noop} onReset={noop} currentRange={undefined} />
);

// Should render both input fields
expect(getAllByTitle(/^Min/).length).to.equal(1);
expect(getAllByTitle(/^Max/).length).to.equal(1);

// Should initialize to min and max item provided, respectively
expect(screen.getByTitle<HTMLInputElement>(/^Min/).value).to.equal("0");
expect(screen.getByTitle<HTMLInputElement>(/^Max/).value).to.equal("20");
});

it("initializes to values passed through props if provided", () => {
// Arrange
const currentRange = new FileFilter("foo", "RANGE(0, 12.34)");
const items: ListItem[] = ["-20", "20"].map((val) => ({
displayValue: val,
value: val,
}));

render(
<RangePicker items={items} onSearch={noop} onReset={noop} currentRange={currentRange} />
);

// Should initialize to min and max item provided, respectively
expect(screen.getByTitle<HTMLInputElement>(/^Min/).value).to.equal("0");
expect(screen.getByTitle<HTMLInputElement>(/^Max/).value).to.equal("12.34");
});

it("renders a 'Reset' button if given a callback", () => {
// Arrange
const onSearch = noop;
const onReset = sinon.spy();
const items: ListItem[] = ["0", "20"].map((val) => ({
displayValue: val,
value: val,
}));

// Act / Assert
const { getByText } = render(
<RangePicker
items={items}
onSearch={onSearch}
onReset={onReset}
currentRange={undefined}
/>
);

// Sanity check
expect(screen.getByTitle<HTMLInputElement>(/^Min/).value).to.equal("0");
expect(screen.getByTitle<HTMLInputElement>(/^Max/).value).to.equal("20");

// Hit reset
expect(onReset.called).to.equal(false);
fireEvent.click(getByText("Reset"));
expect(onReset.called).to.equal(true);

// Should clear min and max values
expect(screen.getByTitle<HTMLInputElement>(/^Min/).value).to.equal("");
expect(screen.getByTitle<HTMLInputElement>(/^Max/).value).to.equal("");
});

it("renders a 'Select Full Range' button that updates min and max values", () => {
// Arrange
const items: ListItem[] = ["0", "20"].map((val) => ({
selected: true, // start with all items selected
displayValue: val,
value: val,
}));
const { getByTitle, getByText } = render(
<RangePicker items={items} onReset={noop} onSearch={noop} currentRange={undefined} />
);

// Enter values
fireEvent.change(getByTitle(/^Min/), {
target: {
value: 5,
},
});
fireEvent.change(getByTitle(/^Max/), {
target: {
value: 10,
},
});

// Sanity check
expect(screen.getByTitle<HTMLInputElement>(/^Min/).value).to.equal("5");
expect(screen.getByTitle<HTMLInputElement>(/^Max/).value).to.equal("10");

// Act
fireEvent.click(getByText("Select Full Range"));

// Assert
expect(screen.getByTitle<HTMLInputElement>(/^Min/).value).to.equal("0");
expect(screen.getByTitle<HTMLInputElement>(/^Max/).value).to.equal("20");
});

it("displays available min and max of items", () => {
// Arrange
const items: ListItem[] = ["-2", "1", "20", "42"].map((val) => ({
displayValue: val,
value: val,
}));
const { getByText } = render(
<RangePicker items={items} onReset={noop} onSearch={noop} currentRange={undefined} />
);

// Act / Assert
expect(
getByText(
`Full range available: ${items[0].displayValue}, ${
items[items.length - 1].displayValue
}`
)
).to.exist;
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function extractValuesFromRangeOperatorFilterString(
// Regex with capture groups for identifying values in the RANGE() filter operator
// e.g. RANGE(-.1, 10) captures "-.1" and "10"
// Does not check for valid values, just captures existing floats
const RANGE_OPERATOR_REGEX = /RANGE\(([\d\-.]+),([\d\-.]+)\)/g;
const RANGE_OPERATOR_REGEX = /RANGE\(([\d\-.]+),\s?([\d\-.]+)\)/g;
const exec = RANGE_OPERATOR_REGEX.exec(filterString);
if (exec && exec.length === 3) {
const minValue = exec[1];
Expand Down

0 comments on commit 4150aff

Please sign in to comment.