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
|
||||
dist
|
||||
converage
|
||||
coverage
|
||||
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 { 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