Skip to content

Commit

Permalink
feat: Add NumberControl and NumericControl tests (#17)
Browse files Browse the repository at this point in the history
* Port over existing code

* Fix typing errors

* Exclude .ts files from prodution build

* Exclude all files with .test. from prod build

* Add untracked file

* Remove unused file

* Move tests to another branch

* Push trivial change

* Revert omit change

* Remove NumericControlProps and use cast

* Add existing tests

* Data can be something other than Record<string, unknown>

* Update to new registry pattern

* Remove unused schemas

* Deleted stories from other branch

* Fix test

* Pass min, max, step, and controls to InputNumber also

* Only show percentage addonAfter if isPercentage

* Only show percentage addonAfter if isPercentage

* Fix marginLeft on pure number slider

* Update tooltip logic for non-percentages on slider

* Update logic to fill functionality gaps

* Add logic for required asterisk

* Fix tooltips

* Use new schemas in tests

* Update tests to use new schemas

* Update value default logic

* Revert changes to FormStateWrapper

* Revert changes to test-render
  • Loading branch information
NathanFarmer authored Mar 6, 2024
1 parent 1186933 commit 70ea4d5
Show file tree
Hide file tree
Showing 5 changed files with 409 additions and 1 deletion.
102 changes: 102 additions & 0 deletions src/controls/NumberControl.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { describe, expect, test, it } from "vitest"
import { screen, waitFor } from "@testing-library/react"
import { userEvent } from "@testing-library/user-event"
import { render } from "../common/test-render"
import { JSONSchema } from "json-schema-to-ts"
import {
numberBasisPointsSchema,
numberBasisPointsUISchema,
numberMagnitudeSchema,
numberMagnitudeUISchema,
numberTheNumberSchema,
numberTheNumberUISchema,
numberUISchemaWithRule,
} from "../testSchemas/numberSchema"

describe("NumberControl", () => {
test("renders a number input with no UISchema provided", () => {
render({
schema: numberMagnitudeSchema,
})

expect(screen.getByText("Magnitude")).not.toBeNull()
})

it("Follows the hide rule", () => {
const data = { magnitude: 1000 }
render({
data: data,
schema: numberMagnitudeSchema,
uischema: numberUISchemaWithRule,
})
expect(screen.queryByText("Magnitude")).toBeNull()
})

it.each([[0], [100]])("renders when data of %s is included", (dataVal: number) => {
const data = { theNumber: dataVal}
render({
data: data,
schema: numberTheNumberSchema, // this has a default of 42.42
uischema: numberTheNumberUISchema,
})
expect(screen.getByText("The Number")).not.toBeNull()
expect(screen.getByRole("spinbutton")).toHaveValue(`${dataVal}`)
})

it.each([[0], [100]])("renders default value of %s when no data is provided", (defaultValue: number) => {
const { properties, ...rest } = numberMagnitudeSchema
properties.magnitude = { ...properties.magnitude, ...{ default: defaultValue }}
render({
schema: { ...rest, properties },
uischema: numberMagnitudeUISchema,
})

expect(screen.getByText("Magnitude")).not.toBeNull()
expect(screen.getByRole("spinbutton")).toHaveValue(`${defaultValue}`)
})
it("renders default value when no data is provided", () => {
render({
schema: numberTheNumberSchema,
uischema: numberTheNumberUISchema,
})
expect(screen.getByRole("spinbutton")).toHaveValue("42.42")
})

it("changes its value when users type", async () => {
let data: JSONSchema
render({
schema: numberMagnitudeSchema,
uischema: numberMagnitudeUISchema,
onChange: (state: { data: JSONSchema }) => {
data = state.data
},
})

await userEvent.clear(screen.getByRole("spinbutton"))
await userEvent.type(screen.getByRole("spinbutton"), "123")

await waitFor(() => {
expect(data).toEqual({ magnitude: 123 })
})
})

it("renders slider when min max values are present", () => {
const data = { basisPoints: 1 }
render({
data: data,
schema: numberBasisPointsSchema,
uischema: numberBasisPointsUISchema,
})
expect(screen.getByText("Basis Points")).not.toBeNull()
expect(screen.getByRole("spinbutton")).toHaveValue("1")
expect(screen.getByRole("slider")).not.toBeNull()
expect(screen.getByRole("slider")).toHaveAttribute("aria-valuenow", "1")
})
it("hides slider when min max values are not present", () => {
render({
schema: numberMagnitudeSchema,
uischema: numberMagnitudeUISchema,
})
expect(screen.queryByRole("slider")).toBeNull()
})
})
55 changes: 55 additions & 0 deletions src/controls/NumericControl.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { describe, expect, it } from "vitest"
import { screen, waitFor } from "@testing-library/react"
import { userEvent } from "@testing-library/user-event"
import { render } from "../common/test-render"
import { numberTheNumberSchema, numberWeightSchema } from "../testSchemas/numberSchema"

describe("NumericControl", () => {
it("does not fall back to default if value is empty", () => {
render({
schema: { ...numberTheNumberSchema },
data: {},
})

expect(screen.getByRole("spinbutton")).toHaveValue("")
})

it("calls onChange with number values", async () => {
let data = { theNumber: 42.00 }
render({
schema: numberTheNumberSchema,
data,
onChange: (state) => {
data = state.data
},
})

await userEvent.clear(screen.getByRole("spinbutton"))
await userEvent.type(screen.getByRole("spinbutton"), "42.00")

await waitFor(() => {
expect(data).toBe(42.00)
})
})

it("calls onChange with empty object and no errors when the input gets cleared out and optional", async () => {
const weight = {}
let state: Record<string, unknown> = {}
render({
schema: numberWeightSchema,
data: weight,
onChange: (newState) => {
state = newState
},
})

await userEvent.clear(screen.getByRole("spinbutton"))

await waitFor(() => {
expect(state.data).toBe(weight)
const errors = state.errors
console.log({errors})
expect(state.errors).toHaveLength(0)
})
})
})
32 changes: 32 additions & 0 deletions src/controls/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, expect, test } from "vitest"
import { decimalToPercentage, percentageStringToDecimal } from "./utils"

describe("percentageStringToDecimal", () => {
test.each([
{ value: "100", expected: 1 },
{ value: "0", expected: 0 },
{ value: "50", expected: 0.5 },
{ value: "0.5", expected: 0.005 },
{ value: "0.005", expected: 0.00005 },
{ value: "0.066", expected: 0.00066 },
{ value: "85.9999999999", expected: 0.859999999999 },
])("when value is $value it returns $expected", ({ value, expected }: { value: string; expected: number }) => {
expect(percentageStringToDecimal(value)).toEqual(expected)
})
})

describe("decimalToPercentage", () => {
test.each([
{ value: 1, expected: "100" },
{ value: 0, expected: "0" },
{ value: 0.5, expected: "50" },
{ value: 0.005, expected: "0.5" },
{ value: 0.00005, expected: "0.005" },
{ value: 0.859999999999, expected: "85.9999999999" },
])(
"when value is $value it returns $expected. If symbol is provided, it is appended to the value",
({ value, expected }: { value: number; expected: string }) => {
expect(decimalToPercentage(value)).toEqual(expected)
},
)
})
Loading

0 comments on commit 70ea4d5

Please sign in to comment.