From 20ff3b97a18c061e08bc157fd3851cf4ba7101c6 Mon Sep 17 00:00:00 2001 From: Ashwin Ramaswami Date: Sat, 10 Aug 2019 08:21:56 -0700 Subject: [PATCH] Allow "0." to be typed in to a text widget (#1360) * fix: allow "0." to be typed in * Fix expected value of input in test for NumberField (#1361) * test: add tests --- src/components/fields/NumberField.js | 2 +- test/NumberField_test.js | 479 +++++++++++++++------------ 2 files changed, 264 insertions(+), 217 deletions(-) diff --git a/src/components/fields/NumberField.js b/src/components/fields/NumberField.js index 396da678ca..e0a87369cd 100644 --- a/src/components/fields/NumberField.js +++ b/src/components/fields/NumberField.js @@ -69,7 +69,7 @@ class NumberField extends React.Component { let value = formData; - if (typeof lastValue === "string" && value) { + if (typeof lastValue === "string" && typeof value === "number") { // Construct a regular expression that checks for a string that consists // of the formData value suffixed with zero or one '.' characters and zero // or more '0' characters diff --git a/test/NumberField_test.js b/test/NumberField_test.js index 73a87c538a..5709dc5314 100644 --- a/test/NumberField_test.js +++ b/test/NumberField_test.js @@ -16,320 +16,367 @@ describe("NumberField", () => { sandbox.restore(); }); - describe("TextWidget", () => { - it("should render a number input", () => { + describe("Number widget", () => { + it("should use step to represent the multipleOf keyword", () => { const { node } = createFormComponent({ schema: { type: "number", + multipleOf: 5, }, }); - expect( - node.querySelectorAll(".field input[type=number]") - ).to.have.length.of(1); + expect(node.querySelector("input").step).to.eql("5"); }); - it("should render a string field with a label", () => { + it("should use min to represent the minimum keyword", () => { const { node } = createFormComponent({ schema: { type: "number", - title: "foo", + minimum: 0, }, }); - expect(node.querySelector(".field label").textContent).eql("foo"); + expect(node.querySelector("input").min).to.eql("0"); }); - it("should render a string field with a description", () => { + it("should use max to represent the maximum keyword", () => { const { node } = createFormComponent({ schema: { type: "number", - description: "bar", + maximum: 100, }, }); - expect(node.querySelector(".field-description").textContent).eql("bar"); - }); - - it("should default state value to undefined", () => { - const { comp } = createFormComponent({ - schema: { type: "number" }, - }); - - expect(comp.state.formData).eql(undefined); + expect(node.querySelector("input").max).to.eql("100"); }); - it("should assign a default value", () => { + it("should use step to represent the multipleOf keyword", () => { const { node } = createFormComponent({ schema: { type: "number", - default: 2, + multipleOf: 5, }, }); - expect(node.querySelector(".field input").value).eql("2"); + expect(node.querySelector("input").step).to.eql("5"); }); - it("should handle a change event", () => { - const { comp, node } = createFormComponent({ + it("should use min to represent the minimum keyword", () => { + const { node } = createFormComponent({ schema: { type: "number", + minimum: 0, }, }); - Simulate.change(node.querySelector("input"), { - target: { value: "2" }, - }); - - expect(comp.state.formData).eql(2); + expect(node.querySelector("input").min).to.eql("0"); }); - it("should handle a blur event", () => { - const onBlur = sandbox.spy(); + it("should use max to represent the maximum keyword", () => { const { node } = createFormComponent({ schema: { type: "number", + maximum: 100, }, - onBlur, }); - const input = node.querySelector("input"); - Simulate.blur(input, { - target: { value: "2" }, - }); - - expect(onBlur.calledWith(input.id, 2)); + expect(node.querySelector("input").max).to.eql("100"); }); + }); + describe("Number and text widget", () => { + let uiSchemas = [ + {}, + { + "ui:options": { + inputType: "text", + }, + }, + ]; + for (let uiSchema of uiSchemas) { + it("should render a string field with a label", () => { + const { node } = createFormComponent({ + schema: { + type: "number", + title: "foo", + }, + uiSchema, + }); - it("should handle a focus event", () => { - const onFocus = sandbox.spy(); - const { node } = createFormComponent({ - schema: { - type: "number", - }, - onFocus, + expect(node.querySelector(".field label").textContent).eql("foo"); }); - const input = node.querySelector("input"); - Simulate.focus(input, { - target: { value: "2" }, + it("should render a string field with a description", () => { + const { node } = createFormComponent({ + schema: { + type: "number", + description: "bar", + }, + uiSchema, + }); + + expect(node.querySelector(".field-description").textContent).eql("bar"); }); - expect(onFocus.calledWith(input.id, 2)); - }); + it("should default state value to undefined", () => { + const { comp } = createFormComponent({ + schema: { type: "number" }, + uiSchema, + }); - it("should fill field with data", () => { - const { node } = createFormComponent({ - schema: { - type: "number", - }, - formData: 2, + expect(comp.state.formData).eql(undefined); }); - expect(node.querySelector(".field input").value).eql("2"); - }); + it("should assign a default value", () => { + const { node } = createFormComponent({ + schema: { + type: "number", + default: 2, + }, + uiSchema, + }); - describe("when inputting a number that ends with a dot and/or zero it should normalize it, without changing the input value", () => { - const { comp, node } = createFormComponent({ - schema: { - type: "number", - }, + expect(node.querySelector(".field input").value).eql("2"); }); - const $input = node.querySelector("input"); + it("should handle a change event", () => { + const { comp, node } = createFormComponent({ + schema: { + type: "number", + }, + uiSchema, + }); - const tests = [ - { - input: "2.", - output: 2, - }, - { - input: "2.0", - output: 2, - }, - { - input: "2.3", - output: 2.3, - }, - { - input: "2.30", - output: 2.3, - }, - { - input: "2.300", - output: 2.3, - }, - { - input: "2.3001", - output: 2.3001, - }, - { - input: "2.03", - output: 2.03, - }, - { - input: "2.003", - output: 2.003, - }, - { - input: "2.00300", - output: 2.003, - }, - { - input: "200300", - output: 200300, - }, - ]; + Simulate.change(node.querySelector("input"), { + target: { value: "2" }, + }); - tests.forEach(test => { - it(`should work with an input value of ${test.input}`, () => { - Simulate.change($input, { - target: { value: test.input }, - }); + expect(comp.state.formData).eql(2); + }); - expect(comp.state.formData).eql(test.output); - expect($input.value).eql(test.input); + it("should handle a blur event", () => { + const onBlur = sandbox.spy(); + const { node } = createFormComponent({ + schema: { + type: "number", + }, + uiSchema, + onBlur, }); + + const input = node.querySelector("input"); + Simulate.blur(input, { + target: { value: "2" }, + }); + + expect(onBlur.calledWith(input.id, 2)); }); - }); - it("should normalize values beginning with a decimal point", () => { - const { comp, node } = createFormComponent({ - schema: { - type: "number", - }, + it("should handle a focus event", () => { + const onFocus = sandbox.spy(); + const { node } = createFormComponent({ + schema: { + type: "number", + }, + uiSchema, + onFocus, + }); + + const input = node.querySelector("input"); + Simulate.focus(input, { + target: { value: "2" }, + }); + + expect(onFocus.calledWith(input.id, 2)); }); - const $input = node.querySelector("input"); + it("should fill field with data", () => { + const { node } = createFormComponent({ + schema: { + type: "number", + }, + uiSchema, + formData: 2, + }); - Simulate.change($input, { - target: { value: ".00" }, + expect(node.querySelector(".field input").value).eql("2"); }); - expect(comp.state.formData).eql(0); - expect($input.value).eql("0"); - }); + describe("when inputting a number that ends with a dot and/or zero it should normalize it, without changing the input value", () => { + const { comp, node } = createFormComponent({ + schema: { + type: "number", + }, + uiSchema, + }); - it("should update input values correctly when formData prop changes", () => { - const schema = { - type: "number", - }; + const $input = node.querySelector("input"); - const { comp, node } = createFormComponent({ - schema, - formData: 2.03, + const tests = [ + { + input: "2.", + output: 2, + }, + { + input: "2.0", + output: 2, + }, + { + input: "2.3", + output: 2.3, + }, + { + input: "2.30", + output: 2.3, + }, + { + input: "2.300", + output: 2.3, + }, + { + input: "2.3001", + output: 2.3001, + }, + { + input: "2.03", + output: 2.03, + }, + { + input: "2.003", + output: 2.003, + }, + { + input: "2.00300", + output: 2.003, + }, + { + input: "200300", + output: 200300, + }, + ]; + + tests.forEach(test => { + it(`should work with an input value of ${test.input}`, () => { + Simulate.change($input, { + target: { value: test.input }, + }); + + expect(comp.state.formData).eql(test.output); + expect($input.value).eql(test.input); + }); + }); }); - const $input = node.querySelector("input"); + it("should normalize values beginning with a decimal point", () => { + const { comp, node } = createFormComponent({ + schema: { + type: "number", + }, + uiSchema, + }); - expect($input.value).eql("2.03"); + const $input = node.querySelector("input"); - setProps(comp, { - schema, - formData: 203, - }); + Simulate.change($input, { + target: { value: ".00" }, + }); - expect($input.value).eql("203"); - }); + expect(comp.state.formData).eql(0); + expect($input.value).eql(".00"); + }); - it("should render the widget with the expected id", () => { - const { node } = createFormComponent({ - schema: { + it("should update input values correctly when formData prop changes", () => { + const schema = { type: "number", - }, - }); + }; - expect(node.querySelector("input[type=number]").id).eql("root"); - }); + const { comp, node } = createFormComponent({ + schema, + uiSchema, + formData: 2.03, + }); - it("should render with trailing zeroes", () => { - const { node } = createFormComponent({ - schema: { - type: "number", - }, - }); + const $input = node.querySelector("input"); - Simulate.change(node.querySelector("input"), { - target: { value: "2." }, - }); - expect(node.querySelector(".field input").value).eql("2."); + expect($input.value).eql("2.03"); - Simulate.change(node.querySelector("input"), { - target: { value: "2.0" }, - }); - expect(node.querySelector(".field input").value).eql("2.0"); + setProps(comp, { + schema, + formData: 203, + }); - Simulate.change(node.querySelector("input"), { - target: { value: "2.00" }, + expect($input.value).eql("203"); }); - expect(node.querySelector(".field input").value).eql("2.00"); - Simulate.change(node.querySelector("input"), { - target: { value: "2.000" }, - }); - expect(node.querySelector(".field input").value).eql("2.000"); - }); + it("should render the widget with the expected id", () => { + const { node } = createFormComponent({ + schema: { + type: "number", + }, + uiSchema, + }); - it("should allow a zero to be input", () => { - const { node } = createFormComponent({ - schema: { - type: "number", - }, + expect(node.querySelector("input").id).eql("root"); }); - Simulate.change(node.querySelector("input"), { - target: { value: "0" }, - }); - expect(node.querySelector(".field input").value).eql("0"); - }); + it("should render with trailing zeroes", () => { + const { node } = createFormComponent({ + schema: { + type: "number", + }, + uiSchema, + }); - it("should render customized StringField", () => { - const CustomStringField = () =>
; + Simulate.change(node.querySelector("input"), { + target: { value: "2." }, + }); + expect(node.querySelector(".field input").value).eql("2."); - const { node } = createFormComponent({ - schema: { - type: "number", - }, - fields: { - StringField: CustomStringField, - }, - }); + Simulate.change(node.querySelector("input"), { + target: { value: "2.0" }, + }); + expect(node.querySelector(".field input").value).eql("2.0"); - expect(node.querySelector("#custom")).to.exist; - }); + Simulate.change(node.querySelector("input"), { + target: { value: "2.00" }, + }); + expect(node.querySelector(".field input").value).eql("2.00"); - it("should use step to represent the multipleOf keyword", () => { - const { node } = createFormComponent({ - schema: { - type: "number", - multipleOf: 5, - }, + Simulate.change(node.querySelector("input"), { + target: { value: "2.000" }, + }); + expect(node.querySelector(".field input").value).eql("2.000"); }); - expect(node.querySelector("input").step).to.eql("5"); - }); + it("should allow a zero to be input", () => { + const { node } = createFormComponent({ + schema: { + type: "number", + }, + uiSchema, + }); - it("should use min to represent the minimum keyword", () => { - const { node } = createFormComponent({ - schema: { - type: "number", - minimum: 0, - }, + Simulate.change(node.querySelector("input"), { + target: { value: "0" }, + }); + expect(node.querySelector(".field input").value).eql("0"); }); - expect(node.querySelector("input").min).to.eql("0"); - }); + it("should render customized StringField", () => { + const CustomStringField = () =>
; - it("should use max to represent the maximum keyword", () => { - const { node } = createFormComponent({ - schema: { - type: "number", - maximum: 100, - }, - }); + const { node } = createFormComponent({ + schema: { + type: "number", + }, + uiSchema, + fields: { + StringField: CustomStringField, + }, + }); - expect(node.querySelector("input").max).to.eql("100"); - }); + expect(node.querySelector("#custom")).to.exist; + }); + } }); describe("SelectWidget", () => {