diff --git a/.vscode/launch.json b/.vscode/launch.json index f446fd4..836a429 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,5 +1,22 @@ { "version": "0.2.0", "configurations": [ + { + "name": "Debug CRA Tests", + "type": "node", + "request": "launch", + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/react-scripts", + "args": [ + "test", + "--runInBand", + "--no-cache", + "--coverage", + "--watchAll=false" + ], + "cwd": "${workspaceRoot}", + "protocol": "inspector", + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen" + } ] } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b99514e..c0bc915 100644 --- a/package-lock.json +++ b/package-lock.json @@ -942,13 +942,13 @@ "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.2.tgz", "integrity": "sha512-r+aumOqJ5QbD6aLPJWqVjMAPsx5pZKz+F5yPqXZ/WWG9JTtHbQqlzrJoknJ0iJxLj9vlXtmpSdjlkszseeG8OA==", "requires": { - "@hapi/hoek": "8.0.1" + "@hapi/hoek": "8.0.2" }, "dependencies": { "@hapi/hoek": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.0.1.tgz", - "integrity": "sha512-cctMYH5RLbElaUpZn3IJaUj9QNQD8iXDnl7xNY6KB1aFD2ciJrwpo3kvZowIT75uA+silJFDnSR2kGakALUymg==" + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.0.2.tgz", + "integrity": "sha512-O6o6mrV4P65vVccxymuruucb+GhP2zl9NLCG8OdoFRS8BEGw3vwpPp20wpAtpbQQxz1CEUtmxJGgWhjq1XA3qw==" } } }, @@ -1187,6 +1187,12 @@ "warning": "3.0.0" } }, + "@sheerun/mutationobserver-shim": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz", + "integrity": "sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q==", + "dev": true + }, "@svgr/babel-plugin-add-jsx-attribute": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz", @@ -1208,9 +1214,9 @@ "integrity": "sha512-U9m870Kqm0ko8beHawRXLGLvSi/ZMrl89gJ5BNcT452fAjtF2p4uRzXkdzvGJJJYBgx7BmqlDjBN/eCp5AAX2w==" }, "@svgr/babel-plugin-svg-dynamic-title": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-4.3.0.tgz", - "integrity": "sha512-3eI17Pb3jlg3oqV4Tie069n1SelYKBUpI90txDcnBWk4EGFW+YQGyQjy6iuJAReH0RnpUJ9jUExrt/xniGvhqw==" + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-4.3.1.tgz", + "integrity": "sha512-p6z6JJroP989jHWcuraeWpzdejehTmLUpyC9smhTBWyPN0VVGe2phbYxpPTV7Vh8XzmFrcG55idrnfWn/2oQEw==" }, "@svgr/babel-plugin-svg-em-dimensions": { "version": "4.2.0", @@ -1228,55 +1234,83 @@ "integrity": "sha512-hYfYuZhQPCBVotABsXKSCfel2slf/yvJY8heTVX1PCTaq/IgASq1IyxPPKJ0chWREEKewIU/JMSsIGBtK1KKxw==" }, "@svgr/babel-preset": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-4.3.0.tgz", - "integrity": "sha512-Lgy1RJiZumGtv6yJroOxzFuL64kG/eIcivJQ7y9ljVWL+0QXvFz4ix1xMrmjMD+rpJWwj50ayCIcFelevG/XXg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-4.3.1.tgz", + "integrity": "sha512-rPFKLmyhlh6oeBv3j2vEAj2nd2QbWqpoJLKzBLjwQVt+d9aeXajVaPNEqrES2spjXKR4OxfgSs7U0NtmAEkr0Q==", "requires": { "@svgr/babel-plugin-add-jsx-attribute": "4.2.0", "@svgr/babel-plugin-remove-jsx-attribute": "4.2.0", "@svgr/babel-plugin-remove-jsx-empty-expression": "4.2.0", "@svgr/babel-plugin-replace-jsx-attribute-value": "4.2.0", - "@svgr/babel-plugin-svg-dynamic-title": "4.3.0", + "@svgr/babel-plugin-svg-dynamic-title": "4.3.1", "@svgr/babel-plugin-svg-em-dimensions": "4.2.0", "@svgr/babel-plugin-transform-react-native-svg": "4.2.0", "@svgr/babel-plugin-transform-svg-component": "4.2.0" } }, "@svgr/core": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-4.3.0.tgz", - "integrity": "sha512-Ycu1qrF5opBgKXI0eQg3ROzupalCZnSDETKCK/3MKN4/9IEmt3jPX/bbBjftklnRW+qqsCEpO0y/X9BTRw2WBg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-4.3.1.tgz", + "integrity": "sha512-TXFcvzp6QjxKP5Oy7qoQY08w/nAix9TMOc4jSi3wjIJBBMUqypVwQJFMxtHrViGMQGmFdaN1y2diQrhvA+xNNQ==", "requires": { - "@svgr/plugin-jsx": "4.3.0", + "@svgr/plugin-jsx": "4.3.1", "camelcase": "5.3.1", "cosmiconfig": "5.2.1" } }, "@svgr/hast-util-to-babel-ast": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-4.2.0.tgz", - "integrity": "sha512-IvAeb7gqrGB5TH9EGyBsPrMRH/QCzIuAkLySKvH2TLfLb2uqk98qtJamordRQTpHH3e6TORfBXoTo7L7Opo/Ow==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-4.3.1.tgz", + "integrity": "sha512-MZbRccEpsro70mE6mhiv5QUXjBwHGDQZ7XrVcrDs44inaNvYUtIcheX0d9eColcnNgJmsfU3tEFfoGRnJ9E5pA==", "requires": { "@babel/types": "7.4.4" } }, "@svgr/plugin-jsx": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-4.3.0.tgz", - "integrity": "sha512-0ab8zJdSOTqPfjZtl89cjq2IOmXXUYV3Fs7grLT9ur1Al3+x3DSp2+/obrYKUGbQUnLq96RMjSZ7Icd+13vwlQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-4.3.1.tgz", + "integrity": "sha512-v9sgsn/VpDM9G1U0ZDCair7ZmYqNrVC5LiSyIQli03DAm34bYLM12xVOOrl3dg8NGNY1k4C3A6YgBL3VKjA6Og==", "requires": { - "@babel/core": "7.4.3", - "@svgr/babel-preset": "4.3.0", - "@svgr/hast-util-to-babel-ast": "4.2.0", - "rehype-parse": "6.0.0", + "@babel/core": "7.4.5", + "@svgr/babel-preset": "4.3.1", + "@svgr/hast-util-to-babel-ast": "4.3.1", + "rehype-parse": "6.0.1", "unified": "7.1.0", "vfile": "4.0.1" + }, + "dependencies": { + "@babel/core": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.5.tgz", + "integrity": "sha512-OvjIh6aqXtlsA8ujtGKfC7LYWksYSX8yQcM8Ay3LuvVeQ63lcOKgoZWVqcpFwkd29aYU9rVx7jxhfhiEDV9MZA==", + "requires": { + "@babel/code-frame": "7.0.0", + "@babel/generator": "7.4.4", + "@babel/helpers": "7.4.4", + "@babel/parser": "7.4.5", + "@babel/template": "7.4.4", + "@babel/traverse": "7.4.5", + "@babel/types": "7.4.4", + "convert-source-map": "1.6.0", + "debug": "4.1.1", + "json5": "2.1.0", + "lodash": "4.17.11", + "resolve": "1.10.0", + "semver": "5.7.0", + "source-map": "0.5.7" + } + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } } }, "@svgr/plugin-svgo": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-4.2.0.tgz", - "integrity": "sha512-zUEKgkT172YzHh3mb2B2q92xCnOAMVjRx+o0waZ1U50XqKLrVQ/8dDqTAtnmapdLsGurv8PSwenjLCUpj6hcvw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-4.3.1.tgz", + "integrity": "sha512-PrMtEDUWjX3Ea65JsVCwTIXuSqa3CG9px+DluF1/eo9mlDrgrtFE7NE/DjdhjJgSM9wenlVBzkzneSIUgfUI/w==", "requires": { "cosmiconfig": "5.2.1", "merge-deep": "3.0.2", @@ -1292,12 +1326,57 @@ "@babel/plugin-transform-react-constant-elements": "7.2.0", "@babel/preset-env": "7.4.5", "@babel/preset-react": "7.0.0", - "@svgr/core": "4.3.0", - "@svgr/plugin-jsx": "4.3.0", - "@svgr/plugin-svgo": "4.2.0", + "@svgr/core": "4.3.1", + "@svgr/plugin-jsx": "4.3.1", + "@svgr/plugin-svgo": "4.3.1", "loader-utils": "1.2.3" } }, + "@testing-library/dom": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-5.5.0.tgz", + "integrity": "sha512-QuY/XBp9fquYXP1jklKlG0nUmFVLJXLWNYANmoFs25RDystdujLXxXSVhacVqL5oIF8ESThBzHFX1FUuV/J0kw==", + "dev": true, + "requires": { + "@babel/runtime": "7.4.5", + "@sheerun/mutationobserver-shim": "0.3.2", + "aria-query": "3.0.0", + "pretty-format": "24.8.0", + "wait-for-expect": "1.2.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz", + "integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==", + "dev": true, + "requires": { + "regenerator-runtime": "0.13.2" + } + } + } + }, + "@testing-library/react": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-8.0.4.tgz", + "integrity": "sha512-omm4D00Z0aMaWfPRRP4X6zIaOVb0Kf1Yc1H5VE4id9D0pQRiBcTtmjbN0kZgT8rQGxHhVAuv1NuwFwMTwKzFqg==", + "dev": true, + "requires": { + "@babel/runtime": "7.4.5", + "@testing-library/dom": "5.5.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz", + "integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==", + "dev": true, + "requires": { + "regenerator-runtime": "0.13.2" + } + } + } + }, "@types/babel__core": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz", @@ -1909,7 +1988,7 @@ "integrity": "sha512-kuip9YilBqhirhHEGHaBTZKXL//xxGnzvsD0FtBQa6z+A69qZD6s/BAX9VzDF1i9VKDquTJDQaPLSEhOnL6FvQ==", "requires": { "browserslist": "4.6.3", - "caniuse-lite": "1.0.30000978", + "caniuse-lite": "1.0.30000979", "chalk": "2.4.2", "normalize-range": "0.1.2", "num2fraction": "1.2.2", @@ -2552,8 +2631,8 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.3.tgz", "integrity": "sha512-CNBqTCq22RKM8wKJNowcqihHJ4SkI8CGeK7KOR9tPboXUuS5Zk5lQgzzTbs4oxD8x+6HUshZUa2OyNI9lR93bQ==", "requires": { - "caniuse-lite": "1.0.30000978", - "electron-to-chromium": "1.3.177", + "caniuse-lite": "1.0.30000979", + "electron-to-chromium": "1.3.184", "node-releases": "1.1.24" } }, @@ -2683,15 +2762,15 @@ "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", "requires": { "browserslist": "4.6.3", - "caniuse-lite": "1.0.30000978", + "caniuse-lite": "1.0.30000979", "lodash.memoize": "4.1.2", "lodash.uniq": "4.5.0" } }, "caniuse-lite": { - "version": "1.0.30000978", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000978.tgz", - "integrity": "sha512-H6gK6kxUzG6oAwg/Jal279z8pHw0BzrpZfwo/CA9FFm/vA0l8IhDfkZtepyJNE2Y4V6Dp3P3ubz6czby1/Mgsw==" + "version": "1.0.30000979", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000979.tgz", + "integrity": "sha512-gcu45yfq3B7Y+WB05fOMfr0EiSlq+1u+m6rPHyJli/Wy3PVQNGaU7VA4bZE5qw+AU2UVOBR/N5g1bzADUqdvFw==" }, "capture-exit": { "version": "2.0.0", @@ -3541,9 +3620,9 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" }, "core-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.1.tgz", - "integrity": "sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew==" + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" }, "core-js-compat": { "version": "3.1.4", @@ -3552,13 +3631,13 @@ "requires": { "browserslist": "4.6.3", "core-js-pure": "3.1.4", - "semver": "6.1.2" + "semver": "6.2.0" }, "dependencies": { "semver": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.2.tgz", - "integrity": "sha512-z4PqiCpomGtWj8633oeAdXm1Kn1W++3T8epkZYnwiVgIYIJ0QHszhInYSJTYxebByQH7KVCEAn8R9duzZW2PhQ==" + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", + "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==" } } }, @@ -3663,6 +3742,26 @@ "randomfill": "1.0.4" } }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "requires": { + "inherits": "2.0.4", + "source-map": "0.6.1", + "source-map-resolve": "0.5.2", + "urix": "0.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "css-blank-pseudo": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz", @@ -3784,6 +3883,12 @@ "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" }, + "css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=", + "dev": true + }, "cssdb": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", @@ -3890,9 +3995,9 @@ "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==" }, "cssstyle": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", - "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.3.0.tgz", + "integrity": "sha512-wXsoRfsRfsLVNaVzoKdqvEmK/5PFaEXNspVT22Ots6K/cnJdpoDKuQFw+qlMiXnmaif1OgeC466X1zISgAOcGg==", "requires": { "cssom": "0.3.6" } @@ -4276,9 +4381,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.177", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.177.tgz", - "integrity": "sha512-vMoq8iU57YZgxrW9kglf1ZEBcU+Ec72OlR+SH/UdBTKS6tZTCypxa8pmD1CPZ3olU4IJX2ujilGkvpn0GHSwTQ==" + "version": "1.3.184", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.184.tgz", + "integrity": "sha512-jctpw2OkTBfpKg6PI3ndZZ+LlH6y0Pc+o9wvOkm9iqqgQ/FcOxpiADeB4kGddAEtZ+PBR4+TStEcoj1ApS3lUA==" }, "elliptic": { "version": "6.5.0", @@ -4436,7 +4541,7 @@ "glob": "7.1.4", "globals": "11.12.0", "ignore": "4.0.6", - "import-fresh": "3.0.0", + "import-fresh": "3.1.0", "imurmurhash": "0.1.4", "inquirer": "6.4.1", "js-yaml": "3.13.1", @@ -4458,9 +4563,9 @@ }, "dependencies": { "import-fresh": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", - "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", + "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", "requires": { "parent-module": "1.0.1", "resolve-from": "4.0.0" @@ -4742,7 +4847,7 @@ "damerau-levenshtein": "1.0.5", "emoji-regex": "7.0.3", "has": "1.0.3", - "jsx-ast-utils": "2.2.0" + "jsx-ast-utils": "2.2.1" } }, "eslint-plugin-react": { @@ -4753,7 +4858,7 @@ "array-includes": "3.0.3", "doctrine": "2.1.0", "has": "1.0.3", - "jsx-ast-utils": "2.2.0", + "jsx-ast-utils": "2.2.1", "object.fromentries": "2.0.0", "prop-types": "15.7.2", "resolve": "1.10.0" @@ -5169,21 +5274,6 @@ "promise": "7.3.1", "setimmediate": "1.0.5", "ua-parser-js": "0.7.20" - }, - "dependencies": { - "core-js": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", - "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" - }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "requires": { - "asap": "2.0.6" - } - } } }, "figgy-pudding": { @@ -6034,6 +6124,12 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, "indexes-of": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", @@ -6554,6 +6650,22 @@ "detect-newline": "2.1.0" } }, + "jest-dom": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jest-dom/-/jest-dom-3.5.0.tgz", + "integrity": "sha512-xHnP3Qo/29oLAo2iixaZsoDrm3XKSVrMH5Wf2ZEiLychJQBTNzOeVMPxrCygCgJiyQMbnymXltme8bPzuiGOIA==", + "dev": true, + "requires": { + "chalk": "2.4.2", + "css": "2.2.4", + "css.escape": "1.5.1", + "jest-diff": "24.8.0", + "jest-matcher-utils": "24.8.0", + "lodash": "4.17.11", + "pretty-format": "24.8.0", + "redent": "2.0.0" + } + }, "jest-each": { "version": "24.8.0", "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.8.0.tgz", @@ -6599,7 +6711,7 @@ "acorn-globals": "4.3.2", "array-equal": "1.0.0", "cssom": "0.3.6", - "cssstyle": "1.2.2", + "cssstyle": "1.3.0", "data-urls": "1.1.0", "domexception": "1.0.1", "escodegen": "1.11.1", @@ -7505,7 +7617,7 @@ "acorn-globals": "4.3.2", "array-equal": "1.0.0", "cssom": "0.3.6", - "cssstyle": "1.2.2", + "cssstyle": "1.3.0", "data-urls": "1.1.0", "domexception": "1.0.1", "escodegen": "1.11.1", @@ -7616,9 +7728,9 @@ } }, "jsx-ast-utils": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.0.tgz", - "integrity": "sha512-yAmhGSzR7TsD0OQpu1AGLz8Bx84cxMqtgoJrufomY6BlveEDlREhvu1rea21936xbe5tlUh7IPda82m5ae0H8Q==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz", + "integrity": "sha512-v3FxCcAf20DayI+uxnCuw795+oOIkVu6EnJ1+kSzhqqTZHNkTZ7B66ZgLp4oLJ/gbA64cI0B7WRoHZMSRdyVRQ==", "requires": { "array-includes": "3.0.3", "object.assign": "4.1.0" @@ -9602,7 +9714,7 @@ "requires": { "autoprefixer": "9.6.0", "browserslist": "4.6.3", - "caniuse-lite": "1.0.30000978", + "caniuse-lite": "1.0.30000979", "css-blank-pseudo": "0.1.4", "css-has-pseudo": "0.10.0", "css-prefers-color-scheme": "3.1.1", @@ -9825,9 +9937,9 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" }, "promise": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.0.2.tgz", - "integrity": "sha512-EIyzM39FpVOMbqgzEHhxdrEhtOSDOtjMZQ0M6iVfCE+kWNgCkAyOdnuCWqfmflylftfadU6FkiMgHZA2kUzwRw==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "requires": { "asap": "2.0.6" } @@ -9879,9 +9991,9 @@ "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" }, "psl": { - "version": "1.1.33", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.33.tgz", - "integrity": "sha512-LTDP2uSrsc7XCb5lO7A8BI1qYxRe/8EqlRvMeEl6rsnYAqDOl8xHR+8lSAIVfrNaSAlTPTNOCgNjWcoUL3AZsw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.2.0.tgz", + "integrity": "sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA==" }, "public-encrypt": { "version": "4.0.3", @@ -10026,6 +10138,21 @@ "raf": "3.4.1", "regenerator-runtime": "0.13.2", "whatwg-fetch": "3.0.0" + }, + "dependencies": { + "core-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.1.tgz", + "integrity": "sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew==" + }, + "promise": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.0.2.tgz", + "integrity": "sha512-EIyzM39FpVOMbqgzEHhxdrEhtOSDOtjMZQ0M6iVfCE+kWNgCkAyOdnuCWqfmflylftfadU6FkiMgHZA2kUzwRw==", + "requires": { + "asap": "2.0.6" + } + } } }, "react-dev-utils": { @@ -10070,8 +10197,8 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.5.4.tgz", "integrity": "sha512-rAjx494LMjqKnMPhFkuLmLp8JWEX0o8ADTGeAbOqaF+XCvYLreZrG5uVjnPBlAQ8REZK4pzXGvp0bWgrFtKaag==", "requires": { - "caniuse-lite": "1.0.30000978", - "electron-to-chromium": "1.3.177", + "caniuse-lite": "1.0.30000979", + "electron-to-chromium": "1.3.184", "node-releases": "1.1.24" } }, @@ -10250,6 +10377,16 @@ "minimatch": "3.0.4" } }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "3.2.0", + "strip-indent": "2.0.0" + } + }, "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", @@ -10329,9 +10466,9 @@ } }, "rehype-parse": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-6.0.0.tgz", - "integrity": "sha512-V2OjMD0xcSt39G4uRdMTqDXXm6HwkUbLMDayYKA/d037j8/OtVSQ+tqKwYWOuyBeoCs/3clXRe30VUjeMDTBSA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-6.0.1.tgz", + "integrity": "sha512-FrGSbOzcGxIvWty1qHjKTvHT4WBTt7C6JLs65EkvFPa7ZKraSmsoDDj6al1eBxaXS1t/kiGdPYazUe58Mgflgw==", "requires": { "hast-util-from-parse5": "5.0.1", "parse5": "5.1.0", @@ -10447,7 +10584,7 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "requires": { - "psl": "1.1.33", + "psl": "1.2.0", "punycode": "1.4.1" } } @@ -11424,6 +11561,12 @@ "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -11688,7 +11831,7 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "requires": { - "psl": "1.1.33", + "psl": "1.2.0", "punycode": "2.1.1" } }, @@ -12137,6 +12280,12 @@ "xml-name-validator": "3.0.0" } }, + "wait-for-expect": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/wait-for-expect/-/wait-for-expect-1.2.0.tgz", + "integrity": "sha512-EJhKpA+5UHixduMBEGhTFuLuVgQBKWxkFbefOdj2bbk2/OpA5Opsc4aUTGmF+qJ+v3kTGxDRNYwKaT4j6g5n8Q==", + "dev": true + }, "walker": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", diff --git a/package.json b/package.json index 136afb0..ad18328 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,14 @@ "react-dom": "^16.8.6", "react-scripts": "3.0.1" }, + "devDependencies": { + "@testing-library/react": "^8.0.4", + "jest-dom": "^3.5.0" + }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", - "test": "react-scripts test", + "test": "react-scripts test --coverage --watchAll=false", "eject": "react-scripts eject" }, "eslintConfig": { @@ -28,5 +32,13 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "jest": { + "collectCoverageFrom" : [ + "/src/**/*.js", + "!/src/index.js" , + "!/src/serviceWorker.js", + "!/src/test-utils/**/*.js" + ] } } diff --git a/src/App.test.js b/src/App.test.js new file mode 100644 index 0000000..462ad56 --- /dev/null +++ b/src/App.test.js @@ -0,0 +1,55 @@ +import React from "react"; +import { cleanup } from "@testing-library/react"; +import "jest-dom/extend-expect"; +import renderWithRoute from "./test-utils/renderWithRoute"; +import App from "./App"; + +afterEach(cleanup); + +test("should render home page", async () => { + const { queryByTestId } = renderWithRoute(, "/"); + + const navigationNode = queryByTestId("navigation"); + expect(navigationNode).toBeInTheDocument(); + + const homeNode = queryByTestId("home"); + expect(homeNode).toBeInTheDocument(); + + const todosNode = queryByTestId("todos"); + expect(todosNode).not.toBeInTheDocument(); + + const notFoundNode = queryByTestId("not-found"); + expect(notFoundNode).not.toBeInTheDocument(); +}); + +test("should render todos page", () => { + const { queryByTestId } = renderWithRoute(, "/todos"); + + const navigationNode = queryByTestId("navigation"); + expect(navigationNode).toBeInTheDocument(); + + const homeNode = queryByTestId("home"); + expect(homeNode).not.toBeInTheDocument(); + + const todosNode = queryByTestId("todos"); + expect(todosNode).toBeInTheDocument(); + + const notFoundNode = queryByTestId("not-found"); + expect(notFoundNode).not.toBeInTheDocument(); +}); + +test("should render not found page", () => { + const { queryByTestId } = renderWithRoute(, "/bad-route"); + + const navigationNode = queryByTestId("navigation"); + expect(navigationNode).toBeInTheDocument(); + + const homeNode = queryByTestId("home"); + expect(homeNode).not.toBeInTheDocument(); + + const todosNode = queryByTestId("todos"); + expect(todosNode).not.toBeInTheDocument(); + + const notFoundNode = queryByTestId("not-found"); + expect(notFoundNode).toBeInTheDocument(); +}); diff --git a/src/Home.test.js b/src/Home.test.js new file mode 100644 index 0000000..892dbc0 --- /dev/null +++ b/src/Home.test.js @@ -0,0 +1,10 @@ +import React from "react"; +import { render, cleanup } from "@testing-library/react"; +import Home from "./Home"; + +afterEach(cleanup); + +test("should render home component", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); +}); diff --git a/src/Navigation.test.js b/src/Navigation.test.js new file mode 100644 index 0000000..65b00b2 --- /dev/null +++ b/src/Navigation.test.js @@ -0,0 +1,51 @@ +import React from "react"; +import { cleanup, fireEvent } from "@testing-library/react"; +import "jest-dom/extend-expect"; +import renderWithRoute from "./test-utils/renderWithRoute"; +import Navigation from "./Navigation"; + +afterEach(cleanup); + +test("should have home not as link when on home route", async () => { + const { getByText } = renderWithRoute(, "/"); + + const navigationNode = getByText("Home"); + expect(navigationNode).toMatchSnapshot(); +}); + +test("should have home as link when not on home route", async () => { + const { getByText } = renderWithRoute(, "/not-home"); + + const navigationNode = getByText("Home"); + expect(navigationNode).toMatchSnapshot(); +}); + +test("should change route to home route when home link clicked", async () => { + const { getByText, history } = renderWithRoute(, "/not-home"); + + const navigationNode = getByText("Home"); + fireEvent.click(navigationNode); + expect(history.location.pathname).toBe("/"); +}); + +test("should have todos example not as link when on todos route", async () => { + const { getByText } = renderWithRoute(, "/todos"); + + const navigationNode = getByText("Todos Example"); + expect(navigationNode).toMatchSnapshot(); +}); + +test("should have todos example as link when not on todos route", async () => { + const { getByText } = renderWithRoute(, "/not-todos"); + + const navigationNode = getByText("Todos Example"); + expect(navigationNode).toMatchSnapshot(); +}); + +test("should change route to todos route when todos link clicked", async () => { + const { getByText, history } = renderWithRoute(, "/not-todos"); + + const navigationNode = getByText("Todos Example"); + fireEvent.click(navigationNode); + expect(history.location.pathname).toBe("/todos"); +}); diff --git a/src/NotFound.test.js b/src/NotFound.test.js new file mode 100644 index 0000000..709783a --- /dev/null +++ b/src/NotFound.test.js @@ -0,0 +1,10 @@ +import React from "react"; +import { render, cleanup } from "@testing-library/react"; +import NotFound from "./NotFound"; + +afterEach(cleanup); + +test("should render not found component", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); +}); diff --git a/src/Todos.test.js b/src/Todos.test.js new file mode 100644 index 0000000..36a2a9e --- /dev/null +++ b/src/Todos.test.js @@ -0,0 +1,82 @@ +import React from "react"; +import { render, cleanup, fireEvent } from "@testing-library/react"; +import "jest-dom/extend-expect"; +import Todos from "./Todos"; + +afterEach(cleanup); + +test("should start with empty list", () => { + const { getByTestId } = render(); + + const todosListNode = getByTestId("todos-list") + expect(todosListNode.hasChildNodes()).toBe(false) +}); + +test("should not add blank item to list", () => { + const { getByPlaceholderText, getByTestId } = render(); + + const todosEntryNode = getByPlaceholderText("Add todo...") + + enterValueAndPressEnter(todosEntryNode, "") + + const todosListNode = getByTestId("todos-list") + expect(todosListNode.hasChildNodes()).toBe(false) +}); + +test("should add items to list", () => { + const { getByPlaceholderText, getByLabelText } = render(); + + const todosEntryNode = getByPlaceholderText("Add todo...") + + enterValueAndPressEnter(todosEntryNode, "Learn to test") + enterValueAndPressEnter(todosEntryNode, "Practice") + enterValueAndPressEnter(todosEntryNode, "Keep trying") + + expect(getByLabelText("Learn to test").checked).toBe(false) + expect(getByLabelText("Practice").checked).toBe(false) + expect(getByLabelText("Keep trying").checked).toBe(false) +}); + +test("should check item to list", () => { + const { getByPlaceholderText, getByLabelText } = render(); + + const todosEntryNode = getByPlaceholderText("Add todo...") + + enterValueAndPressEnter(todosEntryNode, "Learn to test") + enterValueAndPressEnter(todosEntryNode, "Practice") + enterValueAndPressEnter(todosEntryNode, "Keep trying") + + const practiceNode = getByLabelText("Practice") + + fireEvent.click(practiceNode) + + expect(getByLabelText("Learn to test").checked).toBe(false) + expect(practiceNode.checked).toBe(true) + expect(getByLabelText("Keep trying").checked).toBe(false) +}); + +test("should delete item from list", () => { + const { getByPlaceholderText, getAllByText, queryByLabelText } = render(); + + const todosEntryNode = getByPlaceholderText("Add todo...") + + enterValueAndPressEnter(todosEntryNode, "Learn to test") + enterValueAndPressEnter(todosEntryNode, "Practice") + enterValueAndPressEnter(todosEntryNode, "Keep trying") + + const removeButtons = getAllByText("(remove)") + + fireEvent.click(removeButtons[1]) + + expect(queryByLabelText("Learn to test")).toBeInTheDocument() + expect(queryByLabelText("Practice")).not.toBeInTheDocument() + expect(queryByLabelText("Keep trying")).toBeInTheDocument() +}); + +const enterValueAndPressEnter = (node, value) =>{ + fireEvent.change(node, { target: { value } }) + + fireEvent.keyPress(node, { + charCode: 13 + }) +} \ No newline at end of file diff --git a/src/__snapshots__/Home.test.js.snap b/src/__snapshots__/Home.test.js.snap new file mode 100644 index 0000000..ab83766 --- /dev/null +++ b/src/__snapshots__/Home.test.js.snap @@ -0,0 +1,35 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render home component 1`] = ` +
+
+
+ +

+ Edit + + src/App.js + + and save to reload. +

+ + Learn React + +
+
+
+`; diff --git a/src/__snapshots__/Navigation.test.js.snap b/src/__snapshots__/Navigation.test.js.snap new file mode 100644 index 0000000..f8b54dc --- /dev/null +++ b/src/__snapshots__/Navigation.test.js.snap @@ -0,0 +1,29 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should have home as link when not on home route 1`] = ` + + Home + +`; + +exports[`should have home not as link when on home route 1`] = ` + + Home + +`; + +exports[`should have todos example as link when not on todos route 1`] = ` + + Todos Example + +`; + +exports[`should have todos example not as link when on todos route 1`] = ` + + Todos Example + +`; diff --git a/src/__snapshots__/NotFound.test.js.snap b/src/__snapshots__/NotFound.test.js.snap new file mode 100644 index 0000000..c7eead0 --- /dev/null +++ b/src/__snapshots__/NotFound.test.js.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render not found component 1`] = ` +
+
+

+ Whoops! +

+
+
+`; diff --git a/src/test-utils/renderWithRoute.js b/src/test-utils/renderWithRoute.js new file mode 100644 index 0000000..eac39a0 --- /dev/null +++ b/src/test-utils/renderWithRoute.js @@ -0,0 +1,22 @@ +import React from "react"; +import { render } from "@testing-library/react"; +import { + createHistory, + createMemorySource, + LocationProvider +} from "@reach/router"; + +// this is a handy function that I would utilize for any component +// that relies on the router being in context +const renderWithRoute = (ui, route) => { + const history = createHistory(createMemorySource(route)) + return { + ...render({ui}), + // adding `history` to the returned utilities to allow us + // to reference it in our tests (just try to avoid using + // this to test implementation details). + history + } +}; + +export default renderWithRoute \ No newline at end of file