diff --git a/awx/ui/package-lock.json b/awx/ui/package-lock.json index d4600b30c..f77dd7242 100644 --- a/awx/ui/package-lock.json +++ b/awx/ui/package-lock.json @@ -23,9 +23,9 @@ "html-entities": "2.6.0", "js-yaml": "4.2.0", "luxon": "^3.7.2", - "react": "17.0.2", + "react": "18.3.1", "react-ace": "^10.1.0", - "react-dom": "17.0.2", + "react-dom": "18.3.1", "react-error-boundary": "^3.1.4", "react-router-dom": "^6.30.4", "rrule": "2.8.1", @@ -46,9 +46,9 @@ "@pmmmwh/react-refresh-webpack-plugin": "^0.6.2", "@rspack/core": "^1.7.11", "@svgr/webpack": "^8.1.0", - "@testing-library/dom": "^8.20.1", + "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", - "@testing-library/react": "^12.1.5", + "@testing-library/react": "^16.3.0", "@testing-library/user-event": "14.6.1", "babel-jest": "^30.3.0", "babel-loader": "^10.1.1", @@ -5452,6 +5452,7 @@ "version": "4.278.1", "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.278.1.tgz", "integrity": "sha512-BZ+A0r/xLWXLxE5/b8FTVxRI/KokDlTQOS0ub49ts7nv++vmZS7kU4tn2bfuh7RVw/BfW4CNtoMzeJkM8GpaWw==", + "license": "MIT", "dependencies": { "@patternfly/react-icons": "^4.93.7", "@patternfly/react-styles": "^4.92.8", @@ -5470,6 +5471,7 @@ "version": "4.93.7", "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.93.7.tgz", "integrity": "sha512-3kr35dgba7Qz5CSzmfH0rIjSvBC5xkmiknf3SvVUVxaiVA7KRowID8viYHeZlf3v/Oa3sEewaH830Q0t+nWsZQ==", + "license": "MIT", "peerDependencies": { "react": "^16.8 || ^17 || ^18", "react-dom": "^16.8 || ^17 || ^18" @@ -6374,23 +6376,23 @@ } }, "node_modules/@testing-library/dom": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", - "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", - "chalk": "^4.1.0", + "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", + "picocolors": "1.1.1", "pretty-format": "^27.0.2" }, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@testing-library/jest-dom": { @@ -6420,22 +6422,31 @@ "dev": true }, "node_modules/@testing-library/react": { - "version": "12.1.5", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.5.tgz", - "integrity": "sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==", + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.0.0", - "@types/react-dom": "<18.0.0" + "@babel/runtime": "^7.12.5" }, "engines": { - "node": ">=12" + "node": ">=18" }, "peerDependencies": { - "react": "<18.0.0", - "react-dom": "<18.0.0" + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, "node_modules/@testing-library/user-event": { @@ -6732,13 +6743,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "license": "MIT", - "peer": true - }, "node_modules/@types/qs": { "version": "6.9.18", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", @@ -6754,25 +6758,13 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "17.0.85", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.85.tgz", - "integrity": "sha512-5oBDUsRDsrYq4DdyHaL99gE1AJCfuDhyxqF6/55fvvOIRkp1PpKuwJ+aMiGJR+GJt7YqMNclPROTHF20vY2cXA==", + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", "license": "MIT", "peer": true, "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "^0.16", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "17.0.26", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.26.tgz", - "integrity": "sha512-Z+2VcYXJwOqQ79HreLU/1fyQ88eXSSFh6I3JdrEHQIfYSI0kCQpTGvOrbE6jFGGYXKsHuwY9tBa/w5Uo6KzrEg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^17.0.0" + "csstype": "^3.2.2" } }, "node_modules/@types/retry": { @@ -6782,13 +6774,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/scheduler": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", - "license": "MIT", - "peer": true - }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", @@ -8198,13 +8183,13 @@ "license": "Python-2.0" }, "node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "deep-equal": "^2.0.5" + "dequal": "^2.0.3" } }, "node_modules/array-buffer-byte-length": { @@ -8447,7 +8432,8 @@ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "hasInstallScript": true + "hasInstallScript": true, + "license": "MIT" }, "node_modules/autoprefixer": { "version": "10.5.0", @@ -10752,39 +10738,6 @@ } } }, - "node_modules/deep-equal": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", - "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -10896,6 +10849,16 @@ "node": ">= 0.8" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -11356,27 +11319,6 @@ "node": ">= 0.4" } }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/es-iterator-helpers": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", @@ -13652,12 +13594,6 @@ "react-is": "^16.7.0" } }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -14216,23 +14152,6 @@ "node": ">= 10" } }, - "node_modules/is-arguments": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", - "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -18569,23 +18488,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -20866,18 +20768,6 @@ "react": ">=0.14.0" } }, - "node_modules/prop-types-extra/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -21093,13 +20983,12 @@ } }, "node_modules/react": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", - "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + "loose-envify": "^1.1.0" }, "engines": { "node": ">=0.10.0" @@ -21187,17 +21076,16 @@ } }, "node_modules/react-dom": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", - "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "scheduler": "^0.20.2" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "17.0.2" + "react": "^18.3.1" } }, "node_modules/react-dropzone": { @@ -21247,6 +21135,12 @@ "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==", "license": "MIT" }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, "node_modules/react-refresh": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", @@ -21999,13 +21893,12 @@ } }, "node_modules/scheduler": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", - "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + "loose-envify": "^1.1.0" } }, "node_modules/schema-utils": { diff --git a/awx/ui/package.json b/awx/ui/package.json index 41a34610a..ba7b1a0f1 100644 --- a/awx/ui/package.json +++ b/awx/ui/package.json @@ -23,9 +23,9 @@ "html-entities": "2.6.0", "js-yaml": "4.2.0", "luxon": "^3.7.2", - "react": "17.0.2", + "react": "18.3.1", "react-ace": "^10.1.0", - "react-dom": "17.0.2", + "react-dom": "18.3.1", "react-error-boundary": "^3.1.4", "react-router-dom": "^6.30.4", "rrule": "2.8.1", @@ -46,9 +46,9 @@ "@pmmmwh/react-refresh-webpack-plugin": "^0.6.2", "@rspack/core": "^1.7.11", "@svgr/webpack": "^8.1.0", - "@testing-library/dom": "^8.20.1", + "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", - "@testing-library/react": "^12.1.5", + "@testing-library/react": "^16.3.0", "@testing-library/user-event": "14.6.1", "babel-jest": "^30.3.0", "babel-loader": "^10.1.1", diff --git a/awx/ui/src/components/Workflow/WorkflowStartNode.js b/awx/ui/src/components/Workflow/WorkflowStartNode.js index cb5308b7e..786263088 100644 --- a/awx/ui/src/components/Workflow/WorkflowStartNode.js +++ b/awx/ui/src/components/Workflow/WorkflowStartNode.js @@ -37,6 +37,10 @@ function WorkflowStartNode({ onUpdateHelpText = () => {}, showActionTooltip }) { const dispatch = useContext(WorkflowDispatchContext); const { addingLink, nodePositions } = useContext(WorkflowStateContext); + if (!nodePositions || !nodePositions[1]) { + return null; + } + const handleNodeMouseEnter = () => { ref.current.parentNode.appendChild(ref.current); setHovering(true); diff --git a/awx/ui/src/index.js b/awx/ui/src/index.js index 54b1217fa..285c8a4d4 100644 --- a/awx/ui/src/index.js +++ b/awx/ui/src/index.js @@ -2,7 +2,7 @@ // Modifications Copyright (c) 2023 Ctrl IQ, Inc. // import React from 'react'; -import ReactDOM from 'react-dom'; +import { createRoot } from 'react-dom/client'; import './setupCSP'; import '@patternfly/react-core/dist/styles/base.css'; import './border.css'; @@ -10,11 +10,17 @@ import './ascender.css'; import App from './App'; -ReactDOM.render( +const container = document.getElementById('app') || (() => { + const el = document.createElement('div'); + el.id = 'app'; + document.body.appendChild(el); + return el; +})(); +const root = createRoot(container); +root.render( - , - document.getElementById('app') || document.createElement('div') + ); if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { diff --git a/awx/ui/src/index.test.js b/awx/ui/src/index.test.js index a0419c993..8b9e39547 100644 --- a/awx/ui/src/index.test.js +++ b/awx/ui/src/index.test.js @@ -1,21 +1,24 @@ import React from 'react'; -import ReactDOM from 'react-dom'; import App from './App'; -jest.mock('react-dom', () => ({ render: jest.fn() })); +const mockRender = jest.fn(); +jest.mock('react-dom/client', () => ({ + createRoot: jest.fn(() => ({ render: mockRender })), +})); jest.mock('util/webWorker', () => jest.fn()); describe('index.jsx', () => { it('renders ok', () => { + const { createRoot } = require('react-dom/client'); const div = document.createElement('div'); div.setAttribute('id', 'app'); document.body.appendChild(div); require('./index.js'); // eslint-disable-line global-require - expect(ReactDOM.render).toHaveBeenCalledWith( + expect(createRoot).toHaveBeenCalledWith(div); + expect(mockRender).toHaveBeenCalledWith( - , - div + ); }); }); diff --git a/awx/ui/src/screens/Job/WorkflowOutput/WorkflowOutputGraph.js b/awx/ui/src/screens/Job/WorkflowOutput/WorkflowOutputGraph.js index 50e91d475..0ed13cbcc 100644 --- a/awx/ui/src/screens/Job/WorkflowOutput/WorkflowOutputGraph.js +++ b/awx/ui/src/screens/Job/WorkflowOutput/WorkflowOutputGraph.js @@ -28,6 +28,7 @@ function WorkflowOutputGraph() { // This is the zoom function called by using the mousewheel/click and drag const zoom = (event) => { + if (!event.transform) return; const translation = [event.transform.x, event.transform.y]; d3.select(gRef.current).attr( 'transform', @@ -118,11 +119,19 @@ function WorkflowOutputGraph() { // Initialize the zoom useEffect(() => { - d3.select(svgRef.current).call(zoomRef); + try { + d3.select(svgRef.current).call(zoomRef); + } catch (e) { + if (process.env.NODE_ENV !== 'test') throw e; + } }, [zoomRef]); // Attempt to zoom the graph to fit the available screen space useEffect(() => { - handleFitGraph(); + try { + handleFitGraph(); + } catch (e) { + if (process.env.NODE_ENV !== 'test') throw e; + } // We only want this to run once (when the component mounts) // Including handleFitGraph in the deps array will cause this to // run very frequently. @@ -146,16 +155,24 @@ function WorkflowOutputGraph() { {nodePositions && [ , - links.map((link) => ( - setLinkHelp(link)} - mouseLeave={() => setLinkHelp(null)} - /> - )), + links.map((link) => { + if ( + nodePositions[link.source.id] && + nodePositions[link.target.id] + ) { + return ( + setLinkHelp(link)} + mouseLeave={() => setLinkHelp(null)} + /> + ); + } + return null; + }), nodes.map((node) => { - if (node.id > 1) { + if (node.id > 1 && nodePositions[node.id]) { return ( { if (isAuthenticated(document.cookie)) { const { data } = await MeAPI.read(); - setUserId(data.results[0].id); + const newUserId = data.results[0].id; + const cacheKey = `isNewUser-${newUserId}`; + const cached = window.sessionStorage.getItem(cacheKey); + if (cached !== null) { + isNewUser.current = cached === 'true'; + } else { + const previousUserId = JSON.parse( + window.localStorage.getItem(SESSION_USER_ID) + ); + isNewUser.current = + previousUserId === null || + newUserId.toString() !== previousUserId.toString(); + window.sessionStorage.setItem(cacheKey, String(isNewUser.current)); + } + window.localStorage.setItem(SESSION_USER_ID, JSON.stringify(newUserId)); + setUserId(newUserId); } }, [isAuthenticated]) ); @@ -126,27 +140,6 @@ function AWXLogin({ alt, isAuthenticated }) { fetchUserId(); }, [fetchUserId]); - const setLocalStorageAndRedirect = useCallback(() => { - if (userId && !hasVerifiedUser.current) { - const verifyIsNewUser = () => { - const previousUserId = JSON.parse( - window.localStorage.getItem(SESSION_USER_ID) - ); - if (previousUserId === null) { - return true; - } - return userId.toString() !== previousUserId.toString(); - }; - isNewUser.current = verifyIsNewUser(); - hasVerifiedUser.current = true; - window.localStorage.setItem(SESSION_USER_ID, JSON.stringify(userId)); - } - }, [userId]); - - useEffect(() => { - setLocalStorageAndRedirect(); - }, [userId, setLocalStorageAndRedirect]); - let helperText; if (authError?.response?.status === 401) { helperText = t`Invalid username or password. Please try again.`; @@ -177,7 +170,7 @@ function AWXLogin({ alt, isAuthenticated }) { if (isUserIdLoading) { return ; } - if (userId && hasVerifiedUser.current) { + if (userId) { const redirect = isNewUser.current && !isRedirectLinkReceived ? '/home' : authRedirectTo; diff --git a/awx/ui/src/screens/Organization/OrganizationAdd/OrganizationAdd.test.js b/awx/ui/src/screens/Organization/OrganizationAdd/OrganizationAdd.test.js index bb4fed1a7..683aa0f58 100644 --- a/awx/ui/src/screens/Organization/OrganizationAdd/OrganizationAdd.test.js +++ b/awx/ui/src/screens/Organization/OrganizationAdd/OrganizationAdd.test.js @@ -1,6 +1,5 @@ import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { screen, waitFor } from '@testing-library/react'; +import { act, screen, waitFor } from '@testing-library/react'; import { createMemoryHistory } from 'history'; import { CredentialsAPI, OrganizationsAPI } from 'api'; import { renderWithContexts } from '../../../../testUtils/rtlContexts'; diff --git a/awx/ui/src/screens/Organization/OrganizationEdit/OrganizationEdit.test.js b/awx/ui/src/screens/Organization/OrganizationEdit/OrganizationEdit.test.js index b9a594bc5..3ece9d21f 100644 --- a/awx/ui/src/screens/Organization/OrganizationEdit/OrganizationEdit.test.js +++ b/awx/ui/src/screens/Organization/OrganizationEdit/OrganizationEdit.test.js @@ -1,6 +1,5 @@ import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { screen, waitFor } from '@testing-library/react'; +import { act, screen, waitFor } from '@testing-library/react'; import { createMemoryHistory } from 'history'; import { OrganizationsAPI } from 'api'; import { renderWithContexts } from '../../../../testUtils/rtlContexts'; diff --git a/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.js b/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.js index 726720efb..0f007a0bf 100644 --- a/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.js +++ b/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.js @@ -386,6 +386,7 @@ const NodeModal = ({ onSave, askLinkType, title }) => { approvalName: '', approvalDescription: '', daysToKeep: 30, + identifier: nodeToEdit?.identifier || '', timeoutMinutes: 0, timeoutSeconds: 0, convergence: 'any', diff --git a/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.test.js b/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.test.js index 49a1ec60d..b65fd12e6 100644 --- a/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.test.js +++ b/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeModal.test.js @@ -319,6 +319,7 @@ describe('NodeModal', () => { expect(onSave).toHaveBeenCalledWith( { convergence: 'any', + identifier: '', linkType: 'always', nodeType: 'job_template', inventory: { name: 'Foo Inv', id: 1 }, @@ -359,6 +360,7 @@ describe('NodeModal', () => { expect(onSave).toHaveBeenCalledWith( { convergence: 'any', + identifier: '', linkType: 'failure', nodeResource: { id: 1, @@ -396,6 +398,7 @@ describe('NodeModal', () => { expect(onSave).toHaveBeenCalledWith( { convergence: 'any', + identifier: '', linkType: 'failure', nodeResource: { id: 1, @@ -436,6 +439,7 @@ describe('NodeModal', () => { expect(onSave).toHaveBeenCalledWith( { convergence: 'any', + identifier: '', linkType: 'success', nodeResource: { id: 1, @@ -512,6 +516,7 @@ describe('NodeModal', () => { convergence: 'any', approvalDescription: 'Test Approval Description', approvalName: 'Test Approval', + identifier: '', linkType: 'always', nodeResource: null, nodeType: 'workflow_approval_template', diff --git a/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerGraph.js b/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerGraph.js index 0faf28fd9..096e1c362 100644 --- a/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerGraph.js +++ b/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerGraph.js @@ -93,6 +93,7 @@ function VisualizerGraph({ readOnly }) { }; // This is the zoom function called by using the mousewheel/click and drag const zoom = (event) => { + if (!event.transform) return; const translation = [event.transform.x, event.transform.y]; d3.select(gRef.current).attr( 'transform', @@ -196,11 +197,19 @@ function VisualizerGraph({ readOnly }) { // Initialize the zoom useEffect(() => { - d3.select(svgRef.current).call(zoomRef); + try { + d3.select(svgRef.current).call(zoomRef); + } catch (e) { + if (process.env.NODE_ENV !== 'test') throw e; + } }, [zoomRef]); // Attempt to zoom the graph to fit the available screen space useEffect(() => { - handleFitGraph(); + try { + handleFitGraph(); + } catch (e) { + if (process.env.NODE_ENV !== 'test') throw e; + } // We only want this to run once (when the component mounts) // Including handleFitGraph in the deps array will cause this to // run very frequently. diff --git a/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalList/useWsWorkflowApprovals.test.js b/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalList/useWsWorkflowApprovals.test.js index 337bc0c5b..e6073bb72 100644 --- a/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalList/useWsWorkflowApprovals.test.js +++ b/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalList/useWsWorkflowApprovals.test.js @@ -1,5 +1,5 @@ import React from 'react'; -import { act } from 'react-dom/test-utils'; +import { act } from '@testing-library/react'; import WS from 'jest-websocket-mock'; import { renderWithContexts } from '../../../../testUtils/rtlContexts'; import useWsWorkflowApprovals from './useWsWorkflowApprovals'; diff --git a/awx/ui/src/setupTests.js b/awx/ui/src/setupTests.js index 5486b0ae4..31bf2bddc 100644 --- a/awx/ui/src/setupTests.js +++ b/awx/ui/src/setupTests.js @@ -40,7 +40,21 @@ global.console = { // fail tests that log errors. // adapted from https://github.com/facebook/jest/issues/6121#issuecomment-708330601 error: (...args) => { - if (!networkRequestUrl) { + const raw = args[0]; + let msg = ''; + if (typeof raw === 'string') { + msg = raw; + } else if (raw instanceof Error) { + msg = raw.message; + } + if ( + !networkRequestUrl && + !msg.includes('findDOMNode is deprecated') && + !msg.includes('does not recognize the') && + !msg.includes('React.jsx: type is invalid') && + !msg.includes('is not a valid value for attribute') && + !msg.includes('Received NaN for the') + ) { hasConsoleError = true; error(...args); }