mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
431 lines
15 KiB
TypeScript
431 lines
15 KiB
TypeScript
import ComponentsModal from "../../../UI/Components/Workflow/ComponentsModal";
|
|
import { describe, expect, it } from "@jest/globals";
|
|
import "@testing-library/jest-dom/extend-expect";
|
|
import { fireEvent, render, screen } from "@testing-library/react";
|
|
import IconProp from "../../../Types/Icon/IconProp";
|
|
import ComponentMetadata, {
|
|
ComponentCategory,
|
|
ComponentType,
|
|
} from "../../../Types/Workflow/Component";
|
|
import React from "react";
|
|
import getJestMockFunction, { MockFunction } from "../../../Tests/MockType";
|
|
import Faker from "../../../Utils/Faker";
|
|
|
|
/// @dev we use different UUID for (id & title), description, and category to ensure that the component is unique
|
|
|
|
type GetComponentMetadataFunction = (category?: string) => ComponentMetadata;
|
|
|
|
const getComponentMetadata: GetComponentMetadataFunction = (
|
|
category?: string,
|
|
): ComponentMetadata => {
|
|
const id: string = Faker.generateRandomObjectID().toString();
|
|
return {
|
|
id,
|
|
title: id,
|
|
description: Faker.generateRandomObjectID().toString(),
|
|
category: category || Faker.generateRandomObjectID().toString(),
|
|
iconProp: IconProp.Activity,
|
|
componentType: ComponentType.Component,
|
|
arguments: [],
|
|
returnValues: [],
|
|
inPorts: [],
|
|
outPorts: [],
|
|
};
|
|
};
|
|
|
|
type GetComponentCategoryFunction = (name?: string) => ComponentCategory;
|
|
|
|
const getComponentCategory: GetComponentCategoryFunction = (
|
|
name?: string,
|
|
): ComponentCategory => {
|
|
return {
|
|
name: name || Faker.generateRandomObjectID().toString(),
|
|
description: `Description for ${name}`,
|
|
icon: IconProp.Activity,
|
|
};
|
|
};
|
|
|
|
describe("ComponentsModal", () => {
|
|
const mockedCategories: ComponentCategory[] = [
|
|
getComponentCategory(),
|
|
getComponentCategory(),
|
|
getComponentCategory(),
|
|
getComponentCategory(),
|
|
];
|
|
|
|
const mockedComponents: ComponentMetadata[] = [
|
|
getComponentMetadata(mockedCategories[0]?.name),
|
|
getComponentMetadata(mockedCategories[1]?.name),
|
|
getComponentMetadata(mockedCategories[2]?.name),
|
|
getComponentMetadata(mockedCategories[3]?.name),
|
|
];
|
|
|
|
const mockOnCloseModal: MockFunction = getJestMockFunction();
|
|
const mockOnComponentClick: MockFunction = getJestMockFunction();
|
|
|
|
it("should render without crashing", () => {
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={mockedComponents}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
});
|
|
|
|
it("should display search input", () => {
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={mockedComponents}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
expect(
|
|
screen.getByPlaceholderText("Search components by name, description, or category"),
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
it("should display categories and components", () => {
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={mockedComponents}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
for (const cat of mockedCategories) {
|
|
expect(screen.getByText(cat.name)).toBeInTheDocument();
|
|
}
|
|
for (const comp of mockedComponents) {
|
|
expect(screen.getByText(comp.title)).toBeInTheDocument();
|
|
}
|
|
});
|
|
|
|
it("should call onCloseModal when the close button is clicked", () => {
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={mockedComponents}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
fireEvent.click(screen.getByText("Close panel"));
|
|
expect(mockOnCloseModal).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should call onComponentClick when a component is selected", () => {
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={mockedComponents}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
for (const [idx, comp] of mockedComponents.entries()) {
|
|
// simulate selecting a component
|
|
fireEvent.click(screen.getByText(comp.title));
|
|
expect(screen.getByText("Add to Workflow")).not.toBeDisabled();
|
|
|
|
// simulate submitting
|
|
fireEvent.click(screen.getByText("Add to Workflow"));
|
|
|
|
// check if onComponentClick was called with the selected component's metadata
|
|
expect(mockOnComponentClick).toHaveBeenNthCalledWith(idx + 1, comp);
|
|
}
|
|
});
|
|
|
|
it("should display a message when no components are available", () => {
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={[]}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
expect(
|
|
screen.getByText(
|
|
"No components that match your search. If you are looking for an integration that does not exist currently - you can use Custom Code or API component to build anything you like.",
|
|
),
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
it("should not display categories when there are no categories", () => {
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={mockedComponents}
|
|
categories={[]}
|
|
/>,
|
|
);
|
|
mockedCategories.forEach((category: ComponentCategory) => {
|
|
expect(screen.queryByText(category.name)).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it("should display no components message when search yields no results", () => {
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={mockedComponents}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
fireEvent.change(screen.getByPlaceholderText("Search components by name, description, or category"), {
|
|
target: { value: "Non-existent Ccmponent" },
|
|
});
|
|
expect(
|
|
screen.getByText(
|
|
"No components that match your search. If you are looking for an integration that does not exist currently - you can use Custom Code or API component to build anything you like.",
|
|
),
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
it("should disable submit button prop when no component is selected", () => {
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={mockedComponents}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
const submitButton: HTMLElement = screen.getByText("Add to Workflow");
|
|
expect(submitButton).toBeDisabled();
|
|
});
|
|
|
|
it("should change submitButtonDisabled to false when a component is selected", () => {
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={mockedComponents}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
for (const comp of mockedComponents) {
|
|
fireEvent.click(screen.getByText(comp.title));
|
|
const submitButton: HTMLElement = screen.getByText("Add to Workflow");
|
|
expect(submitButton).not.toBeDisabled();
|
|
}
|
|
});
|
|
|
|
// search tests
|
|
|
|
it("should filter components based on search input", () => {
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={mockedComponents}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
|
|
mockedComponents.forEach((comp: ComponentMetadata) => {
|
|
const partialTitle: string = comp.title.substring(
|
|
0,
|
|
comp.title.length - comp.title.length / 2,
|
|
);
|
|
fireEvent.change(screen.getByPlaceholderText("Search components by name, description, or category"), {
|
|
target: { value: partialTitle },
|
|
});
|
|
expect(screen.getByText(comp.title)).toBeInTheDocument();
|
|
|
|
// check other components are not displayed
|
|
mockedComponents
|
|
.filter((c: ComponentMetadata) => {
|
|
return c.title !== comp.title;
|
|
})
|
|
.forEach((c: ComponentMetadata) => {
|
|
return expect(screen.queryByText(c.title)).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should filter components based on description when searching", () => {
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={mockedComponents}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
mockedComponents.forEach((comp: ComponentMetadata) => {
|
|
fireEvent.change(screen.getByPlaceholderText("Search components by name, description, or category"), {
|
|
target: { value: comp.description },
|
|
});
|
|
expect(screen.getByText(comp.title)).toBeInTheDocument();
|
|
|
|
// check other components are not displayed
|
|
mockedComponents
|
|
.filter((c: ComponentMetadata) => {
|
|
return c.title !== comp.title;
|
|
})
|
|
.forEach((c: ComponentMetadata) => {
|
|
return expect(screen.queryByText(c.title)).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should filter components based on category when searching", () => {
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={mockedComponents}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
mockedComponents.forEach((comp: ComponentMetadata) => {
|
|
fireEvent.change(screen.getByPlaceholderText("Search components by name, description, or category"), {
|
|
target: { value: comp.category },
|
|
});
|
|
expect(screen.getByText(comp.title)).toBeInTheDocument();
|
|
|
|
// check other components are not displayed
|
|
mockedComponents
|
|
.filter((c: ComponentMetadata) => {
|
|
return c.category !== comp.category;
|
|
})
|
|
.forEach((c: ComponentMetadata) => {
|
|
return expect(screen.queryByText(c.title)).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should show all components when search is cleared", () => {
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={mockedComponents}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
mockedComponents.forEach((comp: ComponentMetadata) => {
|
|
const searchInput: HTMLElement = screen.getByPlaceholderText(
|
|
"Search components by name, description, or category",
|
|
);
|
|
fireEvent.change(searchInput, { target: { value: comp.title } });
|
|
fireEvent.change(searchInput, { target: { value: "" } }); // clear search
|
|
|
|
mockedComponents.forEach((c: ComponentMetadata) => {
|
|
return expect(screen.getByText(c.title)).toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should return multiple components when similar titles match", () => {
|
|
// we add a new component where its title is a substring of another component's title
|
|
const localComponents: ComponentMetadata[] = [...mockedComponents];
|
|
const commonWord: string = localComponents[0]?.title.substring(0, 5) || "";
|
|
const newComponent: ComponentMetadata = getComponentMetadata(
|
|
mockedCategories[1]?.name,
|
|
);
|
|
newComponent.title += commonWord;
|
|
localComponents.push(newComponent);
|
|
const componentsWithCommonWord: ComponentMetadata[] =
|
|
localComponents.filter((comp: ComponentMetadata) => {
|
|
return comp.title.includes(commonWord);
|
|
});
|
|
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={localComponents}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
|
|
fireEvent.change(screen.getByPlaceholderText("Search components by name, description, or category"), {
|
|
target: { value: commonWord },
|
|
});
|
|
componentsWithCommonWord.forEach((comp: ComponentMetadata) => {
|
|
expect(screen.getByText(comp.title)).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it("should return return components with similar descriptions", () => {
|
|
// we add a new component where its title is a substring of another component's description
|
|
const localComponents: ComponentMetadata[] = [...mockedComponents];
|
|
const partialDescription: string =
|
|
localComponents[0]?.description.substring(0, 10) || "";
|
|
const newComponent: ComponentMetadata = getComponentMetadata(
|
|
mockedCategories[1]?.name,
|
|
);
|
|
newComponent.title = partialDescription || "";
|
|
localComponents.push(newComponent);
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={localComponents}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
|
|
fireEvent.change(screen.getByPlaceholderText("Search components by name, description, or category"), {
|
|
target: { value: partialDescription },
|
|
});
|
|
expect(
|
|
screen.getAllByText(new RegExp(partialDescription, "i")),
|
|
).toHaveLength(2);
|
|
});
|
|
|
|
it("should return components with the same category", () => {
|
|
// we add two components with the same category as the first component
|
|
const localComponents: ComponentMetadata[] = [...mockedComponents];
|
|
const commonCategory: string | undefined = localComponents[0]?.category;
|
|
localComponents.push(getComponentMetadata(commonCategory));
|
|
localComponents.push(getComponentMetadata(commonCategory));
|
|
const componentsInCommonCategory: ComponentMetadata[] =
|
|
localComponents.filter((comp: ComponentMetadata) => {
|
|
return comp.category === commonCategory;
|
|
});
|
|
|
|
render(
|
|
<ComponentsModal
|
|
componentsType={ComponentType.Component}
|
|
onCloseModal={mockOnCloseModal}
|
|
onComponentClick={mockOnComponentClick}
|
|
components={localComponents}
|
|
categories={mockedCategories}
|
|
/>,
|
|
);
|
|
|
|
fireEvent.change(screen.getByPlaceholderText("Search components by name, description, or category"), {
|
|
target: { value: commonCategory },
|
|
});
|
|
componentsInCommonCategory.forEach((comp: ComponentMetadata) => {
|
|
expect(screen.getByText(comp.title)).toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|