Skip to content

Commit

Permalink
Model service tests (#4349)
Browse files Browse the repository at this point in the history
* Add tests for model service filter

* Add tests for model service policies filter

* Remove dead code
  • Loading branch information
steverydz authored Jul 24, 2023
1 parent 93cf7a3 commit 0e96acb
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 5 deletions.
92 changes: 92 additions & 0 deletions static/js/brand-store/components/Model/PoliciesFilter.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React, { useEffect } from "react";
import { BrowserRouter } from "react-router-dom";
import { RecoilRoot, useRecoilValue } from "recoil";
import { QueryClient, QueryClientProvider } from "react-query";
import { render, screen, fireEvent } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";

import { policiesListFilterState } from "../../atoms";
import PoliciesFilter from "./PoliciesFilter";

const searchInputLabel = "Search policies";

const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
refetchOnReconnect: false,
},
},
});

const mockFilterQuery = { filter: "" };

jest.mock("react-router-dom", () => ({
...jest.requireActual("react-router-dom"),
useSearchParams: () => [new URLSearchParams(mockFilterQuery), jest.fn()],
}));

export const RecoilObserver = ({
node,
onChange,
}: {
node: any;
onChange: Function;
}) => {
const value = useRecoilValue(node);
useEffect(() => onChange(value), [onChange, value]);
return null;
};

const onChange = jest.fn();

const renderComponent = (filterQuery?: string) => {
mockFilterQuery.filter = filterQuery || "";

return render(
<RecoilRoot>
<BrowserRouter>
<QueryClientProvider client={queryClient}>
<RecoilObserver node={policiesListFilterState} onChange={onChange} />
<PoliciesFilter />
</QueryClientProvider>
</BrowserRouter>
</RecoilRoot>
);
};

describe("PoliciesFilter", () => {
it("displays a search input", () => {
renderComponent();
expect(screen.getByLabelText(searchInputLabel)).toBeInTheDocument();
});

it("displays an empty input if there is no filter query", () => {
renderComponent();
expect(screen.getByLabelText(searchInputLabel)).toHaveValue("");
});

it("displays an input with the filter query value if it exists", () => {
renderComponent("policy-name");
expect(screen.getByLabelText(searchInputLabel)).toHaveValue("policy-name");
});

it("calls `setFilter` when the search input changes", () => {
renderComponent();
fireEvent.change(screen.getByLabelText(searchInputLabel), {
target: {
value: "policy-name",
},
});
expect(onChange).toHaveBeenCalledWith("policy-name");
});

it("clears the filter when the reset button is clicked", async () => {
const user = userEvent.setup();
renderComponent();
await user.click(screen.getByRole("button", { name: "Clear filter" }));
expect(onChange).toHaveBeenCalledWith("");
expect(screen.getByLabelText(searchInputLabel)).toHaveValue("");
});
});
2 changes: 1 addition & 1 deletion static/js/brand-store/components/Model/PoliciesFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function PoliciesFilter() {
setFilter("");
}}
>
<Icon name="close">Close</Icon>
<Icon name="close">Clear filter</Icon>
</Button>
<Button type="submit" className="p-search-box__button">
<Icon name="search">Search</Icon>
Expand Down
4 changes: 1 addition & 3 deletions static/js/brand-store/components/Model/PoliciesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ import { useParams, Link } from "react-router-dom";
import { format } from "date-fns";
import { MainTable } from "@canonical/react-components";

import { signingKeysListState } from "../../atoms";
import { filteredPoliciesListState } from "../../selectors";

import type { Policy, SigningKey } from "../../types/shared";
import type { Policy } from "../../types/shared";

function ModelsTable() {
const { id } = useParams();
const policiesList = useRecoilValue<Array<Policy>>(filteredPoliciesListState);
const signingKeys = useRecoilValue<Array<SigningKey>>(signingKeysListState);

return (
<MainTable
Expand Down
92 changes: 92 additions & 0 deletions static/js/brand-store/components/Models/ModelsFilter.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React, { useEffect } from "react";
import { BrowserRouter } from "react-router-dom";
import { RecoilRoot, useRecoilValue } from "recoil";
import { QueryClient, QueryClientProvider } from "react-query";
import { render, screen, fireEvent } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";

import ModelsFilter from "./ModelsFilter";
import { modelsListFilterState } from "../../atoms";

const searchInputLabel = "Search models";

const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
refetchOnReconnect: false,
},
},
});

const mockFilterQuery = { filter: "" };

jest.mock("react-router-dom", () => ({
...jest.requireActual("react-router-dom"),
useSearchParams: () => [new URLSearchParams(mockFilterQuery), jest.fn()],
}));

export const RecoilObserver = ({
node,
onChange,
}: {
node: any;
onChange: Function;
}) => {
const value = useRecoilValue(node);
useEffect(() => onChange(value), [onChange, value]);
return null;
};

const onChange = jest.fn();

const renderComponent = (filterQuery?: string) => {
mockFilterQuery.filter = filterQuery || "";

return render(
<RecoilRoot>
<BrowserRouter>
<QueryClientProvider client={queryClient}>
<RecoilObserver node={modelsListFilterState} onChange={onChange} />
<ModelsFilter />
</QueryClientProvider>
</BrowserRouter>
</RecoilRoot>
);
};

describe("ModelsFilter", () => {
it("displays a search input", () => {
renderComponent();
expect(screen.getByLabelText(searchInputLabel)).toBeInTheDocument();
});

it("displays an empty input if there is no filter query", () => {
renderComponent();
expect(screen.getByLabelText(searchInputLabel)).toHaveValue("");
});

it("displays an input with the filter query value if it exists", () => {
renderComponent("model-name");
expect(screen.getByLabelText(searchInputLabel)).toHaveValue("model-name");
});

it("calls `setFilter` when the search input changes", () => {
renderComponent();
fireEvent.change(screen.getByLabelText(searchInputLabel), {
target: {
value: "model-name",
},
});
expect(onChange).toHaveBeenCalledWith("model-name");
});

it("clears the filter when the reset button is clicked", async () => {
const user = userEvent.setup();
renderComponent();
await user.click(screen.getByRole("button", { name: "Clear filter" }));
expect(onChange).toHaveBeenCalledWith("");
expect(screen.getByLabelText(searchInputLabel)).toHaveValue("");
});
});
2 changes: 1 addition & 1 deletion static/js/brand-store/components/Models/ModelsFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function ModelsFilter() {
setFilter("");
}}
>
<Icon name="close">Close</Icon>
<Icon name="close">Clear filter</Icon>
</Button>
<Button type="submit" className="p-search-box__button">
<Icon name="search">Search</Icon>
Expand Down

0 comments on commit 0e96acb

Please sign in to comment.