diff --git a/src/components/CustomTextArea.tsx b/src/components/CustomTextArea.tsx index 4c2dc58..460efd4 100644 --- a/src/components/CustomTextArea.tsx +++ b/src/components/CustomTextArea.tsx @@ -42,7 +42,9 @@ function validateMapJsonStr(inStr: string, currentValidationState: ValidationSta const { name, meta, coordinates } = edge; if ( !name || typeof(name) != 'string' || - (!!meta && typeof(meta) != 'object') || + !meta || typeof(meta) != 'object' || + !meta.endpoint_identifiers || typeof(meta.endpoint_identifiers) != 'object' || + !Array.isArray(meta.endpoint_identifiers?.names) || !coordinates || !Array.isArray(coordinates) || coordinates.some((coordinate) => { return !Array.isArray(coordinate) diff --git a/test/react/CustomTextArea.spec.js b/test/react/CustomTextArea.spec.js index bc433d8..a26cc5b 100644 --- a/test/react/CustomTextArea.spec.js +++ b/test/react/CustomTextArea.spec.js @@ -1,3 +1,4 @@ +import { update } from "lodash"; import { CustomTextArea } from "../../src/testables.ts"; import * as React from "react"; import * as ReactDOM from "react-dom/client"; @@ -20,6 +21,64 @@ const inputTypes = [ window.HTMLTextAreaElement, ]; +const emptyTopology = ` + { + "nodes": [ + ], + "edges": [ + ] + } +`.trim(); + +function makeNodeJson(name) { + return `{ + "name": "${name}", + "meta": { + "displayName": "", + "svg": "", + "template": "" + }, + "coordinate": [ + ${ 30 + Math.random() * 4}, + -${ -90 + Math.random() * 5}, + ] + }`; +}; + +function makeEdgeNameOnlyJson(nodeAName, nodeBName) { + return `{ + "name": "${nodeAName}--${nodeBName}" + }`; +}; + +function makeMultilineEdgePartial(nodeA, nodeB) { + return ` + "meta": { + "endpoint_identifiers": { + "names": [ + "${nodeA.name}", + "${nodeB.name}" + ] + } + }, + "coordinates": [ + [ + ${nodeA.coordinate[0]}, + ${nodeA.coordinate[1]} + ], + [ + ${(nodeA.coordinate[0] + nodeB.coordinate[0]) / 2}, + ${(nodeA.coordinate[1] + nodeB.coordinate[1]) / 2} + ], + [ + ${nodeB.coordinate[0]}, + ${nodeB.coordinate[1]} + ], + ], + "children": [] + `.trim(); +}; + // cribbed from https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-change-or-input-event-in-react-js function triggerInputChange(node, value = '') { // only process the change on elements we know have a value setter in their constructor @@ -64,4 +123,38 @@ describe("Component CustomTextArea esmap", () => { expect(error.innerText).toBeTruthy(); }) + it("should allow adding multiline pasting without resetting the form", async () => { + await act(async () => { + let elems = document.getElementsByTagName("textarea"); + let elem = elems[0]; + triggerInputChange(elem, emptyTopology); + }); + expect(elem.innerText).toEqual(emptyTopology); + + // TODO: complete test script + await act(async () => { + let startEditPos = elem.indexOf('"nodes": ['); + for (edgeName of ["A", "B"]) { + const newNode = makeNodeJson(edgeName); + const newNodeLen = newNode.length; + let updatedText = ` + ${elem.innerText.substring(0, startEditPos)} + ${newNode} + ${elem.innerText.substring(startEditPos + newNodeLen)} + `.trim(); + triggerInputChange(elem, updatedText); + } + }); + await act(async () => { + const newEdge = makeEdgeNameOnlyJson("A", "B"); + const newEdgeLen = newEdge.length; + let startEditPos = elem.indexOf('"edges": ['); + let updatedText = ` + ${elem.innerText.substring(0, startEditPos)} + ${newEdge} + ${elm.innerText.substring(startEditPos + newEdgeLen)} + `.trim(); + triggerInputChange(elem, updatedText); + }); + }); }) \ No newline at end of file