Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { render, screen } from "@testing-library/react";
import { axe } from "@/utils/a11y-test";
import { InspectButton } from "../index";

const baseProps = {
displayOutputPreview: true,
unknownOutput: false,
errorOutput: false,
isToolMode: false,
title: "Output",
onClick: jest.fn(),
id: "ChatOutput",
};

describe("InspectButton accessibility", () => {
it("should_have_no_axe_violations", async () => {
const { container } = render(
<InspectButton
{...baseProps}
disabled={false}
ariaLabel="Inspect output"
/>,
);

expect(await axe(container)).toHaveNoViolations();
});

it("should_expose_inspect_output_as_accessible_name", () => {
render(
<InspectButton
{...baseProps}
disabled={false}
ariaLabel="Inspect output"
/>,
);

expect(
screen.getByRole("button", { name: "Inspect output" }),
).toBeInTheDocument();
});

it("should_expose_build_component_first_as_accessible_name_when_not_built", () => {
render(
<InspectButton
{...baseProps}
displayOutputPreview={false}
disabled={true}
ariaLabel="Please build the component first"
/>,
);

expect(
screen.getByRole("button", { name: "Please build the component first" }),
).toBeInTheDocument();
});

it("should_expose_output_cant_be_displayed_as_accessible_name_when_unknown", () => {
render(
<InspectButton
{...baseProps}
unknownOutput={true}
disabled={false}
ariaLabel="Output can't be displayed"
/>,
);

expect(
screen.getByRole("button", { name: "Output can't be displayed" }),
).toBeInTheDocument();
});

it("should_remain_disabled_while_still_exposing_its_accessible_name", () => {
render(
<InspectButton
{...baseProps}
disabled={true}
ariaLabel="Please build the component first"
/>,
);

const button = screen.getByRole("button", {
name: "Please build the component first",
});
expect(button).toBeDisabled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,22 @@ import HandleRenderComponent from "../handleRenderComponent";
import OutputComponent from "../OutputComponent";
import OutputModal from "../outputModal";

const _EyeIcon = memo(
({ hidden, className }: { hidden: boolean; className: string }) => (
<IconComponent
className={className}
strokeWidth={ICON_STROKE_WIDTH}
name={hidden ? "EyeOff" : "Eye"}
/>
),
);
const SnowflakeIcon = memo(() => (
<IconComponent className="!w-3 !h-3 text-ice" name="Snowflake" />
));

type InspectButtonProps = {
disabled: boolean | undefined;
displayOutputPreview: boolean;
unknownOutput: boolean | undefined;
errorOutput: boolean;
isToolMode: boolean;
title: string;
onClick: () => void;
id: string;
ariaLabel: string;
};

const InspectButton = memo(
forwardRef(
(
Expand All @@ -67,22 +70,15 @@ const InspectButton = memo(
title,
onClick,
id,
}: {
disabled: boolean | undefined;
displayOutputPreview: boolean;
unknownOutput: boolean | undefined;
errorOutput: boolean;
isToolMode: boolean;
title: string;
onClick: () => void;
id: string;
},
ariaLabel,
}: InspectButtonProps,
ref: React.ForwardedRef<HTMLButtonElement>,
) => (
<Button
ref={ref}
disabled={disabled}
data-testid={`output-inspection-${title.toLowerCase()}-${classNameFromType(id).toLowerCase()}`}
aria-label={ariaLabel}
unstyled
onClick={onClick}
>
Expand All @@ -107,6 +103,8 @@ const InspectButton = memo(
);
InspectButton.displayName = "InspectButton";

export { InspectButton };

const MemoizedOutputComponent = memo(OutputComponent);

function NodeOutputField({
Expand Down Expand Up @@ -467,6 +465,13 @@ function NodeOutputField({
title={title}
onClick={() => {}}
id={data?.type}
ariaLabel={
displayOutputPreview
? unknownOutput || emptyOutput
? t("node.outputCantBeDisplayed")
: t("node.inspectOutput")
: t("node.buildComponentFirst")
}
/>
</OutputModal>
{looping && (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { render, screen } from "@testing-library/react";
import { BuildStatus } from "@/constants/enums";
import useAlertStore from "@/stores/alertStore";
import useFlowStore from "@/stores/flowStore";
import { useShortcutsStore } from "@/stores/shortcuts";
import { useUtilityStore } from "@/stores/utilityStore";
import type { NodeDataType } from "@/types/flow";
import type { shortcutsStoreType } from "@/types/store";
import type { AlertStoreType } from "@/types/zustand/alert";
import type { FlowStoreType } from "@/types/zustand/flow";
import type { UtilityStoreType } from "@/types/zustand/utility";
import { axe } from "@/utils/a11y-test";
import NodeStatus from "../index";

jest.mock("@/CustomNodes/helpers/mutate-template", () => ({
mutateTemplate: jest.fn(),
}));

jest.mock("@/controllers/API/queries/nodes/use-post-template-value", () => ({
usePostTemplateValue: () => jest.fn(),
}));

jest.mock("@/customization/utils/analytics", () => ({
track: jest.fn(),
}));

jest.mock("@/customization/utils/custom-open-new-tab", () => ({
customOpenNewTab: jest.fn(),
}));

jest.mock("../../HumanInputNodeBadge", () => ({
__esModule: true,
default: () => <div data-testid="human-input-badge" />,
useAwaitingHumanInput: () => false,
}));

jest.mock("../components/build-status-display", () => ({
__esModule: true,
default: () => <div data-testid="build-status-display" />,
}));

jest.mock("../../../../../components/common/genericIconComponent", () => ({
__esModule: true,
default: ({ name }: { name: string }) => (
<span data-testid={`icon-${name}`}>{name}</span>
),
}));

function resetStores() {
useFlowStore.setState({
flowBuildStatus: {},
buildFlow: jest.fn(),
isBuilding: false,
setNode: jest.fn(),
currentFlow: {
id: "flow-1",
locked: false,
} as FlowStoreType["currentFlow"],
setFlowPool: jest.fn(),
} as Partial<FlowStoreType>);
useUtilityStore.setState({
eventDelivery: undefined,
} as Partial<UtilityStoreType>);
useAlertStore.setState({
setErrorData: jest.fn(),
} as Partial<AlertStoreType>);
useShortcutsStore.setState({} as Partial<shortcutsStoreType>);
}

const baseProps = {
nodeId: "node-1",
display_name: "Chat Input",
setBorderColor: jest.fn(),
showNode: true,
data: { node: { template: {} } } as unknown as NodeDataType,
dismissAll: false,
isOutdated: false,
isUserEdited: false,
isBreakingChange: false,
getValidationStatus: jest.fn(() => null),
};

describe("NodeStatus accessibility", () => {
beforeEach(() => {
resetStores();
});

it("should_have_no_axe_violations_when_idle", async () => {
const { container } = render(
<NodeStatus {...baseProps} buildStatus={BuildStatus.TO_BUILD} />,
);

expect(await axe(container)).toHaveNoViolations();
});

it("should_expose_run_component_as_accessible_name_when_idle", () => {
render(<NodeStatus {...baseProps} buildStatus={BuildStatus.TO_BUILD} />);

expect(
screen.getByRole("button", { name: "Run component" }),
).toBeInTheDocument();
});

it("should_expose_connect_button_with_fallback_accessible_name", () => {
render(
<NodeStatus
{...baseProps}
data={
{
node: {
template: {
auth_link: { type: "auth", value: "" },
},
},
} as unknown as NodeDataType
}
buildStatus={BuildStatus.TO_BUILD}
/>,
);

expect(screen.getByRole("button", { name: "Connect" })).toBeInTheDocument();
});

it("should_expose_connect_button_with_custom_auth_tooltip", () => {
render(
<NodeStatus
{...baseProps}
data={
{
node: {
template: {
auth_link: {
type: "auth",
value: "",
auth_tooltip: "Sign in with Google",
},
},
},
} as unknown as NodeDataType
}
buildStatus={BuildStatus.TO_BUILD}
/>,
);

expect(
screen.getByRole("button", { name: "Sign in with Google" }),
).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ export default function NodeStatus({
)}
onClick={handleClickConnect}
data-testid={getDataTestId()}
aria-label={nodeAuth.auth_tooltip || "Connect"}
>
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2">
<IconComponent
Expand Down Expand Up @@ -551,6 +552,7 @@ export default function NodeStatus({
unstyled
className="nodrag button-run-bg group disabled:cursor-not-allowed disabled:opacity-50"
disabled={executeDenied && buildStatus !== BuildStatus.BUILDING}
aria-label={getTooltipContent()}
>
<div data-testid={`button_run_` + display_name.toLowerCase()}>
<IconComponent
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { render, screen } from "@testing-library/react";
import { axe } from "@/utils/a11y-test";
import QueryComponent from "../index";

const baseProps = {
id: "field-1",
value: "SELECT * FROM table",
editNode: false,
handleOnNewValue: jest.fn(),
disabled: false,
display_name: "Filter",
info: "Query filter",
};

describe("QueryComponent accessibility", () => {
it("should_have_no_axe_violations", async () => {
const { container } = render(<QueryComponent {...baseProps} />);

expect(await axe(container)).toHaveNoViolations();
});

it("should_expose_edit_text_as_accessible_name_on_the_expand_trigger", () => {
render(<QueryComponent {...baseProps} />);

expect(
screen.getByRole("button", { name: "Expand text editor" }),
).toBeInTheDocument();
});

it("should_expose_the_trigger_as_a_real_button_with_valid_aria_expanded", () => {
render(<QueryComponent {...baseProps} />);

const trigger = screen.getByRole("button", { name: "Expand text editor" });
expect(trigger.tagName).toBe("BUTTON");
expect(trigger).toHaveAttribute("aria-haspopup", "dialog");
expect(trigger).toHaveAttribute("aria-expanded", "false");
});

it("should_have_no_axe_violations_when_disabled", async () => {
const { container } = render(
<QueryComponent {...baseProps} disabled={true} />,
);

expect(await axe(container)).toHaveNoViolations();
});
});
Loading
Loading