diff --git a/final/client/package-lock.json b/final/client/package-lock.json
index 49bccde3f..054a6a63d 100644
--- a/final/client/package-lock.json
+++ b/final/client/package-lock.json
@@ -2872,11 +2872,36 @@
"@babel/types": "^7.3.0"
}
},
+ "@types/cheerio": {
+ "version": "0.22.22",
+ "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.22.tgz",
+ "integrity": "sha512-05DYX4zU96IBfZFY+t3Mh88nlwSMtmmzSYaQkKN48T495VV1dkHSah6qYyDTN5ngaS0i0VonH37m+RuzSM0YiA==",
+ "requires": {
+ "@types/node": "*"
+ }
+ },
"@types/color-name": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
},
+ "@types/enzyme": {
+ "version": "3.10.8",
+ "resolved": "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.10.8.tgz",
+ "integrity": "sha512-vlOuzqsTHxog6PV79+tvOHFb6hq4QZKMq1lLD9MaWD1oec2lHTKndn76XOpSwCA0oFTaIbKVPrgM3k78Jjd16g==",
+ "requires": {
+ "@types/cheerio": "*",
+ "@types/react": "*"
+ }
+ },
+ "@types/enzyme-adapter-react-16": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.6.tgz",
+ "integrity": "sha512-VonDkZ15jzqDWL8mPFIQnnLtjwebuL9YnDkqeCDYnB4IVgwUm0mwKkqhrxLL6mb05xm7qqa3IE95m8CZE9imCg==",
+ "requires": {
+ "@types/enzyme": "*"
+ }
+ },
"@types/eslint-visitor-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
@@ -3335,6 +3360,14 @@
"regex-parser": "2.2.10"
},
"dependencies": {
+ "assert": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
+ "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
+ "requires": {
+ "util": "0.10.3"
+ }
+ },
"camelcase": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
@@ -3345,6 +3378,11 @@
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k="
},
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+ "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE="
+ },
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
@@ -3362,6 +3400,14 @@
"emojis-list": "^2.0.0",
"json5": "^1.0.1"
}
+ },
+ "util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+ "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+ "requires": {
+ "inherits": "2.0.1"
+ }
}
}
},
@@ -3380,6 +3426,23 @@
"indent-string": "^4.0.0"
}
},
+ "airbnb-prop-types": {
+ "version": "2.16.0",
+ "resolved": "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz",
+ "integrity": "sha512-7WHOFolP/6cS96PhKNrslCLMYAI8yB1Pp6u6XmxozQOiZbsI5ycglZr5cHhBFfuRcQQjzCMith5ZPZdYiJCxUg==",
+ "dev": true,
+ "requires": {
+ "array.prototype.find": "^2.1.1",
+ "function.prototype.name": "^1.1.2",
+ "is-regex": "^1.1.0",
+ "object-is": "^1.1.2",
+ "object.assign": "^4.1.0",
+ "object.entries": "^1.1.2",
+ "prop-types": "^15.7.2",
+ "prop-types-exact": "^1.2.0",
+ "react-is": "^16.13.1"
+ }
+ },
"ajv": {
"version": "6.12.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
@@ -3997,6 +4060,11 @@
"resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
"integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM="
},
+ "array-filter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz",
+ "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM="
+ },
"array-flatten": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
@@ -4030,6 +4098,16 @@
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
},
+ "array.prototype.find": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.1.1.tgz",
+ "integrity": "sha512-mi+MYNJYLTx2eNYy+Yh6raoQacCsNeeMUaspFPh9Y141lFSsWxxB8V9mM2ye+eqiRs917J6/pJ4M9ZPzenWckA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.4"
+ }
+ },
"array.prototype.flat": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz",
@@ -4249,11 +4327,14 @@
}
},
"assert": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
- "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz",
+ "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==",
"requires": {
- "util": "0.10.3"
+ "es6-object-assign": "^1.1.0",
+ "is-nan": "^1.2.1",
+ "object-is": "^1.0.1",
+ "util": "^0.12.0"
}
},
"assert-plus": {
@@ -4330,6 +4411,14 @@
"postcss-value-parser": "^4.1.0"
}
},
+ "available-typed-arrays": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz",
+ "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==",
+ "requires": {
+ "array-filter": "^1.0.0"
+ }
+ },
"await-to-js": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/await-to-js/-/await-to-js-2.1.1.tgz",
@@ -5291,6 +5380,15 @@
"unset-value": "^1.0.0"
}
},
+ "call-bind": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz",
+ "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==",
+ "requires": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.0"
+ }
+ },
"call-me-maybe": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
@@ -7427,6 +7525,12 @@
}
}
},
+ "discontinuous-range": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
+ "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=",
+ "dev": true
+ },
"dns-equal": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
@@ -7777,6 +7881,93 @@
"java-properties": "^1.0.0"
}
},
+ "enzyme": {
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.11.0.tgz",
+ "integrity": "sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==",
+ "dev": true,
+ "requires": {
+ "array.prototype.flat": "^1.2.3",
+ "cheerio": "^1.0.0-rc.3",
+ "enzyme-shallow-equal": "^1.0.1",
+ "function.prototype.name": "^1.1.2",
+ "has": "^1.0.3",
+ "html-element-map": "^1.2.0",
+ "is-boolean-object": "^1.0.1",
+ "is-callable": "^1.1.5",
+ "is-number-object": "^1.0.4",
+ "is-regex": "^1.0.5",
+ "is-string": "^1.0.5",
+ "is-subset": "^0.1.1",
+ "lodash.escape": "^4.0.1",
+ "lodash.isequal": "^4.5.0",
+ "object-inspect": "^1.7.0",
+ "object-is": "^1.0.2",
+ "object.assign": "^4.1.0",
+ "object.entries": "^1.1.1",
+ "object.values": "^1.1.1",
+ "raf": "^3.4.1",
+ "rst-selector-parser": "^2.2.3",
+ "string.prototype.trim": "^1.2.1"
+ }
+ },
+ "enzyme-adapter-react-16": {
+ "version": "1.15.5",
+ "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.5.tgz",
+ "integrity": "sha512-33yUJGT1nHFQlbVI5qdo5Pfqvu/h4qPwi1o0a6ZZsjpiqq92a3HjynDhwd1IeED+Su60HDWV8mxJqkTnLYdGkw==",
+ "dev": true,
+ "requires": {
+ "enzyme-adapter-utils": "^1.13.1",
+ "enzyme-shallow-equal": "^1.0.4",
+ "has": "^1.0.3",
+ "object.assign": "^4.1.0",
+ "object.values": "^1.1.1",
+ "prop-types": "^15.7.2",
+ "react-is": "^16.13.1",
+ "react-test-renderer": "^16.0.0-0",
+ "semver": "^5.7.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "enzyme-adapter-utils": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.13.1.tgz",
+ "integrity": "sha512-5A9MXXgmh/Tkvee3bL/9RCAAgleHqFnsurTYCbymecO4ohvtNO5zqIhHxV370t7nJAwaCfkgtffarKpC0GPt0g==",
+ "dev": true,
+ "requires": {
+ "airbnb-prop-types": "^2.16.0",
+ "function.prototype.name": "^1.1.2",
+ "object.assign": "^4.1.0",
+ "object.fromentries": "^2.0.2",
+ "prop-types": "^15.7.2",
+ "semver": "^5.7.1"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "enzyme-shallow-equal": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.4.tgz",
+ "integrity": "sha512-MttIwB8kKxypwHvRynuC3ahyNc+cFbR8mjVIltnmzQ0uKGqmsfO4bfBuLxb0beLNPhjblUEYvEbsg+VSygvF1Q==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3",
+ "object-is": "^1.1.2"
+ }
+ },
"errno": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
@@ -7841,6 +8032,11 @@
"es6-symbol": "^3.1.1"
}
},
+ "es6-object-assign": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz",
+ "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw="
+ },
"es6-symbol": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
@@ -8842,6 +9038,11 @@
"for-in": "^1.0.1"
}
},
+ "foreach": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
+ "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k="
+ },
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@@ -8999,11 +9200,84 @@
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
+ "function.prototype.name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.3.tgz",
+ "integrity": "sha512-H51qkbNSp8mtkJt+nyW1gyStBiKZxfRqySNUR99ylq6BPXHKI4SEvIlTKp4odLfjRKJV04DFWMU3G/YRlQOsag==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.1",
+ "functions-have-names": "^1.2.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.18.0-next.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+ "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ },
+ "is-callable": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
+ "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+ "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "object-inspect": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
+ "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
+ "dev": true
+ },
+ "object.assign": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+ "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ }
+ }
+ }
+ },
"functional-red-black-tree": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc="
},
+ "functions-have-names": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.1.tgz",
+ "integrity": "sha512-j48B/ZI7VKs3sgeI2cZp7WXWmZXu7Iq5pl5/vptV5N2mq+DGFuS/ulaDjtaoLpYzuD6u8UgrUKHfgo7fDTSiBA==",
+ "dev": true
+ },
"gaze": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
@@ -9023,6 +9297,16 @@
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
+ "get-intrinsic": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz",
+ "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==",
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1"
+ }
+ },
"get-own-enumerable-property-symbols": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
@@ -9522,6 +9806,15 @@
"resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz",
"integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ=="
},
+ "html-element-map": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.2.0.tgz",
+ "integrity": "sha512-0uXq8HsuG1v2TmQ8QkIhzbrqeskE4kn52Q18QJ9iAA/SnHoEKXWiUxHQtclRsCFWEUD2So34X+0+pZZu862nnw==",
+ "dev": true,
+ "requires": {
+ "array-filter": "^1.0.0"
+ }
+ },
"html-encoding-sniffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
@@ -9753,6 +10046,11 @@
"integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=",
"dev": true
},
+ "immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
+ },
"immer": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz",
@@ -10014,6 +10312,12 @@
"binary-extensions": "^2.0.0"
}
},
+ "is-boolean-object": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz",
+ "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==",
+ "dev": true
+ },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
@@ -10105,6 +10409,11 @@
"resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
"integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ=="
},
+ "is-generator-function": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.8.tgz",
+ "integrity": "sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ=="
+ },
"is-glob": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
@@ -10151,6 +10460,19 @@
}
}
},
+ "is-nan": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.0.tgz",
+ "integrity": "sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ==",
+ "requires": {
+ "define-properties": "^1.1.3"
+ }
+ },
+ "is-negative-zero": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
+ "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE="
+ },
"is-npm": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
@@ -10165,6 +10487,12 @@
"kind-of": "^3.0.2"
}
},
+ "is-number-object": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz",
+ "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==",
+ "dev": true
+ },
"is-obj": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
@@ -10273,6 +10601,12 @@
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
"integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ=="
},
+ "is-subset": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz",
+ "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=",
+ "dev": true
+ },
"is-svg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz",
@@ -10289,6 +10623,68 @@
"has-symbols": "^1.0.1"
}
},
+ "is-typed-array": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.4.tgz",
+ "integrity": "sha512-ILaRgn4zaSrVNXNGtON6iFNotXW3hAPF3+0fB1usg2jFlWqo5fEDdmJkz0zBfoi7Dgskr8Khi2xZ8cXqZEfXNA==",
+ "requires": {
+ "available-typed-arrays": "^1.0.2",
+ "call-bind": "^1.0.0",
+ "es-abstract": "^1.18.0-next.1",
+ "foreach": "^2.0.5",
+ "has-symbols": "^1.0.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.18.0-next.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+ "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ },
+ "is-callable": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
+ "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA=="
+ },
+ "is-regex": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+ "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "object-inspect": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz",
+ "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw=="
+ },
+ "object.assign": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+ "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ }
+ }
+ }
+ },
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
@@ -11313,6 +11709,41 @@
"object.assign": "^4.1.0"
}
},
+ "jszip": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz",
+ "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==",
+ "requires": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "set-immediate-shim": "~1.0.1"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
"killable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@@ -11389,6 +11820,14 @@
"type-check": "~0.3.2"
}
},
+ "lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "requires": {
+ "immediate": "~3.0.5"
+ }
+ },
"lines-and-columns": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
@@ -11674,6 +12113,18 @@
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
"dev": true
},
+ "lodash.escape": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz",
+ "integrity": "sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg=",
+ "dev": true
+ },
+ "lodash.flattendeep": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
+ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=",
+ "dev": true
+ },
"lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
@@ -11686,6 +12137,12 @@
"integrity": "sha1-rXvGpOZH15yXLhuA/u968VYmeHY=",
"dev": true
},
+ "lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=",
+ "dev": true
+ },
"lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
@@ -12337,6 +12794,12 @@
"integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==",
"dev": true
},
+ "moo": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz",
+ "integrity": "sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==",
+ "dev": true
+ },
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
@@ -12377,8 +12840,7 @@
"nan": {
"version": "2.14.1",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
- "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==",
- "optional": true
+ "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw=="
},
"nanomatch": {
"version": "1.2.13",
@@ -12422,6 +12884,27 @@
"integrity": "sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q==",
"dev": true
},
+ "nearley": {
+ "version": "2.19.8",
+ "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.19.8.tgz",
+ "integrity": "sha512-te4JCrxbzLvVqUWfVOASgsbkWaFvJ6JlHTRQzfnU862bnyHGHEGX2s5OYvLAS4NDPmQvRtC2tBdV6THy6xHFyQ==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.19.0",
+ "moo": "^0.5.0",
+ "railroad-diagrams": "^1.0.0",
+ "randexp": "0.4.6",
+ "semver": "^5.4.1"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
@@ -12497,6 +12980,30 @@
"vm-browserify": "^1.0.1"
},
"dependencies": {
+ "assert": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
+ "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
+ "requires": {
+ "object-assign": "^4.1.1",
+ "util": "0.10.3"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+ "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE="
+ },
+ "util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+ "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+ "requires": {
+ "inherits": "2.0.1"
+ }
+ }
+ }
+ },
"punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
@@ -14731,6 +15238,17 @@
"react-is": "^16.8.1"
}
},
+ "prop-types-exact": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/prop-types-exact/-/prop-types-exact-1.2.0.tgz",
+ "integrity": "sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3",
+ "object.assign": "^4.1.0",
+ "reflect.ownkeys": "^0.2.0"
+ }
+ },
"protocols": {
"version": "1.4.7",
"resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.7.tgz",
@@ -14865,6 +15383,22 @@
"performance-now": "^2.1.0"
}
},
+ "railroad-diagrams": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
+ "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=",
+ "dev": true
+ },
+ "randexp": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
+ "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
+ "dev": true,
+ "requires": {
+ "discontinuous-range": "1.0.0",
+ "ret": "~0.1.10"
+ }
+ },
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -15299,6 +15833,18 @@
}
}
},
+ "react-test-renderer": {
+ "version": "16.14.0",
+ "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.14.0.tgz",
+ "integrity": "sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg==",
+ "dev": true,
+ "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",
@@ -15450,6 +15996,12 @@
"esprima": "~4.0.0"
}
},
+ "reflect.ownkeys": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz",
+ "integrity": "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=",
+ "dev": true
+ },
"regenerate": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz",
@@ -15860,6 +16412,16 @@
"inherits": "^2.0.1"
}
},
+ "rst-selector-parser": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
+ "integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=",
+ "dev": true,
+ "requires": {
+ "lodash.flattendeep": "^4.4.0",
+ "nearley": "^2.7.10"
+ }
+ },
"rsvp": {
"version": "4.8.5",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
@@ -16005,6 +16567,40 @@
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
"integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo="
},
+ "selenium-webdriver": {
+ "version": "4.0.0-alpha.8",
+ "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.8.tgz",
+ "integrity": "sha512-yPSaiWySZTEbxuuWQMDqdXh3H3N4Aiw/bSUjpkKMPWWCysfPqUncrq6FewBqdxWD1wQKzy5yWaQMGsgTY/0rCQ==",
+ "requires": {
+ "jszip": "^3.5.0",
+ "rimraf": "^2.7.1",
+ "tmp": "^0.1.0",
+ "ws": "^7.3.1"
+ },
+ "dependencies": {
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "tmp": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz",
+ "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==",
+ "requires": {
+ "rimraf": "^2.6.3"
+ }
+ },
+ "ws": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz",
+ "integrity": "sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ=="
+ }
+ }
+ },
"selfsigned": {
"version": "1.10.7",
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz",
@@ -16180,6 +16776,11 @@
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
+ "set-immediate-shim": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+ "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
+ },
"set-value": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
@@ -16319,6 +16920,14 @@
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="
},
+ "sleep": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/sleep/-/sleep-6.3.0.tgz",
+ "integrity": "sha512-+WgYl951qdUlb1iS97UvQ01pkauoBK9ML9I/CMPg41v0Ze4EyMlTgFTDDo32iYj98IYqxIjDMRd+L71lawFfpQ==",
+ "requires": {
+ "nan": "^2.14.1"
+ }
+ },
"slice-ansi": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
@@ -16933,6 +17542,72 @@
"side-channel": "^1.0.2"
}
},
+ "string.prototype.trim": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.3.tgz",
+ "integrity": "sha512-16IL9pIBA5asNOSukPfxX2W68BaBvxyiRK16H3RA/lWW9BDosh+w7f+LhomPHpXJ82QEe7w7/rY/S1CV97raLg==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.18.0-next.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+ "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ },
+ "is-callable": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
+ "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+ "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "object-inspect": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
+ "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
+ "dev": true
+ },
+ "object.assign": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+ "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ }
+ }
+ }
+ },
"string.prototype.trimend": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
@@ -17999,18 +18674,16 @@
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
},
"util": {
- "version": "0.10.3",
- "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
- "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+ "version": "0.12.3",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz",
+ "integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==",
"requires": {
- "inherits": "2.0.1"
- },
- "dependencies": {
- "inherits": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
- "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE="
- }
+ "inherits": "^2.0.3",
+ "is-arguments": "^1.0.4",
+ "is-generator-function": "^1.0.7",
+ "is-typed-array": "^1.1.3",
+ "safe-buffer": "^5.1.2",
+ "which-typed-array": "^1.1.2"
}
},
"util-deprecate": {
@@ -18877,6 +19550,70 @@
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
+ "which-typed-array": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz",
+ "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==",
+ "requires": {
+ "available-typed-arrays": "^1.0.2",
+ "call-bind": "^1.0.0",
+ "es-abstract": "^1.18.0-next.1",
+ "foreach": "^2.0.5",
+ "function-bind": "^1.1.1",
+ "has-symbols": "^1.0.1",
+ "is-typed-array": "^1.1.3"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.18.0-next.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+ "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ },
+ "is-callable": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
+ "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA=="
+ },
+ "is-regex": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+ "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "object-inspect": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz",
+ "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw=="
+ },
+ "object.assign": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+ "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+ "requires": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ }
+ }
+ }
+ },
"widest-line": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
diff --git a/final/client/package.json b/final/client/package.json
index 6eac9ac57..407af9a39 100644
--- a/final/client/package.json
+++ b/final/client/package.json
@@ -5,10 +5,13 @@
"dependencies": {
"@apollo/client": "^3.2.7",
"@reach/router": "^1.2.1",
+ "@types/enzyme": "^3.10.8",
+ "@types/enzyme-adapter-react-16": "^1.0.6",
"@types/node": "^12.12.14",
"@types/reach__router": "^1.2.6",
"@types/react": "^16.9.15",
"@types/react-dom": "^16.9.4",
+ "assert": "^2.0.0",
"emotion": "^9.2.12",
"graphql": "^14.4.2",
"polished": "^3.4.1",
@@ -16,6 +19,8 @@
"react-dom": "^16.12.0",
"react-emotion": "^9.2.12",
"react-scripts": "^3.4.1",
+ "selenium-webdriver": "^4.0.0-alpha.8",
+ "sleep": "^6.3.0",
"typescript": "^3.7.3"
},
"scripts": {
@@ -41,6 +46,8 @@
"@types/jest": "^24.0.23",
"apollo": "^2.16.3",
"artillery": "^1.6.0-26",
+ "enzyme": "^3.11.0",
+ "enzyme-adapter-react-16": "^1.15.5",
"npm-watch": "^0.6.0"
}
}
diff --git a/final/client/src/components/__tests__/button.tsx b/final/client/src/components/__tests__/button.tsx
index fbb1c3534..d282fc934 100644
--- a/final/client/src/components/__tests__/button.tsx
+++ b/final/client/src/components/__tests__/button.tsx
@@ -1,13 +1,26 @@
-import React from 'react';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import Button from "../button";
-import { render, cleanup } from '../../test-utils';
-import Button from '../button';
+Enzyme.configure({ adapter: new Adapter() });
-describe('Button', () => {
+describe("Button", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders without error', () => {
- render();
+ beforeEach(() => {
+ wrapper = Enzyme.mount();
+ });
+
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
+ });
+
+ it("renders without error", () => {
+ console.log(wrapper.debug());
+ expect(wrapper.find("button").length).toBe(1);
+ expect(wrapper.find("button").text()).toEqual("Hello World");
});
});
diff --git a/final/client/src/components/__tests__/footer.tsx b/final/client/src/components/__tests__/footer.tsx
index e85419c33..a42c559a9 100644
--- a/final/client/src/components/__tests__/footer.tsx
+++ b/final/client/src/components/__tests__/footer.tsx
@@ -1,13 +1,39 @@
-import React from 'react';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { MockedProvider } from "@apollo/client/testing";
+import Footer from "../footer";
-import { renderApollo, cleanup } from '../../test-utils';
-import Footer from '../footer';
+Enzyme.configure({ adapter: new Adapter() });
-describe('Footer', () => {
+describe("Footer", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders without error', () => {
- renderApollo();
+ beforeEach(() => {
+ wrapper = Enzyme.mount(
+
+
+
+ );
+ });
+
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
+ });
+ it("renders without error", () => {
+ var linkText = ["Home", "Cart", "Profile"];
+ expect(wrapper.find("Footer").length).toBe(1);
+ expect(wrapper.find("svg").length).toBe(4);
+ expect(wrapper.find("LogoutButton").length).toBe(1);
+ expect(
+ wrapper.find("button[data-testid='logout-button']").text()
+ ).toContain("Logout");
+ expect(wrapper.find("Link").length).toBe(3);
+ //find link text
+ wrapper.find("Link a").forEach((node, index) => {
+ expect(node.text()).toContain(linkText[index]);
+ });
});
});
diff --git a/final/client/src/components/__tests__/header.tsx b/final/client/src/components/__tests__/header.tsx
index 0a3cf51fd..f70dfd780 100644
--- a/final/client/src/components/__tests__/header.tsx
+++ b/final/client/src/components/__tests__/header.tsx
@@ -1,13 +1,26 @@
-import React from 'react';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import Header from "../header";
-import { render, cleanup } from '../../test-utils';
-import Header from '../header';
+Enzyme.configure({ adapter: new Adapter() });
-describe('Header', () => {
+describe("Header", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders without error', () => {
- render();
+ beforeEach(() => {
+ wrapper = Enzyme.mount();
+ });
+
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
+ });
+
+ it("renders without error", () => {
+ expect(wrapper.find("Header").length).toBe(1);
+ expect(wrapper.find("img").length).toBe(1);
+ expect(wrapper.find("h2").text()).toEqual("Space Explorer");
});
});
diff --git a/final/client/src/components/__tests__/launch-detail.tsx b/final/client/src/components/__tests__/launch-detail.tsx
index e1bcabbf1..136e25eba 100644
--- a/final/client/src/components/__tests__/launch-detail.tsx
+++ b/final/client/src/components/__tests__/launch-detail.tsx
@@ -1,19 +1,37 @@
-import React from 'react';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import LaunchDetail from "../launch-detail";
-import { render, cleanup } from '../../test-utils';
-import LaunchDetail from '../launch-detail';
+Enzyme.configure({ adapter: new Adapter() });
-describe('Launch Detail View', () => {
+describe("Launch Detail View", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders without error', () => {
- render(
+ beforeEach(() => {
+ wrapper = Enzyme.mount(
,
+ id={"1"}
+ site={"earth"}
+ rocket={{
+ name: "that one",
+ type: "big",
+ __typename: "Rocket",
+ id: "1",
+ }}
+ />
);
});
+
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
+ });
+
+ it("renders without error", () => {
+ expect(wrapper.find("LaunchDetail").length).toBe(1);
+ expect(wrapper.find("h3").text()).toEqual("that one (big)");
+ expect(wrapper.find("h5").text()).toEqual("earth");
+ });
});
diff --git a/final/client/src/components/__tests__/launch-tile.tsx b/final/client/src/components/__tests__/launch-tile.tsx
index 6cf72b077..e75a16b91 100644
--- a/final/client/src/components/__tests__/launch-tile.tsx
+++ b/final/client/src/components/__tests__/launch-tile.tsx
@@ -1,23 +1,41 @@
-import React from 'react';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import LaunchTile from "../launch-tile";
-import { render, cleanup } from '../../test-utils';
-import LaunchTile from '../launch-tile';
+Enzyme.configure({ adapter: new Adapter() });
-describe('Launch Tile', () => {
+describe("Launch Tile", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders without error', () => {
- render(
+ beforeEach(() => {
+ wrapper = Enzyme.mount(
,
+ />
);
});
+
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
+ });
+ it("renders without error", () => {
+ expect(wrapper.find("LaunchTile").length).toBe(1);
+ expect(wrapper.find("Link").length).toBe(1);
+ expect(wrapper.find("Location").length).toBe(1);
+ expect(wrapper.find("h3").text()).toEqual("the first one");
+ expect(wrapper.find("h5").text()).toEqual("harambe");
+ });
});
diff --git a/final/client/src/components/__tests__/loading.tsx b/final/client/src/components/__tests__/loading.tsx
index 9e30ffced..8b6337f45 100644
--- a/final/client/src/components/__tests__/loading.tsx
+++ b/final/client/src/components/__tests__/loading.tsx
@@ -1,13 +1,24 @@
-import React from 'react';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import Loading from "../loading";
-import { render, cleanup } from '../../test-utils';
-import Loading from '../loading';
+Enzyme.configure({ adapter: new Adapter() });
-describe('Loading', () => {
+describe("Loading", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders without error', () => {
- render();
+ beforeEach(() => {
+ wrapper = Enzyme.mount();
+ });
+
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
+ });
+ it("renders without error", () => {
+ expect(wrapper.find("Styled(Component)").length).toBe(1);
+ expect(wrapper.find("svg").length).toBe(1);
});
});
diff --git a/final/client/src/components/__tests__/login-form.tsx b/final/client/src/components/__tests__/login-form.tsx
index 595f3e0b1..3c66991f2 100644
--- a/final/client/src/components/__tests__/login-form.tsx
+++ b/final/client/src/components/__tests__/login-form.tsx
@@ -1,13 +1,29 @@
-import React from 'react';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import LoginForm from "../login-form";
-import { render, cleanup } from '../../test-utils';
-import LoginForm from '../login-form';
+Enzyme.configure({ adapter: new Adapter() });
-describe('Login Form', () => {
+describe("Login Form", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders without error', () => {
- render( {}}/>);
+ beforeEach(() => {
+ wrapper = Enzyme.mount( {}} />);
+ });
+
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
+ });
+ it("renders without error", () => {
+ expect(wrapper.find("LoginForm").length).toBe(1);
+ expect(wrapper.find("header").length).toBe(1);
+ expect(wrapper.find("svg").length).toBe(3);
+ expect(wrapper.find("form").length).toBe(1);
+ expect(wrapper.find("button").length).toBe(1);
+ expect(wrapper.find("h1").text()).toEqual("Space Explorer");
+ expect(wrapper.find("button").text()).toEqual("Log in");
});
});
diff --git a/final/client/src/components/__tests__/menu-item.tsx b/final/client/src/components/__tests__/menu-item.tsx
index 962ee059d..acb8373ac 100644
--- a/final/client/src/components/__tests__/menu-item.tsx
+++ b/final/client/src/components/__tests__/menu-item.tsx
@@ -1,13 +1,26 @@
-import React from 'react';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import MenuItem from "../menu-item";
-import { render, cleanup } from '../../test-utils';
-import MenuItem from '../menu-item';
+Enzyme.configure({ adapter: new Adapter() });
-describe('Menu Item', () => {
+describe("Menu Item", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders without error', () => {
- render();
+ beforeEach(() => {
+ wrapper = Enzyme.mount();
+ });
+
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
+ });
+ it("renders without error", () => {
+ expect(wrapper.find("Styled(Link)").length).toBe(1);
+ expect(wrapper.find("Location").length).toBe(1);
+ expect(wrapper.find("LocationProvider").length).toBe(1);
+ expect(wrapper.find("a").length).toBe(1);
});
});
diff --git a/final/client/src/components/__tests__/page-container.tsx b/final/client/src/components/__tests__/page-container.tsx
index 57632ffbe..b58e934d7 100644
--- a/final/client/src/components/__tests__/page-container.tsx
+++ b/final/client/src/components/__tests__/page-container.tsx
@@ -1,13 +1,24 @@
-import React from 'react';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import PageContainer from "../page-container";
-import { render, cleanup } from '../../test-utils';
-import PageContainer from '../page-container';
+Enzyme.configure({ adapter: new Adapter() });
-describe('Page Container', () => {
+describe("Page Container", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders without error', () => {
- render();
+ beforeEach(() => {
+ wrapper = Enzyme.mount();
+ });
+
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
+ });
+ it("renders without error", () => {
+ expect(wrapper.find("PageContainer").length).toBe(1);
+ expect(wrapper.find("Styled(div)").length).toBe(2);
});
});
diff --git a/final/client/src/containers/__tests__/action-button.tsx b/final/client/src/containers/__tests__/action-button.tsx
index c0911e542..06644a104 100644
--- a/final/client/src/containers/__tests__/action-button.tsx
+++ b/final/client/src/containers/__tests__/action-button.tsx
@@ -1,30 +1,60 @@
-import React from 'react';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { MockedProvider } from "@apollo/client/testing";
+import ActionButton from "../action-button";
-import { renderApollo, cleanup } from '../../test-utils';
-import ActionButton from '../action-button';
-import { cartItemsVar } from '../../cache';
+Enzyme.configure({ adapter: new Adapter() });
-describe('action button', () => {
+describe("action button", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders without error', () => {
- const { getByTestId } = renderApollo();
- expect(getByTestId('action-button')).toBeTruthy();
+ beforeEach(() => {
+ wrapper = Enzyme.mount(
+
+
+
+ );
});
- it('shows correct label', () => {
- const { getByText, container } = renderApollo();
- getByText(/add to cart/i);
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
+ });
+
+ it("renders without error", () => {
+ //Assertions
+ expect(
+ wrapper.find('Styled(button)[data-testid="action-button"]').length
+ ).toBe(1);
+ });
- // rerender with different props to same container
- cartItemsVar(['1']);
- renderApollo(, { container });
- getByText(/remove from cart/i);
- cartItemsVar([]);
+ it("Add to Cart", () => {
+ //Assertions
+ expect(wrapper.find("ActionButton").text()).toEqual("Add to Cart");
+ });
+ it("Remove from Cart", () => {
+ wrapper = Enzyme.mount(
+
+
+
+ );
+ //click action button to add to cart
+ wrapper.find("button").simulate("click");
+ wrapper.update();
+
+ //Assertions
+ expect(wrapper.find("ActionButton").text()).toEqual("Remove from Cart");
+ });
+ it("Cancel this Trip", () => {
+ wrapper = Enzyme.mount(
+
+
+
+ );
- // rerender with different props to same container
- renderApollo(, { container });
- getByText(/cancel this trip/i);
+ //Assertions
+ expect(wrapper.find("ActionButton").text()).toEqual("Cancel This Trip");
});
});
diff --git a/final/client/src/containers/__tests__/book-trips.tsx b/final/client/src/containers/__tests__/book-trips.tsx
index 961787063..17dbd00c3 100644
--- a/final/client/src/containers/__tests__/book-trips.tsx
+++ b/final/client/src/containers/__tests__/book-trips.tsx
@@ -1,67 +1,119 @@
-import React from 'react';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { MockedProvider } from "@apollo/client/testing";
+import BookTrips, { BOOK_TRIPS } from "../book-trips";
+import { GET_LAUNCH } from "../cart-item";
+import { wait } from "@testing-library/react";
+import { cache, isLoggedInVar } from "../../cache";
-import {
- renderApollo,
- cleanup,
- fireEvent,
- waitForElement,
-} from '../../test-utils';
-import BookTrips, { BOOK_TRIPS } from '../book-trips';
-import { GET_LAUNCH } from '../cart-item';
+Enzyme.configure({ adapter: new Adapter() });
const mockLaunch = {
- __typename: 'Launch',
+ __typename: "Launch",
id: 1,
isBooked: true,
rocket: {
id: 1,
- name: 'tester',
+ name: "tester",
},
mission: {
- name: 'test mission',
- missionPatch: '/',
+ name: "test mission",
+ missionPatch: "/",
},
};
-describe('book trips', () => {
+describe("book trips", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders without error', () => {
- const { getByTestId } = renderApollo();
- expect(getByTestId('book-button')).toBeTruthy();
+ beforeEach(() => {
+ wrapper = Enzyme.mount(
+
+
+
+ );
+ });
+
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
+ });
+ it("renders without error", () => {
+ //Assertions
+ expect(
+ wrapper.find('Styled(button)[data-testid="book-button"]').length
+ ).toBe(1);
});
- it('completes mutation and shows message', async () => {
+ it("completes mutation and shows message", async () => {
let mocks = [
{
- request: { query: BOOK_TRIPS, variables: { launchIds: ['1'] } },
+ request: { query: BOOK_TRIPS, variables: { launchIds: ["1"] } },
result: {
data: {
- bookTrips: [{ success: true, message: 'success!', launches: [] }],
+ bookTrips: [{ success: true, message: "success!", launches: [] }],
},
},
},
{
// we need this query for refetchQueries
- request: { query: GET_LAUNCH, variables: { launchId: '1' } },
+ request: { query: GET_LAUNCH, variables: { launchId: "1" } },
result: { data: { launch: mockLaunch } },
},
];
- const { getByTestId } = renderApollo(
- ,
- { mocks, addTypename: false },
- );
- fireEvent.click(getByTestId('book-button'));
+ wrapper = Enzyme.mount(
+
+
+
+ );
- // Let's wait until our mocked mutation resolves and
- // the component re-renders.
- // getByTestId throws an error if it cannot find an element with the given ID
- // and waitForElement will wait until the callback doesn't throw an error
- await waitForElement(() => getByTestId('message'));
+ //click action button to add to cart
+ wrapper.find('Styled(button)[data-testid="book-button"]').simulate("click");
+ //wait for onClick function to finish
+ await wait(() => {
+ wrapper.update();
+ //Assertions
+ expect(wrapper.find('[data-testid="message"]').length).toBe(1);
+ });
});
- // >>>> TODO
- it('correctly updates cache', () => {});
+ it("correctly updates cache", async () => {
+ expect(isLoggedInVar()).toBeFalsy();
+
+ let mocks = [
+ {
+ request: { query: BOOK_TRIPS, variables: { launchIds: ["1"] } },
+ result: {
+ data: {
+ bookTrips: [{ success: true, message: "success!", launches: [] }],
+ },
+ },
+ },
+ {
+ // we need this query for refetchQueries
+ request: { query: GET_LAUNCH, variables: { launchId: "1" } },
+ result: { data: { launch: mockLaunch } },
+ },
+ ];
+ wrapper = Enzyme.mount(
+
+
+
+ );
+
+ //click action button to add to cart
+ wrapper.find('Styled(button)[data-testid="book-button"]').simulate("click");
+ //wait for onClick function to finish
+
+ // login is done if loader is gone
+ await wait(() => {
+ wrapper.update();
+ //Assertions
+ expect(
+ wrapper.find('Styled(button)[data-testid="book-button"]').length
+ ).toBe(0);
+ });
+ });
});
diff --git a/final/client/src/containers/__tests__/cart-item.tsx b/final/client/src/containers/__tests__/cart-item.tsx
index 4b1dce6a8..e124c4923 100644
--- a/final/client/src/containers/__tests__/cart-item.tsx
+++ b/final/client/src/containers/__tests__/cart-item.tsx
@@ -1,66 +1,79 @@
-import React from 'react';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { MockedProvider } from "@apollo/client/testing";
+import CartItem, { GET_LAUNCH } from "../cart-item";
+import { wait } from "@testing-library/react";
-import {
- renderApollo,
- cleanup,
- waitForElement,
-} from '../../test-utils';
-import CartItem, { GET_LAUNCH } from '../cart-item';
+Enzyme.configure({ adapter: new Adapter() });
const mockLaunch = {
- __typename: 'Launch',
+ __typename: "Launch",
id: 1,
isBooked: true,
rocket: {
id: 1,
- name: 'tester',
+ name: "tester",
},
mission: {
- name: 'test mission',
- missionPatch: '/',
+ name: "test mission",
+ missionPatch: "/",
},
};
-describe('cart item', () => {
+describe("cart item", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('queries item and renders without error', () => {
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
+ });
+ it("queries item and renders without error", async () => {
let mocks = [
{
- request: { query: GET_LAUNCH, variables: { launchId: '1' } },
+ request: { query: GET_LAUNCH, variables: { launchId: "1" } },
result: { data: { launch: mockLaunch } },
},
];
-
// since we know the name of the mission, and know that name
// will be rendered at some point, we can use getByText
- const { getByText } = renderApollo(, {
- mocks,
- addTypename: false,
- });
+ wrapper = Enzyme.mount(
+
+
+
+ );
// check the loading state
- getByText(/loading/i);
+ expect(wrapper.find("CartItem").text()).toEqual("Loading...");
- return waitForElement(() => getByText(/test mission/i));
+ //wait for onClick function to finish
+ await wait(() => {
+ wrapper.update();
+ //Assertions
+ expect(wrapper.find("CartItem h3").text()).toEqual("test mission");
+ });
});
- it('renders with error state', () => {
+ it("renders with error state", async () => {
let mocks = [
{
- request: { query: GET_LAUNCH, variables: { launchId: 1 } },
- error: new Error('aw shucks'),
+ request: { query: GET_LAUNCH, variables: { launchId: "1" } },
+ error: new Error("aw shucks"),
},
];
- // since we know the error message, we can use getByText
- // to recognize the error
- const { getByText } = renderApollo(, {
- mocks,
- addTypename: false,
- });
+ wrapper = Enzyme.mount(
+
+
+
+ );
- waitForElement(() => getByText(/error: aw shucks/i));
+ //wait for onClick function to finish
+ await wait(() => {
+ wrapper.update();
+ //Assertions
+ expect(wrapper.find("CartItem").text()).toEqual("ERROR: aw shucks");
+ });
});
-});
\ No newline at end of file
+});
diff --git a/final/client/src/containers/__tests__/logout-button.tsx b/final/client/src/containers/__tests__/logout-button.tsx
index 323b064df..590218c0c 100644
--- a/final/client/src/containers/__tests__/logout-button.tsx
+++ b/final/client/src/containers/__tests__/logout-button.tsx
@@ -1,25 +1,46 @@
-import React from 'react';
-import LogoutButton from '../logout-button';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { MockedProvider } from "@apollo/client/testing";
+import LogoutButton from "../logout-button";
+import { cache, isLoggedInVar } from "../../cache";
-import { renderApollo, cleanup, fireEvent } from '../../test-utils';
-import { cache, isLoggedInVar } from '../../cache';
+Enzyme.configure({ adapter: new Adapter() });
-describe('logout button', () => {
+describe("logout button", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders logout button', async () => {
- renderApollo();
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
});
- it('complete logout', async () => {
+ it("renders logout button", async () => {
+ wrapper = Enzyme.mount(
+
+
+
+ );
+ //Assertions
+ expect(wrapper.find("LogoutButton").length).toBe(1);
+ });
+
+ it("complete logout", async () => {
isLoggedInVar(true);
- localStorage.setItem('token', 'testTokenValue');
- localStorage.setItem('userId', 'abc123');
- const { getByTestId } = renderApollo(, { cache });
- fireEvent.click(getByTestId('logout-button'));
+ localStorage.setItem("token", "testTokenValue");
+ localStorage.setItem("userId", "abc123");
+ wrapper = Enzyme.mount(
+
+
+
+ );
+ wrapper.find('button[data-testid="logout-button"]').simulate("click");
+ //wait for onClick function to finish
+ wrapper.update();
+
expect(isLoggedInVar()).toBeFalsy();
- expect(localStorage.getItem('token')).toBeNull();
- expect(localStorage.getItem('userId')).toBeNull();
+ expect(localStorage.getItem("token")).toBeNull();
+ expect(localStorage.getItem("userId")).toBeNull();
});
});
diff --git a/final/client/src/containers/book-trips.tsx b/final/client/src/containers/book-trips.tsx
index e7f1f304b..81f4b60c3 100644
--- a/final/client/src/containers/book-trips.tsx
+++ b/final/client/src/containers/book-trips.tsx
@@ -1,10 +1,10 @@
-import React from 'react';
-import { gql, useMutation } from '@apollo/client';
+import React from "react";
+import { gql, useMutation } from "@apollo/client";
-import Button from '../components/button';
-import { cartItemsVar } from '../cache';
-import * as GetCartItemsTypes from '../pages/__generated__/GetCartItems';
-import * as BookTripsTypes from './__generated__/BookTrips';
+import Button from "../components/button";
+import { cartItemsVar } from "../cache";
+import * as GetCartItemsTypes from "../pages/__generated__/GetCartItems";
+import * as BookTripsTypes from "./__generated__/BookTrips";
export const BOOK_TRIPS = gql`
mutation BookTrips($launchIds: [ID]!) {
@@ -25,26 +25,23 @@ const BookTrips: React.FC = ({ cartItems }) => {
const [bookTrips, { data }] = useMutation<
BookTripsTypes.BookTrips,
BookTripsTypes.BookTripsVariables
- >(
- BOOK_TRIPS,
- {
- variables: { launchIds: cartItems },
- }
- );
+ >(BOOK_TRIPS, {
+ variables: { launchIds: cartItems },
+ });
- return data && data.bookTrips && !data.bookTrips.success
- ? {data.bookTrips.message}
- : (
-
- );
-}
+ return data && data.bookTrips && !data.bookTrips.success ? (
+ {data.bookTrips.message}
+ ) : (
+
+ );
+};
-export default BookTrips;
\ No newline at end of file
+export default BookTrips;
diff --git a/final/client/src/pages/__tests__/cart.tsx b/final/client/src/pages/__tests__/cart.tsx
index a175907c7..64221f331 100644
--- a/final/client/src/pages/__tests__/cart.tsx
+++ b/final/client/src/pages/__tests__/cart.tsx
@@ -1,47 +1,72 @@
-import React from 'react';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { MockedProvider } from "@apollo/client/testing";
+import { wait } from "../../test-utils";
+import Cart from "../cart";
+import { GET_LAUNCH } from "../../containers/cart-item";
+import { cache, cartItemsVar } from "../../cache";
-import {
- renderApollo,
- cleanup,
- waitForElement,
-} from '../../test-utils';
-import Cart from '../cart';
-import { GET_LAUNCH } from '../../containers/cart-item';
-import { cache, cartItemsVar } from '../../cache';
+Enzyme.configure({ adapter: new Adapter() });
const mockLaunch = {
- __typename: 'Launch',
+ __typename: "Launch",
id: 1,
isBooked: true,
rocket: {
id: 1,
- name: 'tester',
+ name: "tester",
},
mission: {
- name: 'test mission',
- missionPatch: '/',
+ name: "test mission",
+ missionPatch: "/",
},
};
-describe('Cart Page', () => {
+describe("Cart Page", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders with message for empty carts', () => {
- const { getByTestId } = renderApollo(, { cache });
- return waitForElement(() => getByTestId('empty-message'));
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
});
- it('renders cart', () => {
+ it("renders with message for empty carts", async () => {
+ wrapper = Enzyme.mount(
+
+
+
+ );
+ expect(wrapper.find('Cart p[data-testid="empty-message"]').length).toBe(1);
+ expect(wrapper.find('Cart p[data-testid="empty-message"]').text()).toEqual(
+ "No items in your cart"
+ );
+ });
+
+ it("renders cart", async () => {
let mocks = [
{
- request: { query: GET_LAUNCH, variables: { launchId: '1' } },
+ request: { query: GET_LAUNCH, variables: { launchId: "1" } },
result: { data: { launch: mockLaunch } },
},
];
- const { getByTestId } = renderApollo(, { cache, mocks });
- cartItemsVar(['1']);
- return waitForElement(() => getByTestId('book-button'));
+ wrapper = Enzyme.mount(
+
+
+
+ );
+ cartItemsVar(["1"]);
+ await wait(() => {
+ wrapper.update();
+ //Assertions
+ expect(
+ wrapper.find('BookTrips button[data-testid="book-button"]').length
+ ).toBe(1);
+ expect(
+ wrapper.find('BookTrips button[data-testid="book-button"]').text()
+ ).toEqual("Book All");
+ });
});
});
diff --git a/final/client/src/pages/__tests__/launch.tsx b/final/client/src/pages/__tests__/launch.tsx
index 1274051e8..51f4d4229 100644
--- a/final/client/src/pages/__tests__/launch.tsx
+++ b/final/client/src/pages/__tests__/launch.tsx
@@ -1,47 +1,57 @@
-import React from 'react';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { MockedProvider } from "@apollo/client/testing";
+import Launch, { GET_LAUNCH_DETAILS } from "../launch";
+import { wait } from "../../test-utils";
-import {
- renderApollo,
- cleanup,
- waitForElement,
-} from '../../test-utils';
-import Launch, { GET_LAUNCH_DETAILS } from '../launch';
+Enzyme.configure({ adapter: new Adapter() });
const mockLaunch = {
- __typename: 'Launch',
+ __typename: "Launch",
id: 1,
isBooked: true,
rocket: {
- __typename: 'Rocket',
+ __typename: "Rocket",
id: 1,
- name: 'tester',
- type: 'test',
+ name: "tester",
+ type: "test",
},
mission: {
- __typename: 'Mission',
+ __typename: "Mission",
id: 1,
- name: 'test mission',
- missionPatch: '/',
+ name: "test mission",
+ missionPatch: "/",
},
- site: 'earth',
+ site: "earth",
isInCart: false,
};
-describe('Launch Page', () => {
+describe("Launch Page", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders launch', async () => {
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
+ });
+
+ it("renders launch", async () => {
const mocks = [
{
request: { query: GET_LAUNCH_DETAILS, variables: { launchId: 1 } },
result: { data: { launch: mockLaunch } },
},
];
- const { getByText } = await renderApollo(, {
- mocks,
- resolvers: {}
+ wrapper = Enzyme.mount(
+
+
+
+ );
+ await wait(() => {
+ wrapper.update();
+ //Assertions
+ expect(wrapper.find("Launch h2").text()).toEqual("test mission");
});
- await waitForElement(() => getByText(/test mission/i));
});
});
diff --git a/final/client/src/pages/__tests__/launches.tsx b/final/client/src/pages/__tests__/launches.tsx
index 8ebb1e093..79e3f84fa 100644
--- a/final/client/src/pages/__tests__/launches.tsx
+++ b/final/client/src/pages/__tests__/launches.tsx
@@ -1,38 +1,43 @@
-import React from 'react';
-import { InMemoryCache } from '@apollo/client';
+import React from "react";
+import { InMemoryCache } from "@apollo/client";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { MockedProvider } from "@apollo/client/testing";
+import { wait } from "../../test-utils";
+import Launches, { GET_LAUNCHES } from "../launches";
-import {
- renderApollo,
- cleanup,
- waitForElement,
-} from '../../test-utils';
-import Launches, { GET_LAUNCHES } from '../launches';
+Enzyme.configure({ adapter: new Adapter() });
const mockLaunch = {
- __typename: 'Launch',
+ __typename: "Launch",
id: 1,
isBooked: true,
rocket: {
- __typename: 'Rocket',
+ __typename: "Rocket",
id: 1,
- name: 'tester',
- type: 'test',
+ name: "tester",
+ type: "test",
},
mission: {
- __typename: 'Mission',
+ __typename: "Mission",
id: 1,
- name: 'test mission',
- missionPatch: '/',
+ name: "test mission",
+ missionPatch: "/",
},
- site: 'earth',
+ site: "earth",
isInCart: false,
};
-describe('Launches Page', () => {
+describe("Launches Page", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders launches', async () => {
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
+ });
+
+ it("renders launches", async () => {
const cache = new InMemoryCache({ addTypename: false });
const mocks = [
{
@@ -40,7 +45,7 @@ describe('Launches Page', () => {
result: {
data: {
launches: {
- cursor: '123',
+ cursor: "123",
hasMore: true,
launches: [mockLaunch],
},
@@ -48,10 +53,15 @@ describe('Launches Page', () => {
},
},
];
- const { getByText } = await renderApollo(, {
- mocks,
- cache,
+ wrapper = Enzyme.mount(
+
+
+
+ );
+ await wait(() => {
+ wrapper.update();
+ //Assertions
+ expect(wrapper.find("Launches h3").text()).toEqual("test mission");
});
- await waitForElement(() => getByText(/test mission/i));
});
});
diff --git a/final/client/src/pages/__tests__/login.tsx b/final/client/src/pages/__tests__/login.tsx
index 1497a7147..c0f12b80b 100644
--- a/final/client/src/pages/__tests__/login.tsx
+++ b/final/client/src/pages/__tests__/login.tsx
@@ -1,53 +1,68 @@
-import React from 'react';
-
-import {
- renderApollo,
- cleanup,
- fireEvent,
- waitForElement,
-} from '../../test-utils';
-import Login, {LOGIN_USER} from '../login';
-import { cache, isLoggedInVar } from '../../cache';
-
-describe('Login Page', () => {
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { MockedProvider } from "@apollo/client/testing";
+import { wait } from "../../test-utils";
+import Login, { LOGIN_USER } from "../login";
+import { cache, isLoggedInVar } from "../../cache";
+
+Enzyme.configure({ adapter: new Adapter() });
+
+describe("Login Page", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders login page', async () => {
- renderApollo();
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
});
- it('fires login mutation and updates cache after done', async () => {
+ it("renders login page", async () => {
+ wrapper = Enzyme.mount(
+
+
+
+ );
+ expect(wrapper.find("Login").length).toBe(1);
+ });
+
+ it("fires login mutation and updates cache after done", async () => {
expect(isLoggedInVar()).toBeFalsy();
const mocks = [
{
- request: {query: LOGIN_USER, variables: {email: 'a@a.a'}},
+ request: { query: LOGIN_USER, variables: { email: "a@a.a" } },
result: {
data: {
login: {
- id: 'abc123',
- token: 'def456',
+ id: "abc123",
+ token: "def456",
},
},
},
},
];
- const {getByText, getByTestId} = await renderApollo(, {
- mocks,
- cache,
- });
+ wrapper = Enzyme.mount(
+
+
+
+ );
- fireEvent.change(getByTestId('login-input'), {
- target: {value: 'a@a.a'},
- });
+ wrapper
+ .find('input[data-testid="login-input"]')
+ .simulate("change", { target: { value: "a@a.a" } });
- fireEvent.click(getByText(/log in/i));
+ wrapper.update();
- // login is done if loader is gone
- await waitForElement(() => getByText(/log in/i));
+ wrapper.find("Login button").simulate("submit");
+
+ wrapper.update();
- expect(isLoggedInVar()).toBeTruthy();
+ // login is done if loader is gone
+ await wait(() => {
+ //Assertions
+ expect(isLoggedInVar()).toBeTruthy();
+ });
});
});
diff --git a/final/client/src/pages/__tests__/profile.tsx b/final/client/src/pages/__tests__/profile.tsx
index f11fba69d..c7f7ea926 100644
--- a/final/client/src/pages/__tests__/profile.tsx
+++ b/final/client/src/pages/__tests__/profile.tsx
@@ -1,41 +1,46 @@
-import React from 'react';
+import React from "react";
+import Enzyme from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { MockedProvider } from "@apollo/client/testing";
+import { wait } from "../../test-utils";
+import Profile, { GET_MY_TRIPS } from "../profile";
-import {
- renderApollo,
- cleanup,
- waitForElement,
-} from '../../test-utils';
-import Profile, { GET_MY_TRIPS } from '../profile';
+Enzyme.configure({ adapter: new Adapter() });
const mockLaunch = {
- __typename: 'Launch',
+ __typename: "Launch",
id: 1,
isBooked: true,
rocket: {
- __typename: 'Rocket',
+ __typename: "Rocket",
id: 1,
- name: 'tester',
+ name: "tester",
},
mission: {
- __typename: 'Mission',
+ __typename: "Mission",
id: 1,
- name: 'test mission',
- missionPatch: '/',
+ name: "test mission",
+ missionPatch: "/",
},
};
const mockMe = {
- __typename: 'User',
+ __typename: "User",
id: 1,
- email: 'a@a.a',
+ email: "a@a.a",
trips: [mockLaunch],
};
-describe('Profile Page', () => {
+describe("Profile Page", () => {
// automatically unmount and cleanup DOM after the test is finished.
- afterEach(cleanup);
+ let wrapper: Enzyme.ReactWrapper;
- it('renders profile page', async () => {
+ afterEach(() => {
+ expect.hasAssertions();
+ wrapper.unmount();
+ });
+
+ it("renders profile page", async () => {
const mocks = [
{
request: { query: GET_MY_TRIPS },
@@ -43,9 +48,16 @@ describe('Profile Page', () => {
},
];
- const { getByText } = renderApollo(, { mocks });
-
+ wrapper = Enzyme.mount(
+
+
+
+ );
// if the profile renders, it will have the list of missions booked
- await waitForElement(() => getByText(/test mission/i));
+ await wait(() => {
+ wrapper.update();
+ //Assertions
+ expect(wrapper.find("Profile h3").text()).toEqual("test mission");
+ });
});
});
diff --git a/final/client/src/pages/__tests__/selenium-home-page.tsx b/final/client/src/pages/__tests__/selenium-home-page.tsx
new file mode 100644
index 000000000..bc766617f
--- /dev/null
+++ b/final/client/src/pages/__tests__/selenium-home-page.tsx
@@ -0,0 +1,36 @@
+import { cleanup } from '../../test-utils';
+
+const {Builder, By, Key, until} = require('selenium-webdriver');
+const assert = require("assert");
+const sleep = require("sleep");
+
+const appURL = "http://localhost:3000";
+const testEmail = "Test@test.com";
+
+
+describe('Main Page', () => {
+ // automatically unmount and cleanup DOM after the test is finished.
+ afterEach(cleanup);
+
+ it('Can reach home page after logging in', async () => {
+ let driver = new Builder().forBrowser('firefox').build();
+ // driver = await driver.build();
+ driver.get(appURL);
+ //Get the email field,
+ let emailField = await driver.findElement(By.name("email"));
+ //Send keys to the input
+ await emailField.sendKeys(testEmail);
+ //submit / login
+ await emailField.sendKeys(Key.ENTER);
+ try{
+ //verify we got to the homepage
+ let email = await driver.wait(until.elementLocated(By.className("css-1sykydy")), 10000);
+ let emailText = await email.getText();
+ assert.equal(emailText, "TEST@TEST.COM", "Able to login and reach the home page");
+ }finally{
+ await driver.quit();
+ }
+ }, 30000); //timeout after 30seconds
+
+
+});
diff --git a/final/client/src/pages/__tests__/selenium-launches.tsx b/final/client/src/pages/__tests__/selenium-launches.tsx
new file mode 100644
index 000000000..b9d8546af
--- /dev/null
+++ b/final/client/src/pages/__tests__/selenium-launches.tsx
@@ -0,0 +1,317 @@
+import { cleanup, wait } from "../../test-utils";
+
+const { Builder, By, Key, until } = require("selenium-webdriver");
+const assert = require("assert");
+const sleep = require("sleep");
+
+const appURL = "http://localhost:3000";
+const testEmail = "Test@test.com";
+
+describe("Launch Tests: ", () => {
+ // automatically unmount and cleanup DOM after the test is finished.
+ afterEach(cleanup);
+
+ it("Launches load after logging into the application", async () => {
+ //define webdriver
+ let driver = new Builder().forBrowser("firefox").build();
+ driver.get(appURL);
+ //Get the email field,
+ let emailField = await driver.wait(
+ until.elementLocated(By.name("email")),
+ 10000
+ );
+ //Send keys to the input
+ await emailField.sendKeys(testEmail);
+ //submit / login
+ await emailField.sendKeys(Key.ENTER);
+
+ try {
+ //grab the first launch and verify its loaded
+ let launchOne = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/div[2]/a[1]')),
+ 10000
+ );
+ let launchOneText = await launchOne.findElement(By.tagName("h3"));
+ launchOneText = await launchOneText.getText();
+ // sleep.sleep(3);
+ //verify that the launch is there
+ assert.equal(
+ launchOneText,
+ "Starlink-15 (v1.0)",
+ "Launch one loaded on home page"
+ );
+
+ //grab the first launch text
+ let launchTwenty = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/div[2]/a[20]')),
+ 10000
+ );
+ let launchTwentyText = await launchTwenty.findElement(By.tagName("h3"));
+ launchTwentyText = await launchTwentyText.getText();
+ // sleep.sleep(3);
+ //verify that the launch is there
+ assert.equal(
+ launchTwentyText,
+ "Starlink 4",
+ "Launch twenty loaded on home page"
+ );
+ } finally {
+ await driver.quit();
+ }
+ }, 30000); //timeout after 30seconds
+
+ it("Can View a launch", async () => {
+ //define webdriver
+ let driver = new Builder().forBrowser("firefox").build();
+ driver.get(appURL);
+ //Get the email field,
+ let emailField = await driver.wait(
+ until.elementLocated(By.name("email")),
+ 10000
+ );
+ //Send keys to the input
+ await emailField.sendKeys(testEmail);
+ //submit / login
+ await emailField.sendKeys(Key.ENTER);
+
+ try {
+ //grab the first launch text
+ let launchOne = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/div[2]/a[1]')),
+ 10000
+ );
+ await launchOne.click();
+ let url = await driver.getCurrentUrl();
+ // sleep.sleep(3);
+ //verify that we are on the correct url
+ assert.equal(
+ url,
+ appURL + "/launch/109",
+ "Can View the first launch on the list"
+ );
+ } finally {
+ await driver.quit();
+ }
+ }, 30000); //timeout after 30seconds
+
+ it("Can add launch to cart", async () => {
+ //define webdriver
+ let driver = new Builder().forBrowser("firefox").build();
+ driver.get(appURL);
+ //Get the email field,
+ let emailField = await driver.wait(
+ until.elementLocated(By.name("email")),
+ 10000
+ );
+ //Send keys to the input
+ await emailField.sendKeys(testEmail);
+ //submit / login
+ await emailField.sendKeys(Key.ENTER);
+
+ try {
+ //grab the first launch text
+ let launchOne = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/div[2]/a[1]')),
+ 10000
+ );
+ await launchOne.click();
+ // sleep.sleep(3);
+ //find the add to cart button and click it
+ let cartButton = await driver.wait(
+ until.elementLocated(By.className("css-wwcn44")),
+ 10000
+ );
+ await cartButton.click();
+
+ //navigate to the cart page and verify that the launch is there
+ //grab the cart icon
+ let cartIcon = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/footer/div/a[2]')),
+ 10000
+ );
+ //simulate a click
+ await cartIcon.click();
+ //grab the new url
+ let url = await driver.getCurrentUrl();
+ //verify it is the cart page.
+ assert.equal(url, appURL + "/cart", "Able to reach the cart via icon");
+
+ //grab the first launch and verify its loaded
+ let launchOneOnCartPage = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/div[2]/a')),
+ 10000
+ );
+ let launchOneTextOnCartPage = await launchOneOnCartPage.findElement(
+ By.tagName("h3")
+ );
+ launchOneTextOnCartPage = await launchOneTextOnCartPage.getText();
+ //verify that the launch is there
+ assert.equal(
+ launchOneTextOnCartPage,
+ "Starlink-15 (v1.0)",
+ "Launch one was added to cart"
+ );
+ } finally {
+ await driver.quit();
+ }
+ }, 30000); //timeout after 30seconds
+
+ it("Can remove launch from cart", async () => {
+ //define webdriver
+ let driver = new Builder().forBrowser("firefox").build();
+ driver.get(appURL);
+ //Get the email field,
+ let emailField = await driver.wait(
+ until.elementLocated(By.name("email")),
+ 10000
+ );
+ //Send keys to the input
+ await emailField.sendKeys(testEmail);
+ //submit / login
+ await emailField.sendKeys(Key.ENTER);
+
+ try {
+ //grab the first launch text
+ let launchOne = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/div[2]/a[1]')),
+ 10000
+ );
+ await launchOne.click();
+ // sleep.sleep(3);
+ //find the add to cart button and click it
+ let cartButton = await driver.wait(
+ until.elementLocated(By.className("css-wwcn44")),
+ 10000
+ );
+ await cartButton.click();
+
+ //navigate to the cart page and verify that the launch is there
+ //grab the cart icon
+ let cartIcon = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/footer/div/a[2]')),
+ 10000
+ );
+ //simulate a click
+ await cartIcon.click();
+ //grab the new url
+ let url = await driver.getCurrentUrl();
+ //verify it is the cart page.
+ assert.equal(url, appURL + "/cart", "Able to reach the cart via icon");
+
+ //grab the first launch and verify its loaded
+ let launchOneOnCartPage = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/div[2]/a')),
+ 10000
+ );
+ let launchOneTextOnCartPage = await launchOneOnCartPage.findElement(
+ By.tagName("h3")
+ );
+ launchOneTextOnCartPage = await launchOneTextOnCartPage.getText();
+ //verify that the launch is there
+ assert.equal(
+ launchOneTextOnCartPage,
+ "Starlink-15 (v1.0)",
+ "Launch one was added to cart"
+ );
+
+ //navigate back to the launch page
+ await launchOneOnCartPage.click();
+ cartButton = await driver.wait(
+ until.elementLocated(By.className("css-wwcn44")),
+ 10000
+ );
+ await cartButton.click(); //remove from cart
+
+ //go back to cart page
+ cartIcon = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/footer/div/a[2]')),
+ 10000
+ );
+ //simulate a click
+ await cartIcon.click();
+
+ //verify that there is no launch in the cart
+ let emptyCartMessage = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/div[2]/p')),
+ 10000
+ );
+ let emptyCartMessageText = await emptyCartMessage.getText();
+ assert.equal(
+ emptyCartMessageText,
+ "No items in your cart",
+ "Verified that launches can be removed from a cart"
+ );
+ } finally {
+ await driver.quit();
+ }
+ }, 30000); //timeout after 30seconds
+
+ //STATUS: when clicking book launch, test fails and errors out with database query error.
+ it("Can book a launch", async () => {
+ //define webdriver
+ let driver = new Builder().forBrowser("firefox").build();
+ driver.get(appURL);
+ //Get the email field,
+ let emailField = await driver.wait(
+ until.elementLocated(By.name("email")),
+ 10000
+ );
+ //Send keys to the input
+ await emailField.sendKeys(testEmail);
+ //submit / login
+ await emailField.sendKeys(Key.ENTER);
+
+ try {
+ //grab the first launch text
+ let launchOne = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/div[2]/a[1]')),
+ 10000
+ );
+
+ await launchOne.click();
+
+ //find the add to cart button and click it
+ let cartButton = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/div[2]/div[3]/button')),
+ 10000
+ );
+
+ await cartButton.click();
+
+ //navigate to the cart page and verify that the launch is there
+ //grab the cart icon
+ let cartIcon = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/footer/div/a[2]')),
+ 10000
+ );
+ //simulate a click
+ await cartIcon.click();
+
+ //grab the new url
+ let url = await driver.getCurrentUrl();
+ //verify it is the cart page.
+ assert.equal(url, appURL + "/cart", "Able to reach the cart via icon");
+ //find the book launches button
+ let bookLaunchesButton = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/div[2]/button')),
+ 10000
+ );
+ //click the button
+ await bookLaunchesButton.click();
+ //get cart url
+ await driver.get(driver.getCurrentUrl());
+ let emptyCartMessage = await driver.wait(
+ until.elementLocated(By.xpath('//*[@id="root"]/div[2]/p'))
+ );
+
+ let emptyCartMessageText = await emptyCartMessage.getText();
+ assert.equal(
+ emptyCartMessageText,
+ "No items in your cart",
+ "Verified that launches can be removed from a cart"
+ );
+ } finally {
+ await driver.quit();
+ }
+ }, 30000); //timeout after 30seconds
+});
diff --git a/final/client/src/pages/__tests__/selenium-login.tsx b/final/client/src/pages/__tests__/selenium-login.tsx
new file mode 100644
index 000000000..c5e1070e5
--- /dev/null
+++ b/final/client/src/pages/__tests__/selenium-login.tsx
@@ -0,0 +1,40 @@
+import { cleanup } from '../../test-utils';
+
+const {Builder, By, Key, until} = require('selenium-webdriver');
+const assert = require("assert");
+const sleep = require("sleep");
+
+
+describe('Login from Main Page', () => {
+ // automatically unmount and cleanup DOM after the test is finished.
+ afterEach(cleanup);
+
+ it('Verify we can login', async () => {
+
+ //launches firefox browser
+ let driver = await new Builder().forBrowser('firefox').build();
+ try {
+ //Go to the main page
+ await driver.get('http://localhost:3000');
+
+ //Get the email field,
+ let emailField = await driver.findElement(By.name("email"));
+
+ //Send keys to the input
+ await emailField.sendKeys("Test@test.com");
+ //submit / login
+ await emailField.sendKeys(Key.ENTER);
+
+ //verify we got to the homepage
+ let email = await driver.wait(until.elementLocated(By.className("css-1sykydy")), 10000);
+ let emailText = await email.getText();
+ assert.equal(emailText, "TEST@TEST.COM", "Able to login and reach the home page");
+
+ } finally {
+ await driver.quit();
+ }
+
+}, 30000); //timeout after 30seconds
+
+
+});
diff --git a/final/client/src/pages/__tests__/selenium-main-page.tsx b/final/client/src/pages/__tests__/selenium-main-page.tsx
new file mode 100644
index 000000000..bace30c01
--- /dev/null
+++ b/final/client/src/pages/__tests__/selenium-main-page.tsx
@@ -0,0 +1,111 @@
+import { cleanup } from "../../test-utils";
+
+const { Builder, By, Key, until } = require("selenium-webdriver");
+const assert = require("assert");
+const sleep = require("sleep");
+
+describe("Main Page", () => {
+ // automatically unmount and cleanup DOM after the test is finished.
+ afterEach(cleanup);
+
+ it("Main page renders properly", async () => {
+ // Open firefox browser
+ let driver = await new Builder().forBrowser("firefox").build();
+ try {
+ //Go to the main page
+ await driver.get("http://localhost:3000");
+ let url = await driver.getCurrentUrl();
+ assert.equal(url, "http://localhost:3000/", "Able to access main page");
+
+ //Verify there is a title
+ // let title = await driver.findElement(By.className("css-1r0stgl"));
+ let title = await driver.wait(
+ until.elementLocated(By.className("css-1r0stgl")),
+ 10000
+ ); //make sure title is rendered on the screen before testing
+ title = await title.getText();
+ assert.equal(title, "Space Explorer", "The main page has a title");
+ } finally {
+ await driver.quit();
+ }
+ }, 30000); //timeout after 30seconds
+
+ it("Main page email input works properly", async () => {
+ //launches firefox browser
+ let driver = await new Builder().forBrowser("firefox").build();
+ try {
+ //Go to the main page
+ await driver.get("http://localhost:3000");
+
+ //Get the email field, should be empty
+ let emailField = await driver.findElement(By.name("email"));
+ let emailText = await emailField.getAttribute("value");
+ assert.equal(emailText, "", "Email field is on the page and empty");
+
+ //Send keys to the input
+ await emailField.sendKeys("Test@test.com");
+ emailText = await emailField.getAttribute("value");
+ assert.equal(
+ emailText,
+ "Test@test.com",
+ "Email field is able to receive keyboard input"
+ );
+ //remove n keys from input
+ let n = 3;
+ for (let i = 0; i < n; i++) {
+ await emailField.sendKeys(Key.BACK_SPACE);
+ }
+ emailText = await emailField.getAttribute("value");
+ assert.equal(
+ emailText,
+ "Test@test.",
+ "Email field is able to receive keyboard delete"
+ );
+
+ //remove all text from input
+ await emailField.sendKeys(Key.chord(Key.CONTROL, "a"));
+ await emailField.sendKeys(Key.BACK_SPACE);
+ emailText = await emailField.getAttribute("value");
+ assert.equal(emailText, "", "Email field is able to be cleared");
+ } finally {
+ await driver.quit();
+ }
+ }, 30000); //timeout after 30seconds
+
+ it("Login button renders on the home page", async () => {
+ // Open firefox browser
+ let driver = await new Builder().forBrowser("firefox").build();
+ try {
+ //Go to the main page
+ await driver.get("http://localhost:3000");
+
+ //Get the email field, should be empty
+ let emailField = await driver.findElement(By.name("email"));
+
+ //Send keys to the input
+ await emailField.sendKeys("dsagasdgasdgasg");
+
+ //Get the login button element
+ let loginButton = await driver.wait(
+ until.elementLocated(By.className("css-wwcn44")),
+ 10000
+ ); //make sure title is rendered on the screen before testing
+ await loginButton.click();
+
+ //verify we didnt go anywhere, were still on the same page
+ let url = await driver.getCurrentUrl();
+ assert.equal(url, "http://localhost:3000/", "Able to access main page");
+
+ //Verify there is a title
+ // let title = await driver.findElement(By.className("css-1r0stgl"));
+ let title = await driver.wait(
+ until.elementLocated(By.className("css-1r0stgl")),
+ 10000
+ ); //make sure title is rendered on the screen before testing
+ title = await title.getText();
+ assert.equal(title, "Space Explorer", "The main page has a title");
+ } finally {
+ await driver.quit();
+ }
+ }, 30000); //timeout after 30seconds
+});
diff --git a/final/client/src/pages/__tests__/selenium-navigation.tsx b/final/client/src/pages/__tests__/selenium-navigation.tsx
new file mode 100644
index 000000000..cc7f1af52
--- /dev/null
+++ b/final/client/src/pages/__tests__/selenium-navigation.tsx
@@ -0,0 +1,155 @@
+import { cleanup } from '../../test-utils';
+
+const {Builder, By, Key, until} = require('selenium-webdriver');
+const assert = require("assert");
+const sleep = require("sleep");
+
+const appURL = "http://localhost:3000";
+const testEmail = "Test@test.com";
+
+
+describe('Navigating with Icons', () => {
+
+ // automatically unmount and cleanup DOM after the test is finished.
+ afterEach(cleanup);
+
+ it('Can reach home page after logging in', async () => {
+ //define webdriver
+ let driver = new Builder().forBrowser('firefox').build();
+ driver.get(appURL);
+ //Get the email field,
+ let emailField = await driver.wait(until.elementLocated(By.name("email")), 10000);
+ //Send keys to the input
+ await emailField.sendKeys(testEmail);
+ //submit / login
+ await emailField.sendKeys(Key.ENTER);
+
+
+ //verify we got to the homepage
+ let email = await driver.wait(until.elementLocated(By.className("css-1sykydy")), 10000);
+ let emailText = await email.getText();
+ assert.equal(emailText, "TEST@TEST.COM", "Able to login and reach the home page");
+
+ }, 30000); //timeout after 30seconds
+
+ it('Can reach the cart page via Icon', async () => {
+ //define webdriver
+ let driver = new Builder().forBrowser('firefox').build();
+ driver.get(appURL);
+ //Get the email field,
+ let emailField = await driver.wait(until.elementLocated(By.name("email")), 10000);
+ //Send keys to the input
+ await emailField.sendKeys(testEmail);
+ //submit / login
+ await emailField.sendKeys(Key.ENTER);
+
+ try{
+ //grab the cart icon
+ let cartIcon = await driver.wait(until.elementLocated(By.xpath('//*[@id="root"]/footer/div/a[2]')), 10000);
+ //simulate a click
+ await cartIcon.click();
+ // sleep.sleep(3);
+ //grab the new url
+ let url = await driver.getCurrentUrl();
+ //verify it is the cart page.
+ assert.equal(url, appURL + "/cart", "Able to reach the cart via icon");
+ }finally{
+ await driver.quit();
+ }
+
+
+ }, 30000); //timeout after 30seconds
+
+ it('Can reach the profile page via Icon', async () => {
+ //define webdriver
+ let driver = new Builder().forBrowser('firefox').build();
+ driver.get(appURL);
+ //Get the email field,
+ let emailField = await driver.wait(until.elementLocated(By.name("email")), 10000);
+ //Send keys to the input
+ await emailField.sendKeys(testEmail);
+ //submit / login
+ await emailField.sendKeys(Key.ENTER);
+
+ try{
+ //grab the cart icon
+ let profileIcon = await driver.wait(until.elementLocated(By.xpath('//*[@id="root"]/footer/div/a[3]')), 10000);
+ //simulate a click
+ await profileIcon.click();
+ // sleep.sleep(3);
+ //grab the new url
+ let url = await driver.getCurrentUrl();
+ //verify it is the cart page.
+ assert.equal(url, appURL + "/profile", "Able to reach the profile page via icon");
+ }finally{
+ await driver.quit();
+ }
+
+
+ }, 30000); //timeout after 30seconds
+
+ it('Can logout via icon', async () => {
+ //define webdriver
+ let driver = new Builder().forBrowser('firefox').build();
+ driver.get(appURL);
+ //Get the email field,
+ let emailField = await driver.wait(until.elementLocated(By.name("email")), 10000);
+ //Send keys to the input
+ await emailField.sendKeys(testEmail);
+ //submit / login
+ await emailField.sendKeys(Key.ENTER);
+
+ try{
+ //grab the cart icon
+ let logoutIcon = await driver.wait(until.elementLocated(By.xpath('//*[@id="root"]/footer/div/button[1]')), 10000);
+ //simulate a click
+ await logoutIcon.click();
+ sleep.sleep(2);
+ //grab the new url
+ let url = await driver.getCurrentUrl();
+ //verify it is the cart page.
+ assert.equal(url, appURL + "/", "Able to logout");
+ }finally{
+ await driver.quit();
+ }
+
+
+ }, 30000); //timeout after 30seconds
+
+ it('Can go back to home after being on another link via icon', async () => {
+ //define webdriver
+ let driver = new Builder().forBrowser('firefox').build();
+ driver.get(appURL);
+ //Get the email field,
+ let emailField = await driver.wait(until.elementLocated(By.name("email")), 10000);
+ //Send keys to the input
+ await emailField.sendKeys(testEmail);
+ //submit / login
+ await emailField.sendKeys(Key.ENTER);
+
+ try{
+ //grab the cart icon
+ let cartIcon = await driver.wait(until.elementLocated(By.xpath('//*[@id="root"]/footer/div/a[2]')), 10000);
+ //simulate a click
+ await cartIcon.click();
+ //grab the new url
+ let url = await driver.getCurrentUrl();
+ //verify it is the cart page.
+ assert.equal(url, appURL + "/cart", "Able to reach the cart via icon");
+
+ //now navigate back to the home page
+ let homeIcon = await driver.wait(until.elementLocated(By.xpath('//*[@id="root"]/footer/div/a[1]')), 10000);
+ //simulate a click
+ await homeIcon.click();
+ //grab the new url
+ url = await driver.getCurrentUrl();
+ //verify it is the cart page.
+ assert.equal(url, appURL + "/", "Able to reach the home page via icon");
+ }finally{
+ await driver.quit();
+ }
+
+
+ }, 30000); //timeout after 30seconds
+
+});