diff --git a/src/vscode-bicep/jest.config.snapshot.js b/src/vscode-bicep/jest.config.snapshot.js index af224309c88..a9120a69170 100644 --- a/src/vscode-bicep/jest.config.snapshot.js +++ b/src/vscode-bicep/jest.config.snapshot.js @@ -6,6 +6,7 @@ module.exports = { transform: { "^.+\\.tsx?$": "ts-jest", }, + setupFiles: ["/src/test/snapshot/setup.ts"], verbose: true, collectCoverage: true, collectCoverageFrom: ["/src/visualizer/app/**/*.ts"], @@ -13,5 +14,7 @@ module.exports = { moduleNameMapper: { "^.+\\.svg$": "/src/test/snapshot/__mocks__/svgMock.ts", "^.+/vscode$": "/src/test/snapshot/__mocks__/vscode.ts", + "^cytoscape$": "/src/test/snapshot/__mocks__/cytoscapeMock.ts", + "^cytoscape-elk$": "/src/test/snapshot/__mocks__/cytoscapeMock.ts", }, }; diff --git a/src/vscode-bicep/package-lock.json b/src/vscode-bicep/package-lock.json index 2598d0ca6a4..92d5133d922 100644 --- a/src/vscode-bicep/package-lock.json +++ b/src/vscode-bicep/package-lock.json @@ -20,8 +20,8 @@ "cytoscape": "^3.30.4", "cytoscape-elk": "^2.3.0", "fs-extra": "^11.3.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.2.4", + "react-dom": "^19.2.4", "react-icons": "^5.5.0", "styled-components": "^6.1.18", "stylis": "^4.3.6", @@ -36,13 +36,14 @@ "@eslint/compat": "^2.0.5", "@eslint/js": "^10.0.1", "@ianvs/prettier-plugin-sort-imports": "^4.4.2", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", "@types/cytoscape": "^3.21.7", "@types/fs-extra": "^11.0.4", "@types/jest": "^30.0.0", "@types/node": "^22.15.2", - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.0", - "@types/react-test-renderer": "^18.3.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", "@types/semver": "^7.7.0", "@types/triple-beam": "^1.3.5", "@types/vscode": "^1.101.0", @@ -61,7 +62,6 @@ "moment": "^2.30.1", "nerdbank-gitversioning": "^3.7.115", "prettier": "^3.5.3", - "react-test-renderer": "^18.3.1", "rimraf": "^6.0.1", "semver": "^7.7.1", "svg-inline-loader": "^0.8.2", @@ -556,8 +556,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@azure/ms-rest-azure-env/-/ms-rest-azure-env-2.0.0.tgz", "integrity": "sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@azure/msal-browser": { "version": "5.6.3", @@ -625,7 +624,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1073,6 +1071,16 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", @@ -4735,6 +4743,131 @@ "text-hex": "1.0.x" } }, + "node_modules/@testing-library/dom": { + "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", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "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": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/react": { + "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" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@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/@textlint/ast-node-types": { "version": "15.5.2", "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.5.2.tgz", @@ -4858,6 +4991,14 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -5030,7 +5171,6 @@ "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -5042,42 +5182,24 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/prop-types": { - "version": "15.7.15", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", - "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/react": { - "version": "18.3.28", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", - "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "dev": true, "license": "MIT", "dependencies": { - "@types/prop-types": "*", "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", - "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", "peerDependencies": { - "@types/react": "^18.0.0" - } - }, - "node_modules/@types/react-test-renderer": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.3.1.tgz", - "integrity": "sha512-vAhnk0tG2eGa37lkU9+s5SoroCsRI08xnsWFiAXOuPH2jqzMbcXvKExXViPi1P5fIklDeCvXqyrdmipFaSkZrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/react": "^18" + "@types/react": "^19.2.0" } }, "node_modules/@types/sarif": { @@ -5201,7 +5323,6 @@ "integrity": "sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.57.1", "@typescript-eslint/types": "8.57.1", @@ -6126,7 +6247,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6185,7 +6305,6 @@ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -6215,19 +6334,6 @@ } } }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -6301,6 +6407,16 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/aria-query": { + "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": { + "dequal": "^2.0.3" + } + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -6595,7 +6711,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -7265,6 +7380,13 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, "node_modules/cssstyle": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", @@ -7459,6 +7581,16 @@ "node": ">=0.4.0" } }, + "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/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -7496,6 +7628,14 @@ "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==", "license": "MIT" }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -7877,7 +8017,6 @@ "integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", @@ -10538,6 +10677,16 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/index-to-position": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", @@ -10904,7 +11053,6 @@ "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.3.0", "@jest/types": "30.3.0", @@ -12360,6 +12508,17 @@ "node": ">=10" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -12552,6 +12711,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -13411,7 +13580,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", @@ -13472,7 +13640,6 @@ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -13665,11 +13832,10 @@ } }, "node_modules/react": { - "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==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -13678,17 +13844,15 @@ } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", - "peer": true, "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.2.4" } }, "node_modules/react-icons": { @@ -13707,35 +13871,6 @@ "dev": true, "license": "MIT" }, - "node_modules/react-shallow-renderer": { - "version": "16.15.0", - "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", - "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "object-assign": "^4.1.1", - "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-test-renderer": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.3.1.tgz", - "integrity": "sha512-KkAgygexHUkQqtvvx/otwxtuFu5cVjfzTCtjXLH9boS19/Nbtg84zS7wIQn39G8IlrhThBpQsMKkq5ZHZIYFXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "react-is": "^18.3.1", - "react-shallow-renderer": "^16.15.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, "node_modules/read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", @@ -13884,6 +14019,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -14180,13 +14329,10 @@ } }, "node_modules/scheduler": { - "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" - } + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" }, "node_modules/schema-utils": { "version": "4.3.3", @@ -14214,7 +14360,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -14835,6 +14980,19 @@ "node": ">=6" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -15356,7 +15514,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -15534,7 +15691,6 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -15584,8 +15740,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "peer": true + "license": "0BSD" }, "node_modules/tunnel": { "version": "0.0.6", @@ -15665,7 +15820,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -16046,7 +16200,6 @@ "integrity": "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -16096,7 +16249,6 @@ "integrity": "sha512-dB0R4T+C/8YuvM+fabdvil6QE44/ChDXikV5lOOkrUeCkW5hTJv2pGLE3keh+D5hjYw8icBaJkZzpFoaHV4T+g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@discoveryjs/json-ext": "^1.0.0", "commander": "^14.0.3", diff --git a/src/vscode-bicep/package.json b/src/vscode-bicep/package.json index b074a662fe0..83440c1164c 100644 --- a/src/vscode-bicep/package.json +++ b/src/vscode-bicep/package.json @@ -808,13 +808,14 @@ "@eslint/compat": "^2.0.5", "@eslint/js": "^10.0.1", "@ianvs/prettier-plugin-sort-imports": "^4.4.2", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", "@types/cytoscape": "^3.21.7", "@types/fs-extra": "^11.0.4", "@types/jest": "^30.0.0", "@types/node": "^22.15.2", - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.0", - "@types/react-test-renderer": "^18.3.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", "@types/semver": "^7.7.0", "@types/triple-beam": "^1.3.5", "@types/vscode": "^1.101.0", @@ -834,7 +835,6 @@ "moment": "^2.30.1", "nerdbank-gitversioning": "^3.7.115", "prettier": "^3.5.3", - "react-test-renderer": "^18.3.1", "rimraf": "^6.0.1", "semver": "^7.7.1", "svg-inline-loader": "^0.8.2", @@ -858,8 +858,8 @@ "cytoscape": "^3.30.4", "cytoscape-elk": "^2.3.0", "fs-extra": "^11.3.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.2.4", + "react-dom": "^19.2.4", "react-icons": "^5.5.0", "styled-components": "^6.1.18", "stylis": "^4.3.6", diff --git a/src/vscode-bicep/src/test/snapshot/App.test.tsx b/src/vscode-bicep/src/test/snapshot/App.test.tsx index 3556f566e90..eb07802ebfa 100644 --- a/src/vscode-bicep/src/test/snapshot/App.test.tsx +++ b/src/vscode-bicep/src/test/snapshot/App.test.tsx @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import renderer from "react-test-renderer"; +import { render } from "@testing-library/react"; import "jest-styled-components"; @@ -8,8 +8,8 @@ import { App } from "../../visualizer/app/components/App"; describe("component App", () => { it("should render", () => { - const tree = renderer.create().toJSON(); + const { asFragment } = render(); - expect(tree).toMatchSnapshot(); + expect(asFragment()).toMatchSnapshot(); }); }); diff --git a/src/vscode-bicep/src/test/snapshot/Graph.test.tsx b/src/vscode-bicep/src/test/snapshot/Graph.test.tsx index 7590b6e2afa..47ad0df3997 100644 --- a/src/vscode-bicep/src/test/snapshot/Graph.test.tsx +++ b/src/vscode-bicep/src/test/snapshot/Graph.test.tsx @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import renderer from "react-test-renderer"; +import { render } from "@testing-library/react"; import { ThemeProvider } from "styled-components"; import "jest-styled-components"; @@ -10,12 +10,12 @@ import { darkTheme } from "../../visualizer/app/themes"; describe("component Graph", () => { it("should render", () => { - const graph = renderer.create( + const { asFragment } = render( , ); - expect(graph.toJSON()).toMatchSnapshot(); + expect(asFragment()).toMatchSnapshot(); }); }); diff --git a/src/vscode-bicep/src/test/snapshot/__mocks__/cytoscapeMock.ts b/src/vscode-bicep/src/test/snapshot/__mocks__/cytoscapeMock.ts new file mode 100644 index 00000000000..f3d591e3f40 --- /dev/null +++ b/src/vscode-bicep/src/test/snapshot/__mocks__/cytoscapeMock.ts @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// Mock cytoscape to avoid canvas/DOM requirements in snapshot tests. +const cytoscapeMock = jest.fn(() => ({ + on: jest.fn(), + addListener: jest.fn(), + removeListener: jest.fn(), + destroy: jest.fn(), + style: jest.fn(), + json: jest.fn(), + layout: jest.fn(() => ({ run: jest.fn(), stop: jest.fn() })), + zoom: jest.fn(), + animate: jest.fn(), + elements: jest.fn(() => []), + maxZoom: jest.fn(), + minZoom: jest.fn(), +})); + +(cytoscapeMock as jest.Mock & { use: jest.Mock }).use = jest.fn(); + +module.exports = cytoscapeMock; diff --git a/src/vscode-bicep/src/test/snapshot/__mocks__/vscode.ts b/src/vscode-bicep/src/test/snapshot/__mocks__/vscode.ts index e3253dc7b86..b91709fcec7 100644 --- a/src/vscode-bicep/src/test/snapshot/__mocks__/vscode.ts +++ b/src/vscode-bicep/src/test/snapshot/__mocks__/vscode.ts @@ -1,11 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -export const vscode = (): { - postMessage(message: unknown): void; - setState(state: unknown): void; - getState(): T; -} => ({ + +// Mock for the vscode API object. The export name must match the original module. +export const vscode = { postMessage: jest.fn(), setState: jest.fn(), getState: jest.fn(), -}); +}; diff --git a/src/vscode-bicep/src/test/snapshot/__snapshots__/App.test.tsx.snap b/src/vscode-bicep/src/test/snapshot/__snapshots__/App.test.tsx.snap index 399a82d919d..9d28eaf41ca 100644 --- a/src/vscode-bicep/src/test/snapshot/__snapshots__/App.test.tsx.snap +++ b/src/vscode-bicep/src/test/snapshot/__snapshots__/App.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`component App should render 1`] = ` -[ + .c0 { position: absolute; left: 0px; @@ -16,8 +16,8 @@ exports[`component App should render 1`] = ` }
, + class="c0" + /> .c1 { display: relative; user-select: none; @@ -60,125 +60,95 @@ exports[`component App should render 1`] = ` }
Zoom in
-
+
Zoom out
-
+
Reset layout
-
+
Fit
-
+
-
, +
.c0 { position: absolute; height: 32px; @@ -263,15 +223,14 @@ exports[`component App should render 1`] = ` }
There are no resources or modules in the file. Nothing to display.
-
, -] +
+ `; diff --git a/src/vscode-bicep/src/test/snapshot/__snapshots__/Graph.test.tsx.snap b/src/vscode-bicep/src/test/snapshot/__snapshots__/Graph.test.tsx.snap index 48c05c41888..4bc227a959c 100644 --- a/src/vscode-bicep/src/test/snapshot/__snapshots__/Graph.test.tsx.snap +++ b/src/vscode-bicep/src/test/snapshot/__snapshots__/Graph.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`component Graph should render 1`] = ` -[ + .c0 { position: absolute; left: 0px; @@ -16,8 +16,8 @@ exports[`component Graph should render 1`] = ` }
, + class="c0" + /> .c1 { display: relative; user-select: none; @@ -60,125 +60,95 @@ exports[`component Graph should render 1`] = ` }
Zoom in
-
+
Zoom out
-
+
Reset layout
-
+
Fit
-
+
-
, -] +
+ `; diff --git a/src/vscode-bicep/src/test/snapshot/setup.ts b/src/vscode-bicep/src/test/snapshot/setup.ts new file mode 100644 index 00000000000..981bf1fa4ce --- /dev/null +++ b/src/vscode-bicep/src/test/snapshot/setup.ts @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// Mock HTMLCanvasElement.getContext as jsdom does not implement it, +// but cytoscape requires it during initialization. +const mockContext = { + canvas: {} as HTMLCanvasElement, + fillRect: jest.fn(), + clearRect: jest.fn(), + getImageData: jest.fn(() => ({ data: new Array(4) })), + putImageData: jest.fn(), + createImageData: jest.fn(() => []), + setTransform: jest.fn(), + drawImage: jest.fn(), + save: jest.fn(), + fillText: jest.fn(), + restore: jest.fn(), + beginPath: jest.fn(), + moveTo: jest.fn(), + lineTo: jest.fn(), + closePath: jest.fn(), + stroke: jest.fn(), + translate: jest.fn(), + scale: jest.fn(), + rotate: jest.fn(), + arc: jest.fn(), + fill: jest.fn(), + measureText: jest.fn(() => ({ width: 0 })), + transform: jest.fn(), + rect: jest.fn(), + clip: jest.fn(), + bezierCurveTo: jest.fn(), + quadraticCurveTo: jest.fn(), + createLinearGradient: jest.fn(() => ({ addColorStop: jest.fn() })), + createRadialGradient: jest.fn(() => ({ addColorStop: jest.fn() })), + createPattern: jest.fn(), + ellipse: jest.fn(), + roundRect: jest.fn(), +}; + +HTMLCanvasElement.prototype.getContext = jest.fn(() => mockContext) as unknown as typeof HTMLCanvasElement.prototype.getContext; diff --git a/src/vscode-bicep/src/visualizer/app/components/App.tsx b/src/vscode-bicep/src/visualizer/app/components/App.tsx index 3a2f8f74dd6..f4293f5bc7b 100644 --- a/src/vscode-bicep/src/visualizer/app/components/App.tsx +++ b/src/vscode-bicep/src/visualizer/app/components/App.tsx @@ -2,7 +2,7 @@ // Licensed under the MIT License. // import cytoscape from "cytoscape"; import { ElementDefinition } from "cytoscape"; -import { useEffect, useState, VFC } from "react"; +import { FC, useEffect, useState } from "react"; import { DefaultTheme, ThemeProvider } from "styled-components"; import { DeploymentGraph } from "../../../language"; import { DeploymentGraphMessage, Message, READY_MESSAGE } from "../../messages"; @@ -51,7 +51,7 @@ async function mapToElements( return [...nodes, ...edges]; } -export const App: VFC = () => { +export const App: FC = () => { const [elements, setElements] = useState([]); const [graph, setGraph] = useState(null); const [theme, setTheme] = useState(darkTheme); diff --git a/src/vscode-bicep/src/visualizer/app/components/StatusBar.tsx b/src/vscode-bicep/src/visualizer/app/components/StatusBar.tsx index 54a85fe085e..2ed330f1f0f 100644 --- a/src/vscode-bicep/src/visualizer/app/components/StatusBar.tsx +++ b/src/vscode-bicep/src/visualizer/app/components/StatusBar.tsx @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { memo, VFC } from "react"; +import { FC, memo } from "react"; import styled from "styled-components"; interface StatusBarProps { @@ -29,7 +29,7 @@ const StatusCircle = styled.div<{ hasErrors: boolean }>` margin-right: 8px; `; -const StatusBarComponent: VFC = ({ errorCount, hasNodes }) => ( +const StatusBarComponent: FC = ({ errorCount, hasNodes }) => ( 0} /> {errorCount > 0 && ( diff --git a/src/vscode-bicep/src/visualizer/app/components/Tooltip.tsx b/src/vscode-bicep/src/visualizer/app/components/Tooltip.tsx index c51a33a6381..5ce204106fa 100644 --- a/src/vscode-bicep/src/visualizer/app/components/Tooltip.tsx +++ b/src/vscode-bicep/src/visualizer/app/components/Tooltip.tsx @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { ReactNode, useRef, useState, VFC } from "react"; +import { FC, ReactNode, useRef, useState } from "react"; import styled from "styled-components"; interface TooltipHostProps { @@ -25,7 +25,7 @@ const TooltipBox = styled.div<{ active: boolean }>` display: ${({ active }) => (active ? "block" : "none")}; `; -export const TooltipHost: VFC = ({ content, children }) => { +export const TooltipHost: FC = ({ content, children }) => { const [active, setActive] = useState(false); const timeoutRef = useRef(null); diff --git a/src/vscode-bicep/src/visualizer/app/hooks/useCytoscape.ts b/src/vscode-bicep/src/visualizer/app/hooks/useCytoscape.ts index 0822e959954..25db4b6db6f 100644 --- a/src/vscode-bicep/src/visualizer/app/hooks/useCytoscape.ts +++ b/src/vscode-bicep/src/visualizer/app/hooks/useCytoscape.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import cytoscape, { Core, LayoutOptions, Layouts, StylesheetStyle } from "cytoscape"; import elk from "cytoscape-elk"; -import React, { createContext, RefObject, useEffect, useRef } from "react"; +import { createContext, RefObject, useEffect, useRef } from "react"; export interface ZoomOptions { minLevel: number; @@ -11,7 +11,7 @@ export interface ZoomOptions { } export interface CreationOptions { - containerRef: RefObject; + containerRef: RefObject; layoutOptions: LayoutOptions; zoomOptions: ZoomOptions; onNodeDoubleTap: (event: cytoscape.EventObjectNode) => void; @@ -21,9 +21,9 @@ export function useCytoscape( elements: cytoscape.ElementDefinition[], stylesheets: StylesheetStyle[], { containerRef, layoutOptions, zoomOptions, onNodeDoubleTap }: CreationOptions, -): [React.MutableRefObject, React.MutableRefObject] { - const cytoscapeRef = useRef(); - const layoutRef = useRef(); +): [RefObject, RefObject] { + const cytoscapeRef = useRef(undefined); + const layoutRef = useRef(undefined); // Initialize cytoscape. useEffect(() => {