Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2540bc3a0 | ||
|
|
ffb5a765e2 | ||
|
|
952e543217 | ||
|
|
45a1da9f5e | ||
|
|
022515d1d2 | ||
|
|
2d737aae7f | ||
|
|
4c4db14eac | ||
|
|
911cea6a0e | ||
|
|
a52ddfd9bd | ||
|
|
8175ba897a | ||
|
|
cfda72b2e2 | ||
|
|
4483a734bc | ||
|
|
db1903cdce | ||
|
|
71851b13a6 | ||
|
|
6efa31092d | ||
|
|
c3541a3d79 |
125
package-lock.json
generated
125
package-lock.json
generated
@@ -9,13 +9,14 @@
|
|||||||
"version": "1.0.0-semantically-released",
|
"version": "1.0.0-semantically-released",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@edx/paragon": "20.44.0",
|
"@edx/paragon": "20.45.0",
|
||||||
"@fortawesome/fontawesome-svg-core": "6.3.0",
|
"@fortawesome/fontawesome-svg-core": "6.3.0",
|
||||||
"@fortawesome/free-brands-svg-icons": "6.3.0",
|
"@fortawesome/free-brands-svg-icons": "6.3.0",
|
||||||
"@fortawesome/free-regular-svg-icons": "6.3.0",
|
"@fortawesome/free-regular-svg-icons": "6.3.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "6.3.0",
|
"@fortawesome/free-solid-svg-icons": "6.3.0",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
"@reduxjs/toolkit": "1.9.5",
|
"@reduxjs/toolkit": "1.9.5",
|
||||||
|
"axios-mock-adapter": "1.21.5",
|
||||||
"babel-polyfill": "6.26.0",
|
"babel-polyfill": "6.26.0",
|
||||||
"classnames": "2.3.2",
|
"classnames": "2.3.2",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
@@ -23,15 +24,16 @@
|
|||||||
"react-responsive": "8.2.0",
|
"react-responsive": "8.2.0",
|
||||||
"react-router-dom": "5.3.4",
|
"react-router-dom": "5.3.4",
|
||||||
"react-transition-group": "4.4.5",
|
"react-transition-group": "4.4.5",
|
||||||
|
"rosie": "2.1.0",
|
||||||
"timeago.js": "4.0.2"
|
"timeago.js": "4.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
|
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
|
||||||
"@edx/browserslist-config": "^1.1.1",
|
"@edx/browserslist-config": "^1.1.1",
|
||||||
"@edx/frontend-build": "12.8.51",
|
"@edx/frontend-build": "12.8.57",
|
||||||
"@edx/frontend-platform": "4.5.1",
|
"@edx/frontend-platform": "4.5.1",
|
||||||
"@edx/reactifex": "^2.1.1",
|
"@edx/reactifex": "^2.1.1",
|
||||||
"@testing-library/dom": "9.3.0",
|
"@testing-library/dom": "9.3.1",
|
||||||
"@testing-library/jest-dom": "5.16.5",
|
"@testing-library/jest-dom": "5.16.5",
|
||||||
"@testing-library/react": "10.4.9",
|
"@testing-library/react": "10.4.9",
|
||||||
"enzyme": "3.11.0",
|
"enzyme": "3.11.0",
|
||||||
@@ -2149,9 +2151,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@edx/frontend-build": {
|
"node_modules/@edx/frontend-build": {
|
||||||
"version": "12.8.51",
|
"version": "12.8.57",
|
||||||
"resolved": "https://registry.npmjs.org/@edx/frontend-build/-/frontend-build-12.8.51.tgz",
|
"resolved": "https://registry.npmjs.org/@edx/frontend-build/-/frontend-build-12.8.57.tgz",
|
||||||
"integrity": "sha512-UsOJ7kN/ECxTDxGYbigGOLQnNtTSmwXqJTed3AUh292U3LEobqho8/C9z2j18xyXhbLY8TJlCWrPkhPFtqFOug==",
|
"integrity": "sha512-mbWyGtF351pKs2/wtN0B16xor5ZowDw70ddBPqE5sasbIlWQw82Yx8Wie6vBjDAweCNDffOCKuIycYJYilWO8Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/cli": "7.22.5",
|
"@babel/cli": "7.22.5",
|
||||||
@@ -2187,7 +2189,7 @@
|
|||||||
"file-loader": "6.2.0",
|
"file-loader": "6.2.0",
|
||||||
"html-webpack-plugin": "5.5.3",
|
"html-webpack-plugin": "5.5.3",
|
||||||
"identity-obj-proxy": "3.0.0",
|
"identity-obj-proxy": "3.0.0",
|
||||||
"image-minimizer-webpack-plugin": "3.8.2",
|
"image-minimizer-webpack-plugin": "3.8.3",
|
||||||
"jest": "26.6.3",
|
"jest": "26.6.3",
|
||||||
"mini-css-extract-plugin": "1.6.2",
|
"mini-css-extract-plugin": "1.6.2",
|
||||||
"postcss": "8.4.24",
|
"postcss": "8.4.24",
|
||||||
@@ -2197,7 +2199,7 @@
|
|||||||
"react-dev-utils": "12.0.1",
|
"react-dev-utils": "12.0.1",
|
||||||
"react-refresh": "0.14.0",
|
"react-refresh": "0.14.0",
|
||||||
"resolve-url-loader": "5.0.0",
|
"resolve-url-loader": "5.0.0",
|
||||||
"sass": "1.63.3",
|
"sass": "1.63.6",
|
||||||
"sass-loader": "13.3.2",
|
"sass-loader": "13.3.2",
|
||||||
"sharp": "^0.32.0",
|
"sharp": "^0.32.0",
|
||||||
"source-map-loader": "^4.0.1",
|
"source-map-loader": "^4.0.1",
|
||||||
@@ -2949,9 +2951,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@edx/frontend-build/node_modules/jest-snapshot/node_modules/semver": {
|
"node_modules/@edx/frontend-build/node_modules/jest-snapshot/node_modules/semver": {
|
||||||
"version": "7.5.1",
|
"version": "7.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz",
|
||||||
"integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
|
"integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lru-cache": "^6.0.0"
|
"lru-cache": "^6.0.0"
|
||||||
@@ -3253,9 +3255,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@edx/paragon": {
|
"node_modules/@edx/paragon": {
|
||||||
"version": "20.44.0",
|
"version": "20.45.0",
|
||||||
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.44.0.tgz",
|
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.45.0.tgz",
|
||||||
"integrity": "sha512-C1uC3RaRmlFANtHebFdZzVDM08vgFJRnHE3u97ix07e0ACSQDbVNoZ2H7JgBy8nqHz2JWGHPnvtpvPf5DAZsZQ==",
|
"integrity": "sha512-9lHcnSJ36sQ+bsYFhydf/Pvp3Bo5N3go8R3ORPTNtvYnwiKSfjlv11QpURC/xHobXsT2eYHiwl2gNmq1yP09BA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.1.1",
|
"@fortawesome/fontawesome-svg-core": "^6.1.1",
|
||||||
"@fortawesome/react-fontawesome": "^0.1.18",
|
"@fortawesome/react-fontawesome": "^0.1.18",
|
||||||
@@ -3339,9 +3341,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@edx/reactifex": {
|
"node_modules/@edx/reactifex": {
|
||||||
"version": "2.1.1",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@edx/reactifex/-/reactifex-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@edx/reactifex/-/reactifex-2.2.0.tgz",
|
||||||
"integrity": "sha512-A/DfCPsNNRuWhhWCquInlfG6Pi//qcxAi0P2jY/UeOVAHoOLkA3L328UtHEuoZbncXT2E1H1EDlpfNrovo/nng==",
|
"integrity": "sha512-vyGDtx3BwCr6Gjbm4y6gJ8Bzc2TOSNBlBa2hMerz59HoXaot14MihxxiDU+JDNybGLLcKDBiK511bOi/77i1lw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
@@ -3375,9 +3377,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@edx/reactifex/node_modules/yargs": {
|
"node_modules/@edx/reactifex/node_modules/yargs": {
|
||||||
"version": "17.7.1",
|
"version": "17.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||||
"integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
|
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cliui": "^8.0.1",
|
"cliui": "^8.0.1",
|
||||||
@@ -6136,15 +6138,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@testing-library/dom": {
|
"node_modules/@testing-library/dom": {
|
||||||
"version": "9.3.0",
|
"version": "9.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz",
|
||||||
"integrity": "sha512-Dffe68pGwI6WlLRYR2I0piIkyole9cSBH5jGQKCGMRpHW5RHCqAUaqc2Kv0tUyd4dU4DLPKhJIjyKOnjv4tuUw==",
|
"integrity": "sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.10.4",
|
"@babel/code-frame": "^7.10.4",
|
||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
"@types/aria-query": "^5.0.1",
|
"@types/aria-query": "^5.0.1",
|
||||||
"aria-query": "^5.0.0",
|
"aria-query": "5.1.3",
|
||||||
"chalk": "^4.1.0",
|
"chalk": "^4.1.0",
|
||||||
"dom-accessibility-api": "^0.5.9",
|
"dom-accessibility-api": "^0.5.9",
|
||||||
"lz-string": "^1.5.0",
|
"lz-string": "^1.5.0",
|
||||||
@@ -7709,8 +7711,7 @@
|
|||||||
"node_modules/asynckit": {
|
"node_modules/asynckit": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/at-least-node": {
|
"node_modules/at-least-node": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -7799,7 +7800,6 @@
|
|||||||
"version": "0.27.2",
|
"version": "0.27.2",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
|
||||||
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
|
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.14.9",
|
"follow-redirects": "^1.14.9",
|
||||||
"form-data": "^4.0.0"
|
"form-data": "^4.0.0"
|
||||||
@@ -7819,6 +7819,40 @@
|
|||||||
"url": "https://github.com/ArthurFiorette/axios-cache-interceptor?sponsor=1"
|
"url": "https://github.com/ArthurFiorette/axios-cache-interceptor?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/axios-mock-adapter": {
|
||||||
|
"version": "1.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.21.5.tgz",
|
||||||
|
"integrity": "sha512-5NI1V/VK+8+JeTF8niqOowuysA4b8mGzdlMN/QnTnoXbYh4HZSNiopsDclN2g/m85+G++IrEtUdZaQ3GnaMsSA==",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"is-buffer": "^2.0.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"axios": ">= 0.17.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/axios-mock-adapter/node_modules/is-buffer": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/axobject-query": {
|
"node_modules/axobject-query": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
|
||||||
@@ -9166,7 +9200,6 @@
|
|||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"delayed-stream": "~1.0.0"
|
"delayed-stream": "~1.0.0"
|
||||||
},
|
},
|
||||||
@@ -9896,7 +9929,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
@@ -11774,8 +11806,7 @@
|
|||||||
"node_modules/fast-deep-equal": {
|
"node_modules/fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/fast-defer": {
|
"node_modules/fast-defer": {
|
||||||
"version": "1.1.7",
|
"version": "1.1.7",
|
||||||
@@ -12052,7 +12083,6 @@
|
|||||||
"version": "1.15.2",
|
"version": "1.15.2",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
@@ -12283,7 +12313,6 @@
|
|||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "^1.0.8",
|
"combined-stream": "^1.0.8",
|
||||||
@@ -13227,12 +13256,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/image-minimizer-webpack-plugin": {
|
"node_modules/image-minimizer-webpack-plugin": {
|
||||||
"version": "3.8.2",
|
"version": "3.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/image-minimizer-webpack-plugin/-/image-minimizer-webpack-plugin-3.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/image-minimizer-webpack-plugin/-/image-minimizer-webpack-plugin-3.8.3.tgz",
|
||||||
"integrity": "sha512-l3nDq/c15y4ViTPtICG6lbFp77SoycSnR1hT/n3ER76uol//OpRptCDl7U1qiDSSEy2AcqPD1T7isRQ8TK27Cw==",
|
"integrity": "sha512-Ex0cjNJc2FUSuwN7WHNyxkIZINP0M9lrN+uWJznMcsehiM5Z7ELwk+SEkSGEookK1GUd2wf+09jy1PEH5a5XmQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"schema-utils": "^4.0.0",
|
"schema-utils": "^4.2.0",
|
||||||
"serialize-javascript": "^6.0.1"
|
"serialize-javascript": "^6.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -13295,9 +13324,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/image-minimizer-webpack-plugin/node_modules/schema-utils": {
|
"node_modules/image-minimizer-webpack-plugin/node_modules/schema-utils": {
|
||||||
"version": "4.0.1",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz",
|
||||||
"integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==",
|
"integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/json-schema": "^7.0.9",
|
"@types/json-schema": "^7.0.9",
|
||||||
@@ -19203,7 +19232,6 @@
|
|||||||
"version": "1.52.0",
|
"version": "1.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@@ -19212,7 +19240,6 @@
|
|||||||
"version": "2.1.35",
|
"version": "2.1.35",
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mime-db": "1.52.0"
|
"mime-db": "1.52.0"
|
||||||
},
|
},
|
||||||
@@ -22628,6 +22655,14 @@
|
|||||||
"rimraf": "bin.js"
|
"rimraf": "bin.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rosie": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rosie/-/rosie-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-Dbzdc+prLXZuB/suRptDnBUY29SdGvND3bLg6cll8n7PNqzuyCxSlRfrkn8PqjS9n4QVsiM7RCvxCkKAkTQRjA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/rst-selector-parser": {
|
"node_modules/rst-selector-parser": {
|
||||||
"version": "2.2.3",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
|
||||||
@@ -23032,9 +23067,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sass": {
|
"node_modules/sass": {
|
||||||
"version": "1.63.3",
|
"version": "1.63.6",
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.63.3.tgz",
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.63.6.tgz",
|
||||||
"integrity": "sha512-ySdXN+DVpfwq49jG1+hmtDslYqpS7SkOR5GpF6o2bmb1RL/xS+wvPmegMvMywyfsmAV6p7TgwXYGrCZIFFbAHg==",
|
"integrity": "sha512-MJuxGMHzaOW7ipp+1KdELtqKbfAWbH7OLIdoSMnVe3EXPMTmxTmlaZDCTsgIpPCs3w99lLo9/zDKkOrJuT5byw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": ">=3.0.0 <4.0.0",
|
"chokidar": ">=3.0.0 <4.0.0",
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -35,10 +35,10 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
|
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
|
||||||
"@edx/browserslist-config": "^1.1.1",
|
"@edx/browserslist-config": "^1.1.1",
|
||||||
"@edx/frontend-build": "12.8.51",
|
"@edx/frontend-build": "12.8.57",
|
||||||
"@edx/frontend-platform": "4.5.1",
|
"@edx/frontend-platform": "4.5.1",
|
||||||
"@edx/reactifex": "^2.1.1",
|
"@edx/reactifex": "^2.1.1",
|
||||||
"@testing-library/dom": "9.3.0",
|
"@testing-library/dom": "9.3.1",
|
||||||
"@testing-library/jest-dom": "5.16.5",
|
"@testing-library/jest-dom": "5.16.5",
|
||||||
"@testing-library/react": "10.4.9",
|
"@testing-library/react": "10.4.9",
|
||||||
"enzyme": "3.11.0",
|
"enzyme": "3.11.0",
|
||||||
@@ -55,21 +55,23 @@
|
|||||||
"redux-saga": "1.2.3"
|
"redux-saga": "1.2.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@edx/paragon": "20.44.0",
|
"@edx/paragon": "20.45.0",
|
||||||
"@fortawesome/fontawesome-svg-core": "6.3.0",
|
"@fortawesome/fontawesome-svg-core": "6.3.0",
|
||||||
"@fortawesome/free-brands-svg-icons": "6.3.0",
|
"@fortawesome/free-brands-svg-icons": "6.3.0",
|
||||||
"@fortawesome/free-regular-svg-icons": "6.3.0",
|
"@fortawesome/free-regular-svg-icons": "6.3.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "6.3.0",
|
"@fortawesome/free-solid-svg-icons": "6.3.0",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
"@reduxjs/toolkit": "1.9.5",
|
"@reduxjs/toolkit": "1.9.5",
|
||||||
|
"axios-mock-adapter": "1.21.5",
|
||||||
"babel-polyfill": "6.26.0",
|
"babel-polyfill": "6.26.0",
|
||||||
"classnames": "2.3.2",
|
"classnames": "2.3.2",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"react-redux": "7.2.9",
|
"react-redux": "7.2.9",
|
||||||
"react-responsive": "8.2.0",
|
"react-responsive": "8.2.0",
|
||||||
|
"react-router-dom": "5.3.4",
|
||||||
"react-transition-group": "4.4.5",
|
"react-transition-group": "4.4.5",
|
||||||
"timeago.js": "4.0.2",
|
"rosie": "2.1.0",
|
||||||
"react-router-dom": "5.3.4"
|
"timeago.js": "4.0.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@edx/frontend-platform": "^4.0.0",
|
"@edx/frontend-platform": "^4.0.0",
|
||||||
|
|||||||
1
src/Notifications/data/__factories__/index.js
Normal file
1
src/Notifications/data/__factories__/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import './notifications.factory';
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { Factory } from 'rosie';
|
||||||
|
|
||||||
|
Factory.define('notificationsCount')
|
||||||
|
.attr('count', 45)
|
||||||
|
.attr('countByAppName', {
|
||||||
|
reminders: 10,
|
||||||
|
discussions: 20,
|
||||||
|
grades: 10,
|
||||||
|
authoring: 5,
|
||||||
|
})
|
||||||
|
.attr('showNotificationsTray', true);
|
||||||
|
|
||||||
|
Factory.define('notification')
|
||||||
|
.sequence('id')
|
||||||
|
.attr('type', 'post')
|
||||||
|
.sequence('content', ['id'], (idx, notificationId) => `<p><b>User ${idx}</b> posts <b>Hello and welcome to SC0x
|
||||||
|
${notificationId}!</b></p>`)
|
||||||
|
.attr('course_name', 'Supply Chain Analytics')
|
||||||
|
.sequence('content_url', (idx) => `https://example.com/${idx}`)
|
||||||
|
.attr('last_read', null)
|
||||||
|
.attr('last_seen', null)
|
||||||
|
.sequence('created_at', ['createdDate'], (idx, date) => date);
|
||||||
@@ -1,40 +1,44 @@
|
|||||||
import { camelCaseObject } from '@edx/frontend-platform';
|
import { getConfig, snakeCaseObject } from '@edx/frontend-platform';
|
||||||
import notificationsList from './notifications.json';
|
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||||
|
|
||||||
|
export const getNotificationsCountApiUrl = () => `${getConfig().LMS_BASE_URL}/api/notifications/count/`;
|
||||||
|
export const getNotificationsApiUrl = () => `${getConfig().LMS_BASE_URL}/api/notifications/`;
|
||||||
|
export const markNotificationsSeenApiUrl = (appName) => `${getConfig().LMS_BASE_URL}/api/notifications/mark-notifications-unseen/${appName}/`;
|
||||||
|
export const markNotificationAsReadApiUrl = () => `${getConfig().LMS_BASE_URL}/api/notifications/read/`;
|
||||||
|
|
||||||
export async function getNotifications(appName, page, pageSize) {
|
export async function getNotifications(appName, page, pageSize) {
|
||||||
const { data } = notificationsList;
|
const params = snakeCaseObject({ page, pageSize });
|
||||||
|
const { data } = await getAuthenticatedHttpClient().get(getNotificationsApiUrl(), { params });
|
||||||
|
|
||||||
const startIndex = (page - 1) * pageSize;
|
const startIndex = (page - 1) * pageSize;
|
||||||
const endIndex = startIndex + pageSize;
|
const endIndex = startIndex + pageSize;
|
||||||
|
|
||||||
const notifications = data.slice(startIndex, endIndex);
|
const notifications = data.slice(startIndex, endIndex);
|
||||||
return { notifications: camelCaseObject(notifications), numPages: 2, currentPage: page };
|
return { notifications, numPages: 2, currentPage: page };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getNotificationCounts() {
|
export async function getNotificationCounts() {
|
||||||
const data = {
|
const { data } = await getAuthenticatedHttpClient().get(getNotificationsCountApiUrl());
|
||||||
count: 45,
|
|
||||||
count_by_app_name: {
|
return data;
|
||||||
reminders: 10,
|
|
||||||
discussions: 20,
|
|
||||||
grades: 10,
|
|
||||||
authoring: 5,
|
|
||||||
},
|
|
||||||
show_notification_tray: false,
|
|
||||||
};
|
|
||||||
return camelCaseObject(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function markNotificationSeen() {
|
export async function markNotificationSeen(appName) {
|
||||||
const data = [];
|
const { data } = await getAuthenticatedHttpClient().put(`${markNotificationsSeenApiUrl(appName)}`);
|
||||||
return camelCaseObject(data);
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function markAllNotificationRead() {
|
export async function markAllNotificationRead(appName) {
|
||||||
const { data } = camelCaseObject(notificationsList);
|
const params = snakeCaseObject({ appName });
|
||||||
|
const { data } = await getAuthenticatedHttpClient().put(markNotificationAsReadApiUrl(), { params });
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function markNotificationRead(notificationId) {
|
export async function markNotificationRead(notificationId) {
|
||||||
const { data } = camelCaseObject(notificationsList);
|
const params = snakeCaseObject({ notificationId });
|
||||||
|
const { data } = await getAuthenticatedHttpClient().put(markNotificationAsReadApiUrl(), { params });
|
||||||
|
|
||||||
return { data, id: notificationId };
|
return { data, id: notificationId };
|
||||||
}
|
}
|
||||||
|
|||||||
150
src/Notifications/data/api.test.js
Normal file
150
src/Notifications/data/api.test.js
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
|
import { Factory } from 'rosie';
|
||||||
|
|
||||||
|
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||||
|
import { initializeMockApp } from '@edx/frontend-platform/testing';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getNotificationsApiUrl, getNotificationsCountApiUrl, markNotificationAsReadApiUrl, markNotificationsSeenApiUrl,
|
||||||
|
getNotificationCounts, getNotifications, markNotificationSeen, markAllNotificationRead, markNotificationRead,
|
||||||
|
} from './api';
|
||||||
|
|
||||||
|
import './__factories__';
|
||||||
|
|
||||||
|
const notificationCountsApiUrl = getNotificationsCountApiUrl();
|
||||||
|
const notificationsApiUrl = getNotificationsApiUrl();
|
||||||
|
const markedAllNotificationsAsSeenApiUrl = markNotificationsSeenApiUrl('discussions');
|
||||||
|
const markedAllNotificationsAsReadApiUrl = markNotificationAsReadApiUrl();
|
||||||
|
|
||||||
|
let axiosMock = null;
|
||||||
|
|
||||||
|
describe('Notifications API', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
initializeMockApp({
|
||||||
|
authenticatedUser: {
|
||||||
|
userId: '123abc',
|
||||||
|
username: 'testuser',
|
||||||
|
administrator: false,
|
||||||
|
roles: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||||
|
Factory.resetAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
axiosMock.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Successfully get notification counts for different tabs.', async () => {
|
||||||
|
axiosMock.onGet(notificationCountsApiUrl).reply(200, (Factory.build('notificationsCount')));
|
||||||
|
|
||||||
|
const { count, countByAppName } = await getNotificationCounts();
|
||||||
|
|
||||||
|
expect(count).toEqual(45);
|
||||||
|
expect(countByAppName.reminders).toEqual(10);
|
||||||
|
expect(countByAppName.discussions).toEqual(20);
|
||||||
|
expect(countByAppName.grades).toEqual(10);
|
||||||
|
expect(countByAppName.authoring).toEqual(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{ statusCode: 404, message: 'Failed to get notification counts.' },
|
||||||
|
{ statusCode: 403, message: 'Denied to get notification counts.' },
|
||||||
|
])('%s for notification counts API.', async ({ statusCode, message }) => {
|
||||||
|
axiosMock.onGet(notificationCountsApiUrl).reply(statusCode, { message });
|
||||||
|
try {
|
||||||
|
await getNotificationCounts();
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.response.status).toEqual(statusCode);
|
||||||
|
expect(error.response.data.message).toEqual(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Successfully get notifications.', async () => {
|
||||||
|
axiosMock.onGet(notificationsApiUrl).reply(
|
||||||
|
200,
|
||||||
|
(Factory.buildList('notification', 2, null, { createdDate: new Date().toISOString() })),
|
||||||
|
);
|
||||||
|
|
||||||
|
const { notifications } = await getNotifications('discussions', 1, 10);
|
||||||
|
|
||||||
|
expect(notifications).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{ statusCode: 404, message: 'Failed to get notifications.' },
|
||||||
|
{ statusCode: 403, message: 'Denied to get notifications.' },
|
||||||
|
])('%s for notification API.', async ({ statusCode, message }) => {
|
||||||
|
axiosMock.onGet(notificationsApiUrl).reply(statusCode, { message });
|
||||||
|
try {
|
||||||
|
await getNotifications({ page: 1, pageSize: 10 });
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.response.status).toEqual(statusCode);
|
||||||
|
expect(error.response.data.message).toEqual(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Successfully marked all notifications as seen for selected app.', async () => {
|
||||||
|
axiosMock.onPut(markedAllNotificationsAsSeenApiUrl).reply(200, { message: 'Notifications marked seen.' });
|
||||||
|
|
||||||
|
const { message } = await markNotificationSeen('discussions');
|
||||||
|
|
||||||
|
expect(message).toEqual('Notifications marked seen.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{ statusCode: 404, message: 'Failed to mark all notifications as seen for selected app.' },
|
||||||
|
{ statusCode: 403, message: 'Denied to mark all notifications as seen for selected app.' },
|
||||||
|
])('%s for notification mark as seen API.', async ({ statusCode, message }) => {
|
||||||
|
axiosMock.onPut(markedAllNotificationsAsSeenApiUrl).reply(statusCode, { message });
|
||||||
|
try {
|
||||||
|
await markNotificationSeen('discussions');
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.response.status).toEqual(statusCode);
|
||||||
|
expect(error.response.data.message).toEqual(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Successfully marked all notifications as read for selected app.', async () => {
|
||||||
|
axiosMock.onPut(markedAllNotificationsAsReadApiUrl).reply(200, { message: 'Notifications marked read.' });
|
||||||
|
|
||||||
|
const { message } = await markAllNotificationRead('discussions');
|
||||||
|
|
||||||
|
expect(message).toEqual('Notifications marked read.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{ statusCode: 404, message: 'Failed to mark all notifications as read for selected app.' },
|
||||||
|
{ statusCode: 403, message: 'Denied to mark all notifications as read for selected app.' },
|
||||||
|
])('%s for notification mark all as read API.', async ({ statusCode, message }) => {
|
||||||
|
axiosMock.onPut(markedAllNotificationsAsReadApiUrl).reply(statusCode, { message });
|
||||||
|
try {
|
||||||
|
await markAllNotificationRead('discussions');
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.response.status).toEqual(statusCode);
|
||||||
|
expect(error.response.data.message).toEqual(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Successfully marked notification as read.', async () => {
|
||||||
|
axiosMock.onPut(markedAllNotificationsAsReadApiUrl).reply(200, { message: 'Notification marked read.' });
|
||||||
|
|
||||||
|
const { data } = await markNotificationRead(1);
|
||||||
|
|
||||||
|
expect(data.message).toEqual('Notification marked read.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{ statusCode: 404, message: 'Failed to mark notification as read.' },
|
||||||
|
{ statusCode: 403, message: 'Denied to mark notification as read.' },
|
||||||
|
])('%s for notification mark as read API.', async ({ statusCode, message }) => {
|
||||||
|
axiosMock.onPut(markedAllNotificationsAsReadApiUrl).reply(statusCode, { message });
|
||||||
|
try {
|
||||||
|
await markAllNotificationRead(1);
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.response.status).toEqual(statusCode);
|
||||||
|
expect(error.response.data.message).toEqual(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
164
src/Notifications/data/redux.test.js
Normal file
164
src/Notifications/data/redux.test.js
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
|
import { Factory } from 'rosie';
|
||||||
|
|
||||||
|
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||||
|
import { initializeMockApp } from '@edx/frontend-platform/testing';
|
||||||
|
|
||||||
|
import { initializeStore } from '../../store';
|
||||||
|
import executeThunk from '../../test-utils';
|
||||||
|
import {
|
||||||
|
getNotificationsApiUrl, getNotificationsCountApiUrl, markNotificationAsReadApiUrl, markNotificationsSeenApiUrl,
|
||||||
|
} from './api';
|
||||||
|
import {
|
||||||
|
fetchAppsNotificationCount, fetchNotificationList, markNotificationsAsRead, markAllNotificationsAsRead,
|
||||||
|
resetNotificationState, markNotificationsAsSeen,
|
||||||
|
} from './thunks';
|
||||||
|
|
||||||
|
import './__factories__';
|
||||||
|
|
||||||
|
const notificationCountsApiUrl = getNotificationsCountApiUrl();
|
||||||
|
const notificationsApiUrl = getNotificationsApiUrl();
|
||||||
|
const markedAllNotificationsAsReadApiUrl = markNotificationAsReadApiUrl();
|
||||||
|
const markedAllNotificationsAsSeenApiUrl = markNotificationsSeenApiUrl('discussions');
|
||||||
|
|
||||||
|
let axiosMock;
|
||||||
|
let store;
|
||||||
|
|
||||||
|
describe('Notification Redux', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
initializeMockApp({
|
||||||
|
authenticatedUser: {
|
||||||
|
userId: '123abc',
|
||||||
|
username: 'testuser',
|
||||||
|
administrator: false,
|
||||||
|
roles: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||||
|
Factory.resetAll();
|
||||||
|
store = initializeStore();
|
||||||
|
|
||||||
|
axiosMock.onGet(notificationCountsApiUrl).reply(200, (Factory.build('notificationsCount')));
|
||||||
|
axiosMock.onGet(notificationsApiUrl).reply(
|
||||||
|
200,
|
||||||
|
(Factory.buildList('notification', 2, null, { createdDate: new Date().toISOString() })),
|
||||||
|
);
|
||||||
|
await executeThunk(fetchAppsNotificationCount(), store.dispatch, store.getState);
|
||||||
|
await executeThunk(fetchNotificationList({ page: 1, pageSize: 10 }), store.dispatch, store.getState);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
axiosMock.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Successfully loaded initial notification states in the redux.', async () => {
|
||||||
|
executeThunk(resetNotificationState(), store.dispatch, store.getState);
|
||||||
|
|
||||||
|
const { notifications } = store.getState();
|
||||||
|
|
||||||
|
expect(notifications.notificationStatus).toEqual('idle');
|
||||||
|
expect(notifications.appName).toEqual('discussions');
|
||||||
|
expect(notifications.appsId).toHaveLength(0);
|
||||||
|
expect(notifications.apps).toEqual({});
|
||||||
|
expect(notifications.notifications).toEqual({});
|
||||||
|
expect(notifications.tabsCount).toEqual({});
|
||||||
|
expect(notifications.showNotificationsTray).toEqual(false);
|
||||||
|
expect(notifications.pagination.count).toEqual(10);
|
||||||
|
expect(notifications.pagination.numPages).toEqual(1);
|
||||||
|
expect(notifications.pagination.currentPage).toEqual(1);
|
||||||
|
expect(notifications.pagination.nextPage).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Successfully loaded notifications list in the redux.', async () => {
|
||||||
|
const { notifications: { notifications } } = store.getState();
|
||||||
|
|
||||||
|
expect(Object.keys(notifications)).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{ statusCode: 404, status: 'failed' },
|
||||||
|
{ statusCode: 403, status: 'denied' },
|
||||||
|
])('%s to load notifications list in the redux.', async ({ statusCode, status }) => {
|
||||||
|
axiosMock.onGet(notificationsApiUrl).reply(statusCode);
|
||||||
|
await executeThunk(fetchNotificationList({ page: 1, pageSize: 10 }), store.dispatch, store.getState);
|
||||||
|
|
||||||
|
const { notifications: { notificationStatus } } = store.getState();
|
||||||
|
|
||||||
|
expect(notificationStatus).toEqual(status);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Successfully loaded notification counts in the redux.', async () => {
|
||||||
|
const { notifications: { tabsCount } } = store.getState();
|
||||||
|
|
||||||
|
expect(tabsCount.count).toEqual(25);
|
||||||
|
expect(tabsCount.reminders).toEqual(10);
|
||||||
|
expect(tabsCount.discussions).toEqual(0);
|
||||||
|
expect(tabsCount.grades).toEqual(10);
|
||||||
|
expect(tabsCount.authoring).toEqual(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{ statusCode: 404, status: 'failed' },
|
||||||
|
{ statusCode: 403, status: 'denied' },
|
||||||
|
])('%s to load notification counts in the redux.', async ({ statusCode, status }) => {
|
||||||
|
axiosMock.onGet(notificationCountsApiUrl).reply(statusCode);
|
||||||
|
await executeThunk(fetchAppsNotificationCount(), store.dispatch, store.getState);
|
||||||
|
|
||||||
|
const { notifications: { notificationStatus } } = store.getState();
|
||||||
|
|
||||||
|
expect(notificationStatus).toEqual(status);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Successfully marked all notifications as seen for selected app.', async () => {
|
||||||
|
axiosMock.onPut(markedAllNotificationsAsSeenApiUrl).reply(200);
|
||||||
|
await executeThunk(markNotificationsAsSeen('discussions'), store.dispatch, store.getState);
|
||||||
|
|
||||||
|
expect(store.getState().notifications.notificationStatus).toEqual('successful');
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{ statusCode: 404, status: 'failed' },
|
||||||
|
{ statusCode: 403, status: 'denied' },
|
||||||
|
])('%s to mark all notifications as seen for selected app.', async ({ statusCode, status }) => {
|
||||||
|
axiosMock.onPut(markedAllNotificationsAsSeenApiUrl).reply(statusCode);
|
||||||
|
await executeThunk(markNotificationsAsSeen('discussions'), store.dispatch, store.getState);
|
||||||
|
|
||||||
|
const { notifications: { notificationStatus } } = store.getState();
|
||||||
|
|
||||||
|
expect(notificationStatus).toEqual(status);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Successfully marked all notifications as read for selected app in the redux.', async () => {
|
||||||
|
axiosMock.onPut(markedAllNotificationsAsReadApiUrl).reply(200);
|
||||||
|
await executeThunk(markAllNotificationsAsRead('discussions'), store.dispatch, store.getState);
|
||||||
|
|
||||||
|
const { notifications: { notificationStatus, notifications } } = store.getState();
|
||||||
|
const firstNotification = Object.values(notifications)[0];
|
||||||
|
|
||||||
|
expect(notificationStatus).toEqual('successful');
|
||||||
|
expect(firstNotification.lastRead).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Successfully marked notification as read in the redux.', async () => {
|
||||||
|
axiosMock.onPut(markedAllNotificationsAsReadApiUrl).reply(200);
|
||||||
|
await executeThunk(markNotificationsAsRead(1), store.dispatch, store.getState);
|
||||||
|
|
||||||
|
const { notifications: { notificationStatus, notifications } } = store.getState();
|
||||||
|
const firstNotification = Object.values(notifications)[0];
|
||||||
|
|
||||||
|
expect(notificationStatus).toEqual('successful');
|
||||||
|
expect(firstNotification.lastRead).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{ statusCode: 404, status: 'failed' },
|
||||||
|
{ statusCode: 403, status: 'denied' },
|
||||||
|
])('%s to marked notification as read in the redux.', async ({ statusCode, status }) => {
|
||||||
|
axiosMock.onPut(markedAllNotificationsAsReadApiUrl).reply(statusCode);
|
||||||
|
await executeThunk(markNotificationsAsRead(1), store.dispatch, store.getState);
|
||||||
|
|
||||||
|
const { notifications: { notificationStatus } } = store.getState();
|
||||||
|
|
||||||
|
expect(notificationStatus).toEqual(status);
|
||||||
|
});
|
||||||
|
});
|
||||||
126
src/Notifications/data/selector.test.jsx
Normal file
126
src/Notifications/data/selector.test.jsx
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
|
import { Factory } from 'rosie';
|
||||||
|
|
||||||
|
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||||
|
import { initializeMockApp } from '@edx/frontend-platform/testing';
|
||||||
|
|
||||||
|
import { initializeStore } from '../../store';
|
||||||
|
import executeThunk from '../../test-utils';
|
||||||
|
import { getNotificationsApiUrl, getNotificationsCountApiUrl } from './api';
|
||||||
|
import {
|
||||||
|
selectNotifications,
|
||||||
|
selectNotificationsByIds,
|
||||||
|
selectNotificationStatus,
|
||||||
|
selectNotificationTabs,
|
||||||
|
selectNotificationTabsCount,
|
||||||
|
selectPaginationData,
|
||||||
|
selectSelectedAppName,
|
||||||
|
selectSelectedAppNotificationIds,
|
||||||
|
selectShowNotificationTray,
|
||||||
|
} from './selectors';
|
||||||
|
import { fetchAppsNotificationCount, fetchNotificationList } from './thunks';
|
||||||
|
|
||||||
|
import './__factories__';
|
||||||
|
|
||||||
|
const notificationCountsApiUrl = getNotificationsCountApiUrl();
|
||||||
|
const notificationsApiUrl = getNotificationsApiUrl();
|
||||||
|
|
||||||
|
let axiosMock;
|
||||||
|
let store;
|
||||||
|
|
||||||
|
describe('Notification Selectors', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
initializeMockApp({
|
||||||
|
authenticatedUser: {
|
||||||
|
userId: '123abc',
|
||||||
|
username: 'testuser',
|
||||||
|
administrator: false,
|
||||||
|
roles: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||||
|
Factory.resetAll();
|
||||||
|
store = initializeStore();
|
||||||
|
|
||||||
|
axiosMock.onGet(notificationCountsApiUrl).reply(200, (Factory.build('notificationsCount')));
|
||||||
|
axiosMock.onGet(notificationsApiUrl).reply(
|
||||||
|
200,
|
||||||
|
(Factory.buildList('notification', 2, null, { createdDate: new Date().toISOString() })),
|
||||||
|
);
|
||||||
|
await executeThunk(fetchAppsNotificationCount(), store.dispatch, store.getState);
|
||||||
|
await executeThunk(fetchNotificationList({ page: 1, pageSize: 10 }), store.dispatch, store.getState);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
axiosMock.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return notification status.', async () => {
|
||||||
|
const state = store.getState();
|
||||||
|
const status = selectNotificationStatus()(state);
|
||||||
|
|
||||||
|
expect(status).toEqual('successful');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return notification tabs count.', async () => {
|
||||||
|
const state = store.getState();
|
||||||
|
const tabsCount = selectNotificationTabsCount()(state);
|
||||||
|
|
||||||
|
expect(tabsCount.count).toEqual(25);
|
||||||
|
expect(tabsCount.reminders).toEqual(10);
|
||||||
|
expect(tabsCount.discussions).toEqual(0);
|
||||||
|
expect(tabsCount.grades).toEqual(10);
|
||||||
|
expect(tabsCount.authoring).toEqual(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return notification tabs.', async () => {
|
||||||
|
const state = store.getState();
|
||||||
|
const tabs = selectNotificationTabs()(state);
|
||||||
|
|
||||||
|
expect(tabs).toHaveLength(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return selected app notification ids.', async () => {
|
||||||
|
const state = store.getState();
|
||||||
|
const notificationIds = selectSelectedAppNotificationIds('discussions')(state);
|
||||||
|
|
||||||
|
expect(notificationIds).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return show notification tray status.', async () => {
|
||||||
|
const state = store.getState();
|
||||||
|
const showNotificationTrayStatus = selectShowNotificationTray()(state);
|
||||||
|
|
||||||
|
expect(showNotificationTrayStatus).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return notifications.', async () => {
|
||||||
|
const state = store.getState();
|
||||||
|
const notifications = selectNotifications()(state);
|
||||||
|
|
||||||
|
expect(Object.keys(notifications)).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return notifications from Ids.', async () => {
|
||||||
|
const state = store.getState();
|
||||||
|
const notifications = selectNotificationsByIds('discussions')(state);
|
||||||
|
|
||||||
|
expect(notifications).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return selected app name.', async () => {
|
||||||
|
const state = store.getState();
|
||||||
|
const appName = selectSelectedAppName()(state);
|
||||||
|
|
||||||
|
expect(appName).toEqual('discussions');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return pagination data.', async () => {
|
||||||
|
const state = store.getState();
|
||||||
|
const paginationData = selectPaginationData()(state);
|
||||||
|
|
||||||
|
expect(paginationData.count).toEqual(10);
|
||||||
|
expect(paginationData.currentPage).toEqual(1);
|
||||||
|
expect(paginationData.numPages).toEqual(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -8,7 +8,7 @@ export const selectNotificationTabs = () => state => state.notifications.appsId;
|
|||||||
|
|
||||||
export const selectSelectedAppNotificationIds = (appName) => state => state.notifications.apps[appName] ?? [];
|
export const selectSelectedAppNotificationIds = (appName) => state => state.notifications.apps[appName] ?? [];
|
||||||
|
|
||||||
export const selectShowNotificationTray = () => state => state.notifications.showNotificationTray;
|
export const selectShowNotificationTray = () => state => state.notifications.showNotificationsTray;
|
||||||
|
|
||||||
export const selectNotifications = () => state => state.notifications.notifications;
|
export const selectNotifications = () => state => state.notifications.notifications;
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const initialState = {
|
|||||||
apps: {},
|
apps: {},
|
||||||
notifications: {},
|
notifications: {},
|
||||||
tabsCount: {},
|
tabsCount: {},
|
||||||
showNotificationTray: false,
|
showNotificationsTray: false,
|
||||||
pagination: {
|
pagination: {
|
||||||
count: 10,
|
count: 10,
|
||||||
numPages: 1,
|
numPages: 1,
|
||||||
@@ -62,12 +62,12 @@ const slice = createSlice({
|
|||||||
},
|
},
|
||||||
fetchNotificationsCountSuccess: (state, { payload }) => {
|
fetchNotificationsCountSuccess: (state, { payload }) => {
|
||||||
const {
|
const {
|
||||||
countByAppName, appIds, apps, count, showNotificationTray,
|
countByAppName, appIds, apps, count, showNotificationsTray,
|
||||||
} = payload;
|
} = payload;
|
||||||
state.tabsCount = { count, ...countByAppName };
|
state.tabsCount = { count, ...countByAppName };
|
||||||
state.appsId = appIds;
|
state.appsId = appIds;
|
||||||
state.apps = apps;
|
state.apps = apps;
|
||||||
state.showNotificationTray = showNotificationTray;
|
state.showNotificationsTray = showNotificationsTray;
|
||||||
state.notificationStatus = RequestStatus.LOADED;
|
state.notificationStatus = RequestStatus.LOADED;
|
||||||
},
|
},
|
||||||
markNotificationsAsSeenRequest: (state) => {
|
markNotificationsAsSeenRequest: (state) => {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { camelCaseObject } from '@edx/frontend-platform';
|
||||||
import {
|
import {
|
||||||
fetchNotificationSuccess,
|
fetchNotificationSuccess,
|
||||||
fetchNotificationRequest,
|
fetchNotificationRequest,
|
||||||
@@ -26,11 +27,11 @@ import {
|
|||||||
} from './api';
|
} from './api';
|
||||||
import { getHttpErrorStatus } from '../utils';
|
import { getHttpErrorStatus } from '../utils';
|
||||||
|
|
||||||
const normalizeNotificationCounts = ({ countByAppName, count, showNotificationTray }) => {
|
const normalizeNotificationCounts = ({ countByAppName, count, showNotificationsTray }) => {
|
||||||
const appIds = Object.keys(countByAppName);
|
const appIds = Object.keys(countByAppName);
|
||||||
const apps = appIds.reduce((acc, appId) => { acc[appId] = []; return acc; }, {});
|
const apps = appIds.reduce((acc, appId) => { acc[appId] = []; return acc; }, {});
|
||||||
return {
|
return {
|
||||||
countByAppName, appIds, apps, count, showNotificationTray,
|
countByAppName, appIds, apps, count, showNotificationsTray,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ export const fetchNotificationList = ({ appName, page, pageSize }) => (
|
|||||||
try {
|
try {
|
||||||
dispatch(fetchNotificationRequest({ appName }));
|
dispatch(fetchNotificationRequest({ appName }));
|
||||||
const data = await getNotifications(appName, page, pageSize);
|
const data = await getNotifications(appName, page, pageSize);
|
||||||
const normalisedData = normalizeNotifications((data));
|
const normalisedData = normalizeNotifications((camelCaseObject(data)));
|
||||||
dispatch(fetchNotificationSuccess({ ...normalisedData, numPages: data.numPages, currentPage: data.currentPage }));
|
dispatch(fetchNotificationSuccess({ ...normalisedData, numPages: data.numPages, currentPage: data.currentPage }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (getHttpErrorStatus(error) === 403) {
|
if (getHttpErrorStatus(error) === 403) {
|
||||||
@@ -64,13 +65,8 @@ export const fetchAppsNotificationCount = () => (
|
|||||||
try {
|
try {
|
||||||
dispatch(fetchNotificationsCountRequest());
|
dispatch(fetchNotificationsCountRequest());
|
||||||
const data = await getNotificationCounts();
|
const data = await getNotificationCounts();
|
||||||
const normalisedData = normalizeNotificationCounts((data));
|
const normalisedData = normalizeNotificationCounts((camelCaseObject(data)));
|
||||||
dispatch(fetchNotificationsCountSuccess({
|
dispatch(fetchNotificationsCountSuccess({ ...normalisedData }));
|
||||||
...normalisedData,
|
|
||||||
countByAppName: data.countByAppName,
|
|
||||||
count: data.count,
|
|
||||||
showNotificationTray: data.showNotificationTray,
|
|
||||||
}));
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (getHttpErrorStatus(error) === 403) {
|
if (getHttpErrorStatus(error) === 403) {
|
||||||
dispatch(fetchNotificationsCountDenied());
|
dispatch(fetchNotificationsCountDenied());
|
||||||
@@ -86,7 +82,7 @@ export const markAllNotificationsAsRead = (appName) => (
|
|||||||
try {
|
try {
|
||||||
dispatch(markAllNotificationsAsReadRequest({ appName }));
|
dispatch(markAllNotificationsAsReadRequest({ appName }));
|
||||||
const data = await markAllNotificationRead(appName);
|
const data = await markAllNotificationRead(appName);
|
||||||
dispatch(markAllNotificationsAsReadSuccess(data));
|
dispatch(markAllNotificationsAsReadSuccess(camelCaseObject(data)));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (getHttpErrorStatus(error) === 403) {
|
if (getHttpErrorStatus(error) === 403) {
|
||||||
dispatch(markAllNotificationsAsReadDenied());
|
dispatch(markAllNotificationsAsReadDenied());
|
||||||
@@ -102,7 +98,7 @@ export const markNotificationsAsRead = (notificationId) => (
|
|||||||
try {
|
try {
|
||||||
dispatch(markNotificationsAsReadRequest({ notificationId }));
|
dispatch(markNotificationsAsReadRequest({ notificationId }));
|
||||||
const data = await markNotificationRead(notificationId);
|
const data = await markNotificationRead(notificationId);
|
||||||
dispatch(markNotificationsAsReadSuccess(data));
|
dispatch(markNotificationsAsReadSuccess(camelCaseObject(data)));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (getHttpErrorStatus(error) === 403) {
|
if (getHttpErrorStatus(error) === 403) {
|
||||||
dispatch(markNotificationsAsReadDenied());
|
dispatch(markNotificationsAsReadDenied());
|
||||||
@@ -118,7 +114,7 @@ export const markNotificationsAsSeen = (appName) => (
|
|||||||
try {
|
try {
|
||||||
dispatch(markNotificationsAsSeenRequest({ appName }));
|
dispatch(markNotificationsAsSeenRequest({ appName }));
|
||||||
const data = await markNotificationSeen(appName);
|
const data = await markNotificationSeen(appName);
|
||||||
dispatch(markNotificationsAsSeenSuccess(data));
|
dispatch(markNotificationsAsSeenSuccess(camelCaseObject(data)));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (getHttpErrorStatus(error) === 403) {
|
if (getHttpErrorStatus(error) === 403) {
|
||||||
dispatch(markNotificationsAsSeenDenied());
|
dispatch(markNotificationsAsSeenDenied());
|
||||||
|
|||||||
@@ -29,5 +29,11 @@
|
|||||||
"header.menu.account.label": "الحساب",
|
"header.menu.account.label": "الحساب",
|
||||||
"header.menu.orderHistory.label": "سجل الطلبيات",
|
"header.menu.orderHistory.label": "سجل الطلبيات",
|
||||||
"header.navigation.skipNavLink": "التخطي إلى المحتوى الرئيسي",
|
"header.navigation.skipNavLink": "التخطي إلى المحتوى الرئيسي",
|
||||||
"header.menu.signOut.label": "تسجيل الخروج"
|
"header.menu.signOut.label": "تسجيل الخروج",
|
||||||
|
"notification.title": "Notifications",
|
||||||
|
"notification.today.heading": "Last 24 hours",
|
||||||
|
"notification.earlier.heading": "Earlier",
|
||||||
|
"notification.mark.as.read": "Mark all as read",
|
||||||
|
"notification.fullStop": "•",
|
||||||
|
"notification.load.more.notifications": "Load more notifications"
|
||||||
}
|
}
|
||||||
@@ -29,5 +29,11 @@
|
|||||||
"header.menu.account.label": "Account",
|
"header.menu.account.label": "Account",
|
||||||
"header.menu.orderHistory.label": "Order History",
|
"header.menu.orderHistory.label": "Order History",
|
||||||
"header.navigation.skipNavLink": "Skip to main content.",
|
"header.navigation.skipNavLink": "Skip to main content.",
|
||||||
"header.menu.signOut.label": "Sign Out"
|
"header.menu.signOut.label": "Sign Out",
|
||||||
|
"notification.title": "Notifications",
|
||||||
|
"notification.today.heading": "Last 24 hours",
|
||||||
|
"notification.earlier.heading": "Earlier",
|
||||||
|
"notification.mark.as.read": "Mark all as read",
|
||||||
|
"notification.fullStop": "•",
|
||||||
|
"notification.load.more.notifications": "Load more notifications"
|
||||||
}
|
}
|
||||||
@@ -29,5 +29,11 @@
|
|||||||
"header.menu.account.label": "Cuenta",
|
"header.menu.account.label": "Cuenta",
|
||||||
"header.menu.orderHistory.label": "Historial de órdenes",
|
"header.menu.orderHistory.label": "Historial de órdenes",
|
||||||
"header.navigation.skipNavLink": "Dirígete al contenido principal.",
|
"header.navigation.skipNavLink": "Dirígete al contenido principal.",
|
||||||
"header.menu.signOut.label": "Cerrar sesión"
|
"header.menu.signOut.label": "Cerrar sesión",
|
||||||
|
"notification.title": "Notifications",
|
||||||
|
"notification.today.heading": "Last 24 hours",
|
||||||
|
"notification.earlier.heading": "Earlier",
|
||||||
|
"notification.mark.as.read": "Mark all as read",
|
||||||
|
"notification.fullStop": "•",
|
||||||
|
"notification.load.more.notifications": "Load more notifications"
|
||||||
}
|
}
|
||||||
@@ -29,5 +29,11 @@
|
|||||||
"header.menu.account.label": "Compte",
|
"header.menu.account.label": "Compte",
|
||||||
"header.menu.orderHistory.label": "Historique des commandes",
|
"header.menu.orderHistory.label": "Historique des commandes",
|
||||||
"header.navigation.skipNavLink": "Passer au contenu principal",
|
"header.navigation.skipNavLink": "Passer au contenu principal",
|
||||||
"header.menu.signOut.label": "Se déconnecter"
|
"header.menu.signOut.label": "Se déconnecter",
|
||||||
|
"notification.title": "Notifications",
|
||||||
|
"notification.today.heading": "Last 24 hours",
|
||||||
|
"notification.earlier.heading": "Earlier",
|
||||||
|
"notification.mark.as.read": "Mark all as read",
|
||||||
|
"notification.fullStop": "•",
|
||||||
|
"notification.load.more.notifications": "Load more notifications"
|
||||||
}
|
}
|
||||||
@@ -29,5 +29,11 @@
|
|||||||
"header.menu.account.label": "Compte",
|
"header.menu.account.label": "Compte",
|
||||||
"header.menu.orderHistory.label": "Historique des commandes",
|
"header.menu.orderHistory.label": "Historique des commandes",
|
||||||
"header.navigation.skipNavLink": "Passer au contenu principal.",
|
"header.navigation.skipNavLink": "Passer au contenu principal.",
|
||||||
"header.menu.signOut.label": "Se déconnecter"
|
"header.menu.signOut.label": "Se déconnecter",
|
||||||
|
"notification.title": "Notifications",
|
||||||
|
"notification.today.heading": "Dernières 24 heures",
|
||||||
|
"notification.earlier.heading": "Plus tôt",
|
||||||
|
"notification.mark.as.read": "tout marquer comme lu",
|
||||||
|
"notification.fullStop": "•",
|
||||||
|
"notification.load.more.notifications": "Charger plus de notifications"
|
||||||
}
|
}
|
||||||
@@ -29,5 +29,11 @@
|
|||||||
"header.menu.account.label": "Account",
|
"header.menu.account.label": "Account",
|
||||||
"header.menu.orderHistory.label": "Order History",
|
"header.menu.orderHistory.label": "Order History",
|
||||||
"header.navigation.skipNavLink": "Skip to main content.",
|
"header.navigation.skipNavLink": "Skip to main content.",
|
||||||
"header.menu.signOut.label": "Sign Out"
|
"header.menu.signOut.label": "Sign Out",
|
||||||
|
"notification.title": "Notifications",
|
||||||
|
"notification.today.heading": "Last 24 hours",
|
||||||
|
"notification.earlier.heading": "Earlier",
|
||||||
|
"notification.mark.as.read": "Mark all as read",
|
||||||
|
"notification.fullStop": "•",
|
||||||
|
"notification.load.more.notifications": "Load more notifications"
|
||||||
}
|
}
|
||||||
@@ -29,5 +29,11 @@
|
|||||||
"header.menu.account.label": "Account",
|
"header.menu.account.label": "Account",
|
||||||
"header.menu.orderHistory.label": "Order History",
|
"header.menu.orderHistory.label": "Order History",
|
||||||
"header.navigation.skipNavLink": "Skip to main content.",
|
"header.navigation.skipNavLink": "Skip to main content.",
|
||||||
"header.menu.signOut.label": "Sign Out"
|
"header.menu.signOut.label": "Sign Out",
|
||||||
|
"notification.title": "Notifications",
|
||||||
|
"notification.today.heading": "Last 24 hours",
|
||||||
|
"notification.earlier.heading": "Earlier",
|
||||||
|
"notification.mark.as.read": "Mark all as read",
|
||||||
|
"notification.fullStop": "•",
|
||||||
|
"notification.load.more.notifications": "Load more notifications"
|
||||||
}
|
}
|
||||||
@@ -29,5 +29,11 @@
|
|||||||
"header.menu.account.label": "Account",
|
"header.menu.account.label": "Account",
|
||||||
"header.menu.orderHistory.label": "Order History",
|
"header.menu.orderHistory.label": "Order History",
|
||||||
"header.navigation.skipNavLink": "Skip to main content.",
|
"header.navigation.skipNavLink": "Skip to main content.",
|
||||||
"header.menu.signOut.label": "Sign Out"
|
"header.menu.signOut.label": "Sign Out",
|
||||||
|
"notification.title": "Notifications",
|
||||||
|
"notification.today.heading": "Last 24 hours",
|
||||||
|
"notification.earlier.heading": "Earlier",
|
||||||
|
"notification.mark.as.read": "Mark all as read",
|
||||||
|
"notification.fullStop": "•",
|
||||||
|
"notification.load.more.notifications": "Load more notifications"
|
||||||
}
|
}
|
||||||
@@ -29,5 +29,11 @@
|
|||||||
"header.menu.account.label": "Account",
|
"header.menu.account.label": "Account",
|
||||||
"header.menu.orderHistory.label": "Order History",
|
"header.menu.orderHistory.label": "Order History",
|
||||||
"header.navigation.skipNavLink": "Skip to main content.",
|
"header.navigation.skipNavLink": "Skip to main content.",
|
||||||
"header.menu.signOut.label": "Sign Out"
|
"header.menu.signOut.label": "Sign Out",
|
||||||
|
"notification.title": "Notifications",
|
||||||
|
"notification.today.heading": "Last 24 hours",
|
||||||
|
"notification.earlier.heading": "Earlier",
|
||||||
|
"notification.mark.as.read": "Mark all as read",
|
||||||
|
"notification.fullStop": "•",
|
||||||
|
"notification.load.more.notifications": "Load more notifications"
|
||||||
}
|
}
|
||||||
@@ -29,5 +29,11 @@
|
|||||||
"header.menu.account.label": "Account",
|
"header.menu.account.label": "Account",
|
||||||
"header.menu.orderHistory.label": "Order History",
|
"header.menu.orderHistory.label": "Order History",
|
||||||
"header.navigation.skipNavLink": "Перейти до головного змісту.",
|
"header.navigation.skipNavLink": "Перейти до головного змісту.",
|
||||||
"header.menu.signOut.label": "Sign Out"
|
"header.menu.signOut.label": "Sign Out",
|
||||||
|
"notification.title": "Notifications",
|
||||||
|
"notification.today.heading": "Last 24 hours",
|
||||||
|
"notification.earlier.heading": "Earlier",
|
||||||
|
"notification.mark.as.read": "Mark all as read",
|
||||||
|
"notification.fullStop": "•",
|
||||||
|
"notification.load.more.notifications": "Load more notifications"
|
||||||
}
|
}
|
||||||
@@ -29,5 +29,11 @@
|
|||||||
"header.menu.account.label": "Account",
|
"header.menu.account.label": "Account",
|
||||||
"header.menu.orderHistory.label": "Order History",
|
"header.menu.orderHistory.label": "Order History",
|
||||||
"header.navigation.skipNavLink": "Skip to main content.",
|
"header.navigation.skipNavLink": "Skip to main content.",
|
||||||
"header.menu.signOut.label": "Sign Out"
|
"header.menu.signOut.label": "Sign Out",
|
||||||
|
"notification.title": "Notifications",
|
||||||
|
"notification.today.heading": "Last 24 hours",
|
||||||
|
"notification.earlier.heading": "Earlier",
|
||||||
|
"notification.mark.as.read": "Mark all as read",
|
||||||
|
"notification.fullStop": "•",
|
||||||
|
"notification.load.more.notifications": "Load more notifications"
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ import { RequestStatus } from '../Notifications/data/slice';
|
|||||||
import messages from './messages';
|
import messages from './messages';
|
||||||
|
|
||||||
const AuthenticatedUserDropdown = ({ intl, username }) => {
|
const AuthenticatedUserDropdown = ({ intl, username }) => {
|
||||||
const showNotificationTray = useSelector(selectShowNotificationTray());
|
const showNotificationsTray = useSelector(selectShowNotificationTray());
|
||||||
const notificationStatus = useSelector(selectNotificationStatus());
|
const notificationStatus = useSelector(selectNotificationStatus());
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ const AuthenticatedUserDropdown = ({ intl, username }) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<a className="text-gray-700" href={`${getConfig().SUPPORT_URL}`}>{intl.formatMessage(messages.help)}</a>
|
<a className="text-gray-700" href={`${getConfig().SUPPORT_URL}`}>{intl.formatMessage(messages.help)}</a>
|
||||||
{showNotificationTray && <Notifications />}
|
{showNotificationsTray && <Notifications />}
|
||||||
<Dropdown className="user-dropdown ml-3">
|
<Dropdown className="user-dropdown ml-3">
|
||||||
<Dropdown.Toggle variant="outline-primary">
|
<Dropdown.Toggle variant="outline-primary">
|
||||||
<FontAwesomeIcon icon={faUserCircle} className="d-md-none" size="lg" />
|
<FontAwesomeIcon icon={faUserCircle} className="d-md-none" size="lg" />
|
||||||
|
|||||||
6
src/test-utils.js
Normal file
6
src/test-utils.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
const executeThunk = async (thunk, dispatch, getState) => {
|
||||||
|
await thunk(dispatch, getState);
|
||||||
|
await new Promise(setImmediate);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default executeThunk;
|
||||||
Reference in New Issue
Block a user