Add error handling code to the Exam settings page. Also include spinners for loading (#10)

This commit is contained in:
Simon Chen
2020-07-22 09:43:49 -04:00
committed by GitHub
parent 3fe529dc12
commit 74f5a44d83
4 changed files with 1000 additions and 91 deletions

829
package-lock.json generated
View File

@@ -1395,7 +1395,6 @@
"integrity": "sha512-y7B0Lb40nhW81ouuDPe1QCdKHeE71aSPIA7koE95UMwXRonaoR1NYc3DsawtVS/fGI08uoB+XO8EAEtPPa6aOw==",
"requires": {
"@cospired/i18n-iso-languages": "2.1.2",
"axios": "0.18.1",
"form-urlencoded": "4.1.4",
"glob": "7.1.6",
"history": "4.10.1",
@@ -3259,6 +3258,12 @@
"integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
"dev": true
},
"array-filter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz",
"integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=",
"dev": true
},
"array-find-index": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
@@ -3461,12 +3466,12 @@
"dev": true
},
"axios": {
"version": "0.18.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz",
"integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
"dev": true,
"requires": {
"follow-redirects": "1.5.10",
"is-buffer": "^2.0.2"
"follow-redirects": "1.5.10"
}
},
"axobject-query": {
@@ -4978,6 +4983,12 @@
"safe-buffer": "^5.0.1"
}
},
"circular-json-es6": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/circular-json-es6/-/circular-json-es6-2.0.2.tgz",
"integrity": "sha512-ODYONMMNb3p658Zv+Pp+/XPa5s6q7afhz3Tzyvo+VRh9WIrJ64J76ZC4GQxnlye/NesTn09jvOiuE8+xxfpwhQ==",
"dev": true
},
"class-utils": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
@@ -6066,6 +6077,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
@@ -6241,6 +6253,27 @@
"regexp.prototype.flags": "^1.2.0"
}
},
"deep-equal-ident": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/deep-equal-ident/-/deep-equal-ident-1.1.1.tgz",
"integrity": "sha1-BvS4nlNxDNbOpKd4HHqVZkLejck=",
"dev": true,
"requires": {
"lodash.isequal": "^3.0"
},
"dependencies": {
"lodash.isequal": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-3.0.4.tgz",
"integrity": "sha1-HDXrO27wzR/1F0Pj6jz3/f/ay2Q=",
"dev": true,
"requires": {
"lodash._baseisequal": "^3.0.0",
"lodash._bindcallback": "^3.0.0"
}
}
}
},
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
@@ -6456,6 +6489,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",
@@ -6787,6 +6826,97 @@
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
"integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ=="
},
"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.2",
"resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.2.tgz",
"integrity": "sha512-SkvDrb8xU3lSxID8Qic9rB8pvevDbLybxPK6D/vW7PrT0s2Cl/zJYuXvsd1EBTz0q4o3iqG3FJhpYz3nUNpM2Q==",
"dev": true,
"requires": {
"enzyme-adapter-utils": "^1.13.0",
"enzyme-shallow-equal": "^1.0.1",
"has": "^1.0.3",
"object.assign": "^4.1.0",
"object.values": "^1.1.1",
"prop-types": "^15.7.2",
"react-is": "^16.12.0",
"react-test-renderer": "^16.0.0-0",
"semver": "^5.7.0"
}
},
"enzyme-adapter-utils": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.13.0.tgz",
"integrity": "sha512-YuEtfQp76Lj5TG1NvtP2eGJnFKogk/zT70fyYHXK2j3v6CtuHqc8YmgH/vaiBfL8K1SgVVbQXtTcgQZFwzTVyQ==",
"dev": true,
"requires": {
"airbnb-prop-types": "^2.15.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"
}
},
"enzyme-matchers": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/enzyme-matchers/-/enzyme-matchers-7.1.2.tgz",
"integrity": "sha512-03WqAg2XDl7id9rARIO97HQ1JIw9F2heJ3R4meGu/13hx0ULTDEgl0E67MGl2Uq1jq1DyRnJfto1/VSzskdV5A==",
"dev": true,
"requires": {
"circular-json-es6": "^2.0.1",
"deep-equal-ident": "^1.1.1"
}
},
"enzyme-shallow-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.1.tgz",
"integrity": "sha512-hGA3i1so8OrYOZSM9whlkNmVHOicJpsjgTzC+wn2JMJXhq1oO4kA4bJ5MsfzSIcC71aLDKzJ6gZpIxrqt3QTAQ==",
"dev": true,
"requires": {
"has": "^1.0.3",
"object-is": "^1.0.2"
}
},
"enzyme-to-json": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/enzyme-to-json/-/enzyme-to-json-3.5.0.tgz",
"integrity": "sha512-clusXRsiaQhG7+wtyc4t7MU8N3zCOgf4eY9+CeSenYzKlFST4lxerfOvnWd4SNaToKhkuba+w6m242YpQOS7eA==",
"dev": true,
"requires": {
"lodash": "^4.17.15",
"react-is": "^16.12.0"
}
},
"errno": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
@@ -8080,6 +8210,7 @@
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
"dev": true,
"requires": {
"debug": "=3.1.0"
}
@@ -8828,6 +8959,15 @@
"integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==",
"dev": true
},
"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",
@@ -9804,10 +9944,11 @@
"binary-extensions": "^1.0.0"
}
},
"is-buffer": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
"integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A=="
"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-callable": {
"version": "1.2.0",
@@ -10029,6 +10170,12 @@
}
}
},
"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",
@@ -10124,6 +10271,12 @@
"integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
"dev": true
},
"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",
@@ -11848,6 +12001,481 @@
}
}
},
"jest-environment-enzyme": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/jest-environment-enzyme/-/jest-environment-enzyme-7.1.2.tgz",
"integrity": "sha512-3tfaYAzO7qZSRrv+srQnfK16Vu5XwH/pHi8FpoqSHjKKngbHzXf7aBCBuWh8y3w0OtknHRfDMFrC60Khj+g1hA==",
"dev": true,
"requires": {
"jest-environment-jsdom": "^24.0.0"
},
"dependencies": {
"@jest/console": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz",
"integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==",
"dev": true,
"requires": {
"@jest/source-map": "^24.9.0",
"chalk": "^2.0.1",
"slash": "^2.0.0"
}
},
"@jest/environment": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz",
"integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==",
"dev": true,
"requires": {
"@jest/fake-timers": "^24.9.0",
"@jest/transform": "^24.9.0",
"@jest/types": "^24.9.0",
"jest-mock": "^24.9.0"
}
},
"@jest/fake-timers": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz",
"integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==",
"dev": true,
"requires": {
"@jest/types": "^24.9.0",
"jest-message-util": "^24.9.0",
"jest-mock": "^24.9.0"
}
},
"@jest/source-map": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz",
"integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==",
"dev": true,
"requires": {
"callsites": "^3.0.0",
"graceful-fs": "^4.1.15",
"source-map": "^0.6.0"
}
},
"@jest/test-result": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz",
"integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==",
"dev": true,
"requires": {
"@jest/console": "^24.9.0",
"@jest/types": "^24.9.0",
"@types/istanbul-lib-coverage": "^2.0.0"
}
},
"@jest/transform": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz",
"integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==",
"dev": true,
"requires": {
"@babel/core": "^7.1.0",
"@jest/types": "^24.9.0",
"babel-plugin-istanbul": "^5.1.0",
"chalk": "^2.0.1",
"convert-source-map": "^1.4.0",
"fast-json-stable-stringify": "^2.0.0",
"graceful-fs": "^4.1.15",
"jest-haste-map": "^24.9.0",
"jest-regex-util": "^24.9.0",
"jest-util": "^24.9.0",
"micromatch": "^3.1.10",
"pirates": "^4.0.1",
"realpath-native": "^1.1.0",
"slash": "^2.0.0",
"source-map": "^0.6.1",
"write-file-atomic": "2.4.1"
}
},
"@jest/types": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz",
"integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==",
"dev": true,
"requires": {
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^1.1.1",
"@types/yargs": "^13.0.0"
}
},
"@types/yargs": {
"version": "13.0.9",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz",
"integrity": "sha512-xrvhZ4DZewMDhoH1utLtOAwYQy60eYFoXeje30TzM3VOvQlBwQaEpKFq5m34k1wOw2AKIi2pwtiAjdmhvlBUzg==",
"dev": true,
"requires": {
"@types/yargs-parser": "*"
}
},
"acorn": {
"version": "5.7.4",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
"integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==",
"dev": true
},
"babel-plugin-istanbul": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz",
"integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
"find-up": "^3.0.0",
"istanbul-lib-instrument": "^3.3.0",
"test-exclude": "^5.2.3"
}
},
"cssom": {
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
"integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
"dev": true
},
"cssstyle": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz",
"integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==",
"dev": true,
"requires": {
"cssom": "0.3.x"
}
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
"locate-path": "^3.0.0"
}
},
"istanbul-lib-coverage": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz",
"integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==",
"dev": true
},
"istanbul-lib-instrument": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz",
"integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==",
"dev": true,
"requires": {
"@babel/generator": "^7.4.0",
"@babel/parser": "^7.4.3",
"@babel/template": "^7.4.0",
"@babel/traverse": "^7.4.3",
"@babel/types": "^7.4.0",
"istanbul-lib-coverage": "^2.0.5",
"semver": "^6.0.0"
}
},
"jest-environment-jsdom": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz",
"integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==",
"dev": true,
"requires": {
"@jest/environment": "^24.9.0",
"@jest/fake-timers": "^24.9.0",
"@jest/types": "^24.9.0",
"jest-mock": "^24.9.0",
"jest-util": "^24.9.0",
"jsdom": "^11.5.1"
}
},
"jest-haste-map": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz",
"integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==",
"dev": true,
"requires": {
"@jest/types": "^24.9.0",
"anymatch": "^2.0.0",
"fb-watchman": "^2.0.0",
"fsevents": "^1.2.7",
"graceful-fs": "^4.1.15",
"invariant": "^2.2.4",
"jest-serializer": "^24.9.0",
"jest-util": "^24.9.0",
"jest-worker": "^24.9.0",
"micromatch": "^3.1.10",
"sane": "^4.0.3",
"walker": "^1.0.7"
}
},
"jest-message-util": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz",
"integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@jest/test-result": "^24.9.0",
"@jest/types": "^24.9.0",
"@types/stack-utils": "^1.0.1",
"chalk": "^2.0.1",
"micromatch": "^3.1.10",
"slash": "^2.0.0",
"stack-utils": "^1.0.1"
}
},
"jest-mock": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz",
"integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==",
"dev": true,
"requires": {
"@jest/types": "^24.9.0"
}
},
"jest-regex-util": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz",
"integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==",
"dev": true
},
"jest-serializer": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz",
"integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==",
"dev": true
},
"jest-util": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz",
"integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==",
"dev": true,
"requires": {
"@jest/console": "^24.9.0",
"@jest/fake-timers": "^24.9.0",
"@jest/source-map": "^24.9.0",
"@jest/test-result": "^24.9.0",
"@jest/types": "^24.9.0",
"callsites": "^3.0.0",
"chalk": "^2.0.1",
"graceful-fs": "^4.1.15",
"is-ci": "^2.0.0",
"mkdirp": "^0.5.1",
"slash": "^2.0.0",
"source-map": "^0.6.0"
}
},
"jest-worker": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz",
"integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==",
"dev": true,
"requires": {
"merge-stream": "^2.0.0",
"supports-color": "^6.1.0"
}
},
"jsdom": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz",
"integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==",
"dev": true,
"requires": {
"abab": "^2.0.0",
"acorn": "^5.5.3",
"acorn-globals": "^4.1.0",
"array-equal": "^1.0.0",
"cssom": ">= 0.3.2 < 0.4.0",
"cssstyle": "^1.0.0",
"data-urls": "^1.0.0",
"domexception": "^1.0.1",
"escodegen": "^1.9.1",
"html-encoding-sniffer": "^1.0.2",
"left-pad": "^1.3.0",
"nwsapi": "^2.0.7",
"parse5": "4.0.0",
"pn": "^1.1.0",
"request": "^2.87.0",
"request-promise-native": "^1.0.5",
"sax": "^1.2.4",
"symbol-tree": "^3.2.2",
"tough-cookie": "^2.3.4",
"w3c-hr-time": "^1.0.1",
"webidl-conversions": "^4.0.2",
"whatwg-encoding": "^1.0.3",
"whatwg-mimetype": "^2.1.0",
"whatwg-url": "^6.4.1",
"ws": "^5.2.0",
"xml-name-validator": "^3.0.0"
}
},
"load-json-file": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
"integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"parse-json": "^4.0.0",
"pify": "^3.0.0",
"strip-bom": "^3.0.0"
}
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
}
},
"parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"dev": true,
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
}
},
"parse5": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
"integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==",
"dev": true
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
},
"path-type": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
"integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
"dev": true,
"requires": {
"pify": "^3.0.0"
}
},
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
},
"read-pkg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
"integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
"dev": true,
"requires": {
"load-json-file": "^4.0.0",
"normalize-package-data": "^2.3.2",
"path-type": "^3.0.0"
}
},
"read-pkg-up": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz",
"integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==",
"dev": true,
"requires": {
"find-up": "^3.0.0",
"read-pkg": "^3.0.0"
}
},
"realpath-native": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz",
"integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==",
"dev": true,
"requires": {
"util.promisify": "^1.0.0"
}
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
},
"supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
},
"test-exclude": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz",
"integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==",
"dev": true,
"requires": {
"glob": "^7.1.3",
"minimatch": "^3.0.4",
"read-pkg-up": "^4.0.0",
"require-main-filename": "^2.0.0"
}
},
"tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
"dev": true,
"requires": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
}
},
"whatwg-url": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
"integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
"dev": true,
"requires": {
"lodash.sortby": "^4.7.0",
"tr46": "^1.0.1",
"webidl-conversions": "^4.0.2"
}
},
"write-file-atomic": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz",
"integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.11",
"imurmurhash": "^0.1.4",
"signal-exit": "^3.0.2"
}
},
"ws": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
"integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
"dev": true,
"requires": {
"async-limiter": "~1.0.0"
}
}
}
},
"jest-environment-jsdom": {
"version": "25.5.0",
"resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-25.5.0.tgz",
@@ -11884,6 +12512,17 @@
}
}
},
"jest-enzyme": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/jest-enzyme/-/jest-enzyme-7.1.2.tgz",
"integrity": "sha512-j+jkph3t5hGBS12eOldpfsnERYRCHi4c/0KWPMnqRPoJJXvCpLIc5th1MHl0xDznQDXVU0AHUXg3rqMrf8vGpA==",
"dev": true,
"requires": {
"enzyme-matchers": "^7.1.2",
"enzyme-to-json": "^3.3.0",
"jest-environment-enzyme": "^7.1.2"
}
},
"jest-get-type": {
"version": "25.2.6",
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz",
@@ -13201,6 +13840,29 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
},
"lodash._baseisequal": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz",
"integrity": "sha1-2AJfdjOdKTQnZ9zIh85cuVpbUfE=",
"dev": true,
"requires": {
"lodash.isarray": "^3.0.0",
"lodash.istypedarray": "^3.0.0",
"lodash.keys": "^3.0.0"
}
},
"lodash._bindcallback": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz",
"integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=",
"dev": true
},
"lodash._getnative": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
"integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=",
"dev": true
},
"lodash.camelcase": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
@@ -13212,6 +13874,53 @@
"integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=",
"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.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=",
"dev": true
},
"lodash.isarray": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
"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.istypedarray": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz",
"integrity": "sha1-yaR3SYYHUB2OhJTSg7h8OSgc72I=",
"dev": true
},
"lodash.keys": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
"integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
"dev": true,
"requires": {
"lodash._getnative": "^3.0.0",
"lodash.isarguments": "^3.0.0",
"lodash.isarray": "^3.0.0"
}
},
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -13893,6 +14602,12 @@
"minimist": "^1.2.5"
}
},
"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",
@@ -13921,7 +14636,8 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"multicast-dns": {
"version": "6.2.3",
@@ -13976,6 +14692,27 @@
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"dev": true
},
"nearley": {
"version": "2.19.5",
"resolved": "https://registry.npmjs.org/nearley/-/nearley-2.19.5.tgz",
"integrity": "sha512-qoh1ZXXl0Kpn40tFhmgvffUAlbpRMcjLUagNVnT1JmliUIsB4tFabmCNhD97+tkf9FZ/SLhhYzNow0V3GitzDg==",
"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": {
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
}
}
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
@@ -16134,6 +16871,31 @@
"integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==",
"dev": true
},
"raf": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"dev": true,
"requires": {
"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",
@@ -16551,6 +17313,30 @@
"tiny-warning": "^1.0.0"
}
},
"react-test-renderer": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.13.1.tgz",
"integrity": "sha512-Sn2VRyOK2YJJldOqoh8Tn/lWQ+ZiKhyZTPtaO0Q6yNj+QDbmRkVFap6pZPy3YQk8DScRDfyqm/KxKYP9gCMRiQ==",
"dev": true,
"requires": {
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"react-is": "^16.8.6",
"scheduler": "^0.19.1"
},
"dependencies": {
"scheduler": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
"integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
}
}
},
"react-transition-group": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz",
@@ -17307,6 +18093,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",
@@ -18788,6 +19584,17 @@
"side-channel": "^1.0.2"
}
},
"string.prototype.trim": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz",
"integrity": "sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0-next.1",
"function-bind": "^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",

View File

@@ -54,11 +54,15 @@
},
"devDependencies": {
"@edx/frontend-build": "^3.0.0",
"axios": "^0.19.2",
"codecov": "3.6.5",
"es-check": "5.1.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2",
"glob": "7.1.6",
"husky": "3.1.0",
"jest": "24.9.0",
"jest-enzyme": "7.1.2",
"reactifex": "1.1.1"
}
}

View File

@@ -7,13 +7,17 @@ import {
import StudioApiService from '../data/services/StudioApiService';
function ExamSettings(props) {
const [loading, setLoading] = useState(true);
const [loaded, setLoaded] = useState(false);
const [loadingConnectionError, setLoadingConnectionError] = useState(false);
const [loadingPermissionError, setLoadingPermissionError] = useState(false);
const [enableProctoredExams, setEnableProctoredExams] = useState(true);
const [allowOptingOut, setAllowOptingOut] = useState(false);
const [proctoringProvider, setProctoringProvider] = useState('');
const [availableProctoringProviders, setAvailableProctoringProviders] = useState([]);
// TODO: we'll probably want to hide this field when proctortrack is not selected; currently,
// this causes some errors in the browser console
const [proctortrackEscalationEmail, setProctortrackEscalationEmail] = useState(null);
const [proctortrackEscalationEmail, setProctortrackEscalationEmail] = useState('');
const [createZendeskTickets, setCreateZendeskTickets] = useState(false);
function onEnableProctoredExamsChange(event) {
@@ -55,13 +59,126 @@ function ExamSettings(props) {
);
}
function renderContent() {
return (
<div className="content-container">
<ValidationFormGroup
for="enableProctoredExams"
helpText="If checked, proctored exams are enabled in your course."
>
<CheckBox
id="enableProctoredExams"
name="enable_proctored_exams"
label="Enable Proctored Exams"
checked={enableProctoredExams}
onChange={onEnableProctoredExamsChange}
/>
</ValidationFormGroup>
<ValidationFormGroup
for="allowingOptingOut"
helpText="If checked, learners can choose to take proctored exams without proctoring. If not checked, all learners must take the exam with proctoring."
>
<CheckBox
id="allowingOptingOut"
name="allow_opting_out"
label="Allow Opting Out of Proctored Exams"
checked={allowOptingOut}
onChange={onAllowOptingOutChange}
/>
</ValidationFormGroup>
<ValidationFormGroup
for="proctoringProvider"
helpText="Select the proctoring provider you want to use for this course run."
>
<label htmlFor="proctoringProvider">Proctoring Provider</label>
<Input
type="select"
id="proctoringProvider"
name="proctoring_provider"
options={getProctoringProviderOptions(availableProctoringProviders)}
value={proctoringProvider}
onChange={onProctoringProviderChange}
/>
</ValidationFormGroup>
<ValidationFormGroup
for="proctortrackEscalationEmail"
helpText="Required if 'proctortrack' is selected as your proctoring provider. Enter an email address to be contacted by the support team whenever there are escalations (e.g. appeals, delayed reviews, etc.)."
>
<label htmlFor="proctortrackEscalationEmail">Proctortrack Escalation Email</label>
<Input
type="text"
id="proctortrackEscalationEmail"
name="proctortrack_escalation_email"
value={proctortrackEscalationEmail}
onChange={onProctortrackEscalationEmailChange}
/>
</ValidationFormGroup>
<ValidationFormGroup
for="createZendeskTickets"
helpText="If checked, a Zendesk ticket will be created for suspicious attempts."
>
<CheckBox
id="createZendeskTickets"
name="create_zendesk_tickets"
label="Create Zendesk Tickets For Suspicious Exam Attempts"
checked={createZendeskTickets}
onChange={onCreateZendeskTicketsChange}
/>
</ValidationFormGroup>
<Button
className="btn-primary mb-3"
onClick={onButtonClick}
>
Submit
</Button>
</div>
);
}
function renderLoading() {
return (
<div className="Loading-container">
<div
className="d-flex justify-content-center align-items-center flex-column"
style={{
height: '50vh',
}}
>
<div className="spinner-border text-primary" role="status" />
</div>
</div>
);
}
function renderConnectionError() {
return (
<div className="justify-content-center align-items-center flex-column">
<div className="alert alert-danger">
The page encountered errors. Please try again later. If the problem persists, please
go to <a href="https://support.edx.org/hc/en-us">edX Support Page</a> to get help.
</div>
</div>
);
}
function renderPermissionError() {
return (
<div className="justify-content-center align-items-center flex-column">
<div className="alert alert-danger">
You do not have enough permission to view this page.
</div>
</div>
);
}
useEffect(
() => {
StudioApiService.getProctoredExamSettingsData(props.courseId)
.then(
response => {
const proctoredExamSettings = response.data.proctored_exam_settings;
setLoaded(true);
setLoading(false);
setEnableProctoredExams(proctoredExamSettings.enable_proctored_exams);
setAllowOptingOut(proctoredExamSettings.allow_proctoring_opt_out);
setProctoringProvider(proctoredExamSettings.proctoring_provider);
@@ -69,8 +186,17 @@ function ExamSettings(props) {
setProctortrackEscalationEmail(proctoredExamSettings.proctoring_escalation_email);
setCreateZendeskTickets(proctoredExamSettings.create_zendesk_tickets);
},
).catch(
errorDetails => {
if (errorDetails.customAttributes.httpErrorStatus === 403) {
setLoadingPermissionError(true);
} else {
setLoadingConnectionError(true);
}
setLoading(false);
setLoaded(false);
},
);
// TODO: handle error
}, [],
);
@@ -79,75 +205,12 @@ function ExamSettings(props) {
<h2 className="mb-1">
Proctored Exam Settings
</h2>
<ValidationFormGroup
for="enableProctoredExams"
helpText="If checked, proctored exams are enabled in your course."
>
<CheckBox
id="enableProctoredExams"
name="enable_proctored_exams"
label="Enable Proctored Exams"
checked={enableProctoredExams}
onChange={onEnableProctoredExamsChange}
/>
</ValidationFormGroup>
<ValidationFormGroup
for="allowingOptingOut"
helpText="If checked, learners can choose to take proctored exams without proctoring. If not checked, all learners must take the exam with proctoring."
>
<CheckBox
id="allowingOptingOut"
name="allow_opting_out"
label="Allow Opting Out of Proctored Exams"
checked={allowOptingOut}
onChange={onAllowOptingOutChange}
/>
</ValidationFormGroup>
<ValidationFormGroup
for="proctoringProvider"
helpText="Select the proctoring provider you want to use for this course run."
>
<label htmlFor="proctoringProvider">Proctoring Provider</label>
<Input
type="select"
id="proctoringProvider"
name="proctoring_provider"
options={getProctoringProviderOptions(availableProctoringProviders)}
value={proctoringProvider}
onChange={onProctoringProviderChange}
/>
</ValidationFormGroup>
<ValidationFormGroup
for="proctortrackEscalationEmail"
helpText="Required if 'proctortrack' is selected as your proctoring provider. Enter an email address to be contacted by the support team whenever there are escalations (e.g. appeals, delayed reviews, etc.)."
>
<label htmlFor="proctortrackEscalationEmail">Proctortrack Escalation Email</label>
<Input
type="text"
id="proctortrackEscalationEmail"
name="proctortrack_escalation_email"
value={proctortrackEscalationEmail}
onChange={onProctortrackEscalationEmailChange}
/>
</ValidationFormGroup>
<ValidationFormGroup
for="createZendeskTickets"
helpText="If checked, a Zendesk ticket will be created for suspicious attempts."
>
<CheckBox
id="createZendeskTickets"
name="create_zendesk_tickets"
label="Create Zendesk Tickets For Suspicious Exam Attempts"
checked={createZendeskTickets}
onChange={onCreateZendeskTicketsChange}
/>
</ValidationFormGroup>
<Button
className="btn-primary mb-3"
onClick={onButtonClick}
>
Submit
</Button>
<div>
{loading ? renderLoading() : null}
{loaded ? renderContent() : null}
{loadingConnectionError ? renderConnectionError() : null}
{loadingPermissionError ? renderPermissionError() : null}
</div>
</div>
);
}

View File

@@ -1,27 +1,62 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { mount } from 'enzyme';
import * as auth from '@edx/frontend-platform/auth';
import ProctoredExamSettings from './ProctoredExamSettings';
const defaultProps = {
courseId: 'course-v1%3AedX%2BDemoX%2BDemo_Course',
};
const waitForComponentToPaint = async (wrapper, callback) => {
await act(async () => {
await new Promise(resolve => setTimeout(resolve, 0));
wrapper.update();
callback(wrapper);
});
};
describe('ProctoredExamSettings', () => {
const component = mount(<ProctoredExamSettings {...defaultProps} />);
beforeEach(() => {
auth.getAuthenticatedHttpClient = jest.fn(() => ({
get: async () => ({
data: {
proctored_exam_settings: {
enable_proctored_exams: true,
allow_proctoring_opt_out: false,
proctoring_provider: 'proctortrack',
proctoring_escalation_email: 'test@example.com',
create_zendesk_tickets: false,
},
available_proctoring_providers: ['software_secure', 'proctortrack', 'mockproc'],
},
catch: () => {},
}),
}));
auth.getAuthenticatedUser = jest.fn(() => ({ userId: 3 }));
});
it('updates zendesk ticket field if proctortrack is provider', () => {
component.find('select#proctoringProvider').simulate('change', { target: { value: 'proctortrack' } });
expect(component.find('input#createZendeskTickets').prop('checked')).toEqual(false);
const component = mount(<ProctoredExamSettings {...defaultProps} />);
waitForComponentToPaint(component, (comp) => {
comp.find('select#proctoringProvider').simulate('change', { target: { value: 'proctortrack' } });
expect(comp.find('input#createZendeskTickets').prop('checked')).toEqual(false);
});
});
it('updates zendesk ticket field if software_secure is provider', () => {
component.find('select#proctoringProvider').simulate('change', { target: { value: 'software_secure' } });
expect(component.find('input#createZendeskTickets').prop('checked')).toEqual(true);
const component = mount(<ProctoredExamSettings {...defaultProps} />);
waitForComponentToPaint(component, (comp) => {
comp.find('select#proctoringProvider').simulate('change', { target: { value: 'software_secure' } });
expect(comp.find('input#createZendeskTickets').prop('checked')).toEqual(true);
});
});
it('does not update zendesk ticket field for any other provider', () => {
component.find('select#proctoringProvider').simulate('change', { target: { value: 'mockprock' } });
expect(component.find('input#createZendeskTickets').prop('checked')).toEqual(true);
const component = mount(<ProctoredExamSettings {...defaultProps} />);
waitForComponentToPaint(component, (comp) => {
comp.find('select#proctoringProvider').simulate('change', { target: { value: 'mockproc' } });
expect(comp.find('input#createZendeskTickets').prop('checked')).toEqual(true);
});
});
});