This commit is contained in:
Stefan Pejcic
2024-11-07 19:03:37 +01:00
parent c6df945ed5
commit 09f9f9502d
2472 changed files with 620417 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
import React from "react";
import type { AutoSaveIndicatorProps } from "@refinedev/core";
import { render } from "@test";
export const autoSaveIndicatorTests = (
AutoSaveIndicator: React.ComponentType<AutoSaveIndicatorProps>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / AutoSaveIndicator", () => {
it("should render success", async () => {
const { findByText, getByText } = render(
<AutoSaveIndicator status="success" />,
);
await findByText("saved");
getByText("saved");
});
it("should render error", async () => {
const { findByText, getByText } = render(
<AutoSaveIndicator status="error" />,
);
await findByText("auto save failure");
getByText("auto save failure");
});
it("should render idle", async () => {
const { findByText, getByText } = render(
<AutoSaveIndicator status="idle" />,
);
await findByText("waiting for changes");
getByText("waiting for changes");
});
it("should render loading", async () => {
const { findByText, getByText } = render(
<AutoSaveIndicator status="loading" />,
);
await findByText("saving...");
getByText("saving...");
});
});
};

View File

@@ -0,0 +1,80 @@
import React from "react";
import { Route, Routes } from "react-router-dom";
import type { RefineBreadcrumbProps } from "@refinedev/ui-types";
import { act, type ITestWrapperProps, render, TestWrapper } from "@test";
const renderBreadcrumb = (
children: React.ReactNode,
wrapperProps: ITestWrapperProps = {},
) => {
return render(
<Routes>
<Route path="/:resource/:action" element={children} />
</Routes>,
{
wrapper: TestWrapper(wrapperProps),
},
);
};
const DummyResourcePage = () => <div>Dummy</div>;
export const breadcrumbTests = (
Breadcrumb: React.ComponentType<RefineBreadcrumbProps<any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / CRUD Create", () => {
it("should render successfuly", async () => {
const { container } = renderBreadcrumb(<Breadcrumb />);
expect(container).toBeTruthy();
});
it("should render breadcrumb items", async () => {
const { getByText } = renderBreadcrumb(<Breadcrumb />, {
resources: [{ name: "posts" }],
routerInitialEntries: ["/posts/create"],
});
getByText("Posts");
getByText("Create");
});
it("should render breadcrumb items with link", async () => {
const { container } = renderBreadcrumb(<Breadcrumb />, {
resources: [{ name: "posts", list: DummyResourcePage }],
routerInitialEntries: ["/posts/create"],
});
expect(container.querySelector("a")?.getAttribute("href")).toBe("/posts");
});
it("should render breadcrumb items with resource icon", async () => {
const { getByTestId } = renderBreadcrumb(<Breadcrumb />, {
resources: [
{
name: "posts",
icon: <div data-testid="resource-icon" />,
},
],
routerInitialEntries: ["/posts/create"],
});
getByTestId("resource-icon");
});
it("should render breadcrumb items without resource icon", async () => {
const { queryByTestId } = renderBreadcrumb(<Breadcrumb hideIcons />, {
resources: [
{
name: "posts",
icon: <div data-testid="resource-icon" />,
},
],
routerInitialEntries: ["/posts/create"],
});
expect(queryByTestId("resource-icon")).not.toBeInTheDocument();
});
});
};

View File

@@ -0,0 +1,441 @@
import React from "react";
import { Route, Routes } from "react-router-dom";
import {
type RefineCloneButtonProps,
RefineButtonTestIds,
} from "@refinedev/ui-types";
import { act, fireEvent, render, TestWrapper, waitFor } from "@test";
export const buttonCloneTests = (
CloneButton: React.ComponentType<RefineCloneButtonProps<any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Clone Button", () => {
beforeAll(() => {
jest.spyOn(console, "warn").mockImplementation(jest.fn());
});
it("should render button successfuly", async () => {
const { container, getByText } = render(<CloneButton />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
expect(getByText("Clone").closest("button")).not.toBeDisabled();
});
it("should have the correct test-id", async () => {
const { queryByTestId } = render(<CloneButton />, {
wrapper: TestWrapper({}),
});
expect(queryByTestId(RefineButtonTestIds.CloneButton)).toBeTruthy();
});
it("should render text by children", async () => {
const { container, getByText } = render(
<CloneButton>refine</CloneButton>,
{
wrapper: TestWrapper({}),
},
);
expect(container).toBeTruthy();
getByText("refine");
});
it("should render without text show only icon", async () => {
const { container, queryByText } = render(<CloneButton hideText />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
expect(queryByText("Clone")).not.toBeInTheDocument();
});
describe("access control", () => {
describe("with global access control only", () => {
describe("with default behaviour", () => {
describe("when user not have access", () => {
it("should render disabled button with reason text", async () => {
const { container, getByText } = render(
<CloneButton recordItemId="1">Clone</CloneButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async ({ params, action }) => {
if (action === "create" && params?.id === "1") {
return {
can: false,
reason: "Access Denied",
};
}
return {
can: true,
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("Clone").closest("button")).toBeDisabled(),
);
waitFor(() =>
expect(
getByText("Clone").closest("button")?.getAttribute("title"),
).toBe("Access Denied"),
);
});
});
describe("when user have access", () => {
it("should render enabled button", async () => {
const { container, getByText } = render(
<CloneButton recordItemId="2">Clone</CloneButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async ({ params, action }) => {
if (action === "create" && params?.id === "1") {
return {
can: false,
};
}
return {
can: true,
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("Clone").closest("button")).not.toBeDisabled(),
);
});
});
});
describe("when hideIfUnauthorized is true", () => {
it("should not render button", async () => {
const { container, queryByText } = render(
<CloneButton>Clone</CloneButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({ can: false }),
options: {
buttons: {
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("Clone")).not.toBeInTheDocument();
});
});
describe("when access control is disabled explicitly", () => {
it("should render enabled button", async () => {
const { container, getByText } = render(
<CloneButton>Clone</CloneButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({ can: false }),
options: {
buttons: {
enableAccessControl: false,
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(getByText("Clone").closest("button")).not.toBeDisabled();
});
});
});
describe("with global config and accessControl prop", () => {
describe("when access control enabled globally", () => {
describe("when access control is disabled with prop", () => {
it("should render enabled button", async () => {
const { container, getByText } = render(
<CloneButton accessControl={{ enabled: false }}>
Clone
</CloneButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => {
return {
can: false,
};
},
options: {
buttons: {
enableAccessControl: true,
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("Clone").closest("button")).not.toBeDisabled(),
);
});
});
describe("when hideIfUnauthorized false globally", () => {
describe("when hideIfUnauthorized enabled with prop", () => {
it("should not render button", async () => {
const { container, queryByText } = render(
<CloneButton
accessControl={{
hideIfUnauthorized: true,
}}
>
Clone
</CloneButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({
can: false,
}),
options: {
buttons: {
hideIfUnauthorized: false,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("Clone")).not.toBeInTheDocument();
});
});
});
});
describe("when access control disabled globally", () => {
describe("when access control enabled with prop", () => {
it("should render disabled button with reason text", async () => {
const { container, getByText } = render(
<CloneButton accessControl={{ enabled: true }}>
Clone
</CloneButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => {
return {
can: false,
reason: "Access Denied",
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("Clone").closest("button")).toBeDisabled(),
);
waitFor(() =>
expect(
getByText("Clone").closest("button")?.getAttribute("title"),
).toBe("Access Denied"),
);
});
});
});
describe("when hideIfUnauthorized enabled globally", () => {
describe("when hideIfUnauthorized disabled with prop", () => {
it("should render button", async () => {
const { container, queryByText } = render(
<CloneButton
accessControl={{
hideIfUnauthorized: false,
}}
>
Clone
</CloneButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({
can: false,
}),
options: {
buttons: {
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("Clone")).toBeInTheDocument();
});
});
});
});
});
it("should render called function successfully if click the button", async () => {
const clone = jest.fn();
const { getByText } = render(
<CloneButton onClick={() => clone()} recordItemId="1" />,
{
wrapper: TestWrapper({
resources: [{ name: "posts" }],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Clone"));
});
expect(clone).toHaveBeenCalledTimes(1);
});
it("should create page redirect clone route called function successfully if click the button", async () => {
const { getByText } = render(
<Routes>
<Route path="/:resource" element={<CloneButton recordItemId="1" />} />
</Routes>,
{
wrapper: TestWrapper({
routerInitialEntries: ["/posts"],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Clone"));
});
expect(window.location.pathname).toBe("/posts/clone/1");
});
it("should edit page redirect clone route called function successfully if click the button", async () => {
const { getByText } = render(
<Routes>
<Route path="/:resource/:action/:id" element={<CloneButton />} />
</Routes>,
{
wrapper: TestWrapper({
routerInitialEntries: ["/posts/edit/1"],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Clone"));
});
expect(window.location.pathname).toBe("/posts/clone/1");
});
it("should custom resource and recordItemId redirect clone route called function successfully if click the button", async () => {
const { getByText } = render(
<Routes>
<Route
path="/:resource"
element={
<CloneButton
resourceNameOrRouteName="categories"
recordItemId="1"
/>
}
/>
</Routes>,
{
wrapper: TestWrapper({
resources: [{ name: "posts" }, { name: "categories" }],
routerInitialEntries: ["/posts"],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Clone"));
});
expect(window.location.pathname).toBe("/categories/clone/1");
});
it("should redirect with custom route called function successfully if click the button", async () => {
const { getByText } = render(
<Routes>
<Route
path="/:resource"
element={
<CloneButton
resourceNameOrRouteName="custom-route-posts"
recordItemId="1"
/>
}
/>
</Routes>,
{
wrapper: TestWrapper({
resources: [
{
name: "posts",
meta: { route: "custom-route-posts" },
},
{ name: "posts" },
],
routerInitialEntries: ["/posts"],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Clone"));
});
expect(window.location.pathname).toBe("/custom-route-posts/clone/1");
});
});
};

View File

@@ -0,0 +1,412 @@
import React from "react";
import { Route, Routes } from "react-router-dom";
import {
type RefineCreateButtonProps,
RefineButtonTestIds,
} from "@refinedev/ui-types";
import { act, render, TestWrapper, fireEvent, waitFor } from "@test";
export const buttonCreateTests = (
CreateButton: React.ComponentType<RefineCreateButtonProps<any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Create Button", () => {
beforeAll(() => {
jest.spyOn(console, "warn").mockImplementation(jest.fn());
});
const create = jest.fn();
it("should render button successfuly", async () => {
const { container, getByText } = render(<CreateButton />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
expect(getByText("Create").closest("button")).not.toBeDisabled();
});
it("should have the correct test-id", async () => {
const { queryByTestId } = render(<CreateButton />, {
wrapper: TestWrapper({}),
});
expect(queryByTestId(RefineButtonTestIds.CreateButton)).toBeTruthy();
});
it("should render text by children", async () => {
const { container, getByText } = render(
<CreateButton>refine</CreateButton>,
{
wrapper: TestWrapper({}),
},
);
expect(container).toBeTruthy();
getByText("refine");
});
it("should render without text show only icon", async () => {
const { container, queryByText } = render(<CreateButton hideText />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
expect(queryByText("Create")).not.toBeInTheDocument();
});
describe("access control", () => {
describe("with global access control only", () => {
describe("with default behaviour", () => {
describe("when user not have access", () => {
it("should render disabled button with reason text", async () => {
const { container, getByText } = render(
<CreateButton>Create</CreateButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async ({ action }) => {
if (action === "create") {
return {
can: false,
reason: "Access Denied",
};
}
return {
can: true,
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("Create").closest("button")).toBeDisabled(),
);
waitFor(() =>
expect(
getByText("Create").closest("button")?.getAttribute("title"),
).toBe("Access Denied"),
);
});
});
describe("when user have access", () => {
it("should render enabled button", async () => {
const { container, getByText } = render(
<CreateButton>Create</CreateButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async ({ action }) => {
if (action === "create") {
return {
can: false,
};
}
return {
can: true,
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(
getByText("Create").closest("button"),
).not.toBeDisabled(),
);
});
});
});
describe("when hideIfUnauthorized is true", () => {
it("should not render button", async () => {
const { container, queryByText } = render(
<CreateButton>Create</CreateButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({ can: false }),
options: {
buttons: {
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("Create")).not.toBeInTheDocument();
});
});
describe("when access control is disabled explicitly", () => {
it("should render enabled button", async () => {
const { container, getByText } = render(
<CreateButton>Create</CreateButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({ can: false }),
options: {
buttons: {
enableAccessControl: false,
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(getByText("Create").closest("button")).not.toBeDisabled();
});
});
});
describe("with global config and accessControl prop", () => {
describe("when access control enabled globally", () => {
describe("when access control is disabled with prop", () => {
it("should render enabled button", async () => {
const { container, getByText } = render(
<CreateButton accessControl={{ enabled: false }}>
Create
</CreateButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => {
return {
can: false,
};
},
options: {
buttons: {
enableAccessControl: true,
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(
getByText("Create").closest("button"),
).not.toBeDisabled(),
);
});
});
describe("when hideIfUnauthorized false globally", () => {
describe("when hideIfUnauthorized enabled with prop", () => {
it("should not render button", async () => {
const { container, queryByText } = render(
<CreateButton
accessControl={{
hideIfUnauthorized: true,
}}
>
Create
</CreateButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({
can: false,
}),
options: {
buttons: {
hideIfUnauthorized: false,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("Create")).not.toBeInTheDocument();
});
});
});
});
describe("when access control disabled globally", () => {
describe("when access control enabled with prop", () => {
it("should render disabled button with reason text", async () => {
const { container, getByText } = render(
<CreateButton accessControl={{ enabled: true }}>
Create
</CreateButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => {
return {
can: false,
reason: "Access Denied",
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("Create").closest("button")).toBeDisabled(),
);
waitFor(() =>
expect(
getByText("Create").closest("button")?.getAttribute("title"),
).toBe("Access Denied"),
);
});
});
});
describe("when hideIfUnauthorized enabled globally", () => {
describe("when hideIfUnauthorized disabled with prop", () => {
it("should render button", async () => {
const { container, queryByText } = render(
<CreateButton
accessControl={{
hideIfUnauthorized: false,
}}
>
Create
</CreateButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({
can: false,
}),
options: {
buttons: {
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("Create")).toBeInTheDocument();
});
});
});
});
});
it("should render called function successfully if click the button", async () => {
const { getByText } = render(<CreateButton onClick={() => create()} />, {
wrapper: TestWrapper({}),
});
await act(async () => {
fireEvent.click(getByText("Create"));
});
expect(create).toHaveBeenCalledTimes(1);
});
it("should redirect custom resource route called function successfully if click the button", async () => {
const { getByText } = render(
<Routes>
<Route
path="/:resource"
element={<CreateButton resourceNameOrRouteName="categories" />}
/>
</Routes>,
{
wrapper: TestWrapper({
resources: [{ name: "posts" }, { name: "categories" }],
routerInitialEntries: ["/posts"],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Create"));
});
expect(window.location.pathname).toBe("/categories/create");
});
it("should redirect create route called function successfully if click the button", async () => {
const { getByText } = render(
<Routes>
<Route path="/:resource" element={<CreateButton />} />
</Routes>,
{
wrapper: TestWrapper({
resources: [{ name: "posts" }],
routerInitialEntries: ["/posts"],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Create"));
});
expect(window.location.pathname).toBe("/posts/create");
});
it("should redirect with custom route called function successfully if click the button", async () => {
const { getByText } = render(
<Routes>
<Route
path="/:resource"
element={
<CreateButton resourceNameOrRouteName="custom-route-posts" />
}
/>
</Routes>,
{
wrapper: TestWrapper({
resources: [
{
name: "posts",
meta: { route: "custom-route-posts" },
},
{ name: "posts" },
],
routerInitialEntries: ["/posts"],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Create"));
});
expect(window.location.pathname).toBe("/custom-route-posts/create");
});
});
};

View File

@@ -0,0 +1,593 @@
import React from "react";
import {
type RefineDeleteButtonProps,
RefineButtonTestIds,
} from "@refinedev/ui-types";
import {
act,
fireEvent,
MockJSONServer,
render,
TestWrapper,
waitFor,
} from "@test";
import { Route, Routes } from "react-router-dom";
export const buttonDeleteTests = (
DeleteButton: React.ComponentType<RefineDeleteButtonProps<any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Delete Button", () => {
beforeAll(() => {
jest.spyOn(console, "error").mockImplementation(jest.fn());
jest.clearAllTimers();
});
it("should render button successfuly", async () => {
const { container, getByText } = render(<DeleteButton />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
expect(getByText("Delete").closest("button")).not.toBeDisabled();
});
it("should have the correct test-id", async () => {
const { getByTestId } = render(<DeleteButton />, {
wrapper: TestWrapper({}),
});
getByTestId(RefineButtonTestIds.DeleteButton);
});
it("should render text by children", async () => {
const { container, getByText } = render(
<DeleteButton>refine</DeleteButton>,
{
wrapper: TestWrapper({}),
},
);
expect(container).toBeTruthy();
getByText("refine");
});
it("should render without text show only icon", async () => {
const { container, queryByText } = render(<DeleteButton hideText />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
expect(queryByText("Delete")).not.toBeInTheDocument();
});
describe("access control", () => {
describe("with global access control only", () => {
describe("with default behaviour", () => {
describe("when user not have access", () => {
it("should render disabled button with reason text", async () => {
const { container, getByTestId } = render(
<DeleteButton recordItemId="1">Delete</DeleteButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async ({ params, action }) => {
if (action === "delete" && params?.id === "1") {
return {
can: false,
reason: "Access Denied",
};
}
return {
can: true,
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(
getByTestId(RefineButtonTestIds.DeleteButton).closest(
"button",
),
).toBeDisabled(),
);
await waitFor(() =>
expect(
getByTestId(RefineButtonTestIds.DeleteButton)
.closest("button")
?.getAttribute("title"),
).toBe("Access Denied"),
);
});
});
describe("when user have access", () => {
it("should render enabled button", async () => {
const { container, getByTestId } = render(
<DeleteButton recordItemId="2">Delete</DeleteButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async ({ params, action }) => {
if (action === "delete" && params?.id === "1") {
return {
can: false,
};
}
return {
can: true,
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(
getByTestId(RefineButtonTestIds.DeleteButton).closest(
"button",
),
).not.toBeDisabled(),
);
});
});
});
describe("when hideIfUnauthorized is true", () => {
it("should not render button", async () => {
const { container, queryByText } = render(
<DeleteButton>Delete</DeleteButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({ can: false }),
options: {
buttons: {
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("Delete")).not.toBeInTheDocument();
});
});
describe("when access control is disabled explicitly", () => {
it("should render enabled button", async () => {
const { container, getByTestId } = render(
<DeleteButton>Delete</DeleteButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({ can: false }),
options: {
buttons: {
enableAccessControl: false,
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(
getByTestId(RefineButtonTestIds.DeleteButton).closest("button"),
).not.toBeDisabled();
});
});
});
describe("with global config and accessControl prop", () => {
describe("when access control enabled globally", () => {
describe("when access control is disabled with prop", () => {
it("should render enabled button", async () => {
const { container, getByTestId } = render(
<DeleteButton accessControl={{ enabled: false }}>
Delete
</DeleteButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => {
return {
can: false,
};
},
options: {
buttons: {
enableAccessControl: true,
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(
getByTestId(RefineButtonTestIds.DeleteButton).closest(
"button",
),
).not.toBeDisabled(),
);
});
});
describe("when hideIfUnauthorized false globally", () => {
describe("when hideIfUnauthorized enabled with prop", () => {
it("should not render button", async () => {
const { container, queryByText } = render(
<DeleteButton
accessControl={{
hideIfUnauthorized: true,
}}
>
Delete
</DeleteButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({
can: false,
}),
options: {
buttons: {
hideIfUnauthorized: false,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("Delete")).not.toBeInTheDocument();
});
});
});
});
describe("when access control disabled globally", () => {
describe("when access control enabled with prop", () => {
it("should render disabled button with reason text", async () => {
const { container, getByTestId } = render(
<DeleteButton accessControl={{ enabled: true }}>
Delete
</DeleteButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => {
return {
can: false,
reason: "Access Denied",
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(
getByTestId(RefineButtonTestIds.DeleteButton).closest(
"button",
),
).toBeDisabled(),
);
await waitFor(() =>
expect(
getByTestId(RefineButtonTestIds.DeleteButton)
.closest("button")
?.getAttribute("title"),
).toBe("Access Denied"),
);
});
});
});
describe("when hideIfUnauthorized enabled globally", () => {
describe("when hideIfUnauthorized disabled with prop", () => {
it("should render button", async () => {
const { container, queryByText } = render(
<DeleteButton
accessControl={{
hideIfUnauthorized: false,
}}
>
Delete
</DeleteButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({
can: false,
}),
options: {
buttons: {
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("Delete")).toBeInTheDocument();
});
});
});
});
});
it("should render called function successfully if click the button", async () => {
const deleteFunc = jest.fn();
const { getByTestId } = render(
<DeleteButton onClick={() => deleteFunc()} />,
{
wrapper: TestWrapper({}),
},
);
await act(async () => {
fireEvent.click(getByTestId(RefineButtonTestIds.DeleteButton));
});
expect(deleteFunc).toHaveBeenCalledTimes(1);
});
it("should render Popconfirm successfuly", async () => {
const { getByText, getAllByText, getByTestId } = render(
<DeleteButton resourceNameOrRouteName="posts" recordItemId="1" />,
{
wrapper: TestWrapper({}),
},
);
await act(async () => {
fireEvent.click(getByTestId(RefineButtonTestIds.DeleteButton));
});
getByText("Are you sure?");
getByText("Cancel");
getAllByText("Delete");
});
it("should confirm Popconfirm successfuly", async () => {
const deleteOneMock = jest.fn();
const { getByText, getAllByText, getByTestId } = render(
<DeleteButton resourceNameOrRouteName="posts" recordItemId="1" />,
{
wrapper: TestWrapper({
dataProvider: {
...MockJSONServer,
deleteOne: deleteOneMock,
},
}),
},
);
await act(async () => {
fireEvent.click(getByTestId(RefineButtonTestIds.DeleteButton));
});
getByText("Are you sure?");
getByText("Cancel");
const deleteButtons = getAllByText("Delete");
await act(async () => {
fireEvent.click(deleteButtons[1]);
});
expect(deleteOneMock).toBeCalledTimes(1);
});
it("should confirm Popconfirm successfuly with recordItemId", async () => {
const deleteOneMock = jest.fn();
const { getByText, getAllByText, getByTestId } = render(
<DeleteButton
recordItemId="record-id"
resourceNameOrRouteName="posts"
/>,
{
wrapper: TestWrapper({
dataProvider: {
...MockJSONServer,
deleteOne: deleteOneMock,
},
}),
},
);
await act(async () => {
fireEvent.click(getByTestId(RefineButtonTestIds.DeleteButton));
});
getByText("Are you sure?");
getByText("Cancel");
const deleteButtons = getAllByText("Delete");
await act(async () => {
fireEvent.click(deleteButtons[1]);
});
expect(deleteOneMock).toBeCalledWith(
expect.objectContaining({ id: "record-id" }),
);
});
it("should confirm Popconfirm successfuly with onSuccess", async () => {
const deleteOneMock = jest.fn();
const onSuccessMock = jest.fn();
const { getByText, getAllByText, getByTestId } = render(
<Routes>
<Route
path="/:resource/:action/:id"
element={<DeleteButton onSuccess={onSuccessMock} />}
/>
</Routes>,
{
wrapper: TestWrapper({
dataProvider: {
...MockJSONServer,
deleteOne: deleteOneMock,
},
routerInitialEntries: ["/posts/edit/1"],
}),
},
);
await act(async () => {
fireEvent.click(getByTestId(RefineButtonTestIds.DeleteButton));
});
getByText("Are you sure?");
getByText("Cancel");
const deleteButtons = getAllByText("Delete");
await act(async () => {
fireEvent.click(deleteButtons[1]);
});
expect(deleteOneMock).toBeCalledTimes(1);
expect(onSuccessMock).toBeCalledTimes(1);
});
it("should confirm Popconfirm successfuly with onSuccess", async () => {
const deleteOneMock = jest.fn();
const onSuccessMock = jest.fn();
const { getByText, getByTestId } = render(
<Routes>
<Route
path="/:resource/:action/:id"
element={
<DeleteButton
onSuccess={onSuccessMock}
confirmOkText="confirmOkText"
confirmCancelText="confirmCancelText"
confirmTitle="confirmTitle"
/>
}
/>
</Routes>,
{
wrapper: TestWrapper({
dataProvider: {
...MockJSONServer,
deleteOne: deleteOneMock,
},
routerInitialEntries: ["/posts/edit/1"],
}),
},
);
await act(async () => {
fireEvent.click(getByTestId(RefineButtonTestIds.DeleteButton));
});
getByText("confirmTitle");
getByText("confirmOkText");
getByText("confirmCancelText");
await act(async () => {
fireEvent.click(getByText("confirmOkText"));
});
expect(deleteOneMock).toBeCalledTimes(1);
expect(onSuccessMock).toBeCalledTimes(1);
});
it("should render with custom mutationMode", async () => {
const { getByTestId } = render(
<Routes>
<Route
path="/:resource"
element={<DeleteButton mutationMode="pessimistic" />}
/>
</Routes>,
{
wrapper: TestWrapper({
resources: [{ name: "posts" }],
routerInitialEntries: ["/posts"],
}),
},
);
getByTestId(RefineButtonTestIds.DeleteButton);
});
it("should render with custom resource", async () => {
const { getByTestId } = render(
<Routes>
<Route
path="/:resource"
element={<DeleteButton resourceNameOrRouteName="categories" />}
/>
</Routes>,
{
wrapper: TestWrapper({
resources: [{ name: "posts" }, { name: "categories" }],
routerInitialEntries: ["/posts"],
}),
},
);
getByTestId(RefineButtonTestIds.DeleteButton);
});
it("should render with resourceNameOrRouteName", async () => {
const { getByTestId } = render(
<Routes>
<Route
path="/:resource"
element={<DeleteButton resourceNameOrRouteName="users" />}
/>
</Routes>,
{
wrapper: TestWrapper({
resources: [{ name: "posts" }, { name: "users" }],
routerInitialEntries: ["/posts"],
}),
},
);
getByTestId(RefineButtonTestIds.DeleteButton);
});
});
};

View File

@@ -0,0 +1,393 @@
import React from "react";
import {
type RefineEditButtonProps,
RefineButtonTestIds,
} from "@refinedev/ui-types";
import { act, fireEvent, render, TestWrapper, waitFor } from "@test";
import { Route, Routes } from "react-router-dom";
export const buttonEditTests = (
EditButton: React.ComponentType<RefineEditButtonProps<any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Edit Button", () => {
const edit = jest.fn();
beforeAll(() => {
jest.spyOn(console, "warn").mockImplementation(jest.fn());
});
it("should render button successfuly", async () => {
const { container, getByText } = render(<EditButton />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
expect(getByText("Edit").closest("button")).not.toBeDisabled();
});
it("should have the correct test-id", async () => {
const { queryByTestId } = render(<EditButton />, {
wrapper: TestWrapper({}),
});
expect(queryByTestId(RefineButtonTestIds.EditButton)).toBeTruthy();
});
it("should render text by children", async () => {
const { container, getByText } = render(<EditButton>refine</EditButton>, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
getByText("refine");
});
it("should render without text show only icon", async () => {
const { container, queryByText } = render(<EditButton hideText />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
expect(queryByText("Edit")).not.toBeInTheDocument();
});
describe("access control", () => {
describe("with global access control only", () => {
describe("with default behaviour", () => {
describe("when user not have access", () => {
it("should render disabled button with reason text", async () => {
const { container, getByText } = render(
<EditButton recordItemId="1">Edit</EditButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async ({ params, action }) => {
if (action === "edit" && params?.id === "1") {
return {
can: false,
reason: "Access Denied",
};
}
return {
can: true,
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("Edit").closest("button")).toBeDisabled(),
);
waitFor(() =>
expect(
getByText("Edit").closest("button")?.getAttribute("title"),
).toBe("Access Denied"),
);
});
});
describe("when user have access", () => {
it("should render enabled button", async () => {
const { container, getByText } = render(
<EditButton recordItemId="2">Edit</EditButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async ({ params, action }) => {
if (action === "edit" && params?.id === "1") {
return {
can: false,
};
}
return {
can: true,
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("Edit").closest("button")).not.toBeDisabled(),
);
});
});
});
describe("when hideIfUnauthorized is true", () => {
it("should not render button", async () => {
const { container, queryByText } = render(
<EditButton>Edit</EditButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({ can: false }),
options: {
buttons: {
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("Edit")).not.toBeInTheDocument();
});
});
describe("when access control is disabled explicitly", () => {
it("should render enabled button", async () => {
const { container, getByText } = render(
<EditButton>Edit</EditButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({ can: false }),
options: {
buttons: {
enableAccessControl: false,
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(getByText("Edit").closest("button")).not.toBeDisabled();
});
});
});
describe("with global config and accessControl prop", () => {
describe("when access control enabled globally", () => {
describe("when access control is disabled with prop", () => {
it("should render enabled button", async () => {
const { container, getByText } = render(
<EditButton accessControl={{ enabled: false }}>
Edit
</EditButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => {
return {
can: false,
};
},
options: {
buttons: {
enableAccessControl: true,
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("Edit").closest("button")).not.toBeDisabled(),
);
});
});
describe("when hideIfUnauthorized false globally", () => {
describe("when hideIfUnauthorized enabled with prop", () => {
it("should not render button", async () => {
const { container, queryByText } = render(
<EditButton
accessControl={{
hideIfUnauthorized: true,
}}
>
Edit
</EditButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({
can: false,
}),
options: {
buttons: {
hideIfUnauthorized: false,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("Edit")).not.toBeInTheDocument();
});
});
});
});
describe("when access control disabled globally", () => {
describe("when access control enabled with prop", () => {
it("should render disabled button with reason text", async () => {
const { container, getByText } = render(
<EditButton accessControl={{ enabled: true }}>Edit</EditButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => {
return {
can: false,
reason: "Access Denied",
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("Edit").closest("button")).toBeDisabled(),
);
waitFor(() =>
expect(
getByText("Edit").closest("button")?.getAttribute("title"),
).toBe("Access Denied"),
);
});
});
});
describe("when hideIfUnauthorized enabled globally", () => {
describe("when hideIfUnauthorized disabled with prop", () => {
it("should render button", async () => {
const { container, queryByText } = render(
<EditButton
accessControl={{
hideIfUnauthorized: false,
}}
>
Edit
</EditButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({
can: false,
}),
options: {
buttons: {
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("Edit")).toBeInTheDocument();
});
});
});
});
});
it("should render called function successfully if click the button", async () => {
const { getByText } = render(<EditButton onClick={() => edit()} />, {
wrapper: TestWrapper({}),
});
await act(async () => {
fireEvent.click(getByText("Edit"));
});
expect(edit).toHaveBeenCalledTimes(1);
});
it("should custom resource and recordItemId redirect show route called function successfully if click the button", async () => {
const { getByText } = render(
<Routes>
<Route
path="/:resource"
element={
<EditButton
resourceNameOrRouteName="categories"
recordItemId="1"
/>
}
/>
</Routes>,
{
wrapper: TestWrapper({
resources: [{ name: "posts" }, { name: "categories" }],
routerInitialEntries: ["/posts"],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Edit"));
});
expect(window.location.pathname).toBe("/categories/edit/1");
});
it("should redirect with custom route called function successfully if click the button", async () => {
const { getByText } = render(
<Routes>
<Route
path="/:resource"
element={
<EditButton
resourceNameOrRouteName="custom-route-posts"
recordItemId={1}
/>
}
/>
</Routes>,
{
wrapper: TestWrapper({
resources: [
{
name: "posts",
meta: { route: "custom-route-posts" },
},
{ name: "posts" },
],
routerInitialEntries: ["/posts"],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Edit"));
});
expect(window.location.pathname).toBe("/custom-route-posts/edit/1");
});
});
};

View File

@@ -0,0 +1,55 @@
import React from "react";
import {
type RefineExportButtonProps,
RefineButtonTestIds,
} from "@refinedev/ui-types";
import { act, render, TestWrapper } from "@test";
export const buttonExportTests = (
ExportButton: React.ComponentType<RefineExportButtonProps<any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Export Button", () => {
beforeAll(() => {
jest.spyOn(console, "warn").mockImplementation(jest.fn());
});
it("should render button successfuly", async () => {
const { container, getByText } = render(<ExportButton />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
getByText("Export");
});
it("should have the correct test-id", async () => {
const { queryByTestId } = render(<ExportButton />, {
wrapper: TestWrapper({}),
});
expect(queryByTestId(RefineButtonTestIds.ExportButton)).toBeTruthy();
});
it("should render text by children", async () => {
const { container, getByText } = render(
<ExportButton>refine</ExportButton>,
{
wrapper: TestWrapper({}),
},
);
expect(container).toBeTruthy();
getByText("refine");
});
it("should render without text show only icon", async () => {
const { container, queryByText } = render(<ExportButton hideText />);
expect(container).toBeTruthy();
expect(queryByText("Export")).not.toBeInTheDocument();
});
});
};

View File

@@ -0,0 +1,60 @@
import React from "react";
import {
type RefineImportButtonProps,
RefineButtonTestIds,
} from "@refinedev/ui-types";
import { act, render, TestWrapper } from "@test";
export const buttonImportTests = (
ImportButton: React.ComponentType<RefineImportButtonProps<any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Import Button", () => {
const parseMock = jest.fn();
beforeAll(() => {
jest.mock("papaparse", () => {
return {
parse: jest.fn(() => parseMock()),
};
});
});
it("should render button successfuly", async () => {
const { container, getByText } = render(<ImportButton />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
getByText("Import");
});
it("should have the correct test-id", async () => {
const { queryByTestId } = render(<ImportButton />, {
wrapper: TestWrapper({}),
});
expect(queryByTestId(RefineButtonTestIds.ImportButton)).toBeTruthy();
});
it("should render text by children", async () => {
const { container, getByText } = render(
<ImportButton>refine</ImportButton>,
{
wrapper: TestWrapper({}),
},
);
expect(container).toBeTruthy();
getByText("refine");
});
it("should render without text show only icon", async () => {
const { container, queryByText } = render(<ImportButton hideText />);
expect(container).toBeTruthy();
expect(queryByText("Import")).not.toBeInTheDocument();
});
});
};

View File

@@ -0,0 +1,10 @@
export { buttonCloneTests } from "./clone";
export { buttonCreateTests } from "./create";
export { buttonDeleteTests } from "./delete";
export { buttonEditTests } from "./edit";
export { buttonExportTests } from "./export";
export { buttonImportTests } from "./import";
export { buttonListTests } from "./list";
export { buttonRefreshTests } from "./refresh";
export { buttonSaveTests } from "./save";
export { buttonShowTests } from "./show";

View File

@@ -0,0 +1,394 @@
import React from "react";
import {
type RefineListButtonProps,
RefineButtonTestIds,
} from "@refinedev/ui-types";
import { act, fireEvent, render, TestWrapper, waitFor } from "@test";
import { Route, Routes } from "react-router-dom";
export const buttonListTests = (
ListButton: React.ComponentType<RefineListButtonProps<any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / List Button", () => {
const list = jest.fn();
it("should render button successfuly", async () => {
const { container, getByText } = render(<ListButton>List</ListButton>, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
expect(getByText("List").closest("button")).not.toBeDisabled();
});
it("should have the correct test-id", async () => {
const { queryByTestId } = render(<ListButton />, {
wrapper: TestWrapper({}),
});
expect(queryByTestId(RefineButtonTestIds.ListButton)).toBeTruthy();
});
it("should render text by children", async () => {
const { container, getByText } = render(<ListButton>refine</ListButton>, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
getByText("refine");
});
it("should render label as children if specified", async () => {
const { container, getByText } = render(
<Routes>
<Route path="/:resource" element={<ListButton />} />
</Routes>,
{
wrapper: TestWrapper({
resources: [{ name: "posts", meta: { label: "test" } }],
routerInitialEntries: ["/posts"],
}),
},
);
expect(container).toBeTruthy();
getByText("Tests");
});
it("should render text by children", async () => {
const { container, getByText } = render(<ListButton>refine</ListButton>, {
wrapper: TestWrapper({
resources: [{ name: "posts" }],
}),
});
expect(container).toBeTruthy();
getByText("refine");
});
it("should render without text show only icon", async () => {
const { container, queryByText } = render(<ListButton hideText />, {
wrapper: TestWrapper({
resources: [{ name: "posts" }],
}),
});
expect(container).toBeTruthy();
expect(queryByText("Posts")).not.toBeInTheDocument();
});
describe("access control", () => {
describe("with global access control only", () => {
describe("with default behaviour", () => {
describe("when user not have access", () => {
it("should render disabled button with reason text", async () => {
const { container, getByText } = render(
<ListButton>List</ListButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async ({ action }) => {
if (action === "list") {
return {
can: false,
reason: "Access Denied",
};
}
return {
can: true,
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("List").closest("button")).toBeDisabled(),
);
waitFor(() =>
expect(
getByText("List").closest("button")?.getAttribute("title"),
).toBe("Access Denied"),
);
});
});
describe("when user have access", () => {
it("should render enabled button", async () => {
const { container, getByText } = render(
<ListButton>List</ListButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async ({ action }) => {
if (action === "list") {
return {
can: false,
};
}
return {
can: true,
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("List").closest("button")).not.toBeDisabled(),
);
});
});
});
describe("when hideIfUnauthorized is true", () => {
it("should not render button", async () => {
const { container, queryByText } = render(
<ListButton>List</ListButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({ can: false }),
options: {
buttons: {
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("List")).not.toBeInTheDocument();
});
});
describe("when access control is disabled explicitly", () => {
it("should render enabled button", async () => {
const { container, getByText } = render(
<ListButton>List</ListButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({ can: false }),
options: {
buttons: {
enableAccessControl: false,
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(getByText("List").closest("button")).not.toBeDisabled();
});
});
});
describe("with global config and accessControl prop", () => {
describe("when access control enabled globally", () => {
describe("when access control is disabled with prop", () => {
it("should render enabled button", async () => {
const { container, getByText } = render(
<ListButton accessControl={{ enabled: false }}>
List
</ListButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => {
return {
can: false,
};
},
options: {
buttons: {
enableAccessControl: true,
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("List").closest("button")).not.toBeDisabled(),
);
});
});
describe("when hideIfUnauthorized false globally", () => {
describe("when hideIfUnauthorized enabled with prop", () => {
it("should not render button", async () => {
const { container, queryByText } = render(
<ListButton
accessControl={{
hideIfUnauthorized: true,
}}
>
List
</ListButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({
can: false,
}),
options: {
buttons: {
hideIfUnauthorized: false,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("List")).not.toBeInTheDocument();
});
});
});
});
describe("when access control disabled globally", () => {
describe("when access control enabled with prop", () => {
it("should render disabled button with reason text", async () => {
const { container, getByText } = render(
<ListButton accessControl={{ enabled: true }}>List</ListButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => {
return {
can: false,
reason: "Access Denied",
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("List").closest("button")).toBeDisabled(),
);
waitFor(() =>
expect(
getByText("List").closest("button")?.getAttribute("title"),
).toBe("Access Denied"),
);
});
});
});
describe("when hideIfUnauthorized enabled globally", () => {
describe("when hideIfUnauthorized disabled with prop", () => {
it("should render button", async () => {
const { container, queryByText } = render(
<ListButton
accessControl={{
hideIfUnauthorized: false,
}}
>
List
</ListButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({
can: false,
}),
options: {
buttons: {
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("List")).toBeInTheDocument();
});
});
});
});
});
it("should render called function successfully if click the button", async () => {
const { getByText } = render(
<ListButton onClick={() => list()} resourceNameOrRouteName="posts" />,
{
wrapper: TestWrapper({
resources: [{ name: "posts" }],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Posts"));
});
expect(list).toHaveBeenCalledTimes(1);
});
it("should redirect with custom route called function successfully if click the button", async () => {
const { getByText } = render(
<Routes>
<Route
path="/:resource"
element={
<ListButton resourceNameOrRouteName="custom-route-posts" />
}
/>
</Routes>,
{
wrapper: TestWrapper({
resources: [
{
name: "posts",
options: { route: "custom-route-posts" },
},
{ name: "posts" },
],
routerInitialEntries: ["/posts"],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Posts"));
});
expect(window.location.pathname).toBe("/custom-route-posts");
});
});
};

View File

@@ -0,0 +1,175 @@
import React from "react";
import {
type RefineRefreshButtonProps,
RefineButtonTestIds,
} from "@refinedev/ui-types";
import { act, fireEvent, render, TestWrapper, waitFor } from "@test";
import { Route, Routes } from "react-router-dom";
import "@refinedev/core";
const invalidateMock = jest.fn();
jest.mock("@refinedev/core", () => ({
__esModule: true,
...jest.requireActual("@refinedev/core"),
useInvalidate: () => {
return invalidateMock;
},
}));
export const buttonRefreshTests = (
RefreshButton: React.ComponentType<RefineRefreshButtonProps<any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Refresh Button", () => {
const refresh = jest.fn();
it("should render button successfuly", async () => {
const { container } = render(<RefreshButton />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
});
it("should have the correct test-id", async () => {
const { queryByTestId } = render(<RefreshButton />, {
wrapper: TestWrapper({}),
});
expect(queryByTestId(RefineButtonTestIds.RefreshButton)).toBeTruthy();
});
it("should render text by children", async () => {
const { container, getByText } = render(
<RefreshButton>refine</RefreshButton>,
{
wrapper: TestWrapper({}),
},
);
expect(container).toBeTruthy();
getByText("refine");
});
it("should render without text show only icon", async () => {
const { container, queryByText } = render(<RefreshButton hideText />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
expect(queryByText("Refresh")).not.toBeInTheDocument();
});
it("should render called function successfully if click the button", async () => {
const { getByText } = render(
<RefreshButton onClick={() => refresh()} />,
{
wrapper: TestWrapper({}),
},
);
await act(async () => {
fireEvent.click(getByText("Refresh"));
});
expect(refresh).toHaveBeenCalledTimes(1);
});
/**
* Previously `useInvalidate` was imported directly inside the UI packages,
* which then can be mocked and tested through jest.
* Now we've switched to `useRefreshButton` from `@refinedev/core`
* which calls `useInvalidate` internally.
* We can't test the internal function calls of `useInvalidate` anymore.
* Extensive tests on the logic of the `useRefreshButton` which powers the `RefreshButton` are already covered in the core package.
*/
xit("should invalidates when button is clicked", async () => {
jest.resetAllMocks();
jest.restoreAllMocks();
const { getByText } = render(
<Routes>
<Route
path="/posts/show/:id"
element={
<RefreshButton dataProviderName="default" resource="posts" />
}
/>
</Routes>,
{
wrapper: TestWrapper({
routerInitialEntries: ["/posts/show/1"],
resources: [
{
name: "posts",
show: "/posts/show/:id",
},
],
}),
},
);
const button = getByText("Refresh");
await act(async () => {
fireEvent.click(button);
});
await waitFor(() => {
expect(invalidateMock).toHaveBeenCalledTimes(1);
expect(invalidateMock).toHaveBeenCalledWith({
id: "1",
invalidates: ["detail"],
dataProviderName: "default",
resource: "posts",
});
});
});
it("should when onClick is not passed, NOT invalidates when button is clicked", async () => {
jest.resetAllMocks();
jest.restoreAllMocks();
const onClickMock = jest.fn();
const { getByText } = render(
<Routes>
<Route
path="/posts/show/:id"
element={
<RefreshButton
onClick={onClickMock}
dataProviderName="default"
resource="posts"
/>
}
/>
</Routes>,
{
wrapper: TestWrapper({
routerInitialEntries: ["/posts/show/1"],
resources: [
{
name: "posts",
show: "/posts/show/:id",
},
],
}),
},
);
const button = getByText("Refresh");
await act(async () => {
fireEvent.click(button);
});
await waitFor(() => {
expect(invalidateMock).toHaveBeenCalledTimes(0);
expect(onClickMock).toHaveBeenCalledTimes(1);
});
});
});
};

View File

@@ -0,0 +1,63 @@
import React from "react";
import {
type RefineSaveButtonProps,
RefineButtonTestIds,
} from "@refinedev/ui-types";
import { act, fireEvent, render, TestWrapper } from "@test";
export const buttonSaveTests = (
SaveButton: React.ComponentType<RefineSaveButtonProps<any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Save Button", () => {
const save = jest.fn();
it("should render button successfuly", async () => {
const { container } = render(<SaveButton />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
});
it("should have the correct test-id", async () => {
const { queryByTestId } = render(<SaveButton />, {
wrapper: TestWrapper({}),
});
expect(queryByTestId(RefineButtonTestIds.SaveButton)).toBeTruthy();
});
it("should render text by children", async () => {
const { container, getByText } = render(<SaveButton>refine</SaveButton>, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
getByText("refine");
});
it("should render without text show only icon", async () => {
const { container, queryByText } = render(<SaveButton hideText />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
expect(queryByText("Save")).not.toBeInTheDocument();
});
it("should render called function successfully if click the button", async () => {
const { getByText } = render(<SaveButton onClick={() => save()} />, {
wrapper: TestWrapper({}),
});
await act(async () => {
fireEvent.click(getByText("Save"));
});
expect(save).toHaveBeenCalledTimes(1);
});
});
};

View File

@@ -0,0 +1,433 @@
import React from "react";
import {
type RefineShowButtonProps,
RefineButtonTestIds,
} from "@refinedev/ui-types";
import { act, fireEvent, render, TestWrapper, waitFor } from "@test";
import { Route, Routes } from "react-router-dom";
export const buttonShowTests = (
ShowButton: React.ComponentType<RefineShowButtonProps<any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Show Button", () => {
const show = jest.fn();
beforeAll(() => {
jest.spyOn(console, "warn").mockImplementation(jest.fn());
});
it("should render button successfuly", async () => {
const { container, getByText } = render(<ShowButton />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
expect(getByText("Show").closest("button")).not.toBeDisabled();
});
it("should have the correct test-id", async () => {
const { queryByTestId } = render(<ShowButton />, {
wrapper: TestWrapper({}),
});
expect(queryByTestId(RefineButtonTestIds.ShowButton)).toBeTruthy();
});
it("should render text by children", async () => {
const { container, getByText } = render(<ShowButton>refine</ShowButton>, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
getByText("refine");
});
it("should render without text show only icon", async () => {
const { container, queryByText } = render(<ShowButton hideText />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
expect(queryByText("Show")).not.toBeInTheDocument();
});
describe("access control", () => {
describe("with global access control only", () => {
describe("with default behaviour", () => {
describe("when user not have access", () => {
it("should render disabled button with reason text", async () => {
const { container, getByText } = render(
<ShowButton recordItemId="1">Show</ShowButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async ({ params, action }) => {
if (action === "show" && params?.id === "1") {
return {
can: false,
reason: "Access Denied",
};
}
return {
can: true,
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("Show").closest("button")).toBeDisabled(),
);
waitFor(() =>
expect(
getByText("Show").closest("button")?.getAttribute("title"),
).toBe("Access Denied"),
);
});
});
describe("when user have access", () => {
it("should render enabled button", async () => {
const { container, getByText } = render(
<ShowButton recordItemId="2">Show</ShowButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async ({ params, action }) => {
if (action === "show" && params?.id === "1") {
return {
can: false,
};
}
return {
can: true,
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("Show").closest("button")).not.toBeDisabled(),
);
});
});
});
describe("when hideIfUnauthorized is true", () => {
it("should not render button", async () => {
const { container, queryByText } = render(
<ShowButton>Show</ShowButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({ can: false }),
options: {
buttons: {
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("Show")).not.toBeInTheDocument();
});
});
describe("when access control is disabled explicitly", () => {
it("should render enabled button", async () => {
const { container, getByText } = render(
<ShowButton>Show</ShowButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({ can: false }),
options: {
buttons: {
enableAccessControl: false,
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(getByText("Show").closest("button")).not.toBeDisabled();
});
});
});
describe("with global config and accessControl prop", () => {
describe("when access control enabled globally", () => {
describe("when access control is disabled with prop", () => {
it("should render enabled button", async () => {
const { container, getByText } = render(
<ShowButton accessControl={{ enabled: false }}>
Show
</ShowButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => {
return {
can: false,
};
},
options: {
buttons: {
enableAccessControl: true,
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("Show").closest("button")).not.toBeDisabled(),
);
});
});
describe("when hideIfUnauthorized false globally", () => {
describe("when hideIfUnauthorized enabled with prop", () => {
it("should not render button", async () => {
const { container, queryByText } = render(
<ShowButton
accessControl={{
hideIfUnauthorized: true,
}}
>
Show
</ShowButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({
can: false,
}),
options: {
buttons: {
hideIfUnauthorized: false,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("Show")).not.toBeInTheDocument();
});
});
});
});
describe("when access control disabled globally", () => {
describe("when access control enabled with prop", () => {
it("should render disabled button with reason text", async () => {
const { container, getByText } = render(
<ShowButton accessControl={{ enabled: true }}>Show</ShowButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => {
return {
can: false,
reason: "Access Denied",
};
},
},
}),
},
);
expect(container).toBeTruthy();
await waitFor(() =>
expect(getByText("Show").closest("button")).toBeDisabled(),
);
waitFor(() =>
expect(
getByText("Show").closest("button")?.getAttribute("title"),
).toBe("Access Denied"),
);
});
});
});
describe("when hideIfUnauthorized enabled globally", () => {
describe("when hideIfUnauthorized disabled with prop", () => {
it("should render button", async () => {
const { container, queryByText } = render(
<ShowButton
accessControl={{
hideIfUnauthorized: false,
}}
>
Show
</ShowButton>,
{
wrapper: TestWrapper({
accessControlProvider: {
can: async () => ({
can: false,
}),
options: {
buttons: {
hideIfUnauthorized: true,
},
},
},
}),
},
);
expect(container).toBeTruthy();
expect(queryByText("Show")).toBeInTheDocument();
});
});
});
});
});
it("should render called function successfully if click the button", async () => {
const { getByText } = render(<ShowButton onClick={() => show()} />, {
wrapper: TestWrapper({}),
});
await act(async () => {
fireEvent.click(getByText("Show"));
});
expect(show).toHaveBeenCalledTimes(1);
});
it("should create page redirect show route called function successfully if click the button", async () => {
const { getByText } = render(
<Routes>
<Route path="/:resource" element={<ShowButton recordItemId="1" />} />
</Routes>,
{
wrapper: TestWrapper({
resources: [{ name: "posts" }],
routerInitialEntries: ["/posts"],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Show"));
});
expect(window.location.pathname).toBe("/posts/show/1");
});
it("should show page redirect show route called function successfully if click the button", async () => {
const { getByText } = render(
<Routes>
<Route path="/:resource/:action/:id" element={<ShowButton />} />
</Routes>,
{
wrapper: TestWrapper({
resources: [{ name: "posts" }],
routerInitialEntries: ["/posts/show/1"],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Show"));
});
expect(window.location.pathname).toBe("/posts/show/1");
});
it("should custom resource and recordItemId redirect show route called function successfully if click the button", async () => {
const { getByText } = render(
<Routes>
<Route
path="/:resource"
element={
<ShowButton
resourceNameOrRouteName="categories"
recordItemId="1"
/>
}
/>
</Routes>,
{
wrapper: TestWrapper({
resources: [{ name: "posts" }, { name: "categories" }],
routerInitialEntries: ["/posts"],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Show"));
});
expect(window.location.pathname).toBe("/categories/show/1");
});
it("should redirect with custom route called function successfully if click the button", async () => {
const { getByText } = render(
<Routes>
<Route
path="/:resource"
element={
<ShowButton
resourceNameOrRouteName="custom-route-posts"
recordItemId="1"
/>
}
/>
</Routes>,
{
wrapper: TestWrapper({
resources: [
{
name: "posts",
meta: { route: "custom-route-posts" },
},
{ name: "posts" },
],
routerInitialEntries: ["/posts"],
}),
},
);
await act(async () => {
fireEvent.click(getByText("Show"));
});
expect(window.location.pathname).toBe("/custom-route-posts/show/1");
});
});
};

View File

@@ -0,0 +1,117 @@
import React from "react";
import { Route, Routes } from "react-router-dom";
import {
type RefineCrudCreateProps,
RefineButtonTestIds,
} from "@refinedev/ui-types";
import { type ITestWrapperProps, render, TestWrapper } from "@test";
const renderCreate = (
create: React.ReactNode,
wrapperProps?: ITestWrapperProps,
) => {
return render(
<Routes>
<Route path="/:resource/:action" element={create} />
</Routes>,
{
wrapper: TestWrapper(
wrapperProps
? wrapperProps
: {
routerInitialEntries: ["/posts/create"],
},
),
},
);
};
export const crudCreateTests = (
Create: React.ComponentType<
RefineCrudCreateProps<any, any, any, any, any, any, {}>
>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / CRUD Create", () => {
beforeAll(() => {
jest.spyOn(console, "warn").mockImplementation(jest.fn());
});
it("should render children", async () => {
const { getByText } = renderCreate(<Create>Something</Create>);
getByText("Something");
});
it("should render default save button successfuly", async () => {
const { queryByTestId } = renderCreate(<Create />);
expect(queryByTestId(RefineButtonTestIds.SaveButton)).not.toBeNull();
});
it("should render optional button with actionButtons prop", async () => {
const { container, getByText } = renderCreate(
<Create footerButtons={<button>Optional Button</button>} />,
);
expect(container.querySelector("button")).toBeTruthy();
getByText("Optional Button");
});
it("should render default title successfuly", async () => {
const { getByText } = renderCreate(<Create />);
getByText("Create Post");
});
it("should not render title when is false", async () => {
const { queryByText } = renderCreate(<Create title={false} />);
const text = queryByText("Create Post");
expect(text).not.toBeInTheDocument();
});
it("should render with label instead of resource name successfully", async () => {
const { getByText } = renderCreate(<Create />, {
resources: [
{
name: "posts",
meta: { route: "posts", label: "test label" },
},
],
routerInitialEntries: ["/posts/create"],
});
getByText("Create Test label");
});
it("should render optional title with title prop", async () => {
const { getByText } = renderCreate(<Create title="New Title" />);
getByText("New Title");
});
it("should render optional resource with resource prop", async () => {
const { queryByText } = renderCreate(<Create resource="posts" />, {
routerInitialEntries: ["/custom"],
});
queryByText("Create Post");
});
it("should render tags", async () => {
const { getByText } = render(
<Routes>
<Route path="/:resource/:action/:id" element={<Create />} />
</Routes>,
{
wrapper: TestWrapper({
routerInitialEntries: ["/posts/clone/1"],
}),
},
);
getByText("Create Post");
});
});
};

View File

@@ -0,0 +1,144 @@
import React from "react";
import { Route, Routes } from "react-router-dom";
import {
type RefineCrudEditProps,
RefineButtonTestIds,
} from "@refinedev/ui-types";
import type { AccessControlProvider } from "@refinedev/core";
import { type ITestWrapperProps, render, TestWrapper } from "@test";
const renderEdit = (
edit: React.ReactNode,
accessControlProvider?: AccessControlProvider,
wrapperProps?: ITestWrapperProps,
) => {
return render(
<Routes>
<Route path="/:resource/edit/:id" element={edit} />
</Routes>,
{
wrapper: TestWrapper(
wrapperProps
? wrapperProps
: {
routerInitialEntries: ["/posts/edit/1"],
accessControlProvider,
},
),
},
);
};
export const crudEditTests = (
Edit: React.ComponentType<
RefineCrudEditProps<any, any, any, any, any, any, any, {}, any, any>
>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / CRUD Edit", () => {
beforeAll(() => {
jest.spyOn(console, "warn").mockImplementation(jest.fn());
});
it("should render children", async () => {
const { getByText } = renderEdit(<Edit>Something</Edit>);
getByText("Something");
});
it("should render default list button successfuly", async () => {
const { queryByTestId } = renderEdit(
<Edit
headerButtons={({ defaultButtons, listButtonProps }) => {
expect(listButtonProps).toBeDefined();
return <>{defaultButtons}</>;
}}
/>,
);
expect(queryByTestId(RefineButtonTestIds.ListButton)).not.toBeNull();
});
it("should render default save and delete buttons successfuly", async () => {
const { container, getByText } = renderEdit(
<Edit
canDelete
footerButtons={({
defaultButtons,
saveButtonProps,
deleteButtonProps,
}) => {
expect(saveButtonProps).toBeDefined();
expect(deleteButtonProps).toBeDefined();
return <>{defaultButtons}</>;
}}
/>,
);
expect(container.querySelector("button")).toBeTruthy();
getByText("Save");
getByText("Delete");
});
it("should render optional buttons with actionButtons prop", async () => {
const { getByText } = renderEdit(
<Edit
footerButtons={
<>
<button>New Save Button</button>
<button>New Delete Button</button>
</>
}
/>,
);
getByText("New Save Button");
getByText("New Delete Button");
});
it("should render default title successfuly", async () => {
const { getByText } = renderEdit(<Edit />);
getByText("Edit Post");
});
it("should not render title when is false", async () => {
const { queryByText } = renderEdit(<Edit title={false} />);
const text = queryByText("Edit Post");
expect(text).not.toBeInTheDocument();
});
it("should render custom title successfuly", async () => {
const { getByText } = renderEdit(<Edit title="Custom Title" />);
getByText("Custom Title");
});
it("should render optional recordItemId with resource prop", async () => {
const { getByText } = renderEdit(<Edit recordItemId="1" />);
getByText("Edit Post");
});
it("should render delete button ", async () => {
const { getByText, queryByTestId } = renderEdit(
<Edit
footerButtons={({ defaultButtons, deleteButtonProps }) => {
expect(deleteButtonProps).toBeDefined();
return <>{defaultButtons}</>;
}}
/>,
undefined,
{
resources: [{ name: "posts", canDelete: true }],
routerInitialEntries: ["/posts/edit/1"],
},
);
expect(queryByTestId(RefineButtonTestIds.DeleteButton)).not.toBeNull();
getByText("Edit Post");
});
});
};

View File

@@ -0,0 +1,4 @@
export * from "./list";
export * from "./create";
export * from "./edit";
export * from "./show";

View File

@@ -0,0 +1,229 @@
import React from "react";
import type { AccessControlProvider } from "@refinedev/core";
import { Route, Routes } from "react-router-dom";
import {
type RefineCrudListProps,
RefineButtonTestIds,
} from "@refinedev/ui-types";
import {
act,
type ITestWrapperProps,
render,
TestWrapper,
waitFor,
} from "@test";
const renderList = (
list: React.ReactNode,
accessControlProvider?: AccessControlProvider,
wrapperProps?: ITestWrapperProps,
) => {
return render(
<Routes>
<Route path="/:resource" element={list} />
</Routes>,
{
wrapper: TestWrapper(
wrapperProps
? wrapperProps
: {
routerInitialEntries: ["/posts"],
accessControlProvider,
},
),
},
);
};
export const crudListTests = (
List: React.ComponentType<RefineCrudListProps<any, any, any, any, any, {}>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / CRUD List", () => {
beforeAll(() => {
jest.spyOn(console, "warn").mockImplementation(jest.fn());
});
it("should render children", async () => {
const { getByText } = renderList(
<List key="posts">
<div>No Data</div>
</List>,
);
getByText("No Data");
});
it("should render optional title with title prop", async () => {
const { getByText } = renderList(<List title="New Title" />);
getByText("New Title");
});
it("should not render title when is false", async () => {
const { queryByText } = renderList(<List title={false} />, undefined, {
resources: [
{
name: "posts",
meta: { route: "posts" },
},
],
routerInitialEntries: ["/posts"],
});
const text = queryByText("Posts");
expect(text).not.toBeInTheDocument();
});
it("should render with label instead of resource name successfully", async () => {
const { getByText } = renderList(<List />, undefined, {
resources: [
{
name: "posts",
meta: { route: "posts", label: "test" },
},
],
routerInitialEntries: ["/posts"],
});
getByText("Tests");
});
it("should render create button", async () => {
const { queryByTestId } = renderList(
<List
headerButtons={({ defaultButtons, createButtonProps }) => {
expect(createButtonProps).toBeDefined();
return <>{defaultButtons}</>;
}}
/>,
undefined,
{
resources: [
{
name: "posts",
create: () => null,
},
],
routerInitialEntries: ["/posts"],
},
);
expect(queryByTestId(RefineButtonTestIds.CreateButton)).not.toBeNull();
});
it("should not render create button on resource#canCreate=false", async () => {
const { queryByTestId } = renderList(
<List
headerButtons={({ defaultButtons, createButtonProps }) => {
expect(createButtonProps).not.toBeDefined();
return <>{defaultButtons}</>;
}}
/>,
undefined,
{
resources: [
{
name: "posts",
canCreate: false,
},
],
routerInitialEntries: ["/posts"],
},
);
expect(queryByTestId(RefineButtonTestIds.CreateButton)).toBeNull();
});
it("should render create button on resource#canCreate=false & props#createButtonProps!=null", async () => {
const { getByText, queryByTestId } = renderList(
<List
createButtonProps={{ "aria-label": "Create Button" }}
headerButtons={({ defaultButtons, createButtonProps }) => {
expect(createButtonProps).toBeDefined();
return <>{defaultButtons}</>;
}}
/>,
undefined,
{ routerInitialEntries: ["/posts"] },
);
expect(queryByTestId(RefineButtonTestIds.CreateButton)).not.toBeNull();
getByText("Posts");
});
it("should not render create button on resource#canCreate=true & props#canCreate=false", async () => {
const { queryByTestId } = renderList(
<List
canCreate={false}
headerButtons={({ defaultButtons, createButtonProps }) => {
expect(createButtonProps).toBeUndefined();
return <>{defaultButtons}</>;
}}
/>,
undefined,
{
resources: [
{
name: "posts",
create: () => null,
},
],
routerInitialEntries: ["/posts"],
},
);
expect(queryByTestId(RefineButtonTestIds.CreateButton)).toBeNull();
});
it("should render create button on resource#canCreate=false & props#canCreate=true", async () => {
const { queryByTestId } = renderList(
<List
canCreate={true}
headerButtons={({ defaultButtons, createButtonProps }) => {
expect(createButtonProps).toBeDefined();
return <>{defaultButtons}</>;
}}
/>,
undefined,
{
resources: [
{
name: "posts",
},
],
routerInitialEntries: ["/posts"],
},
);
expect(queryByTestId(RefineButtonTestIds.CreateButton)).not.toBeNull();
});
it("should render disabled create button if user doesn't have permission", async () => {
const { queryByTestId } = renderList(
<List
canCreate={true}
headerButtons={({ defaultButtons, createButtonProps }) => {
expect(createButtonProps).toBeDefined();
return <>{defaultButtons}</>;
}}
/>,
{
can: ({ action }) => {
switch (action) {
case "create":
return Promise.resolve({ can: false });
default:
return Promise.resolve({ can: false });
}
},
},
);
await waitFor(() =>
expect(queryByTestId(RefineButtonTestIds.CreateButton)).toBeDisabled(),
);
});
});
};

View File

@@ -0,0 +1,120 @@
import React from "react";
import { Route, Routes } from "react-router-dom";
import {
type RefineCrudShowProps,
RefineButtonTestIds,
} from "@refinedev/ui-types";
import type { AccessControlProvider } from "@refinedev/core";
import { type ITestWrapperProps, render, TestWrapper } from "@test";
const renderShow = (
show: React.ReactNode,
accessControlProvider?: AccessControlProvider,
wrapperProps?: ITestWrapperProps,
) => {
return render(
<Routes>
<Route path="/:resource/:action/:id" element={show} />
</Routes>,
{
wrapper: TestWrapper(
wrapperProps
? wrapperProps
: {
routerInitialEntries: ["/posts/show/1"],
accessControlProvider,
},
),
},
);
};
export const crudShowTests = (
Show: React.ComponentType<
RefineCrudShowProps<any, any, any, any, any, {}, any, any, any, any>
>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / CRUD Show", () => {
beforeAll(() => {
jest.spyOn(console, "warn").mockImplementation(jest.fn());
});
it("should render children", async () => {
const { getByText } = renderShow(<Show>Something</Show>);
getByText("Something");
});
it("should render default edit and delete buttons successfuly", async () => {
const { queryByTestId } = renderShow(
<Show
canEdit
canDelete
headerButtons={({
defaultButtons,
editButtonProps,
deleteButtonProps,
}) => {
expect(editButtonProps).toBeDefined();
expect(deleteButtonProps).toBeDefined();
return <>{defaultButtons}</>;
}}
/>,
);
expect(queryByTestId(RefineButtonTestIds.EditButton)).not.toBeNull();
expect(queryByTestId(RefineButtonTestIds.DeleteButton)).not.toBeNull();
});
it("should render optional buttons with actionButtons prop", async () => {
const { findByText } = renderShow(
<Show
headerButtons={
<>
<button>New Save Button</button>
<button>New Delete Button</button>
</>
}
/>,
);
await findByText("New Save Button");
await findByText("New Delete Button");
});
it("should render default title successfuly", async () => {
const { getByText } = renderShow(<Show />);
getByText("Show Post");
});
it("should not render title when is false", async () => {
const { queryByText } = renderShow(<Show title={false} />);
const text = queryByText("Show Post");
expect(text).not.toBeInTheDocument();
});
it("should render optional title with title prop", async () => {
const { getByText } = renderShow(<Show title="Test Title" />);
getByText("Test Title");
});
it("should render optional resource with resource prop", async () => {
const { getByText } = render(
<Routes>
<Route path="/:resource" element={<Show resource="posts" />} />
</Routes>,
{
wrapper: TestWrapper({
routerInitialEntries: ["/custom"],
}),
},
);
getByText("Show Post");
});
});
};

View File

@@ -0,0 +1,37 @@
import React from "react";
import type { RefineFieldBooleanProps } from "@refinedev/ui-types";
import { act, fireEvent, render } from "@test";
export const fieldBooleanTests = (
BooleanField: React.ComponentType<RefineFieldBooleanProps<unknown, any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Boolean Field", () => {
xit("should use prop for custom text", async () => {
const baseDom = render(
<BooleanField value={true} valueLabelTrue="test" />,
);
const booleanField = baseDom.getByTestId("custom-field");
act(() => {
fireEvent.mouseOver(booleanField);
});
expect(baseDom.getByLabelText("test")).toBeInTheDocument();
});
it("renders value with prop for custom icon", () => {
const { getByTestId } = render(
<div data-testid="custom-field">
<BooleanField
value={true}
trueIcon={<div data-testid="icon-custom-element" />}
/>
</div>,
);
expect(getByTestId("icon-custom-element")).toBeTruthy();
});
});
};

View File

@@ -0,0 +1,64 @@
import React from "react";
import type { ConfigType } from "dayjs";
import type { RefineFieldDateProps } from "@refinedev/ui-types";
import { render } from "@test";
import "dayjs/locale/tr";
export const fieldDateTests = (
DateField: React.ComponentType<RefineFieldDateProps<ConfigType, any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Date Field", () => {
it("renders date with default format", () => {
const { getByText } = render(
<DateField value={new Date("2021-05-20")} />,
);
getByText("05/20/2021");
});
it("renders date with given format", () => {
const { getByText } = render(
<DateField value={new Date("2021-05-20")} format="DD/MM/YYYY" />,
);
getByText("20/05/2021");
});
it("renders date with given LocalizedFormat", () => {
const { getByText, rerender } = render(
<DateField value={new Date("2021-05-20")} format="l" locales="tr" />,
);
getByText("20.5.2021");
rerender(<DateField value={new Date("2021-05-20")} format="l" />);
getByText("5/20/2021");
});
it("renders empty with given null", () => {
const { getByTestId } = render(
<DateField value={null} data-testid="date-field" />,
);
expect(getByTestId("date-field").textContent).toBe("");
});
it("renders empty with given undefined", () => {
const { getByTestId } = render(
<DateField value={undefined} data-testid="date-field" />,
);
expect(getByTestId("date-field").textContent).toBe("");
});
it("renders invalid date with given incorrect date", () => {
const { getByText } = render(<DateField value={new Date("test")} />);
getByText("Invalid Date");
});
});
};

View File

@@ -0,0 +1,19 @@
import React, { type ReactNode } from "react";
import type { RefineFieldEmailProps } from "@refinedev/ui-types";
import { render } from "@test";
export const fieldEmailTests = (
EmailField: React.ComponentType<RefineFieldEmailProps<ReactNode, any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Email Field", () => {
it("renders email with mailto href", () => {
const { getByText } = render(<EmailField value="test@test.com" />);
expect(getByText("test@test.com")).toHaveProperty(
"href",
"mailto:test@test.com",
);
});
});
};

View File

@@ -0,0 +1,33 @@
import React from "react";
import type { RefineFieldFileProps } from "@refinedev/ui-types";
import { render } from "@test";
export const fieldFileTests = (
FileField: React.ComponentType<RefineFieldFileProps<any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / File Field", () => {
it("renders an anchor with file link", () => {
const value = {
title: "Test",
src: "www.google.com",
};
const { getByTitle } = render(
<FileField src={value.src} title={value.title} />,
);
expect(getByTitle(value.title)).toHaveAttribute("href", value.src);
});
it("renders an anchor with src", () => {
const value = {
src: "www.google.com",
};
const { getByText } = render(<FileField src={value.src} />);
expect(getByText(value.src)).toHaveAttribute("href", value.src);
});
});
};

View File

@@ -0,0 +1,27 @@
import React from "react";
import type { RefineFieldImageProps } from "@refinedev/ui-types";
import { render } from "@test";
export const fieldImageTests = (
ImageField: React.ComponentType<
RefineFieldImageProps<
string | undefined,
any,
{
imageTitle?: string;
}
>
>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Image Field", () => {
it("renders image with correct title", () => {
const imageUrl = "http://placeimg.com/640/480/animals";
const { getAllByRole } = render(
<ImageField value={imageUrl} data-testid="test-image" />,
);
expect(getAllByRole("img")[0]).toHaveProperty("src", imageUrl);
});
});
};

View File

@@ -0,0 +1,10 @@
export { fieldBooleanTests } from "./boolean";
export { fieldDateTests } from "./date";
export { fieldEmailTests } from "./email";
export { fieldFileTests } from "./file";
export { fieldImageTests } from "./image";
export { fieldMarkdownTests } from "./markdown";
export { fieldNumberTests } from "./number";
export { fieldTagTests } from "./tag";
export { fieldTextTests } from "./text";
export { fieldUrlTests } from "./url";

View File

@@ -0,0 +1,32 @@
import React from "react";
import type { RefineFieldMarkdownProps } from "@refinedev/ui-types";
import { render } from "@test";
export const fieldMarkdownTests = (
MarkdownField: React.ComponentType<
RefineFieldMarkdownProps<string | undefined>
>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Markdown Field", () => {
it("renders markDown text with correct value", () => {
const { getByText, container } = render(
<div data-testid="custom-field">
<MarkdownField value={"**MarkdownField Test**"} />
</div>,
);
expect(container.querySelector("strong")).toBeTruthy();
getByText("MarkdownField Test");
});
it("render markdown with undefined value should show empty string", () => {
const { container } = render(
<div data-testid="custom-field">
<MarkdownField value={undefined} />
</div>,
);
expect(container).toBeTruthy();
});
});
};

View File

@@ -0,0 +1,44 @@
import React, { type ReactChild } from "react";
import type { RefineFieldNumberProps } from "@refinedev/ui-types";
import { render } from "@test";
export const fieldNumberTests = (
NumberField: React.ComponentType<RefineFieldNumberProps<ReactChild, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Number Field", () => {
it("renders numbers with given formatting", () => {
const testPrice = 12345.6789;
const options = {
style: "currency",
currency: "EUR",
maximumFractionDigits: 1,
minimumFractionDigits: 1,
};
const locale = "de-DE";
const { getByText } = render(
<NumberField value={testPrice} locale={locale} options={options} />,
);
const formattedTestPrice = testPrice
.toLocaleString(locale, options)
.replace(String.fromCharCode(160), " ");
// node 14 uses non-breaking space resulting in imcompatibility
getByText(formattedTestPrice);
});
it("should render NaN when value is undefined", () => {
const { getByText } = render(<NumberField value={undefined} />);
getByText("NaN");
});
it("should render NaN when value is string", () => {
const { getByText } = render(<NumberField value={"not a number"} />);
getByText("NaN");
});
});
};

View File

@@ -0,0 +1,22 @@
import React, { type ReactNode } from "react";
import type { RefineFieldTagProps } from "@refinedev/ui-types";
import { render } from "@test";
export const fieldTagTests = (
TagField: React.ComponentType<RefineFieldTagProps<ReactNode, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Tag Field", () => {
it("renders boolean values correctly", () => {
const { getByText } = render(<TagField value={true} />);
getByText("true");
});
it("renders boolean values correctly", () => {
const { queryByText } = render(<TagField value={undefined} />);
expect(queryByText("true")).toBeNull();
});
});
};

View File

@@ -0,0 +1,16 @@
import React, { type ReactNode } from "react";
import type { RefineFieldTextProps } from "@refinedev/ui-types";
import { render } from "@test";
export const fieldTextTests = (
TextField: React.ComponentType<RefineFieldTextProps<ReactNode, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Text Field", () => {
it("renders text correctly", () => {
const { getByText } = render(<TextField value="test" />);
getByText("test");
});
});
};

View File

@@ -0,0 +1,40 @@
import React from "react";
import type { RefineFieldUrlProps } from "@refinedev/ui-types";
import { render } from "@test";
export const fieldUrlTests = (
UrlField: React.ComponentType<
RefineFieldUrlProps<string | undefined, any, any>
>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Url Field", () => {
const url = "https://www.google.com/";
it("renders urlField with given value", () => {
const { getByText } = render(<UrlField value={url} />);
const link = getByText(url) as HTMLAnchorElement;
expect(link.href).toBe(url);
expect(link.tagName).toBe("A");
});
it("renders deep fields", () => {
const record = { id: 1, source: { path: url } };
const { getByText } = render(<UrlField value={record.source.path} />);
const link = getByText(url) as HTMLAnchorElement;
expect(link.href).toBe(url);
});
it("renders children element", () => {
const { getByText } = render(
<UrlField value={url}>Make this link</UrlField>,
);
const link = getByText("Make this link") as HTMLAnchorElement;
expect(link.href).toBe(url);
});
});
};

View File

@@ -0,0 +1,7 @@
export * from "./crud";
export * from "./breadcrumb";
export * from "./buttons";
export * from "./fields";
export * from "./layout";
export * from "./pages";
export * from "./autoSaveIndicator";

View File

@@ -0,0 +1,18 @@
import React from "react";
import type { RefineLayoutFooterProps } from "@refinedev/ui-types";
import { act, render, TestWrapper } from "@test";
export const layoutFooterTests = (
FooterElement: React.ComponentType<RefineLayoutFooterProps>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Footer Element", () => {
it("should render successfully", async () => {
const { container } = render(<FooterElement />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
});
});
};

View File

@@ -0,0 +1,71 @@
import React from "react";
import type { AuthProvider } from "@refinedev/core";
import type { RefineLayoutHeaderProps } from "@refinedev/ui-types";
import { render, TestWrapper } from "@test";
const mockLegacyAuthProvider = {
login: () => Promise.resolve(),
logout: () => Promise.resolve(),
checkError: () => Promise.resolve(),
checkAuth: () => Promise.resolve(),
getPermissions: () => Promise.resolve(["admin"]),
getUserIdentity: () =>
Promise.resolve({ name: "username", avatar: "localhost:3000" }),
};
const mockAuthProvider: AuthProvider = {
check: () => Promise.resolve({ authenticated: true }),
login: () => Promise.resolve({ success: true }),
logout: () => Promise.resolve({ success: true }),
onError: () => Promise.resolve({}),
getPermissions: () => Promise.resolve(["admin"]),
getIdentity: () =>
Promise.resolve({ name: "username", avatar: "localhost:3000" }),
};
export const layoutHeaderTests = (
HeaderElement: React.ComponentType<RefineLayoutHeaderProps>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Header Element", () => {
// NOTE : Will be removed in v5
it("should render successfull user name and avatar in header with legacy authProvider", async () => {
const { findByText, queryByRole, queryByAltText } = render(
<HeaderElement />,
{
wrapper: TestWrapper({
legacyAuthProvider: mockLegacyAuthProvider,
}),
},
);
await findByText("username");
const imgByRole = queryByRole("img", { queryFallbacks: true });
const imgByAltText = queryByAltText("username");
expect(imgByRole ?? imgByAltText).toHaveAttribute(
"src",
"localhost:3000",
);
});
it("should render successfull user name and avatar in header with authProvider", async () => {
const { findByText, queryByRole, queryByAltText } = render(
<HeaderElement />,
{
wrapper: TestWrapper({
authProvider: mockAuthProvider,
}),
},
);
await findByText("username");
const imgByRole = queryByRole("img", { queryFallbacks: true });
const imgByAltText = queryByAltText("username");
expect(imgByRole ?? imgByAltText).toHaveAttribute(
"src",
"localhost:3000",
);
});
});
};

View File

@@ -0,0 +1,5 @@
export { layoutFooterTests } from "./footer";
export { layoutHeaderTests } from "./header";
export { layoutLayoutTests } from "./layout";
export { layoutSiderTests } from "./sider";
export { layoutTitleTests } from "./title";

View File

@@ -0,0 +1,43 @@
import React from "react";
import type { RefineLayoutLayoutProps } from "@refinedev/ui-types";
import { act, render, TestWrapper } from "@test";
export const layoutLayoutTests = (
LayoutElement: React.ComponentType<RefineLayoutLayoutProps>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Layout Element", () => {
it("Layout renders sider, header, footer, title, offLayoutArea if given props", async () => {
const customTitleContent = "customTitleContent";
const CustomTitle = () => <p>{customTitleContent}</p>;
const customSiderContent = "customSiderContent";
const CustomSider = () => <p>{customSiderContent}</p>;
const customHeaderContent = "customHeaderContent";
const CustomHeader = () => <p>{customHeaderContent}</p>;
const customFooterContent = "customFooterContent";
const CustomFooter = () => <p>{customFooterContent}</p>;
const customOffLayoutAreaContent = "customOffLayoutAreaContent";
const CustomOffLayoutArea = () => <p>{customOffLayoutAreaContent}</p>;
const { getByText } = render(
<LayoutElement
Title={CustomTitle}
Sider={CustomSider}
Header={CustomHeader}
Footer={CustomFooter}
OffLayoutArea={CustomOffLayoutArea}
/>,
{ wrapper: TestWrapper({}) },
);
expect(getByText(customSiderContent));
expect(getByText(customHeaderContent));
expect(getByText(customFooterContent));
expect(getByText(customOffLayoutAreaContent));
});
});
};

View File

@@ -0,0 +1,307 @@
import React from "react";
import type { RefineThemedLayoutV2SiderProps } from "@refinedev/ui-types";
import { act, mockRouterBindings, render, TestWrapper, waitFor } from "@test";
import type { AuthProvider, LegacyAuthProvider } from "@refinedev/core";
import { Route, Router, Routes } from "react-router-dom";
const mockLegacyAuthProvider: LegacyAuthProvider & { isProvided: boolean } = {
login: () => Promise.resolve(),
logout: () => Promise.resolve(),
checkError: () => Promise.resolve(),
checkAuth: () => Promise.resolve(),
getPermissions: () => Promise.resolve(["admin"]),
getUserIdentity: () => Promise.resolve(),
isProvided: true,
};
const mockAuthProvider: AuthProvider = {
check: () => Promise.resolve({ authenticated: true }),
login: () => Promise.resolve({ success: true }),
logout: () => Promise.resolve({ success: true }),
getPermissions: () => Promise.resolve(["admin"]),
onError: () => Promise.resolve({}),
};
export const layoutSiderTests = (
SiderElement: React.ComponentType<RefineThemedLayoutV2SiderProps>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Sider Element", () => {
it("should render successful", async () => {
const { container } = render(<SiderElement />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
});
it("should render logout menu item successful", async () => {
const { getAllByText } = render(<SiderElement />, {
wrapper: TestWrapper({
legacyAuthProvider: mockLegacyAuthProvider,
}),
});
await waitFor(() =>
expect(getAllByText("Posts").length).toBeGreaterThanOrEqual(1),
);
expect(getAllByText("Logout").length).toBeGreaterThanOrEqual(1);
});
it("should work menu item click", async () => {
const { getAllByText } = render(<SiderElement />, {
wrapper: TestWrapper({
legacyAuthProvider: mockLegacyAuthProvider,
}),
});
await waitFor(() => getAllByText("Posts")[0].click());
await waitFor(() => expect(window.location.pathname).toBe("/posts"));
});
// NOTE : Will be removed in v5
it("should work legacy logout menu item click", async () => {
const logoutMockedAuthProvider = {
...mockLegacyAuthProvider,
logout: jest.fn().mockImplementation(() => Promise.resolve()),
};
const { getAllByText } = render(<SiderElement />, {
wrapper: TestWrapper({
legacyAuthProvider: logoutMockedAuthProvider,
}),
});
await act(async () => {
getAllByText("Logout")[0].click();
});
expect(logoutMockedAuthProvider.logout).toBeCalledTimes(1);
});
it("should work logout menu item click", async () => {
const logoutMockedAuthProvider = {
...mockAuthProvider,
logout: jest
.fn()
.mockImplementation(() => Promise.resolve({ success: true })),
};
const { getAllByText } = render(<SiderElement />, {
wrapper: TestWrapper({
authProvider: logoutMockedAuthProvider,
}),
});
await act(async () => {
getAllByText("Logout")[0].click();
});
expect(logoutMockedAuthProvider.logout).toBeCalledTimes(1);
});
it("should render only allowed menu items", async () => {
const { getAllByText, queryByText } = render(<SiderElement />, {
wrapper: TestWrapper({
resources: [
{
name: "posts",
list: function render() {
return <div>posts</div>;
},
},
{
name: "users",
list: function render() {
return <div>users</div>;
},
},
],
accessControlProvider: {
can: ({ action, resource }) => {
if (action === "list" && resource === "posts") {
return Promise.resolve({ can: true });
}
if (action === "list" && resource === "users") {
return Promise.resolve({ can: false });
}
return Promise.resolve({ can: false });
},
},
}),
});
await waitFor(() => getAllByText("Posts")[0]);
await waitFor(() => expect(queryByText("Users")).toBeNull());
});
it("should render custom element passed with render prop", async () => {
const { getAllByText, queryAllByText } = render(
<SiderElement
render={({ logout, dashboard, items }) => {
return (
<>
<div>custom-element</div>
{dashboard}
{items}
{logout}
</>
);
}}
/>,
{
wrapper: TestWrapper({
legacyAuthProvider: mockLegacyAuthProvider,
DashboardPage: function Dashboard() {
return <div>Dashboard</div>;
},
}),
},
);
await waitFor(() =>
expect(getAllByText("Posts").length).toBeGreaterThanOrEqual(1),
);
expect(queryAllByText("Logout").length).toBeGreaterThanOrEqual(1);
expect(queryAllByText("Dashboard").length).toBeGreaterThanOrEqual(1);
expect(queryAllByText("custom-element").length).toBeGreaterThanOrEqual(1);
});
it("should item disabled when activeItemDisabled:true (legacyRouterProvider)", async () => {
const { getAllByText, getAllByRole } = render(
<SiderElement activeItemDisabled={true} />,
{
wrapper: TestWrapper({
routerInitialEntries: ["/posts"],
resources: [
{
name: "posts",
list: "/posts",
},
],
}),
},
);
await waitFor(() => {
return expect(getAllByText("Posts").length).toBeGreaterThanOrEqual(1);
});
await waitFor(() => {
const allLinks = getAllByRole("link");
const postLink = allLinks.find((link) => {
return link.getAttribute("href") === "/posts";
});
return expect(postLink).toHaveStyle("pointer-events: none");
});
});
it("should item disabled when activeItemDisabled:true", async () => {
const { getAllByText, getAllByRole } = render(
<SiderElement activeItemDisabled={true} />,
{
wrapper: TestWrapper({
routerInitialEntries: ["/posts"],
routerProvider: mockRouterBindings({
pathname: "/posts",
action: "list",
resource: {
name: "posts",
list: "/posts",
},
}),
resources: [
{
name: "posts",
list: "/posts",
},
{
name: "users",
list: "/users",
},
],
}),
},
);
await waitFor(() => {
return expect(getAllByText("Posts").length).toBeGreaterThanOrEqual(1);
});
await waitFor(() => {
const allLinks = getAllByRole("link");
const postLink = allLinks.find((link) => {
return link.getAttribute("href") === "/posts";
});
return expect(postLink).toHaveStyle("pointer-events: none");
});
});
it("should handle lowercase resource names correctly", async () => {
const { getByText, getAllByText } = render(<SiderElement />, {
wrapper: TestWrapper({
resources: [
{
name: "posts",
list: "/posts",
},
{
name: "users",
list: "/users",
},
],
accessControlProvider: {
can: ({ action, resource }) => {
if (action === "list" && resource === "posts") {
return Promise.resolve({ can: true });
}
if (action === "list" && resource === "users") {
return Promise.resolve({ can: false });
}
return Promise.resolve({ can: false });
},
},
}),
});
const postsElements = await waitFor(() => getAllByText("Posts"));
postsElements.forEach((element) => {
expect(element).toBeInTheDocument();
});
expect(() => getByText("Users")).toThrow();
});
it("should handle camelcased resource names correctly", async () => {
const { getByText, getAllByText } = render(<SiderElement />, {
wrapper: TestWrapper({
resources: [
{
name: "blogPosts",
list: "/blog-posts",
},
{
name: "userProfiles",
list: "/user-profiles",
},
],
accessControlProvider: {
can: ({ action, resource }) => {
if (action === "list" && resource === "blogPosts") {
return Promise.resolve({ can: true });
}
if (action === "list" && resource === "userProfiles") {
return Promise.resolve({ can: false });
}
return Promise.resolve({ can: false });
},
},
}),
});
const blogPostsElements = await waitFor(() => getAllByText("Blog posts"));
blogPostsElements.forEach((element) => {
expect(element).toBeInTheDocument();
});
expect(() => getByText("User profiles")).toThrow();
});
});
};

View File

@@ -0,0 +1,34 @@
import React from "react";
import type { RefineLayoutTitleProps } from "@refinedev/ui-types";
import { render, TestWrapper } from "@test";
export const layoutTitleTests = (
TitleElement: React.ComponentType<RefineLayoutTitleProps>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Title Element", () => {
it("should render successfully", async () => {
const { container } = render(<TitleElement collapsed={false} />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
});
it("should use app name and icon from <Refine /> component", () => {
const { getByTestId } = render(<TitleElement collapsed={false} />, {
wrapper: TestWrapper({
options: {
title: {
text: <div data-testid="my-company-name">My Company</div>,
icon: <div data-testid="my-company-logo" />,
},
},
}),
});
expect(getByTestId("my-company-name")).toBeInTheDocument();
expect(getByTestId("my-company-logo")).toBeInTheDocument();
});
});
};

View File

@@ -0,0 +1,34 @@
import React, { type FC } from "react";
import type { AuthPageProps } from "@refinedev/core";
import { render, TestWrapper } from "@test";
export const authPageTests = (
AuthPage: FC<AuthPageProps<any, any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Auth Page", () => {
it.each(["register", "forgotPassword", "updatePassword", "login"] as const)(
"should render %s type",
async (type) => {
const { getByText } = render(<AuthPage type={type} />, {
wrapper: TestWrapper({}),
});
switch (type) {
case "register":
expect(getByText(/sign up for your account/i)).toBeInTheDocument();
break;
case "forgotPassword":
expect(getByText(/forgot your password?/i)).toBeInTheDocument();
break;
case "updatePassword":
expect(getByText(/update/i)).toBeInTheDocument();
break;
default:
expect(getByText(/Sign in to your account/i)).toBeInTheDocument();
break;
}
},
);
});
};

View File

@@ -0,0 +1,289 @@
import React, { type FC } from "react";
import type { ForgotPasswordPageProps } from "@refinedev/core";
import {
fireEvent,
mockAuthProvider,
mockRouterBindings,
MockRouterProvider,
render,
TestWrapper,
waitFor,
} from "@test";
export const pageForgotPasswordTests = (
ForgotPasswordPage: FC<ForgotPasswordPageProps<any, any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Forgot Password Page", () => {
it("should render card title", async () => {
const { getByText } = render(<ForgotPasswordPage />, {
wrapper: TestWrapper({}),
});
expect(getByText(/forgot your password?/i)).toBeInTheDocument();
});
it("should render email input", async () => {
const { getByLabelText } = render(<ForgotPasswordPage />, {
wrapper: TestWrapper({}),
});
expect(getByLabelText(/email/i)).toBeInTheDocument();
});
it("should login link", async () => {
const { getByRole } = render(<ForgotPasswordPage />, {
wrapper: TestWrapper({}),
});
expect(
getByRole("link", {
name: /sign in/i,
}),
).toBeInTheDocument();
});
it("should not render login link when is false", async () => {
const { queryByRole } = render(<ForgotPasswordPage loginLink={false} />, {
wrapper: TestWrapper({}),
});
expect(
queryByRole("link", {
name: /sign in/i,
}),
).not.toBeInTheDocument();
});
it("should render reset button", async () => {
const { getByRole } = render(<ForgotPasswordPage />, {
wrapper: TestWrapper({}),
});
expect(
getByRole("button", {
name: /send reset/i,
}),
).toBeInTheDocument();
});
it("should render default <ThemedTitle> with icon", async () => {
const { getByText, getByTestId } = render(<ForgotPasswordPage />, {
wrapper: TestWrapper({}),
});
expect(getByText(/refine project/i)).toBeInTheDocument();
expect(getByTestId("refine-logo")).toBeInTheDocument();
});
it("should render custom title", async () => {
const { queryByText, queryByTestId } = render(
<ForgotPasswordPage title="Custom Title" />,
{
wrapper: TestWrapper({}),
},
);
expect(queryByText(/custom title/i)).toBeInTheDocument();
expect(queryByText(/refine project/i)).not.toBeInTheDocument();
expect(queryByTestId("refine-logo")).not.toBeInTheDocument();
});
it("should not render title when is false", async () => {
const { queryByText } = render(<ForgotPasswordPage title={false} />, {
wrapper: TestWrapper({}),
});
expect(queryByText(/refine project/i)).not.toBeInTheDocument();
});
it("should pass contentProps", async () => {
const { getByTestId } = render(
<ForgotPasswordPage contentProps={{ "data-testid": "test" }} />,
{
wrapper: TestWrapper({}),
},
);
expect(getByTestId("test")).toBeInTheDocument();
});
it("should pass wrapperProps", async () => {
const { getByTestId } = render(
<ForgotPasswordPage wrapperProps={{ "data-testid": "test" }} />,
{
wrapper: TestWrapper({}),
},
);
expect(getByTestId("test")).toBeInTheDocument();
});
it("should renderContent only", async () => {
const { queryByText, queryByTestId, queryByRole, queryByLabelText } =
render(
<ForgotPasswordPage
renderContent={() => <div data-testid="custom-content" />}
/>,
{
wrapper: TestWrapper({}),
},
);
expect(queryByLabelText(/email/i)).not.toBeInTheDocument();
expect(queryByText(/refine project/i)).not.toBeInTheDocument();
expect(queryByTestId("refine-logo")).not.toBeInTheDocument();
expect(
queryByRole("button", {
name: /reset/i,
}),
).not.toBeInTheDocument();
expect(queryByTestId("custom-content")).toBeInTheDocument();
});
it("should customizable with renderContent", async () => {
const { queryByText, queryByTestId, queryByRole, queryByLabelText } =
render(
<ForgotPasswordPage
renderContent={(content: any, title: any) => (
<div>
{title}
<div data-testid="custom-content">
<div>Custom Content</div>
</div>
{content}
</div>
)}
/>,
{
wrapper: TestWrapper({}),
},
);
expect(queryByText(/custom content/i)).toBeInTheDocument();
expect(queryByTestId("custom-content")).toBeInTheDocument();
expect(queryByLabelText(/email/i)).toBeInTheDocument();
expect(queryByText(/refine project/i)).toBeInTheDocument();
expect(queryByTestId("refine-logo")).toBeInTheDocument();
expect(
queryByRole("button", {
name: /reset/i,
}),
).toBeInTheDocument();
expect(queryByTestId("custom-content")).toBeInTheDocument();
});
it("should run forgotPassword mutation when form is submitted", async () => {
const forgotPasswordMock = jest.fn().mockResolvedValue({ success: true });
const { getByLabelText, getByText } = render(<ForgotPasswordPage />, {
wrapper: TestWrapper({
authProvider: {
...mockAuthProvider,
forgotPassword: forgotPasswordMock,
},
}),
});
fireEvent.change(getByLabelText(/email/i), {
target: { value: "demo@refine.dev" },
});
fireEvent.click(getByText(/send reset instructions/i));
await waitFor(() => {
expect(forgotPasswordMock).toBeCalledTimes(1);
});
expect(forgotPasswordMock).toBeCalledWith({
email: "demo@refine.dev",
});
});
it("should work with legacy router provider Link", async () => {
jest.spyOn(console, "error").mockImplementation((message) => {
console.warn(message);
});
const LinkComponentMock = jest.fn();
render(<ForgotPasswordPage />, {
wrapper: TestWrapper({
legacyRouterProvider: {
...MockRouterProvider,
Link: LinkComponentMock,
},
}),
});
expect(LinkComponentMock).toBeCalledWith(
expect.objectContaining({
to: "/login",
}),
{},
);
});
it("should work with new router provider Link", async () => {
jest.spyOn(console, "error").mockImplementation((message) => {
console.warn(message);
});
const LinkComponentMock = jest.fn();
render(<ForgotPasswordPage />, {
wrapper: TestWrapper({
routerProvider: mockRouterBindings({
fns: {
Link: LinkComponentMock,
},
}),
}),
});
expect(LinkComponentMock).toBeCalledWith(
expect.objectContaining({
to: "/login",
}),
{},
);
});
it("should should accept 'mutationVariables'", async () => {
const forgotPasswordMock = jest.fn().mockResolvedValue({ success: true });
const { getByRole, getByLabelText } = render(
<ForgotPasswordPage
mutationVariables={{
foo: "bar",
xyz: "abc",
}}
/>,
{
wrapper: TestWrapper({
authProvider: {
...mockAuthProvider,
forgotPassword: forgotPasswordMock,
},
}),
},
);
fireEvent.change(getByLabelText(/email/i), {
target: { value: "demo@refine.dev" },
});
fireEvent.click(
getByRole("button", {
name: /reset/i,
}),
);
await waitFor(() => {
expect(forgotPasswordMock).toHaveBeenCalledWith({
foo: "bar",
xyz: "abc",
email: "demo@refine.dev",
});
});
});
});
};

View File

@@ -0,0 +1,5 @@
export { pageLoginTests } from "./login";
export { pageRegisterTests } from "./register";
export { pageForgotPasswordTests } from "./forgotPassword";
export { pageUpdatePasswordTests } from "./updatePassword";
export { authPageTests } from "./authPage";

View File

@@ -0,0 +1,452 @@
import React, { type FC } from "react";
import {
fireEvent,
mockAuthProvider,
mockRouterBindings,
render,
TestWrapper,
waitFor,
} from "@test";
import type { LoginPageProps } from "@refinedev/core";
export const pageLoginTests = (
LoginPage: FC<LoginPageProps<any, any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Login Page", () => {
it("should render card title", async () => {
const { getByText } = render(<LoginPage />, {
wrapper: TestWrapper({}),
});
expect(getByText(/sign in to your account/i)).toBeInTheDocument();
});
it("should render card email and password input", async () => {
const { getByLabelText } = render(<LoginPage />, {
wrapper: TestWrapper({}),
});
expect(getByLabelText(/email/i)).toBeInTheDocument();
expect(getByLabelText(/password/i)).toBeInTheDocument();
});
it("should render providers", async () => {
const { getByText } = render(
<LoginPage
providers={[
{
name: "Google",
label: "Google",
},
{
name: "Github",
label: "Github",
},
]}
/>,
{
wrapper: TestWrapper({}),
},
);
expect(getByText(/google/i)).toBeInTheDocument();
expect(getByText(/github/i)).toBeInTheDocument();
});
it("should register link", async () => {
const { getByRole } = render(<LoginPage />, {
wrapper: TestWrapper({}),
});
expect(
getByRole("link", {
name: /sign up/i,
}),
).toBeInTheDocument();
});
it("should not render register link when is false", async () => {
const { queryByRole } = render(<LoginPage registerLink={false} />, {
wrapper: TestWrapper({}),
});
expect(
queryByRole("link", {
name: /sign up/i,
}),
).not.toBeInTheDocument();
});
it("should forgotPassword link", async () => {
const { getByRole } = render(<LoginPage />, {
wrapper: TestWrapper({}),
});
expect(
getByRole("link", {
name: /forgot password?/i,
}),
).toBeInTheDocument();
});
it("should not render forgotPassword link when is false", async () => {
const { queryByRole } = render(<LoginPage forgotPasswordLink={false} />, {
wrapper: TestWrapper({}),
});
expect(
queryByRole("link", {
name: /forgot password/i,
}),
).not.toBeInTheDocument();
});
it("should render remember me", async () => {
const { queryByRole } = render(<LoginPage />, {
wrapper: TestWrapper({}),
});
expect(
queryByRole("checkbox", {
name: /remember me/i,
}),
).toBeInTheDocument();
});
it("should not render remember me when is false", async () => {
const { queryByRole } = render(<LoginPage rememberMe={false} />, {
wrapper: TestWrapper({}),
});
expect(
queryByRole("checkbox", {
name: /remember me/i,
}),
).not.toBeInTheDocument();
});
it("should render sign in button", async () => {
const { getByRole } = render(<LoginPage />, {
wrapper: TestWrapper({}),
});
expect(
getByRole("button", {
name: /sign in/i,
}),
).toBeInTheDocument();
});
it("should render default <ThemedTitle> with icon", async () => {
const { getByText, getByTestId } = render(<LoginPage />, {
wrapper: TestWrapper({}),
});
expect(getByText(/refine project/i)).toBeInTheDocument();
expect(getByTestId("refine-logo")).toBeInTheDocument();
});
it("should render custom title", async () => {
const { queryByText, queryByTestId } = render(
<LoginPage title="Custom Title" />,
{
wrapper: TestWrapper({}),
},
);
expect(queryByText(/custom title/i)).toBeInTheDocument();
expect(queryByText(/refine project/i)).not.toBeInTheDocument();
expect(queryByTestId("refine-logo")).not.toBeInTheDocument();
});
it("should not render title when is false", async () => {
const { queryByText } = render(<LoginPage title={false} />, {
wrapper: TestWrapper({}),
});
expect(queryByText(/refine project/i)).not.toBeInTheDocument();
});
it("should pass contentProps", async () => {
const { getByTestId } = render(
<LoginPage contentProps={{ "data-testid": "test" }} />,
{
wrapper: TestWrapper({}),
},
);
expect(getByTestId("test")).toBeInTheDocument();
});
it("should pass wrapperProps", async () => {
const { getByTestId } = render(
<LoginPage wrapperProps={{ "data-testid": "test" }} />,
{
wrapper: TestWrapper({}),
},
);
expect(getByTestId("test")).toBeInTheDocument();
});
it("should renderContent only", async () => {
const { queryByText, queryByTestId, queryByRole, queryByLabelText } =
render(
<LoginPage
renderContent={() => <div data-testid="custom-content" />}
/>,
{
wrapper: TestWrapper({}),
},
);
expect(queryByLabelText(/email/i)).not.toBeInTheDocument();
expect(queryByLabelText(/password/i)).not.toBeInTheDocument();
expect(queryByText(/refine project/i)).not.toBeInTheDocument();
expect(queryByTestId("refine-logo")).not.toBeInTheDocument();
expect(
queryByRole("button", {
name: /sign in/i,
}),
).not.toBeInTheDocument();
expect(queryByTestId("custom-content")).toBeInTheDocument();
});
it("should customizable with renderContent", async () => {
const { queryByText, queryByTestId, queryByRole, queryByLabelText } =
render(
<LoginPage
renderContent={(content: any, title: any) => (
<div>
{title}
<div data-testid="custom-content">
<div>Custom Content</div>
</div>
{content}
</div>
)}
/>,
{
wrapper: TestWrapper({}),
},
);
expect(queryByText(/custom content/i)).toBeInTheDocument();
expect(queryByTestId("custom-content")).toBeInTheDocument();
expect(queryByLabelText(/email/i)).toBeInTheDocument();
expect(queryByLabelText(/password/i)).toBeInTheDocument();
expect(queryByText(/refine project/i)).toBeInTheDocument();
expect(queryByTestId("refine-logo")).toBeInTheDocument();
expect(
queryByRole("button", {
name: /sign in/i,
}),
).toBeInTheDocument();
expect(queryByTestId("custom-content")).toBeInTheDocument();
});
it("should run login mutation when form is submitted", async () => {
const loginMock = jest.fn().mockResolvedValue({ success: true });
const { getByLabelText, getAllByText } = render(<LoginPage />, {
wrapper: TestWrapper({
authProvider: {
...mockAuthProvider,
login: loginMock,
},
}),
});
fireEvent.change(getByLabelText(/email/i), {
target: { value: "demo@refine.dev" },
});
fireEvent.change(getByLabelText(/password/i), {
target: { value: "demo" },
});
fireEvent.click(getByLabelText(/remember me/i));
fireEvent.click(getAllByText(/sign in/i)[1]);
await waitFor(() => {
expect(loginMock).toBeCalledTimes(1);
});
expect(loginMock).toBeCalledWith({
email: "demo@refine.dev",
password: "demo",
remember: true,
});
});
it("should work with new router provider Link", async () => {
jest.spyOn(console, "error").mockImplementation((message) => {
console.warn(message);
});
const LinkComponentMock = jest.fn();
render(<LoginPage />, {
wrapper: TestWrapper({
routerProvider: mockRouterBindings({
fns: {
Link: LinkComponentMock,
},
}),
}),
});
expect(LinkComponentMock).toBeCalledWith(
expect.objectContaining({
to: "/forgot-password",
}),
{},
);
expect(LinkComponentMock).toBeCalledWith(
expect.objectContaining({
to: "/register",
}),
{},
);
});
it("should run login mutation when provider button is clicked", async () => {
const loginMock = jest.fn().mockResolvedValue({ success: true });
const { getByText } = render(
<LoginPage
providers={[
{
name: "Google",
label: "Google",
},
]}
/>,
{
wrapper: TestWrapper({
authProvider: {
...mockAuthProvider,
login: loginMock,
},
}),
},
);
expect(getByText(/google/i)).toBeInTheDocument();
fireEvent.click(getByText(/google/i));
await waitFor(() => {
expect(loginMock).toBeCalledTimes(1);
});
expect(loginMock).toBeCalledWith({
providerName: "Google",
});
});
it("should not render form when `hideForm` is true", async () => {
const { queryByLabelText, getByText, queryByRole } = render(
<LoginPage
hideForm
providers={[
{
name: "google",
label: "Google",
},
{ name: "github", label: "GitHub" },
]}
/>,
{
wrapper: TestWrapper({}),
},
);
expect(queryByLabelText(/email/i)).not.toBeInTheDocument();
expect(queryByLabelText(/password/i)).not.toBeInTheDocument();
expect(queryByLabelText(/remember/i)).not.toBeInTheDocument();
expect(
queryByRole("link", {
name: /forgot password/i,
}),
).not.toBeInTheDocument();
expect(
queryByRole("button", {
name: /sign in/i,
}),
).not.toBeInTheDocument();
expect(getByText(/google/i)).toBeInTheDocument();
expect(getByText(/github/i)).toBeInTheDocument();
expect(
queryByRole("link", {
name: /sign up/i,
}),
).toBeInTheDocument();
});
it.each([true, false])("should has default links", async (hideForm) => {
const { getByRole } = render(<LoginPage hideForm={hideForm} />, {
wrapper: TestWrapper({}),
});
expect(
getByRole("link", {
name: /sign up/i,
}),
).toHaveAttribute("href", "/register");
if (hideForm === false) {
expect(
getByRole("link", {
name: /forgot password/i,
}),
).toHaveAttribute("href", "/forgot-password");
}
});
it("should should accept 'mutationVariables'", async () => {
const loginMock = jest.fn().mockResolvedValue({ success: true });
const { getByRole, getByLabelText } = render(
<LoginPage
mutationVariables={{
foo: "bar",
xyz: "abc",
}}
/>,
{
wrapper: TestWrapper({
authProvider: {
...mockAuthProvider,
login: loginMock,
},
}),
},
);
fireEvent.change(getByLabelText(/email/i), {
target: { value: "demo@refine.dev" },
});
fireEvent.change(getByLabelText(/password/i), {
target: { value: "demo" },
});
fireEvent.click(
getByRole("button", {
name: /sign in/i,
}),
);
await waitFor(() => {
expect(loginMock).toHaveBeenCalledWith({
foo: "bar",
xyz: "abc",
email: "demo@refine.dev",
password: "demo",
remember: false,
});
});
});
});
};

View File

@@ -0,0 +1,384 @@
import React, { type FC } from "react";
import {
fireEvent,
mockAuthProvider,
mockRouterBindings,
render,
TestWrapper,
waitFor,
} from "@test";
import type { RegisterPageProps } from "@refinedev/core";
export const pageRegisterTests = (
RegisterPage: FC<RegisterPageProps<any, any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Register Page", () => {
it("should render card title", async () => {
const { getByText } = render(<RegisterPage />, {
wrapper: TestWrapper({}),
});
expect(getByText(/sign up for your account/i)).toBeInTheDocument();
});
it("should render card email and password input", async () => {
const { getByLabelText } = render(<RegisterPage />, {
wrapper: TestWrapper({}),
});
expect(getByLabelText(/email/i)).toBeInTheDocument();
expect(getByLabelText(/password/i)).toBeInTheDocument();
});
it("should render providers", async () => {
const { getByText } = render(
<RegisterPage
providers={[
{
name: "Google",
label: "Google",
},
{
name: "Github",
label: "Github",
},
]}
/>,
{
wrapper: TestWrapper({}),
},
);
expect(getByText(/google/i)).toBeInTheDocument();
expect(getByText(/github/i)).toBeInTheDocument();
});
it("should login link", async () => {
const { getByRole } = render(<RegisterPage />, {
wrapper: TestWrapper({}),
});
expect(
getByRole("link", {
name: /sign in/i,
}),
).toBeInTheDocument();
});
it("should not render login link when is false", async () => {
const { queryByRole } = render(<RegisterPage loginLink={false} />, {
wrapper: TestWrapper({}),
});
expect(
queryByRole("link", {
name: /sign in/i,
}),
).not.toBeInTheDocument();
});
it("should render sign up button", async () => {
const { getByRole } = render(<RegisterPage />, {
wrapper: TestWrapper({}),
});
expect(
getByRole("button", {
name: /sign up/i,
}),
).toBeInTheDocument();
});
it("should render default <ThemedTitle> with icon", async () => {
const { getByText, getByTestId } = render(<RegisterPage />, {
wrapper: TestWrapper({}),
});
expect(getByText(/refine project/i)).toBeInTheDocument();
expect(getByTestId("refine-logo")).toBeInTheDocument();
});
it("should render custom title", async () => {
const { queryByText, queryByTestId } = render(
<RegisterPage title="Custom Title" />,
{
wrapper: TestWrapper({}),
},
);
expect(queryByText(/custom title/i)).toBeInTheDocument();
expect(queryByText(/refine project/i)).not.toBeInTheDocument();
expect(queryByTestId("refine-logo")).not.toBeInTheDocument();
});
it("should not render title when is false", async () => {
const { queryByText } = render(<RegisterPage title={false} />, {
wrapper: TestWrapper({}),
});
expect(queryByText(/refine project/i)).not.toBeInTheDocument();
});
it("should pass contentProps", async () => {
const { getByTestId } = render(
<RegisterPage contentProps={{ "data-testid": "test" }} />,
{
wrapper: TestWrapper({}),
},
);
expect(getByTestId("test")).toBeInTheDocument();
});
it("should pass wrapperProps", async () => {
const { getByTestId } = render(
<RegisterPage wrapperProps={{ "data-testid": "test" }} />,
{
wrapper: TestWrapper({}),
},
);
expect(getByTestId("test")).toBeInTheDocument();
});
it("should renderContent only", async () => {
const { queryByText, queryByTestId, queryByRole, queryByLabelText } =
render(
<RegisterPage
renderContent={() => <div data-testid="custom-content" />}
/>,
{
wrapper: TestWrapper({}),
},
);
expect(queryByLabelText(/email/i)).not.toBeInTheDocument();
expect(queryByLabelText(/password/i)).not.toBeInTheDocument();
expect(queryByText(/refine project/i)).not.toBeInTheDocument();
expect(queryByTestId("refine-logo")).not.toBeInTheDocument();
expect(
queryByRole("button", {
name: /sign up/i,
}),
).not.toBeInTheDocument();
expect(queryByTestId("custom-content")).toBeInTheDocument();
});
it("should customizable with renderContent", async () => {
const { queryByText, queryByTestId, queryByRole, queryByLabelText } =
render(
<RegisterPage
renderContent={(content: any, title: any) => (
<div>
{title}
<div data-testid="custom-content">
<div>Custom Content</div>
</div>
{content}
</div>
)}
/>,
{
wrapper: TestWrapper({}),
},
);
expect(queryByText(/custom content/i)).toBeInTheDocument();
expect(queryByTestId("custom-content")).toBeInTheDocument();
expect(queryByLabelText(/email/i)).toBeInTheDocument();
expect(queryByLabelText(/password/i)).toBeInTheDocument();
expect(queryByText(/refine project/i)).toBeInTheDocument();
expect(queryByTestId("refine-logo")).toBeInTheDocument();
expect(
queryByRole("button", {
name: /sign up/i,
}),
).toBeInTheDocument();
expect(queryByTestId("custom-content")).toBeInTheDocument();
});
it("should run register mutation when form is submitted", async () => {
const registerMock = jest.fn().mockResolvedValue({ success: true });
const { getByLabelText, getAllByText } = render(<RegisterPage />, {
wrapper: TestWrapper({
authProvider: {
...mockAuthProvider,
register: registerMock,
},
}),
});
fireEvent.change(getByLabelText(/email/i), {
target: { value: "demo@refine.dev" },
});
fireEvent.change(getByLabelText(/password/i), {
target: { value: "demo" },
});
fireEvent.click(getAllByText(/sign up/i)[1]);
await waitFor(() => {
expect(registerMock).toBeCalledTimes(1);
});
expect(registerMock).toBeCalledWith({
email: "demo@refine.dev",
password: "demo",
});
});
it("should run register mutation when provider button is clicked", async () => {
const registerMock = jest.fn().mockResolvedValue({ success: true });
const { getByText } = render(
<RegisterPage
providers={[
{
name: "Google",
label: "Google",
},
]}
/>,
{
wrapper: TestWrapper({
authProvider: {
...mockAuthProvider,
register: registerMock,
},
}),
},
);
expect(getByText(/google/i)).toBeInTheDocument();
fireEvent.click(getByText(/google/i));
await waitFor(() => {
expect(registerMock).toBeCalledTimes(1);
});
expect(registerMock).toBeCalledWith({
providerName: "Google",
});
});
it("should work with new router provider Link", async () => {
jest.spyOn(console, "error").mockImplementation((message) => {
console.warn(message);
});
const LinkComponentMock = jest.fn();
render(<RegisterPage />, {
wrapper: TestWrapper({
routerProvider: mockRouterBindings({
fns: {
Link: LinkComponentMock,
},
}),
}),
});
expect(LinkComponentMock).toBeCalledWith(
expect.objectContaining({
to: "/login",
}),
{},
);
});
it("should not render form when `hideForm` is true", async () => {
const { queryByLabelText, getByText, queryByRole } = render(
<RegisterPage
hideForm
providers={[
{
name: "google",
label: "Google",
},
{ name: "github", label: "GitHub" },
]}
/>,
{
wrapper: TestWrapper({}),
},
);
expect(queryByLabelText(/email/i)).not.toBeInTheDocument();
expect(queryByLabelText(/password/i)).not.toBeInTheDocument();
expect(
queryByRole("link", {
name: /forgot password/i,
}),
).not.toBeInTheDocument();
expect(
queryByRole("button", {
name: /sign up/i,
}),
).not.toBeInTheDocument();
expect(getByText(/google/i)).toBeInTheDocument();
expect(getByText(/github/i)).toBeInTheDocument();
expect(
queryByRole("link", {
name: /sign in/i,
}),
).toBeInTheDocument();
});
it.each([true, false])("should has default links", async (hideForm) => {
const { getByRole } = render(<RegisterPage hideForm={hideForm} />, {
wrapper: TestWrapper({}),
});
expect(
getByRole("link", {
name: /sign in/i,
}),
).toHaveAttribute("href", "/login");
});
it("should should accept 'mutationVariables'", async () => {
const registerMock = jest.fn().mockResolvedValue({ success: true });
const { getByRole, getByLabelText } = render(
<RegisterPage
mutationVariables={{
foo: "bar",
xyz: "abc",
}}
/>,
{
wrapper: TestWrapper({
authProvider: {
...mockAuthProvider,
register: registerMock,
},
}),
},
);
fireEvent.change(getByLabelText(/email/i), {
target: { value: "demo@refine.dev" },
});
fireEvent.change(getByLabelText(/password/i), {
target: { value: "demo" },
});
fireEvent.click(
getByRole("button", {
name: /sign up/i,
}),
);
await waitFor(() => {
expect(registerMock).toHaveBeenCalledWith({
foo: "bar",
xyz: "abc",
email: "demo@refine.dev",
password: "demo",
});
});
});
});
};

View File

@@ -0,0 +1,270 @@
import React, { type FC } from "react";
import {
fireEvent,
mockAuthProvider,
mockLegacyAuthProvider,
render,
TestWrapper,
waitFor,
} from "@test";
import type { UpdatePasswordPageProps } from "@refinedev/core";
export const pageUpdatePasswordTests = (
UpdatePasswordPage: FC<UpdatePasswordPageProps<any, any, any>>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Update Password Page", () => {
it("should render card title", async () => {
const { getByText } = render(<UpdatePasswordPage />, {
wrapper: TestWrapper({}),
});
expect(getByText(/set new password?/i)).toBeInTheDocument();
});
it("should render password input", async () => {
const { getByLabelText } = render(<UpdatePasswordPage />, {
wrapper: TestWrapper({}),
});
expect(getByLabelText("New Password")).toBeInTheDocument();
expect(getByLabelText("Confirm New Password")).toBeInTheDocument();
});
it("should render default <ThemedTitle> with icon", async () => {
const { getByText, getByTestId } = render(<UpdatePasswordPage />, {
wrapper: TestWrapper({}),
});
expect(getByText(/refine project/i)).toBeInTheDocument();
expect(getByTestId("refine-logo")).toBeInTheDocument();
});
it("should render custom title", async () => {
const { queryByText, queryByTestId } = render(
<UpdatePasswordPage title="Custom Title" />,
{
wrapper: TestWrapper({}),
},
);
expect(queryByText(/custom title/i)).toBeInTheDocument();
expect(queryByText(/refine project/i)).not.toBeInTheDocument();
expect(queryByTestId("refine-logo")).not.toBeInTheDocument();
});
it("should renderContent only", async () => {
const { queryByText, queryByTestId, queryByLabelText } = render(
<UpdatePasswordPage
renderContent={() => <div data-testid="custom-content" />}
/>,
{
wrapper: TestWrapper({}),
},
);
expect(queryByText(/refine project/i)).not.toBeInTheDocument();
expect(queryByTestId("refine-logo")).not.toBeInTheDocument();
expect(queryByLabelText("New Password")).not.toBeInTheDocument();
expect(queryByLabelText("Confirm New Password")).not.toBeInTheDocument();
expect(queryByTestId("custom-content")).toBeInTheDocument();
});
it("should not render title when is false", async () => {
const { queryByText } = render(<UpdatePasswordPage title={false} />, {
wrapper: TestWrapper({}),
});
expect(queryByText(/refine project/i)).not.toBeInTheDocument();
});
it("should pass contentProps", async () => {
const { getByTestId } = render(
<UpdatePasswordPage contentProps={{ "data-testid": "test" }} />,
{
wrapper: TestWrapper({}),
},
);
expect(getByTestId("test")).toBeInTheDocument();
});
it("should pass wrapperProps", async () => {
const { getByTestId } = render(
<UpdatePasswordPage wrapperProps={{ "data-testid": "test" }} />,
{
wrapper: TestWrapper({}),
},
);
expect(getByTestId("test")).toBeInTheDocument();
});
it("should customizable with renderContent", async () => {
const { queryByText, queryByTestId, getByLabelText } = render(
<UpdatePasswordPage
renderContent={(content: any, title: any) => (
<div>
{title}
<div data-testid="custom-content">
<div>Custom Content</div>
</div>
{content}
</div>
)}
/>,
{
wrapper: TestWrapper({}),
},
);
expect(queryByText(/custom content/i)).toBeInTheDocument();
expect(queryByTestId("custom-content")).toBeInTheDocument();
expect(queryByText(/refine project/i)).toBeInTheDocument();
expect(queryByTestId("refine-logo")).toBeInTheDocument();
expect(queryByTestId("custom-content")).toBeInTheDocument();
expect(getByLabelText("New Password")).toBeInTheDocument();
expect(getByLabelText("Confirm New Password")).toBeInTheDocument();
});
it("should run updatePassword mutation when form is submitted", async () => {
const updatePasswordMock = jest.fn().mockResolvedValue({ success: true });
const { getAllByLabelText, getByLabelText, getAllByText } = render(
<UpdatePasswordPage />,
{
wrapper: TestWrapper({
authProvider: {
...mockAuthProvider,
updatePassword: updatePasswordMock,
},
}),
},
);
fireEvent.change(getAllByLabelText(/password/i)[0], {
target: { value: "demo" },
});
fireEvent.change(getByLabelText(/confirm new password/i), {
target: { value: "demo" },
});
fireEvent.click(getAllByText(/update/i)[0]);
await waitFor(() => {
expect(updatePasswordMock).toBeCalledTimes(1);
});
expect(updatePasswordMock).toBeCalledWith({
password: "demo",
confirmPassword: "demo",
});
});
it("should run updatePassword mutation when form is submitted with legacy authProvider", async () => {
const updatePasswordMock = jest.fn().mockResolvedValue({ success: true });
const { getAllByLabelText, getByLabelText, getAllByText } = render(
<UpdatePasswordPage />,
{
wrapper: TestWrapper({
legacyAuthProvider: {
...mockLegacyAuthProvider,
updatePassword: updatePasswordMock,
},
}),
},
);
fireEvent.change(getAllByLabelText(/password/i)[0], {
target: { value: "demo" },
});
fireEvent.change(getByLabelText(/confirm new password/i), {
target: { value: "demo" },
});
fireEvent.click(getAllByText(/update/i)[0]);
await waitFor(() => {
expect(updatePasswordMock).toBeCalledTimes(1);
});
expect(updatePasswordMock).toBeCalledWith({
password: "demo",
confirmPassword: "demo",
});
});
it("if passwords are not matched, should display the validation error", async () => {
const { getAllByLabelText, getByLabelText, getByText } = render(
<UpdatePasswordPage />,
{
wrapper: TestWrapper({
authProvider: mockAuthProvider,
}),
},
);
fireEvent.change(getAllByLabelText(/password/i)[0], {
target: { value: "demo" },
});
fireEvent.change(getByLabelText(/confirm new password/i), {
target: { value: "demo2" },
});
fireEvent.click(getByText(/update/i));
await waitFor(() => {
expect(getByText(/asswords do not match/i)).toBeInTheDocument();
});
});
it("should should accept 'mutationVariables'", async () => {
const updatePasswordMock = jest.fn().mockResolvedValue({ success: true });
const { getByRole, getByLabelText, getAllByLabelText } = render(
<UpdatePasswordPage
mutationVariables={{
foo: "bar",
xyz: "abc",
}}
/>,
{
wrapper: TestWrapper({
authProvider: {
...mockAuthProvider,
updatePassword: updatePasswordMock,
},
}),
},
);
fireEvent.change(getAllByLabelText(/password/i)[0], {
target: { value: "demo" },
});
fireEvent.change(getByLabelText(/confirm new password/i), {
target: { value: "demo" },
});
fireEvent.click(
getByRole("button", {
name: /update/i,
}),
);
await waitFor(
() => {
expect(updatePasswordMock).toHaveBeenCalledWith({
foo: "bar",
xyz: "abc",
password: "demo",
confirmPassword: "demo",
});
},
{ timeout: 500 },
);
});
});
};

View File

@@ -0,0 +1,72 @@
import React from "react";
import type { RefineErrorPageProps } from "@refinedev/ui-types";
import {
fireEvent,
mockLegacyRouterProvider,
mockRouterBindings,
render,
TestWrapper,
waitFor,
} from "@test";
export const pageErrorTests = (
ErrorPage: React.ComponentType<RefineErrorPageProps>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Error Page", () => {
it("should render successfully", async () => {
const { container } = render(<ErrorPage />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
});
it("back home button should work with legacy router provider", async () => {
const pushMock = jest.fn();
const { getByText } = render(<ErrorPage />, {
wrapper: TestWrapper({
legacyRouterProvider: {
...mockLegacyRouterProvider(),
useHistory: () => ({
goBack: jest.fn(),
push: pushMock,
replace: jest.fn(),
}),
},
}),
});
fireEvent.click(getByText("Back Home"));
await waitFor(() => {
expect(pushMock).toBeCalledTimes(1);
});
expect(pushMock).toBeCalledWith("/");
});
it("back home button should work with router provider", async () => {
const goMock = jest.fn();
const { getByText } = render(<ErrorPage />, {
wrapper: TestWrapper({
routerProvider: mockRouterBindings({
fns: {
go: () => goMock,
},
}),
}),
});
fireEvent.click(getByText("Back Home"));
await waitFor(() => {
expect(goMock).toBeCalledTimes(1);
});
expect(goMock).toBeCalledWith({ to: "/" });
});
});
};

View File

@@ -0,0 +1,3 @@
export { pageErrorTests } from "./error";
export { pageReadyTests } from "./ready";
export * from "./auth";

View File

@@ -0,0 +1,18 @@
import React from "react";
import type { RefineReadyPageProps } from "@refinedev/ui-types";
import { render, TestWrapper } from "@test";
export const pageReadyTests = (
ReadyPage: React.ComponentType<RefineReadyPageProps>,
): void => {
describe("[@refinedev/ui-tests] Common Tests / Ready Page", () => {
it("should render successfully", async () => {
const { container } = render(<ReadyPage />, {
wrapper: TestWrapper({}),
});
expect(container).toBeTruthy();
});
});
};