diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 6307c40d57c..b10c58bf80e 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -194,7 +194,6 @@ "@testing-library/user-event": "^12.8.3", "@types/classnames": "^2.2.10", "@types/dom-to-image": "^2.6.7", - "@types/enzyme": "^3.10.18", "@types/fetch-mock": "^7.3.2", "@types/jest": "^29.5.12", "@types/jquery": "^3.5.8", @@ -224,7 +223,6 @@ "@types/yargs": "12 - 18", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", - "@wojtekmaj/enzyme-adapter-react-17": "^0.8.0", "babel-jest": "^29.7.0", "babel-loader": "^10.0.0", "babel-plugin-dynamic-import-node": "^2.3.3", @@ -236,8 +234,6 @@ "cross-env": "^7.0.3", "css-loader": "^7.1.2", "css-minimizer-webpack-plugin": "^7.0.2", - "enzyme": "^3.11.0", - "enzyme-matchers": "^7.1.2", "eslint": "^8.56.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-prettier": "^7.2.0", @@ -14053,55 +14049,6 @@ } } }, - "node_modules/@wojtekmaj/enzyme-adapter-react-17": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@wojtekmaj/enzyme-adapter-react-17/-/enzyme-adapter-react-17-0.8.0.tgz", - "integrity": "sha512-zeUGfQRziXW7R7skzNuJyi01ZwuKCH8WiBNnTgUJwdS/CURrJwAhWsfW7nG7E30ak8Pu3ZwD9PlK9skBfAoOBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@wojtekmaj/enzyme-adapter-utils": "^0.2.0", - "enzyme-shallow-equal": "^1.0.0", - "has": "^1.0.0", - "prop-types": "^15.7.0", - "react-is": "^17.0.0", - "react-test-renderer": "^17.0.0" - }, - "funding": { - "url": "https://github.com/wojtekmaj/enzyme-adapter-react-17?sponsor=1" - }, - "peerDependencies": { - "enzyme": "^3.0.0", - "react": "^17.0.0-0", - "react-dom": "^17.0.0-0" - } - }, - "node_modules/@wojtekmaj/enzyme-adapter-react-17/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" - }, - "node_modules/@wojtekmaj/enzyme-adapter-utils": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@wojtekmaj/enzyme-adapter-utils/-/enzyme-adapter-utils-0.2.0.tgz", - "integrity": "sha512-ZvZm9kZxZEKAbw+M1/Q3iDuqQndVoN8uLnxZ8bzxm7KgGTBejrGRoJAp8f1EN8eoO3iAjBNEQnTDW/H4Ekb0FQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function.prototype.name": "^1.1.0", - "has": "^1.0.0", - "object.fromentries": "^2.0.0", - "prop-types": "^15.7.0" - }, - "funding": { - "url": "https://github.com/wojtekmaj/enzyme-adapter-utils?sponsor=1" - }, - "peerDependencies": { - "react": "^17.0.0-0" - } - }, "node_modules/@xmldom/xmldom": { "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", @@ -15647,6 +15594,8 @@ "integrity": "sha512-r+mCJ7zXgXElgR4IRC+fkvNCeoaavWBs6EdCso5Tbcf+iEMKzBU/His60lt34WEZ9vlb8wDkZvQGcVI5GwkfoQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -17792,13 +17741,6 @@ "node": ">=8" } }, - "node_modules/circular-json-es6": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/circular-json-es6/-/circular-json-es6-2.0.2.tgz", - "integrity": "sha512-ODYONMMNb3p658Zv+Pp+/XPa5s6q7afhz3Tzyvo+VRh9WIrJ64J76ZC4GQxnlye/NesTn09jvOiuE8+xxfpwhQ==", - "dev": true, - "license": "MIT" - }, "node_modules/citty": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", @@ -20098,28 +20040,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/deep-equal-ident": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal-ident/-/deep-equal-ident-1.1.1.tgz", - "integrity": "sha512-aWv7VhTl/Lju1zenOD3E1w8PpUVrTDbwXCHtbSNr+p/uadr49Y1P1ld0W3Pl6gbvIbiRjoCVsqw70UupCNGh6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash.isequal": "^3.0" - } - }, - "node_modules/deep-equal-ident/node_modules/lodash.isequal": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-3.0.4.tgz", - "integrity": "sha512-Bsu5fP9Omd+HBk2Dz8qp4BHbC+83DBykZ87Lz1JmPKTVNy4Q0XQVtUrbfXVAK/udQrWNcGStcKSA9yj/Zkm3TQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash._baseisequal": "^3.0.0", - "lodash._bindcallback": "^3.0.0" - } - }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -20516,7 +20436,9 @@ "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/distributions": { "version": "2.2.0", @@ -21204,6 +21126,8 @@ "integrity": "sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "array.prototype.flat": "^1.2.3", "cheerio": "^1.0.0-rc.3", @@ -21232,26 +21156,14 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/enzyme-matchers": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/enzyme-matchers/-/enzyme-matchers-7.1.2.tgz", - "integrity": "sha512-03WqAg2XDl7id9rARIO97HQ1JIw9F2heJ3R4meGu/13hx0ULTDEgl0E67MGl2Uq1jq1DyRnJfto1/VSzskdV5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "circular-json-es6": "^2.0.1", - "deep-equal-ident": "^1.1.1" - }, - "peerDependencies": { - "enzyme": ">=3.4.0" - } - }, "node_modules/enzyme-shallow-equal": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.7.tgz", "integrity": "sha512-/um0GFqUXnpM9SvKtje+9Tjoz3f1fpBC3eXRFrNs8kpYn69JljciYP7KZTqM/YQbUY9KUjvKB4jo/q+L6WGGvg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "hasown": "^2.0.0", "object-is": "^1.1.5" @@ -26262,6 +26174,8 @@ "integrity": "sha512-6XMlxrAFX4UEEGxctfFnmrFaaZFNf9i5fNuV5wZ3WWQ4FVaNP1aX1LkX9j2mfEx1NpjeE/rL3nmgEn23GdFmrg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "array.prototype.filter": "^1.0.0", "call-bind": "^1.0.2" @@ -31531,32 +31445,6 @@ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", "license": "MIT" }, - "node_modules/lodash._baseisequal": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz", - "integrity": "sha512-U+3GsNEZj9ebI03ncLC2pLmYVjgtYZEwdkAPO7UGgtGvAz36JVFPAQUufpSaVL93Cz5arc6JGRKZRhaOhyVJYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash.isarray": "^3.0.0", - "lodash.istypedarray": "^3.0.0", - "lodash.keys": "^3.0.0" - } - }, - "node_modules/lodash._bindcallback": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz", - "integrity": "sha512-2wlI0JRAGX8WEf4Gm1p/mv/SZ+jLijpj0jyaE/AXeuQphzCgD8ZQW4oSpoN8JAopujOFGU3KMuq7qfHBWlGpjQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash._getnative": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.curry": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", @@ -31575,14 +31463,17 @@ "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash.get": { "version": "4.4.2", @@ -31590,20 +31481,6 @@ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "license": "MIT" }, - "node_modules/lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isarray": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -31624,25 +31501,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.istypedarray": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz", - "integrity": "sha512-lGWJ6N8AA3KSv+ZZxlTdn4f6A7kMfpJboeyvbFdE7IU9YAgweODqmOgdUHOA+c6lVWeVLysdaxciFXi+foVsWw==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha512-CuBsapFjcubOGMn3VD+24HOAPxM79tH+V6ivJL3CHYjtrawauDJHUk//Yew9Hvc6e9rbCrURGk8z6PC+8WJBfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" - } - }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -33911,7 +33769,9 @@ "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==", "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "optional": true, + "peer": true }, "node_modules/mousetrap": { "version": "1.6.5", @@ -34059,6 +33919,8 @@ "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "commander": "^2.19.0", "moo": "^0.5.0", @@ -34081,7 +33943,9 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/needle": { "version": "3.3.1", @@ -38276,8 +38140,8 @@ "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "devOptional": true, "license": "MIT", + "optional": true, "dependencies": { "performance-now": "^2.1.0" } @@ -38287,7 +38151,9 @@ "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==", "dev": true, - "license": "CC0-1.0" + "license": "CC0-1.0", + "optional": true, + "peer": true }, "node_modules/ramda": { "version": "0.29.0", @@ -38305,6 +38171,8 @@ "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "discontinuous-range": "1.0.0", "ret": "~0.1.10" @@ -39736,8 +39604,9 @@ "version": "16.15.0", "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", - "devOptional": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "object-assign": "^4.1.1", "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" @@ -39831,8 +39700,9 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-17.0.2.tgz", "integrity": "sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ==", - "devOptional": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "object-assign": "^4.1.1", "react-is": "^17.0.2", @@ -39847,8 +39717,9 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "devOptional": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/react-transition-group": { "version": "4.4.5", @@ -41985,6 +41856,8 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=0.12" } @@ -42157,6 +42030,8 @@ "integrity": "sha512-nDG1rZeP6oFTLN6yNDV/uiAvs1+FS/KlrEwh7+y7dpuApDBy6bI2HTBcc0/V8lv9OTqfyD34eF7au2pm8aBbhA==", "dev": true, "license": "BSD-3-Clause", + "optional": true, + "peer": true, "dependencies": { "lodash.flattendeep": "^4.4.0", "nearley": "^2.7.10" diff --git a/superset-frontend/package.json b/superset-frontend/package.json index 9c9422e7946..ad8a68d444d 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -261,7 +261,6 @@ "@testing-library/user-event": "^12.8.3", "@types/classnames": "^2.2.10", "@types/dom-to-image": "^2.6.7", - "@types/enzyme": "^3.10.18", "@types/fetch-mock": "^7.3.2", "@types/jest": "^29.5.12", "@types/jquery": "^3.5.8", @@ -291,7 +290,6 @@ "@types/yargs": "12 - 18", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", - "@wojtekmaj/enzyme-adapter-react-17": "^0.8.0", "babel-jest": "^29.7.0", "babel-loader": "^10.0.0", "babel-plugin-dynamic-import-node": "^2.3.3", @@ -303,8 +301,6 @@ "cross-env": "^7.0.3", "css-loader": "^7.1.2", "css-minimizer-webpack-plugin": "^7.0.2", - "enzyme": "^3.11.0", - "enzyme-matchers": "^7.1.2", "eslint": "^8.56.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-prettier": "^7.2.0", diff --git a/superset-frontend/packages/superset-ui-chart-controls/package.json b/superset-frontend/packages/superset-ui-chart-controls/package.json index 76a1169a3d2..c487d3449d4 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/package.json +++ b/superset-frontend/packages/superset-ui-chart-controls/package.json @@ -25,7 +25,6 @@ ], "dependencies": { "@react-icons/all-files": "^4.1.0", - "@types/enzyme": "^3.10.18", "@types/react": "*", "lodash": "^4.17.21", "prop-types": "^15.8.1" diff --git a/superset-frontend/packages/superset-ui-core/package.json b/superset-frontend/packages/superset-ui-core/package.json index 158aaa15d35..a6393c22e8b 100644 --- a/superset-frontend/packages/superset-ui-core/package.json +++ b/superset-frontend/packages/superset-ui-core/package.json @@ -56,7 +56,6 @@ "@types/d3-scale": "^2.1.1", "@types/d3-time": "^3.0.4", "@types/d3-time-format": "^4.0.3", - "@types/enzyme": "^3.10.18", "@types/fetch-mock": "^7.3.8", "@types/lodash": "^4.17.16", "@types/math-expression-evaluator": "^1.3.3", diff --git a/superset-frontend/scripts/eslint-metrics-uploader.js b/superset-frontend/scripts/eslint-metrics-uploader.js index 6c6d4a9de92..0b38e590bac 100644 --- a/superset-frontend/scripts/eslint-metrics-uploader.js +++ b/superset-frontend/scripts/eslint-metrics-uploader.js @@ -58,7 +58,7 @@ module.exports = async results => { // Ignore thie rule here - the messages in eslintrc_metrics.js are sufficient descriptions, broken down by file type. 'no-restricted-imports': { description: - "This rule catches several things that shouldn't be used anymore. LESS, antD, enzyme, etc. See individual occurrence messages for details", + "This rule catches several things that shouldn't be used anymore. LESS, antD, etc. See individual occurrence messages for details", }, 'no-console': { description: diff --git a/superset-frontend/spec/helpers/shim.tsx b/superset-frontend/spec/helpers/shim.tsx index af5ea846815..c8c19a56613 100644 --- a/superset-frontend/spec/helpers/shim.tsx +++ b/superset-frontend/spec/helpers/shim.tsx @@ -20,10 +20,7 @@ import { AriaAttributes } from 'react'; import 'core-js/stable'; import 'regenerator-runtime/runtime'; import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'; -import 'enzyme-matchers'; import jQuery from 'jquery'; -import Enzyme from 'enzyme'; -import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; // https://jestjs.io/docs/jest-object#jestmockmodulename-factory-options // in order to mock modules in test case, so avoid absolute import module import { configure as configureTranslation } from '../../packages/superset-ui-core/src/translation'; @@ -33,8 +30,6 @@ import { ResizeObserver } from './ResizeObserver'; import setupSupersetClient from './setupSupersetClient'; import CacheStorage from './CacheStorage'; -Enzyme.configure({ adapter: new Adapter() }); - const exposedProperties = ['window', 'navigator', 'document']; const { defaultView } = document; diff --git a/superset-frontend/spec/helpers/theming.ts b/superset-frontend/spec/helpers/theming.ts deleted file mode 100644 index d24126b8d16..00000000000 --- a/superset-frontend/spec/helpers/theming.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { mount as enzymeMount } from 'enzyme'; -// eslint-disable-next-line no-restricted-imports -import { supersetTheme } from '@superset-ui/core'; -import { ReactElement } from 'react'; -import { ProviderWrapper } from './ProviderWrapper'; - -type optionsType = { - wrappingComponentProps?: any; - wrappingComponent?: ReactElement; - context?: any; - newOption?: string; -}; - -export function styledMount( - component: ReactElement, - options: optionsType = {}, -): any { - return enzymeMount(component, { - ...options, - wrappingComponent: ProviderWrapper, - wrappingComponentProps: { - theme: supersetTheme, - ...options?.wrappingComponentProps, - }, - }); -} diff --git a/superset-frontend/spec/helpers/waitForComponentToPaint.ts b/superset-frontend/spec/helpers/waitForComponentToPaint.ts deleted file mode 100644 index 2e57a80b413..00000000000 --- a/superset-frontend/spec/helpers/waitForComponentToPaint.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { ReactWrapper } from 'enzyme'; -import { act } from 'react-dom/test-utils'; - -// taken from: https://github.com/enzymejs/enzyme/issues/2073 -// There is currently and issue with enzyme and react-16's hooks -// that results in a race condition between tests and react hook updates. -// This function ensures tests run after all react updates are done. -export default async function waitForComponentToPaint

( - wrapper: ReactWrapper

, - amount = 0, -) { - await act(async () => { - await new Promise(resolve => setTimeout(resolve, amount)); - wrapper.update(); - }); -} diff --git a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx index ef931af8735..837f892ff69 100644 --- a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx +++ b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx @@ -230,7 +230,6 @@ class TabbedSqlEditors extends PureComponent { } - // for tests - key prop isn't handled by enzyme well bcs it's a react keyword data-key={qe.id} > {}, -}; - -const setup = overrideProps => - mount( - - - , - { - wrappingComponent: ThemeProvider, - wrappingComponentProps: { theme: supersetTheme }, - }, - ); - -// all these tests need to be moved to dashboard/components/PropertiesModal/PropertiesModal.test.tsx -describe.skip('PropertiesModal', () => { - afterEach(() => { - jest.restoreAllMocks(); - jest.resetAllMocks(); - }); - - describe('onColorSchemeChange', () => { - it('sets up a default state', () => { - const wrapper = setup({ colorScheme: 'SUPERSET_DEFAULT' }); - expect( - wrapper.find('PropertiesModal').instance().state.values.colorScheme, - ).toEqual('SUPERSET_DEFAULT'); - }); - describe('with a valid color scheme as an arg', () => { - describe('without metadata', () => { - const wrapper = setup({ colorScheme: 'SUPERSET_DEFAULT' }); - const modalInstance = wrapper.find('PropertiesModal').instance(); - it('updates the color scheme in the metadata', () => { - const spy = jest.spyOn(modalInstance, 'onMetadataChange'); - modalInstance.onColorSchemeChange('SUPERSET_DEFAULT'); - expect(spy).toHaveBeenCalledWith( - '{"something": "foo", "color_scheme": "SUPERSET_DEFAULT", "label_colors": {}}', - ); - }); - }); - describe('with metadata', () => { - describe('with color_scheme in the metadata', () => { - it('will update the metadata', () => { - const wrapper = setup(); - const modalInstance = wrapper.find('PropertiesModal').instance(); - modalInstance.setState({ - values: { - json_metadata: '{"color_scheme": "foo"}', - }, - }); - const spy = jest.spyOn(modalInstance, 'onMetadataChange'); - modalInstance.onColorSchemeChange('SUPERSET_DEFAULT'); - expect(spy).toHaveBeenCalledWith( - '{"color_scheme": "SUPERSET_DEFAULT", "label_colors": {}}', - ); - }); - }); - it('without color_scheme in the metadata', () => { - const wrapper = setup(); - const modalInstance = wrapper.find('PropertiesModal').instance(); - modalInstance.setState({ - values: { - json_metadata: '{"timed_refresh_immune_slices": []}', - }, - }); - it('will update the metadata', () => { - const spy = jest.spyOn(modalInstance, 'onMetadataChange'); - modalInstance.onColorSchemeChange('SUPERSET_DEFAULT'); - expect(spy).toHaveBeenCalledWith( - '{"something": "foo", "color_scheme": "SUPERSET_DEFAULT", "label_colors": {}}', - ); - }); - }); - }); - }); - describe('with an invalid color scheme as an arg', () => { - const wrapper = setup(); - const modalInstance = wrapper.find('PropertiesModal').instance(); - it('will raise an error', () => { - const spy = jest.spyOn(Modal, 'error'); - expect(() => - modalInstance.onColorSchemeChange('THIS_WILL_NOT_WORK'), - ).toThrow('A valid color scheme is required'); - expect(spy).toHaveBeenCalled(); - }); - }); - describe('with an empty color scheme as an arg', () => { - const wrapper = setup(); - const modalInstance = wrapper.find('PropertiesModal').instance(); - it('will not raise an error', () => { - const spy = jest.spyOn(Modal, 'error'); - modalInstance.onColorSchemeChange(''); - expect(spy).not.toHaveBeenCalled(); - }); - }); - }); - describe('onOwnersChange', () => { - it('should update the state with the value passed', () => { - const wrapper = setup(); - const modalInstance = wrapper.find('PropertiesModal').instance(); - const spy = jest.spyOn(modalInstance, 'updateFormState'); - const newOwners = [{ value: 1, label: 'foo' }]; - modalInstance.onOwnersChange(newOwners); - expect(spy).toHaveBeenCalledWith('owners', newOwners); - }); - }); - describe('onMetadataChange', () => { - it('should update the state with the value passed', () => { - const wrapper = setup(); - const modalInstance = wrapper.find('PropertiesModal').instance(); - const spy = jest.spyOn(modalInstance, 'updateFormState'); - modalInstance.onMetadataChange('foo'); - expect(spy).toHaveBeenCalledWith('json_metadata', 'foo'); - }); - }); - describe('onChange', () => { - it('should update the state with the value passed', () => { - const wrapper = setup(); - const modalInstance = wrapper.find('PropertiesModal').instance(); - const spy = jest.spyOn(modalInstance, 'updateFormState'); - modalInstance.onChange({ target: { name: 'test', value: 'foo' } }); - expect(spy).toHaveBeenCalledWith('test', 'foo'); - }); - }); - describe('fetchDashboardDetails', () => { - it('should make an api call', () => { - const spy = jest.spyOn(SupersetClient, 'get'); - const wrapper = setup(); - const modalInstance = wrapper.find('PropertiesModal').instance(); - modalInstance.fetchDashboardDetails(); - expect(spy).toHaveBeenCalledWith({ - endpoint: '/api/v1/dashboard/1', - }); - }); - - it('should update state', async () => { - const wrapper = setup(); - const modalInstance = wrapper.find('PropertiesModal').instance(); - const fetchSpy = jest - .spyOn(SupersetClient, 'get') - .mockResolvedValue(dashboardResult); - modalInstance.fetchDashboardDetails(); - await fetchSpy(); - expect(modalInstance.state.values.colorScheme).toBeUndefined(); - expect(modalInstance.state.values.dashboard_title).toEqual('New Title'); - expect(modalInstance.state.values.slug).toEqual('/new'); - expect(modalInstance.state.values.json_metadata).toEqual( - '{"something": "foo"}', - ); - }); - - it('should call onOwnersChange', async () => { - const wrapper = setup(); - const modalInstance = wrapper.find('PropertiesModal').instance(); - const fetchSpy = jest.spyOn(SupersetClient, 'get').mockResolvedValue({ - json: { - result: { - dashboard_title: 'New Title', - slug: '/new', - json_metadata: '{"something":"foo"}', - owners: [{ id: 1, first_name: 'Al', last_name: 'Pacino' }], - roles: [], - }, - }, - }); - const onOwnersSpy = jest.spyOn(modalInstance, 'onOwnersChange'); - modalInstance.fetchDashboardDetails(); - await fetchSpy(); - expect(modalInstance.state.values.colorScheme).toBeUndefined(); - expect(onOwnersSpy).toHaveBeenCalledWith([ - { value: 1, label: 'Al Pacino' }, - ]); - }); - - it('should call onRolesChange', async () => { - const wrapper = setup(); - const modalInstance = wrapper.find('PropertiesModal').instance(); - const fetchSpy = jest.spyOn(SupersetClient, 'get').mockResolvedValue({ - json: { - result: { - dashboard_title: 'New Title', - slug: '/new', - json_metadata: '{"something":"foo"}', - owners: [], - roles: [{ id: 1, name: 'Alpha' }], - }, - }, - }); - const onRolwesSpy = jest.spyOn(modalInstance, 'onRolesChange'); - modalInstance.fetchDashboardDetails(); - await fetchSpy(); - expect(modalInstance.state.values.colorScheme).toBeUndefined(); - expect(onRolwesSpy).toHaveBeenCalledWith([{ value: 1, label: 'Alpha' }]); - }); - - describe('when colorScheme is undefined as a prop', () => { - describe('when color_scheme is defined in json_metadata', () => { - const wrapper = setup(); - const modalInstance = wrapper.find('PropertiesModal').instance(); - it('should use the color_scheme from json_metadata in the api response', async () => { - const fetchSpy = jest.spyOn(SupersetClient, 'get').mockResolvedValue({ - json: { - result: { - dashboard_title: 'New Title', - slug: '/new', - json_metadata: '{"color_scheme":"SUPERSET_DEFAULT"}', - owners: [], - roles: [], - }, - }, - }); - modalInstance.fetchDashboardDetails(); - - // this below triggers the callback of the api call - await fetchSpy(); - - expect(modalInstance.state.values.colorScheme).toEqual( - 'SUPERSET_DEFAULT', - ); - }); - describe('when color_scheme is not defined in json_metadata', () => { - const wrapper = setup(); - const modalInstance = wrapper.find('PropertiesModal').instance(); - it('should be undefined', async () => { - const fetchSpy = jest - .spyOn(SupersetClient, 'get') - .mockResolvedValue(dashboardResult); - modalInstance.fetchDashboardDetails(); - await fetchSpy(); - expect(modalInstance.state.values.colorScheme).toBeUndefined(); - }); - }); - }); - }); - describe('when colorScheme is defined as a prop', () => { - describe('when color_scheme is defined in json_metadata', () => { - const wrapper = setup({ colorScheme: 'SUPERSET_DEFAULT' }); - const modalInstance = wrapper.find('PropertiesModal').instance(); - it('should use the color_scheme from json_metadata in the api response', async () => { - const fetchSpy = jest.spyOn(SupersetClient, 'get').mockResolvedValue({ - json: { - result: { - dashboard_title: 'New Title', - slug: '/new', - json_metadata: '{"color_scheme":"SUPERSET_DEFAULT"}', - owners: [], - roles: [], - }, - }, - }); - modalInstance.fetchDashboardDetails(); - await fetchSpy(); - expect(modalInstance.state.values.colorScheme).toEqual( - 'SUPERSET_DEFAULT', - ); - }); - }); - describe('when color_scheme is not defined in json_metadata', () => { - const wrapper = setup({ colorScheme: 'SUPERSET_DEFAULT' }); - const modalInstance = wrapper.find('PropertiesModal').instance(); - it('should use the colorScheme from the prop', async () => { - const fetchSpy = jest - .spyOn(SupersetClient, 'get') - .mockResolvedValue(dashboardResult); - modalInstance.fetchDashboardDetails(); - await fetchSpy(); - expect(modalInstance.state.values.colorScheme).toBeUndefined(); - }); - }); - }); - }); -}); diff --git a/superset-frontend/src/features/annotationLayers/AnnotationLayerModal.test.jsx b/superset-frontend/src/features/annotationLayers/AnnotationLayerModal.test.jsx deleted file mode 100644 index f1ccfdacc29..00000000000 --- a/superset-frontend/src/features/annotationLayers/AnnotationLayerModal.test.jsx +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import thunk from 'redux-thunk'; -import configureStore from 'redux-mock-store'; -import { Provider } from 'react-redux'; -import fetchMock from 'fetch-mock'; -import Modal from 'src/components/Modal'; -import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; -import { styledMount as mount } from 'spec/helpers/theming'; -import AnnotationLayerModal from './AnnotationLayerModal'; - -const mockData = { id: 1, name: 'test', descr: 'test description' }; -const FETCH_ANNOTATION_LAYER_ENDPOINT = 'glob:*/api/v1/annotation_layer/*'; -const ANNOTATION_LAYER_PAYLOAD = { result: mockData }; - -fetchMock.get(FETCH_ANNOTATION_LAYER_ENDPOINT, ANNOTATION_LAYER_PAYLOAD); - -const mockStore = configureStore([thunk]); -const store = mockStore({}); - -const mockedProps = { - addDangerToast: () => {}, - onLayerAdd: jest.fn(() => []), - onHide: () => {}, - show: true, - layer: mockData, -}; - -async function mountAndWait(props = mockedProps) { - const mounted = mount( - - - , - ); - await waitForComponentToPaint(mounted); - - return mounted; -} - -describe('AnnotationLayerModal', () => { - let wrapper; - - beforeAll(async () => { - wrapper = await mountAndWait(); - }); - - it('renders', () => { - expect(wrapper.find(AnnotationLayerModal)).toBeTruthy(); - }); - - it('renders a Modal', () => { - expect(wrapper.find(Modal)).toBeTruthy(); - }); - - it('renders add header when no layer is included', async () => { - const addWrapper = await mountAndWait({}); - expect( - addWrapper.find('[data-test="annotation-layer-modal-title"]').text(), - ).toEqual('Add annotation layer'); - }); - - it('renders edit header when layer prop is included', () => { - expect( - wrapper.find('[data-test="annotation-layer-modal-title"]').text(), - ).toEqual('Edit annotation layer properties'); - }); - - it('renders input element for name', () => { - expect(wrapper.find('input[name="name"]')).toBeTruthy(); - }); - - it('renders textarea element for description', () => { - expect(wrapper.find('textarea[name="descr"]')).toBeTruthy(); - }); -}); diff --git a/superset-frontend/src/features/annotations/AnnotationModal.test.jsx b/superset-frontend/src/features/annotations/AnnotationModal.test.jsx deleted file mode 100644 index 53da727d60c..00000000000 --- a/superset-frontend/src/features/annotations/AnnotationModal.test.jsx +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import thunk from 'redux-thunk'; -import configureStore from 'redux-mock-store'; -import { Provider } from 'react-redux'; -import fetchMock from 'fetch-mock'; -import Modal from 'src/components/Modal'; -import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; -import { JsonEditor } from 'src/components/AsyncAceEditor'; -import { styledMount as mount } from 'spec/helpers/theming'; -import AnnotationModal from './AnnotationModal'; - -const mockData = { - id: 1, - short_descr: 'annotation 1', - start_dttm: '2019-07-01T10:25:00', - end_dttm: '2019-06-11T10:25:00', -}; - -const FETCH_ANNOTATION_ENDPOINT = - 'glob:*/api/v1/annotation_layer/*/annotation/*'; -const ANNOTATION_PAYLOAD = { result: mockData }; - -fetchMock.get(FETCH_ANNOTATION_ENDPOINT, ANNOTATION_PAYLOAD); - -const mockStore = configureStore([thunk]); -const store = mockStore({}); - -const mockedProps = { - addDangerToast: () => {}, - addSuccessToast: () => {}, - annotation: mockData, - onAnnotationAdd: jest.fn(() => []), - onHide: () => {}, - show: true, -}; - -async function mountAndWait(props = mockedProps) { - const mounted = mount( - - - , - ); - await waitForComponentToPaint(mounted); - return mounted; -} - -describe('AnnotationModal', () => { - let wrapper; - - beforeAll(async () => { - wrapper = await mountAndWait(); - }); - - it('renders', () => { - expect(wrapper.find(AnnotationModal)).toBeTruthy(); - }); - - it('renders a Modal', () => { - expect(wrapper.find(Modal)).toBeTruthy(); - }); - - it('renders add header when no annotation prop is included', async () => { - const addWrapper = await mountAndWait({}); - expect( - addWrapper.find('[data-test="annotation-modal-title"]').text(), - ).toEqual('Add annotation'); - }); - - it('renders edit header when annotation prop is included', () => { - expect(wrapper.find('[data-test="annotation-modal-title"]').text()).toEqual( - 'Edit annotation', - ); - }); - - it('renders input elements for annotation name', () => { - expect(wrapper.find('input[name="short_descr"]')).toBeTruthy(); - }); - - it('renders json editor for json metadata', () => { - expect(wrapper.find(JsonEditor)).toBeTruthy(); - }); -}); diff --git a/superset-frontend/src/features/cssTemplates/CssTemplateModal.test.jsx b/superset-frontend/src/features/cssTemplates/CssTemplateModal.test.jsx deleted file mode 100644 index d636835d006..00000000000 --- a/superset-frontend/src/features/cssTemplates/CssTemplateModal.test.jsx +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import thunk from 'redux-thunk'; -import { Provider } from 'react-redux'; -import configureStore from 'redux-mock-store'; -import fetchMock from 'fetch-mock'; -import Modal from 'src/components/Modal'; -import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; -import { CssEditor } from 'src/components/AsyncAceEditor'; -import { styledMount as mount } from 'spec/helpers/theming'; -import CssTemplateModal from './CssTemplateModal'; - -const mockData = { id: 1, template_name: 'test' }; -const FETCH_CSS_TEMPLATE_ENDPOINT = 'glob:*/api/v1/css_template/*'; -const CSS_TEMPLATE_PAYLOAD = { result: mockData }; - -fetchMock.get(FETCH_CSS_TEMPLATE_ENDPOINT, CSS_TEMPLATE_PAYLOAD); - -const mockStore = configureStore([thunk]); -const store = mockStore({}); - -const mockedProps = { - addDangerToast: () => {}, - onCssTemplateAdd: jest.fn(() => []), - onHide: () => {}, - show: true, - cssTemplate: mockData, -}; - -async function mountAndWait(props = mockedProps) { - const mounted = mount( - - - , - ); - await waitForComponentToPaint(mounted); - - return mounted; -} - -describe('CssTemplateModal', () => { - let wrapper; - - beforeAll(async () => { - wrapper = await mountAndWait(); - }); - - it('renders', () => { - expect(wrapper.find(CssTemplateModal)).toBeTruthy(); - }); - - it('renders a Modal', () => { - expect(wrapper.find(Modal)).toBeTruthy(); - }); - - it('renders add header when no css template is included', async () => { - const addWrapper = await mountAndWait({}); - expect( - addWrapper.find('[data-test="css-template-modal-title"]').text(), - ).toEqual('Add CSS template'); - }); - - it('renders edit header when css template prop is included', () => { - expect( - wrapper.find('[data-test="css-template-modal-title"]').text(), - ).toEqual('Edit CSS template properties'); - }); - - it('renders input elements for template name', () => { - expect(wrapper.find('input[name="template_name"]')).toBeTruthy(); - }); - - it('renders css editor for css', () => { - expect(wrapper.find(CssEditor)).toBeTruthy(); - }); -}); diff --git a/superset-frontend/src/features/home/SavedQueries.test.tsx b/superset-frontend/src/features/home/SavedQueries.test.tsx deleted file mode 100644 index 10530c45750..00000000000 --- a/superset-frontend/src/features/home/SavedQueries.test.tsx +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import thunk from 'redux-thunk'; -import { styledMount as mount } from 'spec/helpers/theming'; -import fetchMock from 'fetch-mock'; -import configureStore from 'redux-mock-store'; -import { act } from 'spec/helpers/testing-library'; -import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; -import SubMenu from './SubMenu'; -import SavedQueries from './SavedQueries'; - -// store needed for withToasts(DashboardTable) -const mockStore = configureStore([thunk]); -const store = mockStore({}); - -const queriesEndpoint = 'glob:*/api/v1/saved_query/?*'; -const savedQueriesInfo = 'glob:*/api/v1/saved_query/_info*'; - -const mockqueries = [...new Array(3)].map((_, i) => ({ - created_by: { - id: i, - first_name: `user`, - last_name: `${i}`, - }, - created_on: `${i}-2020`, - database: { - database_name: `db ${i}`, - id: i, - }, - changed_on_delta_humanized: '1 day ago', - db_id: i, - description: `SQL for ${i}`, - id: i, - label: `query ${i}`, - schema: 'public', - sql: `SELECT ${i} FROM table`, - sql_tables: [ - { - catalog: null, - schema: null, - table: `${i}`, - }, - ], -})); - -fetchMock.get(queriesEndpoint, { - result: mockqueries, -}); - -fetchMock.get(savedQueriesInfo, { - permissions: ['can_list', 'can_edit', 'can_delete'], -}); - -describe('SavedQueries', () => { - const savedQueryProps = { - user: { - userId: '1', - }, - mine: mockqueries, - }; - - const wrapper = mount(); - - const clickTab = (idx: number) => { - act(() => { - const handler = wrapper.find('[role="tab"] a').at(idx).prop('onClick'); - if (handler) { - handler({} as any); - } - }); - }; - - beforeAll(async () => { - await waitForComponentToPaint(wrapper); - }); - - it('is valid', () => { - expect(wrapper.find(SavedQueries)).toBeTruthy(); - }); - - it('fetches queries mine and renders listviewcard cards', async () => { - clickTab(0); - await waitForComponentToPaint(wrapper); - expect(fetchMock.calls(/saved_query\/\?q/)).toHaveLength(1); - expect(wrapper.find('ListViewCard')).toBeTruthy(); - }); - - it('renders a submenu with clickable tables and buttons', async () => { - expect(wrapper.find(SubMenu)).toBeTruthy(); - expect(wrapper.find('[role="tab"]')).toHaveLength(1); - expect(wrapper.find('button')).toHaveLength(5); - clickTab(0); - await waitForComponentToPaint(wrapper); - expect(fetchMock.calls(/saved_query\/\?q/)).toHaveLength(2); - }); -}); diff --git a/superset-frontend/src/features/queries/QueryPreviewModal.test.tsx b/superset-frontend/src/features/queries/QueryPreviewModal.test.tsx deleted file mode 100644 index 5a9df74f52e..00000000000 --- a/superset-frontend/src/features/queries/QueryPreviewModal.test.tsx +++ /dev/null @@ -1,182 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { MouseEvent } from 'react'; -import thunk from 'redux-thunk'; -import configureStore from 'redux-mock-store'; - -import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; -import { styledMount as mount } from 'spec/helpers/theming'; - -import { QueryObject } from 'src/views/CRUD/types'; -import SyntaxHighlighter from 'react-syntax-highlighter/dist/cjs/light'; -import { act } from 'spec/helpers/testing-library'; -import { QueryState } from '@superset-ui/core'; -import QueryPreviewModal from './QueryPreviewModal'; - -// store needed for withToasts -const mockStore = configureStore([thunk]); -const store = mockStore({}); - -const mockQueries: QueryObject[] = [...new Array(3)].map((_, i) => ({ - changed_on: new Date().toISOString(), - id: i, - slice_name: `cool chart ${i}`, - database: { - database_name: 'main db', - }, - schema: 'public', - sql: `SELECT ${i} FROM table`, - executed_sql: `SELECT ${i} FROM table LIMIT 1000`, - sql_tables: [ - { schema: 'foo', table: 'table' }, - { schema: 'bar', table: 'table_2' }, - ], - status: QueryState.Success, - tab_name: 'Main Tab', - user: { - first_name: 'cool', - last_name: 'dude', - id: 2, - username: 'cooldude', - }, - start_time: new Date().valueOf(), - end_time: new Date().valueOf(), - rows: 200, - tmp_table_name: '', - tracking_url: '', -})); - -describe('QueryPreviewModal', () => { - let currentIndex = 0; - let currentQuery = mockQueries[currentIndex]; - const mockedProps = { - onHide: jest.fn(), - openInSqlLab: jest.fn(), - queries: mockQueries, - query: currentQuery, - fetchData: jest.fn(() => { - currentIndex += 1; - currentQuery = mockQueries[currentIndex]; - }), - show: true, - }; - const wrapper = mount(); - - beforeAll(async () => { - await waitForComponentToPaint(wrapper); - }); - - it('renders a SyntaxHighlighter', () => { - expect(wrapper.find(SyntaxHighlighter)).toBeTruthy(); - }); - - it('toggles between user sql and executed sql', () => { - expect( - wrapper.find(SyntaxHighlighter).props().children, - ).toMatchInlineSnapshot(`"SELECT 0 FROM table"`); - - act(() => { - const props = wrapper - .find('[data-test="toggle-executed-sql"]') - .first() - .props(); - - if (typeof props.onClick === 'function') { - props.onClick({} as MouseEvent); - } - }); - - wrapper.update(); - - expect( - wrapper.find(SyntaxHighlighter).props().children, - ).toMatchInlineSnapshot(`"SELECT 0 FROM table LIMIT 1000"`); - }); - - describe('Previous button', () => { - it('disabled when query is the first in list', () => { - expect( - wrapper.find('[data-test="previous-query"]').first().props().disabled, - ).toBe(true); - }); - - it('falls fetchData with previous index', () => { - const mockedProps2 = { - ...mockedProps, - query: mockQueries[1], - }; - const wrapper2 = mount( - , - ); - act(() => { - const props = wrapper2 - .find('[data-test="previous-query"]') - .first() - .props(); - if (typeof props.onClick === 'function') { - props.onClick({} as MouseEvent); - } - }); - - expect(mockedProps2.fetchData).toHaveBeenCalledWith(0); - }); - }); - - describe('Next button', () => { - it('calls fetchData with next index', () => { - act(() => { - const props = wrapper.find('[data-test="next-query"]').first().props(); - if (typeof props.onClick === 'function') { - props.onClick({} as MouseEvent); - } - }); - - expect(mockedProps.fetchData).toHaveBeenCalledWith(1); - }); - - it('disabled when query is last in list', () => { - const mockedProps2 = { - ...mockedProps, - query: mockQueries[2], - }; - const wrapper2 = mount( - , - ); - - expect( - wrapper2.find('[data-test="next-query"]').first().props().disabled, - ).toBe(true); - }); - }); - - describe('Open in SQL Lab button', () => { - it('calls openInSqlLab prop', () => { - const props = wrapper - .find('[data-test="open-in-sql-lab"]') - .first() - .props(); - - if (typeof props.onClick === 'function') { - props.onClick({} as MouseEvent); - } - - expect(mockedProps.openInSqlLab).toHaveBeenCalled(); - }); - }); -}); diff --git a/superset-frontend/src/features/queries/SavedQueryPreviewModal.test.jsx b/superset-frontend/src/features/queries/SavedQueryPreviewModal.test.jsx deleted file mode 100644 index 3adac7f9f81..00000000000 --- a/superset-frontend/src/features/queries/SavedQueryPreviewModal.test.jsx +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import thunk from 'redux-thunk'; -import configureStore from 'redux-mock-store'; -import fetchMock from 'fetch-mock'; -import { styledMount as mount } from 'spec/helpers/theming'; -import Button from 'src/components/Button'; -import Modal from 'src/components/Modal'; -import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; -import { act } from 'spec/helpers/testing-library'; -import SavedQueryPreviewModal from './SavedQueryPreviewModal'; - -// store needed for withToasts(DatabaseList) -const mockStore = configureStore([thunk]); -const store = mockStore({}); - -const mockqueries = [...new Array(3)].map((_, i) => ({ - created_by: { - id: i, - first_name: `user`, - last_name: `${i}`, - }, - created_on: `${i}-2020`, - database: { - database_name: `db ${i}`, - id: i, - }, - changed_on_delta_humanized: '1 day ago', - db_id: i, - description: `SQL for ${i}`, - id: i, - label: `query ${i}`, - schema: 'public', - sql: `SELECT ${i} FROM table`, - sql_tables: [ - { - catalog: null, - schema: null, - table: `${i}`, - }, - ], -})); - -const mockedProps = { - fetchData: jest.fn(() => {}), - openInSqlLab: jest.fn(() => {}), - onHide: () => {}, - queries: mockqueries, - savedQuery: mockqueries[1], - show: true, -}; - -const FETCH_SAVED_QUERY_ENDPOINT = 'glob:*/api/v1/saved_query/*'; -const SAVED_QUERY_PAYLOAD = { result: mockqueries[1] }; - -fetchMock.get(FETCH_SAVED_QUERY_ENDPOINT, SAVED_QUERY_PAYLOAD); - -async function mountAndWait(props = mockedProps) { - const mounted = mount(); - await waitForComponentToPaint(mounted); - - return mounted; -} - -describe('SavedQueryPreviewModal', () => { - let wrapper; - - beforeAll(async () => { - wrapper = await mountAndWait(); - }); - - it('renders', () => { - expect(wrapper.find(SavedQueryPreviewModal)).toBeTruthy(); - }); - - it('renders a Modal', () => { - expect(wrapper.find(Modal)).toBeTruthy(); - }); - - it('renders sql from saved query', () => { - expect(wrapper.find('pre').text()).toEqual('SELECT 1 FROM table'); - }); - - it('renders buttons with correct text', () => { - expect(wrapper.find(Button).contains('Previous')).toBe(true); - expect(wrapper.find(Button).contains('Next')).toBe(true); - expect(wrapper.find(Button).contains('Open in SQL Lab')).toBe(true); - }); - - it('handle next save query', () => { - const button = wrapper.find('button[data-test="next-saved-query"]'); - expect(button.props().disabled).toBe(false); - act(() => { - button.props().onClick(false); - }); - expect(mockedProps.fetchData).toHaveBeenCalled(); - expect(mockedProps.fetchData.mock.calls[0][0]).toEqual(2); - }); - - it('handle previous save query', () => { - const button = wrapper - .find('[data-test="previous-saved-query"]') - .find(Button); - expect(button.props().disabled).toBe(false); - act(() => { - button.props().onClick(true); - }); - wrapper.update(); - expect(mockedProps.fetchData).toHaveBeenCalled(); - expect(mockedProps.fetchData.mock.calls[0][0]).toEqual(2); - }); - - it('handle open in sql lab', async () => { - act(() => { - wrapper.find('[data-test="open-in-sql-lab"]').first().props().onClick({}); - }); - expect(mockedProps.openInSqlLab).toHaveBeenCalled(); - expect(mockedProps.openInSqlLab.mock.calls[0][0]).toEqual(1); - }); -}); diff --git a/superset-frontend/src/pages/AnnotationList/AnnotationList.test.jsx b/superset-frontend/src/pages/AnnotationList/AnnotationList.test.jsx deleted file mode 100644 index 487f4484bfa..00000000000 --- a/superset-frontend/src/pages/AnnotationList/AnnotationList.test.jsx +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import thunk from 'redux-thunk'; -import configureStore from 'redux-mock-store'; -import fetchMock from 'fetch-mock'; -import { Provider } from 'react-redux'; -import { styledMount as mount } from 'spec/helpers/theming'; - -import AnnotationList from 'src/pages/AnnotationList'; -import DeleteModal from 'src/components/DeleteModal'; -import IndeterminateCheckbox from 'src/components/IndeterminateCheckbox'; -import ListView from 'src/components/ListView'; -import SubMenu from 'src/features/home/SubMenu'; - -import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; -import { act } from 'spec/helpers/testing-library'; - -// store needed for withToasts(AnnotationList) -const mockStore = configureStore([thunk]); -const store = mockStore({}); - -const annotationsEndpoint = 'glob:*/api/v1/annotation_layer/*/annotation*'; -const annotationLayerEndpoint = 'glob:*/api/v1/annotation_layer/*'; - -fetchMock.delete(annotationsEndpoint, {}); - -const mockannotations = [...new Array(3)].map((_, i) => ({ - changed_on_delta_humanized: `${i} day(s) ago`, - created_by: { - first_name: `user`, - id: i, - }, - changed_by: { - first_name: `user`, - id: i, - }, - end_dttm: new Date().toISOString, - id: i, - long_descr: `annotation ${i} description`, - short_descr: `annotation ${i} label`, - start_dttm: new Date().toISOString, -})); - -fetchMock.get(annotationsEndpoint, { - ids: [2, 0, 1], - result: mockannotations, - count: 3, -}); - -fetchMock.get(annotationLayerEndpoint, { - id: 1, - result: { descr: 'annotations test 0', name: 'Test 0' }, -}); - -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), // use actual for all non-hook parts - useParams: () => ({ annotationLayerId: '1' }), -})); - -async function mountAndWait(props) { - const mounted = mount( - - - , - ); - await waitForComponentToPaint(mounted); - - return mounted; -} - -describe('AnnotationList', () => { - let wrapper; - - beforeAll(async () => { - wrapper = await mountAndWait(); - }); - - it('renders', () => { - expect(wrapper.find(AnnotationList)).toBeTruthy(); - }); - - it('renders a SubMenu', () => { - expect(wrapper.find(SubMenu)).toBeTruthy(); - }); - - it('renders a ListView', () => { - expect(wrapper.find(ListView)).toBeTruthy(); - }); - - it('fetches annotation layer', () => { - const callsQ = fetchMock.calls(/annotation_layer\/1/); - expect(callsQ).toHaveLength(2); - expect(callsQ[1][0]).toMatchInlineSnapshot( - `"http://localhost/api/v1/annotation_layer/1"`, - ); - }); - - it('fetches annotations', () => { - const callsQ = fetchMock.calls(/annotation_layer\/1\/annotation/); - expect(callsQ).toHaveLength(1); - expect(callsQ[0][0]).toMatchInlineSnapshot( - `"http://localhost/api/v1/annotation_layer/1/annotation/?q=(order_column:short_descr,order_direction:desc,page:0,page_size:25)"`, - ); - }); - - it('renders a DeleteModal', () => { - expect(wrapper.find(DeleteModal)).toBeTruthy(); - }); - - it('deletes', async () => { - act(() => { - wrapper.find('[data-test="delete-action"]').first().props().onClick(); - }); - await waitForComponentToPaint(wrapper); - - expect( - wrapper.find(DeleteModal).first().props().description, - ).toMatchInlineSnapshot( - `"Are you sure you want to delete annotation 0 label?"`, - ); - - act(() => { - wrapper - .find('#delete') - .first() - .props() - .onChange({ target: { value: 'DELETE' } }); - }); - await waitForComponentToPaint(wrapper); - act(() => { - wrapper.find('button').last().props().onClick(); - }); - await waitForComponentToPaint(wrapper); - }); - - it('shows/hides bulk actions when bulk actions is clicked', async () => { - const button = wrapper.find('[data-test="annotation-bulk-select"]').first(); - act(() => { - button.props().onClick(); - }); - await waitForComponentToPaint(wrapper); - expect(wrapper.find(IndeterminateCheckbox)).toHaveLength( - mockannotations.length + 1, // 1 for each row and 1 for select all - ); - }); -}); diff --git a/superset-frontend/src/pages/DatabaseList/DatabaseList.test.jsx b/superset-frontend/src/pages/DatabaseList/DatabaseList.test.jsx deleted file mode 100644 index e81020e3986..00000000000 --- a/superset-frontend/src/pages/DatabaseList/DatabaseList.test.jsx +++ /dev/null @@ -1,248 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License.src/pages/DatabaseList/DatabaseList.test.jsx - */ -import thunk from 'redux-thunk'; -import * as reactRedux from 'react-redux'; -import configureStore from 'redux-mock-store'; -import fetchMock from 'fetch-mock'; -import { Provider } from 'react-redux'; -import { styledMount as mount } from 'spec/helpers/theming'; - -import DatabaseList from 'src/pages/DatabaseList'; -import DatabaseModal from 'src/features/databases/DatabaseModal'; -import DeleteModal from 'src/components/DeleteModal'; -import SubMenu from 'src/features/home/SubMenu'; -import ListView from 'src/components/ListView'; -import Filters from 'src/components/ListView/Filters'; -import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; -import { act } from 'spec/helpers/testing-library'; - -// store needed for withToasts(DatabaseList) - -const mockStore = configureStore([thunk]); -const store = mockStore({}); - -const databasesInfoEndpoint = 'glob:*/api/v1/database/_info*'; -const databasesEndpoint = 'glob:*/api/v1/database/?*'; -const databaseEndpoint = 'glob:*/api/v1/database/*'; -const databaseRelatedEndpoint = 'glob:*/api/v1/database/*/related_objects*'; - -const mockdatabases = [...new Array(3)].map((_, i) => ({ - changed_by: { - first_name: `user`, - last_name: `${i}`, - }, - database_name: `db ${i}`, - backend: 'postgresql', - allow_run_async: true, - allow_dml: false, - allow_file_upload: true, - expose_in_sqllab: false, - changed_on_delta_humanized: `${i} day(s) ago`, - changed_on: new Date().toISOString, - id: i, -})); - -jest.mock('react-redux', () => ({ - ...jest.requireActual('react-redux'), - useSelector: jest.fn(), -})); - -fetchMock.get(databasesInfoEndpoint, { - permissions: ['can_write'], -}); -fetchMock.get(databasesEndpoint, { - result: mockdatabases, - database_count: 3, -}); - -fetchMock.delete(databaseEndpoint, {}); -fetchMock.get(databaseRelatedEndpoint, { - charts: { - count: 0, - result: [], - }, - dashboards: { - count: 0, - result: [], - }, - sqllab_tab_states: { - count: 0, - result: [], - }, -}); - -fetchMock.get( - 'glob:*api/v1/database/?q=(filters:!((col:allow_file_upload,opr:upload_is_enabled,value:!t)))', - {}, -); - -const useSelectorMock = jest.spyOn(reactRedux, 'useSelector'); -const userSelectorMock = jest.spyOn(reactRedux, 'useSelector'); - -describe('Admin DatabaseList', () => { - useSelectorMock.mockReturnValue({ - CSV_EXTENSIONS: ['csv'], - EXCEL_EXTENSIONS: ['xls', 'xlsx'], - COLUMNAR_EXTENSIONS: ['parquet', 'zip'], - ALLOWED_EXTENSIONS: ['parquet', 'zip', 'xls', 'xlsx', 'csv'], - }); - userSelectorMock.mockReturnValue({ - createdOn: '2021-04-27T18:12:38.952304', - email: 'admin', - firstName: 'admin', - isActive: true, - lastName: 'admin', - permissions: {}, - roles: { - Admin: [ - ['can_sqllab', 'Superset'], - ['can_write', 'Dashboard'], - ['can_write', 'Chart'], - ], - }, - userId: 1, - username: 'admin', - }); - - const wrapper = mount( - - - , - ); - - beforeAll(async () => { - await waitForComponentToPaint(wrapper); - }); - - test('renders', () => { - expect(wrapper.find(DatabaseList)).toBeTruthy(); - }); - - test('renders a SubMenu', () => { - expect(wrapper.find(SubMenu)).toBeTruthy(); - }); - - test('renders a SubMenu with no tabs', () => { - expect(wrapper.find(SubMenu).props().tabs).toBeUndefined(); - }); - - test('renders a DatabaseModal', () => { - expect(wrapper.find(DatabaseModal)).toBeTruthy(); - }); - - test('renders a ListView', () => { - expect(wrapper.find(ListView)).toBeTruthy(); - }); - - test('fetches Databases', () => { - const callsD = fetchMock.calls(/database\/\?q/); - expect(callsD).toHaveLength(2); - expect(callsD[0][0]).toMatchInlineSnapshot( - `"http://localhost/api/v1/database/?q=(order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:25)"`, - ); - }); - - test('deletes', async () => { - act(() => { - wrapper.find('[data-test="database-delete"]').first().props().onClick(); - }); - await waitForComponentToPaint(wrapper); - - expect(wrapper.find(DeleteModal).props().description).toMatchSnapshot(); - - act(() => { - wrapper - .find('#delete') - .first() - .props() - .onChange({ target: { value: 'DELETE' } }); - }); - await waitForComponentToPaint(wrapper); - act(() => { - wrapper - .find({ 'data-test': 'modal-confirm-button' }) - .last() - .props() - .onClick(); - }); - - await waitForComponentToPaint(wrapper); - - expect(fetchMock.calls(/database\/0\/related_objects/, 'GET')).toHaveLength( - 1, - ); - expect(fetchMock.calls(/database\/0/, 'DELETE')).toHaveLength(1); - }); - - test('filters', async () => { - const filtersWrapper = wrapper.find(Filters); - act(() => { - filtersWrapper - .find('[name="expose_in_sqllab"]') - .first() - .props() - .onSelect({ label: 'Yes', value: true }); - - filtersWrapper - .find('[name="allow_run_async"]') - .first() - .props() - .onSelect({ label: 'Yes', value: false }); - - filtersWrapper - .find('[name="database_name"]') - .first() - .props() - .onSubmit('fooo'); - }); - await waitForComponentToPaint(wrapper); - - expect(fetchMock.lastCall()[0]).toMatchInlineSnapshot( - `"http://localhost/api/v1/database/?q=(filters:!((col:database_name,opr:ct,value:fooo),(col:expose_in_sqllab,opr:eq,value:!t),(col:allow_run_async,opr:eq,value:!f)),order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:25)"`, - ); - }); - - test('should not render dropdown menu button if user is not admin', async () => { - userSelectorMock.mockReturnValue({ - createdOn: '2021-05-27T18:12:38.952304', - email: 'alpha@gmail.com', - firstName: 'alpha', - isActive: true, - lastName: 'alpha', - permissions: {}, - roles: { - Alpha: [ - ['can_sqllab', 'Superset'], - ['can_write', 'Dashboard'], - ['can_write', 'Chart'], - ], - }, - userId: 2, - username: 'alpha', - }); - const newWrapper = mount( - - - , - ); - await waitForComponentToPaint(newWrapper); - - expect(newWrapper.find('.dropdown-menu-links').length).toBe(0); - }); -}); diff --git a/superset-frontend/src/pages/DatabaseList/__snapshots__/DatabaseList.test.jsx.snap b/superset-frontend/src/pages/DatabaseList/__snapshots__/DatabaseList.test.jsx.snap deleted file mode 100644 index c4f6ebc9659..00000000000 --- a/superset-frontend/src/pages/DatabaseList/__snapshots__/DatabaseList.test.jsx.snap +++ /dev/null @@ -1,15 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Admin DatabaseList deletes 1`] = ` - -

- The database - - - db 0 - - - is linked to 0 charts that appear on 0 dashboards and users have 0 SQL Lab tabs using this database open. Are you sure you want to continue? Deleting the database will break those objects. -

- -`; diff --git a/superset-frontend/src/pages/DatasetList/DatasetList.test.tsx b/superset-frontend/src/pages/DatasetList/DatasetList.test.tsx deleted file mode 100644 index 0253f6074b6..00000000000 --- a/superset-frontend/src/pages/DatasetList/DatasetList.test.tsx +++ /dev/null @@ -1,331 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import thunk from 'redux-thunk'; -import configureStore from 'redux-mock-store'; -import fetchMock from 'fetch-mock'; -import { Provider } from 'react-redux'; -import { styledMount as mount } from 'spec/helpers/theming'; -import { - act, - cleanup, - render, - screen, - userEvent, -} from 'spec/helpers/testing-library'; -import { isFeatureEnabled } from '@superset-ui/core'; -import { QueryParamProvider } from 'use-query-params'; - -import DatasetList from 'src/pages/DatasetList'; -import ListView from 'src/components/ListView'; -import Button from 'src/components/Button'; -import IndeterminateCheckbox from 'src/components/IndeterminateCheckbox'; -import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; -import SubMenu from 'src/features/home/SubMenu'; -import * as reactRedux from 'react-redux'; - -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), - isFeatureEnabled: jest.fn(), -})); - -const mockedIsFeatureEnabled = isFeatureEnabled as jest.Mock; - -// store needed for withToasts(DatasetList) -const mockStore = configureStore([thunk]); -const store = mockStore({}); - -const datasetsInfoEndpoint = 'glob:*/api/v1/dataset/_info*'; -const datasetsOwnersEndpoint = 'glob:*/api/v1/dataset/related/owners*'; -const datasetsSchemaEndpoint = 'glob:*/api/v1/dataset/distinct/schema*'; -const datasetsDuplicateEndpoint = 'glob:*/api/v1/dataset/duplicate*'; -const databaseEndpoint = 'glob:*/api/v1/dataset/related/database*'; -const datasetsEndpoint = 'glob:*/api/v1/dataset/?*'; - -const useSelectorMock = jest.spyOn(reactRedux, 'useSelector'); - -const mockdatasets = [...new Array(3)].map((_, i) => ({ - changed_by_name: 'user', - kind: i === 0 ? 'virtual' : 'physical', // ensure there is 1 virtual - changed_by: 'user', - changed_on: new Date().toISOString(), - database_name: `db ${i}`, - explore_url: `https://www.google.com?${i}`, - id: i, - schema: `schema ${i}`, - table_name: `coolest table ${i}`, - owners: [{ username: 'admin', userId: 1 }], -})); - -const mockUser = { - userId: 1, -}; - -fetchMock.get(datasetsInfoEndpoint, { - permissions: ['can_read', 'can_write', 'can_duplicate'], -}); -fetchMock.get(datasetsOwnersEndpoint, { - result: [], -}); -fetchMock.get(datasetsSchemaEndpoint, { - result: [], -}); -fetchMock.post(datasetsDuplicateEndpoint, { - result: [], -}); -fetchMock.get(datasetsEndpoint, { - result: mockdatasets, - dataset_count: 3, -}); -fetchMock.get(databaseEndpoint, { - result: [], -}); - -async function mountAndWait(props: {}) { - const mounted = mount( - - - , - ); - await waitForComponentToPaint(mounted); - - return mounted; -} - -describe('DatasetList', () => { - const mockedProps = {}; - let wrapper: any; - - beforeAll(async () => { - wrapper = await mountAndWait(mockedProps); - }); - - it('renders', () => { - expect(wrapper.find(DatasetList)).toBeTruthy(); - }); - - it('renders a ListView', () => { - expect(wrapper.find(ListView)).toBeTruthy(); - }); - - it('fetches info', () => { - const callsI = fetchMock.calls(/dataset\/_info/); - expect(callsI).toHaveLength(1); - }); - - it('fetches data', () => { - const callsD = fetchMock.calls(/dataset\/\?q/); - expect(callsD).toHaveLength(1); - expect(callsD[0][0]).toMatchInlineSnapshot( - `"http://localhost/api/v1/dataset/?q=(order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:25)"`, - ); - }); - - it('does not fetch owner filter values on mount', async () => { - await waitForComponentToPaint(wrapper); - expect(fetchMock.calls(/dataset\/related\/owners/)).toHaveLength(0); - }); - - it('does not fetch schema filter values on mount', async () => { - await waitForComponentToPaint(wrapper); - expect(fetchMock.calls(/dataset\/distinct\/schema/)).toHaveLength(0); - }); - - it('shows/hides bulk actions when bulk actions is clicked', async () => { - await waitForComponentToPaint(wrapper); - const button = wrapper.find(Button).at(0); - act(() => { - button.props().onClick(); - }); - await waitForComponentToPaint(wrapper); - expect(wrapper.find(IndeterminateCheckbox)).toHaveLength( - mockdatasets.length + 1, // 1 for each row and 1 for select all - ); - }); - - it('renders different bulk selected copy depending on type of row selected', async () => { - // None selected - const checkedEvent = { target: { checked: true } }; - const uncheckedEvent = { target: { checked: false } }; - expect( - wrapper.find('[data-test="bulk-select-copy"]').text(), - ).toMatchInlineSnapshot(`"0 Selected"`); - - // Virtual Selected - act(() => { - wrapper.find(IndeterminateCheckbox).at(1).props().onChange(checkedEvent); - }); - await waitForComponentToPaint(wrapper); - expect( - wrapper.find('[data-test="bulk-select-copy"]').text(), - ).toMatchInlineSnapshot(`"1 Selected (Virtual)"`); - - // Physical Selected - act(() => { - wrapper - .find(IndeterminateCheckbox) - .at(1) - .props() - .onChange(uncheckedEvent); - wrapper.find(IndeterminateCheckbox).at(2).props().onChange(checkedEvent); - }); - await waitForComponentToPaint(wrapper); - expect( - wrapper.find('[data-test="bulk-select-copy"]').text(), - ).toMatchInlineSnapshot(`"1 Selected (Physical)"`); - - // All Selected - act(() => { - wrapper.find(IndeterminateCheckbox).at(0).props().onChange(checkedEvent); - }); - await waitForComponentToPaint(wrapper); - expect( - wrapper.find('[data-test="bulk-select-copy"]').text(), - ).toMatchInlineSnapshot(`"3 Selected (2 Physical, 1 Virtual)"`); - }); - - it('shows duplicate modal when duplicate action is clicked', async () => { - await waitForComponentToPaint(wrapper); - expect( - wrapper.find('[data-test="duplicate-modal-input"]').exists(), - ).toBeFalsy(); - act(() => { - wrapper - .find('#duplicate-action-tooltip') - .at(0) - .find('.action-button') - .props() - .onClick(); - }); - await waitForComponentToPaint(wrapper); - expect( - wrapper.find('[data-test="duplicate-modal-input"]').exists(), - ).toBeTruthy(); - }); - - it('calls the duplicate endpoint', async () => { - await waitForComponentToPaint(wrapper); - await act(async () => { - wrapper - .find('#duplicate-action-tooltip') - .at(0) - .find('.action-button') - .props() - .onClick(); - await waitForComponentToPaint(wrapper); - wrapper - .find('[data-test="duplicate-modal-input"]') - .at(0) - .props() - .onPressEnter(); - }); - expect(fetchMock.calls(/dataset\/duplicate/)).toHaveLength(1); - }); - - it('renders a SubMenu', () => { - expect(wrapper.find(SubMenu)).toBeTruthy(); - }); - - it('renders a SubMenu with no tabs', () => { - expect(wrapper.find(SubMenu).props().tabs).toBeUndefined(); - }); -}); - -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useLocation: () => ({}), - useHistory: () => ({}), -})); - -describe('RTL', () => { - async function renderAndWait() { - const mounted = act(async () => { - const mockedProps = {}; - render( - - - , - { useRedux: true, useRouter: true }, - ); - }); - - return mounted; - } - - beforeEach(async () => { - mockedIsFeatureEnabled.mockReturnValue(true); - await renderAndWait(); - }); - - afterEach(() => { - cleanup(); - mockedIsFeatureEnabled.mockRestore(); - }); - - it('renders an "Import Dataset" tooltip under import button', async () => { - const importButton = await screen.findByTestId('import-button'); - userEvent.hover(importButton); - - await screen.findByRole('tooltip'); - const importTooltip = screen.getByRole('tooltip', { - name: 'Import datasets', - }); - - expect(importTooltip).toBeInTheDocument(); - }); -}); - -describe('Prevent unsafe URLs', () => { - const columnCount = 8; - const exploreUrlIndex = 1; - const getTdIndex = (rowNumber: number): number => - rowNumber * columnCount + exploreUrlIndex; - - const mockedProps = {}; - let wrapper: any; - - it('Check prevent unsafe is on renders relative links', async () => { - useSelectorMock.mockReturnValue(true); - wrapper = await mountAndWait(mockedProps); - const tdElements = wrapper.find(ListView).find('td'); - expect(tdElements.at(getTdIndex(0)).find('a').prop('href')).toBe( - '/https://www.google.com?0', - ); - expect(tdElements.at(getTdIndex(1)).find('a').prop('href')).toBe( - '/https://www.google.com?1', - ); - expect(tdElements.at(getTdIndex(2)).find('a').prop('href')).toBe( - '/https://www.google.com?2', - ); - }); - - it('Check prevent unsafe is off renders absolute links', async () => { - useSelectorMock.mockReturnValue(false); - wrapper = await mountAndWait(mockedProps); - const tdElements = wrapper.find(ListView).find('td'); - expect(tdElements.at(getTdIndex(0)).find('a').prop('href')).toBe( - 'https://www.google.com?0', - ); - expect(tdElements.at(getTdIndex(1)).find('a').prop('href')).toBe( - 'https://www.google.com?1', - ); - expect(tdElements.at(getTdIndex(2)).find('a').prop('href')).toBe( - 'https://www.google.com?2', - ); - }); -}); diff --git a/superset-frontend/src/pages/QueryHistoryList/QueryHistoryList.test.tsx b/superset-frontend/src/pages/QueryHistoryList/QueryHistoryList.test.tsx deleted file mode 100644 index 0afe5ad1ced..00000000000 --- a/superset-frontend/src/pages/QueryHistoryList/QueryHistoryList.test.tsx +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { MouseEvent } from 'react'; -import thunk from 'redux-thunk'; -import configureStore from 'redux-mock-store'; -import { Provider } from 'react-redux'; -import fetchMock from 'fetch-mock'; -import { act } from 'spec/helpers/testing-library'; - -import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; -import { styledMount as mount } from 'spec/helpers/theming'; - -import QueryList from 'src/pages/QueryHistoryList'; -import QueryPreviewModal from 'src/features/queries/QueryPreviewModal'; -import { QueryObject } from 'src/views/CRUD/types'; -import ListView from 'src/components/ListView'; -import Filters from 'src/components/ListView/Filters'; -import SyntaxHighlighter from 'react-syntax-highlighter/dist/cjs/light'; -import SubMenu from 'src/features/home/SubMenu'; -import { QueryState } from '@superset-ui/core'; - -// store needed for withToasts -const mockStore = configureStore([thunk]); -const store = mockStore({}); - -const queriesEndpoint = 'glob:*/api/v1/query/?*'; - -const mockQueries: QueryObject[] = [...new Array(3)].map((_, i) => ({ - changed_on: new Date().toISOString(), - id: i, - slice_name: `cool chart ${i}`, - database: { - database_name: 'main db', - }, - schema: 'public', - sql: `SELECT ${i} FROM table`, - executed_sql: `SELECT ${i} FROM table`, - sql_tables: [ - { schema: 'foo', table: 'table' }, - { schema: 'bar', table: 'table_2' }, - ], - status: QueryState.Success, - tab_name: 'Main Tab', - user: { - first_name: 'cool', - last_name: 'dude', - id: 2, - username: 'cooldude', - }, - start_time: new Date().valueOf(), - end_time: new Date().valueOf(), - rows: 200, - tmp_table_name: '', - tracking_url: '', -})); - -fetchMock.get(queriesEndpoint, { - result: mockQueries, - chart_count: 3, -}); - -fetchMock.get('glob:*/api/v1/query/related/user*', { - result: [], - count: 0, -}); -fetchMock.get('glob:*/api/v1/query/related/database*', { - result: [], - count: 0, -}); -fetchMock.get('glob:*/api/v1/query/disting/status*', { - result: [], - count: 0, -}); - -describe('QueryList', () => { - const mockedProps = {}; - const wrapper = mount( - - - , - { - context: { store }, - }, - ); - - beforeAll(async () => { - await waitForComponentToPaint(wrapper); - }); - - it('renders', () => { - expect(wrapper.find(QueryList)).toBeTruthy(); - }); - - it('renders a ListView', () => { - expect(wrapper.find(ListView)).toBeTruthy(); - }); - - it('fetches data', () => { - wrapper.update(); - const callsD = fetchMock.calls(/query\/\?q/); - expect(callsD).toHaveLength(1); - expect(callsD[0][0]).toMatchInlineSnapshot( - `"http://localhost/api/v1/query/?q=(order_column:start_time,order_direction:desc,page:0,page_size:25)"`, - ); - }); - - it('renders a SyntaxHighlight', () => { - expect(wrapper.find(SyntaxHighlighter)).toBeTruthy(); - }); - - it('opens a query preview', () => { - act(() => { - const props = wrapper - .find('[data-test="open-sql-preview-0"]') - .first() - .props(); - if (props.onClick) props.onClick({} as MouseEvent); - }); - wrapper.update(); - - expect(wrapper.find(QueryPreviewModal)).toBeTruthy(); - }); - - it('searches', async () => { - const filtersWrapper = wrapper.find(Filters); - act(() => { - const props = filtersWrapper.find('[name="sql"]').first().props(); - // @ts-ignore - if (props.onSubmit) props.onSubmit('fooo'); - }); - await waitForComponentToPaint(wrapper); - expect((fetchMock.lastCall() ?? [])[0]).toMatchInlineSnapshot( - `"http://localhost/api/v1/query/?q=(filters:!((col:sql,opr:ct,value:fooo)),order_column:start_time,order_direction:desc,page:0,page_size:25)"`, - ); - }); - - it('renders a SubMenu', () => { - expect(wrapper.find(SubMenu)).toBeTruthy(); - }); - - it('renders a SubMenu with Saved queries and Query History links', () => { - expect(wrapper.find(SubMenu).props().tabs).toEqual( - expect.arrayContaining([ - expect.objectContaining({ label: 'Saved queries' }), - expect.objectContaining({ label: 'Query history' }), - ]), - ); - }); - - it('renders a SubMenu without Databases and Datasets links', () => { - expect(wrapper.find(SubMenu).props().tabs).not.toEqual( - expect.arrayContaining([ - expect.objectContaining({ label: 'Databases' }), - expect.objectContaining({ label: 'Datasets' }), - ]), - ); - }); -});