Add Unit Tests to UI (#2015)
* Update testing framework * Update action button test * Add unit tests for language and authentication page * Add unit tests for the custom selector * Fix packages, add new testing plugin for eslint, fix issues * Add unit tests for ChipInput * Add coverage and test ui. Add more tests * Fix formatting issues * Try to fix the styling issues again * Fix formatting issuespull/2060/head
parent
3310f6aeb8
commit
0b7a1a90a1
@ -1,4 +1,4 @@
|
|||||||
build
|
build
|
||||||
dist
|
dist
|
||||||
converage
|
coverage
|
||||||
public
|
public
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,82 @@
|
|||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
|
import { Language } from ".";
|
||||||
|
|
||||||
|
describe("Language text", () => {
|
||||||
|
const testLanguage: Language.Info = {
|
||||||
|
code2: "en",
|
||||||
|
name: "English",
|
||||||
|
};
|
||||||
|
|
||||||
|
it("should show short text", () => {
|
||||||
|
render(<Language.Text value={testLanguage}></Language.Text>);
|
||||||
|
|
||||||
|
expect(screen.getByText(testLanguage.code2)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show long text", () => {
|
||||||
|
render(<Language.Text value={testLanguage} long></Language.Text>);
|
||||||
|
|
||||||
|
expect(screen.getByText(testLanguage.name)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
const testLanguageWithHi: Language.Info = { ...testLanguage, hi: true };
|
||||||
|
|
||||||
|
it("should show short text with HI", () => {
|
||||||
|
render(<Language.Text value={testLanguageWithHi}></Language.Text>);
|
||||||
|
|
||||||
|
const expectedText = `${testLanguageWithHi.code2}:HI`;
|
||||||
|
|
||||||
|
expect(screen.getByText(expectedText)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show long text with HI", () => {
|
||||||
|
render(<Language.Text value={testLanguageWithHi} long></Language.Text>);
|
||||||
|
|
||||||
|
const expectedText = `${testLanguageWithHi.name} HI`;
|
||||||
|
|
||||||
|
expect(screen.getByText(expectedText)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
const testLanguageWithForced: Language.Info = {
|
||||||
|
...testLanguage,
|
||||||
|
forced: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
it("should show short text with Forced", () => {
|
||||||
|
render(<Language.Text value={testLanguageWithForced}></Language.Text>);
|
||||||
|
|
||||||
|
const expectedText = `${testLanguageWithHi.code2}:Forced`;
|
||||||
|
|
||||||
|
expect(screen.getByText(expectedText)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show long text with Forced", () => {
|
||||||
|
render(<Language.Text value={testLanguageWithForced} long></Language.Text>);
|
||||||
|
|
||||||
|
const expectedText = `${testLanguageWithHi.name} Forced`;
|
||||||
|
|
||||||
|
expect(screen.getByText(expectedText)).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Language list", () => {
|
||||||
|
const elements: Language.Info[] = [
|
||||||
|
{
|
||||||
|
code2: "en",
|
||||||
|
name: "English",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code2: "zh",
|
||||||
|
name: "Chinese",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
it("should show all languages", () => {
|
||||||
|
render(<Language.List value={elements}></Language.List>);
|
||||||
|
|
||||||
|
elements.forEach((value) => {
|
||||||
|
expect(screen.getByText(value.name)).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,38 @@
|
|||||||
|
import { faStickyNote } from "@fortawesome/free-regular-svg-icons";
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
import { describe, it, vitest } from "vitest";
|
||||||
|
import Action from "./Action";
|
||||||
|
|
||||||
|
const testLabel = "Test Label";
|
||||||
|
const testIcon = faStickyNote;
|
||||||
|
|
||||||
|
describe("Action button", () => {
|
||||||
|
it("should be a button", () => {
|
||||||
|
render(<Action icon={testIcon} label={testLabel}></Action>);
|
||||||
|
const element = screen.getByRole("button", { name: testLabel });
|
||||||
|
|
||||||
|
expect(element.getAttribute("type")).toEqual("button");
|
||||||
|
expect(element.getAttribute("aria-label")).toEqual(testLabel);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show icon", () => {
|
||||||
|
render(<Action icon={testIcon} label={testLabel}></Action>);
|
||||||
|
// TODO: use getBy...
|
||||||
|
const element = screen.getByRole("img", { hidden: true });
|
||||||
|
|
||||||
|
expect(element.getAttribute("data-prefix")).toEqual(testIcon.prefix);
|
||||||
|
expect(element.getAttribute("data-icon")).toEqual(testIcon.iconName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call on-click event when clicked", async () => {
|
||||||
|
const onClickFn = vitest.fn();
|
||||||
|
render(
|
||||||
|
<Action icon={testIcon} label={testLabel} onClick={onClickFn}></Action>
|
||||||
|
);
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByRole("button", { name: testLabel }));
|
||||||
|
|
||||||
|
expect(onClickFn).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,46 @@
|
|||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
import { describe, it, vitest } from "vitest";
|
||||||
|
import ChipInput from "./ChipInput";
|
||||||
|
|
||||||
|
describe("ChipInput", () => {
|
||||||
|
const existedValues = ["value_1", "value_2"];
|
||||||
|
|
||||||
|
// TODO: Support default value
|
||||||
|
it.skip("should works with default value", () => {
|
||||||
|
render(<ChipInput defaultValue={existedValues}></ChipInput>);
|
||||||
|
|
||||||
|
existedValues.forEach((value) => {
|
||||||
|
expect(screen.getByText(value)).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should works with value", () => {
|
||||||
|
render(<ChipInput value={existedValues}></ChipInput>);
|
||||||
|
|
||||||
|
existedValues.forEach((value) => {
|
||||||
|
expect(screen.getByText(value)).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip("should allow user creates new value", async () => {
|
||||||
|
const typedValue = "value_3";
|
||||||
|
const mockedFn = vitest.fn((values: string[]) => {
|
||||||
|
expect(values).toContain(typedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<ChipInput value={existedValues} onChange={mockedFn}></ChipInput>);
|
||||||
|
|
||||||
|
const element = screen.getByRole("searchbox");
|
||||||
|
|
||||||
|
await userEvent.type(element, typedValue);
|
||||||
|
|
||||||
|
expect(element).toHaveValue(typedValue);
|
||||||
|
|
||||||
|
const createBtn = screen.getByText(`Add "${typedValue}"`);
|
||||||
|
|
||||||
|
await userEvent.click(createBtn);
|
||||||
|
|
||||||
|
expect(mockedFn).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,147 @@
|
|||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
import { describe, it, vitest } from "vitest";
|
||||||
|
import { Selector, SelectorOption } from "./Selector";
|
||||||
|
|
||||||
|
const selectorName = "Test Selections";
|
||||||
|
const testOptions: SelectorOption<string>[] = [
|
||||||
|
{
|
||||||
|
label: "Option 1",
|
||||||
|
value: "option_1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Option 2",
|
||||||
|
value: "option_2",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
describe("Selector", () => {
|
||||||
|
describe("options", () => {
|
||||||
|
it("should work with the SelectorOption", () => {
|
||||||
|
render(<Selector name={selectorName} options={testOptions}></Selector>);
|
||||||
|
|
||||||
|
// TODO: selectorName
|
||||||
|
expect(screen.getByRole("searchbox")).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should display when clicked", async () => {
|
||||||
|
render(<Selector name={selectorName} options={testOptions}></Selector>);
|
||||||
|
|
||||||
|
const element = screen.getByRole("searchbox");
|
||||||
|
|
||||||
|
await userEvent.click(element);
|
||||||
|
|
||||||
|
expect(screen.queryAllByRole("option")).toHaveLength(testOptions.length);
|
||||||
|
|
||||||
|
testOptions.forEach((option) => {
|
||||||
|
expect(screen.getByText(option.label)).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shouldn't show default value", async () => {
|
||||||
|
const option = testOptions[0];
|
||||||
|
render(
|
||||||
|
<Selector
|
||||||
|
name={selectorName}
|
||||||
|
options={testOptions}
|
||||||
|
defaultValue={option.value}
|
||||||
|
></Selector>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByDisplayValue(option.label)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shouldn't show value", async () => {
|
||||||
|
const option = testOptions[0];
|
||||||
|
render(
|
||||||
|
<Selector
|
||||||
|
name={selectorName}
|
||||||
|
options={testOptions}
|
||||||
|
value={option.value}
|
||||||
|
></Selector>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByDisplayValue(option.label)).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("event", () => {
|
||||||
|
it("should fire on-change event when clicking option", async () => {
|
||||||
|
const clickedOption = testOptions[0];
|
||||||
|
const mockedFn = vitest.fn((value: string | null) => {
|
||||||
|
expect(value).toEqual(clickedOption.value);
|
||||||
|
});
|
||||||
|
render(
|
||||||
|
<Selector
|
||||||
|
name={selectorName}
|
||||||
|
options={testOptions}
|
||||||
|
onChange={mockedFn}
|
||||||
|
></Selector>
|
||||||
|
);
|
||||||
|
|
||||||
|
const element = screen.getByRole("searchbox");
|
||||||
|
|
||||||
|
await userEvent.click(element);
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByText(clickedOption.label));
|
||||||
|
|
||||||
|
expect(mockedFn).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("with object options", () => {
|
||||||
|
const objectOptions: SelectorOption<{ name: string }>[] = [
|
||||||
|
{
|
||||||
|
label: "Option 1",
|
||||||
|
value: {
|
||||||
|
name: "option_1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Option 2",
|
||||||
|
value: {
|
||||||
|
name: "option_2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
it("should fire on-change event with payload", async () => {
|
||||||
|
const clickedOption = objectOptions[0];
|
||||||
|
|
||||||
|
const mockedFn = vitest.fn((value: { name: string } | null) => {
|
||||||
|
expect(value).toEqual(clickedOption.value);
|
||||||
|
});
|
||||||
|
render(
|
||||||
|
<Selector
|
||||||
|
name={selectorName}
|
||||||
|
options={objectOptions}
|
||||||
|
onChange={mockedFn}
|
||||||
|
getkey={(v) => v.name}
|
||||||
|
></Selector>
|
||||||
|
);
|
||||||
|
|
||||||
|
const element = screen.getByRole("searchbox");
|
||||||
|
|
||||||
|
await userEvent.click(element);
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByText(clickedOption.label));
|
||||||
|
|
||||||
|
expect(mockedFn).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("placeholder", () => {
|
||||||
|
it("should show when no selection", () => {
|
||||||
|
const placeholder = "Empty Selection";
|
||||||
|
render(
|
||||||
|
<Selector
|
||||||
|
name={selectorName}
|
||||||
|
options={testOptions}
|
||||||
|
placeholder={placeholder}
|
||||||
|
></Selector>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByPlaceholderText(placeholder)).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,12 +0,0 @@
|
|||||||
import { describe, it } from "vitest";
|
|
||||||
import { StaticModals } from "./WithModal";
|
|
||||||
|
|
||||||
describe("modal tests", () => {
|
|
||||||
it.skip("no duplicated modals", () => {
|
|
||||||
const existedKeys = new Set<string>();
|
|
||||||
StaticModals.forEach(({ modalKey }) => {
|
|
||||||
expect(existedKeys.has(modalKey)).toBeFalsy();
|
|
||||||
existedKeys.add(modalKey);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -0,0 +1,20 @@
|
|||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { QueryClientProvider } from "react-query";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
|
import Authentication from "./Authentication";
|
||||||
|
|
||||||
|
import queryClient from "@/apis/queries";
|
||||||
|
|
||||||
|
describe("Authentication", () => {
|
||||||
|
it("should render without crash", () => {
|
||||||
|
render(
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<Authentication></Authentication>
|
||||||
|
</QueryClientProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByPlaceholderText("Username")).toBeDefined();
|
||||||
|
expect(screen.getByPlaceholderText("Password")).toBeDefined();
|
||||||
|
expect(screen.getByRole("button", { name: "Login" })).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,31 @@
|
|||||||
|
import queryClient from "@/apis/queries";
|
||||||
|
import { Text } from "@mantine/core";
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { QueryClientProvider } from "react-query";
|
||||||
|
import { BrowserRouter } from "react-router-dom";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
|
import Layout from "./Layout";
|
||||||
|
|
||||||
|
const renderLayout = () => {
|
||||||
|
render(
|
||||||
|
<BrowserRouter>
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<Layout name="Test Settings">
|
||||||
|
<Text>Value</Text>
|
||||||
|
</Layout>
|
||||||
|
</QueryClientProvider>
|
||||||
|
</BrowserRouter>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("Settings layout", () => {
|
||||||
|
it.concurrent("should be able to render without issues", () => {
|
||||||
|
renderLayout();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.concurrent("save button should be disabled by default", () => {
|
||||||
|
renderLayout();
|
||||||
|
|
||||||
|
expect(screen.getByRole("button", { name: "Save" })).toBeDisabled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,38 @@
|
|||||||
|
import { Text } from "@mantine/core";
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
|
import { Section } from "./Section";
|
||||||
|
|
||||||
|
describe("Settings section", () => {
|
||||||
|
const header = "Section Header";
|
||||||
|
it("should show header", () => {
|
||||||
|
render(<Section header="Section Header"></Section>);
|
||||||
|
|
||||||
|
expect(screen.getByText(header)).toBeDefined();
|
||||||
|
expect(screen.getByRole("separator")).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show children", () => {
|
||||||
|
const text = "Section Child";
|
||||||
|
render(
|
||||||
|
<Section header="Section Header">
|
||||||
|
<Text>{text}</Text>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText(header)).toBeDefined();
|
||||||
|
expect(screen.getByText(text)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should work with hidden", () => {
|
||||||
|
const text = "Section Child";
|
||||||
|
render(
|
||||||
|
<Section header="Section Header" hidden>
|
||||||
|
<Text>{text}</Text>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText(header)).not.toBeVisible();
|
||||||
|
expect(screen.getByText(text)).not.toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,42 @@
|
|||||||
|
import { useForm } from "@mantine/form";
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { FunctionComponent } from "react";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
|
import { FormContext, FormValues } from "../utilities/FormValues";
|
||||||
|
import { Number, Text } from "./forms";
|
||||||
|
|
||||||
|
const FormSupport: FunctionComponent = ({ children }) => {
|
||||||
|
const form = useForm<FormValues>({
|
||||||
|
initialValues: {
|
||||||
|
settings: {},
|
||||||
|
hooks: {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return <FormContext.Provider value={form}>{children}</FormContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("Settings form", () => {
|
||||||
|
describe("number component", () => {
|
||||||
|
it("should be able to render", () => {
|
||||||
|
render(
|
||||||
|
<FormSupport>
|
||||||
|
<Number settingKey="test-numberValue"></Number>
|
||||||
|
</FormSupport>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByRole("textbox")).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("text component", () => {
|
||||||
|
it("should be able to render", () => {
|
||||||
|
render(
|
||||||
|
<FormSupport>
|
||||||
|
<Text settingKey="test-textValue"></Text>
|
||||||
|
</FormSupport>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByRole("textbox")).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1 +1,28 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
|
|
||||||
import "@testing-library/jest-dom";
|
import "@testing-library/jest-dom";
|
||||||
|
import { vitest } from "vitest";
|
||||||
|
|
||||||
|
// From https://stackoverflow.com/questions/39830580/jest-test-fails-typeerror-window-matchmedia-is-not-a-function
|
||||||
|
Object.defineProperty(window, "matchMedia", {
|
||||||
|
writable: true,
|
||||||
|
value: vitest.fn().mockImplementation((query) => ({
|
||||||
|
matches: false,
|
||||||
|
media: query,
|
||||||
|
onchange: null,
|
||||||
|
addListener: vitest.fn(), // Deprecated
|
||||||
|
removeListener: vitest.fn(), // Deprecated
|
||||||
|
addEventListener: vitest.fn(),
|
||||||
|
removeEventListener: vitest.fn(),
|
||||||
|
dispatchEvent: vitest.fn(),
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
|
||||||
|
// From https://github.com/mantinedev/mantine/blob/master/configuration/jest/jsdom.mocks.js
|
||||||
|
class ResizeObserver {
|
||||||
|
observe() {}
|
||||||
|
unobserve() {}
|
||||||
|
disconnect() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.ResizeObserver = ResizeObserver;
|
||||||
|
Loading…
Reference in new issue