mirror of
https://github.com/stefanpejcic/openpanel
synced 2025-06-26 18:28:26 +00:00
packages
This commit is contained in:
44
packages/ui-tests/src/tests/autoSaveIndicator.tsx
Normal file
44
packages/ui-tests/src/tests/autoSaveIndicator.tsx
Normal 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...");
|
||||
});
|
||||
});
|
||||
};
|
||||
80
packages/ui-tests/src/tests/breadcrumb.tsx
Normal file
80
packages/ui-tests/src/tests/breadcrumb.tsx
Normal 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();
|
||||
});
|
||||
});
|
||||
};
|
||||
441
packages/ui-tests/src/tests/buttons/clone.tsx
Normal file
441
packages/ui-tests/src/tests/buttons/clone.tsx
Normal 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");
|
||||
});
|
||||
});
|
||||
};
|
||||
412
packages/ui-tests/src/tests/buttons/create.tsx
Normal file
412
packages/ui-tests/src/tests/buttons/create.tsx
Normal 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");
|
||||
});
|
||||
});
|
||||
};
|
||||
593
packages/ui-tests/src/tests/buttons/delete.tsx
Normal file
593
packages/ui-tests/src/tests/buttons/delete.tsx
Normal 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);
|
||||
});
|
||||
});
|
||||
};
|
||||
393
packages/ui-tests/src/tests/buttons/edit.tsx
Normal file
393
packages/ui-tests/src/tests/buttons/edit.tsx
Normal 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");
|
||||
});
|
||||
});
|
||||
};
|
||||
55
packages/ui-tests/src/tests/buttons/export.tsx
Normal file
55
packages/ui-tests/src/tests/buttons/export.tsx
Normal 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();
|
||||
});
|
||||
});
|
||||
};
|
||||
60
packages/ui-tests/src/tests/buttons/import.tsx
Normal file
60
packages/ui-tests/src/tests/buttons/import.tsx
Normal 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();
|
||||
});
|
||||
});
|
||||
};
|
||||
10
packages/ui-tests/src/tests/buttons/index.tsx
Normal file
10
packages/ui-tests/src/tests/buttons/index.tsx
Normal 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";
|
||||
394
packages/ui-tests/src/tests/buttons/list.tsx
Normal file
394
packages/ui-tests/src/tests/buttons/list.tsx
Normal 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");
|
||||
});
|
||||
});
|
||||
};
|
||||
175
packages/ui-tests/src/tests/buttons/refresh.tsx
Normal file
175
packages/ui-tests/src/tests/buttons/refresh.tsx
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
63
packages/ui-tests/src/tests/buttons/save.tsx
Normal file
63
packages/ui-tests/src/tests/buttons/save.tsx
Normal 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);
|
||||
});
|
||||
});
|
||||
};
|
||||
433
packages/ui-tests/src/tests/buttons/show.tsx
Normal file
433
packages/ui-tests/src/tests/buttons/show.tsx
Normal 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");
|
||||
});
|
||||
});
|
||||
};
|
||||
117
packages/ui-tests/src/tests/crud/create.tsx
Normal file
117
packages/ui-tests/src/tests/crud/create.tsx
Normal 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");
|
||||
});
|
||||
});
|
||||
};
|
||||
144
packages/ui-tests/src/tests/crud/edit.tsx
Normal file
144
packages/ui-tests/src/tests/crud/edit.tsx
Normal 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");
|
||||
});
|
||||
});
|
||||
};
|
||||
4
packages/ui-tests/src/tests/crud/index.tsx
Normal file
4
packages/ui-tests/src/tests/crud/index.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from "./list";
|
||||
export * from "./create";
|
||||
export * from "./edit";
|
||||
export * from "./show";
|
||||
229
packages/ui-tests/src/tests/crud/list.tsx
Normal file
229
packages/ui-tests/src/tests/crud/list.tsx
Normal 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(),
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
120
packages/ui-tests/src/tests/crud/show.tsx
Normal file
120
packages/ui-tests/src/tests/crud/show.tsx
Normal 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");
|
||||
});
|
||||
});
|
||||
};
|
||||
37
packages/ui-tests/src/tests/fields/boolean.tsx
Normal file
37
packages/ui-tests/src/tests/fields/boolean.tsx
Normal 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();
|
||||
});
|
||||
});
|
||||
};
|
||||
64
packages/ui-tests/src/tests/fields/date.tsx
Normal file
64
packages/ui-tests/src/tests/fields/date.tsx
Normal 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");
|
||||
});
|
||||
});
|
||||
};
|
||||
19
packages/ui-tests/src/tests/fields/email.tsx
Normal file
19
packages/ui-tests/src/tests/fields/email.tsx
Normal 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",
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
33
packages/ui-tests/src/tests/fields/file.tsx
Normal file
33
packages/ui-tests/src/tests/fields/file.tsx
Normal 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);
|
||||
});
|
||||
});
|
||||
};
|
||||
27
packages/ui-tests/src/tests/fields/image.tsx
Normal file
27
packages/ui-tests/src/tests/fields/image.tsx
Normal 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);
|
||||
});
|
||||
});
|
||||
};
|
||||
10
packages/ui-tests/src/tests/fields/index.tsx
Normal file
10
packages/ui-tests/src/tests/fields/index.tsx
Normal 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";
|
||||
32
packages/ui-tests/src/tests/fields/markdown.tsx
Normal file
32
packages/ui-tests/src/tests/fields/markdown.tsx
Normal 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();
|
||||
});
|
||||
});
|
||||
};
|
||||
44
packages/ui-tests/src/tests/fields/number.tsx
Normal file
44
packages/ui-tests/src/tests/fields/number.tsx
Normal 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");
|
||||
});
|
||||
});
|
||||
};
|
||||
22
packages/ui-tests/src/tests/fields/tag.tsx
Normal file
22
packages/ui-tests/src/tests/fields/tag.tsx
Normal 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();
|
||||
});
|
||||
});
|
||||
};
|
||||
16
packages/ui-tests/src/tests/fields/text.tsx
Normal file
16
packages/ui-tests/src/tests/fields/text.tsx
Normal 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");
|
||||
});
|
||||
});
|
||||
};
|
||||
40
packages/ui-tests/src/tests/fields/url.tsx
Normal file
40
packages/ui-tests/src/tests/fields/url.tsx
Normal 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);
|
||||
});
|
||||
});
|
||||
};
|
||||
7
packages/ui-tests/src/tests/index.tsx
Normal file
7
packages/ui-tests/src/tests/index.tsx
Normal 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";
|
||||
18
packages/ui-tests/src/tests/layout/footer.tsx
Normal file
18
packages/ui-tests/src/tests/layout/footer.tsx
Normal 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();
|
||||
});
|
||||
});
|
||||
};
|
||||
71
packages/ui-tests/src/tests/layout/header.tsx
Normal file
71
packages/ui-tests/src/tests/layout/header.tsx
Normal 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",
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
5
packages/ui-tests/src/tests/layout/index.tsx
Normal file
5
packages/ui-tests/src/tests/layout/index.tsx
Normal 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";
|
||||
43
packages/ui-tests/src/tests/layout/layout.tsx
Normal file
43
packages/ui-tests/src/tests/layout/layout.tsx
Normal 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));
|
||||
});
|
||||
});
|
||||
};
|
||||
307
packages/ui-tests/src/tests/layout/sider.tsx
Normal file
307
packages/ui-tests/src/tests/layout/sider.tsx
Normal 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();
|
||||
});
|
||||
});
|
||||
};
|
||||
34
packages/ui-tests/src/tests/layout/title.tsx
Normal file
34
packages/ui-tests/src/tests/layout/title.tsx
Normal 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();
|
||||
});
|
||||
});
|
||||
};
|
||||
34
packages/ui-tests/src/tests/pages/auth/authPage.tsx
Normal file
34
packages/ui-tests/src/tests/pages/auth/authPage.tsx
Normal 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;
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
289
packages/ui-tests/src/tests/pages/auth/forgotPassword.tsx
Normal file
289
packages/ui-tests/src/tests/pages/auth/forgotPassword.tsx
Normal 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",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
5
packages/ui-tests/src/tests/pages/auth/index.ts
Normal file
5
packages/ui-tests/src/tests/pages/auth/index.ts
Normal 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";
|
||||
452
packages/ui-tests/src/tests/pages/auth/login.tsx
Normal file
452
packages/ui-tests/src/tests/pages/auth/login.tsx
Normal 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,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
384
packages/ui-tests/src/tests/pages/auth/register.tsx
Normal file
384
packages/ui-tests/src/tests/pages/auth/register.tsx
Normal 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",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
270
packages/ui-tests/src/tests/pages/auth/updatePassword.tsx
Normal file
270
packages/ui-tests/src/tests/pages/auth/updatePassword.tsx
Normal 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 },
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
72
packages/ui-tests/src/tests/pages/error.tsx
Normal file
72
packages/ui-tests/src/tests/pages/error.tsx
Normal 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: "/" });
|
||||
});
|
||||
});
|
||||
};
|
||||
3
packages/ui-tests/src/tests/pages/index.tsx
Normal file
3
packages/ui-tests/src/tests/pages/index.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export { pageErrorTests } from "./error";
|
||||
export { pageReadyTests } from "./ready";
|
||||
export * from "./auth";
|
||||
18
packages/ui-tests/src/tests/pages/ready.tsx
Normal file
18
packages/ui-tests/src/tests/pages/ready.tsx
Normal 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();
|
||||
});
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user