diff --git a/.eslintrc.js b/.eslintrc.js index a085aef..594cd68 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,7 +1,8 @@ module.exports = { env: { browser: true, - es6: true + es6: true, + jest: true }, extends: [ 'plugin:react/recommended', diff --git a/package-lock.json b/package-lock.json index 46bca4f..4b54f2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2003,45 +2003,68 @@ "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==" }, "@typescript-eslint/eslint-plugin": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz", - "integrity": "sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.7.1.tgz", + "integrity": "sha512-3DB9JDYkMrc8Au00rGFiJLK2Ja9CoMP6Ut0sHsXp3ZtSugjNxvSSHTnKLfo4o+QmjYBJqEznDqsG1zj4F2xnsg==", + "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "2.34.0", + "@typescript-eslint/experimental-utils": "3.7.1", + "debug": "^4.1.1", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.0.0", + "semver": "^7.3.2", "tsutils": "^3.17.1" + }, + "dependencies": { + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } } }, "@typescript-eslint/experimental-utils": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz", - "integrity": "sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.7.1.tgz", + "integrity": "sha512-TqE97pv7HrqWcGJbLbZt1v59tcqsSVpWTOf1AqrWK7n8nok2sGgVtYRuGXeNeLw3wXlLEbY1MKP3saB2HsO/Ng==", + "dev": true, "requires": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.34.0", + "@typescript-eslint/types": "3.7.1", + "@typescript-eslint/typescript-estree": "3.7.1", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" } }, "@typescript-eslint/parser": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.34.0.tgz", - "integrity": "sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.7.1.tgz", + "integrity": "sha512-W4QV/gXvfIsccN8225784LNOorcm7ch68Fi3V4Wg7gmkWSQRKevO4RrRqWo6N/Z/myK1QAiGgeaXN57m+R/8iQ==", + "dev": true, "requires": { "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "2.34.0", - "@typescript-eslint/typescript-estree": "2.34.0", + "@typescript-eslint/experimental-utils": "3.7.1", + "@typescript-eslint/types": "3.7.1", + "@typescript-eslint/typescript-estree": "3.7.1", "eslint-visitor-keys": "^1.1.0" } }, + "@typescript-eslint/types": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.7.1.tgz", + "integrity": "sha512-PZe8twm5Z4b61jt7GAQDor6KiMhgPgf4XmUb9zdrwTbgtC/Sj29gXP1dws9yEn4+aJeyXrjsD9XN7AWFhmnUfg==", + "dev": true + }, "@typescript-eslint/typescript-estree": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz", - "integrity": "sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.7.1.tgz", + "integrity": "sha512-m97vNZkI08dunYOr2lVZOHoyfpqRs0KDpd6qkGaIcLGhQ2WPtgHOd/eVbsJZ0VYCQvupKrObAGTOvk3tfpybYA==", + "dev": true, "requires": { + "@typescript-eslint/types": "3.7.1", + "@typescript-eslint/visitor-keys": "3.7.1", "debug": "^4.1.1", - "eslint-visitor-keys": "^1.1.0", "glob": "^7.1.6", "is-glob": "^4.0.1", "lodash": "^4.17.15", @@ -2052,10 +2075,20 @@ "semver": { "version": "7.3.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true } } }, + "@typescript-eslint/visitor-keys": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.7.1.tgz", + "integrity": "sha512-xn22sQbEya+Utj2IqJHGLA3i1jDzR43RzWupxojbSWnj3nnPLavaQmWe5utw03CwYao3r00qzXfgJMGNkrzrAA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, "@webassemblyjs/ast": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", @@ -4651,6 +4684,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, "requires": { "esutils": "^2.0.2" } @@ -4904,6 +4938,23 @@ } } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + }, + "dependencies": { + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + } + } + }, "entities": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", @@ -5010,21 +5061,23 @@ } }, "eslint": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", - "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.6.0.tgz", + "integrity": "sha512-QlAManNtqr7sozWm5TF4wIH9gmUm2hE3vNRUvyoYAa4y1l5/jxD/PQStEjBMQtCqZmSep8UxrcecI60hOpe61w==", + "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.4.3", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.1.2", - "esquery": "^1.0.1", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.0", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^1.3.0", + "espree": "^7.2.0", + "esquery": "^1.2.0", "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", @@ -5033,60 +5086,199 @@ "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", - "inquirer": "^7.0.0", "is-glob": "^4.0.0", "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", + "levn": "^0.4.1", + "lodash": "^4.17.19", "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "optionator": "^0.8.3", + "optionator": "^0.9.1", "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", "table": "^5.2.3", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, "dependencies": { - "eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, "globals": { "version": "12.4.0", "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, "requires": { "type-fest": "^0.8.1" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==" + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } } } }, @@ -5518,13 +5710,14 @@ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" }, "espree": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", - "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.2.0.tgz", + "integrity": "sha512-H+cQ3+3JYRMEIOl87e7QdHX70ocly5iW4+dttuR8iYSPr/hXKFb+7dBsZ7+u1adC4VrnPlTkv0+OwuPnDop19g==", + "dev": true, "requires": { - "acorn": "^7.1.1", + "acorn": "^7.3.1", "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^1.3.0" } }, "esprima": { @@ -11070,6 +11263,73 @@ "workbox-webpack-plugin": "4.3.1" }, "dependencies": { + "@typescript-eslint/eslint-plugin": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz", + "integrity": "sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ==", + "requires": { + "@typescript-eslint/experimental-utils": "2.34.0", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz", + "integrity": "sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==", + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.34.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.34.0.tgz", + "integrity": "sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA==", + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "2.34.0", + "@typescript-eslint/typescript-estree": "2.34.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz", + "integrity": "sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==", + "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" + } + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -11087,6 +11347,86 @@ "isarray": "^1.0.0" } }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==" + } + } + }, "eslint-plugin-import": { "version": "2.20.1", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.1.tgz", @@ -11143,6 +11483,16 @@ } } }, + "espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + } + }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", @@ -11151,6 +11501,23 @@ "locate-path": "^2.0.0" } }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "requires": { + "type-fest": "^0.8.1" + } + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, "load-json-file": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", @@ -11236,9 +11603,25 @@ "find-up": "^2.0.0", "read-pkg": "^2.0.0" } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" } } }, + "react-test-renderer": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.13.1.tgz", + "integrity": "sha512-Sn2VRyOK2YJJldOqoh8Tn/lWQ+ZiKhyZTPtaO0Q6yNj+QDbmRkVFap6pZPy3YQk8DScRDfyqm/KxKYP9gCMRiQ==", + "requires": { + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "react-is": "^16.8.6", + "scheduler": "^0.19.1" + } + }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", diff --git a/package.json b/package.json index 1bb87cd..e7043d2 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "react-dom": "^16.13.1", "react-redux": "^7.2.0", "react-scripts": "3.4.1", + "react-test-renderer": "^16.13.1", "redux": "^4.0.5", "redux-devtools-extension": "^2.13.8", "reselect": "^4.0.0", @@ -31,7 +32,7 @@ "eject": "react-scripts eject", "predeploy": "npm run build", "deploy": "gh-pages -d build", - "lint": "eslint --ext .jsx,.js ./src/ --fix", + "lint": "eslint --ext .tsx, ./src/ --fix", "ts-coverage": "typescript-coverage-report" }, "browserslist": { @@ -47,6 +48,9 @@ ] }, "devDependencies": { + "@typescript-eslint/eslint-plugin": "^3.7.1", + "@typescript-eslint/parser": "^3.7.1", + "eslint": "^7.6.0", "eslint-config-standard": "^14.1.1", "eslint-plugin-import": "^2.22.0", "eslint-plugin-node": "^11.1.0", diff --git a/src/__test__/App.test.js b/src/__test__/App.test.js new file mode 100644 index 0000000..982a592 --- /dev/null +++ b/src/__test__/App.test.js @@ -0,0 +1,70 @@ +import React from 'react' +import { Provider } from 'react-redux' +import App from '../App' +import { act, create } from 'react-test-renderer' +import { createStore } from 'redux' +import { rootReducer } from '../store/reducers' + +// START STORE +const params = { rowsAmount: 2, columnsAmount: 2, lightsAmount: 2 } +const table = ['row1', 'row2'] +const rows = { + row1: ['somecell1', 'somecell2'], + row2: ['somecell3', 'somecell4'] +} + +const cells = { + somecell1: { id: 'somecell1', amount: 1 }, + somecell2: { id: 'somecell2', amount: 2 }, + somecell3: { id: 'somecell3', amount: 0 }, + somecell4: { id: 'somecell4', amount: 1 } +} +const buttons = true +const store = createStore(rootReducer, { params, table, rows, cells, buttons }) +// END STORE + +it('should correct render with some values', () => { + const tree = create( + + + + ) + .toJSON() + expect(tree).toMatchSnapshot() +}) + +describe('test cases for buttons', () => { + let component + let root + + beforeEach(() => { + act(() => { + component = create( + + + + ) + }) + root = component.root + }) + + it('click on button delete', () => { + const id = 'button-delete' + + const ButtonDelete = root.findByProps({ id }) + + act(() => ButtonDelete.props.onClick()) + + expect(component.toJSON()).toMatchSnapshot() + }) + + // it('click on button add', () => { + // const id = 'button-add' + + // const ButtonAdd = root.findByProps({ id }) + + // act(() => ButtonAdd.props.onClick()) + + // expect(component.toJSON()).toMatchSnapshot() + // }) +}) diff --git a/src/__test__/__snapshots__/App.test.js.snap b/src/__test__/__snapshots__/App.test.js.snap new file mode 100644 index 0000000..ec93999 --- /dev/null +++ b/src/__test__/__snapshots__/App.test.js.snap @@ -0,0 +1,272 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should correct render with some values 1`] = ` +
+
+ + + + +
+
+ + +
+ + + + + + + + + + + + + + + + + +
+ 1 + + 2 + + Sum: + 3 +
+ 0 + + 1 + + Sum: + 1 +
+ Average sum: + 0 + + Average sum: + 1 +
+
+`; + +exports[`test cases for buttons click on button delete 1`] = ` +
+
+ + + + +
+
+ + +
+ + + + + + + + + + + + +
+ 1 + + 2 + + Sum: + 3 +
+ Average sum: + 1 + + Average sum: + 2 +
+
+`; diff --git a/src/components/AverageRow/AverageRow.test.js b/src/components/AverageRow/AverageRow.test.js new file mode 100644 index 0000000..600680e --- /dev/null +++ b/src/components/AverageRow/AverageRow.test.js @@ -0,0 +1,33 @@ +import React from 'react' +import { Provider } from 'react-redux' +import AverageRow from './AverageRow' +import TestRenderer from 'react-test-renderer' +import { createStore } from 'redux' +import { rootReducer } from '../../store/reducers' + +it('should correct render average row with some value', () => { + const params = { rowsAmount: 2, columnsAmount: 2, lightsAmount: 2 } + const table = ['row1', 'row2'] + const rows = { + row1: ['somecell1', 'somecell2'], + row2: ['somecell3', 'somecell4'] + } + + const cells = { + somecell1: { id: 'somecell1', amount: 1 }, + somecell2: { id: 'somecell2', amount: 2 }, + somecell3: { id: 'somecell3', amount: 0 }, + somecell4: { id: 'somecell4', amount: 1 } + } + const buttons = false + const store = createStore(rootReducer, { params, table, rows, cells, buttons }) + + const tree = TestRenderer + .create( + + + + ) + .toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/src/components/AverageRow/__snapshots__/AverageRow.test.js.snap b/src/components/AverageRow/__snapshots__/AverageRow.test.js.snap new file mode 100644 index 0000000..71684cf --- /dev/null +++ b/src/components/AverageRow/__snapshots__/AverageRow.test.js.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should correct render average row with some value 1`] = ` + + + Average sum: + 0 + + + Average sum: + 1 + + +`; diff --git a/src/components/ButtonAdd/ButtonAdd.tsx b/src/components/ButtonAdd/ButtonAdd.tsx index 0dc0bdb..8b30252 100644 --- a/src/components/ButtonAdd/ButtonAdd.tsx +++ b/src/components/ButtonAdd/ButtonAdd.tsx @@ -7,6 +7,7 @@ interface Props { export const ButtonAdd: React.FC = ({ addRow }) => { return ( + +`; + +exports[`testing form methods should correct render initial form 1`] = ` +
+ + + + +
+`; + +exports[`testing form methods typed to form input 1`] = ` +
+ + + + +
+`; diff --git a/src/components/Table/Table.test.js b/src/components/Table/Table.test.js new file mode 100644 index 0000000..e22cf9f --- /dev/null +++ b/src/components/Table/Table.test.js @@ -0,0 +1,79 @@ +import React from 'react' +import { create, act } from 'react-test-renderer' +import Table from '../Table/Table' +import TableCellSum from '../TableCellSum/TableCellSum' +import { Provider } from 'react-redux' +import { rootReducer } from '../../store/reducers' +import { createStore } from 'redux' + +// START STORE +const params = { rowsAmount: 2, columnsAmount: 2, lightsAmount: 2 } +const table = ['row1', 'row2'] +const rows = { + row1: ['somecell1', 'somecell2'], + row2: ['somecell3', 'somecell4'] +} + +const cells = { + somecell1: { id: 'somecell1', amount: 1 }, + somecell2: { id: 'somecell2', amount: 2 }, + somecell3: { id: 'somecell3', amount: 0 }, + somecell4: { id: 'somecell4', amount: 1 } +} +const buttons = true +const store = createStore(rootReducer, { params, table, rows, cells, buttons }) +// END STORE + +let component +let root + +beforeEach(() => { + act(() => { + component = create( + + + + ) + }) + root = component.root +}) + +it('test should render initial table', () => { + expect(component.toJSON()).toMatchSnapshot() +}) + +it('hover on TableCellSum td with row sum', () => { + const CellSum = root.findAllByType(TableCellSum) + + act(() => CellSum[0].props.onMouseEnterPercent()) + + expect(component.toJSON()).toMatchSnapshot() +}) + +describe('test cases for td cell', () => { + const id = 'somecell1' + const event = { currentTarget: { id } } + let Cell + + beforeEach(() => { + Cell = root.findByProps({ id }) + }) + + it('hover cell', () => { + act(() => Cell.props.onMouseEnterHandler(event)) + + expect(component.toJSON()).toMatchSnapshot() + }) + + it('leave hover cell', () => { + act(() => Cell.props.onMouseLeaveHandler()) + + expect(component.toJSON).toMatchSnapshot() + }) + + it('click cell', () => { + act(() => Cell.props.onClickIncrement(event)) + + expect(component.toJSON()).toMatchSnapshot() + }) +}) diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx index a66563b..86f4b53 100644 --- a/src/components/Table/Table.tsx +++ b/src/components/Table/Table.tsx @@ -7,14 +7,14 @@ import { increment } from '../../store/actions' import './Table.css' import { useDispatch, useSelector } from 'react-redux' -const Table: React.FC = () => { +const Table: React.FC = () => { const [lightList, setLightList] = useState({}) - const dispatch = useDispatch(); + const dispatch = useDispatch() - const table = useSelector(getTable); - const lightsAmount = useSelector(getLightsAmount); - const rows = useSelector(getRows); - const cells = useSelector(getCells); + const table = useSelector(getTable) + const lightsAmount = useSelector(getLightsAmount) + const rows = useSelector(getRows) + const cells = useSelector(getCells) const sumRowArray = useSelector(getRowSum) const onMouseEnterHandler = (event: SyntheticEvent) => { diff --git a/src/components/Table/__snapshots__/Table.test.js.snap b/src/components/Table/__snapshots__/Table.test.js.snap new file mode 100644 index 0000000..1b8d8ae --- /dev/null +++ b/src/components/Table/__snapshots__/Table.test.js.snap @@ -0,0 +1,375 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`hover on TableCellSum td with row sum 1`] = ` +
+ + + + + + + + + + + + + + + + +
+ 33.33 + + 66.67 + + Sum: + 3 +
+ 0 + + 1 + + Sum: + 1 +
+ Average sum: + 0 + + Average sum: + 1 +
+`; + +exports[`test cases for td cell click cell 1`] = ` + + + + + + + + + + + + + + + + + +
+ 2 + + 2 + + Sum: + 4 +
+ 0 + + 1 + + Sum: + 1 +
+ Average sum: + 1 + + Average sum: + 1 +
+`; + +exports[`test cases for td cell hover cell 1`] = ` + + + + + + + + + + + + + + + + + +
+ 1 + + 2 + + Sum: + 3 +
+ 0 + + 1 + + Sum: + 1 +
+ Average sum: + 0 + + Average sum: + 1 +
+`; + +exports[`test cases for td cell leave hover cell 1`] = `[Function]`; + +exports[`test should render initial table 1`] = ` + + + + + + + + + + + + + + + + + +
+ 1 + + 2 + + Sum: + 3 +
+ 0 + + 1 + + Sum: + 1 +
+ Average sum: + 0 + + Average sum: + 1 +
+`; diff --git a/src/components/TableCell/TableCell.test.js b/src/components/TableCell/TableCell.test.js new file mode 100644 index 0000000..3a25de6 --- /dev/null +++ b/src/components/TableCell/TableCell.test.js @@ -0,0 +1,21 @@ +import React from 'react' +import { Provider } from 'react-redux' +import TableCell from './TableCell' +import TestRenderer from 'react-test-renderer' +import store from '../../store/store' + +it('should correct render table cell with some value', () => { + const styleString = '#6c757d' + + const tree = TestRenderer + .create( + + + + ) + .toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/src/components/TableCell/__snapshots__/TableCell.test.js.snap b/src/components/TableCell/__snapshots__/TableCell.test.js.snap new file mode 100644 index 0000000..4339340 --- /dev/null +++ b/src/components/TableCell/__snapshots__/TableCell.test.js.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should correct render table cell with some value 1`] = ` + + 1 + +`; diff --git a/src/components/TableRow/TableRow.test.js b/src/components/TableRow/TableRow.test.js new file mode 100644 index 0000000..70877e4 --- /dev/null +++ b/src/components/TableRow/TableRow.test.js @@ -0,0 +1,39 @@ +import React from 'react' +import { Provider } from 'react-redux' +import TableRow from './TableRow' +import TestRenderer from 'react-test-renderer' +import { createStore } from 'redux' +import { rootReducer } from '../../store/reducers' + +it('should correct render table row with some value', () => { + const params = { rowsAmount: 2, columnsAmount: 2, lightsAmount: 2 } + const table = ['row1', 'row2'] + const rows = { + row1: ['somecell1', 'somecell2'], + row2: ['somecell3', 'somecell4'] + } + + const cells = { + somecell1: { id: 'somecell1', amount: 1 }, + somecell2: { id: 'somecell2', amount: 2 }, + somecell3: { id: 'somecell3', amount: 0 }, + somecell4: { id: 'somecell4', amount: 1 } + } + const buttons = false + const lightList = ['somecell1', 'somecell2'] + const store = createStore(rootReducer, { params, table, rows, cells, buttons }) + + const tree = TestRenderer + .create( + + + + ) + .toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/src/components/TableRow/__snapshots__/TableRow.test.js.snap b/src/components/TableRow/__snapshots__/TableRow.test.js.snap new file mode 100644 index 0000000..4fe3553 --- /dev/null +++ b/src/components/TableRow/__snapshots__/TableRow.test.js.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should correct render table row with some value 1`] = ` + + + 1 + + + 2 + + + Sum: + 3 + + +`; diff --git a/src/helpers/__test__/generate.test.js b/src/helpers/__test__/generate.test.js new file mode 100644 index 0000000..7ca00ac --- /dev/null +++ b/src/helpers/__test__/generate.test.js @@ -0,0 +1,43 @@ +import { generateTable } from '../generate' + +test('should return correct objects', () => { + const { table, rows, cells } = generateTable(2, 3) + + expect(typeof table).toMatch(/object/) + expect(typeof rows).toMatch(/object/) + expect(typeof cells).toMatch(/object/) +}) + +test('should be correct table', () => { + const { table } = generateTable(2, 3) + const length = table.length + const data = typeof table[0] + + expect(length).toBe(2) + expect(data).toMatch(/string/) +}) + +test('should be correct row', () => { + const { rows } = generateTable(2, 3) + const length = Object.keys(rows).length + const data = typeof Object.keys(rows)[0] + + expect(length).toBe(2) + expect(data).toMatch(/string/) +}) + +test('should be correct cell object', () => { + const { cells } = generateTable(2, 3) + const length = Object.keys(cells).length + const data = typeof Object.keys(cells) + const oneCell = Object.values(cells)[0] + + expect(length).toBe(6) + expect(data).toMatch(/object/) + expect(oneCell).toEqual( + expect.objectContaining({ + id: expect.any(String), + amount: expect.any(Number) + }) + ) +}) diff --git a/src/helpers/__test__/getLightClosest.test.js b/src/helpers/__test__/getLightClosest.test.js new file mode 100644 index 0000000..2f5af6c --- /dev/null +++ b/src/helpers/__test__/getLightClosest.test.js @@ -0,0 +1,23 @@ +import { getLightClosest } from '../getLightClosest' + +const id = 'somecell1' + +const cells = { + somecell1: { id: 'somecell1', amount: 1 }, + somecell2: { id: 'somecell2', amount: 100 }, + somecell3: { id: 'somecell3', amount: 2 }, + somecell4: { id: 'somecell4', amount: 5 }, + somecell5: { id: 'somecell5', amount: 3 }, + somecell6: { id: 'somecell6', amount: 10 } +} + +const lightsAmount = 3 + +test('should return correct object of light cells true', () => { + const result = getLightClosest(id, cells, lightsAmount) + expect(result).toEqual({ + somecell1: true, + somecell3: true, + somecell5: true + }) +}) diff --git a/src/helpers/__test__/selectors.test.js b/src/helpers/__test__/selectors.test.js new file mode 100644 index 0000000..bca978e --- /dev/null +++ b/src/helpers/__test__/selectors.test.js @@ -0,0 +1,27 @@ +import { getRowSum, getAverageRowSum } from '../selectors' + +const rows1 = { + row1: ['somecell1', 'somecell2'], + row2: ['somecell3', 'somecell4'] +} + +const cells1 = { + somecell1: { id: 'somecell1', amount: 1 }, + somecell2: { id: 'somecell2', amount: 2 }, + somecell3: { id: 'somecell3', amount: 0 }, + somecell4: { id: 'somecell4', amount: 1 } +} + +// getRowSum TEST CASES + +test('should return correct sum for each row', () => { + const result = getRowSum.resultFunc(rows1, cells1) + expect(result).toEqual([3, 1]) +}) + +// getAverageRowSum TEST CASES + +test('should return correct average sum for each columns', () => { + const result = getAverageRowSum.resultFunc(rows1, cells1, 2) + expect(result).toEqual([0, 1]) +}) diff --git a/src/helpers/generate.tsx b/src/helpers/generate.tsx index a17e858..70a8dfd 100644 --- a/src/helpers/generate.tsx +++ b/src/helpers/generate.tsx @@ -1,5 +1,5 @@ import { v4 as uuidv4 } from 'uuid' -import { RowsParams, CellsParams } from '../store/actions' +import { RowsParams, CellsParams } from '../store/types' export const generateTable = (rowsAmount: string | number, columnsAmount: string | number) => { const table: Array = new Array(+rowsAmount).fill(0).map((item: string) => item = uuidv4()) @@ -21,12 +21,12 @@ export const generateTable = (rowsAmount: string | number, columnsAmount: string (acum: CellsParams, cellID: string) => ( (acum[cellID] = { id: cellID, - amount: Math.floor(Math.random() * 999) + amount: Math.floor(Math.random() * 999) }), acum ), {} - ); + ) - return { table, rows, cells }; + return { table, rows, cells } } diff --git a/src/helpers/getLightClosest.tsx b/src/helpers/getLightClosest.tsx index 6a95e52..9dfa0ea 100644 --- a/src/helpers/getLightClosest.tsx +++ b/src/helpers/getLightClosest.tsx @@ -1,4 +1,4 @@ -import { CellsParams } from '../store/actions' +import { CellsParams } from '../store/types' export const getLightClosest = (id: string, cells: CellsParams, lightsAmount: string | number) => { const arr = Object.values(cells).map(item => ({ @@ -8,7 +8,7 @@ export const getLightClosest = (id: string, cells: CellsParams, lightsAmount: st .slice(0, +lightsAmount) .map(item => item.id) - const resObj: { [name: string]: boolean } = arr.reduce((prev, cur) => ( { ...prev, [cur]: true } ), {}); + const resObj: { [name: string]: boolean } = arr.reduce((prev, cur) => ({ ...prev, [cur]: true }), {}) return resObj } diff --git a/src/helpers/interface.tsx b/src/helpers/interface.tsx index 009f710..0d0bcc1 100644 --- a/src/helpers/interface.tsx +++ b/src/helpers/interface.tsx @@ -1,4 +1,4 @@ -import { SyntheticEvent } from 'react'; +import { SyntheticEvent } from 'react' export interface TableRowProps { id: string diff --git a/src/helpers/selectors.tsx b/src/helpers/selectors.tsx index 0c0fd08..84c53d2 100644 --- a/src/helpers/selectors.tsx +++ b/src/helpers/selectors.tsx @@ -1,5 +1,5 @@ import { createSelector } from 'reselect' -import type { State } from '../store/actions' +import { State } from '../store/types' export const getRows = (state: State) => state.rows @@ -29,12 +29,12 @@ export const getRowSum = createSelector( export const getAverageRowSum = createSelector( [getRows, getCells, getColumnsAmount], (rows, cells, columnsAmount) => { - const onlyAmount = Object.values(rows).map(row => ( - row.map(item => cells[item].amount) - )) + const onlyAmount = Object.values(rows).map(row => ( + row.map(item => cells[item].amount) + )) const averageRow = onlyAmount.reduce((acum, cur) => - cur.map((amount, index) => (acum[index] += amount)), new Array(+columnsAmount).fill(0) + cur.map((amount, index) => (acum[index] += amount)), new Array(+columnsAmount).fill(0) ) return averageRow.map((item: number) => Math.floor(item / onlyAmount.length)) } diff --git a/src/store/__test__/actions.test.js b/src/store/__test__/actions.test.js new file mode 100644 index 0000000..124deb5 --- /dev/null +++ b/src/store/__test__/actions.test.js @@ -0,0 +1,112 @@ +import { + setParams, + setTable, + setRows, + setCells, + setShowButtons, + increment, + setNewRow, + removeRow +} from '../actions' +import * as types from '../types' + +describe('actions', () => { + // START STORE + const params = { rowsAmount: 2, columnsAmount: 2, lightsAmount: 2 } + const table = ['row1', 'row2'] + const rows = { + row1: ['somecell1', 'somecell2'], + row2: ['somecell3', 'somecell4'] + } + + const cells = { + somecell1: { id: 'somecell1', amount: 1 }, + somecell2: { id: 'somecell2', amount: 2 }, + somecell3: { id: 'somecell3', amount: 0 }, + somecell4: { id: 'somecell4', amount: 1 } + } + const buttons = true + // END STORE + + test('should create action SET_PARAMS', () => { + const expectedAction = { + payload: params, + type: types.ACTION_TYPES.SET_PARAMS + } + + expect(setParams(params)).toEqual(expectedAction) + }) + + test('should create action SET_TABLE', () => { + const params = table + const action = { + payload: params, + type: types.ACTION_TYPES.SET_TABLE + } + + expect(setTable(params)).toEqual(action) + }) + + test('should create action SET_ROWS', () => { + const params = rows + const action = { + payload: params, + type: types.ACTION_TYPES.SET_ROWS + } + + expect(setRows(params)).toEqual(action) + }) + + test('should create action SET_CELLS', () => { + const params = cells + const action = { + payload: params, + type: types.ACTION_TYPES.SET_CELLS + } + + expect(setCells(params)).toEqual(action) + }) + + test('should create action SHOW_BUTTONS', () => { + const params = buttons + const action = { + payload: params, + type: types.ACTION_TYPES.SHOW_BUTTONS + } + + expect(setShowButtons(params)).toEqual(action) + }) + + test('should create action INCREMENT', () => { + const params = 'somecell1' + const action = { + payload: params, + type: types.ACTION_TYPES.INCREMENT + } + + expect(increment(params)).toEqual(action) + }) + + test('should create action ADD_ROW', () => { + const rows = 1 + const params = { table, rows, cells } + const action = { + payload: params, + type: types.ACTION_TYPES.ADD_ROW + } + + expect(setNewRow(params)).toEqual(action) + }) + + test('should create action REMOVE_ROW', () => { + const lastRowKey = 'row2' + const columnsAmount = 2 + const params = { lastRowKey, columnsAmount } + const action = { + payload: params, + type: types.ACTION_TYPES.REMOVE_ROW + } + + expect(removeRow(params)).toEqual(action) + }) +}) diff --git a/src/store/__test__/redusers.test.js b/src/store/__test__/redusers.test.js new file mode 100644 index 0000000..84f29c9 --- /dev/null +++ b/src/store/__test__/redusers.test.js @@ -0,0 +1,91 @@ +import { rootReducer } from '../reducers' +import { createStore } from 'redux' +import * as types from '../types' + +describe('reducers test cases', () => { + // START STORE + const params = { rowsAmount: 2, columnsAmount: 2, lightsAmount: 2 } + const table = ['row1', 'row2'] + const rows = { + row1: ['somecell1', 'somecell2'], + row2: ['somecell3', 'somecell4'] + } + + const cells = { + somecell1: { id: 'somecell1', amount: 1 }, + somecell2: { id: 'somecell2', amount: 2 }, + somecell3: { id: 'somecell3', amount: 0 }, + somecell4: { id: 'somecell4', amount: 1 } + } + const buttons = true + const store = createStore(rootReducer, { params, table, rows, cells, buttons }) + // END STORE + + it('should return params', () => { + const action = { + payload: params, + type: types.ACTION_TYPES.SET_PARAMS + } + store.dispatch(action) + + expect(store.getState().params).toEqual(params) + }) + + it('should return table', () => { + const params = table + const action = { + payload: params, + type: types.ACTION_TYPES.SET_TABLE + } + store.dispatch(action) + + expect(store.getState().table).toEqual(params) + }) + + it('should return rows', () => { + const params = rows + const action = { + payload: params, + type: types.ACTION_TYPES.SET_ROWS + } + store.dispatch(action) + + expect(store.getState().rows).toEqual(params) + }) + + it('should return cells', () => { + const params = cells + const action = { + payload: params, + type: types.ACTION_TYPES.SET_CELLS + } + store.dispatch(action) + + expect(store.getState().cells).toEqual(params) + }) + + it('should return table without one row', () => { + const lastRowKey = 'row2' + const columnsAmount = 2 + + const params = { lastRowKey, columnsAmount } + const action = { + payload: params, + type: types.ACTION_TYPES.REMOVE_ROW + } + store.dispatch(action) + + expect(Object.keys(store.getState().rows).length).toBe(1) + }) + + it('should return increment table td cell', () => { + const params = 'somecell1' + const action = { + payload: params, + type: types.ACTION_TYPES.INCREMENT + } + store.dispatch(action) + + expect(store.getState().cells[params].amount).toBe(2) + }) +}) diff --git a/src/store/actions.tsx b/src/store/actions.tsx index f60666e..5a2431f 100644 --- a/src/store/actions.tsx +++ b/src/store/actions.tsx @@ -1,55 +1,10 @@ -export const ACTION_TYPES = { - SET_PARAMS: 'SET::PARAMS', - SET_TABLE: 'SET::TABLE', - SET_ROWS: 'SET::ROWS', - SET_CELLS: 'SET::CELLS', - SHOW_BUTTONS: 'SHOW::BUTTONS', - INCREMENT: 'INCREMENT', - ADD_ROW: 'ADD::ROW', - REMOVE_ROW: 'REMOVE::ROW' -} - -// TYPES - -export type State = { - table: Array - rows: RowsParams - cells: CellsParams - params: Params - buttons: boolean -} - -export type Action = ( - | ReturnType - | ReturnType - | ReturnType - | ReturnType - | ReturnType - | ReturnType - | ReturnType - | ReturnType -) - -export type Params = { - [name: string]: string | number -} - -export type RowsParams = { - [name: string]: Array -} - -export type CellsParams = { - [name: string]: { id: string, amount: number } -} - - -export type NewRowsParams = { - table: Array - rows: RowsParams - cells: CellsParams -} - -// ACTIONS +import { + ACTION_TYPES, + Params, + RowsParams, + CellsParams, + NewRowsParams +} from './types' export const setParams = (params: Params) => ({ payload: params, diff --git a/src/store/reducers.tsx b/src/store/reducers.tsx index 0f056d0..45a4c7b 100644 --- a/src/store/reducers.tsx +++ b/src/store/reducers.tsx @@ -1,8 +1,7 @@ import { combineReducers } from 'redux' -import { ACTION_TYPES } from './actions' -import { Action, Params, RowsParams, CellsParams } from './actions' +import { ACTION_TYPES, Action, Params, RowsParams, CellsParams } from './types' -const paramsReducer = (state: Params = {}, action: Action): Params => { +export const paramsReducer = (state: Params = {}, action: Action): Params => { const { SET_PARAMS } = ACTION_TYPES switch (action.type) { @@ -13,7 +12,7 @@ const paramsReducer = (state: Params = {}, action: Action): Params => { } } -function tableReducer (state: Array = [], action: Action): Array { +export const tableReducer = (state: Array = [], action: Action): Array => { const { SET_TABLE, ADD_ROW, REMOVE_ROW } = ACTION_TYPES switch (action.type) { @@ -75,7 +74,7 @@ const cellsReducer = (state: CellsParams = {}, action: Action): CellsParams => { } } -const buttonsReducer = (state: boolean = false, action: Action): boolean => { +const buttonsReducer = (state = false, action: Action): boolean => { const { SHOW_BUTTONS } = ACTION_TYPES switch (action.type) { diff --git a/src/store/types.tsx b/src/store/types.tsx new file mode 100644 index 0000000..5c9828a --- /dev/null +++ b/src/store/types.tsx @@ -0,0 +1,58 @@ +import { + setParams, + setTable, + setRows, + setCells, + setShowButtons, + increment, + setNewRow, + removeRow +} from './actions' + +export const ACTION_TYPES = { + SET_PARAMS: 'SET::PARAMS', + SET_TABLE: 'SET::TABLE', + SET_ROWS: 'SET::ROWS', + SET_CELLS: 'SET::CELLS', + SHOW_BUTTONS: 'SHOW::BUTTONS', + INCREMENT: 'INCREMENT', + ADD_ROW: 'ADD::ROW', + REMOVE_ROW: 'REMOVE::ROW' +} + +export type State = { + table: Array + rows: RowsParams + cells: CellsParams + params: Params + buttons: boolean +} + +export type Action = ( + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType +) + +export type Params = { + [name: string]: string | number +} + +export type RowsParams = { + [name: string]: Array +} + +export type CellsParams = { + [name: string]: { id: string, amount: number } +} + +export type NewRowsParams = { + table: Array + rows: RowsParams + cells: CellsParams +}