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);
}