From f38c28d2c0ffd64272cc41caf2e03bacc8dab465 Mon Sep 17 00:00:00 2001 From: Cameron Hyde Date: Wed, 2 Feb 2022 14:40:45 +1000 Subject: [PATCH 01/43] Replace _TextField with FormText.vue --- .../src/components/Form/Elements/FormText.vue | 134 ++++++++++++++++++ .../components/Form/Elements/parameters.js | 42 ------ client/src/components/Form/FormElement.vue | 19 ++- 3 files changed, 151 insertions(+), 44 deletions(-) create mode 100644 client/src/components/Form/Elements/FormText.vue diff --git a/client/src/components/Form/Elements/FormText.vue b/client/src/components/Form/Elements/FormText.vue new file mode 100644 index 00000000000..4472a305f06 --- /dev/null +++ b/client/src/components/Form/Elements/FormText.vue @@ -0,0 +1,134 @@ + + + + + diff --git a/client/src/components/Form/Elements/parameters.js b/client/src/components/Form/Elements/parameters.js index 40ad903c675..63629dec42a 100644 --- a/client/src/components/Form/Elements/parameters.js +++ b/client/src/components/Form/Elements/parameters.js @@ -15,8 +15,6 @@ import DataPicker from "mvc/ui/ui-data-picker"; export default Backbone.View.extend({ /** Available parameter types */ types: { - text: "_fieldText", - password: "_fieldText", select: "_fieldSelect", data_column: "_fieldSelect", genomebuild: "_fieldSelect", @@ -128,46 +126,6 @@ export default Backbone.View.extend({ }); }, - /** Text input field */ - _fieldText: function (input_def) { - // field replaces e.g. a select field - const inputClass = input_def.optional && input_def.type === "select" ? Ui.NullableText : Ui.Input; - if ( - ["SelectTagParameter", "ColumnListParameter"].includes(input_def.model_class) || - (input_def.options && input_def.data) - ) { - input_def.area = input_def.multiple; - if (Utils.isEmpty(input_def.value)) { - input_def.value = null; - } else { - if (Array.isArray(input_def.value)) { - var str_value = ""; - for (var i in input_def.value) { - str_value += String(input_def.value[i]); - if (!input_def.multiple) { - break; - } - str_value += "\n"; - } - input_def.value = str_value; - } - } - } - // create input element - return new inputClass({ - id: input_def.id, - type: input_def.type, - area: input_def.area, - readonly: input_def.readonly, - color: input_def.color, - style: input_def.style, - placeholder: input_def.placeholder, - datalist: input_def.datalist, - onchange: input_def.onchange, - value: input_def.value, - }); - }, - /** Data dialog picker field */ _fieldDialog: function (input_def) { return new DataPicker({ diff --git a/client/src/components/Form/FormElement.vue b/client/src/components/Form/FormElement.vue index b66906be4e0..674644e4d4d 100644 --- a/client/src/components/Form/FormElement.vue +++ b/client/src/components/Form/FormElement.vue @@ -29,7 +29,19 @@ :min="attrs.min" :type="type" v-model="currentValue" - :id="id" /> + :id="id" + /> + + ref="params" + />
@@ -56,6 +69,7 @@ import FormParameter from "./Elements/FormParameter"; import FormColor from "./Elements/FormColor"; import FormDirectory from "./Elements/FormDirectory"; import FormNumber from "./Elements/FormNumber"; +import FormText from "./Elements/FormText"; export default { components: { @@ -63,6 +77,7 @@ export default { FormHidden, FormInput, FormNumber, + FormText, FormColor, FormParameter, FormDirectory, From 7deb82e6db04843a2f6a34ca1de2efee2c3f838c Mon Sep 17 00:00:00 2001 From: Cameron Hyde Date: Tue, 15 Feb 2022 13:30:13 +1000 Subject: [PATCH 02/43] FormText pass unit tests --- .../components/Form/Elements/FormText.test.js | 70 +++++++++++++++++++ .../src/components/Form/Elements/FormText.vue | 54 ++++++++------ 2 files changed, 102 insertions(+), 22 deletions(-) create mode 100644 client/src/components/Form/Elements/FormText.test.js diff --git a/client/src/components/Form/Elements/FormText.test.js b/client/src/components/Form/Elements/FormText.test.js new file mode 100644 index 00000000000..9650786cce6 --- /dev/null +++ b/client/src/components/Form/Elements/FormText.test.js @@ -0,0 +1,70 @@ +import { mount } from "@vue/test-utils"; +import flushPromises from "flush-promises"; +import { getLocalVue } from "jest/helpers"; +import FormText from "./FormText"; + +const localVue = getLocalVue(); + +describe("FormText", () => { + const mountFormText = async (props) => + await mount(FormText, { + propsData: props, + localVue, + }); + + // constants + const getAlert = async (wrapper) => await wrapper.find("[role='alert']"); + + it("should render the appropriate input type", async () => { + let wrapper = await mountFormText({}); + await flushPromises(); + let el = await wrapper.find("input[type='text']"); + expect(el.exists()).toBe(true); + + wrapper = await mountFormText({ type: "password" }); + await flushPromises(); + el = await wrapper.find("input[type='password']"); + expect(el.exists()).toBe(true); + }); + + it("should render the appropriate component", async () => { + let wrapper = await mountFormText({ area: true }); + await flushPromises(); + let el = await wrapper.find("textarea"); + expect(el.exists()).toBe(true); + + wrapper = await mountFormText({ multiple: true }); + await flushPromises(); + el = await wrapper.find("textarea"); + expect(el.exists()).toBe(true); + }); + + it("should be able to render a datalist", async () => { + let wrapper = await mountFormText({ datalist: ["one", "two", "three"] }); + await flushPromises(); + let el = await wrapper.find("datalist"); + expect(el.exists()).toBe(true); + el = await wrapper.find("option"); + expect(el.exists()).toBe(true); + }); + + it("should be able to render style from props", async () => { + let wrapper = await mountFormText({ styleObj: { fontSize: "18px" } }); + await flushPromises(); + let el = await wrapper.find("input"); + expect(el.element.style).toMatchObject({ fontSize: "18px" }); + + wrapper = await mountFormText({ styleObj: { fontSize: "18px" }, color: 'white' }); + await flushPromises(); + el = await wrapper.find("input"); + expect(el.element.style).toMatchObject({ fontSize: "18px", color: 'white' }); + }); + + it("should be able to accept a default value", async () => { + const v = "something"; + let wrapper = await mountFormText({ value: v }); + await flushPromises(); + let el = await wrapper.find("input"); + expect(el.props('value')).toEqual(v); + }); +}); diff --git a/client/src/components/Form/Elements/FormText.vue b/client/src/components/Form/Elements/FormText.vue index 4472a305f06..b34755eddf7 100644 --- a/client/src/components/Form/Elements/FormText.vue +++ b/client/src/components/Form/Elements/FormText.vue @@ -5,33 +5,32 @@ - - - - - + + + + + - + - - + @change="onInputChange" /> + + + +
@@ -51,7 +50,8 @@ export default { }, type: { type: String, - required: true, + required: false, + default: "text", validator: (prop) => ["text", "password"].includes(prop.toLowerCase()), }, area: { @@ -59,6 +59,11 @@ export default { required: false, default: false, }, + multiple: { + type: Boolean, + required: false, + default: false, + }, readonly: { type: Boolean, required: false, @@ -72,9 +77,10 @@ export default { type: String, required: false, }, - style: { - type: Object, // Or String? + styleObj: { + type: Object, required: false, + default: () => {}, }, datalist: { // Display list of suggestions in autocomplete dialog @@ -93,7 +99,7 @@ export default { currentValue: { get() { // TODO: is silent fail on non-strings appropriate? - v = this.value || ""; + const v = this.value || ""; if (typeof v === "array") { return this.multiple ? v.map((i) => String(i)).join("\n") : String(v[0]); } @@ -107,16 +113,22 @@ export default { }, }, componentName() { - return this.area ? "b-form-textarea" : "b-form-input"; + return this.area || this.multiple ? "b-form-textarea" : "b-form-input"; + }, + style() { + return this.color ? { ...this.styleObj, color: this.color } : this.styleObj; }, }, methods: { onInputChange(value) { this.resetAlert(); // Some validation? - if (cond) { - // Show some info alert - } + + // Could we accept a validation function in props? + + // if (value !== xxx) { + // // Show some info alert + // } }, showAlert(error) { if (error) { @@ -130,5 +142,3 @@ export default { }, }; - - From 4598d7178eeca2d9dfa607a26d7b74fb5af14602 Mon Sep 17 00:00:00 2001 From: Cameron Hyde Date: Wed, 20 Apr 2022 15:56:36 +1000 Subject: [PATCH 03/43] Restore _fieldText component to maintain compatability --- .../components/Form/Elements/parameters.js | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/client/src/components/Form/Elements/parameters.js b/client/src/components/Form/Elements/parameters.js index 63629dec42a..40ad903c675 100644 --- a/client/src/components/Form/Elements/parameters.js +++ b/client/src/components/Form/Elements/parameters.js @@ -15,6 +15,8 @@ import DataPicker from "mvc/ui/ui-data-picker"; export default Backbone.View.extend({ /** Available parameter types */ types: { + text: "_fieldText", + password: "_fieldText", select: "_fieldSelect", data_column: "_fieldSelect", genomebuild: "_fieldSelect", @@ -126,6 +128,46 @@ export default Backbone.View.extend({ }); }, + /** Text input field */ + _fieldText: function (input_def) { + // field replaces e.g. a select field + const inputClass = input_def.optional && input_def.type === "select" ? Ui.NullableText : Ui.Input; + if ( + ["SelectTagParameter", "ColumnListParameter"].includes(input_def.model_class) || + (input_def.options && input_def.data) + ) { + input_def.area = input_def.multiple; + if (Utils.isEmpty(input_def.value)) { + input_def.value = null; + } else { + if (Array.isArray(input_def.value)) { + var str_value = ""; + for (var i in input_def.value) { + str_value += String(input_def.value[i]); + if (!input_def.multiple) { + break; + } + str_value += "\n"; + } + input_def.value = str_value; + } + } + } + // create input element + return new inputClass({ + id: input_def.id, + type: input_def.type, + area: input_def.area, + readonly: input_def.readonly, + color: input_def.color, + style: input_def.style, + placeholder: input_def.placeholder, + datalist: input_def.datalist, + onchange: input_def.onchange, + value: input_def.value, + }); + }, + /** Data dialog picker field */ _fieldDialog: function (input_def) { return new DataPicker({ From 4175e37204c69cefff399f4603ca3c072bda07c6 Mon Sep 17 00:00:00 2001 From: Cameron Hyde Date: Wed, 20 Apr 2022 16:04:09 +1000 Subject: [PATCH 04/43] Trying to accomodate special input cases in FormText.vue --- .../src/components/Form/Elements/FormText.vue | 66 ++++++++++++------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/client/src/components/Form/Elements/FormText.vue b/client/src/components/Form/Elements/FormText.vue index b34755eddf7..ac3443ca518 100644 --- a/client/src/components/Form/Elements/FormText.vue +++ b/client/src/components/Form/Elements/FormText.vue @@ -5,19 +5,6 @@ - - - - - - - - - - + + diff --git a/client/src/components/Form/Elements/parameters.js b/client/src/components/Form/Elements/parameters.js index 40ad903c675..63629dec42a 100644 --- a/client/src/components/Form/Elements/parameters.js +++ b/client/src/components/Form/Elements/parameters.js @@ -15,8 +15,6 @@ import DataPicker from "mvc/ui/ui-data-picker"; export default Backbone.View.extend({ /** Available parameter types */ types: { - text: "_fieldText", - password: "_fieldText", select: "_fieldSelect", data_column: "_fieldSelect", genomebuild: "_fieldSelect", @@ -128,46 +126,6 @@ export default Backbone.View.extend({ }); }, - /** Text input field */ - _fieldText: function (input_def) { - // field replaces e.g. a select field - const inputClass = input_def.optional && input_def.type === "select" ? Ui.NullableText : Ui.Input; - if ( - ["SelectTagParameter", "ColumnListParameter"].includes(input_def.model_class) || - (input_def.options && input_def.data) - ) { - input_def.area = input_def.multiple; - if (Utils.isEmpty(input_def.value)) { - input_def.value = null; - } else { - if (Array.isArray(input_def.value)) { - var str_value = ""; - for (var i in input_def.value) { - str_value += String(input_def.value[i]); - if (!input_def.multiple) { - break; - } - str_value += "\n"; - } - input_def.value = str_value; - } - } - } - // create input element - return new inputClass({ - id: input_def.id, - type: input_def.type, - area: input_def.area, - readonly: input_def.readonly, - color: input_def.color, - style: input_def.style, - placeholder: input_def.placeholder, - datalist: input_def.datalist, - onchange: input_def.onchange, - value: input_def.value, - }); - }, - /** Data dialog picker field */ _fieldDialog: function (input_def) { return new DataPicker({ diff --git a/client/src/components/Form/FormElement.vue b/client/src/components/Form/FormElement.vue index c1c387f5fea..ff9329b275b 100644 --- a/client/src/components/Form/FormElement.vue +++ b/client/src/components/Form/FormElement.vue @@ -31,6 +31,16 @@ :min="attrs.min" :type="type" :workflow-building-mode="workflowBuildingMode" /> + Date: Tue, 15 Feb 2022 13:30:13 +1000 Subject: [PATCH 08/43] FormText pass unit tests --- .../components/Form/Elements/FormText.test.js | 70 +++++++++++++++++++ .../src/components/Form/Elements/FormText.vue | 54 ++++++++------ 2 files changed, 102 insertions(+), 22 deletions(-) create mode 100644 client/src/components/Form/Elements/FormText.test.js diff --git a/client/src/components/Form/Elements/FormText.test.js b/client/src/components/Form/Elements/FormText.test.js new file mode 100644 index 00000000000..9650786cce6 --- /dev/null +++ b/client/src/components/Form/Elements/FormText.test.js @@ -0,0 +1,70 @@ +import { mount } from "@vue/test-utils"; +import flushPromises from "flush-promises"; +import { getLocalVue } from "jest/helpers"; +import FormText from "./FormText"; + +const localVue = getLocalVue(); + +describe("FormText", () => { + const mountFormText = async (props) => + await mount(FormText, { + propsData: props, + localVue, + }); + + // constants + const getAlert = async (wrapper) => await wrapper.find("[role='alert']"); + + it("should render the appropriate input type", async () => { + let wrapper = await mountFormText({}); + await flushPromises(); + let el = await wrapper.find("input[type='text']"); + expect(el.exists()).toBe(true); + + wrapper = await mountFormText({ type: "password" }); + await flushPromises(); + el = await wrapper.find("input[type='password']"); + expect(el.exists()).toBe(true); + }); + + it("should render the appropriate component", async () => { + let wrapper = await mountFormText({ area: true }); + await flushPromises(); + let el = await wrapper.find("textarea"); + expect(el.exists()).toBe(true); + + wrapper = await mountFormText({ multiple: true }); + await flushPromises(); + el = await wrapper.find("textarea"); + expect(el.exists()).toBe(true); + }); + + it("should be able to render a datalist", async () => { + let wrapper = await mountFormText({ datalist: ["one", "two", "three"] }); + await flushPromises(); + let el = await wrapper.find("datalist"); + expect(el.exists()).toBe(true); + el = await wrapper.find("option"); + expect(el.exists()).toBe(true); + }); + + it("should be able to render style from props", async () => { + let wrapper = await mountFormText({ styleObj: { fontSize: "18px" } }); + await flushPromises(); + let el = await wrapper.find("input"); + expect(el.element.style).toMatchObject({ fontSize: "18px" }); + + wrapper = await mountFormText({ styleObj: { fontSize: "18px" }, color: 'white' }); + await flushPromises(); + el = await wrapper.find("input"); + expect(el.element.style).toMatchObject({ fontSize: "18px", color: 'white' }); + }); + + it("should be able to accept a default value", async () => { + const v = "something"; + let wrapper = await mountFormText({ value: v }); + await flushPromises(); + let el = await wrapper.find("input"); + expect(el.props('value')).toEqual(v); + }); +}); diff --git a/client/src/components/Form/Elements/FormText.vue b/client/src/components/Form/Elements/FormText.vue index 4472a305f06..b34755eddf7 100644 --- a/client/src/components/Form/Elements/FormText.vue +++ b/client/src/components/Form/Elements/FormText.vue @@ -5,33 +5,32 @@ - - - - - + + + + + - + - - + @change="onInputChange" /> + + + + @@ -51,7 +50,8 @@ export default { }, type: { type: String, - required: true, + required: false, + default: "text", validator: (prop) => ["text", "password"].includes(prop.toLowerCase()), }, area: { @@ -59,6 +59,11 @@ export default { required: false, default: false, }, + multiple: { + type: Boolean, + required: false, + default: false, + }, readonly: { type: Boolean, required: false, @@ -72,9 +77,10 @@ export default { type: String, required: false, }, - style: { - type: Object, // Or String? + styleObj: { + type: Object, required: false, + default: () => {}, }, datalist: { // Display list of suggestions in autocomplete dialog @@ -93,7 +99,7 @@ export default { currentValue: { get() { // TODO: is silent fail on non-strings appropriate? - v = this.value || ""; + const v = this.value || ""; if (typeof v === "array") { return this.multiple ? v.map((i) => String(i)).join("\n") : String(v[0]); } @@ -107,16 +113,22 @@ export default { }, }, componentName() { - return this.area ? "b-form-textarea" : "b-form-input"; + return this.area || this.multiple ? "b-form-textarea" : "b-form-input"; + }, + style() { + return this.color ? { ...this.styleObj, color: this.color } : this.styleObj; }, }, methods: { onInputChange(value) { this.resetAlert(); // Some validation? - if (cond) { - // Show some info alert - } + + // Could we accept a validation function in props? + + // if (value !== xxx) { + // // Show some info alert + // } }, showAlert(error) { if (error) { @@ -130,5 +142,3 @@ export default { }, }; - - From 6ddce14d3587171c91aa1f4a57f385d0a201a645 Mon Sep 17 00:00:00 2001 From: Cameron Hyde Date: Wed, 20 Apr 2022 15:56:36 +1000 Subject: [PATCH 09/43] Restore _fieldText component to maintain compatability --- .../components/Form/Elements/parameters.js | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/client/src/components/Form/Elements/parameters.js b/client/src/components/Form/Elements/parameters.js index 63629dec42a..40ad903c675 100644 --- a/client/src/components/Form/Elements/parameters.js +++ b/client/src/components/Form/Elements/parameters.js @@ -15,6 +15,8 @@ import DataPicker from "mvc/ui/ui-data-picker"; export default Backbone.View.extend({ /** Available parameter types */ types: { + text: "_fieldText", + password: "_fieldText", select: "_fieldSelect", data_column: "_fieldSelect", genomebuild: "_fieldSelect", @@ -126,6 +128,46 @@ export default Backbone.View.extend({ }); }, + /** Text input field */ + _fieldText: function (input_def) { + // field replaces e.g. a select field + const inputClass = input_def.optional && input_def.type === "select" ? Ui.NullableText : Ui.Input; + if ( + ["SelectTagParameter", "ColumnListParameter"].includes(input_def.model_class) || + (input_def.options && input_def.data) + ) { + input_def.area = input_def.multiple; + if (Utils.isEmpty(input_def.value)) { + input_def.value = null; + } else { + if (Array.isArray(input_def.value)) { + var str_value = ""; + for (var i in input_def.value) { + str_value += String(input_def.value[i]); + if (!input_def.multiple) { + break; + } + str_value += "\n"; + } + input_def.value = str_value; + } + } + } + // create input element + return new inputClass({ + id: input_def.id, + type: input_def.type, + area: input_def.area, + readonly: input_def.readonly, + color: input_def.color, + style: input_def.style, + placeholder: input_def.placeholder, + datalist: input_def.datalist, + onchange: input_def.onchange, + value: input_def.value, + }); + }, + /** Data dialog picker field */ _fieldDialog: function (input_def) { return new DataPicker({ From d348c6bbd8cee0a4d9a3bb84fbaf39ebd17b61e5 Mon Sep 17 00:00:00 2001 From: Cameron Hyde Date: Wed, 20 Apr 2022 16:04:09 +1000 Subject: [PATCH 10/43] Trying to accomodate special input cases in FormText.vue --- .../src/components/Form/Elements/FormText.vue | 66 ++++++++++++------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/client/src/components/Form/Elements/FormText.vue b/client/src/components/Form/Elements/FormText.vue index b34755eddf7..ac3443ca518 100644 --- a/client/src/components/Form/Elements/FormText.vue +++ b/client/src/components/Form/Elements/FormText.vue @@ -5,19 +5,6 @@ - - - - - - - - - - diff --git a/client/src/components/Form/FormElement.vue b/client/src/components/Form/FormElement.vue index 3861f236984..4643bb0bdba 100644 --- a/client/src/components/Form/FormElement.vue +++ b/client/src/components/Form/FormElement.vue @@ -31,6 +31,19 @@ :min="attrs.min" :type="type" :workflow-building-mode="workflowBuildingMode" /> + Date: Fri, 18 Nov 2022 23:23:35 -0500 Subject: [PATCH 23/43] Selenium test passing now, all logic seemingly in place, race condition still exists --- .../Form/Elements/FormOptionalText.vue | 45 ++++++++++++++++--- .../src/components/Form/Elements/FormText.vue | 1 + client/src/utils/navigation/navigation.yml | 4 +- .../selenium/test_workflow_editor.py | 7 +-- 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/client/src/components/Form/Elements/FormOptionalText.vue b/client/src/components/Form/Elements/FormOptionalText.vue index 1dd869b7ce5..53e206243f0 100644 --- a/client/src/components/Form/Elements/FormOptionalText.vue +++ b/client/src/components/Form/Elements/FormOptionalText.vue @@ -1,10 +1,15 @@