Compare commits

..

2 Commits

Author SHA1 Message Date
ilee2u
3a13efa3d9 feat: merge special exams store 2024-01-10 14:35:15 -05:00
ilee2u
a3d0a4e2c9 feat: add EXAMS_BASE_URL to dev env 2024-01-09 16:44:44 -05:00
18 changed files with 99 additions and 681 deletions

View File

@@ -16,7 +16,7 @@ ENABLE_JUMPNAV='true'
ENABLE_NEW_SIDEBAR=''
ENABLE_NOTICES=''
ENTERPRISE_LEARNER_PORTAL_HOSTNAME='localhost:8734'
EXAMS_BASE_URL=''
EXAMS_BASE_URL='http://localhost:18740'
FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico
IGNORED_ERROR_REGEX=''
LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'

View File

@@ -53,15 +53,13 @@ pull_translations:
rm -rf src/i18n/messages
mkdir src/i18n/messages
cd src/i18n/messages \
&& atlas pull $(ATLAS_OPTIONS) \
translations/frontend-platform/src/i18n/messages:frontend-platform \
&& atlas pull --filter=$(transifex_langs) \
translations/paragon/src/i18n/messages:paragon \
translations/frontend-component-header/src/i18n/messages:frontend-component-header \
translations/frontend-component-footer/src/i18n/messages:frontend-component-footer \
translations/frontend-lib-special-exams/src/i18n/messages:frontend-lib-special-exams \
translations/frontend-app-learning/src/i18n/messages:frontend-app-learning
$(intl_imports) frontend-platform paragon frontend-component-header frontend-component-footer frontend-lib-special-exams frontend-app-learning
$(intl_imports) paragon frontend-component-header frontend-component-footer frontend-app-learning
endif
# This target is used by Travis.

413
package-lock.json generated
View File

@@ -19,17 +19,16 @@
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
"@edx/frontend-component-footer": "12.2.1",
"@edx/frontend-component-header": "4.6.0",
"@edx/frontend-lib-learning-assistant": "^1.20.1",
"@edx/frontend-lib-learning-assistant": "^1.19.0",
"@edx/frontend-lib-special-exams": "2.27.0",
"@edx/frontend-platform": "5.5.2",
"@edx/openedx-atlas": "^0.6.0",
"@edx/paragon": "20.46.0",
"@edx/react-unit-test-utils": "npm:@edx/react-unit-test-utils@1.7.0",
"@fortawesome/fontawesome-svg-core": "1.3.0",
"@fortawesome/free-brands-svg-icons": "5.15.4",
"@fortawesome/free-regular-svg-icons": "5.15.4",
"@fortawesome/free-solid-svg-icons": "5.15.4",
"@fortawesome/react-fontawesome": "^0.1.4",
"@openedx/paragon": "22.0.0",
"@popperjs/core": "2.11.8",
"@reduxjs/toolkit": "1.8.1",
"classnames": "2.3.2",
@@ -1972,23 +1971,6 @@
"node": ">=6.9.0"
}
},
"node_modules/@babel/runtime-corejs3": {
"version": "7.23.9",
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.23.9.tgz",
"integrity": "sha512-oeOFTrYWdWXCvXGB5orvMTJ6gCZ9I6FBjR+M38iKNXCsPxr4xT0RTdg5uz1H7QP8pp74IzPtwritEr+JscqHXQ==",
"dependencies": {
"core-js-pure": "^3.30.2",
"regenerator-runtime": "^0.14.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/runtime-corejs3/node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"node_modules/@babel/runtime/node_modules/regenerator-runtime": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
@@ -3478,9 +3460,9 @@
}
},
"node_modules/@edx/frontend-lib-learning-assistant": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/@edx/frontend-lib-learning-assistant/-/frontend-lib-learning-assistant-1.20.1.tgz",
"integrity": "sha512-/JUSfs4CZwZj2Bg0BqlMGIC6BrDu4PufUSFnrgusL4ZD/N8DR3UyX/AQZml7S9O/ci4OY3YU7V4VDM8X00+6Lg==",
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/@edx/frontend-lib-learning-assistant/-/frontend-lib-learning-assistant-1.19.0.tgz",
"integrity": "sha512-wBdeQladtvXmS3RA/LOXVNpmEwtG5zAYtu7E7Dh9N8sN2p12ptWCEEdPa92ViK6d5bmqo2NA9ZhnZeEmqs+oRg==",
"dependencies": {
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
"@fortawesome/fontawesome-svg-core": "1.2.36",
@@ -3718,14 +3700,6 @@
"@newrelic/publish-sourcemap": "^5.0.1"
}
},
"node_modules/@edx/openedx-atlas": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@edx/openedx-atlas/-/openedx-atlas-0.6.0.tgz",
"integrity": "sha512-wZO7hA4VJ/bXjaQNNR7KXGYyTCNs1mBJd3HwQK2EmOwFZYFNX6nzSAm9S7HCfi/kb1PCRpmp3wJt+v/Eu9BEQg==",
"bin": {
"atlas": "atlas"
}
},
"node_modules/@edx/paragon": {
"version": "20.46.0",
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.46.0.tgz",
@@ -5596,225 +5570,6 @@
"node": ">= 8"
}
},
"node_modules/@openedx/paragon": {
"version": "22.0.0",
"resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-22.0.0.tgz",
"integrity": "sha512-2tD5SEu6kNf2Llop/FylqTI87mlG0jaeAhGiX57bABcHZ/cDParCxX/unPiui6LCfdrDhghjVBKD3U2+Qn6Wag==",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/react-fontawesome": "^0.1.18",
"@popperjs/core": "^2.11.4",
"bootstrap": "^4.6.2",
"chalk": "^4.1.2",
"child_process": "^1.0.2",
"classnames": "^2.3.1",
"email-prop-type": "^3.0.0",
"file-selector": "^0.6.0",
"font-awesome": "^4.7.0",
"glob": "^8.0.3",
"inquirer": "^8.2.5",
"lodash.uniqby": "^4.7.0",
"mailto-link": "^2.0.0",
"prop-types": "^15.8.1",
"react-bootstrap": "^1.6.5",
"react-colorful": "^5.6.1",
"react-dropzone": "^14.2.1",
"react-focus-on": "^3.5.4",
"react-imask": "^7.1.3",
"react-loading-skeleton": "^3.1.0",
"react-popper": "^2.2.5",
"react-proptype-conditional-require": "^1.0.4",
"react-responsive": "^8.2.0",
"react-table": "^7.7.0",
"react-transition-group": "^4.4.2",
"tabbable": "^5.3.3",
"uncontrollable": "^7.2.1",
"uuid": "^9.0.0"
},
"bin": {
"paragon": "bin/paragon-scripts.js"
},
"peerDependencies": {
"react": "^16.8.6 || ^17.0.0",
"react-dom": "^16.8.6 || ^17.0.0",
"react-intl": "^5.25.1 || ^6.4.0"
}
},
"node_modules/@openedx/paragon/node_modules/@fortawesome/fontawesome-common-types": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz",
"integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==",
"hasInstallScript": true,
"engines": {
"node": ">=6"
}
},
"node_modules/@openedx/paragon/node_modules/@fortawesome/fontawesome-svg-core": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz",
"integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==",
"hasInstallScript": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.5.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@openedx/paragon/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/@openedx/paragon/node_modules/cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
"dependencies": {
"restore-cursor": "^3.1.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@openedx/paragon/node_modules/cli-width": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
"integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
"engines": {
"node": ">= 10"
}
},
"node_modules/@openedx/paragon/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/@openedx/paragon/node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@openedx/paragon/node_modules/figures": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
"integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
"dependencies": {
"escape-string-regexp": "^1.0.5"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@openedx/paragon/node_modules/glob": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^5.0.1",
"once": "^1.3.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@openedx/paragon/node_modules/inquirer": {
"version": "8.2.6",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz",
"integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==",
"dependencies": {
"ansi-escapes": "^4.2.1",
"chalk": "^4.1.1",
"cli-cursor": "^3.1.0",
"cli-width": "^3.0.0",
"external-editor": "^3.0.3",
"figures": "^3.0.0",
"lodash": "^4.17.21",
"mute-stream": "0.0.8",
"ora": "^5.4.1",
"run-async": "^2.4.0",
"rxjs": "^7.5.5",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0",
"through": "^2.3.6",
"wrap-ansi": "^6.0.1"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/@openedx/paragon/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"engines": {
"node": ">=8"
}
},
"node_modules/@openedx/paragon/node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@openedx/paragon/node_modules/mute-stream": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
},
"node_modules/@openedx/paragon/node_modules/restore-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
"dependencies": {
"onetime": "^5.1.0",
"signal-exit": "^3.0.2"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@openedx/paragon/node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/@openedx/paragon/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@optimizely/js-sdk-logging": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@optimizely/js-sdk-logging/-/js-sdk-logging-0.3.1.tgz",
@@ -8586,7 +8341,8 @@
"node_modules/chardet": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
"dev": true
},
"node_modules/check-types": {
"version": "7.3.0",
@@ -8709,11 +8465,6 @@
"node": ">= 6"
}
},
"node_modules/child_process": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz",
"integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g=="
},
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
@@ -8933,17 +8684,6 @@
"node": ">=4"
}
},
"node_modules/cli-spinners": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
"integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/cli-width": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
@@ -9010,14 +8750,6 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/clone": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
"integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
"engines": {
"node": ">=0.8"
}
},
"node_modules/clone-deep": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
@@ -9901,17 +9633,6 @@
"node": ">= 10"
}
},
"node_modules/defaults": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
"integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
"dependencies": {
"clone": "^1.0.2"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
@@ -11513,6 +11234,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
"integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
"dev": true,
"dependencies": {
"chardet": "^0.7.0",
"iconv-lite": "^0.4.24",
@@ -13117,17 +12839,6 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/imask": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/imask/-/imask-7.3.0.tgz",
"integrity": "sha512-TG+/rfb62JaQDM2KVrzEHMb4lv2srbsby7vHndXhqgQFB1MgPIXl60VQUfly/Xv5iWfA9ytB+rfQ+skUgINw7A==",
"dependencies": {
"@babel/runtime-corejs3": "^7.23.6"
},
"engines": {
"npm": ">=4.0.0"
}
},
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
@@ -13769,14 +13480,6 @@
"node": ">=0.10.0"
}
},
"node_modules/is-interactive": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
"integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
"engines": {
"node": ">=8"
}
},
"node_modules/is-invalid-path": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/is-invalid-path/-/is-invalid-path-0.1.0.tgz",
@@ -14019,17 +13722,6 @@
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
},
"node_modules/is-unicode-supported": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
"integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-valid-path": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-valid-path/-/is-valid-path-0.1.1.tgz",
@@ -17528,21 +17220,6 @@
"resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz",
"integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww=="
},
"node_modules/log-symbols": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
"integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
"dependencies": {
"chalk": "^4.1.0",
"is-unicode-supported": "^0.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/logform": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz",
@@ -19062,55 +18739,11 @@
"node": ">= 0.8.0"
}
},
"node_modules/ora": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
"integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
"dependencies": {
"bl": "^4.1.0",
"chalk": "^4.1.0",
"cli-cursor": "^3.1.0",
"cli-spinners": "^2.5.0",
"is-interactive": "^1.0.0",
"is-unicode-supported": "^0.1.0",
"log-symbols": "^4.1.0",
"strip-ansi": "^6.0.0",
"wcwidth": "^1.0.1"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ora/node_modules/cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
"dependencies": {
"restore-cursor": "^3.1.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ora/node_modules/restore-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
"dependencies": {
"onetime": "^5.1.0",
"signal-exit": "^3.0.2"
},
"engines": {
"node": ">=8"
}
},
"node_modules/os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -20871,21 +20504,6 @@
"react": ">=16.3.0"
}
},
"node_modules/react-imask": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/react-imask/-/react-imask-7.3.0.tgz",
"integrity": "sha512-AHoQUeXil6PfqDzJHN08hO2liWxNDRJosNUa2XSqliFY2tXGL/3Elm0msupDNAyNPItAnyF9G5FGFoCfiCn+AQ==",
"dependencies": {
"imask": "^7.3.0",
"prop-types": "^15.8.1"
},
"engines": {
"npm": ">=4.0.0"
},
"peerDependencies": {
"react": ">=0.14.0"
}
},
"node_modules/react-intl": {
"version": "5.25.1",
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz",
@@ -21790,6 +21408,7 @@
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
"integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
"dev": true,
"engines": {
"node": ">=0.12.0"
}
@@ -23985,7 +23604,8 @@
"node_modules/through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
"dev": true
},
"node_modules/thunky": {
"version": "1.1.0",
@@ -24016,6 +23636,7 @@
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
"dev": true,
"dependencies": {
"os-tmpdir": "~1.0.2"
},
@@ -24939,14 +24560,6 @@
"minimalistic-assert": "^1.0.0"
}
},
"node_modules/wcwidth": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
"integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
"dependencies": {
"defaults": "^1.0.3"
}
},
"node_modules/webidl-conversions": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",

View File

@@ -33,10 +33,9 @@
"@edx/frontend-component-footer": "12.2.1",
"@edx/frontend-component-header": "4.6.0",
"@edx/frontend-lib-special-exams": "2.27.0",
"@edx/frontend-lib-learning-assistant": "^1.20.1",
"@edx/frontend-lib-learning-assistant": "^1.19.0",
"@edx/frontend-platform": "5.5.2",
"@edx/openedx-atlas": "^0.6.0",
"@openedx/paragon": "22.0.0",
"@edx/paragon": "20.46.0",
"@edx/react-unit-test-utils": "npm:@edx/react-unit-test-utils@1.7.0",
"@fortawesome/fontawesome-svg-core": "1.3.0",
"@fortawesome/free-brands-svg-icons": "5.15.4",

View File

@@ -55,29 +55,6 @@ describe('Data layer integration tests', () => {
expect(store.getState().courseHome.courseStatus).toEqual('failed');
});
it('should result in fetch failed if course metadata call errored', async () => {
const datesTabData = Factory.build('datesTabData');
const datesUrl = `${datesBaseUrl}/${courseId}`;
axiosMock.onGet(courseMetadataUrl).networkError();
axiosMock.onGet(datesUrl).reply(200, datesTabData);
await executeThunk(thunks.fetchDatesTab(courseId), store.dispatch);
expect(loggingService.logError).toHaveBeenCalled();
expect(store.getState().courseHome.courseStatus).toEqual('failed');
});
it('should result in fetch failed if course metadata call errored', async () => {
axiosMock.onGet(courseMetadataUrl).reply(200, courseHomeMetadata);
axiosMock.onGet(`${datesBaseUrl}/${courseId}`).networkError();
await executeThunk(thunks.fetchDatesTab(courseId), store.dispatch);
expect(loggingService.logError).toHaveBeenCalled();
expect(store.getState().courseHome.courseStatus).toEqual('failed');
});
it('Should fetch, normalize, and save metadata', async () => {
const datesTabData = Factory.build('datesTabData');
@@ -101,14 +78,18 @@ describe('Data layer integration tests', () => {
});
it.each([401, 403, 404])(
'should result in fetch denied if course access is denied, regardless of dates API status',
'should result in fetch denied for expected errors and failed for all others',
async (errorStatus) => {
axiosMock.onGet(courseMetadataUrl).reply(200, courseHomeAccessDeniedMetadata);
axiosMock.onGet(`${datesBaseUrl}/${courseId}`).reply(errorStatus, {});
await executeThunk(thunks.fetchDatesTab(courseId), store.dispatch);
expect(store.getState().courseHome.courseStatus).toEqual('denied');
let expectedState = 'failed';
if (errorStatus === 401 || errorStatus === 403) {
expectedState = 'denied';
}
expect(store.getState().courseHome.courseStatus).toEqual(expectedState);
},
);
});
@@ -148,14 +129,18 @@ describe('Data layer integration tests', () => {
});
it.each([401, 403, 404])(
'should result in fetch denied if course access is denied, regardless of outline API status',
'should result in fetch denied for expected errors and failed for all others',
async (errorStatus) => {
axiosMock.onGet(courseMetadataUrl).reply(200, courseHomeAccessDeniedMetadata);
axiosMock.onGet(outlineUrl).reply(errorStatus, {});
await executeThunk(thunks.fetchOutlineTab(courseId), store.dispatch);
expect(store.getState().courseHome.courseStatus).toEqual('denied');
let expectedState = 'failed';
if (errorStatus === 403) {
expectedState = 'denied';
}
expect(store.getState().courseHome.courseStatus).toEqual(expectedState);
},
);
});

View File

@@ -38,41 +38,28 @@ export function fetchTab(courseId, tab, getTabData, targetUserId) {
return async (dispatch) => {
dispatch(fetchTabRequest({ courseId }));
try {
const promisesToFulfill = [getCourseHomeCourseMetadata(courseId, 'outline')];
if (getTabData) {
promisesToFulfill.push(getTabData(courseId, targetUserId));
}
const [
courseHomeCourseMetadataResult,
tabDataResult,
] = await Promise.allSettled(promisesToFulfill);
if (courseHomeCourseMetadataResult.status === 'fulfilled') {
dispatch(addModel({
modelType: 'courseHomeMeta',
model: {
id: courseId,
...courseHomeCourseMetadataResult.value,
},
}));
}
if (tabDataResult?.status === 'fulfilled') {
const courseHomeCourseMetadata = await getCourseHomeCourseMetadata(courseId, 'outline');
dispatch(addModel({
modelType: 'courseHomeMeta',
model: {
id: courseId,
...courseHomeCourseMetadata,
},
}));
const tabDataResult = getTabData && await getTabData(courseId, targetUserId);
if (tabDataResult) {
dispatch(addModel({
modelType: tab,
model: {
id: courseId,
...tabDataResult.value,
...tabDataResult,
},
}));
}
if (courseHomeCourseMetadataResult.status === 'rejected') {
throw courseHomeCourseMetadataResult.reason;
} else if (!courseHomeCourseMetadataResult.value.courseAccess.hasAccess) {
// If the learner does not have access to the course, short cut to dispatch to a denied response regardless of
// the tabDataResult.
// Disable the access-denied path for now - it caused a regression
if (!courseHomeCourseMetadata.courseAccess.hasAccess) {
dispatch(fetchTabDenied({ courseId }));
} else if (tabDataResult?.status === 'rejected') {
throw tabDataResult.reason;
} else {
} else if (tabDataResult || !getTabData) {
dispatch(fetchTabSuccess({
courseId,
targetUserId,

View File

@@ -91,7 +91,6 @@ const Course = ({
courseId={courseId}
contentToolsEnabled={course.showCalculator || course.notes.enabled}
unitId={unitId}
endDate={course.end ? course.end : ''}
/>
{enableNewSidebar === 'true' ? <NewSidebarTriggers /> : <SidebarTriggers /> }
</>

View File

@@ -1,16 +1,18 @@
import React from 'react';
import { Factory } from 'rosie';
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import MockAdapter from 'axios-mock-adapter';
import { breakpoints } from '@edx/paragon';
import {
act, fireEvent, getByRole, initializeTestStore, loadUnit, render, screen, waitFor,
} from '../../setupTest';
import * as celebrationUtils from './celebration/utils';
import { buildTopicsFromUnits } from '../data/__factories__/discussionTopics.factory';
import { handleNextSectionCelebration } from './celebration';
import * as celebrationUtils from './celebration/utils';
import Course from './Course';
import setupDiscussionSidebar from './test-utils';
import { executeThunk } from '../../utils';
import * as thunks from '../data/thunks';
jest.mock('@edx/frontend-platform/analytics');
jest.mock('@edx/frontend-lib-special-exams/dist/data/thunks.js', () => ({
@@ -49,6 +51,26 @@ describe('Course', () => {
setItemSpy.mockRestore();
});
const setupDiscussionSidebar = async () => {
const courseHomeMetadata = Factory.build('courseHomeMetadata', { verified_mode: null });
const testStore = await initializeTestStore({ provider: 'openedx', courseHomeMetadata });
const state = testStore.getState();
const { courseware: { courseId } } = state;
const axiosMock = new MockAdapter(getAuthenticatedHttpClient());
axiosMock.onGet(`${getConfig().LMS_BASE_URL}/api/discussion/v1/courses/${courseId}`).reply(200, { provider: 'openedx' });
const topicsResponse = buildTopicsFromUnits(state.models.units);
axiosMock.onGet(`${getConfig().LMS_BASE_URL}/api/discussion/v2/course_topics/${courseId}`)
.reply(200, topicsResponse);
await executeThunk(thunks.getCourseDiscussionTopics(courseId), testStore.dispatch);
const [firstUnitId] = Object.keys(state.models.units);
mockData.unitId = firstUnitId;
const [firstSequenceId] = Object.keys(state.models.sequences);
mockData.sequenceId = firstSequenceId;
await render(<Course {...mockData} />, { store: testStore, wrapWithRouter: true });
};
it('loads learning sequence', async () => {
render(<Course {...mockData} />, { wrapWithRouter: true });
expect(screen.getByRole('navigation', { name: 'breadcrumb' })).toBeInTheDocument();
@@ -161,7 +183,7 @@ describe('Course', () => {
});
it('handles click to open/close notification tray', async () => {
await setupDiscussionSidebar();
render(<Course {...mockData} />, { wrapWithRouter: true });
const notificationShowButton = await screen.findByRole('button', { name: /Show notification tray/i });
expect(screen.queryByRole('region', { name: /notification tray/i })).toHaveClass('d-none');
fireEvent.click(notificationShowButton);

View File

@@ -11,7 +11,6 @@ const Chat = ({
courseId,
contentToolsEnabled,
unitId,
endDate,
}) => {
const VERIFIED_MODES = [
'professional',
@@ -24,23 +23,22 @@ const Chat = ({
'paid-bootcamp',
];
const hasVerifiedEnrollment = (
const AUDIT_MODES = [
'audit',
'honor',
'unpaid-executive-education',
'unpaid-bootcamp',
];
const isEnrolled = (
enrollmentMode !== null
&& enrollmentMode !== undefined
&& [...VERIFIED_MODES].some(mode => mode === enrollmentMode)
&& [...VERIFIED_MODES, ...AUDIT_MODES].some(mode => mode === enrollmentMode)
);
const endDatePassed = () => {
const date = new Date();
const utcDate = date.toISOString();
return endDate ? utcDate > endDate : false; // evaluate if end date has passed only if course has end date
};
const shouldDisplayChat = (
enabled
&& (hasVerifiedEnrollment || isStaff) // display only to verified learners or staff
&& !endDatePassed()
&& (isEnrolled || isStaff) // display only to enrolled or staff
);
return (
@@ -61,7 +59,6 @@ Chat.propTypes = {
courseId: PropTypes.string.isRequired,
contentToolsEnabled: PropTypes.bool.isRequired,
unitId: PropTypes.string.isRequired,
endDate: PropTypes.string.isRequired,
};
Chat.defaultProps = {

View File

@@ -16,10 +16,9 @@ let enabledTestCases = [];
let disabledTestCases = [];
const enabledModes = [
'professional', 'verified', 'no-id-professional', 'credit', 'masters', 'executive-education',
'paid-executive-education', 'paid-bootcamp',
'paid-executive-education', 'paid-bootcamp', 'audit', 'honor', 'unpaid-executive-education', 'unpaid-bootcamp',
];
const disabledModes = [null, undefined, 'xyz', 'audit', 'honor', 'unpaid-executive-education', 'unpaid-bootcamp'];
const currentTime = new Date();
const disabledModes = [null, undefined, 'xyz'];
describe('Chat', () => {
// Generate test cases.
@@ -45,7 +44,6 @@ describe('Chat', () => {
enabled
courseId={courseId}
contentToolsEnabled={false}
endDate={new Date(currentTime.getTime() + 10 * 60000).toISOString()}
/>
</BrowserRouter>,
{ store },
@@ -79,7 +77,6 @@ describe('Chat', () => {
enabled
courseId={courseId}
contentToolsEnabled={false}
endDate={new Date(currentTime.getTime() + 10 * 60000).toISOString()}
/>
</BrowserRouter>,
{ store },
@@ -141,7 +138,6 @@ describe('Chat', () => {
enabled={test.enabled}
courseId={courseId}
contentToolsEnabled={false}
endDate={new Date(currentTime.getTime() + 10 * 60000).toISOString()}
/>
</BrowserRouter>,
{ store },
@@ -156,54 +152,4 @@ describe('Chat', () => {
},
);
});
it('if course end date has passed, component should not be visible', async () => {
const store = configureStore({
reducer: {
learningAssistant: learningAssistantReducer,
},
});
render(
<BrowserRouter>
<Chat
enrollmentMode="verified"
isStaff
enabled
courseId={courseId}
contentToolsEnabled={false}
endDate={new Date(currentTime.getTime() - 10 * 60000).toISOString()}
/>
</BrowserRouter>,
{ store },
);
const chat = screen.queryByTestId('toggle-button');
expect(chat).not.toBeInTheDocument();
});
it('if course has no end date, component should be visible', async () => {
const store = configureStore({
reducer: {
learningAssistant: learningAssistantReducer,
},
});
render(
<BrowserRouter>
<Chat
enrollmentMode="verified"
isStaff
enabled
courseId={courseId}
contentToolsEnabled={false}
endDate={null}
/>
</BrowserRouter>,
{ store },
);
const chat = screen.queryByTestId('toggle-button');
expect(chat).toBeInTheDocument();
});
});

View File

@@ -91,7 +91,7 @@ const SidebarBase = ({
};
SidebarBase.propTypes = {
title: PropTypes.string,
title: PropTypes.string.isRequired,
ariaLabel: PropTypes.string.isRequired,
sidebarId: PropTypes.string.isRequired,
className: PropTypes.string,
@@ -103,7 +103,6 @@ SidebarBase.propTypes = {
};
SidebarBase.defaultProps = {
title: '',
width: '50rem',
allowFullHeight: false,
showTitleBar: true,

View File

@@ -12,7 +12,6 @@ import { executeThunk } from '../../../../../../utils';
import { buildTopicsFromUnits } from '../../../../../data/__factories__/discussionTopics.factory';
import { getCourseDiscussionTopics } from '../../../../../data/thunks';
import SidebarContext from '../../../SidebarContext';
import DiscussionsNotificationsSidebar from '../DiscussionsNotificationsSidebar';
import DiscussionsWidget from './DiscussionsWidget';
initializeMockApp();
@@ -52,29 +51,24 @@ describe('DiscussionsWidget', () => {
await executeThunk(getCourseDiscussionTopics(courseId), store.dispatch);
});
function renderWithProvider(Component, testData = {}) {
function renderWithProvider(testData = {}) {
const { container } = render(
<SidebarContext.Provider value={{ ...mockData, ...testData }}>
<Component />
<DiscussionsWidget />
</SidebarContext.Provider>,
);
return container;
}
it('should show up if unit discussions associated with it', async () => {
renderWithProvider(DiscussionsWidget);
renderWithProvider();
expect(screen.queryByTitle('Discussions')).toBeInTheDocument();
expect(screen.queryByTitle('Discussions'))
.toHaveAttribute('src', `http://localhost:2002/${courseId}/category/${unitId}?inContextSidebar`);
});
it('should show nothing if unit has no discussions associated with it', async () => {
renderWithProvider(DiscussionsWidget, { isDiscussionbarAvailable: false });
renderWithProvider({ isDiscussionbarAvailable: false });
expect(screen.queryByTitle('Discussions')).not.toBeInTheDocument();
});
it('should display the Back to course button on small screens.', async () => {
renderWithProvider(DiscussionsNotificationsSidebar, { shouldDisplayFullScreen: true });
expect(screen.queryByText('Back to course')).toBeInTheDocument();
});
});

View File

@@ -38,7 +38,7 @@ const NotificationsWidget = () => {
if (hideNotificationbar || !isNotificationbarAvailable) { return null; }
return (
<div className="border border-light-400 rounded-sm" data-testid="notification-widget">
<div className="border border-light-400 rounded-sm">
<UpgradeNotification
offer={offer}
verifiedMode={verifiedMode}

View File

@@ -4,19 +4,16 @@ import React from 'react';
import MockAdapter from 'axios-mock-adapter';
import { Factory } from 'rosie';
import { mergeConfig, getConfig } from '@edx/frontend-platform';
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { breakpoints } from '@edx/paragon';
import {
initializeMockApp, render, screen, within, act, fireEvent, waitFor,
} from '../../../../../../setupTest';
import { initializeMockApp, render, screen } from '../../../../../../setupTest';
import initializeStore from '../../../../../../store';
import { appendBrowserTimezoneToUrl, executeThunk } from '../../../../../../utils';
import { fetchCourse } from '../../../../../data';
import SidebarContext from '../../../SidebarContext';
import NotificationsWidget from './NotificationsWidget';
import setupDiscussionSidebar from '../../../../test-utils';
initializeMockApp();
jest.mock('@edx/frontend-platform/analytics');
@@ -25,6 +22,7 @@ describe('NotificationsWidget', () => {
let axiosMock;
let store;
const ID = 'NEWSIDEBAR';
const defaultMetadata = Factory.build('courseMetadata');
const courseId = defaultMetadata.id;
let courseMetadataUrl = `${getConfig().LMS_BASE_URL}/api/courseware/course/${defaultMetadata.id}`;
@@ -49,35 +47,6 @@ describe('NotificationsWidget', () => {
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
axiosMock.onGet(courseMetadataUrl).reply(200, defaultMetadata);
axiosMock.onGet(courseHomeMetadataUrl).reply(200, courseHomeMetadata);
mergeConfig({ ENABLE_NEW_SIDEBAR: 'true' }, 'Custom app config');
});
it('successfully Open/Hide sidebar tray.', async () => {
const userVerifiedMode = Factory.build('verifiedMode');
await setupDiscussionSidebar(userVerifiedMode);
const sidebarButton = await screen.getByRole('button', { name: /Show sidebar tray/i });
await act(async () => {
fireEvent.click(sidebarButton);
});
await waitFor(async () => {
expect(screen.queryByTestId('sidebar-DISCUSSIONS_NOTIFICATIONS')).toBeInTheDocument();
expect(screen.queryByTestId('notification-widget')).toBeInTheDocument();
expect(screen.queryByTitle('Discussions')).toBeInTheDocument();
});
await act(async () => {
fireEvent.click(sidebarButton);
});
await waitFor(async () => {
expect(screen.queryByTestId('sidebar-DISCUSSIONS_NOTIFICATIONS')).not.toBeInTheDocument();
expect(screen.queryByTestId('notification-widget')).not.toBeInTheDocument();
expect(screen.queryByTitle('Discussions')).not.toBeInTheDocument();
});
});
it('renders upgrade card', async () => {
@@ -121,41 +90,6 @@ describe('NotificationsWidget', () => {
.toBeInTheDocument();
});
it.each([
{
description: 'close the notification widget.',
enabledInContext: true,
testId:
'notification-widget',
},
{
description: 'close the sidebar when the notification widget is closed, and the discussion widget is unavailable.',
enabledInContext: false,
testId: 'sidebar-DISCUSSIONS_NOTIFICATIONS',
},
])('successfully %s', async ({ enabledInContext, testId }) => {
const userVerifiedMode = Factory.build('verifiedMode');
await setupDiscussionSidebar(userVerifiedMode, enabledInContext);
const sidebarButton = screen.getByRole('button', { name: /Show sidebar tray/i });
await act(async () => {
fireEvent.click(sidebarButton);
});
const notificationWidget = await waitFor(() => screen.getByTestId('notification-widget'));
const closeNotificationButton = within(notificationWidget).getByRole('button', { name: /Close/i });
await act(async () => {
fireEvent.click(closeNotificationButton);
});
await waitFor(() => {
expect(screen.queryByTestId(testId)).not.toBeInTheDocument();
});
});
it('marks notification as seen 3 seconds later', async () => {
jest.useFakeTimers();
const onNotificationSeen = jest.fn();

View File

@@ -1,49 +0,0 @@
import React from 'react';
import { Factory } from 'rosie';
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import MockAdapter from 'axios-mock-adapter';
import { breakpoints } from '@edx/paragon';
import { initializeTestStore, render } from '../../setupTest';
import { buildTopicsFromUnits } from '../data/__factories__/discussionTopics.factory';
import { executeThunk } from '../../utils';
import * as thunks from '../data/thunks';
import Course from './Course';
const mockData = {
nextSequenceHandler: () => {},
previousSequenceHandler: () => {},
unitNavigationHandler: () => {},
};
const setupDiscussionSidebar = async (verifiedMode = null, enabledInContext = true) => {
const store = await initializeTestStore();
const { courseware, models } = store.getState();
const { courseId, sequenceId } = courseware;
Object.assign(mockData, {
courseId,
sequenceId,
unitId: Object.values(models.units)[0].id,
});
global.innerWidth = breakpoints.extraLarge.minWidth;
const courseHomeMetadata = Factory.build('courseHomeMetadata', { verified_mode: verifiedMode });
const testStore = await initializeTestStore({ provider: 'openedx', courseHomeMetadata });
const state = testStore.getState();
const axiosMock = new MockAdapter(getAuthenticatedHttpClient());
axiosMock.onGet(`${getConfig().LMS_BASE_URL}/api/discussion/v1/courses/${courseId}`).reply(200, { provider: 'openedx' });
const topicsResponse = buildTopicsFromUnits(state.models.units, enabledInContext);
axiosMock.onGet(`${getConfig().LMS_BASE_URL}/api/discussion/v2/course_topics/${courseId}`)
.reply(200, topicsResponse);
await executeThunk(thunks.getCourseDiscussionTopics(courseId), testStore.dispatch);
const [firstUnitId] = Object.keys(state.models.units);
mockData.unitId = firstUnitId;
const [firstSequenceId] = Object.keys(state.models.sequences);
mockData.sequenceId = firstSequenceId;
const wrapper = await render(<Course {...mockData} />, { store: testStore, wrapWithRouter: true });
return wrapper;
};
export default setupDiscussionSidebar;

View File

@@ -1,13 +1,6 @@
/* eslint-disable import/prefer-default-export */
import { Factory } from 'rosie'; // eslint-disable-line import/no-extraneous-dependencies
Factory.define('verifiedMode')
.attr('currency', 'USD')
.attr('currencySymbol', '$')
.attr('price', '$149')
.attr('sku', '8CF08E5')
.attr('upgradeUrl', 'http://localhost:18130/basket/add/?sku=8CF08E5');
Factory.define('discussionTopic')
.option('topicPrefix', null, '')
.option('courseId', null, 'course-v1:edX+DemoX+Demo_Course')
@@ -18,14 +11,13 @@ Factory.define('discussionTopic')
['id', 'courseId'],
(idx, id, courseId) => `block-v1:${courseId.replace('course-v1:', '')}+type@vertical+block@${id}`,
)
.attr('enabled_in_context', ['enabled_in_context'], (enabledInContext) => Boolean(enabledInContext))
.attr('enabled_in_context', null, true)
.attr('thread_counts', [], {
discussion: 0,
question: 0,
});
// Given a pre-build units state, build topics from it.
export function buildTopicsFromUnits(units, enabledInContext = true) {
return Object.values(units).map(unit => Factory.build('discussionTopic', { usage_key: unit.id, enabled_in_context: enabledInContext }));
export function buildTopicsFromUnits(units) {
return Object.values(units).map(unit => Factory.build('discussionTopic', { usage_key: unit.id }));
}

View File

@@ -4,7 +4,7 @@
@import "~@edx/brand/paragon/overrides";
@import "~@edx/frontend-component-footer/dist/footer";
@import "@edx/frontend-component-header/dist/index";
@import "~@edx/frontend-component-header/dist/index";
#root {

View File

@@ -1,4 +1,5 @@
import { reducer as learningAssistantReducer } from '@edx/frontend-lib-learning-assistant';
import { reducer as examsReducer } from '@edx/frontend-lib-special-exams';
import { configureStore } from '@reduxjs/toolkit';
import { reducer as courseHomeReducer } from './course-home/data';
import { reducer as coursewareReducer } from './courseware/data/slice';
@@ -12,6 +13,7 @@ export default function initializeStore() {
models: modelsReducer,
courseware: coursewareReducer,
courseHome: courseHomeReducer,
exams: examsReducer,
learningAssistant: learningAssistantReducer,
recommendations: recommendationsReducer,
tours: toursReducer,