mirror of
https://github.com/morpheus65535/bazarr
synced 2024-12-21 23:32:31 +00:00
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 issues
This commit is contained in:
parent
3310f6aeb8
commit
0b7a1a90a1
17 changed files with 1273 additions and 138 deletions
|
@ -11,5 +11,15 @@
|
|||
"plugin:react-hooks/recommended",
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"plugins": ["testing-library"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"**/__tests__/**/*.[jt]s?(x)",
|
||||
"**/?(*.)+(spec|test).[jt]s?(x)"
|
||||
],
|
||||
"extends": ["plugin:testing-library/react"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
build
|
||||
dist
|
||||
converage
|
||||
coverage
|
||||
public
|
||||
|
|
875
frontend/package-lock.json
generated
875
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -14,11 +14,11 @@
|
|||
"private": true,
|
||||
"dependencies": {
|
||||
"@mantine/core": "^5.6.0",
|
||||
"@mantine/dropzone": "^5.6.0",
|
||||
"@mantine/form": "^5.6.0",
|
||||
"@mantine/hooks": "^5.6.0",
|
||||
"@mantine/modals": "^5.6.0",
|
||||
"@mantine/notifications": "^5.6.0",
|
||||
"@mantine/dropzone": "^5.6.0",
|
||||
"axios": "^0.27.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
|
@ -34,7 +34,7 @@
|
|||
"@fortawesome/free-solid-svg-icons": "^6.2.0",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@testing-library/react": "^12.1.0",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/lodash": "^4.14.0",
|
||||
|
@ -43,10 +43,13 @@
|
|||
"@types/react-dom": "^17.0.0",
|
||||
"@types/react-table": "^7.7.0",
|
||||
"@vitejs/plugin-react": "^2.2.0",
|
||||
"@vitest/coverage-c8": "^0.25.0",
|
||||
"@vitest/ui": "^0.25.0",
|
||||
"clsx": "^1.2.0",
|
||||
"eslint": "^8.26.0",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-testing-library": "^5.9.0",
|
||||
"husky": "^8.0.2",
|
||||
"jsdom": "^20.0.1",
|
||||
"lodash": "^4.17.0",
|
||||
|
@ -60,7 +63,7 @@
|
|||
"typescript": "^4",
|
||||
"vite": "^3.2.1",
|
||||
"vite-plugin-checker": "^0.5.5",
|
||||
"vitest": "^0.24.3"
|
||||
"vitest": "^0.25.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
|
@ -70,6 +73,8 @@
|
|||
"check:ts": "tsc --noEmit --incremental false",
|
||||
"check:fmt": "prettier -c .",
|
||||
"test": "vitest",
|
||||
"test:ui": "vitest --ui",
|
||||
"coverage": "vitest run --coverage",
|
||||
"format": "prettier -w .",
|
||||
"prepare": "cd .. && husky install frontend/.husky"
|
||||
},
|
||||
|
|
82
frontend/src/components/bazarr/Language.test.tsx
Normal file
82
frontend/src/components/bazarr/Language.test.tsx
Normal file
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
38
frontend/src/components/inputs/Action.test.tsx
Normal file
38
frontend/src/components/inputs/Action.test.tsx
Normal file
|
@ -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();
|
||||
});
|
||||
});
|
46
frontend/src/components/inputs/ChipInput.test.tsx
Normal file
46
frontend/src/components/inputs/ChipInput.test.tsx
Normal file
|
@ -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);
|
||||
});
|
||||
});
|
147
frontend/src/components/inputs/Selector.test.tsx
Normal file
147
frontend/src/components/inputs/Selector.test.tsx
Normal file
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
20
frontend/src/pages/Authentication.test.tsx
Normal file
20
frontend/src/pages/Authentication.test.tsx
Normal file
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -40,11 +40,13 @@ const Authentication: FunctionComponent = () => {
|
|||
>
|
||||
<Stack>
|
||||
<TextInput
|
||||
name="Username"
|
||||
placeholder="Username"
|
||||
required
|
||||
{...form.getInputProps("username")}
|
||||
></TextInput>
|
||||
<PasswordInput
|
||||
name="Password"
|
||||
required
|
||||
placeholder="Password"
|
||||
{...form.getInputProps("password")}
|
||||
|
|
31
frontend/src/pages/Settings/components/Layout.test.tsx
Normal file
31
frontend/src/pages/Settings/components/Layout.test.tsx
Normal file
|
@ -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();
|
||||
});
|
||||
});
|
38
frontend/src/pages/Settings/components/Section.test.tsx
Normal file
38
frontend/src/pages/Settings/components/Section.test.tsx
Normal file
|
@ -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();
|
||||
});
|
||||
});
|
42
frontend/src/pages/Settings/components/forms.test.tsx
Normal file
42
frontend/src/pages/Settings/components/forms.test.tsx
Normal file
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
5
frontend/src/types/react-table.d.ts
vendored
5
frontend/src/types/react-table.d.ts
vendored
|
@ -45,9 +45,8 @@ declare module "react-table" {
|
|||
interface CustomTableProps<D extends Record<string, unknown>>
|
||||
extends useSelectionProps<D> {}
|
||||
|
||||
export interface TableOptions<
|
||||
D extends Record<string, unknown>
|
||||
> extends UseExpandedOptions<D>,
|
||||
export interface TableOptions<D extends Record<string, unknown>>
|
||||
extends UseExpandedOptions<D>,
|
||||
// UseFiltersOptions<D>,
|
||||
// UseGlobalFiltersOptions<D>,
|
||||
UseGroupByOptions<D>,
|
||||
|
|
|
@ -1,27 +1,10 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { StrictMode } from "react";
|
||||
import { describe, it, vitest } from "vitest";
|
||||
import { describe, it } from "vitest";
|
||||
import { Main } from "../src/main";
|
||||
|
||||
describe("render test", () => {
|
||||
beforeAll(() => {
|
||||
// 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(),
|
||||
})),
|
||||
});
|
||||
});
|
||||
|
||||
it("render without crashing", () => {
|
||||
describe("App", () => {
|
||||
it("should render without crash", () => {
|
||||
render(
|
||||
<StrictMode>
|
||||
<Main />
|
||||
|
|
|
@ -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 a new issue