diff --git a/tests/NumberInput.test.svelte b/tests/NumberInput.test.svelte
deleted file mode 100644
index c1176783..00000000
--- a/tests/NumberInput.test.svelte
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-{value}
-
- {
- console.log({ input: e.detail }); // null | number
- }}
- on:change={(e) => {
- console.log({ change: e.detail }); // null | number
- }}
- on:keydown
- on:keyup
- on:paste
-/>
-
- {
- console.log({ input: e.detail }); // null | number
- }}
- on:change={(e) => {
- console.log(e.detail); // null | number
- }}
- on:keydown
- on:keyup
- on:paste
-/>
-
-
diff --git a/tests/NumberInput/NumberInput.test.svelte b/tests/NumberInput/NumberInput.test.svelte
new file mode 100644
index 00000000..9794540c
--- /dev/null
+++ b/tests/NumberInput/NumberInput.test.svelte
@@ -0,0 +1,59 @@
+
+
+
+
+{value}
diff --git a/tests/NumberInput/NumberInput.test.ts b/tests/NumberInput/NumberInput.test.ts
new file mode 100644
index 00000000..769a9f7c
--- /dev/null
+++ b/tests/NumberInput/NumberInput.test.ts
@@ -0,0 +1,237 @@
+import { render, screen } from "@testing-library/svelte";
+import { user } from "../setup-tests";
+import NumberInput from "./NumberInput.test.svelte";
+import NumberInputCustom from "./NumberInputCustom.test.svelte";
+
+describe("NumberInput", () => {
+ it("should render with default props", () => {
+ render(NumberInput);
+
+ expect(screen.getByLabelText("Clusters")).toBeInTheDocument();
+ expect(screen.getByRole("spinbutton")).toHaveValue(0);
+ });
+
+ it("should handle step value", () => {
+ render(NumberInput, { props: { step: 0.1 } });
+
+ expect(screen.getByRole("spinbutton")).toHaveAttribute("step", "0.1");
+ });
+
+ it("should handle min and max values", () => {
+ render(NumberInput, { props: { min: 4, max: 20 } });
+
+ const input = screen.getByRole("spinbutton");
+ expect(input).toHaveAttribute("min", "4");
+ expect(input).toHaveAttribute("max", "20");
+ });
+
+ it("should handle different sizes", () => {
+ (["sm", "xl"] as const).forEach((size) => {
+ const { container } = render(NumberInput, {
+ props: { size },
+ });
+
+ const input = container.querySelector("input");
+ expect(input?.closest(".bx--number")).toHaveClass(`bx--number--${size}`);
+ container.remove();
+ });
+ });
+
+ it("should handle light variant", () => {
+ render(NumberInput, { props: { light: true } });
+
+ expect(screen.getByRole("spinbutton").closest(".bx--number")).toHaveClass(
+ "bx--number--light",
+ );
+ });
+
+ it("should handle disabled state", () => {
+ render(NumberInput, {
+ props: { disabled: true },
+ });
+
+ const input = screen.getByRole("spinbutton");
+ expect(input).toBeDisabled();
+ expect(screen.getByText("Clusters")).toHaveClass("bx--label--disabled");
+ });
+
+ it("should handle helper text", () => {
+ render(NumberInput, {
+ props: { helperText: "Helper text" },
+ });
+
+ expect(screen.getByText("Helper text")).toHaveClass(
+ "bx--form__helper-text",
+ );
+ });
+
+ it("should handle invalid state", () => {
+ render(NumberInput, {
+ props: { invalid: true, invalidText: "Invalid input" },
+ });
+
+ const input = screen.getByRole("spinbutton");
+ expect(input).toHaveAttribute("aria-invalid", "true");
+ expect(screen.getByText("Invalid input")).toBeInTheDocument();
+
+ expect(input.closest(".bx--number")).toHaveAttribute(
+ "data-invalid",
+ "true",
+ );
+ });
+
+ it("should handle warning state", () => {
+ render(NumberInput, {
+ props: { warn: true, warnText: "Warning message" },
+ });
+
+ const input = screen.getByRole("spinbutton");
+ expect(input.closest(".bx--number__input-wrapper")).toHaveClass(
+ "bx--number__input-wrapper--warning",
+ );
+ expect(screen.getByText("Warning message")).toBeInTheDocument();
+ });
+
+ it("should handle hidden label", () => {
+ render(NumberInput, { props: { hideLabel: true } });
+
+ expect(screen.getByText("Clusters")).toHaveClass("bx--visually-hidden");
+ });
+
+ it("should handle custom id", () => {
+ render(NumberInput, { props: { id: "custom-id" } });
+
+ const input = screen.getByRole("spinbutton");
+ expect(input).toHaveAttribute("id", "custom-id");
+ expect(screen.getByText("Clusters")).toHaveAttribute("for", "custom-id");
+ });
+
+ it("should handle custom name", () => {
+ render(NumberInput, { props: { name: "custom-name" } });
+
+ expect(screen.getByRole("spinbutton")).toHaveAttribute(
+ "name",
+ "custom-name",
+ );
+ });
+
+ it("should handle readonly state", () => {
+ render(NumberInput, { props: { readonly: true } });
+
+ expect(screen.getByRole("spinbutton")).toHaveAttribute("readonly");
+ });
+
+ it("should handle hidden steppers", () => {
+ render(NumberInput, { props: { hideSteppers: true } });
+
+ expect(
+ screen.queryByRole("button", { name: "Increment number" }),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.queryByRole("button", { name: "Decrement number" }),
+ ).not.toBeInTheDocument();
+ });
+
+ // TODO(bug): The icon descriptions are not being applied.
+ it.skip("should handle custom icon descriptions", () => {
+ render(NumberInput, {
+ props: { iconDescription: "Custom description" },
+ });
+
+ screen.getAllByRole("button").forEach((button) => {
+ expect(button).toHaveAttribute("title", "Custom description");
+ });
+ });
+
+ it("should handle custom slots", () => {
+ render(NumberInputCustom);
+
+ expect(screen.getByText("Custom Label Text")).toBeInTheDocument();
+ });
+
+ it("should handle value binding", async () => {
+ render(NumberInput);
+
+ const input = screen.getByRole("spinbutton");
+ await user.type(input, "5");
+ expect(screen.getByTestId("value").textContent).toBe("5");
+ });
+
+ it("should handle increment/decrement buttons", async () => {
+ render(NumberInput);
+
+ const incrementButton = screen.getByRole("button", {
+ name: "Increment number",
+ });
+ const decrementButton = screen.getByRole("button", {
+ name: "Decrement number",
+ });
+
+ await user.click(incrementButton);
+ expect(screen.getByTestId("value").textContent).toBe("1");
+
+ await user.click(decrementButton);
+ expect(screen.getByTestId("value").textContent).toBe("0");
+ });
+
+ it("should handle empty value when allowEmpty is true", async () => {
+ render(NumberInput, {
+ props: { allowEmpty: true },
+ });
+
+ const input = screen.getByRole("spinbutton");
+ await user.clear(input);
+ expect(input).toHaveValue(null);
+ });
+
+ it("should handle min/max validation", async () => {
+ render(NumberInput, { props: { min: 4, max: 20 } });
+
+ const input = screen.getByRole("spinbutton");
+ await user.type(input, "25");
+ expect(screen.getByTestId("value").textContent).toBe("25");
+ expect(screen.getByRole("spinbutton")).toHaveAttribute(
+ "aria-invalid",
+ "true",
+ );
+ });
+
+ it("should not show helper text when invalid", () => {
+ render(NumberInput, {
+ props: {
+ invalid: true,
+ invalidText: "Invalid input",
+ helperText: "Helper text",
+ },
+ });
+
+ expect(screen.queryByText("Helper text")).not.toBeInTheDocument();
+ expect(screen.getByText("Invalid input")).toBeInTheDocument();
+ });
+
+ it("should not show helper text when warning", () => {
+ render(NumberInput, {
+ props: {
+ warn: true,
+ warnText: "Warning message",
+ helperText: "Helper text",
+ },
+ });
+
+ expect(screen.queryByText("Helper text")).not.toBeInTheDocument();
+ expect(screen.getByText("Warning message")).toBeInTheDocument();
+ });
+
+ it("should handle disabled helper text", () => {
+ render(NumberInput, {
+ props: {
+ disabled: true,
+ helperText: "Helper text",
+ },
+ });
+
+ expect(screen.getByText("Helper text")).toHaveClass(
+ "bx--form__helper-text--disabled",
+ );
+ });
+});
diff --git a/tests/NumberInput/NumberInputCustom.test.svelte b/tests/NumberInput/NumberInputCustom.test.svelte
new file mode 100644
index 00000000..4fa329a9
--- /dev/null
+++ b/tests/NumberInput/NumberInputCustom.test.svelte
@@ -0,0 +1,7 @@
+
+
+
+ Custom Label Text
+