@octopusdeploy/design-system-components
Version:
The design systems component library.
249 lines (248 loc) • 11.7 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("@testing-library/react");
const user_event_1 = __importDefault(require("@testing-library/user-event"));
const vitest_1 = require("vitest");
const NumberField_1 = require("../NumberField");
(0, vitest_1.describe)("<NumberField />", () => {
(0, vitest_1.test)("renders with correct label", () => {
const input = renderNumberInput({ label: "Test Label" });
input.shouldHaveLabel("Test Label");
input.shouldBeInDocument();
});
(0, vitest_1.test)("renders with placeholder text", () => {
const placeholder = "Enter a number";
const input = renderNumberInput({ label: "Test Label", placeholder });
input.shouldHavePlaceholder(placeholder);
});
(0, vitest_1.test)("renders with initial value", () => {
const value = 42;
const input = renderNumberInput({ label: "Test Label", value });
input.shouldHaveValue(value);
});
(0, vitest_1.test)("renders with description", () => {
const description = "This is a helpful description";
const input = renderNumberInput({ label: "Test Label", description });
input.shouldHaveDescription(description);
});
(0, vitest_1.test)("renders with error state", () => {
const validationMessage = "This field is required";
const input = renderNumberInput({ label: "Test Label", validationMessage });
input.shouldHaveError(validationMessage);
input.shouldHaveAriaInvalid(true);
});
(0, vitest_1.test)("handles empty onChange gracefully", () => {
(0, react_1.render)((0, jsx_runtime_1.jsx)(NumberField_1.NumberField, { label: "Test Label", onChange: vitest_1.vi.fn() }));
const input = react_1.screen.getByRole("spinbutton");
(0, vitest_1.expect)(input).toBeInTheDocument();
});
(0, vitest_1.test)("calls onChange with parsed number when user types", async () => {
const input = renderNumberInput({ label: "Test Label" });
await input.type("123");
input.shouldHaveCalledOnChangeWith(123);
});
(0, vitest_1.test)("calls onChange with undefined when input is cleared", async () => {
const input = renderNumberInput({ label: "Test Label", value: 123 });
await input.clear();
input.shouldHaveCalledOnChangeWith(undefined);
});
(0, vitest_1.test)("parses decimal numbers correctly", async () => {
const input = renderNumberInput({ label: "Test Label" });
await input.type("123.45");
input.shouldHaveCalledOnChangeWith(123.45);
});
(0, vitest_1.test)("parses negative numbers correctly", async () => {
const input = renderNumberInput({ label: "Test Label" });
await input.type("-42");
input.shouldHaveCalledOnChangeWith(-42);
});
(0, vitest_1.test)("handles partial invalid input", async () => {
const input = renderNumberInput({ label: "Test Label" });
await input.type("12abc");
// parseFloat("12abc") returns 12
input.shouldHaveCalledOnChangeWith(12);
});
(0, vitest_1.test)("uses provided min attribute", () => {
const input = renderNumberInput({ label: "Test Label", min: 0 });
input.shouldHaveMin(0);
});
(0, vitest_1.test)("uses provided max attribute", () => {
const input = renderNumberInput({ label: "Test Label", max: 100 });
input.shouldHaveMax(100);
});
(0, vitest_1.test)("uses provided step attribute", () => {
const input = renderNumberInput({ label: "Test Label", step: 0.5 });
input.shouldHaveStep(0.5);
});
(0, vitest_1.test)("uses 'any' as step attribute", () => {
const input = renderNumberInput({ label: "Test Label", step: "any" });
input.shouldHaveStep("any");
});
(0, vitest_1.describe)("accessibility attributes", () => {
(0, vitest_1.test)("has correct accessibility attributes when error is present", () => {
const validationMessage = "Invalid input";
const input = renderNumberInput({ label: "Test Label", validationMessage });
input.shouldHaveAriaInvalid(true);
input.shouldHaveAriaDescribedByError();
});
(0, vitest_1.test)("has correct accessibility attributes when description is present", () => {
const description = "Help text";
const input = renderNumberInput({ label: "Test Label", description });
input.shouldHaveAriaDescribedByDescription();
});
(0, vitest_1.test)("has correct accessibility attributes when both description and error are present", () => {
const description = "Help text";
const validationMessage = "Error message";
const input = renderNumberInput({ label: "Test Label", description, validationMessage });
input.shouldHaveAriaDescribedByBoth();
input.shouldHaveAriaInvalid(true);
});
});
});
function renderNumberInput(options) {
const onChange = vitest_1.vi.fn();
(0, react_1.render)((0, jsx_runtime_1.jsx)(NumberField_1.NumberField, { ...options, onChange: onChange }));
return getNumberInputInteractions(options.label, onChange);
}
function getNumberInputInteractions(label, onChange) {
const input = () => react_1.screen.getByLabelText(label);
const user = user_event_1.default.setup();
return {
shouldBeInDocument: () => {
(0, vitest_1.expect)(input()).toBeInTheDocument();
},
shouldHaveLabel: (labelText) => {
(0, vitest_1.expect)(react_1.screen.getByLabelText(labelText)).toBeInTheDocument();
},
shouldHavePlaceholder: (placeholder) => {
(0, vitest_1.expect)(input()).toHaveAttribute("placeholder", placeholder);
},
shouldHaveValue: (value) => {
// HTML input.value is always a string, so convert expected value to string for comparison
const inputElement = input();
if (inputElement instanceof HTMLInputElement) {
(0, vitest_1.expect)(inputElement.value).toBe(String(value));
}
else {
(0, vitest_1.expect)(inputElement).toHaveValue(String(value));
}
},
shouldHaveDescription: (description) => {
(0, vitest_1.expect)(react_1.screen.getByText(description)).toBeInTheDocument();
},
shouldHaveError: (error) => {
(0, vitest_1.expect)(react_1.screen.getByText(error)).toBeInTheDocument();
},
shouldBeDisabled: () => {
(0, vitest_1.expect)(input()).toBeDisabled();
},
shouldBeReadOnly: () => {
(0, vitest_1.expect)(input()).toHaveAttribute("readonly");
},
shouldHaveFocus: () => {
(0, vitest_1.expect)(input()).toHaveFocus();
},
shouldHaveRequiredMarker: () => {
(0, vitest_1.expect)(react_1.screen.getByText(/\(required\)/)).toBeInTheDocument();
},
shouldHaveOptionalMarker: () => {
(0, vitest_1.expect)(react_1.screen.getByText(/\(optional\)/)).toBeInTheDocument();
},
shouldHaveDefaultMarker: () => {
(0, vitest_1.expect)(react_1.screen.getByText(/\(Default\)/)).toBeInTheDocument();
},
shouldHaveName: (name) => {
(0, vitest_1.expect)(input()).toHaveAttribute("name", name);
},
shouldHaveMin: (min) => {
(0, vitest_1.expect)(input()).toHaveAttribute("min", min.toString());
},
shouldHaveMax: (max) => {
(0, vitest_1.expect)(input()).toHaveAttribute("max", max.toString());
},
shouldHaveStep: (step) => {
(0, vitest_1.expect)(input()).toHaveAttribute("step", step.toString());
},
shouldHaveAriaInvalid: (invalid) => {
(0, vitest_1.expect)(input()).toHaveAttribute("aria-invalid", invalid.toString());
},
shouldHaveAriaDescribedByError: () => {
const inputElement = input();
const ariaDescribedBy = inputElement.getAttribute("aria-describedby");
(0, vitest_1.expect)(ariaDescribedBy).toContain("validation");
},
shouldHaveAriaDescribedByDescription: () => {
const inputElement = input();
const ariaDescribedBy = inputElement.getAttribute("aria-describedby");
(0, vitest_1.expect)(ariaDescribedBy).toContain("description");
},
shouldHaveAriaDescribedByBoth: () => {
const inputElement = input();
const ariaDescribedBy = inputElement.getAttribute("aria-describedby");
(0, vitest_1.expect)(ariaDescribedBy).toContain("description");
(0, vitest_1.expect)(ariaDescribedBy).toContain("validation");
},
type: async (text) => {
await user.clear(input());
await user.type(input(), text);
},
clear: async () => {
await user.clear(input());
},
attemptToType: async (text) => {
await user.type(input(), text);
},
shouldHaveCalledOnChangeWith: (value) => {
// Check the last call since typing fires onChange multiple times
(0, vitest_1.expect)(onChange).toHaveBeenLastCalledWith(value);
},
shouldNotHaveCalledOnChange: () => {
(0, vitest_1.expect)(onChange).not.toHaveBeenCalled();
},
shouldHaveSuffix: (suffixText) => {
const suffixElement = react_1.screen.getByText(suffixText);
(0, vitest_1.expect)(suffixElement).toBeInTheDocument();
},
shouldHaveSuffixElement: () => {
const spinbutton = react_1.screen.getByRole("spinbutton");
(0, vitest_1.expect)(spinbutton).toBeInTheDocument();
},
shouldHaveSuffixButton: () => {
const suffixButton = react_1.screen.queryByRole("button");
(0, vitest_1.expect)(suffixButton).toBeInTheDocument();
},
shouldNotHaveSuffix: () => {
const suffixButton = react_1.screen.queryByRole("button");
(0, vitest_1.expect)(suffixButton).not.toBeInTheDocument();
const input = react_1.screen.getByRole("spinbutton");
(0, vitest_1.expect)(input).not.toHaveClass("inputStylesWithSuffix");
},
clickSuffixButton: async () => {
const suffixButton = react_1.screen.getByRole("button");
await user.click(suffixButton);
},
shouldHaveCalledSuffixClick: (clickHandler) => {
(0, vitest_1.expect)(clickHandler).toHaveBeenCalled();
},
shouldHavePrefix: (prefixText) => {
const prefixElement = react_1.screen.getByText(prefixText);
(0, vitest_1.expect)(prefixElement).toBeInTheDocument();
},
shouldHavePrefixElement: () => {
const spinbutton = react_1.screen.getByRole("spinbutton");
(0, vitest_1.expect)(spinbutton).toBeInTheDocument();
},
shouldNotHavePrefix: () => {
const input = react_1.screen.getByRole("spinbutton");
(0, vitest_1.expect)(input).not.toHaveClass("inputStylesWithPrefix");
},
shouldHavePrefixWithTitle: (title) => {
const prefixElement = react_1.screen.getByTitle(title);
(0, vitest_1.expect)(prefixElement).toBeInTheDocument();
},
};
}