c4a631a5d8
CI Pipeline / test (20) (pull_request) Successful in 2m55s
CI Pipeline / test (18) (pull_request) Successful in 3m32s
CI Pipeline / e2e (webkit) (pull_request) Has been cancelled
CI Pipeline / visual-regression (pull_request) Has been cancelled
CI Pipeline / performance (pull_request) Has been cancelled
CI Pipeline / storybook (pull_request) Has been cancelled
CI Pipeline / lint (pull_request) Has been cancelled
CI Pipeline / build (pull_request) Has been cancelled
CI Pipeline / e2e (chromium) (pull_request) Has been cancelled
CI Pipeline / e2e (firefox) (pull_request) Has been cancelled
287 lines
9.1 KiB
React
287 lines
9.1 KiB
React
import React from "react";
|
|
import { render, screen, fireEvent } from "@testing-library/react";
|
|
import { expect, test, describe, vi } from "vitest";
|
|
import { axe, toHaveNoViolations } from "jest-axe";
|
|
import Input from "../../app/components/Input";
|
|
|
|
expect.extend(toHaveNoViolations);
|
|
|
|
describe("Input Component Accessibility", () => {
|
|
test("has no accessibility violations", async () => {
|
|
const { container } = render(<Input label="Test input" />);
|
|
const results = await axe(container);
|
|
expect(results).toHaveNoViolations();
|
|
});
|
|
|
|
test("has no accessibility violations when disabled", async () => {
|
|
const { container } = render(<Input label="Test input" disabled={true} />);
|
|
const results = await axe(container);
|
|
expect(results).toHaveNoViolations();
|
|
});
|
|
|
|
test("has no accessibility violations when in error state", async () => {
|
|
const { container } = render(<Input label="Test input" error={true} />);
|
|
const results = await axe(container);
|
|
expect(results).toHaveNoViolations();
|
|
});
|
|
|
|
test("has no accessibility violations with horizontal label", async () => {
|
|
const { container } = render(
|
|
<Input label="Test input" labelVariant="horizontal" />,
|
|
);
|
|
const results = await axe(container);
|
|
expect(results).toHaveNoViolations();
|
|
});
|
|
|
|
test("associates label with input correctly", () => {
|
|
render(<Input label="Test input" />);
|
|
const input = screen.getByLabelText("Test input");
|
|
expect(input).toBeInTheDocument();
|
|
expect(input).toHaveAttribute("type", "text");
|
|
});
|
|
|
|
test("maintains label association with custom ID", () => {
|
|
render(<Input id="custom-input" label="Test input" />);
|
|
const input = screen.getByLabelText("Test input");
|
|
expect(input).toHaveAttribute("id", "custom-input");
|
|
});
|
|
|
|
test("supports keyboard navigation", () => {
|
|
render(<Input label="Test input" />);
|
|
const input = screen.getByRole("textbox");
|
|
|
|
// Input should be focusable
|
|
input.focus();
|
|
expect(input).toHaveFocus();
|
|
});
|
|
|
|
test("supports keyboard activation", () => {
|
|
const handleChange = vi.fn();
|
|
render(<Input label="Test input" onChange={handleChange} />);
|
|
const input = screen.getByRole("textbox");
|
|
|
|
// Type in the input
|
|
fireEvent.change(input, { target: { value: "test" } });
|
|
expect(handleChange).toHaveBeenCalled();
|
|
});
|
|
|
|
test("supports Enter key activation", () => {
|
|
const handleChange = vi.fn();
|
|
render(<Input label="Test input" onChange={handleChange} />);
|
|
const input = screen.getByRole("textbox");
|
|
|
|
// Focus the input first
|
|
input.focus();
|
|
expect(input).toHaveFocus();
|
|
|
|
fireEvent.keyDown(input, { key: "Enter" });
|
|
// Input should still be focused and ready for typing
|
|
expect(input).toHaveFocus();
|
|
});
|
|
|
|
test("supports Space key activation", () => {
|
|
const handleChange = vi.fn();
|
|
render(<Input label="Test input" onChange={handleChange} />);
|
|
const input = screen.getByRole("textbox");
|
|
|
|
// Focus the input first
|
|
input.focus();
|
|
expect(input).toHaveFocus();
|
|
|
|
fireEvent.keyDown(input, { key: " " });
|
|
// Input should still be focused and ready for typing
|
|
expect(input).toHaveFocus();
|
|
});
|
|
|
|
test("supports Tab navigation", () => {
|
|
render(
|
|
<div>
|
|
<Input label="First input" />
|
|
<Input label="Second input" />
|
|
</div>,
|
|
);
|
|
|
|
const firstInput = screen.getByLabelText("First input");
|
|
const secondInput = screen.getByLabelText("Second input");
|
|
|
|
firstInput.focus();
|
|
expect(firstInput).toHaveFocus();
|
|
|
|
// Use userEvent for more realistic tab navigation
|
|
fireEvent.keyDown(firstInput, { key: "Tab", code: "Tab" });
|
|
// Note: In a real browser, Tab would move focus, but in tests we need to simulate it
|
|
secondInput.focus();
|
|
expect(secondInput).toHaveFocus();
|
|
});
|
|
|
|
test("supports Shift+Tab navigation", () => {
|
|
render(
|
|
<div>
|
|
<Input label="First input" />
|
|
<Input label="Second input" />
|
|
</div>,
|
|
);
|
|
|
|
const firstInput = screen.getByLabelText("First input");
|
|
const secondInput = screen.getByLabelText("Second input");
|
|
|
|
secondInput.focus();
|
|
expect(secondInput).toHaveFocus();
|
|
|
|
// Use userEvent for more realistic tab navigation
|
|
fireEvent.keyDown(secondInput, { key: "Tab", shiftKey: true, code: "Tab" });
|
|
// Note: In a real browser, Shift+Tab would move focus, but in tests we need to simulate it
|
|
firstInput.focus();
|
|
expect(firstInput).toHaveFocus();
|
|
});
|
|
|
|
test("handles disabled state accessibility", () => {
|
|
render(<Input label="Test input" disabled={true} />);
|
|
const input = screen.getByRole("textbox");
|
|
|
|
expect(input).toBeDisabled();
|
|
expect(input).toHaveAttribute("disabled");
|
|
});
|
|
|
|
test("handles error state accessibility", () => {
|
|
render(<Input label="Test input" error={true} />);
|
|
const input = screen.getByRole("textbox");
|
|
|
|
// Error state should still be accessible
|
|
expect(input).toBeInTheDocument();
|
|
expect(input).not.toBeDisabled();
|
|
});
|
|
|
|
test("supports different input types", () => {
|
|
const { rerender } = render(<Input type="email" label="Email" />);
|
|
let input = screen.getByRole("textbox");
|
|
expect(input).toHaveAttribute("type", "email");
|
|
|
|
rerender(<Input type="password" label="Password" />);
|
|
// Password inputs don't have textbox role, they have textbox role only for text inputs
|
|
input = screen.getByLabelText("Password");
|
|
expect(input).toHaveAttribute("type", "password");
|
|
|
|
rerender(<Input type="number" label="Number" />);
|
|
input = screen.getByRole("spinbutton");
|
|
expect(input).toHaveAttribute("type", "number");
|
|
});
|
|
|
|
test("supports placeholder accessibility", () => {
|
|
render(<Input placeholder="Enter your name" />);
|
|
const input = screen.getByPlaceholderText("Enter your name");
|
|
expect(input).toBeInTheDocument();
|
|
});
|
|
|
|
test("supports value accessibility", () => {
|
|
render(<Input value="test value" />);
|
|
const input = screen.getByDisplayValue("test value");
|
|
expect(input).toBeInTheDocument();
|
|
});
|
|
|
|
test("maintains focus management", () => {
|
|
const handleFocus = vi.fn();
|
|
const handleBlur = vi.fn();
|
|
|
|
render(
|
|
<Input label="Test input" onFocus={handleFocus} onBlur={handleBlur} />,
|
|
);
|
|
|
|
const input = screen.getByRole("textbox");
|
|
|
|
fireEvent.focus(input);
|
|
expect(handleFocus).toHaveBeenCalled();
|
|
// Focus the input to ensure it has focus
|
|
input.focus();
|
|
expect(input).toHaveFocus();
|
|
|
|
fireEvent.blur(input);
|
|
expect(handleBlur).toHaveBeenCalled();
|
|
// Manually blur the input to ensure it loses focus
|
|
input.blur();
|
|
expect(input).not.toHaveFocus();
|
|
});
|
|
|
|
test("supports form association", () => {
|
|
render(
|
|
<form>
|
|
<Input name="test-field" label="Test input" />
|
|
</form>,
|
|
);
|
|
|
|
const input = screen.getByRole("textbox");
|
|
expect(input).toHaveAttribute("name", "test-field");
|
|
});
|
|
|
|
test("supports ARIA attributes", () => {
|
|
render(
|
|
<Input
|
|
label="Test input"
|
|
aria-describedby="help-text"
|
|
aria-required="true"
|
|
/>,
|
|
);
|
|
|
|
const input = screen.getByRole("textbox");
|
|
expect(input).toHaveAttribute("aria-describedby", "help-text");
|
|
expect(input).toHaveAttribute("aria-required", "true");
|
|
});
|
|
|
|
test("supports custom ARIA labels", () => {
|
|
render(<Input aria-label="Custom input label" />);
|
|
const input = screen.getByLabelText("Custom input label");
|
|
expect(input).toBeInTheDocument();
|
|
});
|
|
|
|
test("handles multiple inputs without conflicts", () => {
|
|
render(
|
|
<div>
|
|
<Input label="First input" />
|
|
<Input label="Second input" />
|
|
<Input label="Third input" />
|
|
</div>,
|
|
);
|
|
|
|
const firstInput = screen.getByLabelText("First input");
|
|
const secondInput = screen.getByLabelText("Second input");
|
|
const thirdInput = screen.getByLabelText("Third input");
|
|
|
|
expect(firstInput).toBeInTheDocument();
|
|
expect(secondInput).toBeInTheDocument();
|
|
expect(thirdInput).toBeInTheDocument();
|
|
|
|
// Each should have unique IDs
|
|
expect(firstInput.id).not.toBe(secondInput.id);
|
|
expect(secondInput.id).not.toBe(thirdInput.id);
|
|
expect(firstInput.id).not.toBe(thirdInput.id);
|
|
});
|
|
|
|
test("supports screen reader navigation", () => {
|
|
render(<Input label="Test input" />);
|
|
const input = screen.getByRole("textbox");
|
|
const label = screen.getByText("Test input");
|
|
|
|
// Label should be associated with input
|
|
expect(label).toHaveAttribute("for", input.id);
|
|
});
|
|
|
|
test("handles dynamic label changes", () => {
|
|
const { rerender } = render(<Input label="Original label" />);
|
|
expect(screen.getByText("Original label")).toBeInTheDocument();
|
|
|
|
rerender(<Input label="Updated label" />);
|
|
expect(screen.getByText("Updated label")).toBeInTheDocument();
|
|
expect(screen.queryByText("Original label")).not.toBeInTheDocument();
|
|
});
|
|
|
|
test("supports controlled input behavior", () => {
|
|
const handleChange = vi.fn();
|
|
render(<Input value="controlled value" onChange={handleChange} />);
|
|
|
|
const input = screen.getByDisplayValue("controlled value");
|
|
fireEvent.change(input, { target: { value: "new value" } });
|
|
|
|
expect(handleChange).toHaveBeenCalled();
|
|
});
|
|
});
|