Compare commits
65 Commits
cdeery/AA-
...
KristinAok
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8882026a01 | ||
|
|
c6578d4e2e | ||
|
|
fe4680646e | ||
|
|
c09ba48615 | ||
|
|
c46da1dc34 | ||
|
|
9ca5c61088 | ||
|
|
a17e2a1a15 | ||
|
|
ea02b2f70f | ||
|
|
5fa33e4015 | ||
|
|
569b628961 | ||
|
|
43eb58974a | ||
|
|
6f2281c1a4 | ||
|
|
5538b48ebb | ||
|
|
847cdfa0bd | ||
|
|
38db0ebfe1 | ||
|
|
7b57b06ed5 | ||
|
|
9c2190980e | ||
|
|
b4c83a38aa | ||
|
|
5efc22220f | ||
|
|
0ba9ed7d31 | ||
|
|
a32a58019d | ||
|
|
367c8ad0df | ||
|
|
ea93aea4dd | ||
|
|
e05428e01d | ||
|
|
24de9d7add | ||
|
|
4e136d9c55 | ||
|
|
296607fb76 | ||
|
|
544e11b628 | ||
|
|
75b195bdc0 | ||
|
|
07042d9908 | ||
|
|
2d1a13ab0a | ||
|
|
7fde146edd | ||
|
|
5f0968e348 | ||
|
|
20935e7860 | ||
|
|
40ea41996f | ||
|
|
f0fab488a5 | ||
|
|
7f2df8b886 | ||
|
|
9b33f20eaa | ||
|
|
7242583f13 | ||
|
|
229692255f | ||
|
|
96a5753b1b | ||
|
|
7b45c8b6fa | ||
|
|
f2d7e119a5 | ||
|
|
4baf78c79e | ||
|
|
d517f94c49 | ||
|
|
43ff07af3e | ||
|
|
aeca68fd56 | ||
|
|
29a24aa62e | ||
|
|
4be725b4c2 | ||
|
|
c592753182 | ||
|
|
174be4adc7 | ||
|
|
388b9dfe59 | ||
|
|
e4ec845bd4 | ||
|
|
e96d885114 | ||
|
|
eb70d3733d | ||
|
|
fcda48513a | ||
|
|
abac174e2e | ||
|
|
457dc4b279 | ||
|
|
3b2f91cd32 | ||
|
|
19f318679f | ||
|
|
d38c07a206 | ||
|
|
3b2bbbdbc4 | ||
|
|
832107f084 | ||
|
|
b23a6330f1 | ||
|
|
8970352cdd |
2
.env
2
.env
@@ -4,7 +4,6 @@
|
||||
NODE_ENV='production'
|
||||
ACCESS_TOKEN_COOKIE_NAME=''
|
||||
BASE_URL=''
|
||||
CONTACT_URL=''
|
||||
CREDENTIALS_BASE_URL=''
|
||||
CSRF_TOKEN_API_PATH=''
|
||||
DISCOVERY_API_BASE_URL=''
|
||||
@@ -37,4 +36,3 @@ TWITTER_HASHTAG=''
|
||||
TWITTER_URL=''
|
||||
USER_INFO_COOKIE_NAME=''
|
||||
SESSION_COOKIE_DOMAIN=''
|
||||
ENABLE_JUMPNAV='true'
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
NODE_ENV='development'
|
||||
ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
|
||||
BASE_URL='http://localhost:2000'
|
||||
CONTACT_URL='http://localhost:18000/contact'
|
||||
CREDENTIALS_BASE_URL='http://localhost:18150'
|
||||
CSRF_TOKEN_API_PATH='/csrf/api/v1/token'
|
||||
DISCOVERY_API_BASE_URL='http://localhost:18381'
|
||||
@@ -37,4 +36,3 @@ TWITTER_HASHTAG='myedxjourney'
|
||||
TWITTER_URL='https://twitter.com/edXOnline'
|
||||
USER_INFO_COOKIE_NAME='edx-user-info'
|
||||
SESSION_COOKIE_DOMAIN='localhost'
|
||||
ENABLE_JUMPNAV='true'
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
NODE_ENV='test'
|
||||
ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
|
||||
BASE_URL='http://localhost:2000'
|
||||
CONTACT_URL='http://localhost:18000/contact'
|
||||
CREDENTIALS_BASE_URL='http://localhost:18150'
|
||||
CSRF_TOKEN_API_PATH='/csrf/api/v1/token'
|
||||
DISCOVERY_API_BASE_URL='http://localhost:18381'
|
||||
@@ -36,4 +35,3 @@ TERMS_OF_SERVICE_URL='https://www.edx.org/edx-terms-service'
|
||||
TWITTER_HASHTAG='myedxjourney'
|
||||
TWITTER_URL='https://twitter.com/edXOnline'
|
||||
USER_INFO_COOKIE_NAME='edx-user-info'
|
||||
ENABLE_JUMPNAV='true'
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -20,6 +20,3 @@ logs
|
||||
|
||||
# Local package dependencies
|
||||
module.config.js
|
||||
|
||||
# Local environment overrides
|
||||
.env.private
|
||||
|
||||
@@ -109,9 +109,3 @@ TWITTER_URL
|
||||
unless this is set. Optional.
|
||||
|
||||
Example: https://twitter.com/edXOnline
|
||||
|
||||
ENABLE_JUMPNAV
|
||||
Enables the new Jump Navigation feature in the course breadcrumbs, defaulted to the string 'true'.
|
||||
Disable to have simple hyperlinks for breadcrumbs. Setting it to any other value but 'true' ('false','I love flags', 'etc' would disable the Jumpnav).
|
||||
This feature flag is slated to be removed as jumpnav becomes default. Follow the progress of this ticket here:
|
||||
https://openedx.atlassian.net/browse/TNL-8678
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# Courseware Page Decisions
|
||||
|
||||
**See [0009-courseware-api-direction.md](0009-courseware-api-direction.md) for updates!**
|
||||
|
||||
## Courseware data loading
|
||||
|
||||
Today we have strictly hierarchical courses - a course contains sections, which contain sequences, which contain units, which contain components.
|
||||
|
||||
@@ -88,3 +88,6 @@ And more like:
|
||||
```
|
||||
https://learning.edx.org/course/course-v1:edX+DemoX.1+2T2019/Being_Social/Teams
|
||||
```
|
||||
|
||||
_This further work has been expanded upon in
|
||||
[ADR #9: Courseware URL shortening](./0009-courseware-url-shortening.md)._
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
# Direction of Courseware APIs
|
||||
|
||||
In order to allow for greater flexibility and separation of concerns, we're going to stop using the Course Blocks API for navigational data, and pull that data from the Learning Sequences Outlines API instead. The intention is to give us four distinct layers of courseware that can eventually be composed in different ways:
|
||||
|
||||
* Learning Context Metadata
|
||||
* Learning Context Navigation
|
||||
* Sequence Navigation
|
||||
* Unit Rendering
|
||||
|
||||
Note that "Learning Context" is a generalization of "Course" that includes other things like Content Libraries, Learning Pathways, and potentially other logical groupings of content.
|
||||
|
||||
This is a refinement of [0002-courseware-page-decisions.md](0002-courseware-page-decisions.md). The fundamental layers remain the same, but this document tries to better clarify the boundaries and path forward for these layers. We're not making these layers completely swappable/pluggable now, but we should separate the data access in a way that allows for that in the future.
|
||||
|
||||
## Background
|
||||
|
||||
We currently make four primary requests to the LMS when rendering courseware instructional content:
|
||||
|
||||
1. Course Metadata: `/api/courseware/course/{courseId}` (REST API)
|
||||
2. Course Blocks API: `/api/courses/v2/blocks/?course_id={courseId}` (REST API)
|
||||
3. Sequence Metadata: `/api/courseware/sequence/{sequenceUsageKey}` (REST API)
|
||||
4. Unit: `/xblock/{unitBlockUsageKey}` (rendered in an iframe)
|
||||
|
||||
There is a significant amount of overlap between the Course Blocks API and the others at the moment, since Course Blocks takes a static snapshot of the entire tree of course content at once. There are a few problems with the current arrangement:
|
||||
|
||||
* It's slow and complex. The Course Blocks API can be difficult to maintain and reason about, and trickier to optimize.
|
||||
* Assuming that all course structures are the same makes it difficult to support other content types, like LabXchange Learning Pathways or adaptive content.
|
||||
* The overlap between Course Blocks and the other APIs means that there can be conflicts about the state.
|
||||
|
||||
## Motivating Vision
|
||||
|
||||
We have seen a desire to extend or enhance the courseware experience in various ways:
|
||||
|
||||
Learning Context Navigation
|
||||
* Allowing for shorter, human-readable URLs in courseware.
|
||||
* Smaller courses that do not need the current navigational hierarchy.
|
||||
* LabXchange pathways.
|
||||
|
||||
Sequence Navigation
|
||||
* Adaptive content, where the full list of units is not known up front.
|
||||
* More limited navigation, where content is pushed linearly, without the ability to jump ahead.
|
||||
* Different layouts for content browsing.
|
||||
|
||||
Unit Rendering
|
||||
* Use of QTI content (currently serviced by cc2olx conversion).
|
||||
* Desire to experiment with a next-gen version of XBlock.
|
||||
* Use of entirely LTI units.
|
||||
|
||||
The idea would be to insulate each layer from the layers above and below it. Sequence rendering shouldn't be affected by whether or not it's in a two level hierarchy (Course → Section → Sequence), or a flat one (Course → Sequence). Learning Context Navigation should be able to reference Sequences without caring if a Sequence is an adaptive one or not. Sequences should be able to have a common interface to call Unit iframes, whether those Units are rendering XBlocks or QTI content.
|
||||
|
||||
Note that supporting these types of course structures would require downstream changes in other systems as well (e.g. analytics).
|
||||
|
||||
## Next Step: Removing use of the Course Blocks API.
|
||||
|
||||
The next step in this process is to remove the call to the Course Blocks API, and split its responsibilities across just the existing Learning Sequences Outline and Sequence Metadata APIs. This will involve at least a couple of steps.
|
||||
|
||||
### Complete rollout of Learning Sequences Outline calls.
|
||||
|
||||
We're currently in a transitional state between these APIs where the Learning Sequences Outline calls are only rolled out on a small handful of courses.
|
||||
|
||||
### Shift Sequence and Unit metadata to only come from Sequence Metadata API.
|
||||
|
||||
We currently pull this information from both Course Blocks and the Sequence Metadata API. We can consolidate on just the Sequence Metadata API. There is also server side optimization that can be done with the Sequence Metadata API calls as part of this work.
|
||||
58
docs/decisions/0009-courseware-url-shortening.md
Normal file
58
docs/decisions/0009-courseware-url-shortening.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Courseware URL shortening
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
_This updates some of the content in [ADR #8: Liberal courseware path handling](./0008-liberal-courseware-path-handling.md)._
|
||||
|
||||
## Context
|
||||
|
||||
The current URL is not human-readable. The URL is composed of the UsageKeys for the current sequence and unit. We can't make UsageKeys themselves more readable because they're tied to student state.
|
||||
|
||||
This is what the URLs currently look like:
|
||||
|
||||
```
|
||||
|
||||
https://learning.edx.org/course/course-v1:edX+DemoX.1+2T2019/block-v1:edX+DemoX.1+2T2019+type@sequential+block@e0a61b3d5a2046949e76d12cac5df493/block-v1:edX+DemoX.1+2T2019+type@vertical+block@52dbad5a28e140f291a476f92ce11996
|
||||
|
||||
```
|
||||
|
||||
After exploring different URL patterns and possible redundancies in the current URL format, the following key points were noticed. The course, run, and organization are stated in every portion of the URL. We also do not need the URL to tell us the type of block since it has been determined that all URLs will follow the path` /course/:courseId/:sequenceId/:unitId`.
|
||||
|
||||
## Decision
|
||||
|
||||
The courseware URL will format to the following structure:
|
||||
|
||||
```
|
||||
|
||||
https://learning.edx.org/c/:courseId/:sequenceHash/:unitHash/:sectionSlug/:sequenceSlug/:unitSlug/
|
||||
|
||||
```
|
||||
|
||||
Example URL:
|
||||
|
||||
```
|
||||
|
||||
https://learning.edx.org/c/course-v1:edX+DemoX.1+2T2019/YmxvY2/njuRCq/optional-example-problem-types/stem-problems/code-grader
|
||||
|
||||
```
|
||||
|
||||
The fields definition and requirements ar as follows:
|
||||
|
||||
* :courseId (required) - same as the previous `courseId`.
|
||||
* :sequenceHash (required) - a `blake2b` version of the `sequenceId`'s `urlsafe_b64encode` .
|
||||
* :unitHash (required) - a `blake2b` version of the `unitId`'s `urlsafe_b64encode`.
|
||||
* :sectionSlug (optional) - `display_name` of the current sequence's parent section.
|
||||
* :sequenceSlug (optional) - `display_name` of the current sequence.
|
||||
* :unitSlug (optional) - `display_name` of the current unit
|
||||
|
||||
Partial paths will update with the required parameters as dicussed in [ADR #8: Liberal courseware path handling](./0008-liberal-courseware-path-handling.md). The `sequenceHash` and `unitHash` will shorten their respective ids using `hashlib.blake2b` with `digest_size` of 6 bytes. `Blake2b` will reduce the length of the id so the encoded version can also be short. Hashing will be handled by `blake2b` because it is the fastest hashing function in the `hashlib` library. The hash will be generated and mapped in LMS. The slugs based on `display_name` are optional because not all blocks have an associated `display_name` attributes, most likely to occur in OLX imports. The `display_name` will be pulled from the current section, sequence, and unit attribute, and if there is not an attribute `display`, the url will use the attribute `display_name_with_default`. The `display_name` will be formatted safely for a url using Django's [slugify](https://docs.djangoproject.com/en/3.2/ref/utils/#django.utils.text.slugify). Slugify allows unicode identifiers in the slug. If the slugs are omitted, it will redirect to the canonical version without the slugs.
|
||||
|
||||
## Consequences
|
||||
|
||||
If old URLs are not properly routed then the content and those links will no longer be accessible to the user. The old URLs could include, but not limited to, bookmarks and exams.
|
||||
|
||||
## Further work
|
||||
|
||||
At some point, we may decide to further extend the URL shortening to the entire platform. At the moment, the hashes for the sequences and units are generated when the sequences and units are being called. In the future, it would be better if the hashes would be generated and stored when the sequences and units are originally created. This would require `learning_sequences` to include a class for unit storage, which is not being stored at the moment.
|
||||
2224
package-lock.json
generated
2224
package-lock.json
generated
@@ -3378,9 +3378,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@edx/frontend-build": {
|
||||
"version": "8.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-build/-/frontend-build-8.0.4.tgz",
|
||||
"integrity": "sha512-j1GXQEONHyWgCBRDKuZIIQYh0Uda4sTmDI9kShPgEa93wwLryvUexsoJhrr7gHz+cHF2EdyyR8/3fnZYhZLjdw==",
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-build/-/frontend-build-8.0.0.tgz",
|
||||
"integrity": "sha512-Z3DAqGiDPzFd0BufNHdI1p0J2wfLAxDrPH/pDZSyj38T/0GBIgfea0fODYwaqP8K3gwO+lLeaJWvOdzgMN/G8g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/cli": "7.10.5",
|
||||
@@ -3391,7 +3391,6 @@
|
||||
"@babel/preset-env": "7.10.4",
|
||||
"@babel/preset-react": "7.10.4",
|
||||
"@edx/eslint-config": "1.2.0",
|
||||
"@edx/new-relic-source-map-webpack-plugin": "1.0.0",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "0.5.0-rc.2",
|
||||
"@svgr/webpack": "5.5.0",
|
||||
"autoprefixer": "10.2.6",
|
||||
@@ -3419,6 +3418,7 @@
|
||||
"image-webpack-loader": "7.0.1",
|
||||
"jest": "26.4.2",
|
||||
"mini-css-extract-plugin": "1.6.2",
|
||||
"new-relic-source-map-webpack-plugin": "1.2.0",
|
||||
"postcss": "8.3.5",
|
||||
"postcss-loader": "6.1.1",
|
||||
"postcss-rtlcss": "3.3.4",
|
||||
@@ -3430,10 +3430,10 @@
|
||||
"source-map-loader": "0.2.4",
|
||||
"style-loader": "2.0.0",
|
||||
"url-loader": "4.1.1",
|
||||
"webpack": "5.50.0",
|
||||
"webpack": "5.44.0",
|
||||
"webpack-bundle-analyzer": "3.9.0",
|
||||
"webpack-cli": "4.8.0",
|
||||
"webpack-dev-server": "4.0.0-rc.1",
|
||||
"webpack-cli": "4.7.2",
|
||||
"webpack-dev-server": "4.0.0-beta.3",
|
||||
"webpack-merge": "5.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -3446,12 +3446,6 @@
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -3462,17 +3456,6 @@
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"cliui": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^6.2.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -3521,52 +3504,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "15.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^6.0.0",
|
||||
"decamelize": "^1.2.0",
|
||||
"find-up": "^4.1.0",
|
||||
"get-caller-file": "^2.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^2.0.0",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^4.2.0",
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^4.0.0",
|
||||
"yargs-parser": "^18.1.2"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "18.1.3",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -3583,9 +3520,9 @@
|
||||
}
|
||||
},
|
||||
"@edx/frontend-enterprise-utils": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-utils/-/frontend-enterprise-utils-1.0.0.tgz",
|
||||
"integrity": "sha512-EBReGc/Kj7pYpyvy6akd3p2okQ/GRNZpomALgLmhv/kfuF2D9Z1wmmtQ+aJXLtghpHFsmUVsVOtAJxB42orN3g==",
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-enterprise-utils/-/frontend-enterprise-utils-0.1.7.tgz",
|
||||
"integrity": "sha512-rLS/Fmq+TQPFhy1yMli4e9DsCxGAKcpZp55HvjdiiIbuMpUrWqXuP/UFemL8w45yo9osw6vH0vKhqb14RX8y4A==",
|
||||
"requires": {
|
||||
"@testing-library/react": "11.2.6",
|
||||
"history": "4.10.1",
|
||||
@@ -3659,13 +3596,41 @@
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.7.tgz",
|
||||
"integrity": "sha512-ml3lJIq9YjUfM9TUnEPvEYWFSwivwIGBPKpewX7tii7fwCazA8yCioGdqQcNsItPpfFvSJ3VIdMQPj60LJhcQA=="
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
},
|
||||
"query-string": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
|
||||
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
|
||||
"requires": {
|
||||
"decode-uri-component": "^0.2.0",
|
||||
"object-assign": "^4.1.0",
|
||||
"strict-uri-encode": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"strict-uri-encode": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@edx/frontend-lib-special-exams": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-1.13.2.tgz",
|
||||
"integrity": "sha512-NzMgyVAg63x6vg0vSe00MhREyeRSyXE16aBt/34XQHXQKG0ieEVnu3o3oQdlgzu2JWzGxxVmZr4gGeYB3qA7hQ==",
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-1.12.0.tgz",
|
||||
"integrity": "sha512-JxbsVlAYpmRmeIU8H7ftEvitrB9A54jAvY2uIFltLA2lTosThmjfq7Rf/F9UUGsJNLDHd77Kqtils5AHD3JiJw==",
|
||||
"requires": {
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.34",
|
||||
"@fortawesome/free-brands-svg-icons": "5.11.2",
|
||||
@@ -3719,12 +3684,12 @@
|
||||
}
|
||||
},
|
||||
"@edx/frontend-platform": {
|
||||
"version": "1.12.7",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-1.12.7.tgz",
|
||||
"integrity": "sha512-q4QmqVfYjuFCoG0oJthxhSx6rDljaSFZ0rjbQYccBogfwAKi+QedcGgYiPVsiRQN+b+hRleS/r+D0hJ+7zjtfQ==",
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-1.12.3.tgz",
|
||||
"integrity": "sha512-ZesfCUJS43CHEaVsakE7BxGpVgTAxmJtn4K6QEz7vWbgXsqBzCMo0msmLRhyFzhma+o86IbWOVpv/zq3rx8wnw==",
|
||||
"requires": {
|
||||
"@cospired/i18n-iso-languages": "2.2.0",
|
||||
"axios": "0.21.4",
|
||||
"axios": "0.21.1",
|
||||
"axios-cache-adapter": "2.7.3",
|
||||
"form-urlencoded": "4.1.4",
|
||||
"glob": "7.1.7",
|
||||
@@ -3742,19 +3707,10 @@
|
||||
"universal-cookie": "4.0.4"
|
||||
}
|
||||
},
|
||||
"@edx/new-relic-source-map-webpack-plugin": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/new-relic-source-map-webpack-plugin/-/new-relic-source-map-webpack-plugin-1.0.0.tgz",
|
||||
"integrity": "sha512-6z7EQxQGl/SvX2ivHxhTEgn56fU3c99kEDPbJdp8s80IWoiMN+Yq46hfCW/J0fiN1qsJsNNNAdwWlgChg/4aLQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@newrelic/publish-sourcemap": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"@edx/paragon": {
|
||||
"version": "16.13.3",
|
||||
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-16.13.3.tgz",
|
||||
"integrity": "sha512-wEXi+OE4tzNsBKpT7ZgFtgKkkDRmIB+3UOz7+cv0Ivc7YzOYcgoO6Uy4MvSsVu6eBLuGSsgLxCOk5tAx+Kixag==",
|
||||
"version": "16.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-16.7.0.tgz",
|
||||
"integrity": "sha512-WVcbFKv2OzxAQuiJ8BskrIo+ybv8p7QUEC5Ondg+/eWTt3aYtfYVvmDy7000JwPdsktVS3yKtMoDY2VbdgCNug==",
|
||||
"requires": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.30",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.14.0",
|
||||
@@ -3853,9 +3809,9 @@
|
||||
}
|
||||
},
|
||||
"@fortawesome/fontawesome-common-types": {
|
||||
"version": "0.2.36",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
|
||||
"integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg=="
|
||||
"version": "0.2.35",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.35.tgz",
|
||||
"integrity": "sha512-IHUfxSEDS9dDGqYwIW7wTN6tn/O8E0n5PcAHz9cAaBoZw6UpG20IG/YM3NNLaGPwPqgjBAFjIURzqoQs3rrtuw=="
|
||||
},
|
||||
"@fortawesome/fontawesome-svg-core": {
|
||||
"version": "1.2.36",
|
||||
@@ -4138,6 +4094,15 @@
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
@@ -4463,13 +4428,97 @@
|
||||
}
|
||||
},
|
||||
"@newrelic/publish-sourcemap": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@newrelic/publish-sourcemap/-/publish-sourcemap-5.0.1.tgz",
|
||||
"integrity": "sha512-eXkc7+RAPJPVBhgYrJWq2nLUDDj1yrgM1yyaT6kDbczZe+NtecOwc3m3yx2WCkVRiAaSANQrKdUsbKkoqt5msg==",
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@newrelic/publish-sourcemap/-/publish-sourcemap-4.4.2.tgz",
|
||||
"integrity": "sha512-okPE9K1A5hOHgFm/gfBwxPM8MfBykxbusPEfI20CzS/MboF+4eCx+PS/gfBiDaVf/OrsHPCGTwADkXPPk48a/Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"superagent": "^3.4.1",
|
||||
"yargs": "^16.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"cliui": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^7.0.2",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.0",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^20.2.2"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "20.2.9",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
|
||||
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
@@ -4499,11 +4548,11 @@
|
||||
}
|
||||
},
|
||||
"@pact-foundation/pact": {
|
||||
"version": "9.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@pact-foundation/pact/-/pact-9.16.1.tgz",
|
||||
"integrity": "sha512-hknXmKy3uvZsJ2rJlazyUk1hJWnxRuxAFbMHZ/edWjxF2gmQO3xyA7SKFjgEhbghORcgLnK3308q/5rJFOfbQg==",
|
||||
"version": "9.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@pact-foundation/pact/-/pact-9.16.0.tgz",
|
||||
"integrity": "sha512-UC6xBATLHvfzdMa14IkzvQgmBPxGiLfrWicljpotD9KrIguKQnxnRmpB1vkflcN3kpOLQM4f8HLiSD1HKYeCkw==",
|
||||
"requires": {
|
||||
"@pact-foundation/pact-node": "^10.13.7",
|
||||
"@pact-foundation/pact-node": "^10.12.2",
|
||||
"@types/bluebird": "^3.5.20",
|
||||
"@types/express": "^4.17.11",
|
||||
"bluebird": "~3.5.1",
|
||||
@@ -4536,11 +4585,10 @@
|
||||
}
|
||||
},
|
||||
"@pact-foundation/pact-node": {
|
||||
"version": "10.13.7",
|
||||
"resolved": "https://registry.npmjs.org/@pact-foundation/pact-node/-/pact-node-10.13.7.tgz",
|
||||
"integrity": "sha512-EhSo5t0QCW5CXdqXPtLo/tkAmAn0Phm7qNgPibh5p5+38Mdrjee77Muk1LVd/MjlW6NV5dH+zLAlD40z4CRelw==",
|
||||
"version": "10.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@pact-foundation/pact-node/-/pact-node-10.13.1.tgz",
|
||||
"integrity": "sha512-TmBBrBmGTrJ3XxnYeIl3ZqrebmOKy6mw6Hz9Cd3pXeXnfPA4MG3gEbNh6NNZ7F/D998AcV7Nq5dH0o68LOgu1Q==",
|
||||
"requires": {
|
||||
"@types/needle": "^2.5.1",
|
||||
"@types/pino": "^6.3.5",
|
||||
"@types/q": "1.0.7",
|
||||
"@types/request": "2.48.2",
|
||||
@@ -4555,7 +4603,7 @@
|
||||
"q": "1.5.1",
|
||||
"rimraf": "2.6.2",
|
||||
"sumchecker": "^2.0.2",
|
||||
"tar": "^6.1.11",
|
||||
"tar": "4.4.2",
|
||||
"underscore": "1.12.1",
|
||||
"unixify": "1.0.0",
|
||||
"unzipper": "^0.10.10",
|
||||
@@ -4665,9 +4713,9 @@
|
||||
}
|
||||
},
|
||||
"@popperjs/core": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.1.tgz",
|
||||
"integrity": "sha512-HnUhk1Sy9IuKrxEMdIRCxpIqPw6BFsbYSEUO9p/hNw5sMld/+3OLMWQP80F8/db9qsv3qUjs7ZR5bS/R+iinXw=="
|
||||
"version": "2.9.3",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.3.tgz",
|
||||
"integrity": "sha512-xDu17cEfh7Kid/d95kB6tZsLOmSWKCZKtprnhVepjsSaCij+lM3mItSJDuuHDMbCWTh8Ejmebwb+KONcCJ0eXQ=="
|
||||
},
|
||||
"@reduxjs/toolkit": {
|
||||
"version": "1.6.1",
|
||||
@@ -5696,9 +5744,9 @@
|
||||
}
|
||||
},
|
||||
"@testing-library/user-event": {
|
||||
"version": "13.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.2.1.tgz",
|
||||
"integrity": "sha512-cczlgVl+krjOb3j1625usarNEibI0IFRJrSWX9UsJ1HKYFgCQv9Nb7QAipUDXl3Xdz8NDTsiS78eAkPSxlzTlw==",
|
||||
"version": "12.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-12.8.3.tgz",
|
||||
"integrity": "sha512-IR0iWbFkgd56Bu5ZI/ej8yQwrkCv8Qydx6RzwbKz9faXazR/+5tvYKsZQgyXJiwgpcva127YO6JcWy7YlCfofQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.5"
|
||||
@@ -5902,9 +5950,9 @@
|
||||
}
|
||||
},
|
||||
"@types/invariant": {
|
||||
"version": "2.2.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.35.tgz",
|
||||
"integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg=="
|
||||
"version": "2.2.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.34.tgz",
|
||||
"integrity": "sha512-lYUtmJ9BqUN688fGY1U1HZoWT1/Jrmgigx2loq4ZcJpICECm/Om3V314BxdzypO0u5PORKGMM6x0OXaljV1YFg=="
|
||||
},
|
||||
"@types/istanbul-lib-coverage": {
|
||||
"version": "2.0.3",
|
||||
@@ -5954,14 +6002,6 @@
|
||||
"integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/needle": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/needle/-/needle-2.5.2.tgz",
|
||||
"integrity": "sha512-FSckojxsXODVYE4oJ7q0OjUki27a6gsdIxp2WJHs9oEmXit/0rjzb/NK+tJnKwFMMyR6mzo+1Nyr83ELw3YT+Q==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "15.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz",
|
||||
@@ -6044,9 +6084,9 @@
|
||||
}
|
||||
},
|
||||
"@types/react-redux": {
|
||||
"version": "7.1.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.18.tgz",
|
||||
"integrity": "sha512-9iwAsPyJ9DLTRH+OFeIrm9cAbIj1i2ANL3sKQFATqnPWRbg+jEFXyZOKHiQK/N86pNRXbb4HRxAxo0SIX1XwzQ==",
|
||||
"version": "7.1.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.16.tgz",
|
||||
"integrity": "sha512-f/FKzIrZwZk7YEO9E1yoxIuDNRiDducxkFlkw/GNMGEnK9n4K8wJzlJBghpSuOVDgEUHoDkDF7Gi9lHNQR4siw==",
|
||||
"requires": {
|
||||
"@types/hoist-non-react-statics": "^3.3.0",
|
||||
"@types/react": "*",
|
||||
@@ -6380,9 +6420,9 @@
|
||||
}
|
||||
},
|
||||
"@webpack-cli/serve": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.5.2.tgz",
|
||||
"integrity": "sha512-vgJ5OLWadI8aKjDlOH3rb+dYyPd2GTZuQC/Tihjct6F9GpXGZINo3Y/IVuZVTM1eDQB+/AOsjPUWH/WySDaXvw==",
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.5.1.tgz",
|
||||
"integrity": "sha512-4vSVUiOPJLmr45S8rMGy7WDvpWxfFxfP/Qx/cxZFCfvoypTYpPPL1X8VIZMe0WTA+Jr7blUxwUSEZNkjoMTgSw==",
|
||||
"dev": true
|
||||
},
|
||||
"@xtuc/ieee754": {
|
||||
@@ -6428,12 +6468,6 @@
|
||||
"acorn-walk": "^7.1.1"
|
||||
}
|
||||
},
|
||||
"acorn-import-assertions": {
|
||||
"version": "1.7.6",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.7.6.tgz",
|
||||
"integrity": "sha512-FlVvVFA1TX6l3lp8VjDnYYq7R1nyW6x3svAt4nDgrWQ9SBaSh9CnbwgSUTasgfNfOG5HlM1ehugCvM+hjo56LA==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn-jsx": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
|
||||
@@ -6911,11 +6945,11 @@
|
||||
"integrity": "sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.21.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
|
||||
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.0"
|
||||
"follow-redirects": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"axios-cache-adapter": {
|
||||
@@ -6928,14 +6962,13 @@
|
||||
}
|
||||
},
|
||||
"axios-mock-adapter": {
|
||||
"version": "1.20.0",
|
||||
"resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.20.0.tgz",
|
||||
"integrity": "sha512-shZRhTjLP0WWdcvHKf3rH3iW9deb3UdKbdnKUoHmmsnBhVXN3sjPJM6ZvQ2r/ywgvBVQrMnjrSyQab60G1sr2w==",
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.19.0.tgz",
|
||||
"integrity": "sha512-D+0U4LNPr7WroiBDvWilzTMYPYTuZlbo6BI8YHZtj7wYQS8NkARlP9KBt8IWWHTQJ0q/8oZ0ClPBtKCCkx8cQg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"is-blob": "^2.1.0",
|
||||
"is-buffer": "^2.0.5"
|
||||
"is-buffer": "^2.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-buffer": {
|
||||
@@ -7872,24 +7905,16 @@
|
||||
"dev": true
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.16.8",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.8.tgz",
|
||||
"integrity": "sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ==",
|
||||
"version": "4.16.7",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.7.tgz",
|
||||
"integrity": "sha512-7I4qVwqZltJ7j37wObBe3SoTz+nS8APaNcrBOlgoirb6/HbEU2XxW/LpUDTCngM6iauwFqmRTuOMfyKnFGY5JA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001251",
|
||||
"colorette": "^1.3.0",
|
||||
"electron-to-chromium": "^1.3.811",
|
||||
"caniuse-lite": "^1.0.30001248",
|
||||
"colorette": "^1.2.2",
|
||||
"electron-to-chromium": "^1.3.793",
|
||||
"escalade": "^3.1.1",
|
||||
"node-releases": "^1.1.75"
|
||||
},
|
||||
"dependencies": {
|
||||
"colorette": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz",
|
||||
"integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==",
|
||||
"dev": true
|
||||
}
|
||||
"node-releases": "^1.1.73"
|
||||
}
|
||||
},
|
||||
"bser": {
|
||||
@@ -8308,9 +8333,9 @@
|
||||
}
|
||||
},
|
||||
"chownr": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||
},
|
||||
"chrome-trace-event": {
|
||||
"version": "1.0.3",
|
||||
@@ -8427,6 +8452,13 @@
|
||||
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
|
||||
"dev": true
|
||||
},
|
||||
"colors": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
|
||||
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||
@@ -8461,14 +8493,25 @@
|
||||
"dev": true
|
||||
},
|
||||
"cliui": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
"wrap-ansi": "^6.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"clone-deep": {
|
||||
@@ -8557,9 +8600,9 @@
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
},
|
||||
"colord": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/colord/-/colord-2.7.0.tgz",
|
||||
"integrity": "sha512-pZJBqsHz+pYyw3zpX6ZRXWoCHM1/cvFikY9TV8G3zcejCaKE0lhankoj8iScyrrePA8C7yJ5FStfA9zbcOnw7Q==",
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/colord/-/colord-2.6.0.tgz",
|
||||
"integrity": "sha512-8yMrtE20ZxH1YWvvSoeJFtvqY+GIAOfU+mZ3jx7ZSiEMasnAmNqD1BKUP3CuCWcy/XHgcXkLW6YU8C35nhOYVg==",
|
||||
"dev": true
|
||||
},
|
||||
"colorette": {
|
||||
@@ -8568,12 +8611,6 @@
|
||||
"integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==",
|
||||
"dev": true
|
||||
},
|
||||
"colors": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
|
||||
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
|
||||
"dev": true
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
@@ -8782,14 +8819,14 @@
|
||||
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
|
||||
},
|
||||
"core-js": {
|
||||
"version": "3.16.4",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.4.tgz",
|
||||
"integrity": "sha512-Tq4GVE6XCjE+hcyW6hPy0ofN3hwtLudz5ZRdrlCnsnD/xkm/PWQRudzYHiKgZKUcefV6Q57fhDHjZHJP5dpfSg=="
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.1.tgz",
|
||||
"integrity": "sha512-AAkP8i35EbefU+JddyWi12AWE9f2N/qr/pwnDtWz4nyUIBGMJPX99ANFFRSw6FefM374lDujdtLDyhN2A/btHw=="
|
||||
},
|
||||
"core-js-compat": {
|
||||
"version": "3.16.2",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.16.2.tgz",
|
||||
"integrity": "sha512-4lUshXtBXsdmp8cDWh6KKiHUg40AjiuPD3bOWkNVsr1xkAhpUqCjaZ8lB1bKx9Gb5fXcbRbFJ4f4qpRIRTuJqQ==",
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.16.1.tgz",
|
||||
"integrity": "sha512-NHXQXvRbd4nxp9TEmooTJLUf94ySUG6+DSsscBpTftN1lQLQ4LjnWvc7AoIo4UjDsFF3hB8Uh5LLCRRdaiT5MQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browserslist": "^4.16.7",
|
||||
@@ -8961,9 +8998,9 @@
|
||||
}
|
||||
},
|
||||
"cssnano-preset-default": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.4.tgz",
|
||||
"integrity": "sha512-sPpQNDQBI3R/QsYxQvfB4mXeEcWuw0wGtKtmS5eg8wudyStYMgKOQT39G07EbW1LB56AOYrinRS9f0ig4Y3MhQ==",
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.3.tgz",
|
||||
"integrity": "sha512-qo9tX+t4yAAZ/yagVV3b+QBKeLklQbmgR3wI7mccrDcR+bEk9iHgZN1E7doX68y9ThznLya3RDmR+nc7l6/2WQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"css-declaration-sorter": "^6.0.3",
|
||||
@@ -8978,7 +9015,7 @@
|
||||
"postcss-merge-longhand": "^5.0.2",
|
||||
"postcss-merge-rules": "^5.0.2",
|
||||
"postcss-minify-font-values": "^5.0.1",
|
||||
"postcss-minify-gradients": "^5.0.2",
|
||||
"postcss-minify-gradients": "^5.0.1",
|
||||
"postcss-minify-params": "^5.0.1",
|
||||
"postcss-minify-selectors": "^5.1.0",
|
||||
"postcss-normalize-charset": "^5.0.1",
|
||||
@@ -9424,12 +9461,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"define-lazy-prop": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
|
||||
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
|
||||
"dev": true
|
||||
},
|
||||
"define-properties": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
||||
@@ -9847,9 +9878,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.813",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.813.tgz",
|
||||
"integrity": "sha512-YcSRImHt6JZZ2sSuQ4Bzajtk98igQ0iKkksqlzZLzbh4p0OIyJRSvUbsgqfcR8txdfsoYCc4ym306t4p2kP/aw==",
|
||||
"version": "1.3.803",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.803.tgz",
|
||||
"integrity": "sha512-tmRK9qB8Zs8eLMtTBp+w2zVS9MUe62gQQQHjYdAc5Zljam3ZIokNb+vZLPRz9RCREp6EFRwyhOFwbt1fEriQ2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"email-prop-type": {
|
||||
@@ -9959,20 +9990,20 @@
|
||||
}
|
||||
},
|
||||
"es-check": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es-check/-/es-check-6.0.0.tgz",
|
||||
"integrity": "sha512-FwWQ03GgWL8HplV7gWMDtpR5I+n0K/JYLnLoMDw2DrdsfxF2jV+dtpqo0sIVFeoHgYjEeWkb0ntZvEah7R7T1w==",
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/es-check/-/es-check-5.2.4.tgz",
|
||||
"integrity": "sha512-FZ3qAJ9hwguqPvGGagaKAVDnusSkezeHbiKNM5rOepOjloeVuX2e6meMxQ+mKcnWbAFucCG7fszNrzUT8bvHcQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"acorn": "^8.4.1",
|
||||
"caporal": "^1.4.0",
|
||||
"glob": "^7.1.7"
|
||||
"acorn": "^6.4.1",
|
||||
"caporal": "1.4.0",
|
||||
"glob": "^7.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "8.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz",
|
||||
"integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==",
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
|
||||
"integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -10125,12 +10156,6 @@
|
||||
"v8-compile-cache": "^2.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||
"dev": true
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
|
||||
@@ -10169,15 +10194,6 @@
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^4.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -10204,9 +10220,9 @@
|
||||
}
|
||||
},
|
||||
"eslint-import-resolver-node": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz",
|
||||
"integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==",
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.5.tgz",
|
||||
"integrity": "sha512-XMoPKjSpXbkeJ7ZZ9icLnJMTY5Mc1kZbCakHquaFsXPpyWOwK0TK6CODO+0ca54UoM9LKOxyUNnoVZRl8TeaAg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "^3.2.7",
|
||||
@@ -10795,11 +10811,11 @@
|
||||
}
|
||||
},
|
||||
"ext": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.5.0.tgz",
|
||||
"integrity": "sha512-+ONcYoWj/SoQwUofMr94aGu05Ou4FepKi7N7b+O8T4jVfyIsZQV1/xeS8jpaBzF0csAk0KLXoHCxU7cKYZjo1Q==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
|
||||
"integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
|
||||
"requires": {
|
||||
"type": "^2.5.0"
|
||||
"type": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"type": {
|
||||
@@ -11017,14 +11033,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"fast-redact": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.0.2.tgz",
|
||||
"integrity": "sha512-YN+CYfCVRVMUZOUPeinHNKgytM1wPI/C/UCLEi56EsY2dwwvI00kIJHJoI7pMVqGoMew8SMZ2SSfHKHULHXDsg=="
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.0.1.tgz",
|
||||
"integrity": "sha512-kYpn4Y/valC9MdrISg47tZOpYBNoTXKgT9GYXFpHN/jYFs+lFkPoisY+LcBODdKVMY96ATzvzsWv+ES/4Kmufw=="
|
||||
},
|
||||
"fast-safe-stringify": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
|
||||
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.8.tgz",
|
||||
"integrity": "sha512-lXatBjf3WPjmWD6DpIZxkeSsCOwqI0maYMpgDlx8g4U2qi4lbjA9oH/HD2a87G+KfsUmo5WbJFmqBZlPxtptag=="
|
||||
},
|
||||
"fast-url-parser": {
|
||||
"version": "1.1.3",
|
||||
@@ -11056,15 +11072,10 @@
|
||||
"integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==",
|
||||
"dev": true
|
||||
},
|
||||
"fastify-warning": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fastify-warning/-/fastify-warning-0.2.0.tgz",
|
||||
"integrity": "sha512-s1EQguBw/9qtc1p/WTY4eq9WMRIACkj+HTcOIK1in4MV5aFaQC9ZCIt0dJ7pr5bIf4lPpHvAtP2ywpTNgs7hqw=="
|
||||
},
|
||||
"fastq": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.12.0.tgz",
|
||||
"integrity": "sha512-VNX0QkHK3RsXVKr9KrlUv/FoTa0NdbYoHHl7uXHv2rzyHSlxjdNAKug2twd9luJxpcyNeAgf5iPPMutJO67Dfg==",
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz",
|
||||
"integrity": "sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"reusify": "^1.0.4"
|
||||
@@ -11269,9 +11280,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"focus-lock": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.9.2.tgz",
|
||||
"integrity": "sha512-YtHxjX7a0IC0ZACL5wsX8QdncXofWpGPNoVMuI/nZUrPGp6LmNI6+D5j0pPj+v8Kw5EpweA+T5yImK0rnWf7oQ==",
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.9.1.tgz",
|
||||
"integrity": "sha512-/2Nj60Cps6yOLSO+CkVbeSKfwfns5XbX6HOedIK9PdzODP04N9c3xqOcPXayN0WsT9YjJvAnXmI0NdqNIDf5Kw==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.3"
|
||||
},
|
||||
@@ -11444,11 +11455,11 @@
|
||||
}
|
||||
},
|
||||
"fs-minipass": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
|
||||
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
|
||||
"requires": {
|
||||
"minipass": "^3.0.0"
|
||||
"minipass": "^2.6.0"
|
||||
}
|
||||
},
|
||||
"fs-monkey": {
|
||||
@@ -11838,9 +11849,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
|
||||
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -11979,6 +11990,12 @@
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||
"dev": true
|
||||
},
|
||||
"hex-color-regex": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
|
||||
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==",
|
||||
"dev": true
|
||||
},
|
||||
"history": {
|
||||
"version": "4.10.1",
|
||||
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
|
||||
@@ -12062,6 +12079,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"hsl-regex": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz",
|
||||
"integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=",
|
||||
"dev": true
|
||||
},
|
||||
"hsla-regex": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz",
|
||||
"integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=",
|
||||
"dev": true
|
||||
},
|
||||
"html-encoding-sniffer": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz",
|
||||
@@ -12236,11 +12265,11 @@
|
||||
}
|
||||
},
|
||||
"http-proxy-middleware": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.2.tgz",
|
||||
"integrity": "sha512-aYk1rTKqLTus23X3L96LGNCGNgWpG4cG0XoZIT1GUPhhulEHX/QalnO6Vbo+WmKWi4AL2IidjuC0wZtbpg0yhQ==",
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz",
|
||||
"integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==",
|
||||
"requires": {
|
||||
"http-proxy": "^1.18.1",
|
||||
"http-proxy": "^1.17.0",
|
||||
"is-glob": "^4.0.0",
|
||||
"lodash": "^4.17.11",
|
||||
"micromatch": "^3.1.10"
|
||||
@@ -12280,9 +12309,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"husky": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/husky/-/husky-7.0.2.tgz",
|
||||
"integrity": "sha512-8yKEWNX4z2YsofXAMT7KvA1g8p+GxtB1ffV8XtpAEGuXNAbCV5wdNKH+qTpw8SM9fh4aMPDR+yQuKfgnreyZlg==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/husky/-/husky-7.0.1.tgz",
|
||||
"integrity": "sha512-gceRaITVZ+cJH9sNHqx5tFwbzlLCVxtVZcusME8JYQ8Edy5mpGDOqD8QBCdMhpyo9a+JXddnujQ4rpY2Ff9SJA==",
|
||||
"dev": true
|
||||
},
|
||||
"hyphenate-style-name": {
|
||||
@@ -12850,6 +12879,15 @@
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -13016,12 +13054,6 @@
|
||||
"binary-extensions": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-blob": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-blob/-/is-blob-2.1.0.tgz",
|
||||
"integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==",
|
||||
"dev": true
|
||||
},
|
||||
"is-boolean-object": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz",
|
||||
@@ -13049,6 +13081,28 @@
|
||||
"ci-info": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"is-color-stop": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz",
|
||||
"integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"css-color-names": "^0.0.4",
|
||||
"hex-color-regex": "^1.1.0",
|
||||
"hsl-regex": "^1.0.0",
|
||||
"hsla-regex": "^1.0.0",
|
||||
"rgb-regex": "^1.0.1",
|
||||
"rgba-regex": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"css-color-names": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
|
||||
"integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"is-core-module": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz",
|
||||
@@ -13620,49 +13674,49 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@jest/console": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/console/-/console-27.1.0.tgz",
|
||||
"integrity": "sha512-+Vl+xmLwAXLNlqT61gmHEixeRbS4L8MUzAjtpBCOPWH+izNI/dR16IeXjkXJdRtIVWVSf9DO1gdp67B1XorZhQ==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/console/-/console-27.0.6.tgz",
|
||||
"integrity": "sha512-fMlIBocSHPZ3JxgWiDNW/KPj6s+YRd0hicb33IrmelCcjXo/pXPwvuiKFmZz+XuqI/1u7nbUK10zSsWL/1aegg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"jest-message-util": "^27.1.0",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-message-util": "^27.0.6",
|
||||
"jest-util": "^27.0.6",
|
||||
"slash": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"@jest/core": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/core/-/core-27.1.0.tgz",
|
||||
"integrity": "sha512-3l9qmoknrlCFKfGdrmiQiPne+pUR4ALhKwFTYyOeKw6egfDwJkO21RJ1xf41rN8ZNFLg5W+w6+P4fUqq4EMRWA==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/core/-/core-27.0.6.tgz",
|
||||
"integrity": "sha512-SsYBm3yhqOn5ZLJCtccaBcvD/ccTLCeuDv8U41WJH/V1MW5eKUkeMHT9U+Pw/v1m1AIWlnIW/eM2XzQr0rEmow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/console": "^27.1.0",
|
||||
"@jest/reporters": "^27.1.0",
|
||||
"@jest/test-result": "^27.1.0",
|
||||
"@jest/transform": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/console": "^27.0.6",
|
||||
"@jest/reporters": "^27.0.6",
|
||||
"@jest/test-result": "^27.0.6",
|
||||
"@jest/transform": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/node": "*",
|
||||
"ansi-escapes": "^4.2.1",
|
||||
"chalk": "^4.0.0",
|
||||
"emittery": "^0.8.1",
|
||||
"exit": "^0.1.2",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"jest-changed-files": "^27.1.0",
|
||||
"jest-config": "^27.1.0",
|
||||
"jest-haste-map": "^27.1.0",
|
||||
"jest-message-util": "^27.1.0",
|
||||
"jest-changed-files": "^27.0.6",
|
||||
"jest-config": "^27.0.6",
|
||||
"jest-haste-map": "^27.0.6",
|
||||
"jest-message-util": "^27.0.6",
|
||||
"jest-regex-util": "^27.0.6",
|
||||
"jest-resolve": "^27.1.0",
|
||||
"jest-resolve-dependencies": "^27.1.0",
|
||||
"jest-runner": "^27.1.0",
|
||||
"jest-runtime": "^27.1.0",
|
||||
"jest-snapshot": "^27.1.0",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-validate": "^27.1.0",
|
||||
"jest-watcher": "^27.1.0",
|
||||
"jest-resolve": "^27.0.6",
|
||||
"jest-resolve-dependencies": "^27.0.6",
|
||||
"jest-runner": "^27.0.6",
|
||||
"jest-runtime": "^27.0.6",
|
||||
"jest-snapshot": "^27.0.6",
|
||||
"jest-util": "^27.0.6",
|
||||
"jest-validate": "^27.0.6",
|
||||
"jest-watcher": "^27.0.6",
|
||||
"micromatch": "^4.0.4",
|
||||
"p-each-series": "^2.1.0",
|
||||
"rimraf": "^3.0.0",
|
||||
@@ -13671,53 +13725,53 @@
|
||||
}
|
||||
},
|
||||
"@jest/environment": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.1.0.tgz",
|
||||
"integrity": "sha512-wRp50aAMY2w1U2jP1G32d6FUVBNYqmk8WaGkiIEisU48qyDV0WPtw3IBLnl7orBeggveommAkuijY+RzVnNDOQ==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.0.6.tgz",
|
||||
"integrity": "sha512-4XywtdhwZwCpPJ/qfAkqExRsERW+UaoSRStSHCCiQTUpoYdLukj+YJbQSFrZjhlUDRZeNiU9SFH0u7iNimdiIg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/fake-timers": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/fake-timers": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/node": "*",
|
||||
"jest-mock": "^27.1.0"
|
||||
"jest-mock": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"@jest/fake-timers": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.1.0.tgz",
|
||||
"integrity": "sha512-22Zyn8il8DzpS+30jJNVbTlm7vAtnfy1aYvNeOEHloMlGy1PCYLHa4PWlSws0hvNsMM5bON6GISjkLoQUV3oMA==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.0.6.tgz",
|
||||
"integrity": "sha512-sqd+xTWtZ94l3yWDKnRTdvTeZ+A/V7SSKrxsrOKSqdyddb9CeNRF8fbhAU0D7ZJBpTTW2nbp6MftmKJDZfW2LQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@sinonjs/fake-timers": "^7.0.2",
|
||||
"@types/node": "*",
|
||||
"jest-message-util": "^27.1.0",
|
||||
"jest-mock": "^27.1.0",
|
||||
"jest-util": "^27.1.0"
|
||||
"jest-message-util": "^27.0.6",
|
||||
"jest-mock": "^27.0.6",
|
||||
"jest-util": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"@jest/globals": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.1.0.tgz",
|
||||
"integrity": "sha512-73vLV4aNHAlAgjk0/QcSIzzCZSqVIPbmFROJJv9D3QUR7BI4f517gVdJpSrCHxuRH3VZFhe0yGG/tmttlMll9g==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.0.6.tgz",
|
||||
"integrity": "sha512-DdTGCP606rh9bjkdQ7VvChV18iS7q0IMJVP1piwTWyWskol4iqcVwthZmoJEf7obE1nc34OpIyoVGPeqLC+ryw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/environment": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"expect": "^27.1.0"
|
||||
"@jest/environment": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"expect": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"@jest/reporters": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.1.0.tgz",
|
||||
"integrity": "sha512-5T/zlPkN2HnK3Sboeg64L5eC8iiaZueLpttdktWTJsvALEtP2YMkC5BQxwjRWQACG9SwDmz+XjjkoxXUDMDgdw==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.0.6.tgz",
|
||||
"integrity": "sha512-TIkBt09Cb2gptji3yJXb3EE+eVltW6BjO7frO7NEfjI9vSIYoISi5R3aI3KpEDXlB1xwB+97NXIqz84qYeYsfA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@bcoe/v8-coverage": "^0.2.3",
|
||||
"@jest/console": "^27.1.0",
|
||||
"@jest/test-result": "^27.1.0",
|
||||
"@jest/transform": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/console": "^27.0.6",
|
||||
"@jest/test-result": "^27.0.6",
|
||||
"@jest/transform": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"chalk": "^4.0.0",
|
||||
"collect-v8-coverage": "^1.0.0",
|
||||
"exit": "^0.1.2",
|
||||
@@ -13728,10 +13782,10 @@
|
||||
"istanbul-lib-report": "^3.0.0",
|
||||
"istanbul-lib-source-maps": "^4.0.0",
|
||||
"istanbul-reports": "^3.0.2",
|
||||
"jest-haste-map": "^27.1.0",
|
||||
"jest-resolve": "^27.1.0",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-worker": "^27.1.0",
|
||||
"jest-haste-map": "^27.0.6",
|
||||
"jest-resolve": "^27.0.6",
|
||||
"jest-util": "^27.0.6",
|
||||
"jest-worker": "^27.0.6",
|
||||
"slash": "^3.0.0",
|
||||
"source-map": "^0.6.0",
|
||||
"string-length": "^4.0.1",
|
||||
@@ -13751,45 +13805,45 @@
|
||||
}
|
||||
},
|
||||
"@jest/test-result": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.1.0.tgz",
|
||||
"integrity": "sha512-Aoz00gpDL528ODLghat3QSy6UBTD5EmmpjrhZZMK/v1Q2/rRRqTGnFxHuEkrD4z/Py96ZdOHxIWkkCKRpmnE1A==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.0.6.tgz",
|
||||
"integrity": "sha512-ja/pBOMTufjX4JLEauLxE3LQBPaI2YjGFtXexRAjt1I/MbfNlMx0sytSX3tn5hSLzQsR3Qy2rd0hc1BWojtj9w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/console": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/console": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||
"collect-v8-coverage": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"@jest/test-sequencer": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.1.0.tgz",
|
||||
"integrity": "sha512-lnCWawDr6Z1DAAK9l25o3AjmKGgcutq1iIbp+hC10s/HxnB8ZkUsYq1FzjOoxxZ5hW+1+AthBtvS4x9yno3V1A==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.0.6.tgz",
|
||||
"integrity": "sha512-bISzNIApazYOlTHDum9PwW22NOyDa6VI31n6JucpjTVM0jD6JDgqEZ9+yn575nDdPF0+4csYDxNNW13NvFQGZA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/test-result": "^27.1.0",
|
||||
"@jest/test-result": "^27.0.6",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"jest-haste-map": "^27.1.0",
|
||||
"jest-runtime": "^27.1.0"
|
||||
"jest-haste-map": "^27.0.6",
|
||||
"jest-runtime": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"@jest/transform": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.1.0.tgz",
|
||||
"integrity": "sha512-ZRGCA2ZEVJ00ubrhkTG87kyLbN6n55g1Ilq0X9nJb5bX3MhMp3O6M7KG+LvYu+nZRqG5cXsQnJEdZbdpTAV8pQ==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.0.6.tgz",
|
||||
"integrity": "sha512-rj5Dw+mtIcntAUnMlW/Vju5mr73u8yg+irnHwzgtgoeI6cCPOvUwQ0D1uQtc/APmWgvRweEb1g05pkUpxH3iCA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/core": "^7.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"babel-plugin-istanbul": "^6.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"convert-source-map": "^1.4.0",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"jest-haste-map": "^27.1.0",
|
||||
"jest-haste-map": "^27.0.6",
|
||||
"jest-regex-util": "^27.0.6",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-util": "^27.0.6",
|
||||
"micromatch": "^4.0.4",
|
||||
"pirates": "^4.0.1",
|
||||
"slash": "^3.0.0",
|
||||
@@ -13798,9 +13852,9 @@
|
||||
}
|
||||
},
|
||||
"@jest/types": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-27.1.0.tgz",
|
||||
"integrity": "sha512-pRP5cLIzN7I7Vp6mHKRSaZD7YpBTK7hawx5si8trMKqk4+WOdK8NEKOTO2G8PKWD1HbKMVckVB6/XHh/olhf2g==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-27.0.6.tgz",
|
||||
"integrity": "sha512-aSquT1qa9Pik26JK5/3rvnYb4bGtm1VFNesHKmNTwmPIgOrixvhL2ghIvFRNEpzy3gU+rUgjIF/KodbkFAl++g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||
@@ -13828,6 +13882,12 @@
|
||||
"@types/yargs-parser": "*"
|
||||
}
|
||||
},
|
||||
"acorn": {
|
||||
"version": "8.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz",
|
||||
"integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
@@ -13848,13 +13908,13 @@
|
||||
}
|
||||
},
|
||||
"babel-jest": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.1.0.tgz",
|
||||
"integrity": "sha512-6NrdqzaYemALGCuR97QkC/FkFIEBWP5pw5TMJoUHZTVXyOgocujp6A0JE2V6gE0HtqAAv6VKU/nI+OCR1Z4gHA==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.0.6.tgz",
|
||||
"integrity": "sha512-iTJyYLNc4wRofASmofpOc5NK9QunwMk+TLFgGXsTFS8uEqmd8wdI7sga0FPe2oVH3b5Agt/EAK1QjPEuKL8VfA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/transform": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/transform": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/babel__core": "^7.1.14",
|
||||
"babel-plugin-istanbul": "^6.0.0",
|
||||
"babel-preset-jest": "^27.0.6",
|
||||
@@ -13916,6 +13976,17 @@
|
||||
"integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==",
|
||||
"dev": true
|
||||
},
|
||||
"cliui": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -13972,16 +14043,16 @@
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/expect/-/expect-27.1.0.tgz",
|
||||
"integrity": "sha512-9kJngV5hOJgkFil4F/uXm3hVBubUK2nERVfvqNNwxxuW8ZOUwSTTSysgfzckYtv/LBzj/LJXbiAF7okHCXgdug==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/expect/-/expect-27.0.6.tgz",
|
||||
"integrity": "sha512-psNLt8j2kwg42jGBDSfAlU49CEZxejN1f1PlANWDZqIhBOVU/c2Pm888FcjWJzFewhIsNWfZJeLjUjtKGiPuSw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"ansi-styles": "^5.0.0",
|
||||
"jest-get-type": "^27.0.6",
|
||||
"jest-matcher-utils": "^27.1.0",
|
||||
"jest-message-util": "^27.1.0",
|
||||
"jest-matcher-utils": "^27.0.6",
|
||||
"jest-message-util": "^27.0.6",
|
||||
"jest-regex-util": "^27.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -14002,6 +14073,17 @@
|
||||
"to-regex-range": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"form-data": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
|
||||
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
@@ -14021,6 +14103,12 @@
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"human-signals": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
||||
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
|
||||
"dev": true
|
||||
},
|
||||
"is-ci": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz",
|
||||
@@ -14043,75 +14131,75 @@
|
||||
"dev": true
|
||||
},
|
||||
"jest-changed-files": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.1.0.tgz",
|
||||
"integrity": "sha512-eRcb13TfQw0xiV2E98EmiEgs9a5uaBIqJChyl0G7jR9fCIvGjXovnDS6Zbku3joij4tXYcSK4SE1AXqOlUxjWg==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.0.6.tgz",
|
||||
"integrity": "sha512-BuL/ZDauaq5dumYh5y20sn4IISnf1P9A0TDswTxUi84ORGtVa86ApuBHqICL0vepqAnZiY6a7xeSPWv2/yy4eA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"execa": "^5.0.0",
|
||||
"throat": "^6.0.1"
|
||||
}
|
||||
},
|
||||
"jest-cli": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.1.0.tgz",
|
||||
"integrity": "sha512-h6zPUOUu+6oLDrXz0yOWY2YXvBLk8gQinx4HbZ7SF4V3HzasQf+ncoIbKENUMwXyf54/6dBkYXvXJos+gOHYZw==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.0.6.tgz",
|
||||
"integrity": "sha512-qUUVlGb9fdKir3RDE+B10ULI+LQrz+MCflEH2UJyoUjoHHCbxDrMxSzjQAPUMsic4SncI62ofYCcAvW6+6rhhg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/core": "^27.1.0",
|
||||
"@jest/test-result": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/core": "^27.0.6",
|
||||
"@jest/test-result": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"chalk": "^4.0.0",
|
||||
"exit": "^0.1.2",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"import-local": "^3.0.2",
|
||||
"jest-config": "^27.1.0",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-validate": "^27.1.0",
|
||||
"jest-config": "^27.0.6",
|
||||
"jest-util": "^27.0.6",
|
||||
"jest-validate": "^27.0.6",
|
||||
"prompts": "^2.0.1",
|
||||
"yargs": "^16.0.3"
|
||||
}
|
||||
},
|
||||
"jest-config": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.1.0.tgz",
|
||||
"integrity": "sha512-GMo7f76vMYUA3b3xOdlcKeKQhKcBIgurjERO2hojo0eLkKPGcw7fyIoanH+m6KOP2bLad+fGnF8aWOJYxzNPeg==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.0.6.tgz",
|
||||
"integrity": "sha512-JZRR3I1Plr2YxPBhgqRspDE2S5zprbga3swYNrvY3HfQGu7p/GjyLOqwrYad97tX3U3mzT53TPHVmozacfP/3w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/core": "^7.1.0",
|
||||
"@jest/test-sequencer": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"babel-jest": "^27.1.0",
|
||||
"@jest/test-sequencer": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"babel-jest": "^27.0.6",
|
||||
"chalk": "^4.0.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
"glob": "^7.1.1",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"is-ci": "^3.0.0",
|
||||
"jest-circus": "^27.1.0",
|
||||
"jest-environment-jsdom": "^27.1.0",
|
||||
"jest-environment-node": "^27.1.0",
|
||||
"jest-circus": "^27.0.6",
|
||||
"jest-environment-jsdom": "^27.0.6",
|
||||
"jest-environment-node": "^27.0.6",
|
||||
"jest-get-type": "^27.0.6",
|
||||
"jest-jasmine2": "^27.1.0",
|
||||
"jest-jasmine2": "^27.0.6",
|
||||
"jest-regex-util": "^27.0.6",
|
||||
"jest-resolve": "^27.1.0",
|
||||
"jest-runner": "^27.1.0",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-validate": "^27.1.0",
|
||||
"jest-resolve": "^27.0.6",
|
||||
"jest-runner": "^27.0.6",
|
||||
"jest-util": "^27.0.6",
|
||||
"jest-validate": "^27.0.6",
|
||||
"micromatch": "^4.0.4",
|
||||
"pretty-format": "^27.1.0"
|
||||
"pretty-format": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"jest-diff": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.1.0.tgz",
|
||||
"integrity": "sha512-rjfopEYl58g/SZTsQFmspBODvMSytL16I+cirnScWTLkQVXYVZfxm78DFfdIIXc05RCYuGjxJqrdyG4PIFzcJg==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.0.6.tgz",
|
||||
"integrity": "sha512-Z1mqgkTCSYaFgwTlP/NUiRzdqgxmmhzHY1Tq17zL94morOHfHu3K4bgSgl+CR4GLhpV8VxkuOYuIWnQ9LnFqmg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^4.0.0",
|
||||
"diff-sequences": "^27.0.6",
|
||||
"jest-get-type": "^27.0.6",
|
||||
"pretty-format": "^27.1.0"
|
||||
"pretty-format": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"jest-docblock": {
|
||||
@@ -14124,45 +14212,45 @@
|
||||
}
|
||||
},
|
||||
"jest-each": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.1.0.tgz",
|
||||
"integrity": "sha512-K/cNvQlmDqQMRHF8CaQ0XPzCfjP5HMJc2bIJglrIqI9fjwpNqITle63IWE+wq4p+3v+iBgh7Wq0IdGpLx5xjDg==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.0.6.tgz",
|
||||
"integrity": "sha512-m6yKcV3bkSWrUIjxkE9OC0mhBZZdhovIW5ergBYirqnkLXkyEn3oUUF/QZgyecA1cF1QFyTE8bRRl8Tfg1pfLA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"chalk": "^4.0.0",
|
||||
"jest-get-type": "^27.0.6",
|
||||
"jest-util": "^27.1.0",
|
||||
"pretty-format": "^27.1.0"
|
||||
"jest-util": "^27.0.6",
|
||||
"pretty-format": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"jest-environment-jsdom": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.1.0.tgz",
|
||||
"integrity": "sha512-JbwOcOxh/HOtsj56ljeXQCUJr3ivnaIlM45F5NBezFLVYdT91N5UofB1ux2B1CATsQiudcHdgTaeuqGXJqjJYQ==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.0.6.tgz",
|
||||
"integrity": "sha512-FvetXg7lnXL9+78H+xUAsra3IeZRTiegA3An01cWeXBspKXUhAwMM9ycIJ4yBaR0L7HkoMPaZsozCLHh4T8fuw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/environment": "^27.1.0",
|
||||
"@jest/fake-timers": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/environment": "^27.0.6",
|
||||
"@jest/fake-timers": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/node": "*",
|
||||
"jest-mock": "^27.1.0",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-mock": "^27.0.6",
|
||||
"jest-util": "^27.0.6",
|
||||
"jsdom": "^16.6.0"
|
||||
}
|
||||
},
|
||||
"jest-environment-node": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.1.0.tgz",
|
||||
"integrity": "sha512-JIyJ8H3wVyM4YCXp7njbjs0dIT87yhGlrXCXhDKNIg1OjurXr6X38yocnnbXvvNyqVTqSI4M9l+YfPKueqL1lw==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.0.6.tgz",
|
||||
"integrity": "sha512-+Vi6yLrPg/qC81jfXx3IBlVnDTI6kmRr08iVa2hFCWmJt4zha0XW7ucQltCAPhSR0FEKEoJ3i+W4E6T0s9is0w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/environment": "^27.1.0",
|
||||
"@jest/fake-timers": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/environment": "^27.0.6",
|
||||
"@jest/fake-timers": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/node": "*",
|
||||
"jest-mock": "^27.1.0",
|
||||
"jest-util": "^27.1.0"
|
||||
"jest-mock": "^27.0.6",
|
||||
"jest-util": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"jest-get-type": {
|
||||
@@ -14172,12 +14260,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"jest-haste-map": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.1.0.tgz",
|
||||
"integrity": "sha512-7mz6LopSe+eA6cTFMf10OfLLqRoIPvmMyz5/OnSXnHO7hB0aDP1iIeLWCXzAcYU5eIJVpHr12Bk9yyq2fTW9vg==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.0.6.tgz",
|
||||
"integrity": "sha512-4ldjPXX9h8doB2JlRzg9oAZ2p6/GpQUNAeiYXqcpmrKbP0Qev0wdZlxSMOmz8mPOEnt4h6qIzXFLDi8RScX/1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/graceful-fs": "^4.1.2",
|
||||
"@types/node": "*",
|
||||
"anymatch": "^3.0.3",
|
||||
@@ -14186,84 +14274,84 @@
|
||||
"graceful-fs": "^4.2.4",
|
||||
"jest-regex-util": "^27.0.6",
|
||||
"jest-serializer": "^27.0.6",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-worker": "^27.1.0",
|
||||
"jest-util": "^27.0.6",
|
||||
"jest-worker": "^27.0.6",
|
||||
"micromatch": "^4.0.4",
|
||||
"walker": "^1.0.7"
|
||||
}
|
||||
},
|
||||
"jest-jasmine2": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.1.0.tgz",
|
||||
"integrity": "sha512-Z/NIt0wBDg3przOW2FCWtYjMn3Ip68t0SL60agD/e67jlhTyV3PIF8IzT9ecwqFbeuUSO2OT8WeJgHcalDGFzQ==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.0.6.tgz",
|
||||
"integrity": "sha512-cjpH2sBy+t6dvCeKBsHpW41mjHzXgsavaFMp+VWRf0eR4EW8xASk1acqmljFtK2DgyIECMv2yCdY41r2l1+4iA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/traverse": "^7.1.0",
|
||||
"@jest/environment": "^27.1.0",
|
||||
"@jest/environment": "^27.0.6",
|
||||
"@jest/source-map": "^27.0.6",
|
||||
"@jest/test-result": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/test-result": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"co": "^4.6.0",
|
||||
"expect": "^27.1.0",
|
||||
"expect": "^27.0.6",
|
||||
"is-generator-fn": "^2.0.0",
|
||||
"jest-each": "^27.1.0",
|
||||
"jest-matcher-utils": "^27.1.0",
|
||||
"jest-message-util": "^27.1.0",
|
||||
"jest-runtime": "^27.1.0",
|
||||
"jest-snapshot": "^27.1.0",
|
||||
"jest-util": "^27.1.0",
|
||||
"pretty-format": "^27.1.0",
|
||||
"jest-each": "^27.0.6",
|
||||
"jest-matcher-utils": "^27.0.6",
|
||||
"jest-message-util": "^27.0.6",
|
||||
"jest-runtime": "^27.0.6",
|
||||
"jest-snapshot": "^27.0.6",
|
||||
"jest-util": "^27.0.6",
|
||||
"pretty-format": "^27.0.6",
|
||||
"throat": "^6.0.1"
|
||||
}
|
||||
},
|
||||
"jest-leak-detector": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.1.0.tgz",
|
||||
"integrity": "sha512-oHvSkz1E80VyeTKBvZNnw576qU+cVqRXUD3/wKXh1zpaki47Qty2xeHg2HKie9Hqcd2l4XwircgNOWb/NiGqdA==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.0.6.tgz",
|
||||
"integrity": "sha512-2/d6n2wlH5zEcdctX4zdbgX8oM61tb67PQt4Xh8JFAIy6LRKUnX528HulkaG6nD5qDl5vRV1NXejCe1XRCH5gQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"jest-get-type": "^27.0.6",
|
||||
"pretty-format": "^27.1.0"
|
||||
"pretty-format": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"jest-matcher-utils": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.1.0.tgz",
|
||||
"integrity": "sha512-VmAudus2P6Yt/JVBRdTPFhUzlIN8DYJd+et5Rd9QDsO/Z82Z4iwGjo43U8Z+PTiz8CBvKvlb6Fh3oKy39hykkQ==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.0.6.tgz",
|
||||
"integrity": "sha512-OFgF2VCQx9vdPSYTHWJ9MzFCehs20TsyFi6bIHbk5V1u52zJOnvF0Y/65z3GLZHKRuTgVPY4Z6LVePNahaQ+tA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^4.0.0",
|
||||
"jest-diff": "^27.1.0",
|
||||
"jest-diff": "^27.0.6",
|
||||
"jest-get-type": "^27.0.6",
|
||||
"pretty-format": "^27.1.0"
|
||||
"pretty-format": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"jest-message-util": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.1.0.tgz",
|
||||
"integrity": "sha512-Eck8NFnJ5Sg36R9XguD65cf2D5+McC+NF5GIdEninoabcuoOfWrID5qJhufq5FB0DRKoiyxB61hS7MKoMD0trQ==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.0.6.tgz",
|
||||
"integrity": "sha512-rBxIs2XK7rGy+zGxgi+UJKP6WqQ+KrBbD1YMj517HYN3v2BG66t3Xan3FWqYHKZwjdB700KiAJ+iES9a0M+ixw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.12.13",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/stack-utils": "^2.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"micromatch": "^4.0.4",
|
||||
"pretty-format": "^27.1.0",
|
||||
"pretty-format": "^27.0.6",
|
||||
"slash": "^3.0.0",
|
||||
"stack-utils": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"jest-mock": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.1.0.tgz",
|
||||
"integrity": "sha512-iT3/Yhu7DwAg/0HvvLCqLvrTKTRMyJlrrfJYWzuLSf9RCAxBoIXN3HoymZxMnYsC3eD8ewGbUa9jUknwBenx2w==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.0.6.tgz",
|
||||
"integrity": "sha512-lzBETUoK8cSxts2NYXSBWT+EJNzmUVtVVwS1sU9GwE1DLCfGsngg+ZVSIe0yd0ZSm+y791esiuo+WSwpXJQ5Bw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
@@ -14274,94 +14362,92 @@
|
||||
"dev": true
|
||||
},
|
||||
"jest-resolve": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.1.0.tgz",
|
||||
"integrity": "sha512-TXvzrLyPg0vLOwcWX38ZGYeEztSEmW+cQQKqc4HKDUwun31wsBXwotRlUz4/AYU/Fq4GhbMd/ileIWZEtcdmIA==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.0.6.tgz",
|
||||
"integrity": "sha512-yKmIgw2LgTh7uAJtzv8UFHGF7Dm7XfvOe/LQ3Txv101fLM8cx2h1QVwtSJ51Q/SCxpIiKfVn6G2jYYMDNHZteA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"chalk": "^4.0.0",
|
||||
"escalade": "^3.1.1",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"jest-haste-map": "^27.1.0",
|
||||
"jest-pnp-resolver": "^1.2.2",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-validate": "^27.1.0",
|
||||
"jest-util": "^27.0.6",
|
||||
"jest-validate": "^27.0.6",
|
||||
"resolve": "^1.20.0",
|
||||
"slash": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"jest-resolve-dependencies": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.1.0.tgz",
|
||||
"integrity": "sha512-Kq5XuDAELuBnrERrjFYEzu/A+i2W7l9HnPWqZEeKGEQ7m1R+6ndMbdXCVCx29Se1qwLZLgvoXwinB3SPIaitMQ==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.0.6.tgz",
|
||||
"integrity": "sha512-mg9x9DS3BPAREWKCAoyg3QucCr0n6S8HEEsqRCKSPjPcu9HzRILzhdzY3imsLoZWeosEbJZz6TKasveczzpJZA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"jest-regex-util": "^27.0.6",
|
||||
"jest-snapshot": "^27.1.0"
|
||||
"jest-snapshot": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"jest-runner": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.1.0.tgz",
|
||||
"integrity": "sha512-ZWPKr9M5w5gDplz1KsJ6iRmQaDT/yyAFLf18fKbb/+BLWsR1sCNC2wMT0H7pP3gDcBz0qZ6aJraSYUNAGSJGaw==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.0.6.tgz",
|
||||
"integrity": "sha512-W3Bz5qAgaSChuivLn+nKOgjqNxM7O/9JOJoKDCqThPIg2sH/d4A/lzyiaFgnb9V1/w29Le11NpzTJSzga1vyYQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/console": "^27.1.0",
|
||||
"@jest/environment": "^27.1.0",
|
||||
"@jest/test-result": "^27.1.0",
|
||||
"@jest/transform": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/console": "^27.0.6",
|
||||
"@jest/environment": "^27.0.6",
|
||||
"@jest/test-result": "^27.0.6",
|
||||
"@jest/transform": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"emittery": "^0.8.1",
|
||||
"exit": "^0.1.2",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"jest-docblock": "^27.0.6",
|
||||
"jest-environment-jsdom": "^27.1.0",
|
||||
"jest-environment-node": "^27.1.0",
|
||||
"jest-haste-map": "^27.1.0",
|
||||
"jest-leak-detector": "^27.1.0",
|
||||
"jest-message-util": "^27.1.0",
|
||||
"jest-resolve": "^27.1.0",
|
||||
"jest-runtime": "^27.1.0",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-worker": "^27.1.0",
|
||||
"jest-environment-jsdom": "^27.0.6",
|
||||
"jest-environment-node": "^27.0.6",
|
||||
"jest-haste-map": "^27.0.6",
|
||||
"jest-leak-detector": "^27.0.6",
|
||||
"jest-message-util": "^27.0.6",
|
||||
"jest-resolve": "^27.0.6",
|
||||
"jest-runtime": "^27.0.6",
|
||||
"jest-util": "^27.0.6",
|
||||
"jest-worker": "^27.0.6",
|
||||
"source-map-support": "^0.5.6",
|
||||
"throat": "^6.0.1"
|
||||
}
|
||||
},
|
||||
"jest-runtime": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.1.0.tgz",
|
||||
"integrity": "sha512-okiR2cpGjY0RkWmUGGado6ETpFOi9oG3yV0CioYdoktkVxy5Hv0WRLWnJFuArSYS8cHMCNcceUUMGiIfgxCO9A==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.0.6.tgz",
|
||||
"integrity": "sha512-BhvHLRVfKibYyqqEFkybsznKwhrsu7AWx2F3y9G9L95VSIN3/ZZ9vBpm/XCS2bS+BWz3sSeNGLzI3TVQ0uL85Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/console": "^27.1.0",
|
||||
"@jest/environment": "^27.1.0",
|
||||
"@jest/fake-timers": "^27.1.0",
|
||||
"@jest/globals": "^27.1.0",
|
||||
"@jest/console": "^27.0.6",
|
||||
"@jest/environment": "^27.0.6",
|
||||
"@jest/fake-timers": "^27.0.6",
|
||||
"@jest/globals": "^27.0.6",
|
||||
"@jest/source-map": "^27.0.6",
|
||||
"@jest/test-result": "^27.1.0",
|
||||
"@jest/transform": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/test-result": "^27.0.6",
|
||||
"@jest/transform": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/yargs": "^16.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"cjs-module-lexer": "^1.0.0",
|
||||
"collect-v8-coverage": "^1.0.0",
|
||||
"execa": "^5.0.0",
|
||||
"exit": "^0.1.2",
|
||||
"glob": "^7.1.3",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"jest-haste-map": "^27.1.0",
|
||||
"jest-message-util": "^27.1.0",
|
||||
"jest-mock": "^27.1.0",
|
||||
"jest-haste-map": "^27.0.6",
|
||||
"jest-message-util": "^27.0.6",
|
||||
"jest-mock": "^27.0.6",
|
||||
"jest-regex-util": "^27.0.6",
|
||||
"jest-resolve": "^27.1.0",
|
||||
"jest-snapshot": "^27.1.0",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-validate": "^27.1.0",
|
||||
"jest-resolve": "^27.0.6",
|
||||
"jest-snapshot": "^27.0.6",
|
||||
"jest-util": "^27.0.6",
|
||||
"jest-validate": "^27.0.6",
|
||||
"slash": "^3.0.0",
|
||||
"strip-bom": "^4.0.0",
|
||||
"yargs": "^16.0.3"
|
||||
@@ -14378,9 +14464,9 @@
|
||||
}
|
||||
},
|
||||
"jest-snapshot": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.1.0.tgz",
|
||||
"integrity": "sha512-eaeUBoEjuuRwmiRI51oTldUsKOohB1F6fPqWKKILuDi/CStxzp2IWekVUXbuHHoz5ik33ioJhshiHpgPFbYgcA==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.0.6.tgz",
|
||||
"integrity": "sha512-NTHaz8He+ATUagUgE7C/UtFcRoHqR2Gc+KDfhQIyx+VFgwbeEMjeP+ILpUTLosZn/ZtbNdCF5LkVnN/l+V751A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/core": "^7.7.2",
|
||||
@@ -14389,33 +14475,33 @@
|
||||
"@babel/plugin-syntax-typescript": "^7.7.2",
|
||||
"@babel/traverse": "^7.7.2",
|
||||
"@babel/types": "^7.0.0",
|
||||
"@jest/transform": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/transform": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/babel__traverse": "^7.0.4",
|
||||
"@types/prettier": "^2.1.5",
|
||||
"babel-preset-current-node-syntax": "^1.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"expect": "^27.1.0",
|
||||
"expect": "^27.0.6",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"jest-diff": "^27.1.0",
|
||||
"jest-diff": "^27.0.6",
|
||||
"jest-get-type": "^27.0.6",
|
||||
"jest-haste-map": "^27.1.0",
|
||||
"jest-matcher-utils": "^27.1.0",
|
||||
"jest-message-util": "^27.1.0",
|
||||
"jest-resolve": "^27.1.0",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-haste-map": "^27.0.6",
|
||||
"jest-matcher-utils": "^27.0.6",
|
||||
"jest-message-util": "^27.0.6",
|
||||
"jest-resolve": "^27.0.6",
|
||||
"jest-util": "^27.0.6",
|
||||
"natural-compare": "^1.4.0",
|
||||
"pretty-format": "^27.1.0",
|
||||
"pretty-format": "^27.0.6",
|
||||
"semver": "^7.3.2"
|
||||
}
|
||||
},
|
||||
"jest-util": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.1.0.tgz",
|
||||
"integrity": "sha512-edSLD2OneYDKC6gZM1yc+wY/877s/fuJNoM1k3sOEpzFyeptSmke3SLnk1dDHk9CgTA+58mnfx3ew3J11Kes/w==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.0.6.tgz",
|
||||
"integrity": "sha512-1JjlaIh+C65H/F7D11GNkGDDZtDfMEM8EBXsvd+l/cxtgQ6QhxuloOaiayt89DxUvDarbVhqI98HhgrM1yliFQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"graceful-fs": "^4.2.4",
|
||||
@@ -14424,38 +14510,38 @@
|
||||
}
|
||||
},
|
||||
"jest-validate": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.1.0.tgz",
|
||||
"integrity": "sha512-QiJ+4XuSuMsfPi9zvdO//IrSRSlG6ybJhOpuqYSsuuaABaNT84h0IoD6vvQhThBOKT+DIKvl5sTM0l6is9+SRA==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.0.6.tgz",
|
||||
"integrity": "sha512-yhZZOaMH3Zg6DC83n60pLmdU1DQE46DW+KLozPiPbSbPhlXXaiUTDlhHQhHFpaqIFRrInko1FHXjTRpjWRuWfA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"camelcase": "^6.2.0",
|
||||
"chalk": "^4.0.0",
|
||||
"jest-get-type": "^27.0.6",
|
||||
"leven": "^3.1.0",
|
||||
"pretty-format": "^27.1.0"
|
||||
"pretty-format": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"jest-watcher": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.1.0.tgz",
|
||||
"integrity": "sha512-ivaWTrA46aHWdgPDgPypSHiNQjyKnLBpUIHeBaGg11U+pDzZpkffGlcB1l1a014phmG0mHgkOHtOgiqJQM6yKQ==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.0.6.tgz",
|
||||
"integrity": "sha512-/jIoKBhAP00/iMGnTwUBLgvxkn7vsOweDrOTSPzc7X9uOyUtJIDthQBTI1EXz90bdkrxorUZVhJwiB69gcHtYQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/test-result": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/test-result": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/node": "*",
|
||||
"ansi-escapes": "^4.2.1",
|
||||
"chalk": "^4.0.0",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-util": "^27.0.6",
|
||||
"string-length": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"jest-worker": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.1.0.tgz",
|
||||
"integrity": "sha512-mO4PHb2QWLn9yRXGp7rkvXLAYuxwhq1ZYUo0LoDhg8wqvv4QizP1ZWEJOeolgbEgAWZLIEU0wsku8J+lGWfBhg==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.0.6.tgz",
|
||||
"integrity": "sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
@@ -14474,6 +14560,50 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"jsdom": {
|
||||
"version": "16.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz",
|
||||
"integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"abab": "^2.0.5",
|
||||
"acorn": "^8.2.4",
|
||||
"acorn-globals": "^6.0.0",
|
||||
"cssom": "^0.4.4",
|
||||
"cssstyle": "^2.3.0",
|
||||
"data-urls": "^2.0.0",
|
||||
"decimal.js": "^10.2.1",
|
||||
"domexception": "^2.0.1",
|
||||
"escodegen": "^2.0.0",
|
||||
"form-data": "^3.0.0",
|
||||
"html-encoding-sniffer": "^2.0.1",
|
||||
"http-proxy-agent": "^4.0.1",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"is-potential-custom-element-name": "^1.0.1",
|
||||
"nwsapi": "^2.2.0",
|
||||
"parse5": "6.0.1",
|
||||
"saxes": "^5.0.1",
|
||||
"symbol-tree": "^3.2.4",
|
||||
"tough-cookie": "^4.0.0",
|
||||
"w3c-hr-time": "^1.0.2",
|
||||
"w3c-xmlserializer": "^2.0.0",
|
||||
"webidl-conversions": "^6.1.0",
|
||||
"whatwg-encoding": "^1.0.5",
|
||||
"whatwg-mimetype": "^2.3.0",
|
||||
"whatwg-url": "^8.5.0",
|
||||
"ws": "^7.4.6",
|
||||
"xml-name-validator": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"micromatch": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
|
||||
@@ -14500,12 +14630,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"pretty-format": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.1.0.tgz",
|
||||
"integrity": "sha512-4aGaud3w3rxAO6OXmK3fwBFQ0bctIOG3/if+jYEFGNGIs0EvuidQm3bZ9mlP2/t9epLNC/12czabfy7TZNSwVA==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.0.6.tgz",
|
||||
"integrity": "sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"ansi-regex": "^5.0.0",
|
||||
"ansi-styles": "^5.0.0",
|
||||
"react-is": "^17.0.1"
|
||||
@@ -14564,12 +14694,30 @@
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"strip-bom": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
|
||||
"integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"throat": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
|
||||
@@ -14612,6 +14760,56 @@
|
||||
"requires": {
|
||||
"isexe": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.5.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz",
|
||||
"integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==",
|
||||
"dev": true
|
||||
},
|
||||
"y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^7.0.2",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.0",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^20.2.2"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "20.2.9",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
|
||||
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -14723,81 +14921,81 @@
|
||||
}
|
||||
},
|
||||
"jest-circus": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.1.0.tgz",
|
||||
"integrity": "sha512-6FWtHs3nZyZlMBhRf1wvAC5CirnflbGJAY1xssSAnERLiiXQRH+wY2ptBVtXjX4gz4AA2EwRV57b038LmifRbA==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.0.6.tgz",
|
||||
"integrity": "sha512-OJlsz6BBeX9qR+7O9lXefWoc2m9ZqcZ5Ohlzz0pTEAG4xMiZUJoacY8f4YDHxgk0oKYxj277AfOk9w6hZYvi1Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/environment": "^27.1.0",
|
||||
"@jest/test-result": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/environment": "^27.0.6",
|
||||
"@jest/test-result": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"co": "^4.6.0",
|
||||
"dedent": "^0.7.0",
|
||||
"expect": "^27.1.0",
|
||||
"expect": "^27.0.6",
|
||||
"is-generator-fn": "^2.0.0",
|
||||
"jest-each": "^27.1.0",
|
||||
"jest-matcher-utils": "^27.1.0",
|
||||
"jest-message-util": "^27.1.0",
|
||||
"jest-runtime": "^27.1.0",
|
||||
"jest-snapshot": "^27.1.0",
|
||||
"jest-util": "^27.1.0",
|
||||
"pretty-format": "^27.1.0",
|
||||
"jest-each": "^27.0.6",
|
||||
"jest-matcher-utils": "^27.0.6",
|
||||
"jest-message-util": "^27.0.6",
|
||||
"jest-runtime": "^27.0.6",
|
||||
"jest-snapshot": "^27.0.6",
|
||||
"jest-util": "^27.0.6",
|
||||
"pretty-format": "^27.0.6",
|
||||
"slash": "^3.0.0",
|
||||
"stack-utils": "^2.0.3",
|
||||
"throat": "^6.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jest/console": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/console/-/console-27.1.0.tgz",
|
||||
"integrity": "sha512-+Vl+xmLwAXLNlqT61gmHEixeRbS4L8MUzAjtpBCOPWH+izNI/dR16IeXjkXJdRtIVWVSf9DO1gdp67B1XorZhQ==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/console/-/console-27.0.6.tgz",
|
||||
"integrity": "sha512-fMlIBocSHPZ3JxgWiDNW/KPj6s+YRd0hicb33IrmelCcjXo/pXPwvuiKFmZz+XuqI/1u7nbUK10zSsWL/1aegg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"jest-message-util": "^27.1.0",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-message-util": "^27.0.6",
|
||||
"jest-util": "^27.0.6",
|
||||
"slash": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"@jest/environment": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.1.0.tgz",
|
||||
"integrity": "sha512-wRp50aAMY2w1U2jP1G32d6FUVBNYqmk8WaGkiIEisU48qyDV0WPtw3IBLnl7orBeggveommAkuijY+RzVnNDOQ==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.0.6.tgz",
|
||||
"integrity": "sha512-4XywtdhwZwCpPJ/qfAkqExRsERW+UaoSRStSHCCiQTUpoYdLukj+YJbQSFrZjhlUDRZeNiU9SFH0u7iNimdiIg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/fake-timers": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/fake-timers": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/node": "*",
|
||||
"jest-mock": "^27.1.0"
|
||||
"jest-mock": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"@jest/fake-timers": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.1.0.tgz",
|
||||
"integrity": "sha512-22Zyn8il8DzpS+30jJNVbTlm7vAtnfy1aYvNeOEHloMlGy1PCYLHa4PWlSws0hvNsMM5bON6GISjkLoQUV3oMA==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.0.6.tgz",
|
||||
"integrity": "sha512-sqd+xTWtZ94l3yWDKnRTdvTeZ+A/V7SSKrxsrOKSqdyddb9CeNRF8fbhAU0D7ZJBpTTW2nbp6MftmKJDZfW2LQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@sinonjs/fake-timers": "^7.0.2",
|
||||
"@types/node": "*",
|
||||
"jest-message-util": "^27.1.0",
|
||||
"jest-mock": "^27.1.0",
|
||||
"jest-util": "^27.1.0"
|
||||
"jest-message-util": "^27.0.6",
|
||||
"jest-mock": "^27.0.6",
|
||||
"jest-util": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"@jest/globals": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.1.0.tgz",
|
||||
"integrity": "sha512-73vLV4aNHAlAgjk0/QcSIzzCZSqVIPbmFROJJv9D3QUR7BI4f517gVdJpSrCHxuRH3VZFhe0yGG/tmttlMll9g==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.0.6.tgz",
|
||||
"integrity": "sha512-DdTGCP606rh9bjkdQ7VvChV18iS7q0IMJVP1piwTWyWskol4iqcVwthZmoJEf7obE1nc34OpIyoVGPeqLC+ryw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/environment": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"expect": "^27.1.0"
|
||||
"@jest/environment": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"expect": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"@jest/source-map": {
|
||||
@@ -14812,33 +15010,33 @@
|
||||
}
|
||||
},
|
||||
"@jest/test-result": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.1.0.tgz",
|
||||
"integrity": "sha512-Aoz00gpDL528ODLghat3QSy6UBTD5EmmpjrhZZMK/v1Q2/rRRqTGnFxHuEkrD4z/Py96ZdOHxIWkkCKRpmnE1A==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.0.6.tgz",
|
||||
"integrity": "sha512-ja/pBOMTufjX4JLEauLxE3LQBPaI2YjGFtXexRAjt1I/MbfNlMx0sytSX3tn5hSLzQsR3Qy2rd0hc1BWojtj9w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/console": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/console": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||
"collect-v8-coverage": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"@jest/transform": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.1.0.tgz",
|
||||
"integrity": "sha512-ZRGCA2ZEVJ00ubrhkTG87kyLbN6n55g1Ilq0X9nJb5bX3MhMp3O6M7KG+LvYu+nZRqG5cXsQnJEdZbdpTAV8pQ==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.0.6.tgz",
|
||||
"integrity": "sha512-rj5Dw+mtIcntAUnMlW/Vju5mr73u8yg+irnHwzgtgoeI6cCPOvUwQ0D1uQtc/APmWgvRweEb1g05pkUpxH3iCA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/core": "^7.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"babel-plugin-istanbul": "^6.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"convert-source-map": "^1.4.0",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"jest-haste-map": "^27.1.0",
|
||||
"jest-haste-map": "^27.0.6",
|
||||
"jest-regex-util": "^27.0.6",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-util": "^27.0.6",
|
||||
"micromatch": "^4.0.4",
|
||||
"pirates": "^4.0.1",
|
||||
"slash": "^3.0.0",
|
||||
@@ -14847,9 +15045,9 @@
|
||||
}
|
||||
},
|
||||
"@jest/types": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-27.1.0.tgz",
|
||||
"integrity": "sha512-pRP5cLIzN7I7Vp6mHKRSaZD7YpBTK7hawx5si8trMKqk4+WOdK8NEKOTO2G8PKWD1HbKMVckVB6/XHh/olhf2g==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-27.0.6.tgz",
|
||||
"integrity": "sha512-aSquT1qa9Pik26JK5/3rvnYb4bGtm1VFNesHKmNTwmPIgOrixvhL2ghIvFRNEpzy3gU+rUgjIF/KodbkFAl++g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||
@@ -14927,6 +15125,17 @@
|
||||
"integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==",
|
||||
"dev": true
|
||||
},
|
||||
"cliui": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -14942,51 +15151,23 @@
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
"which": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"diff-sequences": {
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.0.6.tgz",
|
||||
"integrity": "sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==",
|
||||
"dev": true
|
||||
},
|
||||
"execa": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
|
||||
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cross-spawn": "^7.0.3",
|
||||
"get-stream": "^6.0.0",
|
||||
"human-signals": "^2.1.0",
|
||||
"is-stream": "^2.0.0",
|
||||
"merge-stream": "^2.0.0",
|
||||
"npm-run-path": "^4.0.1",
|
||||
"onetime": "^5.1.2",
|
||||
"signal-exit": "^3.0.3",
|
||||
"strip-final-newline": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/expect/-/expect-27.1.0.tgz",
|
||||
"integrity": "sha512-9kJngV5hOJgkFil4F/uXm3hVBubUK2nERVfvqNNwxxuW8ZOUwSTTSysgfzckYtv/LBzj/LJXbiAF7okHCXgdug==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/expect/-/expect-27.0.6.tgz",
|
||||
"integrity": "sha512-psNLt8j2kwg42jGBDSfAlU49CEZxejN1f1PlANWDZqIhBOVU/c2Pm888FcjWJzFewhIsNWfZJeLjUjtKGiPuSw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"ansi-styles": "^5.0.0",
|
||||
"jest-get-type": "^27.0.6",
|
||||
"jest-matcher-utils": "^27.1.0",
|
||||
"jest-message-util": "^27.1.0",
|
||||
"jest-matcher-utils": "^27.0.6",
|
||||
"jest-message-util": "^27.0.6",
|
||||
"jest-regex-util": "^27.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -15014,12 +15195,6 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"get-stream": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
||||
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -15041,35 +15216,29 @@
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true
|
||||
},
|
||||
"is-stream": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
|
||||
"dev": true
|
||||
},
|
||||
"jest-diff": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.1.0.tgz",
|
||||
"integrity": "sha512-rjfopEYl58g/SZTsQFmspBODvMSytL16I+cirnScWTLkQVXYVZfxm78DFfdIIXc05RCYuGjxJqrdyG4PIFzcJg==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.0.6.tgz",
|
||||
"integrity": "sha512-Z1mqgkTCSYaFgwTlP/NUiRzdqgxmmhzHY1Tq17zL94morOHfHu3K4bgSgl+CR4GLhpV8VxkuOYuIWnQ9LnFqmg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^4.0.0",
|
||||
"diff-sequences": "^27.0.6",
|
||||
"jest-get-type": "^27.0.6",
|
||||
"pretty-format": "^27.1.0"
|
||||
"pretty-format": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"jest-each": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.1.0.tgz",
|
||||
"integrity": "sha512-K/cNvQlmDqQMRHF8CaQ0XPzCfjP5HMJc2bIJglrIqI9fjwpNqITle63IWE+wq4p+3v+iBgh7Wq0IdGpLx5xjDg==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.0.6.tgz",
|
||||
"integrity": "sha512-m6yKcV3bkSWrUIjxkE9OC0mhBZZdhovIW5ergBYirqnkLXkyEn3oUUF/QZgyecA1cF1QFyTE8bRRl8Tfg1pfLA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"chalk": "^4.0.0",
|
||||
"jest-get-type": "^27.0.6",
|
||||
"jest-util": "^27.1.0",
|
||||
"pretty-format": "^27.1.0"
|
||||
"jest-util": "^27.0.6",
|
||||
"pretty-format": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"jest-get-type": {
|
||||
@@ -15079,12 +15248,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"jest-haste-map": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.1.0.tgz",
|
||||
"integrity": "sha512-7mz6LopSe+eA6cTFMf10OfLLqRoIPvmMyz5/OnSXnHO7hB0aDP1iIeLWCXzAcYU5eIJVpHr12Bk9yyq2fTW9vg==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.0.6.tgz",
|
||||
"integrity": "sha512-4ldjPXX9h8doB2JlRzg9oAZ2p6/GpQUNAeiYXqcpmrKbP0Qev0wdZlxSMOmz8mPOEnt4h6qIzXFLDi8RScX/1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/graceful-fs": "^4.1.2",
|
||||
"@types/node": "*",
|
||||
"anymatch": "^3.0.3",
|
||||
@@ -15093,48 +15262,48 @@
|
||||
"graceful-fs": "^4.2.4",
|
||||
"jest-regex-util": "^27.0.6",
|
||||
"jest-serializer": "^27.0.6",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-worker": "^27.1.0",
|
||||
"jest-util": "^27.0.6",
|
||||
"jest-worker": "^27.0.6",
|
||||
"micromatch": "^4.0.4",
|
||||
"walker": "^1.0.7"
|
||||
}
|
||||
},
|
||||
"jest-matcher-utils": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.1.0.tgz",
|
||||
"integrity": "sha512-VmAudus2P6Yt/JVBRdTPFhUzlIN8DYJd+et5Rd9QDsO/Z82Z4iwGjo43U8Z+PTiz8CBvKvlb6Fh3oKy39hykkQ==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.0.6.tgz",
|
||||
"integrity": "sha512-OFgF2VCQx9vdPSYTHWJ9MzFCehs20TsyFi6bIHbk5V1u52zJOnvF0Y/65z3GLZHKRuTgVPY4Z6LVePNahaQ+tA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^4.0.0",
|
||||
"jest-diff": "^27.1.0",
|
||||
"jest-diff": "^27.0.6",
|
||||
"jest-get-type": "^27.0.6",
|
||||
"pretty-format": "^27.1.0"
|
||||
"pretty-format": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"jest-message-util": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.1.0.tgz",
|
||||
"integrity": "sha512-Eck8NFnJ5Sg36R9XguD65cf2D5+McC+NF5GIdEninoabcuoOfWrID5qJhufq5FB0DRKoiyxB61hS7MKoMD0trQ==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.0.6.tgz",
|
||||
"integrity": "sha512-rBxIs2XK7rGy+zGxgi+UJKP6WqQ+KrBbD1YMj517HYN3v2BG66t3Xan3FWqYHKZwjdB700KiAJ+iES9a0M+ixw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.12.13",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/stack-utils": "^2.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"micromatch": "^4.0.4",
|
||||
"pretty-format": "^27.1.0",
|
||||
"pretty-format": "^27.0.6",
|
||||
"slash": "^3.0.0",
|
||||
"stack-utils": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"jest-mock": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.1.0.tgz",
|
||||
"integrity": "sha512-iT3/Yhu7DwAg/0HvvLCqLvrTKTRMyJlrrfJYWzuLSf9RCAxBoIXN3HoymZxMnYsC3eD8ewGbUa9jUknwBenx2w==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.0.6.tgz",
|
||||
"integrity": "sha512-lzBETUoK8cSxts2NYXSBWT+EJNzmUVtVVwS1sU9GwE1DLCfGsngg+ZVSIe0yd0ZSm+y791esiuo+WSwpXJQ5Bw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
@@ -15145,53 +15314,51 @@
|
||||
"dev": true
|
||||
},
|
||||
"jest-resolve": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.1.0.tgz",
|
||||
"integrity": "sha512-TXvzrLyPg0vLOwcWX38ZGYeEztSEmW+cQQKqc4HKDUwun31wsBXwotRlUz4/AYU/Fq4GhbMd/ileIWZEtcdmIA==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.0.6.tgz",
|
||||
"integrity": "sha512-yKmIgw2LgTh7uAJtzv8UFHGF7Dm7XfvOe/LQ3Txv101fLM8cx2h1QVwtSJ51Q/SCxpIiKfVn6G2jYYMDNHZteA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"chalk": "^4.0.0",
|
||||
"escalade": "^3.1.1",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"jest-haste-map": "^27.1.0",
|
||||
"jest-pnp-resolver": "^1.2.2",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-validate": "^27.1.0",
|
||||
"jest-util": "^27.0.6",
|
||||
"jest-validate": "^27.0.6",
|
||||
"resolve": "^1.20.0",
|
||||
"slash": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"jest-runtime": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.1.0.tgz",
|
||||
"integrity": "sha512-okiR2cpGjY0RkWmUGGado6ETpFOi9oG3yV0CioYdoktkVxy5Hv0WRLWnJFuArSYS8cHMCNcceUUMGiIfgxCO9A==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.0.6.tgz",
|
||||
"integrity": "sha512-BhvHLRVfKibYyqqEFkybsznKwhrsu7AWx2F3y9G9L95VSIN3/ZZ9vBpm/XCS2bS+BWz3sSeNGLzI3TVQ0uL85Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/console": "^27.1.0",
|
||||
"@jest/environment": "^27.1.0",
|
||||
"@jest/fake-timers": "^27.1.0",
|
||||
"@jest/globals": "^27.1.0",
|
||||
"@jest/console": "^27.0.6",
|
||||
"@jest/environment": "^27.0.6",
|
||||
"@jest/fake-timers": "^27.0.6",
|
||||
"@jest/globals": "^27.0.6",
|
||||
"@jest/source-map": "^27.0.6",
|
||||
"@jest/test-result": "^27.1.0",
|
||||
"@jest/transform": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/test-result": "^27.0.6",
|
||||
"@jest/transform": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/yargs": "^16.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"cjs-module-lexer": "^1.0.0",
|
||||
"collect-v8-coverage": "^1.0.0",
|
||||
"execa": "^5.0.0",
|
||||
"exit": "^0.1.2",
|
||||
"glob": "^7.1.3",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"jest-haste-map": "^27.1.0",
|
||||
"jest-message-util": "^27.1.0",
|
||||
"jest-mock": "^27.1.0",
|
||||
"jest-haste-map": "^27.0.6",
|
||||
"jest-message-util": "^27.0.6",
|
||||
"jest-mock": "^27.0.6",
|
||||
"jest-regex-util": "^27.0.6",
|
||||
"jest-resolve": "^27.1.0",
|
||||
"jest-snapshot": "^27.1.0",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-validate": "^27.1.0",
|
||||
"jest-resolve": "^27.0.6",
|
||||
"jest-snapshot": "^27.0.6",
|
||||
"jest-util": "^27.0.6",
|
||||
"jest-validate": "^27.0.6",
|
||||
"slash": "^3.0.0",
|
||||
"strip-bom": "^4.0.0",
|
||||
"yargs": "^16.0.3"
|
||||
@@ -15208,9 +15375,9 @@
|
||||
}
|
||||
},
|
||||
"jest-snapshot": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.1.0.tgz",
|
||||
"integrity": "sha512-eaeUBoEjuuRwmiRI51oTldUsKOohB1F6fPqWKKILuDi/CStxzp2IWekVUXbuHHoz5ik33ioJhshiHpgPFbYgcA==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.0.6.tgz",
|
||||
"integrity": "sha512-NTHaz8He+ATUagUgE7C/UtFcRoHqR2Gc+KDfhQIyx+VFgwbeEMjeP+ILpUTLosZn/ZtbNdCF5LkVnN/l+V751A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/core": "^7.7.2",
|
||||
@@ -15219,33 +15386,33 @@
|
||||
"@babel/plugin-syntax-typescript": "^7.7.2",
|
||||
"@babel/traverse": "^7.7.2",
|
||||
"@babel/types": "^7.0.0",
|
||||
"@jest/transform": "^27.1.0",
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/transform": "^27.0.6",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/babel__traverse": "^7.0.4",
|
||||
"@types/prettier": "^2.1.5",
|
||||
"babel-preset-current-node-syntax": "^1.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"expect": "^27.1.0",
|
||||
"expect": "^27.0.6",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"jest-diff": "^27.1.0",
|
||||
"jest-diff": "^27.0.6",
|
||||
"jest-get-type": "^27.0.6",
|
||||
"jest-haste-map": "^27.1.0",
|
||||
"jest-matcher-utils": "^27.1.0",
|
||||
"jest-message-util": "^27.1.0",
|
||||
"jest-resolve": "^27.1.0",
|
||||
"jest-util": "^27.1.0",
|
||||
"jest-haste-map": "^27.0.6",
|
||||
"jest-matcher-utils": "^27.0.6",
|
||||
"jest-message-util": "^27.0.6",
|
||||
"jest-resolve": "^27.0.6",
|
||||
"jest-util": "^27.0.6",
|
||||
"natural-compare": "^1.4.0",
|
||||
"pretty-format": "^27.1.0",
|
||||
"pretty-format": "^27.0.6",
|
||||
"semver": "^7.3.2"
|
||||
}
|
||||
},
|
||||
"jest-util": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.1.0.tgz",
|
||||
"integrity": "sha512-edSLD2OneYDKC6gZM1yc+wY/877s/fuJNoM1k3sOEpzFyeptSmke3SLnk1dDHk9CgTA+58mnfx3ew3J11Kes/w==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.0.6.tgz",
|
||||
"integrity": "sha512-1JjlaIh+C65H/F7D11GNkGDDZtDfMEM8EBXsvd+l/cxtgQ6QhxuloOaiayt89DxUvDarbVhqI98HhgrM1yliFQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"graceful-fs": "^4.2.4",
|
||||
@@ -15254,23 +15421,23 @@
|
||||
}
|
||||
},
|
||||
"jest-validate": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.1.0.tgz",
|
||||
"integrity": "sha512-QiJ+4XuSuMsfPi9zvdO//IrSRSlG6ybJhOpuqYSsuuaABaNT84h0IoD6vvQhThBOKT+DIKvl5sTM0l6is9+SRA==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.0.6.tgz",
|
||||
"integrity": "sha512-yhZZOaMH3Zg6DC83n60pLmdU1DQE46DW+KLozPiPbSbPhlXXaiUTDlhHQhHFpaqIFRrInko1FHXjTRpjWRuWfA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"camelcase": "^6.2.0",
|
||||
"chalk": "^4.0.0",
|
||||
"jest-get-type": "^27.0.6",
|
||||
"leven": "^3.1.0",
|
||||
"pretty-format": "^27.1.0"
|
||||
"pretty-format": "^27.0.6"
|
||||
}
|
||||
},
|
||||
"jest-worker": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.1.0.tgz",
|
||||
"integrity": "sha512-mO4PHb2QWLn9yRXGp7rkvXLAYuxwhq1ZYUo0LoDhg8wqvv4QizP1ZWEJOeolgbEgAWZLIEU0wsku8J+lGWfBhg==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.0.6.tgz",
|
||||
"integrity": "sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
@@ -15289,6 +15456,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"micromatch": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
|
||||
@@ -15299,28 +15475,13 @@
|
||||
"picomatch": "^2.2.3"
|
||||
}
|
||||
},
|
||||
"npm-run-path": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
||||
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-key": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"dev": true
|
||||
},
|
||||
"pretty-format": {
|
||||
"version": "27.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.1.0.tgz",
|
||||
"integrity": "sha512-4aGaud3w3rxAO6OXmK3fwBFQ0bctIOG3/if+jYEFGNGIs0EvuidQm3bZ9mlP2/t9epLNC/12czabfy7TZNSwVA==",
|
||||
"version": "27.0.6",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.0.6.tgz",
|
||||
"integrity": "sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.1.0",
|
||||
"@jest/types": "^27.0.6",
|
||||
"ansi-regex": "^5.0.0",
|
||||
"ansi-styles": "^5.0.0",
|
||||
"react-is": "^17.0.1"
|
||||
@@ -15349,33 +15510,36 @@
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"shebang-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"dev": true
|
||||
},
|
||||
"slash": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"strip-bom": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
|
||||
"integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"throat": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
|
||||
@@ -15391,14 +15555,49 @@
|
||||
"is-number": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isexe": "^2.0.0"
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^7.0.2",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.0",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^20.2.2"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "20.2.9",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
|
||||
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -16221,12 +16420,6 @@
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -16237,17 +16430,6 @@
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"cliui": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^6.2.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -16274,52 +16456,6 @@
|
||||
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
|
||||
"integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
|
||||
"dev": true
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "15.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^6.0.0",
|
||||
"decamelize": "^1.2.0",
|
||||
"find-up": "^4.1.0",
|
||||
"get-caller-file": "^2.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^2.0.0",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^4.2.0",
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^4.0.0",
|
||||
"yargs-parser": "^18.1.2"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "18.1.3",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -16786,6 +16922,12 @@
|
||||
"json-buffer": "3.0.0"
|
||||
}
|
||||
},
|
||||
"killable": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
|
||||
"integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==",
|
||||
"dev": true
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
|
||||
@@ -17233,6 +17375,11 @@
|
||||
"requires": {
|
||||
"strict-uri-encode": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"strict-uri-encode": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -17627,20 +17774,27 @@
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"minipass": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
|
||||
"integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
|
||||
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"minizlib": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
|
||||
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
|
||||
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
|
||||
"requires": {
|
||||
"minipass": "^3.0.0",
|
||||
"yallist": "^4.0.0"
|
||||
"minipass": "^2.9.0"
|
||||
}
|
||||
},
|
||||
"mixin-deep": {
|
||||
@@ -17752,9 +17906,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"needle": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz",
|
||||
"integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==",
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/needle/-/needle-2.8.0.tgz",
|
||||
"integrity": "sha512-ZTq6WYkN/3782H1393me3utVYdq2XyqNUFBsprEE3VMAT0+hP/cItpnITpqsY6ep2yeFE4Tqtqwc74VqUlUYtw==",
|
||||
"requires": {
|
||||
"debug": "^3.2.6",
|
||||
"iconv-lite": "^0.4.4",
|
||||
@@ -17787,6 +17941,15 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"dev": true
|
||||
},
|
||||
"new-relic-source-map-webpack-plugin": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/new-relic-source-map-webpack-plugin/-/new-relic-source-map-webpack-plugin-1.2.0.tgz",
|
||||
"integrity": "sha512-bsF07nt2mlauMhxdKW3aIirazreq5muRLHSxubQ18XVqYR2ta0N6CghwHNDJsivY1j/Qrt22FNFuz0GZrFPbTw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@newrelic/publish-sourcemap": "^4.4.1"
|
||||
}
|
||||
},
|
||||
"next-tick": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
||||
@@ -17885,9 +18048,9 @@
|
||||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.75",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz",
|
||||
"integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==",
|
||||
"version": "1.1.74",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.74.tgz",
|
||||
"integrity": "sha512-caJBVempXZPepZoZAPCWRTNxYQ+xtG/KAi4ozTA5A+nJ7IU+kLQCbqaUjb5Rwy14M9upBWiQ4NutcmW04LJSRw==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-package-data": {
|
||||
@@ -18532,13 +18695,12 @@
|
||||
}
|
||||
},
|
||||
"pino": {
|
||||
"version": "6.13.2",
|
||||
"resolved": "https://registry.npmjs.org/pino/-/pino-6.13.2.tgz",
|
||||
"integrity": "sha512-vmD/cabJ4xKqo9GVuAoAEeQhra8XJ7YydPV/JyIP+0zDtFTu5JSKdtt8eksGVWKtTSrNGcRrzJ4/IzvUWep3FA==",
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/pino/-/pino-6.13.0.tgz",
|
||||
"integrity": "sha512-mRXSTfa34tbfrWqCIp1sUpZLqBhcoaGapoyxfEwaWwJGMpLijlRdDKIQUyvq4M3DUfFH5vEglwSw8POZYwbThA==",
|
||||
"requires": {
|
||||
"fast-redact": "^3.0.0",
|
||||
"fast-safe-stringify": "^2.0.8",
|
||||
"fastify-warning": "^0.2.0",
|
||||
"flatstr": "^1.0.12",
|
||||
"pino-std-serializers": "^3.1.0",
|
||||
"quick-format-unescaped": "^4.0.3",
|
||||
@@ -18604,6 +18766,19 @@
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -19006,13 +19181,13 @@
|
||||
}
|
||||
},
|
||||
"postcss-minify-gradients": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.2.tgz",
|
||||
"integrity": "sha512-7Do9JP+wqSD6Prittitt2zDLrfzP9pqKs2EcLX7HJYxsxCOwrrcLt4x/ctQTsiOw+/8HYotAoqNkrzItL19SdQ==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.1.tgz",
|
||||
"integrity": "sha512-odOwBFAIn2wIv+XYRpoN2hUV3pPQlgbJ10XeXPq8UY2N+9ZG42xu45lTn/g9zZ+d70NKSQD6EOi6UiCMu3FN7g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"colord": "^2.6",
|
||||
"cssnano-utils": "^2.0.1",
|
||||
"is-color-stop": "^1.1.0",
|
||||
"postcss-value-parser": "^4.1.0"
|
||||
}
|
||||
},
|
||||
@@ -19217,6 +19392,40 @@
|
||||
"svgo": "^2.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
@@ -19296,13 +19505,13 @@
|
||||
}
|
||||
},
|
||||
"svgo": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-2.4.0.tgz",
|
||||
"integrity": "sha512-W25S1UUm9Lm9VnE0TvCzL7aso/NCzDEaXLaElCUO/KaVitw0+IBicSVfM1L1c0YHK5TOFh73yQ2naCpVHEQ/OQ==",
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-2.3.1.tgz",
|
||||
"integrity": "sha512-riDDIQgXpEnn0BEl9Gvhh1LNLIyiusSpt64IR8upJu7MwxnzetmF/Y57pXQD2NMX2lVyMRzXt5f2M5rO4wG7Dw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@trysound/sax": "0.1.1",
|
||||
"colorette": "^1.2.2",
|
||||
"chalk": "^4.1.0",
|
||||
"commander": "^7.1.0",
|
||||
"css-select": "^4.1.3",
|
||||
"css-tree": "^1.1.2",
|
||||
@@ -19399,6 +19608,14 @@
|
||||
"requires": {
|
||||
"colors": "^1.1.2",
|
||||
"minimist": "^1.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"colors": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
|
||||
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"process-nextick-args": {
|
||||
@@ -19516,6 +19733,8 @@
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
|
||||
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"decode-uri-component": "^0.2.0",
|
||||
"object-assign": "^4.1.0",
|
||||
@@ -19574,9 +19793,9 @@
|
||||
}
|
||||
},
|
||||
"react-bootstrap": {
|
||||
"version": "1.6.3",
|
||||
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.6.3.tgz",
|
||||
"integrity": "sha512-zsd4l0g68pusOmJ/R5LhTfofT+9RniCwcZsMMNFGJo97d1vT1H2nGlbhLWp/j/pfeXXj9zzR8ugUtKkadcoWnA==",
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.6.1.tgz",
|
||||
"integrity": "sha512-ojEPQ6OtyIMdLg0Smofk+85PKN6MLKQX3bU0Vwmok/4yNa8DQ2vCGhO2IgHJvT+ERQZ4X+gAQcdn6msAHSwLBg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.14.0",
|
||||
"@restart/context": "^2.1.4",
|
||||
@@ -19591,7 +19810,7 @@
|
||||
"invariant": "^2.2.4",
|
||||
"prop-types": "^15.7.2",
|
||||
"prop-types-extra": "^1.1.0",
|
||||
"react-overlays": "^5.1.1",
|
||||
"react-overlays": "^5.0.1",
|
||||
"react-transition-group": "^4.4.1",
|
||||
"uncontrollable": "^7.2.1",
|
||||
"warning": "^4.0.3"
|
||||
@@ -19754,6 +19973,15 @@
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
@@ -19875,9 +20103,9 @@
|
||||
"integrity": "sha1-acLVdB5t9eCPIw82u8KUTuEiJVU="
|
||||
},
|
||||
"react-redux": {
|
||||
"version": "7.2.5",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.5.tgz",
|
||||
"integrity": "sha512-Dt29bNyBsbQaysp6s/dN0gUodcq+dVKKER8Qv82UrpeygwYeX1raTtil7O/fftw/rFqzaf6gJhDZRkkZnn6bjg==",
|
||||
"version": "7.2.4",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.4.tgz",
|
||||
"integrity": "sha512-hOQ5eOSkEJEXdpIKbnRyl04LhaWabkDPV+Ix97wqQX3T3d2NQ8DUblNXXtNMavc7DpswyQM6xfaN4HQDKNY2JA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.1",
|
||||
"@types/react-redux": "^7.1.16",
|
||||
@@ -19926,11 +20154,11 @@
|
||||
}
|
||||
},
|
||||
"react-router": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.1.tgz",
|
||||
"integrity": "sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ==",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
|
||||
"integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"history": "^4.9.0",
|
||||
"hoist-non-react-statics": "^3.1.0",
|
||||
"loose-envify": "^1.3.1",
|
||||
@@ -19943,15 +20171,15 @@
|
||||
}
|
||||
},
|
||||
"react-router-dom": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.1.tgz",
|
||||
"integrity": "sha512-xhFFkBGVcIVPbWM2KEYzED+nuHQPmulVa7sqIs3ESxzYd1pYg8N8rxPnQ4T2o1zu/2QeDUWcaqST131SO1LR3w==",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz",
|
||||
"integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"history": "^4.9.0",
|
||||
"loose-envify": "^1.3.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-router": "5.2.1",
|
||||
"react-router": "5.2.0",
|
||||
"tiny-invariant": "^1.0.2",
|
||||
"tiny-warning": "^1.0.0"
|
||||
}
|
||||
@@ -20537,6 +20765,18 @@
|
||||
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
|
||||
"integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
|
||||
},
|
||||
"rgb-regex": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz",
|
||||
"integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=",
|
||||
"dev": true
|
||||
},
|
||||
"rgba-regex": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz",
|
||||
"integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
@@ -21166,9 +21406,9 @@
|
||||
}
|
||||
},
|
||||
"sonic-boom": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.2.3.tgz",
|
||||
"integrity": "sha512-dm32bzlBchhXoJZe0yLY/kdYsHtXhZphidIcCzJib1aEjfciZyvHJ3NjA1zh6jJCO/OBLfdjc5iw6jLS/Go2fg==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.1.0.tgz",
|
||||
"integrity": "sha512-x2j9LXx27EDlyZEC32gBM+scNVMdPutU7FIKV2BOTKCnPrp7bY5BsplCMQ4shYYR3IhDSIrEXoqb6GlS+z7KyQ==",
|
||||
"requires": {
|
||||
"atomic-sleep": "^1.0.0"
|
||||
}
|
||||
@@ -21525,7 +21765,9 @@
|
||||
"strict-uri-encode": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
|
||||
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"string-length": {
|
||||
"version": "4.0.2",
|
||||
@@ -21646,12 +21888,20 @@
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
"ansi-regex": "^4.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"strip-bom": {
|
||||
@@ -21816,6 +22066,7 @@
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
@@ -21823,7 +22074,8 @@
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -21924,12 +22176,6 @@
|
||||
"string-width": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||
"dev": true
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
||||
@@ -21952,15 +22198,6 @@
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
"strip-ansi": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^4.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -22144,22 +22381,23 @@
|
||||
"dev": true
|
||||
},
|
||||
"tar": {
|
||||
"version": "6.1.11",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
|
||||
"integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.2.tgz",
|
||||
"integrity": "sha512-BfkE9CciGGgDsATqkikUHrQrraBCO+ke/1f6SFAEMnxyyfN9lxC+nW1NFWMpqH865DhHIy9vQi682gk1X7friw==",
|
||||
"requires": {
|
||||
"chownr": "^2.0.0",
|
||||
"fs-minipass": "^2.0.0",
|
||||
"minipass": "^3.0.0",
|
||||
"minizlib": "^2.1.1",
|
||||
"mkdirp": "^1.0.3",
|
||||
"yallist": "^4.0.0"
|
||||
"chownr": "^1.0.1",
|
||||
"fs-minipass": "^1.2.5",
|
||||
"minipass": "^2.2.4",
|
||||
"minizlib": "^1.1.0",
|
||||
"mkdirp": "^0.5.0",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -23131,9 +23369,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"webpack": {
|
||||
"version": "5.50.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.50.0.tgz",
|
||||
"integrity": "sha512-hqxI7t/KVygs0WRv/kTgUW8Kl3YC81uyWQSo/7WUs5LsuRw0htH/fCwbVBGCuiX/t4s7qzjXFcf41O8Reiypag==",
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.44.0.tgz",
|
||||
"integrity": "sha512-I1S1w4QLoKmH19pX6YhYN0NiSXaWY8Ou00oA+aMcr9IUGeF5azns+IKBkfoAAG9Bu5zOIzZt/mN35OffBya8AQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/eslint-scope": "^3.7.0",
|
||||
@@ -23142,7 +23380,6 @@
|
||||
"@webassemblyjs/wasm-edit": "1.11.1",
|
||||
"@webassemblyjs/wasm-parser": "1.11.1",
|
||||
"acorn": "^8.4.1",
|
||||
"acorn-import-assertions": "^1.7.6",
|
||||
"browserslist": "^4.14.5",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.8.0",
|
||||
@@ -23155,11 +23392,11 @@
|
||||
"loader-runner": "^4.2.0",
|
||||
"mime-types": "^2.1.27",
|
||||
"neo-async": "^2.6.2",
|
||||
"schema-utils": "^3.1.0",
|
||||
"schema-utils": "^3.0.0",
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.1.3",
|
||||
"watchpack": "^2.2.0",
|
||||
"webpack-sources": "^3.2.0"
|
||||
"webpack-sources": "^2.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
@@ -23169,10 +23406,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"webpack-sources": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.0.tgz",
|
||||
"integrity": "sha512-fahN08Et7P9trej8xz/Z7eRu8ltyiygEo/hnRi9KqBUs80KeDcnf96ZJo++ewWd84fEf3xSX9bp4ZS9hbw0OBw==",
|
||||
"dev": true
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz",
|
||||
"integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"source-list-map": "^2.0.1",
|
||||
"source-map": "^0.6.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -23221,15 +23462,15 @@
|
||||
}
|
||||
},
|
||||
"webpack-cli": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.8.0.tgz",
|
||||
"integrity": "sha512-+iBSWsX16uVna5aAYN6/wjhJy1q/GKk4KjKvfg90/6hykCTSgozbfz5iRgDTSJt/LgSbYxdBX3KBHeobIs+ZEw==",
|
||||
"version": "4.7.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.7.2.tgz",
|
||||
"integrity": "sha512-mEoLmnmOIZQNiRl0ebnjzQ74Hk0iKS5SiEEnpq3dRezoyR3yPaeQZCMCe+db4524pj1Pd5ghZXjT41KLzIhSLw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@discoveryjs/json-ext": "^0.5.0",
|
||||
"@webpack-cli/configtest": "^1.0.4",
|
||||
"@webpack-cli/info": "^1.3.0",
|
||||
"@webpack-cli/serve": "^1.5.2",
|
||||
"@webpack-cli/serve": "^1.5.1",
|
||||
"colorette": "^1.2.1",
|
||||
"commander": "^7.0.0",
|
||||
"execa": "^5.0.0",
|
||||
@@ -23339,75 +23580,54 @@
|
||||
}
|
||||
},
|
||||
"webpack-dev-middleware": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.0.0.tgz",
|
||||
"integrity": "sha512-9zng2Z60pm6A98YoRcA0wSxw1EYn7B7y5owX/Tckyt9KGyULTkLtiavjaXlWqOMkM0YtqGgL3PvMOFgyFLq8vw==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-4.3.0.tgz",
|
||||
"integrity": "sha512-PjwyVY95/bhBh6VUqt6z4THplYcsvQ8YNNBTBM873xLVmw8FLeALn0qurHbs9EmcfhzQis/eoqypSnZeuUz26w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"colorette": "^1.2.2",
|
||||
"mem": "^8.1.1",
|
||||
"memfs": "^3.2.2",
|
||||
"mime-types": "^2.1.31",
|
||||
"mime-types": "^2.1.30",
|
||||
"range-parser": "^1.2.1",
|
||||
"schema-utils": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"mime-db": {
|
||||
"version": "1.49.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
|
||||
"integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==",
|
||||
"dev": true
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.32",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz",
|
||||
"integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mime-db": "1.49.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"webpack-dev-server": {
|
||||
"version": "4.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.0.0-rc.1.tgz",
|
||||
"integrity": "sha512-gZlGe0CMA0YZ5bIXFbtSegd33tYsUujYv+rgJu9Y75xHvXBSXFJiBvakMV7yTkBE+k8dgz4VsBzl7J5I5xatyg==",
|
||||
"version": "4.0.0-beta.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.0.0-beta.3.tgz",
|
||||
"integrity": "sha512-Ud7ieH15No/KiSdRuzk+2k+S4gSCR/N7m4hJhesDbKQEZy3P+NPXTXfsimNOZvbVX2TRuIEFB+VdLZFn8DwGwg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-html": "^0.0.7",
|
||||
"bonjour": "^3.5.0",
|
||||
"chokidar": "^3.5.1",
|
||||
"colorette": "^1.2.2",
|
||||
"compression": "^1.7.4",
|
||||
"connect-history-api-fallback": "^1.6.0",
|
||||
"del": "^6.0.0",
|
||||
"express": "^4.17.1",
|
||||
"find-cache-dir": "^3.3.1",
|
||||
"graceful-fs": "^4.2.6",
|
||||
"html-entities": "^2.3.2",
|
||||
"http-proxy-middleware": "^2.0.0",
|
||||
"http-proxy-middleware": "^1.3.1",
|
||||
"internal-ip": "^6.2.0",
|
||||
"ipaddr.js": "^2.0.1",
|
||||
"open": "^8.0.9",
|
||||
"ipaddr.js": "^2.0.0",
|
||||
"is-absolute-url": "^3.0.3",
|
||||
"killable": "^1.0.1",
|
||||
"open": "^7.4.2",
|
||||
"p-retry": "^4.5.0",
|
||||
"portfinder": "^1.0.28",
|
||||
"schema-utils": "^3.1.0",
|
||||
"schema-utils": "^3.0.0",
|
||||
"selfsigned": "^1.10.11",
|
||||
"serve-index": "^1.9.1",
|
||||
"sockjs": "^0.3.21",
|
||||
"spdy": "^4.0.2",
|
||||
"strip-ansi": "^7.0.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"url": "^0.11.0",
|
||||
"webpack-dev-middleware": "^5.0.0",
|
||||
"ws": "^8.1.0"
|
||||
"webpack-dev-middleware": "^4.1.0",
|
||||
"ws": "^7.4.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.0.tgz",
|
||||
"integrity": "sha512-tAaOSrWCHF+1Ear1Z4wnJCXA9GGox4K6Ic85a5qalES2aeEwQGr7UC93mwef49536PkCYjzkp0zIxfFvexJ6zQ==",
|
||||
"dev": true
|
||||
},
|
||||
"anymatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
||||
@@ -23480,6 +23700,17 @@
|
||||
"to-regex-range": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"find-cache-dir": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz",
|
||||
"integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commondir": "^1.0.1",
|
||||
"make-dir": "^3.0.2",
|
||||
"pkg-dir": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
@@ -23511,9 +23742,9 @@
|
||||
}
|
||||
},
|
||||
"http-proxy-middleware": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz",
|
||||
"integrity": "sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg==",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz",
|
||||
"integrity": "sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/http-proxy": "^1.17.5",
|
||||
@@ -23562,6 +23793,15 @@
|
||||
"integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==",
|
||||
"dev": true
|
||||
},
|
||||
"make-dir": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"semver": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"micromatch": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
|
||||
@@ -23572,17 +23812,6 @@
|
||||
"picomatch": "^2.2.3"
|
||||
}
|
||||
},
|
||||
"open": {
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/open/-/open-8.2.1.tgz",
|
||||
"integrity": "sha512-rXILpcQlkF/QuFez2BJDf3GsqpjGKbkUUToAIGo9A0Q6ZkoSGogZJulrUdwRkrAsoQvoZsrjCYt8+zblOk7JQQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"define-lazy-prop": "^2.0.0",
|
||||
"is-docker": "^2.1.1",
|
||||
"is-wsl": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"p-map": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
|
||||
@@ -23592,6 +23821,15 @@
|
||||
"aggregate-error": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"pkg-dir": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"find-up": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
@@ -23610,6 +23848,12 @@
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true
|
||||
},
|
||||
"slash": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
@@ -23617,12 +23861,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-UhDTSnGF1dc0DRbUqr1aXwNoY3RgVkSWG8BrpnuFIxhP57IqbS7IRta2Gfiavds4yCxc5+fEAVVOgBZWnYkvzg==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^6.0.0"
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"to-regex-range": {
|
||||
@@ -23633,12 +23877,6 @@
|
||||
"requires": {
|
||||
"is-number": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.0.tgz",
|
||||
"integrity": "sha512-uYhVJ/m9oXwEI04iIVmgLmugh2qrZihkywG9y5FfZV2ATeLIzHf93qs+tUNqlttbQK957/VX3mtwAS+UfIwA4g==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -23826,9 +24064,9 @@
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
@@ -23859,6 +24097,15 @@
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -23923,15 +24170,16 @@
|
||||
"optional": true
|
||||
},
|
||||
"y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"yaml": {
|
||||
"version": "1.10.2",
|
||||
@@ -23940,25 +24188,41 @@
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
|
||||
"version": "15.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^7.0.2",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"cliui": "^6.0.0",
|
||||
"decamelize": "^1.2.0",
|
||||
"find-up": "^4.1.0",
|
||||
"get-caller-file": "^2.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^2.0.0",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^4.2.0",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^20.2.2"
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^4.0.0",
|
||||
"yargs-parser": "^18.1.2"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "20.2.9",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
|
||||
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
|
||||
"dev": true
|
||||
"version": "18.1.3",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"yauzl": {
|
||||
"version": "2.10.0",
|
||||
|
||||
28
package.json
28
package.json
@@ -33,19 +33,19 @@
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@edx/brand-openedx@1.1.0",
|
||||
"@edx/frontend-component-footer": "10.1.6",
|
||||
"@edx/frontend-enterprise-utils": "1.0.0",
|
||||
"@edx/frontend-lib-special-exams": "1.13.2",
|
||||
"@edx/frontend-platform": "1.12.7",
|
||||
"@edx/paragon": "16.13.3",
|
||||
"@edx/frontend-enterprise-utils": "0.1.7",
|
||||
"@edx/frontend-lib-special-exams": "1.12.0",
|
||||
"@edx/frontend-platform": "1.12.3",
|
||||
"@edx/paragon": "16.7.0",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.36",
|
||||
"@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.15",
|
||||
"@pact-foundation/pact": "9.16.1",
|
||||
"@pact-foundation/pact": "9.16.0",
|
||||
"@reduxjs/toolkit": "1.6.1",
|
||||
"classnames": "2.3.1",
|
||||
"core-js": "3.16.4",
|
||||
"core-js": "3.16.1",
|
||||
"js-cookie": "2.2.1",
|
||||
"lodash.camelcase": "4.3.0",
|
||||
"prop-types": "15.7.2",
|
||||
@@ -53,9 +53,9 @@
|
||||
"react-break": "1.3.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-helmet": "6.1.0",
|
||||
"react-redux": "7.2.5",
|
||||
"react-router": "5.2.1",
|
||||
"react-router-dom": "5.2.1",
|
||||
"react-redux": "7.2.4",
|
||||
"react-router": "5.2.0",
|
||||
"react-router-dom": "5.2.0",
|
||||
"react-share": "4.4.0",
|
||||
"redux": "4.1.1",
|
||||
"regenerator-runtime": "0.13.9",
|
||||
@@ -64,16 +64,16 @@
|
||||
"util": "0.12.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@edx/frontend-build": "8.0.4",
|
||||
"@edx/frontend-build": "8.0.0",
|
||||
"@testing-library/dom": "7.16.3",
|
||||
"@testing-library/jest-dom": "5.14.1",
|
||||
"@testing-library/react": "10.3.0",
|
||||
"@testing-library/user-event": "13.2.1",
|
||||
"axios-mock-adapter": "1.20.0",
|
||||
"@testing-library/user-event": "12.8.3",
|
||||
"axios-mock-adapter": "1.19.0",
|
||||
"codecov": "3.8.3",
|
||||
"es-check": "6.0.0",
|
||||
"es-check": "5.2.4",
|
||||
"glob": "7.1.7",
|
||||
"husky": "7.0.2",
|
||||
"husky": "7.0.1",
|
||||
"jest": "27.0.6",
|
||||
"jest-chain": "1.1.5",
|
||||
"reactifex": "1.1.1",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -9,6 +9,7 @@ import { Info } from '@edx/paragon/icons';
|
||||
|
||||
import messages from './messages';
|
||||
import AccessExpirationAlertMMP2P from './AccessExpirationAlertMMP2P';
|
||||
import AccessExpirationAlertMasquerade from './AccessExpirationAlertMasquerade';
|
||||
|
||||
function AccessExpirationAlert({ intl, payload }) {
|
||||
/** [MM-P2P] Experiment */
|
||||
@@ -35,10 +36,17 @@ function AccessExpirationAlert({ intl, payload }) {
|
||||
|
||||
const {
|
||||
expirationDate,
|
||||
masqueradingExpiredCourse,
|
||||
upgradeDeadline,
|
||||
upgradeUrl,
|
||||
} = accessExpiration;
|
||||
|
||||
if (masqueradingExpiredCourse) {
|
||||
return (
|
||||
<AccessExpirationAlertMasquerade payload={payload} />
|
||||
);
|
||||
}
|
||||
|
||||
/** [MM-P2P] Experiment */
|
||||
if (showMMP2P) {
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage, FormattedDate } from '@edx/frontend-platform/i18n';
|
||||
import { Alert } from '@edx/paragon';
|
||||
import { Info } from '@edx/paragon/icons';
|
||||
|
||||
function AccessExpirationAlertMasquerade({ payload }) {
|
||||
const {
|
||||
accessExpiration,
|
||||
userTimezone,
|
||||
} = payload;
|
||||
|
||||
if (!accessExpiration) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
expirationDate,
|
||||
masqueradingExpiredCourse,
|
||||
} = accessExpiration;
|
||||
|
||||
if (!masqueradingExpiredCourse) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const timezoneFormatArgs = userTimezone ? { timeZone: userTimezone } : {};
|
||||
|
||||
return (
|
||||
<Alert variant="info" icon={Info}>
|
||||
<FormattedMessage
|
||||
id="learning.accessExpiration.expired"
|
||||
defaultMessage="This learner does not have access to this course. Their access expired on {date}."
|
||||
values={{
|
||||
date: (
|
||||
<FormattedDate
|
||||
key="accessExpirationExpiredDate"
|
||||
day="numeric"
|
||||
month="short"
|
||||
year="numeric"
|
||||
value={expirationDate}
|
||||
{...timezoneFormatArgs}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
AccessExpirationAlertMasquerade.propTypes = {
|
||||
payload: PropTypes.shape({
|
||||
accessExpiration: PropTypes.shape({
|
||||
expirationDate: PropTypes.string.isRequired,
|
||||
masqueradingExpiredCourse: PropTypes.bool.isRequired,
|
||||
}).isRequired,
|
||||
userTimezone: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default AccessExpirationAlertMasquerade;
|
||||
@@ -1,38 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage, FormattedDate } from '@edx/frontend-platform/i18n';
|
||||
import { PageBanner } from '@edx/paragon';
|
||||
|
||||
function AccessExpirationMasqueradeBanner({ payload }) {
|
||||
const {
|
||||
expirationDate,
|
||||
userTimezone,
|
||||
} = payload;
|
||||
|
||||
const timezoneFormatArgs = userTimezone ? { timeZone: userTimezone } : {};
|
||||
|
||||
return (
|
||||
<PageBanner variant="warning">
|
||||
<FormattedMessage
|
||||
id="instructorToolbar.pageBanner.courseHasExpired"
|
||||
defaultMessage="This learner no longer has access to this course. Their access expired on {date}."
|
||||
values={{
|
||||
date: <FormattedDate
|
||||
key="instructorToolbar.pageBanner.accessExpirationDate"
|
||||
value={expirationDate}
|
||||
{...timezoneFormatArgs}
|
||||
/>,
|
||||
}}
|
||||
/>
|
||||
</PageBanner>
|
||||
);
|
||||
}
|
||||
|
||||
AccessExpirationMasqueradeBanner.propTypes = {
|
||||
payload: PropTypes.shape({
|
||||
expirationDate: PropTypes.string.isRequired,
|
||||
userTimezone: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default AccessExpirationMasqueradeBanner;
|
||||
@@ -1,12 +1,11 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { useAlert } from '../../generic/user-messages';
|
||||
import { useModel } from '../../generic/model-store';
|
||||
|
||||
const AccessExpirationAlert = React.lazy(() => import('./AccessExpirationAlert'));
|
||||
const AccessExpirationMasqueradeBanner = React.lazy(() => import('./AccessExpirationMasqueradeBanner'));
|
||||
const AccessExpirationAlertMasquerade = React.lazy(() => import('./AccessExpirationAlertMasquerade'));
|
||||
|
||||
function useAccessExpirationAlert(accessExpiration, courseId, org, userTimezone, topic, analyticsPageName) {
|
||||
const isVisible = accessExpiration && !accessExpiration.masqueradingExpiredCourse; // If it exists, show it.
|
||||
const isVisible = !!accessExpiration; // If it exists, show it.
|
||||
const payload = {
|
||||
accessExpiration,
|
||||
courseId,
|
||||
@@ -24,28 +23,20 @@ function useAccessExpirationAlert(accessExpiration, courseId, org, userTimezone,
|
||||
return { clientAccessExpirationAlert: AccessExpirationAlert };
|
||||
}
|
||||
|
||||
export function useAccessExpirationMasqueradeBanner(courseId, tab) {
|
||||
const {
|
||||
userTimezone,
|
||||
} = useModel('courseHomeMeta', courseId);
|
||||
const {
|
||||
accessExpiration,
|
||||
} = useModel(tab, courseId);
|
||||
|
||||
const isVisible = accessExpiration && accessExpiration.masqueradingExpiredCourse;
|
||||
const expirationDate = accessExpiration && accessExpiration.expirationDate;
|
||||
export function useAccessExpirationAlertMasquerade(accessExpiration, userTimezone, topic) {
|
||||
const isVisible = !!accessExpiration; // If it exists, show it.
|
||||
const payload = {
|
||||
expirationDate,
|
||||
accessExpiration,
|
||||
userTimezone,
|
||||
};
|
||||
|
||||
useAlert(isVisible, {
|
||||
code: 'clientAccessExpirationMasqueradeBanner',
|
||||
code: 'clientAccessExpirationAlertMasquerade',
|
||||
payload: useMemo(() => payload, Object.values(payload).sort()),
|
||||
topic: 'instructor-toolbar-alerts',
|
||||
topic,
|
||||
});
|
||||
|
||||
return { clientAccessExpirationMasqueradeBanner: AccessExpirationMasqueradeBanner };
|
||||
return { clientAccessExpirationAlertMasquerade: AccessExpirationAlertMasquerade };
|
||||
}
|
||||
|
||||
export default useAccessExpirationAlert;
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default, useAccessExpirationMasqueradeBanner } from './hooks';
|
||||
export { default, useAccessExpirationAlertMasquerade } from './hooks';
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage, FormattedDate } from '@edx/frontend-platform/i18n';
|
||||
import { PageBanner } from '@edx/paragon';
|
||||
|
||||
import { useModel } from '../../generic/model-store';
|
||||
|
||||
function CourseStartMasqueradeBanner({ payload }) {
|
||||
const {
|
||||
courseId,
|
||||
} = payload;
|
||||
|
||||
const {
|
||||
start,
|
||||
userTimezone,
|
||||
} = useModel('courseHomeMeta', courseId);
|
||||
|
||||
const timezoneFormatArgs = userTimezone ? { timeZone: userTimezone } : {};
|
||||
|
||||
return (
|
||||
<PageBanner variant="warning">
|
||||
<FormattedMessage
|
||||
id="instructorToolbar.pageBanner.courseHasNotStarted"
|
||||
defaultMessage="This learner does not yet have access to this course. The course starts on {date}."
|
||||
values={{
|
||||
date: <FormattedDate
|
||||
key="instructorToolbar.pageBanner.courseStartDate"
|
||||
value={start}
|
||||
{...timezoneFormatArgs}
|
||||
/>,
|
||||
}}
|
||||
/>
|
||||
</PageBanner>
|
||||
);
|
||||
}
|
||||
|
||||
CourseStartMasqueradeBanner.propTypes = {
|
||||
payload: PropTypes.shape({
|
||||
courseId: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default CourseStartMasqueradeBanner;
|
||||
@@ -1,62 +0,0 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { useAlert } from '../../generic/user-messages';
|
||||
import { useModel } from '../../generic/model-store';
|
||||
|
||||
const CourseStartAlert = React.lazy(() => import('./CourseStartAlert'));
|
||||
const CourseStartMasqueradeBanner = React.lazy(() => import('./CourseStartMasqueradeBanner'));
|
||||
|
||||
function isStartDateInFuture(courseId) {
|
||||
const {
|
||||
start,
|
||||
} = useModel('courseHomeMeta', courseId);
|
||||
|
||||
const today = new Date();
|
||||
const startDate = new Date(start);
|
||||
return startDate > today;
|
||||
}
|
||||
|
||||
function useCourseStartAlert(courseId) {
|
||||
const {
|
||||
isEnrolled,
|
||||
} = useModel('courseHomeMeta', courseId);
|
||||
|
||||
const isVisible = isEnrolled && isStartDateInFuture(courseId);
|
||||
|
||||
const payload = {
|
||||
courseId,
|
||||
};
|
||||
|
||||
useAlert(isVisible, {
|
||||
code: 'clientCourseStartAlert',
|
||||
payload: useMemo(() => payload, Object.values(payload).sort()),
|
||||
topic: 'outline-course-alerts',
|
||||
});
|
||||
|
||||
return {
|
||||
clientCourseStartAlert: CourseStartAlert,
|
||||
};
|
||||
}
|
||||
|
||||
export function useCourseStartMasqueradeBanner(courseId, tab) {
|
||||
const {
|
||||
isMasquerading,
|
||||
} = useModel('courseHomeMeta', courseId);
|
||||
|
||||
const isVisible = isMasquerading && tab === 'progress' && isStartDateInFuture(courseId);
|
||||
|
||||
const payload = {
|
||||
courseId,
|
||||
};
|
||||
|
||||
useAlert(isVisible, {
|
||||
code: 'clientCourseStartMasqueradeBanner',
|
||||
payload: useMemo(() => payload, Object.values(payload).sort()),
|
||||
topic: 'instructor-toolbar-alerts',
|
||||
});
|
||||
|
||||
return {
|
||||
clientCourseStartMasqueradeBanner: CourseStartMasqueradeBanner,
|
||||
};
|
||||
}
|
||||
|
||||
export default useCourseStartAlert;
|
||||
@@ -1 +0,0 @@
|
||||
export { default, useCourseStartMasqueradeBanner } from './hooks';
|
||||
@@ -9,13 +9,10 @@ import {
|
||||
Icon,
|
||||
} from '@edx/paragon';
|
||||
import { Check, ArrowForward } from '@edx/paragon/icons';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { FormattedMessage, injectIntl } from '@edx/frontend-platform/i18n';
|
||||
import { sendActivationEmail } from '../../courseware/data';
|
||||
import messages from './messages';
|
||||
|
||||
function AccountActivationAlert({
|
||||
intl,
|
||||
}) {
|
||||
function AccountActivationAlert() {
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [showSpinner, setShowSpinner] = useState(false);
|
||||
const [showCheck, setShowCheck] = useState(false);
|
||||
@@ -32,12 +29,22 @@ function AccountActivationAlert({
|
||||
if (showAccountActivationAlert !== undefined) {
|
||||
Cookies.remove('show-account-activation-popup', { path: '/', domain: process.env.SESSION_COOKIE_DOMAIN });
|
||||
// extra check to make sure cookie was removed before updating the state. Updating the state without removal
|
||||
// of cookie would make it infinite rendering
|
||||
// of cookie would make it infinit rendering
|
||||
if (Cookies.get('show-account-activation-popup') === undefined) {
|
||||
setShowModal(true);
|
||||
}
|
||||
}
|
||||
|
||||
const title = (
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="account-activation.alert.title"
|
||||
defaultMessage="Activate your account so you can log back in"
|
||||
description="Title for account activation alert which is shown after the registration"
|
||||
/>
|
||||
</h3>
|
||||
);
|
||||
|
||||
const button = (
|
||||
<Button
|
||||
variant="primary"
|
||||
@@ -57,7 +64,7 @@ function AccountActivationAlert({
|
||||
);
|
||||
|
||||
const children = () => {
|
||||
let bodyContent;
|
||||
let bodyContent = null;
|
||||
const message = (
|
||||
<FormattedMessage
|
||||
id="account-activation.alert.message"
|
||||
@@ -116,7 +123,7 @@ function AccountActivationAlert({
|
||||
return (
|
||||
<AlertModal
|
||||
isOpen={showModal}
|
||||
title={intl.formatMessage(messages.accountActivationAlertTitle)}
|
||||
title={title}
|
||||
footerNode={button}
|
||||
onClose={() => ({})}
|
||||
>
|
||||
@@ -125,8 +132,4 @@ function AccountActivationAlert({
|
||||
);
|
||||
}
|
||||
|
||||
AccountActivationAlert.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(AccountActivationAlert);
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { defineMessages } from '@edx/frontend-platform/i18n';
|
||||
|
||||
const messages = defineMessages({
|
||||
accountActivationAlertTitle: {
|
||||
id: 'account-activation.alert.title',
|
||||
defaultMessage: 'Activate your account so you can log back in',
|
||||
description: 'Title for account activation alert which is shown after the registration',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
@@ -29,7 +29,7 @@ LinkedLogo.propTypes = {
|
||||
};
|
||||
|
||||
function Header({
|
||||
courseOrg, courseNumber, courseTitle, intl, showUserDropdown,
|
||||
courseOrg, courseNumber, courseTitle, intl,
|
||||
}) {
|
||||
const { authenticatedUser } = useContext(AppContext);
|
||||
|
||||
@@ -67,13 +67,13 @@ function Header({
|
||||
<span className="d-block small m-0">{courseOrg} {courseNumber}</span>
|
||||
<span className="d-block m-0 font-weight-bold course-title">{courseTitle}</span>
|
||||
</div>
|
||||
{showUserDropdown && authenticatedUser && (
|
||||
{authenticatedUser && (
|
||||
<AuthenticatedUserDropdown
|
||||
enterpriseLearnerPortalLink={enterpriseLearnerPortalLink}
|
||||
username={authenticatedUser.username}
|
||||
/>
|
||||
)}
|
||||
{showUserDropdown && !authenticatedUser && (
|
||||
{!authenticatedUser && (
|
||||
<AnonymousUserMenu />
|
||||
)}
|
||||
</div>
|
||||
@@ -86,14 +86,12 @@ Header.propTypes = {
|
||||
courseNumber: PropTypes.string,
|
||||
courseTitle: PropTypes.string,
|
||||
intl: intlShape.isRequired,
|
||||
showUserDropdown: PropTypes.bool,
|
||||
};
|
||||
|
||||
Header.defaultProps = {
|
||||
courseOrg: null,
|
||||
courseNumber: null,
|
||||
courseTitle: null,
|
||||
showUserDropdown: true,
|
||||
};
|
||||
|
||||
export default injectIntl(Header);
|
||||
|
||||
@@ -19,5 +19,4 @@ Factory.define('courseHomeMetadata')
|
||||
user_message: null,
|
||||
},
|
||||
start: '2013-02-05T05:00:00Z',
|
||||
user_timezone: 'UTC',
|
||||
});
|
||||
|
||||
@@ -219,4 +219,5 @@ Factory.define('datesTabData')
|
||||
],
|
||||
has_ended: false,
|
||||
learner_is_full_access: true,
|
||||
user_timezone: 'America/New_York',
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@ Factory.define('outlineTabData')
|
||||
})
|
||||
.attr('dates_widget', ['date_blocks'], (dateBlocks) => ({
|
||||
course_date_blocks: dateBlocks,
|
||||
user_timezone: 'UTC',
|
||||
}))
|
||||
.attr('resume_course', ['host', 'courseId'], (host, courseId) => ({
|
||||
has_visited_course: false,
|
||||
@@ -40,9 +41,6 @@ Factory.define('outlineTabData')
|
||||
course_goals: {
|
||||
goal_options: [],
|
||||
selected_goal: null,
|
||||
number_of_days_goals_enabled: false,
|
||||
days_per_week: null,
|
||||
subscribed_to_reminders: null,
|
||||
},
|
||||
course_tools: [
|
||||
{
|
||||
@@ -50,6 +48,11 @@ Factory.define('outlineTabData')
|
||||
title: 'Bookmarks',
|
||||
url: 'https://example.com/bookmarks',
|
||||
},
|
||||
{
|
||||
analytics_id: 'edx.tool.verified_upgrade',
|
||||
title: 'Upgrade to Verified',
|
||||
url: 'https://example.com/upgrade',
|
||||
},
|
||||
],
|
||||
dates_banner_info: {
|
||||
content_type_gating_enabled: false,
|
||||
@@ -63,4 +66,5 @@ Factory.define('outlineTabData')
|
||||
handouts_html: '<ul><li>Handout 1</li></ul>',
|
||||
offer: null,
|
||||
welcome_message_html: '<p>Welcome to this course!</p>',
|
||||
mfe_short_url_is_active: true,
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Factory } from 'rosie'; // eslint-disable-line import/no-extraneous-dep
|
||||
// This set of data may not be realistic, but it is intended to demonstrate many UI results.
|
||||
Factory.define('progressTabData')
|
||||
.attrs({
|
||||
access_expiration: null,
|
||||
end: '3027-03-31T00:00:00Z',
|
||||
certificate_data: {},
|
||||
completion_summary: {
|
||||
|
||||
@@ -13,8 +13,11 @@ Object {
|
||||
"courseware": Object {
|
||||
"courseId": null,
|
||||
"courseStatus": "loading",
|
||||
"proctoredExamsEnabledWaffleFlag": false,
|
||||
"sequenceId": null,
|
||||
"sequenceStatus": "loading",
|
||||
"shortLinkFeatureFlag": false,
|
||||
"specialExamsEnabledWaffleFlag": false,
|
||||
},
|
||||
"models": Object {
|
||||
"courseHomeMeta": Object {
|
||||
@@ -30,7 +33,6 @@ Object {
|
||||
},
|
||||
"id": "course-v1:edX+DemoX+Demo_Course_1",
|
||||
"isEnrolled": false,
|
||||
"isMasquerading": false,
|
||||
"isSelfPaced": false,
|
||||
"isStaff": false,
|
||||
"number": "DemoX",
|
||||
@@ -70,7 +72,6 @@ Object {
|
||||
},
|
||||
],
|
||||
"title": "Demonstration Course",
|
||||
"userTimezone": "UTC",
|
||||
"verifiedMode": Object {
|
||||
"currencySymbol": "$",
|
||||
"price": 10,
|
||||
@@ -295,6 +296,7 @@ Object {
|
||||
"hasEnded": false,
|
||||
"id": "course-v1:edX+DemoX+Demo_Course_1",
|
||||
"learnerIsFullAccess": true,
|
||||
"userTimezone": "America/New_York",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -317,8 +319,11 @@ Object {
|
||||
"courseware": Object {
|
||||
"courseId": null,
|
||||
"courseStatus": "loading",
|
||||
"proctoredExamsEnabledWaffleFlag": false,
|
||||
"sequenceId": null,
|
||||
"sequenceStatus": "loading",
|
||||
"shortLinkFeatureFlag": false,
|
||||
"specialExamsEnabledWaffleFlag": false,
|
||||
},
|
||||
"models": Object {
|
||||
"courseHomeMeta": Object {
|
||||
@@ -334,7 +339,6 @@ Object {
|
||||
},
|
||||
"id": "course-v1:edX+DemoX+Demo_Course_1",
|
||||
"isEnrolled": false,
|
||||
"isMasquerading": false,
|
||||
"isSelfPaced": false,
|
||||
"isStaff": false,
|
||||
"number": "DemoX",
|
||||
@@ -374,7 +378,6 @@ Object {
|
||||
},
|
||||
],
|
||||
"title": "Demonstration Course",
|
||||
"userTimezone": "UTC",
|
||||
"verifiedMode": Object {
|
||||
"currencySymbol": "$",
|
||||
"price": 10,
|
||||
@@ -422,6 +425,7 @@ Object {
|
||||
"due": null,
|
||||
"effortActivities": 2,
|
||||
"effortTime": 15,
|
||||
"hash_key": "abcdabcd1",
|
||||
"icon": null,
|
||||
"id": "block-v1:edX+DemoX+Demo_Course+type@sequential+block@bcdabcdabcdabcdabcdabcdabcdabcd1",
|
||||
"legacyWebUrl": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course/jump_to/block-v1:edX+DemoX+Demo_Course+type@sequential+block@bcdabcdabcdabcdabcdabcdabcdabcd1?experience=legacy",
|
||||
@@ -432,11 +436,8 @@ Object {
|
||||
},
|
||||
},
|
||||
"courseGoals": Object {
|
||||
"daysPerWeek": null,
|
||||
"goalOptions": Array [],
|
||||
"numberOfDaysGoalsEnabled": false,
|
||||
"selectedGoal": null,
|
||||
"subscribedToReminders": null,
|
||||
},
|
||||
"courseTools": Array [
|
||||
Object {
|
||||
@@ -444,6 +445,11 @@ Object {
|
||||
"title": "Bookmarks",
|
||||
"url": "https://example.com/bookmarks",
|
||||
},
|
||||
Object {
|
||||
"analyticsId": "edx.tool.verified_upgrade",
|
||||
"title": "Upgrade to Verified",
|
||||
"url": "https://example.com/upgrade",
|
||||
},
|
||||
],
|
||||
"datesBannerInfo": Object {
|
||||
"contentTypeGatingEnabled": false,
|
||||
@@ -452,6 +458,7 @@ Object {
|
||||
},
|
||||
"datesWidget": Object {
|
||||
"courseDateBlocks": Array [],
|
||||
"userTimezone": "UTC",
|
||||
},
|
||||
"enrollAlert": Object {
|
||||
"canEnroll": true,
|
||||
@@ -467,6 +474,7 @@ Object {
|
||||
"hasVisitedCourse": false,
|
||||
"url": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course/jump_to/block-v1:edX+Test+Block@12345abcde",
|
||||
},
|
||||
"shortLinkFeatureFlag": true,
|
||||
"timeOffsetMillis": 0,
|
||||
"userHasPassingGrade": undefined,
|
||||
"verifiedMode": Object {
|
||||
@@ -500,8 +508,11 @@ Object {
|
||||
"courseware": Object {
|
||||
"courseId": null,
|
||||
"courseStatus": "loading",
|
||||
"proctoredExamsEnabledWaffleFlag": false,
|
||||
"sequenceId": null,
|
||||
"sequenceStatus": "loading",
|
||||
"shortLinkFeatureFlag": false,
|
||||
"specialExamsEnabledWaffleFlag": false,
|
||||
},
|
||||
"models": Object {
|
||||
"courseHomeMeta": Object {
|
||||
@@ -517,7 +528,6 @@ Object {
|
||||
},
|
||||
"id": "course-v1:edX+DemoX+Demo_Course_1",
|
||||
"isEnrolled": false,
|
||||
"isMasquerading": false,
|
||||
"isSelfPaced": false,
|
||||
"isStaff": false,
|
||||
"number": "DemoX",
|
||||
@@ -557,7 +567,6 @@ Object {
|
||||
},
|
||||
],
|
||||
"title": "Demonstration Course",
|
||||
"userTimezone": "UTC",
|
||||
"verifiedMode": Object {
|
||||
"currencySymbol": "$",
|
||||
"price": 10,
|
||||
@@ -567,7 +576,6 @@ Object {
|
||||
},
|
||||
"progress": Object {
|
||||
"course-v1:edX+DemoX+Demo_Course_1": Object {
|
||||
"accessExpiration": null,
|
||||
"certificateData": Object {},
|
||||
"completionSummary": Object {
|
||||
"completeCount": 1,
|
||||
|
||||
@@ -98,7 +98,6 @@ function normalizeCourseHomeCourseMetadata(metadata) {
|
||||
title: tab.title,
|
||||
url: tab.url,
|
||||
})),
|
||||
isMasquerading: data.originalUserIsStaff && !data.isStaff,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -145,6 +144,7 @@ export function normalizeOutlineBlocks(courseId, blocks) {
|
||||
// link to the MFE ourselves).
|
||||
showLink: !!block.legacy_web_url,
|
||||
title: block.display_name,
|
||||
hash_key: block.hash_key,
|
||||
};
|
||||
break;
|
||||
|
||||
@@ -180,7 +180,7 @@ export function normalizeOutlineBlocks(courseId, blocks) {
|
||||
}
|
||||
|
||||
export async function getCourseHomeCourseMetadata(courseId) {
|
||||
let url = `${getConfig().LMS_BASE_URL}/api/course_home/course_metadata/${courseId}`;
|
||||
let url = `${getConfig().LMS_BASE_URL}/api/course_home/v1/course_metadata/${courseId}`;
|
||||
url = appendBrowserTimezoneToUrl(url);
|
||||
const { data } = await getAuthenticatedHttpClient().get(url);
|
||||
return normalizeCourseHomeCourseMetadata(data);
|
||||
@@ -192,7 +192,7 @@ export async function getCourseHomeCourseMetadata(courseId) {
|
||||
// import './__factories__';
|
||||
export async function getDatesTabData(courseId) {
|
||||
// return camelCaseObject(Factory.build('datesTabData'));
|
||||
const url = `${getConfig().LMS_BASE_URL}/api/course_home/dates/${courseId}`;
|
||||
const url = `${getConfig().LMS_BASE_URL}/api/course_home/v1/dates/${courseId}`;
|
||||
try {
|
||||
const { data } = await getAuthenticatedHttpClient().get(url);
|
||||
return camelCaseObject(data);
|
||||
@@ -212,7 +212,7 @@ export async function getDatesTabData(courseId) {
|
||||
}
|
||||
|
||||
export async function getProgressTabData(courseId, targetUserId) {
|
||||
let url = `${getConfig().LMS_BASE_URL}/api/course_home/progress/${courseId}`;
|
||||
let url = `${getConfig().LMS_BASE_URL}/api/course_home/v1/progress/${courseId}`;
|
||||
|
||||
// If targetUserId is passed in, we will get the progress page data
|
||||
// for the user with the provided id, rather than the requesting user.
|
||||
@@ -313,7 +313,7 @@ export function getTimeOffsetMillis(headerDate, requestTime, responseTime) {
|
||||
}
|
||||
|
||||
export async function getOutlineTabData(courseId) {
|
||||
const url = `${getConfig().LMS_BASE_URL}/api/course_home/outline/${courseId}`;
|
||||
const url = `${getConfig().LMS_BASE_URL}/api/course_home/v1/outline/${courseId}`;
|
||||
let { tabData } = {};
|
||||
let requestTime = Date.now();
|
||||
let responseTime = requestTime;
|
||||
@@ -354,6 +354,7 @@ export async function getOutlineTabData(courseId) {
|
||||
const userHasPassingGrade = data.user_has_passing_grade;
|
||||
const verifiedMode = camelCaseObject(data.verified_mode);
|
||||
const welcomeMessageHtml = data.welcome_message_html;
|
||||
const shortLinkFeatureFlag = data.mfe_short_url_is_active;
|
||||
|
||||
return {
|
||||
accessExpiration,
|
||||
@@ -375,6 +376,7 @@ export async function getOutlineTabData(courseId) {
|
||||
userHasPassingGrade,
|
||||
verifiedMode,
|
||||
welcomeMessageHtml,
|
||||
shortLinkFeatureFlag,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -387,21 +389,12 @@ export async function postCourseDeadlines(courseId, model) {
|
||||
}
|
||||
|
||||
export async function postCourseGoals(courseId, goalKey) {
|
||||
const url = new URL(`${getConfig().LMS_BASE_URL}/api/course_home/save_course_goal`);
|
||||
const url = new URL(`${getConfig().LMS_BASE_URL}/api/course_home/v1/save_course_goal`);
|
||||
return getAuthenticatedHttpClient().post(url.href, { course_id: courseId, goal_key: goalKey });
|
||||
}
|
||||
|
||||
export async function postWeeklyCourseGoals(courseId, daysPerWeek, subscribedToReminders) {
|
||||
const url = new URL(`${getConfig().LMS_BASE_URL}/api/course_home/save_course_goal`);
|
||||
return getAuthenticatedHttpClient().post(url.href, {
|
||||
course_id: courseId,
|
||||
days_per_week: daysPerWeek,
|
||||
subscribed_to_reminders: subscribedToReminders,
|
||||
});
|
||||
}
|
||||
|
||||
export async function postDismissWelcomeMessage(courseId) {
|
||||
const url = new URL(`${getConfig().LMS_BASE_URL}/api/course_home/dismiss_welcome_message`);
|
||||
const url = new URL(`${getConfig().LMS_BASE_URL}/api/course_home/v1/dismiss_welcome_message`);
|
||||
await getAuthenticatedHttpClient().post(url.href, { course_id: courseId });
|
||||
}
|
||||
|
||||
@@ -417,9 +410,3 @@ export async function executePostFromPostEvent(postData, researchEventData) {
|
||||
research_event_data: researchEventData,
|
||||
});
|
||||
}
|
||||
|
||||
export async function unsubscribeFromCourseGoal(token) {
|
||||
const url = new URL(`${getConfig().LMS_BASE_URL}/api/course_home/unsubscribe_from_course_goal/${token}`);
|
||||
return getAuthenticatedHttpClient().post(url.href)
|
||||
.then(res => camelCaseObject(res));
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ export {
|
||||
fetchProgressTab,
|
||||
resetDeadlines,
|
||||
saveCourseGoal,
|
||||
saveWeeklyCourseGoal,
|
||||
} from './thunks';
|
||||
|
||||
export { reducer } from './slice';
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
import { Pact, Matchers } from '@pact-foundation/pact';
|
||||
import path from 'path';
|
||||
import { mergeConfig, getConfig } from '@edx/frontend-platform';
|
||||
|
||||
import {
|
||||
getCourseHomeCourseMetadata,
|
||||
getDatesTabData,
|
||||
} from '../api';
|
||||
|
||||
import { initializeMockApp } from '../../../setupTest';
|
||||
import {
|
||||
courseId, dateRegex, opaqueKeysRegex, dateTypeRegex,
|
||||
} from '../../../pacts/constants';
|
||||
|
||||
const {
|
||||
somethingLike: like, term, boolean, string, eachLike,
|
||||
} = Matchers;
|
||||
const provider = new Pact({
|
||||
consumer: 'frontend-app-learning',
|
||||
provider: 'lms',
|
||||
log: path.resolve(process.cwd(), 'src/course-home/data/pact-tests/logs', 'pact.log'),
|
||||
dir: path.resolve(process.cwd(), 'src/pacts'),
|
||||
pactfileWriteMode: 'merge',
|
||||
logLevel: 'DEBUG',
|
||||
cors: true,
|
||||
});
|
||||
|
||||
describe('Course Home Service', () => {
|
||||
beforeAll(async () => {
|
||||
initializeMockApp();
|
||||
await provider
|
||||
.setup()
|
||||
.then((options) => mergeConfig({
|
||||
LMS_BASE_URL: `http://localhost:${options.port}`,
|
||||
}, 'Custom app config for pact tests'));
|
||||
});
|
||||
|
||||
afterEach(() => provider.verify());
|
||||
afterAll(() => provider.finalize());
|
||||
describe('When a request to fetch tab is made', () => {
|
||||
it('returns tab data for a course_id', async () => {
|
||||
await provider.addInteraction({
|
||||
state: `Tab data exists for course_id ${courseId}`,
|
||||
uponReceiving: 'a request to fetch tab',
|
||||
withRequest: {
|
||||
method: 'GET',
|
||||
path: `/api/course_home/course_metadata/${courseId}`,
|
||||
},
|
||||
willRespondWith: {
|
||||
status: 200,
|
||||
body: {
|
||||
can_show_upgrade_sock: boolean(false),
|
||||
verified_mode: like({
|
||||
access_expiration_date: null,
|
||||
currency: 'USD',
|
||||
currency_symbol: '$',
|
||||
price: 149,
|
||||
sku: '8CF08E5',
|
||||
upgrade_url: `${getConfig().ECOMMERCE_BASE_URL}/basket/add/?sku=8CF08E5`,
|
||||
}),
|
||||
can_load_courseware: boolean(true),
|
||||
celebrations: like({
|
||||
first_section: false,
|
||||
streak_length_to_celebrate: null,
|
||||
streak_discount_enabled: false,
|
||||
}),
|
||||
course_access: {
|
||||
has_access: boolean(true),
|
||||
error_code: null,
|
||||
developer_message: null,
|
||||
user_message: null,
|
||||
additional_context_user_message: null,
|
||||
user_fragment: null,
|
||||
},
|
||||
course_id: term({
|
||||
generate: 'course-v1:edX+DemoX+Demo_Course',
|
||||
matcher: opaqueKeysRegex,
|
||||
}),
|
||||
is_enrolled: boolean(true),
|
||||
is_self_paced: boolean(false),
|
||||
is_staff: boolean(true),
|
||||
number: string('DemoX'),
|
||||
org: string('edX'),
|
||||
original_user_is_staff: boolean(true),
|
||||
start: term({
|
||||
generate: '2013-02-05T05:00:00Z',
|
||||
matcher: dateRegex,
|
||||
}),
|
||||
tabs: eachLike({
|
||||
tab_id: 'courseware',
|
||||
title: 'Course',
|
||||
url: `${getConfig().BASE_URL}/course/course-v1:edX+DemoX+Demo_Course/home`,
|
||||
}),
|
||||
title: string('Demonstration Course'),
|
||||
username: string('edx'),
|
||||
},
|
||||
},
|
||||
});
|
||||
const normalizedTabData = {
|
||||
canShowUpgradeSock: false,
|
||||
verifiedMode: {
|
||||
accessExpirationDate: null,
|
||||
currency: 'USD',
|
||||
currencySymbol: '$',
|
||||
price: 149,
|
||||
sku: '8CF08E5',
|
||||
upgradeUrl: `${getConfig().ECOMMERCE_BASE_URL}/basket/add/?sku=8CF08E5`,
|
||||
},
|
||||
canLoadCourseware: true,
|
||||
celebrations: {
|
||||
firstSection: false,
|
||||
streakLengthToCelebrate: null,
|
||||
streakDiscountEnabled: false,
|
||||
},
|
||||
courseAccess: {
|
||||
hasAccess: true,
|
||||
errorCode: null,
|
||||
developerMessage: null,
|
||||
userMessage: null,
|
||||
additionalContextUserMessage: null,
|
||||
userFragment: null,
|
||||
},
|
||||
courseId: 'course-v1:edX+DemoX+Demo_Course',
|
||||
isEnrolled: true,
|
||||
isMasquerading: false,
|
||||
isSelfPaced: false,
|
||||
isStaff: true,
|
||||
number: 'DemoX',
|
||||
org: 'edX',
|
||||
originalUserIsStaff: true,
|
||||
start: '2013-02-05T05:00:00Z',
|
||||
tabs: [
|
||||
{
|
||||
slug: 'outline',
|
||||
title: 'Course',
|
||||
url: `${getConfig().BASE_URL}/course/course-v1:edX+DemoX+Demo_Course/home`,
|
||||
},
|
||||
],
|
||||
title: 'Demonstration Course',
|
||||
username: 'edx',
|
||||
};
|
||||
const response = await getCourseHomeCourseMetadata(courseId);
|
||||
expect(response).toBeTruthy();
|
||||
expect(response).toEqual(normalizedTabData);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When a request to fetch dates tab is made', () => {
|
||||
it('returns course date blocks for a course_id', async () => {
|
||||
await provider.addInteraction({
|
||||
state: `course date blocks exist for course_id ${courseId}`,
|
||||
uponReceiving: 'a request to fetch dates tab',
|
||||
withRequest: {
|
||||
method: 'GET',
|
||||
path: `/api/course_home/dates/${courseId}`,
|
||||
},
|
||||
willRespondWith: {
|
||||
status: 200,
|
||||
body: {
|
||||
dates_banner_info: like({
|
||||
missed_deadlines: false,
|
||||
content_type_gating_enabled: false,
|
||||
missed_gated_content: false,
|
||||
verified_upgrade_link: `${getConfig().ECOMMERCE_BASE_URL}/basket/add/?sku=8CF08E5`,
|
||||
}),
|
||||
course_date_blocks: eachLike({
|
||||
assignment_type: null,
|
||||
complete: null,
|
||||
date: term({
|
||||
generate: '2013-02-05T05:00:00Z',
|
||||
matcher: dateRegex,
|
||||
}),
|
||||
date_type: term({
|
||||
generate: 'verified-upgrade-deadline',
|
||||
matcher: dateTypeRegex,
|
||||
}),
|
||||
description: 'You are still eligible to upgrade to a Verified Certificate! Pursue it to highlight the knowledge and skills you gain in this course.',
|
||||
learner_has_access: true,
|
||||
link: `${getConfig().ECOMMERCE_BASE_URL}/basket/add/?sku=8CF08E5`,
|
||||
link_text: 'Upgrade to Verified Certificate',
|
||||
title: 'Verification Upgrade Deadline',
|
||||
extra_info: null,
|
||||
first_component_block_id: '',
|
||||
}),
|
||||
has_ended: boolean(false),
|
||||
learner_is_full_access: boolean(true),
|
||||
user_timezone: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
const camelCaseResponse = {
|
||||
datesBannerInfo: {
|
||||
missedDeadlines: false,
|
||||
contentTypeGatingEnabled: false,
|
||||
missedGatedContent: false,
|
||||
verifiedUpgradeLink: `${getConfig().ECOMMERCE_BASE_URL}/basket/add/?sku=8CF08E5`,
|
||||
},
|
||||
courseDateBlocks: [
|
||||
{
|
||||
assignmentType: null,
|
||||
complete: null,
|
||||
date: '2013-02-05T05:00:00Z',
|
||||
dateType: 'verified-upgrade-deadline',
|
||||
description: 'You are still eligible to upgrade to a Verified Certificate! Pursue it to highlight the knowledge and skills you gain in this course.',
|
||||
learnerHasAccess: true,
|
||||
link: `${getConfig().ECOMMERCE_BASE_URL}/basket/add/?sku=8CF08E5`,
|
||||
linkText: 'Upgrade to Verified Certificate',
|
||||
title: 'Verification Upgrade Deadline',
|
||||
extraInfo: null,
|
||||
firstComponentBlockId: '',
|
||||
},
|
||||
],
|
||||
hasEnded: false,
|
||||
learnerIsFullAccess: true,
|
||||
userTimezone: null,
|
||||
};
|
||||
|
||||
const response = await getDatesTabData(courseId);
|
||||
expect(response).toBeTruthy();
|
||||
expect(response).toEqual(camelCaseResponse);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -18,7 +18,7 @@ const axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
describe('Data layer integration tests', () => {
|
||||
const courseHomeMetadata = Factory.build('courseHomeMetadata');
|
||||
const { id: courseId } = courseHomeMetadata;
|
||||
let courseMetadataUrl = `${getConfig().LMS_BASE_URL}/api/course_home/course_metadata/${courseId}`;
|
||||
let courseMetadataUrl = `${getConfig().LMS_BASE_URL}/api/course_home/v1/course_metadata/${courseId}`;
|
||||
courseMetadataUrl = appendBrowserTimezoneToUrl(courseMetadataUrl);
|
||||
|
||||
let store;
|
||||
@@ -31,7 +31,7 @@ describe('Data layer integration tests', () => {
|
||||
});
|
||||
|
||||
describe('Test fetchDatesTab', () => {
|
||||
const datesBaseUrl = `${getConfig().LMS_BASE_URL}/api/course_home/dates`;
|
||||
const datesBaseUrl = `${getConfig().LMS_BASE_URL}/api/course_home/v1/dates`;
|
||||
|
||||
it('Should fail to fetch if error occurs', async () => {
|
||||
axiosMock.onGet(courseMetadataUrl).networkError();
|
||||
@@ -60,7 +60,7 @@ describe('Data layer integration tests', () => {
|
||||
});
|
||||
|
||||
describe('Test fetchOutlineTab', () => {
|
||||
const outlineBaseUrl = `${getConfig().LMS_BASE_URL}/api/course_home/outline`;
|
||||
const outlineBaseUrl = `${getConfig().LMS_BASE_URL}/api/course_home/v1/outline`;
|
||||
|
||||
it('Should result in fetch failure if error occurs', async () => {
|
||||
axiosMock.onGet(courseMetadataUrl).networkError();
|
||||
@@ -89,7 +89,7 @@ describe('Data layer integration tests', () => {
|
||||
});
|
||||
|
||||
describe('Test fetchProgressTab', () => {
|
||||
const progressBaseUrl = `${getConfig().LMS_BASE_URL}/api/course_home/progress`;
|
||||
const progressBaseUrl = `${getConfig().LMS_BASE_URL}/api/course_home/v1/progress`;
|
||||
|
||||
it('Should result in fetch failure if error occurs', async () => {
|
||||
axiosMock.onGet(courseMetadataUrl).networkError();
|
||||
@@ -133,7 +133,7 @@ describe('Data layer integration tests', () => {
|
||||
|
||||
describe('Test saveCourseGoal', () => {
|
||||
it('Should save course goal', async () => {
|
||||
const goalUrl = `${getConfig().LMS_BASE_URL}/api/course_home/save_course_goal`;
|
||||
const goalUrl = `${getConfig().LMS_BASE_URL}/api/course_home/v1/save_course_goal`;
|
||||
axiosMock.onPost(goalUrl).reply(200, {});
|
||||
|
||||
await thunks.saveCourseGoal(courseId, 'unsure');
|
||||
@@ -164,7 +164,7 @@ describe('Data layer integration tests', () => {
|
||||
|
||||
describe('Test dismissWelcomeMessage', () => {
|
||||
it('Should dismiss welcome message', async () => {
|
||||
const dismissUrl = `${getConfig().LMS_BASE_URL}/api/course_home/dismiss_welcome_message`;
|
||||
const dismissUrl = `${getConfig().LMS_BASE_URL}/api/course_home/v1/dismiss_welcome_message`;
|
||||
axiosMock.onPost(dismissUrl).reply(201);
|
||||
|
||||
await executeThunk(thunks.dismissWelcomeMessage(courseId), store.dispatch);
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
getProgressTabData,
|
||||
postCourseDeadlines,
|
||||
postCourseGoals,
|
||||
postWeeklyCourseGoals,
|
||||
postDismissWelcomeMessage,
|
||||
postRequestCert,
|
||||
} from './api';
|
||||
@@ -114,10 +113,6 @@ export async function saveCourseGoal(courseId, goalKey) {
|
||||
return postCourseGoals(courseId, goalKey);
|
||||
}
|
||||
|
||||
export async function saveWeeklyCourseGoal(courseId, daysPerWeek, subscribedToReminders) {
|
||||
return postWeeklyCourseGoals(courseId, daysPerWeek, subscribedToReminders);
|
||||
}
|
||||
|
||||
export function processEvent(eventData, getTabData) {
|
||||
return async (dispatch) => {
|
||||
// Pulling this out early so the data doesn't get camelCased and is easier
|
||||
|
||||
@@ -32,7 +32,7 @@ describe('DatesTab', () => {
|
||||
component = (
|
||||
<AppProvider store={store}>
|
||||
<UserMessagesProvider>
|
||||
<Route path="/course/:courseId/dates">
|
||||
<Route path="/c/:courseId/dates">
|
||||
<TabContainer tab="dates" fetch={fetchDatesTab} slice="courseHome">
|
||||
<DatesTab />
|
||||
</TabContainer>
|
||||
@@ -43,11 +43,11 @@ describe('DatesTab', () => {
|
||||
});
|
||||
|
||||
const datesTabData = Factory.build('datesTabData');
|
||||
let courseMetadata = Factory.build('courseHomeMetadata', { user_timezone: 'America/New_York' });
|
||||
let courseMetadata = Factory.build('courseHomeMetadata');
|
||||
const { id: courseId } = courseMetadata;
|
||||
|
||||
const datesUrl = `${getConfig().LMS_BASE_URL}/api/course_home/dates/${courseId}`;
|
||||
let courseMetadataUrl = `${getConfig().LMS_BASE_URL}/api/course_home/course_metadata/${courseId}`;
|
||||
const datesUrl = `${getConfig().LMS_BASE_URL}/api/course_home/v1/dates/${courseId}`;
|
||||
let courseMetadataUrl = `${getConfig().LMS_BASE_URL}/api/course_home/v1/course_metadata/${courseId}`;
|
||||
courseMetadataUrl = appendBrowserTimezoneToUrl(courseMetadataUrl);
|
||||
|
||||
function setMetadata(attributes, options) {
|
||||
@@ -81,7 +81,7 @@ describe('DatesTab', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onGet(courseMetadataUrl).reply(200, courseMetadata);
|
||||
axiosMock.onGet(datesUrl).reply(200, datesTabData);
|
||||
history.push(`/course/${courseId}/dates`); // so tab can pull course id from url
|
||||
history.push(`/c/${courseId}/dates`); // so tab can pull course id from url
|
||||
|
||||
render(component);
|
||||
});
|
||||
@@ -147,7 +147,7 @@ describe('DatesTab', () => {
|
||||
describe('Suggested schedule messaging', () => {
|
||||
beforeEach(() => {
|
||||
setMetadata({ is_self_paced: true, is_enrolled: true });
|
||||
history.push(`/course/${courseId}/dates`);
|
||||
history.push(`/c/${courseId}/dates`);
|
||||
});
|
||||
|
||||
it('renders SuggestedScheduleHeader', async () => {
|
||||
@@ -316,7 +316,7 @@ describe('DatesTab', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
axiosMock.onGet(datesUrl).reply(200, datesTabData);
|
||||
history.push(`/course/${courseId}/dates`); // so tab can pull course id from url
|
||||
history.push(`/c/${courseId}/dates`); // so tab can pull course id from url
|
||||
});
|
||||
|
||||
it('redirects to course survey for a survey_required error code', async () => {
|
||||
|
||||
@@ -29,10 +29,10 @@ function Day({
|
||||
const {
|
||||
courseId,
|
||||
} = useSelector(state => state.courseHome);
|
||||
|
||||
const {
|
||||
userTimezone,
|
||||
} = useModel('courseHomeMeta', courseId);
|
||||
|
||||
} = useModel('dates', courseId);
|
||||
const timezoneFormatArgs = userTimezone ? { timeZone: userTimezone } : {};
|
||||
|
||||
const { color, badges } = getBadgeListAndColor(date, intl, null, items);
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { Header } from '../../course-header';
|
||||
import PageLoading from '../../generic/PageLoading';
|
||||
import { unsubscribeFromCourseGoal } from '../data/api';
|
||||
|
||||
import messages from './messages';
|
||||
import ResultPage from './ResultPage';
|
||||
|
||||
function GoalUnsubscribe({ intl }) {
|
||||
const { token } = useParams();
|
||||
const [error, setError] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [data, setData] = useState({});
|
||||
|
||||
// We don't need to bother with redux for this simple page. We're not sharing state with other pages at all.
|
||||
useEffect(() => {
|
||||
unsubscribeFromCourseGoal(token)
|
||||
.then(
|
||||
(result) => {
|
||||
setIsLoading(false);
|
||||
setData(result.data);
|
||||
},
|
||||
() => {
|
||||
setIsLoading(false);
|
||||
setError(true);
|
||||
},
|
||||
);
|
||||
}, []); // deps=[] to only run once
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header showUserDropdown={false} />
|
||||
<main id="main-content" className="container my-5 text-center">
|
||||
{isLoading && (
|
||||
<PageLoading srMessage={`${intl.formatMessage(messages.loading)}`} />
|
||||
)}
|
||||
{!isLoading && (
|
||||
<ResultPage error={error} courseTitle={data.courseTitle} />
|
||||
)}
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
GoalUnsubscribe.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(GoalUnsubscribe);
|
||||
@@ -1,62 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Route } from 'react-router';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { getConfig, history } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import GoalUnsubscribe from './GoalUnsubscribe';
|
||||
import { act, initializeMockApp } from '../../setupTest';
|
||||
import initializeStore from '../../store';
|
||||
import { UserMessagesProvider } from '../../generic/user-messages';
|
||||
|
||||
initializeMockApp();
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
|
||||
describe('GoalUnsubscribe', () => {
|
||||
let axiosMock;
|
||||
let store;
|
||||
let component;
|
||||
const unsubscribeUrl = `${getConfig().LMS_BASE_URL}/api/course_home/unsubscribe_from_course_goal/TOKEN`;
|
||||
|
||||
beforeEach(() => {
|
||||
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
store = initializeStore();
|
||||
component = (
|
||||
<AppProvider store={store}>
|
||||
<UserMessagesProvider>
|
||||
<Route path="/goal-unsubscribe/:token" component={GoalUnsubscribe} />
|
||||
</UserMessagesProvider>
|
||||
</AppProvider>
|
||||
);
|
||||
history.push('/goal-unsubscribe/TOKEN'); // so we can pull token from url
|
||||
});
|
||||
|
||||
it('starts with a spinner', () => {
|
||||
render(component);
|
||||
expect(screen.getByRole('status')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('loads a real token', async () => {
|
||||
const response = { course_title: 'My Sample Course' };
|
||||
axiosMock.onPost(unsubscribeUrl).reply(200, response);
|
||||
await act(async () => render(component));
|
||||
|
||||
expect(screen.getByText('You’ve unsubscribed from goal reminders')).toBeInTheDocument();
|
||||
expect(screen.getByText(/your goal for My Sample Course/)).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: 'Go to dashboard' }))
|
||||
.toHaveAttribute('href', 'http://localhost:18000/dashboard');
|
||||
});
|
||||
|
||||
it('loads a bad token with an error page', async () => {
|
||||
axiosMock.onPost(unsubscribeUrl).reply(404, {});
|
||||
await act(async () => render(component));
|
||||
|
||||
expect(screen.getByText('Something went wrong')).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: 'Go to dashboard' }))
|
||||
.toHaveAttribute('href', 'http://localhost:18000/dashboard');
|
||||
expect(screen.getByRole('link', { name: 'contact support' }))
|
||||
.toHaveAttribute('href', 'http://localhost:18000/contact');
|
||||
});
|
||||
});
|
||||
@@ -1,58 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Button, Hyperlink } from '@edx/paragon';
|
||||
|
||||
import messages from './messages';
|
||||
import { ReactComponent as UnsubscribeIcon } from './unsubscribe.svg';
|
||||
|
||||
function ResultPage({ courseTitle, error, intl }) {
|
||||
const errorDescription = (
|
||||
<FormattedMessage
|
||||
id="learning.goals.unsubscribe.errorDescription"
|
||||
defaultMessage="We were unable to unsubscribe you from goal reminder emails. Please try again later or {contactSupport} for help."
|
||||
values={{
|
||||
contactSupport: (
|
||||
<Hyperlink
|
||||
className="text-reset"
|
||||
style={{ textDecoration: 'underline' }}
|
||||
destination={`${getConfig().CONTACT_URL}`}
|
||||
>
|
||||
{intl.formatMessage(messages.contactSupport)}
|
||||
</Hyperlink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const header = error
|
||||
? intl.formatMessage(messages.errorHeader)
|
||||
: intl.formatMessage(messages.header);
|
||||
const description = error
|
||||
? errorDescription
|
||||
: intl.formatMessage(messages.description, { courseTitle });
|
||||
|
||||
return (
|
||||
<>
|
||||
<UnsubscribeIcon className="text-primary" alt="" />
|
||||
<div role="heading" aria-level="1" className="h2">{header}</div>
|
||||
<div>{description}</div>
|
||||
<Button variant="brand" href={`${getConfig().LMS_BASE_URL}/dashboard`} className="mt-4">
|
||||
{intl.formatMessage(messages.goToDashboard)}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
ResultPage.defaultProps = {
|
||||
courseTitle: null,
|
||||
error: false,
|
||||
};
|
||||
|
||||
ResultPage.propTypes = {
|
||||
courseTitle: PropTypes.string,
|
||||
error: PropTypes.bool,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(ResultPage);
|
||||
@@ -1,3 +0,0 @@
|
||||
import GoalUnsubscribe from './GoalUnsubscribe';
|
||||
|
||||
export default GoalUnsubscribe;
|
||||
@@ -1,30 +0,0 @@
|
||||
import { defineMessages } from '@edx/frontend-platform/i18n';
|
||||
|
||||
const messages = defineMessages({
|
||||
contactSupport: {
|
||||
id: 'learning.goals.unsubscribe.contact',
|
||||
defaultMessage: 'contact support',
|
||||
},
|
||||
description: {
|
||||
id: 'learning.goals.unsubscribe.description',
|
||||
defaultMessage: 'You will no longer receive email reminders about your goal for {courseTitle}.',
|
||||
},
|
||||
errorHeader: {
|
||||
id: 'learning.goals.unsubscribe.errorHeader',
|
||||
defaultMessage: 'Something went wrong',
|
||||
},
|
||||
goToDashboard: {
|
||||
id: 'learning.goals.unsubscribe.goToDashboard',
|
||||
defaultMessage: 'Go to dashboard',
|
||||
},
|
||||
header: {
|
||||
id: 'learning.goals.unsubscribe.header',
|
||||
defaultMessage: 'You’ve unsubscribed from goal reminders',
|
||||
},
|
||||
loading: {
|
||||
id: 'learning.goals.unsubscribe.loading',
|
||||
defaultMessage: 'Unsubscribing…',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
@@ -1,5 +0,0 @@
|
||||
<svg width="167" height="153" viewBox="0 0 167 153" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M140.25 25.5H12.75V127.5H140.25V25.5ZM127.5 46L76.5 77.875L25.5 46V38.25L76.5 70.125L127.5 38.25V46Z" fill="currentColor"/>
|
||||
<circle cx="134" cy="39" r="33" transform="rotate(-90 134 39)" fill="white"/>
|
||||
<path d="M134 11C118.544 11 106 23.544 106 39C106 54.456 118.544 67 134 67C149.456 67 162 54.456 162 39C162 23.544 149.456 11 134 11ZM134 61.4C121.624 61.4 111.6 51.376 111.6 39C111.6 33.82 113.364 29.06 116.332 25.28L147.72 56.668C143.94 59.636 139.18 61.4 134 61.4ZM151.668 52.72L120.28 21.332C124.06 18.364 128.82 16.6 134 16.6C146.376 16.6 156.4 26.624 156.4 39C156.4 44.18 154.636 48.94 151.668 52.72Z" fill="#D23228"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 743 B |
@@ -1,7 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
|
||||
import { sendTrackEvent, sendTrackingLogEvent } from '@edx/frontend-platform/analytics';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { Button, Toast } from '@edx/paragon';
|
||||
@@ -10,8 +9,6 @@ import { AlertList } from '../../generic/user-messages';
|
||||
import CourseDates from './widgets/CourseDates';
|
||||
import CourseGoalCard from './widgets/CourseGoalCard';
|
||||
import CourseHandouts from './widgets/CourseHandouts';
|
||||
import StartOrResumeCourseCard from './widgets/StartOrResumeCourseCard';
|
||||
import WeeklyLearningGoalCard from './widgets/WeeklyLearningGoalCard';
|
||||
import CourseTools from './widgets/CourseTools';
|
||||
import { fetchOutlineTab } from '../data';
|
||||
import genericMessages from '../../generic/messages';
|
||||
@@ -20,10 +17,11 @@ import Section from './Section';
|
||||
import ShiftDatesAlert from '../suggested-schedule-messaging/ShiftDatesAlert';
|
||||
import UpdateGoalSelector from './widgets/UpdateGoalSelector';
|
||||
import UpgradeNotification from '../../generic/upgrade-notification/UpgradeNotification';
|
||||
import { useAccessExpirationAlertMasquerade } from '../../alerts/access-expiration-alert';
|
||||
import UpgradeToShiftDatesAlert from '../suggested-schedule-messaging/UpgradeToShiftDatesAlert';
|
||||
import useCertificateAvailableAlert from './alerts/certificate-status-alert';
|
||||
import useCourseEndAlert from './alerts/course-end-alert';
|
||||
import useCourseStartAlert from '../../alerts/course-start-alert';
|
||||
import useCourseStartAlert from './alerts/course-start-alert';
|
||||
import usePrivateCourseAlert from './alerts/private-course-alert';
|
||||
import useScheduledContentAlert from './alerts/scheduled-content-alert';
|
||||
import { useModel } from '../../generic/model-store';
|
||||
@@ -44,7 +42,6 @@ function OutlineTab({ intl }) {
|
||||
org,
|
||||
title,
|
||||
username,
|
||||
userTimezone,
|
||||
} = useModel('courseHomeMeta', courseId);
|
||||
|
||||
const {
|
||||
@@ -56,13 +53,14 @@ function OutlineTab({ intl }) {
|
||||
courseGoals: {
|
||||
goalOptions,
|
||||
selectedGoal,
|
||||
weeklyLearningGoalEnabled,
|
||||
} = {},
|
||||
datesBannerInfo,
|
||||
datesWidget: {
|
||||
courseDateBlocks,
|
||||
userTimezone,
|
||||
},
|
||||
resumeCourse: {
|
||||
hasVisitedCourse,
|
||||
url: resumeCourseUrl,
|
||||
},
|
||||
offer,
|
||||
@@ -70,7 +68,7 @@ function OutlineTab({ intl }) {
|
||||
verifiedMode,
|
||||
} = useModel('outline', courseId);
|
||||
|
||||
const [deprecatedCourseGoalToDisplay, setDeprecatedCourseGoalToDisplay] = useState(selectedGoal);
|
||||
const [courseGoalToDisplay, setCourseGoalToDisplay] = useState(selectedGoal);
|
||||
const [goalToastHeader, setGoalToastHeader] = useState('');
|
||||
const [expandAll, setExpandAll] = useState(false);
|
||||
|
||||
@@ -79,7 +77,16 @@ function OutlineTab({ intl }) {
|
||||
courserun_key: courseId,
|
||||
};
|
||||
|
||||
const logResumeCourseClick = () => {
|
||||
sendTrackingLogEvent('edx.course.home.resume_course.clicked', {
|
||||
...eventProperties,
|
||||
event_type: hasVisitedCourse ? 'resume' : 'start',
|
||||
url: resumeCourseUrl,
|
||||
});
|
||||
};
|
||||
|
||||
// Below the course title alerts (appearing in the order listed here)
|
||||
const accessExpirationAlertMasquerade = useAccessExpirationAlertMasquerade(accessExpiration, userTimezone, 'outline-course-alerts');
|
||||
const courseStartAlert = useCourseStartAlert(courseId);
|
||||
const courseEndAlert = useCourseEndAlert(courseId);
|
||||
const certificateAvailableAlert = useCertificateAvailableAlert(courseId);
|
||||
@@ -100,19 +107,9 @@ function OutlineTab({ intl }) {
|
||||
});
|
||||
};
|
||||
|
||||
const isEnterpriseUser = () => {
|
||||
const authenticatedUser = getAuthenticatedUser();
|
||||
const userRoleNames = authenticatedUser ? authenticatedUser.roles.map(role => role.split(':')[0]) : [];
|
||||
|
||||
return userRoleNames.includes('enterprise_learner');
|
||||
};
|
||||
|
||||
/** [[MM-P2P] Experiment */
|
||||
const MMP2P = initHomeMMP2P(courseId);
|
||||
|
||||
/** show post enrolment survey to only B2C learners */
|
||||
const learnerType = isEnterpriseUser() ? 'enterprise_learner' : 'b2c_learner';
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toast
|
||||
@@ -122,10 +119,17 @@ function OutlineTab({ intl }) {
|
||||
>
|
||||
{goalToastHeader}
|
||||
</Toast>
|
||||
<div data-learner-type={learnerType} className="row w-100 mx-0 my-3 justify-content-between">
|
||||
<div className="row w-100 mx-0 my-3 justify-content-between">
|
||||
<div className="col-12 col-sm-auto p-0">
|
||||
<div role="heading" aria-level="1" className="h2">{title}</div>
|
||||
</div>
|
||||
{resumeCourseUrl && (
|
||||
<div className="col-12 col-sm-auto p-0">
|
||||
<Button variant="brand" block href={resumeCourseUrl} onClick={() => logResumeCourseClick()}>
|
||||
{hasVisitedCourse ? intl.formatMessage(messages.resume) : intl.formatMessage(messages.start)}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/** [MM-P2P] Experiment (className for optimizely trigger) */}
|
||||
<div className="row course-outline-tab">
|
||||
@@ -146,6 +150,7 @@ function OutlineTab({ intl }) {
|
||||
topic="outline-course-alerts"
|
||||
className="mb-3"
|
||||
customAlerts={{
|
||||
...accessExpirationAlertMasquerade,
|
||||
...certificateAvailableAlert,
|
||||
...courseEndAlert,
|
||||
...courseStartAlert,
|
||||
@@ -159,18 +164,15 @@ function OutlineTab({ intl }) {
|
||||
<UpgradeToShiftDatesAlert model="outline" logUpgradeLinkClick={logUpgradeToShiftDatesLinkClick} />
|
||||
</>
|
||||
)}
|
||||
{!deprecatedCourseGoalToDisplay && goalOptions && goalOptions.length > 0 && (
|
||||
{!courseGoalToDisplay && goalOptions && goalOptions.length > 0 && (
|
||||
<CourseGoalCard
|
||||
courseId={courseId}
|
||||
goalOptions={goalOptions}
|
||||
title={title}
|
||||
setGoalToDisplay={(newGoal) => { setDeprecatedCourseGoalToDisplay(newGoal); }}
|
||||
setGoalToDisplay={(newGoal) => { setCourseGoalToDisplay(newGoal); }}
|
||||
setGoalToastHeader={(newHeader) => { setGoalToastHeader(newHeader); }}
|
||||
/>
|
||||
)}
|
||||
{resumeCourseUrl && (
|
||||
<StartOrResumeCourseCard />
|
||||
)}
|
||||
<WelcomeMessage courseId={courseId} />
|
||||
{rootCourseId && (
|
||||
<>
|
||||
@@ -201,22 +203,15 @@ function OutlineTab({ intl }) {
|
||||
courseId={courseId}
|
||||
username={username}
|
||||
/>
|
||||
{deprecatedCourseGoalToDisplay && goalOptions && goalOptions.length > 0 && (
|
||||
{courseGoalToDisplay && goalOptions && goalOptions.length > 0 && (
|
||||
<UpdateGoalSelector
|
||||
courseId={courseId}
|
||||
goalOptions={goalOptions}
|
||||
selectedGoal={deprecatedCourseGoalToDisplay}
|
||||
setGoalToDisplay={(newGoal) => { setDeprecatedCourseGoalToDisplay(newGoal); }}
|
||||
selectedGoal={courseGoalToDisplay}
|
||||
setGoalToDisplay={(newGoal) => { setCourseGoalToDisplay(newGoal); }}
|
||||
setGoalToastHeader={(newHeader) => { setGoalToastHeader(newHeader); }}
|
||||
/>
|
||||
)}
|
||||
{weeklyLearningGoalEnabled && (
|
||||
<WeeklyLearningGoalCard
|
||||
daysPerWeek={selectedGoal && 'daysPerWeek' in selectedGoal ? selectedGoal.daysPerWeek : null}
|
||||
subscribedToReminders={selectedGoal && 'subscribedToReminders' in selectedGoal ? selectedGoal.subscribedToReminders : false}
|
||||
courseId={courseId}
|
||||
/>
|
||||
)}
|
||||
<CourseTools
|
||||
courseId={courseId}
|
||||
/>
|
||||
|
||||
@@ -6,7 +6,6 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import Cookies from 'js-cookie';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import messages from './messages';
|
||||
|
||||
import { buildMinimalCourseBlocks } from '../../shared/data/__factories__/courseBlocks.factory';
|
||||
import {
|
||||
@@ -17,7 +16,6 @@ import * as thunks from '../data/thunks';
|
||||
import initializeStore from '../../store';
|
||||
import { CERT_STATUS_TYPE } from './alerts/certificate-status-alert/CertificateStatusAlert';
|
||||
import OutlineTab from './OutlineTab';
|
||||
import LoadedTabPage from '../../tab-page/LoadedTabPage';
|
||||
|
||||
initializeMockApp();
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
@@ -26,12 +24,11 @@ describe('Outline Tab', () => {
|
||||
let axiosMock;
|
||||
|
||||
const courseId = 'course-v1:edX+Test+run';
|
||||
let courseMetadataUrl = `${getConfig().LMS_BASE_URL}/api/course_home/course_metadata/${courseId}`;
|
||||
let courseMetadataUrl = `${getConfig().LMS_BASE_URL}/api/course_home/v1/course_metadata/${courseId}`;
|
||||
courseMetadataUrl = appendBrowserTimezoneToUrl(courseMetadataUrl);
|
||||
const enrollmentUrl = `${getConfig().LMS_BASE_URL}/api/enrollment/v1/enrollment`;
|
||||
const goalUrl = `${getConfig().LMS_BASE_URL}/api/course_home/save_course_goal`;
|
||||
const masqueradeUrl = `${getConfig().LMS_BASE_URL}/courses/${courseId}/masquerade`;
|
||||
const outlineUrl = `${getConfig().LMS_BASE_URL}/api/course_home/outline/${courseId}`;
|
||||
const goalUrl = `${getConfig().LMS_BASE_URL}/api/course_home/v1/save_course_goal`;
|
||||
const outlineUrl = `${getConfig().LMS_BASE_URL}/api/course_home/v1/outline/${courseId}`;
|
||||
const proctoringInfoUrl = `${getConfig().LMS_BASE_URL}/api/edx_proctoring/v1/user_onboarding/status?is_learning_mfe=true&course_id=${encodeURIComponent(courseId)}`;
|
||||
|
||||
const store = initializeStore();
|
||||
@@ -60,7 +57,6 @@ describe('Outline Tab', () => {
|
||||
axiosMock.onGet(courseMetadataUrl).reply(200, defaultMetadata);
|
||||
axiosMock.onPost(enrollmentUrl).reply(200, {});
|
||||
axiosMock.onPost(goalUrl).reply(200, { header: 'Success' });
|
||||
axiosMock.onGet(masqueradeUrl).reply(200, { success: true });
|
||||
axiosMock.onGet(outlineUrl).reply(200, defaultTabData);
|
||||
axiosMock.onGet(proctoringInfoUrl).reply(200, {
|
||||
onboarding_status: 'created',
|
||||
@@ -160,7 +156,7 @@ describe('Outline Tab', () => {
|
||||
await fetchAndRender();
|
||||
|
||||
const sequenceLink = screen.getByText('Title of Sequence');
|
||||
expect(sequenceLink.getAttribute('href')).toContain(`/course/${courseId}`);
|
||||
expect(sequenceLink.getAttribute('href')).toContain(`/c/${courseId}`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -414,68 +410,6 @@ describe('Outline Tab', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Weekly Learning Goals', () => {
|
||||
it('does not render weekly learning goal if weeklyLearningGoalEnabled is false', async () => {
|
||||
await fetchAndRender();
|
||||
expect(screen.queryByTestId('weekly-learning-goal-card')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('weekly learning goal is not set', () => {
|
||||
beforeEach(async () => {
|
||||
setTabData({
|
||||
course_goals: {
|
||||
weekly_learning_goal_enabled: true,
|
||||
},
|
||||
});
|
||||
await fetchAndRender();
|
||||
});
|
||||
|
||||
it('renders weekly learning goal card', async () => {
|
||||
expect(screen.queryByTestId('weekly-learning-goal-card')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders startOrResumeCourseCard', async () => {
|
||||
expect(screen.queryByTestId('start-resume-card')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('disables the subscribe button if no goal is set', async () => {
|
||||
expect(screen.getByLabelText(messages.setGoalReminder.defaultMessage)).toBeDisabled();
|
||||
});
|
||||
|
||||
it('calls the API when a button is clicked', async () => {
|
||||
expect(screen.queryByText(messages.casualGoalButtonText.defaultMessage)).toBeInTheDocument();
|
||||
expect(screen.getByText(messages.casualGoalButtonText.defaultMessage).closest('button')).toBeInTheDocument();
|
||||
|
||||
// click on Casual goal
|
||||
const button = await screen.getByText(messages.casualGoalButtonText.defaultMessage).closest('button');
|
||||
fireEvent.click(button);
|
||||
// Verify the request was made
|
||||
await waitFor(() => {
|
||||
expect(axiosMock.history.post[0].url).toMatch(goalUrl);
|
||||
// subscribe is turned on automatically
|
||||
expect(axiosMock.history.post[0].data).toMatch(`{"course_id":"${courseId}","days_per_week":3,"subscribed_to_reminders":true}`);
|
||||
// verify that the additional info about subscriptions shows up
|
||||
expect(screen.queryByText(messages.goalReminderDetail.defaultMessage)).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByLabelText(messages.setGoalReminder.defaultMessage)).toBeEnabled();
|
||||
|
||||
// Click on subscribe to reminders
|
||||
const subscriptionSwitch = await screen.getByRole('switch', { name: messages.setGoalReminder.defaultMessage });
|
||||
expect(subscriptionSwitch).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(subscriptionSwitch);
|
||||
await waitFor(() => {
|
||||
expect(axiosMock.history.post[1].url).toMatch(goalUrl);
|
||||
expect(axiosMock.history.post[1].data)
|
||||
.toMatch(`{"course_id":"${courseId}","days_per_week":3,"subscribed_to_reminders":false}`);
|
||||
});
|
||||
|
||||
// verify that the additional info about subscriptions gets hidden
|
||||
expect(screen.queryByText(messages.goalReminderDetail.defaultMessage)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Course Handouts', () => {
|
||||
it('renders title when handouts are available', async () => {
|
||||
await fetchAndRender();
|
||||
@@ -503,6 +437,35 @@ describe('Outline Tab', () => {
|
||||
await fetchAndRender();
|
||||
expect(screen.queryByRole('heading', { name: 'Course Tools' })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('analytics sent when upgrade link clicked', async () => {
|
||||
await fetchAndRender();
|
||||
expect(screen.getByRole('heading', { name: 'Course Tools' })).toBeInTheDocument();
|
||||
sendTrackEvent.mockClear();
|
||||
sendTrackingLogEvent.mockClear();
|
||||
|
||||
const upgradeLink = screen.getByRole('link', { name: 'Upgrade to Verified' });
|
||||
fireEvent.click(upgradeLink);
|
||||
|
||||
expect(sendTrackEvent).toHaveBeenCalledTimes(1);
|
||||
expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.ecommerce.upsell_links_clicked', {
|
||||
org_key: 'edX',
|
||||
courserun_key: courseId,
|
||||
linkCategory: '(none)',
|
||||
linkName: 'course_home_course_tools',
|
||||
linkType: 'link',
|
||||
pageName: 'course_home',
|
||||
});
|
||||
|
||||
expect(sendTrackingLogEvent).toHaveBeenCalledTimes(1);
|
||||
expect(sendTrackingLogEvent).toHaveBeenCalledWith('edx.course.tool.accessed', {
|
||||
org_key: 'edX',
|
||||
courserun_key: courseId,
|
||||
course_id: courseId,
|
||||
is_staff: false,
|
||||
tool_name: 'edx.tool.verified_upgrade',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Alert List', () => {
|
||||
@@ -558,35 +521,32 @@ describe('Outline Tab', () => {
|
||||
});
|
||||
|
||||
describe('Access Expiration Alert', () => {
|
||||
it('renders page banner on masquerade', async () => {
|
||||
setMetadata({ is_enrolled: true, original_user_is_staff: true });
|
||||
it('has special masquerade text', async () => {
|
||||
setTabData({
|
||||
access_expiration: {
|
||||
expiration_date: '2020-01-01T12:00:00Z',
|
||||
masquerading_expired_course: true,
|
||||
upgrade_deadline: null,
|
||||
upgrade_url: null,
|
||||
},
|
||||
});
|
||||
await executeThunk(thunks.fetchOutlineTab(courseId), store.dispatch);
|
||||
await act(async () => render(<LoadedTabPage courseId={courseId} activeTabSlug="outline">...</LoadedTabPage>, { store }));
|
||||
const instructorToolbar = await screen.getByTestId('instructor-toolbar');
|
||||
expect(instructorToolbar).toBeInTheDocument();
|
||||
expect(screen.getByText('This learner no longer has access to this course. Their access expired on', { exact: false })).toBeInTheDocument();
|
||||
expect(screen.getByText('1/1/2020')).toBeInTheDocument();
|
||||
await fetchAndRender();
|
||||
const check = await screen.queryByText('This learner does not have access to this course.', { exact: false });
|
||||
expect(check).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not render banner when not masquerading', async () => {
|
||||
setMetadata({ is_enrolled: true, original_user_is_staff: true });
|
||||
it('does not have special masquerade text', async () => {
|
||||
setTabData({
|
||||
access_expiration: {
|
||||
expiration_date: '2020-01-01T12:00:00Z',
|
||||
masquerading_expired_course: false,
|
||||
upgrade_deadline: null,
|
||||
upgrade_url: null,
|
||||
},
|
||||
});
|
||||
await executeThunk(thunks.fetchOutlineTab(courseId), store.dispatch);
|
||||
await act(async () => render(<LoadedTabPage courseId={courseId} activeTabSlug="outline">...</LoadedTabPage>, { store }));
|
||||
const instructorToolbar = await screen.getByTestId('instructor-toolbar');
|
||||
expect(instructorToolbar).toBeInTheDocument();
|
||||
expect(screen.queryByText('This learner no longer has access to this course. Their access expired on', { exact: false })).not.toBeInTheDocument();
|
||||
await fetchAndRender();
|
||||
const check = await screen.queryByText('This learner does not have access to this course.', { exact: false });
|
||||
expect(check).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -595,7 +555,16 @@ describe('Outline Tab', () => {
|
||||
it('appears several days out', async () => {
|
||||
const startDate = new Date();
|
||||
startDate.setDate(startDate.getDate() + 100);
|
||||
setMetadata({ is_enrolled: true, start: '2999-01-01T00:00:00Z' });
|
||||
setMetadata({ is_enrolled: true });
|
||||
setTabData({}, {
|
||||
date_blocks: [
|
||||
{
|
||||
date_type: 'course-start-date',
|
||||
date: startDate.toISOString(),
|
||||
title: 'Start',
|
||||
},
|
||||
],
|
||||
});
|
||||
await fetchAndRender();
|
||||
const node = await screen.findByText('Course starts', { exact: false });
|
||||
expect(node.textContent).toMatch(/.* on .*/); // several days away uses "on" before date
|
||||
@@ -604,7 +573,16 @@ describe('Outline Tab', () => {
|
||||
it('appears today', async () => {
|
||||
const startDate = new Date();
|
||||
startDate.setHours(startDate.getHours() + 1);
|
||||
setMetadata({ is_enrolled: true, start: startDate });
|
||||
setMetadata({ is_enrolled: true });
|
||||
setTabData({}, {
|
||||
date_blocks: [
|
||||
{
|
||||
date_type: 'course-start-date',
|
||||
date: startDate.toISOString(),
|
||||
title: 'Start',
|
||||
},
|
||||
],
|
||||
});
|
||||
await fetchAndRender();
|
||||
const node = await screen.findByText('Course starts', { exact: false });
|
||||
expect(node.textContent).toMatch(/.* at .*/); // same day uses "at" before date
|
||||
|
||||
@@ -28,6 +28,7 @@ function Section({
|
||||
courseBlocks: {
|
||||
sequences,
|
||||
},
|
||||
shortLinkFeatureFlag,
|
||||
} = useModel('outline', courseId);
|
||||
|
||||
const [open, setOpen] = useState(defaultOpen);
|
||||
@@ -39,6 +40,28 @@ function Section({
|
||||
useEffect(() => {
|
||||
setOpen(defaultOpen);
|
||||
}, []);
|
||||
let sequenceLinks;
|
||||
if (shortLinkFeatureFlag) {
|
||||
sequenceLinks = sequenceIds.map((sequenceId, index) => (
|
||||
<SequenceLink
|
||||
key={sequenceId}
|
||||
id={sequences[sequenceId].hash_key}
|
||||
courseId={courseId}
|
||||
sequence={sequences[sequenceId]}
|
||||
first={index === 0}
|
||||
/>
|
||||
));
|
||||
} else {
|
||||
sequenceLinks = sequenceIds.map((sequenceId, index) => (
|
||||
<SequenceLink
|
||||
key={sequenceId}
|
||||
id={sequenceId}
|
||||
courseId={courseId}
|
||||
sequence={sequences[sequenceId]}
|
||||
first={index === 0}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
||||
const sectionTitle = (
|
||||
<div className="row w-100 m-0">
|
||||
@@ -96,15 +119,7 @@ function Section({
|
||||
)}
|
||||
>
|
||||
<ol className="list-unstyled">
|
||||
{sequenceIds.map((sequenceId, index) => (
|
||||
<SequenceLink
|
||||
key={sequenceId}
|
||||
id={sequenceId}
|
||||
courseId={courseId}
|
||||
sequence={sequences[sequenceId]}
|
||||
first={index === 0}
|
||||
/>
|
||||
))}
|
||||
{sequenceLinks}
|
||||
</ol>
|
||||
</Collapsible>
|
||||
</li>
|
||||
|
||||
@@ -33,7 +33,9 @@ function SequenceLink({
|
||||
title,
|
||||
} = sequence;
|
||||
const {
|
||||
userTimezone,
|
||||
datesWidget: {
|
||||
userTimezone,
|
||||
},
|
||||
} = useModel('outline', courseId);
|
||||
const {
|
||||
canLoadCourseware,
|
||||
@@ -44,7 +46,7 @@ function SequenceLink({
|
||||
// canLoadCourseware is true if the Courseware MFE is enabled, false otherwise
|
||||
const coursewareUrl = (
|
||||
canLoadCourseware
|
||||
? <Link to={`/course/${courseId}/${id}`}>{title}</Link>
|
||||
? <Link to={`/c/${courseId}/${id}`}>{title}</Link>
|
||||
: <Hyperlink destination={legacyWebUrl}>{title}</Hyperlink>
|
||||
);
|
||||
const displayTitle = showLink ? coursewareUrl : title;
|
||||
|
||||
@@ -39,11 +39,11 @@ function useCertificateStatusAlert(courseId) {
|
||||
const {
|
||||
datesWidget: {
|
||||
courseDateBlocks,
|
||||
userTimezone,
|
||||
},
|
||||
certData,
|
||||
hasEnded,
|
||||
userHasPassingGrade,
|
||||
userTimezone,
|
||||
enrollmentMode,
|
||||
} = useModel('outline', courseId);
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ export function useCourseEndAlert(courseId) {
|
||||
const {
|
||||
datesWidget: {
|
||||
courseDateBlocks,
|
||||
userTimezone,
|
||||
},
|
||||
userTimezone,
|
||||
} = useModel('outline', courseId);
|
||||
|
||||
const endBlock = courseDateBlocks.find(b => b.dateType === 'course-end-date');
|
||||
|
||||
@@ -9,19 +9,13 @@ import {
|
||||
import { Alert } from '@edx/paragon';
|
||||
import { Info } from '@edx/paragon/icons';
|
||||
|
||||
import { useModel } from '../../generic/model-store';
|
||||
|
||||
const DAY_MS = 24 * 60 * 60 * 1000; // in ms
|
||||
|
||||
function CourseStartAlert({ payload }) {
|
||||
const {
|
||||
courseId,
|
||||
} = payload;
|
||||
|
||||
const {
|
||||
start: startDate,
|
||||
startDate,
|
||||
userTimezone,
|
||||
} = useModel('courseHomeMeta', courseId);
|
||||
} = payload;
|
||||
|
||||
const timezoneFormatArgs = userTimezone ? { timeZone: userTimezone } : {};
|
||||
|
||||
@@ -93,7 +87,8 @@ function CourseStartAlert({ payload }) {
|
||||
|
||||
CourseStartAlert.propTypes = {
|
||||
payload: PropTypes.shape({
|
||||
courseId: PropTypes.string,
|
||||
startDate: PropTypes.string,
|
||||
userTimezone: PropTypes.string,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { useAlert } from '../../../../generic/user-messages';
|
||||
import { useModel } from '../../../../generic/model-store';
|
||||
|
||||
const CourseStartAlert = React.lazy(() => import('./CourseStartAlert'));
|
||||
|
||||
function useCourseStartAlert(courseId) {
|
||||
const {
|
||||
isEnrolled,
|
||||
} = useModel('courseHomeMeta', courseId);
|
||||
const {
|
||||
datesWidget: {
|
||||
courseDateBlocks,
|
||||
userTimezone,
|
||||
},
|
||||
} = useModel('outline', courseId);
|
||||
|
||||
const startBlock = courseDateBlocks.find(b => b.dateType === 'course-start-date');
|
||||
const delta = startBlock ? new Date(startBlock.date) - new Date() : 0;
|
||||
const isVisible = isEnrolled && startBlock && delta > 0;
|
||||
const payload = {
|
||||
startDate: startBlock && startBlock.date,
|
||||
userTimezone,
|
||||
};
|
||||
|
||||
useAlert(isVisible, {
|
||||
code: 'clientCourseStartAlert',
|
||||
payload: useMemo(() => payload, Object.values(payload).sort()),
|
||||
topic: 'outline-course-alerts',
|
||||
});
|
||||
|
||||
return {
|
||||
clientCourseStartAlert: CourseStartAlert,
|
||||
};
|
||||
}
|
||||
|
||||
export default useCourseStartAlert;
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './hooks';
|
||||
@@ -66,15 +66,6 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Open',
|
||||
description: 'A button to open the given section of the course outline',
|
||||
},
|
||||
startBlurb: {
|
||||
id: 'learning.outline.startBlurb',
|
||||
defaultMessage: 'Begin your course today',
|
||||
},
|
||||
resumeBlurb: {
|
||||
id: 'learning.outline.resumeBlurb',
|
||||
defaultMessage: 'Pick up where you left off',
|
||||
description: 'Text describing to the learner that they can return to the last section they visited within the course.',
|
||||
},
|
||||
resume: {
|
||||
id: 'learning.outline.resume',
|
||||
defaultMessage: 'Resume course',
|
||||
@@ -83,14 +74,6 @@ const messages = defineMessages({
|
||||
id: 'learning.outline.setGoal',
|
||||
defaultMessage: 'To start, set a course goal by selecting the option below that best describes your learning plan.',
|
||||
},
|
||||
setWeeklyGoal: {
|
||||
id: 'learning.outline.setWeeklyGoal',
|
||||
defaultMessage: 'Set a weekly learning goal',
|
||||
},
|
||||
setWeeklyGoalDetail: {
|
||||
id: 'learning.outline.setWeeklyGoalDetail',
|
||||
defaultMessage: 'Setting a goal motivates you to finish the course. You can always change it later.',
|
||||
},
|
||||
start: {
|
||||
id: 'learning.outline.start',
|
||||
defaultMessage: 'Start Course',
|
||||
@@ -129,46 +112,6 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Welcome to',
|
||||
description: 'This precedes the title of the course',
|
||||
},
|
||||
setLearningGoalButtonScreenReaderText: {
|
||||
id: 'learning.outline.goalButton.casual.title',
|
||||
defaultMessage: 'Set a learning goal style.',
|
||||
description: 'screen reader text informing learner they can select an intensity of learning goal',
|
||||
},
|
||||
casualGoalButtonTitle: {
|
||||
id: 'learning.outline.goalButton.screenReader.text',
|
||||
defaultMessage: 'Casual',
|
||||
description: 'A very short description of the least intense of three learning goals',
|
||||
},
|
||||
casualGoalButtonText: {
|
||||
id: 'learning.outline.goalButton.casual.text',
|
||||
defaultMessage: '1 day a week',
|
||||
},
|
||||
regularGoalButtonTitle: {
|
||||
id: 'learning.outline.goalButton.regular.title',
|
||||
defaultMessage: 'Regular',
|
||||
description: 'A very short description of the middle option of three learning goals, Casual, Regular and Intense',
|
||||
},
|
||||
regularGoalButtonText: {
|
||||
id: 'learning.outline.goalButton.regular.text',
|
||||
defaultMessage: '3 days a week',
|
||||
},
|
||||
intenseGoalButtonTitle: {
|
||||
id: 'learning.outline.goalButton.intense.title',
|
||||
defaultMessage: 'Intense',
|
||||
description: 'A very short description of the most intensive option of three learning goals, Casual, Regular and Intense',
|
||||
},
|
||||
intenseGoalButtonText: {
|
||||
id: 'learning.outline.goalButton.intense.text',
|
||||
defaultMessage: '5 days a week',
|
||||
},
|
||||
setGoalReminder: {
|
||||
id: 'learning.outline.setGoalReminder',
|
||||
defaultMessage: 'Set a goal reminder',
|
||||
},
|
||||
goalReminderDetail: {
|
||||
id: 'learning.outline.goalReminderDetail',
|
||||
defaultMessage: 'If we notice you’re not quite at your goal, we’ll send you an email reminder.',
|
||||
},
|
||||
proctoringInfoPanel: {
|
||||
id: 'learning.proctoringPanel.header',
|
||||
defaultMessage: 'This course contains proctored exams',
|
||||
@@ -273,15 +216,6 @@ const messages = defineMessages({
|
||||
id: 'learning.proctoringPanel.reviewRequirementsButton',
|
||||
defaultMessage: 'Review instructions and system requirements',
|
||||
},
|
||||
proctoringOnboardingButtonPastDue: {
|
||||
id: 'learning.proctoringPanel.onboardingButtonPastDue',
|
||||
defaultMessage: 'Onboarding Past Due',
|
||||
},
|
||||
accountActivationAlertTitle: {
|
||||
id: 'account-activation.alert.title',
|
||||
defaultMessage: 'Activate your account so you can log back in',
|
||||
description: 'Title for account activation alert which is shown after the registration',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
|
||||
@@ -13,13 +13,11 @@ function CourseDates({
|
||||
/** [MM-P2P] Experiment */
|
||||
mmp2p,
|
||||
}) {
|
||||
const {
|
||||
userTimezone,
|
||||
} = useModel('courseHomeMeta', courseId);
|
||||
const {
|
||||
datesWidget: {
|
||||
courseDateBlocks,
|
||||
datesTabLink,
|
||||
userTimezone,
|
||||
},
|
||||
} = useModel('outline', courseId);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { sendTrackingLogEvent } from '@edx/frontend-platform/analytics';
|
||||
import { sendTrackEvent, sendTrackingLogEvent } from '@edx/frontend-platform/analytics';
|
||||
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
@@ -36,6 +36,16 @@ function CourseTools({ courseId, intl }) {
|
||||
is_staff: administrator,
|
||||
tool_name: analyticsId,
|
||||
});
|
||||
|
||||
if (analyticsId === 'edx.tool.verified_upgrade') {
|
||||
sendTrackEvent('edx.bi.ecommerce.upsell_links_clicked', {
|
||||
...eventProperties,
|
||||
linkCategory: '(none)',
|
||||
linkName: 'course_home_course_tools',
|
||||
linkType: 'link',
|
||||
pageName: 'course_home',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const renderIcon = (iconClasses) => {
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
function FlagButton({
|
||||
ButtonIcon,
|
||||
srText,
|
||||
title,
|
||||
text,
|
||||
isEnabled,
|
||||
handleSelect,
|
||||
}) {
|
||||
const [isSelected, setIsSelected] = useState(false);
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={classNames('col flex-grow-1 p-3 border border-light rounded bg-white', { 'border-dark': isEnabled || isSelected })}
|
||||
onMouseEnter={() => setIsSelected(true)}
|
||||
onMouseLeave={() => setIsSelected(false)}
|
||||
onClick={() => handleSelect()}
|
||||
>
|
||||
<div className=" justify-content-center">
|
||||
{ButtonIcon}
|
||||
</div>
|
||||
<span className="sr-only sr-only-focusable">{srText}</span>
|
||||
<div className="text-center small">
|
||||
{title}
|
||||
</div>
|
||||
<div className="text-center micro">
|
||||
{text}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
FlagButton.propTypes = {
|
||||
ButtonIcon: PropTypes.element.isRequired,
|
||||
srText: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
text: PropTypes.string,
|
||||
isEnabled: PropTypes.bool,
|
||||
handleSelect: PropTypes.func.isRequired,
|
||||
};
|
||||
FlagButton.defaultProps = {
|
||||
isEnabled: false,
|
||||
text: '',
|
||||
};
|
||||
|
||||
export default FlagButton;
|
||||
@@ -9,12 +9,10 @@ import messages from '../messages';
|
||||
import { getProctoringInfoData } from '../../data/api';
|
||||
|
||||
function ProctoringInfoPanel({ courseId, username, intl }) {
|
||||
const [link, setLink] = useState('');
|
||||
const [onboardingPastDue, setOnboardingPastDue] = useState(false);
|
||||
const [showInfoPanel, setShowInfoPanel] = useState(false);
|
||||
const [status, setStatus] = useState('');
|
||||
const [readableStatus, setReadableStatus] = useState('');
|
||||
const [link, setLink] = useState('');
|
||||
const [releaseDate, setReleaseDate] = useState(null);
|
||||
const [readableStatus, setReadableStatus] = useState('');
|
||||
|
||||
const readableStatuses = {
|
||||
notStarted: 'notStarted',
|
||||
@@ -79,10 +77,6 @@ function ProctoringInfoPanel({ courseId, username, intl }) {
|
||||
.then(
|
||||
response => {
|
||||
if (response) {
|
||||
if (Object.keys(response).length > 0) {
|
||||
setShowInfoPanel(true);
|
||||
}
|
||||
|
||||
setStatus(response.onboarding_status);
|
||||
setLink(response.onboarding_link);
|
||||
const expirationDate = response.expiration_date;
|
||||
@@ -92,54 +86,14 @@ function ProctoringInfoPanel({ courseId, username, intl }) {
|
||||
setReadableStatus(getReadableStatusClass(response.onboarding_status));
|
||||
}
|
||||
setReleaseDate(new Date(response.onboarding_release_date));
|
||||
setOnboardingPastDue(response.onboarding_past_due);
|
||||
}
|
||||
},
|
||||
);
|
||||
}, []);
|
||||
|
||||
let onboardingExamButton = null;
|
||||
|
||||
if (isNotYetReleased(releaseDate)) {
|
||||
onboardingExamButton = (
|
||||
<Button variant="secondary" block disabled aria-disabled="true">
|
||||
{intl.formatMessage(
|
||||
messages.proctoringOnboardingButtonNotOpen,
|
||||
{
|
||||
releaseDate: intl.formatDate(releaseDate, {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric',
|
||||
}),
|
||||
},
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
} else if (onboardingPastDue) {
|
||||
onboardingExamButton = (
|
||||
<Button variant="secondary" block disabled aria-disabled="true">
|
||||
{intl.formatMessage(messages.proctoringOnboardingButtonPastDue)}
|
||||
</Button>
|
||||
);
|
||||
} else if (!isNotYetReleased(releaseDate)) {
|
||||
if (readableStatus === readableStatuses.otherCourseApproved) {
|
||||
onboardingExamButton = (
|
||||
<Button variant="primary" block href={link}>
|
||||
{intl.formatMessage(messages.proctoringOnboardingPracticeButton)}
|
||||
</Button>
|
||||
);
|
||||
} else if (readableStatus !== readableStatuses.otherCourseApproved) {
|
||||
onboardingExamButton = (
|
||||
<Button variant="primary" block href={link}>
|
||||
{intl.formatMessage(messages.proctoringOnboardingButton)}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{ showInfoPanel && (
|
||||
{ link && (
|
||||
<section className={`mb-4 p-3 outline-sidebar-proctoring-panel ${getBorderClass()}`}>
|
||||
<h2 className="h4" id="outline-sidebar-upgrade-header">{intl.formatMessage(messages.proctoringInfoPanel)}</h2>
|
||||
<div>
|
||||
@@ -160,17 +114,50 @@ function ProctoringInfoPanel({ courseId, username, intl }) {
|
||||
<>
|
||||
<p>
|
||||
{isNotYetSubmitted(status) && (
|
||||
intl.formatMessage(messages.proctoringPanelGeneralInfo)
|
||||
<>
|
||||
{intl.formatMessage(messages.proctoringPanelGeneralInfo)}
|
||||
</>
|
||||
)}
|
||||
{!isNotYetSubmitted(status) && (
|
||||
intl.formatMessage(messages.proctoringPanelGeneralInfoSubmitted)
|
||||
<>
|
||||
{intl.formatMessage(messages.proctoringPanelGeneralInfoSubmitted)}
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
<p>{intl.formatMessage(messages.proctoringPanelGeneralTime)}</p>
|
||||
</>
|
||||
)}
|
||||
{isNotYetSubmitted(status) && (
|
||||
onboardingExamButton
|
||||
<>
|
||||
{!isNotYetReleased(releaseDate) && (
|
||||
<Button variant="primary" block href={link}>
|
||||
{readableStatus === readableStatuses.otherCourseApproved && (
|
||||
<>
|
||||
{intl.formatMessage(messages.proctoringOnboardingPracticeButton)}
|
||||
</>
|
||||
)}
|
||||
{readableStatus !== readableStatuses.otherCourseApproved && (
|
||||
<>
|
||||
{intl.formatMessage(messages.proctoringOnboardingButton)}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
{isNotYetReleased(releaseDate) && (
|
||||
<Button variant="secondary" block disabled aria-disabled="true">
|
||||
{intl.formatMessage(
|
||||
messages.proctoringOnboardingButtonNotOpen,
|
||||
{
|
||||
releaseDate: intl.formatDate(releaseDate, {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric',
|
||||
}),
|
||||
},
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Button variant="outline-primary" block href="https://support.edx.org/hc/en-us/sections/115004169247-Taking-Timed-and-Proctored-Exams">
|
||||
{intl.formatMessage(messages.proctoringReviewRequirementsButton)}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Button, Card } from '@edx/paragon';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import { sendTrackingLogEvent } from '@edx/frontend-platform/analytics';
|
||||
import messages from '../messages';
|
||||
import { useModel } from '../../../generic/model-store';
|
||||
|
||||
function StartOrResumeCourseCard({ intl }) {
|
||||
const {
|
||||
courseId,
|
||||
} = useSelector(state => state.courseHome);
|
||||
|
||||
const {
|
||||
org,
|
||||
} = useModel('courseHomeMeta', courseId);
|
||||
|
||||
const eventProperties = {
|
||||
org_key: org,
|
||||
courserun_key: courseId,
|
||||
};
|
||||
|
||||
const {
|
||||
resumeCourse: {
|
||||
hasVisitedCourse,
|
||||
url: resumeCourseUrl,
|
||||
},
|
||||
|
||||
} = useModel('outline', courseId);
|
||||
|
||||
const logResumeCourseClick = () => {
|
||||
sendTrackingLogEvent('edx.course.home.resume_course.clicked', {
|
||||
...eventProperties,
|
||||
event_type: hasVisitedCourse ? 'resume' : 'start',
|
||||
url: resumeCourseUrl,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="mb-3" data-testid="start-resume-card">
|
||||
<Card.Body>
|
||||
<div className="row w-100 m-0 ">
|
||||
<h2 className="h4 col-auto flex-grow-1">{intl.formatMessage(messages.startBlurb)}</h2>
|
||||
<div className="col col-auto p-0 justify-content-end">
|
||||
<Button
|
||||
variant="brand"
|
||||
block
|
||||
href={resumeCourseUrl}
|
||||
onClick={() => logResumeCourseClick()}
|
||||
>
|
||||
{hasVisitedCourse ? intl.formatMessage(messages.resume) : intl.formatMessage(messages.start)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
StartOrResumeCourseCard.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(StartOrResumeCourseCard);
|
||||
@@ -1,173 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Form, Card, Icon } from '@edx/paragon';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { Email } from '@edx/paragon/icons';
|
||||
import { ReactComponent as FlagIntenseIcon } from '@edx/paragon/icons/svg/flag.svg';
|
||||
import { ReactComponent as FlagCasualIcon } from './flag_outline.svg';
|
||||
import { ReactComponent as FlagRegularIcon } from './flag_gray.svg';
|
||||
import messages from '../messages';
|
||||
import FlagButton from './FlagButton';
|
||||
|
||||
import { saveWeeklyCourseGoal } from '../../data';
|
||||
|
||||
function WeeklyLearningGoalCard({
|
||||
daysPerWeek,
|
||||
subscribedToReminders,
|
||||
courseId,
|
||||
intl,
|
||||
}) {
|
||||
const [daysPerWeekGoal, setDaysPerWeekGoal] = useState(daysPerWeek);
|
||||
// eslint-disable-next-line react/prop-types
|
||||
const [isGetReminderSelected, setGetReminderSelected] = useState(subscribedToReminders);
|
||||
const weeklyLearningGoalLevels = {
|
||||
CASUAL: 3,
|
||||
REGULAR: 4,
|
||||
INTENSE: 5,
|
||||
};
|
||||
Object.freeze(weeklyLearningGoalLevels);
|
||||
|
||||
function handleSelect(days) {
|
||||
// Set the subscription button if this is the first time selecting a goal
|
||||
const selectReminders = daysPerWeekGoal === null ? true : isGetReminderSelected;
|
||||
setGetReminderSelected(selectReminders);
|
||||
setDaysPerWeekGoal(days);
|
||||
saveWeeklyCourseGoal(courseId, days, selectReminders);
|
||||
}
|
||||
|
||||
function handleSubscribeToReminders(event) {
|
||||
const isGetReminderChecked = event.target.checked;
|
||||
setGetReminderSelected(isGetReminderChecked);
|
||||
saveWeeklyCourseGoal(courseId, daysPerWeekGoal, isGetReminderChecked);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="row w-100 m-0 p-0">
|
||||
<Card className="mb-3" data-testid="weekly-learning-goal-card">
|
||||
<Card.Body>
|
||||
<Card.Title>
|
||||
<h4 className="m-0">{intl.formatMessage(messages.setWeeklyGoal)}</h4>
|
||||
</Card.Title>
|
||||
<Card.Text>
|
||||
{intl.formatMessage(messages.setWeeklyGoalDetail)}
|
||||
</Card.Text>
|
||||
<div
|
||||
className="row w-100 m-0 p-0 justify-content-around"
|
||||
>
|
||||
<label
|
||||
htmlFor={weeklyLearningGoalLevels.CASUAL}
|
||||
className="col-auto col-md-12 col-xl-auto m-0 p-0 pb-md-3 pb-xl-0"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
id={weeklyLearningGoalLevels.CASUAL}
|
||||
name="learningGoal"
|
||||
radioGroup="learningGoal"
|
||||
value={weeklyLearningGoalLevels.CASUAL}
|
||||
onChange={() => handleSelect(weeklyLearningGoalLevels.CASUAL)}
|
||||
tabIndex="0"
|
||||
checked={weeklyLearningGoalLevels.CASUAL === daysPerWeekGoal}
|
||||
className="position-absolute invisible"
|
||||
/>
|
||||
<FlagButton
|
||||
ButtonIcon={<FlagCasualIcon />}
|
||||
srText={intl.formatMessage(messages.setLearningGoalButtonScreenReaderText)}
|
||||
title={intl.formatMessage(messages.casualGoalButtonTitle)}
|
||||
text={intl.formatMessage(messages.casualGoalButtonText)}
|
||||
isEnabled={weeklyLearningGoalLevels.CASUAL === daysPerWeekGoal}
|
||||
handleSelect={() => { handleSelect(weeklyLearningGoalLevels.CASUAL); }}
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
htmlFor={weeklyLearningGoalLevels.REGULAR}
|
||||
className="col-auto col-md-12 col-xl-auto m-0 p-0 pb-md-3 pb-xl-0"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
id={weeklyLearningGoalLevels.REGULAR}
|
||||
name="learningGoal"
|
||||
radioGroup="learningGoal"
|
||||
value={weeklyLearningGoalLevels.REGULAR}
|
||||
onChange={() => handleSelect(weeklyLearningGoalLevels.REGULAR)}
|
||||
tabIndex="-1"
|
||||
checked={weeklyLearningGoalLevels.REGULAR === daysPerWeekGoal}
|
||||
className="position-absolute invisible"
|
||||
/>
|
||||
|
||||
<FlagButton
|
||||
ButtonIcon={<FlagRegularIcon />}
|
||||
srText={intl.formatMessage(messages.setLearningGoalButtonScreenReaderText)}
|
||||
title={intl.formatMessage(messages.regularGoalButtonTitle)}
|
||||
text={intl.formatMessage(messages.regularGoalButtonText)}
|
||||
isEnabled={weeklyLearningGoalLevels.REGULAR === daysPerWeekGoal}
|
||||
handleSelect={() => { handleSelect(weeklyLearningGoalLevels.REGULAR); }}
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
htmlFor={weeklyLearningGoalLevels.INTENSE}
|
||||
className="col-auto col-md-12 col-xl-auto m-0 p-0 pb-md-3 pb-xl-0"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
id={weeklyLearningGoalLevels.INTENSE}
|
||||
name="learningGoal"
|
||||
radioGroup="learningGoal"
|
||||
value={weeklyLearningGoalLevels.INTENSE}
|
||||
onChange={() => handleSelect(weeklyLearningGoalLevels.INTENSE)}
|
||||
tabIndex="-1"
|
||||
checked={weeklyLearningGoalLevels.INTENSE === daysPerWeekGoal}
|
||||
className="position-absolute invisible"
|
||||
/>
|
||||
|
||||
<FlagButton
|
||||
ButtonIcon={<FlagIntenseIcon />}
|
||||
srText={intl.formatMessage(messages.setLearningGoalButtonScreenReaderText)}
|
||||
title={intl.formatMessage(messages.intenseGoalButtonTitle)}
|
||||
text={intl.formatMessage(messages.intenseGoalButtonText)}
|
||||
isEnabled={weeklyLearningGoalLevels.INTENSE === daysPerWeekGoal}
|
||||
handleSelect={() => { handleSelect(weeklyLearningGoalLevels.INTENSE); }}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className="row p-3">
|
||||
<Form.Switch
|
||||
checked={isGetReminderSelected}
|
||||
onChange={(event) => handleSubscribeToReminders(event)}
|
||||
disabled={!daysPerWeekGoal}
|
||||
>
|
||||
{intl.formatMessage(messages.setGoalReminder)}
|
||||
</Form.Switch>
|
||||
</div>
|
||||
</Card.Body>
|
||||
{isGetReminderSelected && (
|
||||
<Card.Footer className="border-0 px-2.5">
|
||||
<div className="row w-100 m-0 small align-center">
|
||||
<div className="d-flex align-items-center pr-1.5">
|
||||
<Icon src={Email} />
|
||||
</div>
|
||||
<div className="col align-center">
|
||||
{intl.formatMessage(messages.goalReminderDetail)}
|
||||
</div>
|
||||
</div>
|
||||
</Card.Footer>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
WeeklyLearningGoalCard.propTypes = {
|
||||
daysPerWeek: PropTypes.number,
|
||||
subscribedToReminders: PropTypes.bool,
|
||||
courseId: PropTypes.string.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
WeeklyLearningGoalCard.defaultProps = {
|
||||
daysPerWeek: null,
|
||||
subscribedToReminders: false,
|
||||
};
|
||||
export default injectIntl(WeeklyLearningGoalCard);
|
||||
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="15"
|
||||
height="17"
|
||||
viewBox="0 0 15 17"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg11"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M9.4 2L9 0H0V17H2V10H7.6L8 12H15V2H9.4ZM13 10H9.64L9.24 8H2V2H7.36L7.76 4H13V10Z"
|
||||
fill="#002B2B"
|
||||
id="path9" />
|
||||
<path
|
||||
style="fill:#808080;fill-rule:evenodd;stroke-width:0.0150977"
|
||||
d="M 9.6594698,9.9871226 C 9.6577909,9.9829707 9.5654776,9.5311723 9.4543296,8.9831261 L 9.2522415,7.9866785 5.6376662,7.9790074 2.0230906,7.9713362 V 4.9970494 2.0227625 l 2.6636151,0.00771 2.6636151,0.00771 0.1968204,0.9888987 0.1968205,0.9888988 h 2.6200263 2.620026 v 2.9893428 2.9893428 h -1.660746 c -0.91341,0 -1.6621194,-0.0034 -1.6637982,-0.00755 z"
|
||||
id="path302" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 801 B |
@@ -1,3 +0,0 @@
|
||||
<svg width="15" height="17" viewBox="0 0 15 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.4 2L9 0H0V17H2V10H7.6L8 12H15V2H9.4ZM13 10H9.64L9.24 8H2V2H7.36L7.76 4H13V10Z" fill="#002B2B"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 211 B |
@@ -12,7 +12,6 @@ import { appendBrowserTimezoneToUrl, executeThunk } from '../../utils';
|
||||
import * as thunks from '../data/thunks';
|
||||
import initializeStore from '../../store';
|
||||
import ProgressTab from './ProgressTab';
|
||||
import LoadedTabPage from '../../tab-page/LoadedTabPage';
|
||||
|
||||
initializeMockApp();
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
@@ -21,10 +20,9 @@ describe('Progress Tab', () => {
|
||||
let axiosMock;
|
||||
|
||||
const courseId = 'course-v1:edX+Test+run';
|
||||
let courseMetadataUrl = `${getConfig().LMS_BASE_URL}/api/course_home/course_metadata/${courseId}`;
|
||||
let courseMetadataUrl = `${getConfig().LMS_BASE_URL}/api/course_home/v1/course_metadata/${courseId}`;
|
||||
courseMetadataUrl = appendBrowserTimezoneToUrl(courseMetadataUrl);
|
||||
const progressUrl = new RegExp(`${getConfig().LMS_BASE_URL}/api/course_home/progress/*`);
|
||||
const masqueradeUrl = `${getConfig().LMS_BASE_URL}/courses/${courseId}/masquerade`;
|
||||
const progressUrl = new RegExp(`${getConfig().LMS_BASE_URL}/api/course_home/v1/progress/*`);
|
||||
|
||||
const store = initializeStore();
|
||||
const defaultMetadata = Factory.build('courseHomeMetadata', { id: courseId });
|
||||
@@ -51,7 +49,6 @@ describe('Progress Tab', () => {
|
||||
// Set defaults for network requests
|
||||
axiosMock.onGet(courseMetadataUrl).reply(200, defaultMetadata);
|
||||
axiosMock.onGet(progressUrl).reply(200, defaultTabData);
|
||||
axiosMock.onGet(masqueradeUrl).reply(200, { success: true });
|
||||
|
||||
logUnhandledRequests(axiosMock);
|
||||
});
|
||||
@@ -1189,64 +1186,6 @@ describe('Progress Tab', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Access expiration masquerade banner', () => {
|
||||
it('renders banner when masquerading as a user', async () => {
|
||||
setMetadata({ is_enrolled: true, original_user_is_staff: true });
|
||||
setTabData({
|
||||
access_expiration: {
|
||||
expiration_date: '2020-01-01T12:00:00Z',
|
||||
masquerading_expired_course: true,
|
||||
},
|
||||
});
|
||||
await executeThunk(thunks.fetchProgressTab(courseId), store.dispatch);
|
||||
await act(async () => render(<LoadedTabPage courseId={courseId} activeTabSlug="progress">...</LoadedTabPage>, { store }));
|
||||
expect(screen.getByTestId('instructor-toolbar')).toBeInTheDocument();
|
||||
expect(screen.getByText('This learner no longer has access to this course. Their access expired on', { exact: false })).toBeInTheDocument();
|
||||
expect(screen.getByText('1/1/2020')).toBeInTheDocument();
|
||||
});
|
||||
it('does not render banner when not masquerading', async () => {
|
||||
setMetadata({ is_enrolled: true, original_user_is_staff: true });
|
||||
setTabData({
|
||||
access_expiration: {
|
||||
expiration_date: '2020-01-01T12:00:00Z',
|
||||
masquerading_expired_course: false,
|
||||
},
|
||||
});
|
||||
await executeThunk(thunks.fetchProgressTab(courseId), store.dispatch);
|
||||
await act(async () => render(<LoadedTabPage courseId={courseId} activeTabSlug="progress">...</LoadedTabPage>, { store }));
|
||||
expect(screen.queryByText('This learner no longer has access to this course. Their access expired on', { exact: false })).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('1/1/2020')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Course start masquerade banner', () => {
|
||||
it('renders banner when masquerading as a user', async () => {
|
||||
setMetadata({
|
||||
is_enrolled: true,
|
||||
original_user_is_staff: true,
|
||||
is_staff: false,
|
||||
start: '2999-01-01T00:00:00Z',
|
||||
});
|
||||
await executeThunk(thunks.fetchProgressTab(courseId), store.dispatch);
|
||||
await act(async () => render(<LoadedTabPage courseId={courseId} activeTabSlug="progress">...</LoadedTabPage>, { store }));
|
||||
expect(screen.getByTestId('instructor-toolbar')).toBeInTheDocument();
|
||||
expect(screen.getByText('This learner does not yet have access to this course. The course starts on', { exact: false })).toBeInTheDocument();
|
||||
expect(screen.getByText('1/1/2999')).toBeInTheDocument();
|
||||
});
|
||||
it('does not render banner when not masquerading', async () => {
|
||||
setMetadata({
|
||||
is_enrolled: true,
|
||||
original_user_is_staff: true,
|
||||
is_staff: true,
|
||||
start: '2999-01-01T00:00:00Z',
|
||||
});
|
||||
await executeThunk(thunks.fetchProgressTab(courseId), store.dispatch);
|
||||
await act(async () => render(<LoadedTabPage courseId={courseId} activeTabSlug="progress">...</LoadedTabPage>, { store }));
|
||||
expect(screen.queryByText('This learner does not yet have access to this course. The course starts on', { exact: false })).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('1/1/2999')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Viewing progress page of other students by changing url', () => {
|
||||
it('Changing the url changes the header', async () => {
|
||||
setMetadata({ is_enrolled: true });
|
||||
|
||||
@@ -130,7 +130,7 @@ function CertificateStatus({ intl }) {
|
||||
<FormattedMessage
|
||||
id="progress.certificateStatus.downloadableBody"
|
||||
defaultMessage="
|
||||
Showcase your accomplishment on LinkedIn or your resumé today.
|
||||
Showcase your accomplishment on LinkedIn or your resume today.
|
||||
You can download your certificate now and access it any time from your
|
||||
{dashboardLink} and {profileLink}."
|
||||
values={{ dashboardLink, profileLink }}
|
||||
|
||||
@@ -47,7 +47,7 @@ const messages = defineMessages({
|
||||
},
|
||||
downloadableBody: {
|
||||
id: 'progress.certificateStatus.downloadableBody',
|
||||
defaultMessage: 'Showcase your accomplishment on LinkedIn or your resumé today. You can download your certificate now and access it any time from your Dashboard and Profile.',
|
||||
defaultMessage: 'Showcase your accomplishment on LinkedIn or your resume today. You can download your certificate now and access it any time from your Dashboard and Profile.',
|
||||
},
|
||||
downloadableButton: {
|
||||
id: 'progress.certificateStatus.downloadableButton',
|
||||
|
||||
@@ -51,7 +51,7 @@ function DetailedGradesTable({ intl }) {
|
||||
Header: `${intl.formatMessage(messages.score)}`,
|
||||
accessor: 'score',
|
||||
headerClassName: 'justify-content-end h5 mb-0',
|
||||
cellClassName: 'align-top text-right small',
|
||||
cellClassName: 'float-right text-right small',
|
||||
},
|
||||
]}
|
||||
>
|
||||
|
||||
@@ -52,17 +52,7 @@ function SubsectionTitleCell({ intl, subsection }) {
|
||||
<Collapsible.Visible whenOpen><Icon src={ArrowDropUp} /></Collapsible.Visible>
|
||||
</Collapsible.Trigger>
|
||||
<span className="small d-inline ml-4 pl-1">
|
||||
{gradesFeatureIsFullyLocked || subsection.learnerHasAccess ? ''
|
||||
: (
|
||||
<Icon
|
||||
id={`detailedGradesBlockedIcon${subsection.blockKey}`}
|
||||
aria-label={intl.formatMessage(messages.noAccessToSubsection, { displayName })}
|
||||
className="mr-1 mt-1 d-inline-flex"
|
||||
style={{ height: '1rem', width: '1rem' }}
|
||||
src={Blocked}
|
||||
data-testid="blocked-icon"
|
||||
/>
|
||||
)}
|
||||
{gradesFeatureIsFullyLocked || subsection.learnerHasAccess ? '' : <Icon id={`detailedGradesBlockedIcon${subsection.blockKey}`} aria-label={intl.formatMessage(messages.noAcessToSubsection, { displayName })} className="mr-1 mt-1 d-inline-flex" style={{ height: '1rem', width: '1rem' }} src={Blocked} data-testid="blocked-icon" />}
|
||||
{url ? (
|
||||
<a
|
||||
href={url}
|
||||
@@ -78,7 +68,7 @@ function SubsectionTitleCell({ intl, subsection }) {
|
||||
)}
|
||||
</span>
|
||||
</Row>
|
||||
<Collapsible.Body className="d-flex w-100">
|
||||
<Collapsible.Body>
|
||||
<ProblemScoreDrawer problemScores={problemScores} subsection={subsection} />
|
||||
</Collapsible.Body>
|
||||
</Collapsible.Advanced>
|
||||
|
||||
@@ -18,28 +18,25 @@ function AssignmentTypeCell({
|
||||
gradesFeatureIsFullyLocked,
|
||||
} = useModel('progress', courseId);
|
||||
|
||||
const lockedIcon = locked ? <Icon id={`assignmentTypeBlockedIcon${assignmentType}`} aria-label={intl.formatMessage(messages.noAccessToAssignmentType, { assignmentType })} className="mr-1 mt-1 d-inline-flex" style={{ height: '1rem', width: '1rem' }} src={Blocked} data-testid="blocked-icon" /> : '';
|
||||
const lockedIcon = locked ? <Icon id={`assignmentTypeBlockedIcon${assignmentType}`} aria-label={intl.formatMessage(messages.noAcessToAssignmentType, { assignmentType })} className="mr-1 mt-1 d-inline-flex" style={{ height: '1rem', width: '1rem' }} src={Blocked} data-testid="blocked-icon" /> : '';
|
||||
|
||||
return (
|
||||
<div className="d-flex small">
|
||||
<div className="d-flex">{lockedIcon}</div>
|
||||
<div>
|
||||
{assignmentType}
|
||||
{footnoteId && footnoteMarker && (
|
||||
<sup>
|
||||
<a
|
||||
id={`${footnoteId}-ref`}
|
||||
className="muted-link"
|
||||
href={`#${footnoteId}-footnote`}
|
||||
aria-describedby="grade-summary-footnote-label"
|
||||
tabIndex={gradesFeatureIsFullyLocked ? '-1' : '0'}
|
||||
aria-labelledby={`assignmentTypeBlockedIcon${assignmentType}`}
|
||||
>
|
||||
{footnoteMarker}
|
||||
</a>
|
||||
</sup>
|
||||
)}
|
||||
</div>
|
||||
<div className="small">
|
||||
<span className="d-inline-flex">{lockedIcon}{assignmentType}</span>
|
||||
{footnoteId && footnoteMarker && (
|
||||
<sup>
|
||||
<a
|
||||
id={`${footnoteId}-ref`}
|
||||
className="muted-link"
|
||||
href={`#${footnoteId}-footnote`}
|
||||
aria-describedby="grade-summary-footnote-label"
|
||||
tabIndex={gradesFeatureIsFullyLocked ? '-1' : '0'}
|
||||
aria-labelledby={`assignmentTypeBlockedIcon${assignmentType}`}
|
||||
>
|
||||
{footnoteMarker}
|
||||
</a>
|
||||
</sup>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ function GradeSummaryTable({ intl, setAllOfSomeAssignmentTypeIsLocked }) {
|
||||
Cell: ({ value }) => (
|
||||
<span className={value.locked ? 'greyed-out' : ''}>{value.weight}</span> // eslint-disable-line react/prop-types
|
||||
),
|
||||
cellClassName: 'text-right small',
|
||||
cellClassName: 'float-right small',
|
||||
},
|
||||
{
|
||||
Header: `${intl.formatMessage(messages.grade)}`,
|
||||
@@ -114,7 +114,7 @@ function GradeSummaryTable({ intl, setAllOfSomeAssignmentTypeIsLocked }) {
|
||||
Cell: ({ value }) => (
|
||||
<span className={value.locked ? 'greyed-out' : ''}>{value.grade}</span> // eslint-disable-line react/prop-types
|
||||
),
|
||||
cellClassName: 'text-right small',
|
||||
cellClassName: 'float-right small',
|
||||
},
|
||||
{
|
||||
Header: `${intl.formatMessage(messages.weightedGrade)}`,
|
||||
@@ -124,7 +124,7 @@ function GradeSummaryTable({ intl, setAllOfSomeAssignmentTypeIsLocked }) {
|
||||
Cell: ({ value }) => (
|
||||
<span className={value.locked ? 'greyed-out' : ''}>{value.weightedGrade}</span> // eslint-disable-line react/prop-types
|
||||
),
|
||||
cellClassName: 'text-right font-weight-bold small',
|
||||
cellClassName: 'float-right font-weight-bold small',
|
||||
},
|
||||
]}
|
||||
>
|
||||
|
||||
@@ -139,11 +139,11 @@ const messages = defineMessages({
|
||||
id: 'progress.weightedGradeSummary',
|
||||
defaultMessage: 'Your current weighted grade summary',
|
||||
},
|
||||
noAccessToAssignmentType: {
|
||||
noAcessToAssignmentType: {
|
||||
id: 'progress.noAcessToAssignmentType',
|
||||
defaultMessage: 'You do not have access to assignments of type {assignmentType}',
|
||||
},
|
||||
noAccessToSubsection: {
|
||||
noAcessToSubsection: {
|
||||
id: 'progress.noAcessToSubsection',
|
||||
defaultMessage: 'You do not have access to subsection {displayName}',
|
||||
},
|
||||
|
||||
@@ -17,15 +17,21 @@ import { TabPage } from '../tab-page';
|
||||
import Course from './course';
|
||||
import { handleNextSectionCelebration } from './course/celebration';
|
||||
|
||||
const checkUrlLength = memoize((shortLinkFeatureFlag, courseStatus, courseId, sequence, unitHashKey) => {
|
||||
if (shortLinkFeatureFlag && courseStatus === 'loaded' && sequence && unitHashKey) {
|
||||
history.replace(`/c/${courseId}/${sequence.hash_key}/${unitHashKey}`);
|
||||
}
|
||||
});
|
||||
|
||||
const checkResumeRedirect = memoize((courseStatus, courseId, sequenceId, firstSequenceId) => {
|
||||
if (courseStatus === 'loaded' && !sequenceId) {
|
||||
// Note that getResumeBlock is just an API call, not a redux thunk.
|
||||
getResumeBlock(courseId).then((data) => {
|
||||
// This is a replace because we don't want this change saved in the browser's history.
|
||||
if (data.sectionId && data.unitId) {
|
||||
history.replace(`/course/${courseId}/${data.sectionId}/${data.unitId}`);
|
||||
history.replace(`/c/${courseId}/${data.sectionId}/${data.unitId}`);
|
||||
} else if (firstSequenceId) {
|
||||
history.replace(`/course/${courseId}/${firstSequenceId}`);
|
||||
history.replace(`/c/${courseId}/${firstSequenceId}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -33,7 +39,7 @@ const checkResumeRedirect = memoize((courseStatus, courseId, sequenceId, firstSe
|
||||
|
||||
const checkSectionUnitToUnitRedirect = memoize((courseStatus, courseId, sequenceStatus, section, unitId) => {
|
||||
if (courseStatus === 'loaded' && sequenceStatus === 'failed' && section && unitId) {
|
||||
history.replace(`/course/${courseId}/${unitId}`);
|
||||
history.replace(`/c/${courseId}/${unitId}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -41,10 +47,10 @@ const checkSectionToSequenceRedirect = memoize((courseStatus, courseId, sequence
|
||||
if (courseStatus === 'loaded' && sequenceStatus === 'failed' && section && !unitId) {
|
||||
// If the section is non-empty, redirect to its first sequence.
|
||||
if (section.sequenceIds && section.sequenceIds[0]) {
|
||||
history.replace(`/course/${courseId}/${section.sequenceIds[0]}`);
|
||||
history.replace(`/c/${courseId}/${section.sequenceIds[0]}`);
|
||||
// Otherwise, just go to the course root, letting the resume redirect take care of things.
|
||||
} else {
|
||||
history.replace(`/course/${courseId}`);
|
||||
history.replace(`/c/${courseId}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -53,7 +59,17 @@ const checkUnitToSequenceUnitRedirect = memoize((courseStatus, courseId, sequenc
|
||||
if (courseStatus === 'loaded' && sequenceStatus === 'failed' && unit) {
|
||||
// If the sequence failed to load as a sequence, but it *did* load as a unit, then
|
||||
// insert the unit's parent sequenceId into the URL.
|
||||
history.replace(`/course/${courseId}/${unit.sequenceId}/${unit.id}`);
|
||||
history.replace(`/c/${courseId}/${unit.sequenceId}/${unit.id}`);
|
||||
}
|
||||
});
|
||||
|
||||
const checkSpecialExamRedirect = memoize((sequenceStatus, sequence, specialExamsEnabled, proctoredExamsEnabled) => {
|
||||
if (sequenceStatus === 'loaded') {
|
||||
const shouldRedirectTimeLimited = sequence.isTimeLimited && !specialExamsEnabled;
|
||||
const shouldRedirectProctored = sequence.isProctored && !proctoredExamsEnabled;
|
||||
if ((shouldRedirectTimeLimited || shouldRedirectProctored) && sequence.legacyWebUrl !== undefined) {
|
||||
global.location.assign(sequence.legacyWebUrl);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -62,7 +78,7 @@ const checkSequenceToSequenceUnitRedirect = memoize((courseId, sequenceStatus, s
|
||||
if (sequence.unitIds !== undefined && sequence.unitIds.length > 0) {
|
||||
const nextUnitId = sequence.unitIds[sequence.activeUnitIndex];
|
||||
// This is a replace because we don't want this change saved in the browser's history.
|
||||
history.replace(`/course/${courseId}/${sequence.id}/${nextUnitId}`);
|
||||
history.replace(`/c/${courseId}/${sequence.id}/${nextUnitId}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -96,13 +112,13 @@ class CoursewareContainer extends Component {
|
||||
match: {
|
||||
params: {
|
||||
courseId: routeCourseId,
|
||||
sequenceId: routeSequenceId,
|
||||
sequenceId: routeSequenceHash,
|
||||
},
|
||||
},
|
||||
} = this.props;
|
||||
// Load data whenever the course or sequence ID changes.
|
||||
this.checkFetchCourse(routeCourseId);
|
||||
this.checkFetchSequence(routeSequenceId);
|
||||
this.checkFetchSequence(routeSequenceHash);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
@@ -111,22 +127,34 @@ class CoursewareContainer extends Component {
|
||||
sequenceId,
|
||||
courseStatus,
|
||||
sequenceStatus,
|
||||
specialExamsEnabledWaffleFlag,
|
||||
proctoredExamsEnabledWaffleFlag,
|
||||
sequence,
|
||||
firstSequenceId,
|
||||
unitViaSequenceId,
|
||||
sectionViaSequenceId,
|
||||
unitIdHashKeyMap,
|
||||
shortLinkFeatureFlag,
|
||||
match: {
|
||||
params: {
|
||||
courseId: routeCourseId,
|
||||
sequenceId: routeSequenceId,
|
||||
sequenceId: routeSequenceHash,
|
||||
unitId: routeUnitId,
|
||||
},
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
// Load data whenever the course or sequence ID changes.
|
||||
this.checkFetchCourse(routeCourseId);
|
||||
this.checkFetchSequence(routeSequenceId);
|
||||
this.checkFetchSequence(routeSequenceHash);
|
||||
if (sequence && routeSequenceHash.includes('block') && unitIdHashKeyMap) {
|
||||
let unitHashKey;
|
||||
Object.values(unitIdHashKeyMap).forEach(id => {
|
||||
if (id === routeUnitId) {
|
||||
unitHashKey = Object.keys(unitIdHashKeyMap).find(key => unitIdHashKeyMap[key] === id);
|
||||
}
|
||||
});
|
||||
checkUrlLength(shortLinkFeatureFlag, courseStatus, courseId, sequence, unitHashKey);
|
||||
}
|
||||
|
||||
// All courseware URLs should normalize to the format /course/:courseId/:sequenceId/:unitId
|
||||
// via the series of redirection rules below.
|
||||
@@ -163,6 +191,11 @@ class CoursewareContainer extends Component {
|
||||
// by filling in the ID of the parent sequence of :unitId.
|
||||
checkUnitToSequenceUnitRedirect(courseStatus, courseId, sequenceStatus, unitViaSequenceId);
|
||||
|
||||
// Check special exam redirect:
|
||||
// /course/:courseId/:sequenceId(/:unitId) -> :legacyWebUrl
|
||||
// because special exams are currently still served in the legacy LMS frontend.
|
||||
checkSpecialExamRedirect(sequenceStatus, sequence, specialExamsEnabledWaffleFlag, proctoredExamsEnabledWaffleFlag);
|
||||
|
||||
// Check to sequence to sequence-unit redirect:
|
||||
// /course/:courseId/:sequenceId -> /course/:courseId/:sequenceId/:unitId
|
||||
// by filling in the ID the most-recently-active unit in the sequence, OR
|
||||
@@ -184,7 +217,7 @@ class CoursewareContainer extends Component {
|
||||
} = this.props;
|
||||
|
||||
this.props.checkBlockCompletion(courseId, sequenceId, routeUnitId);
|
||||
history.push(`/course/${courseId}/${sequenceId}/${nextUnitId}`);
|
||||
history.push(`/c/${courseId}/${sequenceId}/${nextUnitId}`);
|
||||
}
|
||||
|
||||
handleNextSequenceClick = () => {
|
||||
@@ -194,16 +227,20 @@ class CoursewareContainer extends Component {
|
||||
nextSequence,
|
||||
sequence,
|
||||
sequenceId,
|
||||
shortLinkFeatureFlag,
|
||||
} = this.props;
|
||||
|
||||
if (nextSequence !== null) {
|
||||
let nextSequenceParam = nextSequence.id;
|
||||
if (shortLinkFeatureFlag) {
|
||||
nextSequenceParam = nextSequence.hash_key;
|
||||
}
|
||||
let nextUnitId = null;
|
||||
if (nextSequence.unitIds.length > 0) {
|
||||
[nextUnitId] = nextSequence.unitIds;
|
||||
history.push(`/course/${courseId}/${nextSequence.id}/${nextUnitId}`);
|
||||
history.push(`/c/${courseId}/${nextSequenceParam}/${nextUnitId}`);
|
||||
} else {
|
||||
// Some sequences have no units. This will show a blank page with prev/next buttons.
|
||||
history.push(`/course/${courseId}/${nextSequence.id}`);
|
||||
history.push(`/c/${courseId}/${nextSequenceParam}`);
|
||||
}
|
||||
|
||||
const celebrateFirstSection = course && course.celebrations && course.celebrations.firstSection;
|
||||
@@ -214,14 +251,22 @@ class CoursewareContainer extends Component {
|
||||
}
|
||||
|
||||
handlePreviousSequenceClick = () => {
|
||||
const { previousSequence, courseId } = this.props;
|
||||
const {
|
||||
previousSequence,
|
||||
courseId,
|
||||
shortLinkFeatureFlag,
|
||||
} = this.props;
|
||||
if (previousSequence !== null) {
|
||||
let previousSequenceParam = previousSequence.id;
|
||||
if (shortLinkFeatureFlag) {
|
||||
previousSequenceParam = previousSequence.hash_key;
|
||||
}
|
||||
if (previousSequence.unitIds.length > 0) {
|
||||
const previousUnitId = previousSequence.unitIds[previousSequence.unitIds.length - 1];
|
||||
history.push(`/course/${courseId}/${previousSequence.id}/${previousUnitId}`);
|
||||
history.push(`/c/${courseId}/${previousSequenceParam}/${previousUnitId}`);
|
||||
} else {
|
||||
// Some sequences have no units. This will show a blank page with prev/next buttons.
|
||||
history.push(`/course/${courseId}/${previousSequence.id}`);
|
||||
history.push(`/c/${courseId}/${previousSequenceParam}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,6 +276,9 @@ class CoursewareContainer extends Component {
|
||||
courseStatus,
|
||||
courseId,
|
||||
sequenceId,
|
||||
sequence,
|
||||
shortLinkFeatureFlag,
|
||||
unitIdHashKeyMap,
|
||||
match: {
|
||||
params: {
|
||||
unitId: routeUnitId,
|
||||
@@ -238,18 +286,30 @@ class CoursewareContainer extends Component {
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
// This helps process old URLS that still use a blocks usage key in the URL.
|
||||
let updatedSequenceId;
|
||||
let updatedUnitId;
|
||||
if (shortLinkFeatureFlag && sequence) {
|
||||
if (!sequenceId.includes('block')) {
|
||||
updatedSequenceId = sequence.id;
|
||||
}
|
||||
if (routeUnitId && !routeUnitId.includes('block')) {
|
||||
updatedUnitId = unitIdHashKeyMap[routeUnitId];
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<TabPage
|
||||
activeTabSlug="courseware"
|
||||
courseId={courseId}
|
||||
unitId={routeUnitId}
|
||||
unitId={updatedUnitId || routeUnitId}
|
||||
courseStatus={courseStatus}
|
||||
metadataModel="coursewareMeta"
|
||||
>
|
||||
<Course
|
||||
courseId={courseId}
|
||||
sequenceId={sequenceId}
|
||||
unitId={routeUnitId}
|
||||
sequenceId={updatedSequenceId || sequenceId}
|
||||
unitId={updatedUnitId || routeUnitId}
|
||||
nextSequenceHandler={this.handleNextSequenceClick}
|
||||
previousSequenceHandler={this.handlePreviousSequenceClick}
|
||||
unitNavigationHandler={this.handleUnitNavigationClick}
|
||||
@@ -268,6 +328,7 @@ const sequenceShape = PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
unitIds: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
sectionId: PropTypes.string.isRequired,
|
||||
hash_key: PropTypes.string.isRequired,
|
||||
isTimeLimited: PropTypes.bool,
|
||||
isProctored: PropTypes.bool,
|
||||
legacyWebUrl: PropTypes.string,
|
||||
@@ -301,12 +362,16 @@ CoursewareContainer.propTypes = {
|
||||
previousSequence: sequenceShape,
|
||||
unitViaSequenceId: unitShape,
|
||||
sectionViaSequenceId: sectionShape,
|
||||
unitIdHashKeyMap: unitShape,
|
||||
course: courseShape,
|
||||
sequence: sequenceShape,
|
||||
saveSequencePosition: PropTypes.func.isRequired,
|
||||
checkBlockCompletion: PropTypes.func.isRequired,
|
||||
fetchCourse: PropTypes.func.isRequired,
|
||||
fetchSequence: PropTypes.func.isRequired,
|
||||
specialExamsEnabledWaffleFlag: PropTypes.bool.isRequired,
|
||||
proctoredExamsEnabledWaffleFlag: PropTypes.bool.isRequired,
|
||||
shortLinkFeatureFlag: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
CoursewareContainer.defaultProps = {
|
||||
@@ -319,6 +384,7 @@ CoursewareContainer.defaultProps = {
|
||||
sectionViaSequenceId: null,
|
||||
course: null,
|
||||
sequence: null,
|
||||
unitIdHashKeyMap: null,
|
||||
};
|
||||
|
||||
const currentCourseSelector = createSelector(
|
||||
@@ -330,7 +396,16 @@ const currentCourseSelector = createSelector(
|
||||
const currentSequenceSelector = createSelector(
|
||||
(state) => state.models.sequences || {},
|
||||
(state) => state.courseware.sequenceId,
|
||||
(sequencesById, sequenceId) => (sequencesById[sequenceId] ? sequencesById[sequenceId] : null),
|
||||
(state) => state.models.sequenceIdToHashKeyMap,
|
||||
(sequencesById, sequenceId, sequenceMap) => {
|
||||
if (!sequencesById[sequenceId] && Object.keys(sequencesById).length > 0 && sequenceMap) {
|
||||
if (sequenceId in sequenceMap) {
|
||||
const updatedSequenceId = sequenceMap[sequenceId];
|
||||
return sequencesById[updatedSequenceId];
|
||||
}
|
||||
}
|
||||
return sequencesById[sequenceId] ? sequencesById[sequenceId] : null;
|
||||
},
|
||||
);
|
||||
|
||||
const sequenceIdsSelector = createSelector(
|
||||
@@ -350,11 +425,18 @@ const previousSequenceSelector = createSelector(
|
||||
sequenceIdsSelector,
|
||||
(state) => state.models.sequences || {},
|
||||
(state) => state.courseware.sequenceId,
|
||||
(sequenceIds, sequencesById, sequenceId) => {
|
||||
(state) => state.models.sequenceIdToHashKeyMap,
|
||||
(sequenceIds, sequencesById, sequenceId, sequenceMap) => {
|
||||
if (!sequenceId || sequenceIds.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const sequenceIndex = sequenceIds.indexOf(sequenceId);
|
||||
let sequenceIndex = sequenceIds.indexOf(sequenceId);
|
||||
if (!sequencesById[sequenceId] && Object.keys(sequencesById).length > 0 && sequenceMap) {
|
||||
if (sequenceId in sequenceMap) {
|
||||
const updatedSequenceId = sequenceMap[sequenceId];
|
||||
sequenceIndex = sequenceIds.indexOf(updatedSequenceId);
|
||||
}
|
||||
}
|
||||
const previousSequenceId = sequenceIndex > 0 ? sequenceIds[sequenceIndex - 1] : null;
|
||||
return previousSequenceId !== null ? sequencesById[previousSequenceId] : null;
|
||||
},
|
||||
@@ -364,11 +446,18 @@ const nextSequenceSelector = createSelector(
|
||||
sequenceIdsSelector,
|
||||
(state) => state.models.sequences || {},
|
||||
(state) => state.courseware.sequenceId,
|
||||
(sequenceIds, sequencesById, sequenceId) => {
|
||||
(state) => state.models.sequenceIdToHashKeyMap,
|
||||
(sequenceIds, sequencesById, sequenceId, sequenceMap) => {
|
||||
if (!sequenceId || sequenceIds.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const sequenceIndex = sequenceIds.indexOf(sequenceId);
|
||||
let sequenceIndex = sequenceIds.indexOf(sequenceId);
|
||||
if (!sequencesById[sequenceId] && Object.keys(sequencesById).length > 0 && sequenceMap) {
|
||||
if (sequenceId in sequenceMap) {
|
||||
const updatedSequenceId = sequenceMap[sequenceId];
|
||||
sequenceIndex = sequenceIds.indexOf(updatedSequenceId);
|
||||
}
|
||||
}
|
||||
const nextSequenceId = sequenceIndex < sequenceIds.length - 1 ? sequenceIds[sequenceIndex + 1] : null;
|
||||
return nextSequenceId !== null ? sequencesById[nextSequenceId] : null;
|
||||
},
|
||||
@@ -401,7 +490,21 @@ const sectionViaSequenceIdSelector = createSelector(
|
||||
const unitViaSequenceIdSelector = createSelector(
|
||||
(state) => state.models.units || {},
|
||||
(state) => state.courseware.sequenceId,
|
||||
(unitsById, sequenceId) => (unitsById[sequenceId] ? unitsById[sequenceId] : null),
|
||||
(state) => state.models.unitIdHashKeyMap,
|
||||
(unitsById, sequenceId, unitMap) => {
|
||||
if (!unitsById[sequenceId] && Object.keys(unitsById).length > 0 && unitMap) {
|
||||
if (sequenceId in unitMap) {
|
||||
const updatedSequenceId = unitMap[sequenceId];
|
||||
return unitsById[updatedSequenceId];
|
||||
}
|
||||
}
|
||||
return unitsById[sequenceId] ? unitsById[sequenceId] : null;
|
||||
},
|
||||
);
|
||||
|
||||
const unitIdHashKeyMapSelector = createSelector(
|
||||
(state) => state.models.unitIdToHashKeyMap,
|
||||
(unitIdToHashKeyMap) => (unitIdToHashKeyMap),
|
||||
);
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
@@ -410,6 +513,9 @@ const mapStateToProps = (state) => {
|
||||
sequenceId,
|
||||
courseStatus,
|
||||
sequenceStatus,
|
||||
specialExamsEnabledWaffleFlag,
|
||||
proctoredExamsEnabledWaffleFlag,
|
||||
shortLinkFeatureFlag,
|
||||
} = state.courseware;
|
||||
|
||||
return {
|
||||
@@ -417,6 +523,9 @@ const mapStateToProps = (state) => {
|
||||
sequenceId,
|
||||
courseStatus,
|
||||
sequenceStatus,
|
||||
specialExamsEnabledWaffleFlag,
|
||||
proctoredExamsEnabledWaffleFlag,
|
||||
shortLinkFeatureFlag,
|
||||
course: currentCourseSelector(state),
|
||||
sequence: currentSequenceSelector(state),
|
||||
previousSequence: previousSequenceSelector(state),
|
||||
@@ -424,6 +533,7 @@ const mapStateToProps = (state) => {
|
||||
firstSequenceId: firstSequenceIdSelector(state),
|
||||
sectionViaSequenceId: sectionViaSequenceIdSelector(state),
|
||||
unitViaSequenceId: unitViaSequenceIdSelector(state),
|
||||
unitIdHashKeyMap: unitIdHashKeyMapSelector(state),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -85,9 +85,9 @@ describe('CoursewareContainer', () => {
|
||||
<Switch>
|
||||
<Route
|
||||
path={[
|
||||
'/course/:courseId/:sequenceId/:unitId',
|
||||
'/course/:courseId/:sequenceId',
|
||||
'/course/:courseId',
|
||||
'/c/:courseId/:sequenceId/:unitId',
|
||||
'/c/:courseId/:sequenceId',
|
||||
'/c/:courseId',
|
||||
]}
|
||||
component={CoursewareContainer}
|
||||
/>
|
||||
@@ -128,8 +128,10 @@ describe('CoursewareContainer', () => {
|
||||
axiosMock.onGet(courseMetadataUrl).reply(200, courseMetadata);
|
||||
|
||||
sequenceMetadatas.forEach(sequenceMetadata => {
|
||||
const sequenceMetadataUrl = `${getConfig().LMS_BASE_URL}/api/courseware/sequence/${sequenceMetadata.item_id}`;
|
||||
const sequenceMetadataUrl = `${getConfig().LMS_BASE_URL}/api/courseware/sequence/${sequenceMetadata.hash_key}`;
|
||||
axiosMock.onGet(sequenceMetadataUrl).reply(200, sequenceMetadata);
|
||||
const sequenceMetadataUrlFull = `${getConfig().LMS_BASE_URL}/api/courseware/sequence/${sequenceMetadata.item_id}`;
|
||||
axiosMock.onGet(sequenceMetadataUrlFull).reply(200, sequenceMetadata);
|
||||
const proctoredExamApiUrl = `${getConfig().LMS_BASE_URL}/api/edx_proctoring/v1/proctored_exam/attempt/course_id/${courseId}/content_id/${sequenceMetadata.item_id}?is_learning_mfe=true`;
|
||||
axiosMock.onGet(proctoredExamApiUrl).reply(200, { exam: {}, active_attempt: {} });
|
||||
});
|
||||
@@ -144,7 +146,7 @@ describe('CoursewareContainer', () => {
|
||||
}
|
||||
|
||||
it('should initialize to show a spinner', () => {
|
||||
history.push('/course/abc123');
|
||||
history.push('/c/abc123');
|
||||
render(component);
|
||||
|
||||
const spinner = screen.getByRole('status');
|
||||
@@ -190,11 +192,11 @@ describe('CoursewareContainer', () => {
|
||||
|
||||
it('should use the resume block repsonse to pick a unit if it contains one', async () => {
|
||||
axiosMock.onGet(`${getConfig().LMS_BASE_URL}/api/courseware/resume/${courseId}`).reply(200, {
|
||||
sectionId: sequenceBlock.id,
|
||||
unitId: unitBlocks[1].id,
|
||||
sectionId: sequenceBlock.hash_key,
|
||||
unitId: unitBlocks[1].hash_key,
|
||||
});
|
||||
|
||||
history.push(`/course/${courseId}`);
|
||||
history.push(`/c/${courseId}`);
|
||||
const container = await loadContainer();
|
||||
|
||||
assertLoadedHeader(container);
|
||||
@@ -202,7 +204,7 @@ describe('CoursewareContainer', () => {
|
||||
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent('Unit Contents');
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent(courseId);
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent(unitBlocks[1].id);
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent(unitBlocks[1].hash_key);
|
||||
});
|
||||
|
||||
it('should use the first sequence ID and activeUnitIndex if the resume block response is empty', async () => {
|
||||
@@ -217,7 +219,7 @@ describe('CoursewareContainer', () => {
|
||||
// Note how there is no sectionId/unitId returned in this mock response!
|
||||
axiosMock.onGet(`${getConfig().LMS_BASE_URL}/api/courseware/resume/${courseId}`).reply(200, {});
|
||||
|
||||
history.push(`/course/${courseId}`);
|
||||
history.push(`/c/${courseId}`);
|
||||
const container = await loadContainer();
|
||||
|
||||
assertLoadedHeader(container);
|
||||
@@ -237,11 +239,11 @@ describe('CoursewareContainer', () => {
|
||||
);
|
||||
|
||||
function setUrl(urlSequenceId, urlUnitId = null) {
|
||||
history.push(`/course/${courseId}/${urlSequenceId}/${urlUnitId || ''}`);
|
||||
history.push(`/c/${courseId}/${urlSequenceId}/${urlUnitId || ''}`);
|
||||
}
|
||||
|
||||
function assertLocation(container, sequenceId, unitId) {
|
||||
const expectedUrl = `http://localhost/course/${courseId}/${sequenceId}/${unitId}`;
|
||||
const expectedUrl = `http://localhost/c/${courseId}/${sequenceId}/${unitId}`;
|
||||
expect(global.location.href).toEqual(expectedUrl);
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent(unitId);
|
||||
}
|
||||
@@ -257,7 +259,7 @@ describe('CoursewareContainer', () => {
|
||||
const container = await loadContainer();
|
||||
assertLoadedHeader(container);
|
||||
assertSequenceNavigation(container, 2);
|
||||
assertLocation(container, sequenceTree[1][1].id, urlUnit.id);
|
||||
assertLocation(container, sequenceTree[1][1].hash_key, urlUnit.hash_key);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -267,7 +269,7 @@ describe('CoursewareContainer', () => {
|
||||
const container = await loadContainer();
|
||||
assertLoadedHeader(container);
|
||||
assertSequenceNavigation(container, 2);
|
||||
assertLocation(container, sequenceTree[1][0].id, unitTree[1][0][0].id);
|
||||
assertLocation(container, sequenceTree[1][0].hash_key, unitTree[1][0][0].hash_key);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -293,14 +295,14 @@ describe('CoursewareContainer', () => {
|
||||
it('should ignore the section ID and instead redirect to the course root', async () => {
|
||||
setUrl(sectionTree[1].id);
|
||||
await loadContainer();
|
||||
expect(global.location.href).toEqual(`http://localhost/course/${courseId}`);
|
||||
expect(global.location.href).toEqual(`http://localhost/c/${courseId}`);
|
||||
});
|
||||
|
||||
it('should ignore the section and unit IDs and instead to the course root', async () => {
|
||||
// Specific unit ID used here shouldn't matter; is ignored due to empty section.
|
||||
setUrl(sectionTree[1].id, unitTree[0][0][0]);
|
||||
await loadContainer();
|
||||
expect(global.location.href).toEqual(`http://localhost/course/${courseId}`);
|
||||
expect(global.location.href).toEqual(`http://localhost/c/${courseId}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -314,15 +316,15 @@ describe('CoursewareContainer', () => {
|
||||
|
||||
it('should insert the sequence ID into the URL', async () => {
|
||||
const unit = unitTree[1][0][1];
|
||||
history.push(`/course/${courseId}/${unit.id}`);
|
||||
history.push(`/c/${courseId}/${unit.id}`);
|
||||
const container = await loadContainer();
|
||||
|
||||
assertLoadedHeader(container);
|
||||
assertSequenceNavigation(container, 2);
|
||||
const expectedSequenceId = sequenceTree[1][0].id;
|
||||
const expectedUrl = `http://localhost/course/${courseId}/${expectedSequenceId}/${unit.id}`;
|
||||
const expectedSequenceId = sequenceTree[1][0].hash_key;
|
||||
const expectedUrl = `http://localhost/c/${courseId}/${expectedSequenceId}/${unit.hash_key}`;
|
||||
expect(global.location.href).toEqual(expectedUrl);
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent(unit.id);
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent(unit.hash_key);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -331,7 +333,7 @@ describe('CoursewareContainer', () => {
|
||||
const unitBlocks = defaultUnitBlocks;
|
||||
|
||||
it('should pick the first unit if position was not defined (activeUnitIndex becomes 0)', async () => {
|
||||
history.push(`/course/${courseId}/${sequenceBlock.id}`);
|
||||
history.push(`/c/${courseId}/${sequenceBlock.hash_key}`);
|
||||
const container = await loadContainer();
|
||||
|
||||
assertLoadedHeader(container);
|
||||
@@ -339,7 +341,7 @@ describe('CoursewareContainer', () => {
|
||||
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent('Unit Contents');
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent(courseId);
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent(unitBlocks[0].id);
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent(unitBlocks[0].hash_key);
|
||||
});
|
||||
|
||||
it('should use activeUnitIndex to pick a unit from the sequence', async () => {
|
||||
@@ -350,7 +352,7 @@ describe('CoursewareContainer', () => {
|
||||
);
|
||||
setUpMockRequests({ sequenceMetadatas: [sequenceMetadata] });
|
||||
|
||||
history.push(`/course/${courseId}/${sequenceBlock.id}`);
|
||||
history.push(`/c/${courseId}/${sequenceBlock.hash_key}`);
|
||||
const container = await loadContainer();
|
||||
|
||||
assertLoadedHeader(container);
|
||||
@@ -358,7 +360,7 @@ describe('CoursewareContainer', () => {
|
||||
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent('Unit Contents');
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent(courseId);
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent(unitBlocks[2].id);
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent(unitBlocks[2].hash_key);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -367,7 +369,7 @@ describe('CoursewareContainer', () => {
|
||||
const unitBlocks = defaultUnitBlocks;
|
||||
|
||||
it('should load the specified unit', async () => {
|
||||
history.push(`/course/${courseId}/${sequenceBlock.id}/${unitBlocks[2].id}`);
|
||||
history.push(`/c/${courseId}/${sequenceBlock.hash_key}/${unitBlocks[2].hash_key}`);
|
||||
const container = await loadContainer();
|
||||
|
||||
assertLoadedHeader(container);
|
||||
@@ -375,7 +377,7 @@ describe('CoursewareContainer', () => {
|
||||
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent('Unit Contents');
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent(courseId);
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent(unitBlocks[2].id);
|
||||
expect(container.querySelector('.fake-unit')).toHaveTextContent(unitBlocks[2].hash_key);
|
||||
});
|
||||
|
||||
it('should navigate between units and check block completion', async () => {
|
||||
@@ -383,7 +385,7 @@ describe('CoursewareContainer', () => {
|
||||
complete: true,
|
||||
});
|
||||
|
||||
history.push(`/course/${courseId}/${sequenceBlock.id}/${unitBlocks[0].id}`);
|
||||
history.push(`/c/${courseId}/${sequenceBlock.hash_key}/${unitBlocks[0].id}`);
|
||||
const container = await loadContainer();
|
||||
|
||||
const sequenceNavButtons = container.querySelectorAll('nav.sequence-navigation button');
|
||||
@@ -391,12 +393,14 @@ describe('CoursewareContainer', () => {
|
||||
expect(sequenceNextButton).toHaveTextContent('Next');
|
||||
fireEvent.click(sequenceNavButtons[4]);
|
||||
|
||||
expect(global.location.href).toEqual(`http://localhost/course/${courseId}/${sequenceBlock.id}/${unitBlocks[1].id}`);
|
||||
expect(global.location.href).toEqual(`http://localhost/c/${courseId}/${sequenceBlock.hash_key}/${unitBlocks[1].id}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the current sequence is an exam', () => {
|
||||
const { location } = window;
|
||||
const sequenceBlock = defaultSequenceBlock;
|
||||
const unitBlocks = defaultUnitBlocks;
|
||||
|
||||
beforeEach(() => {
|
||||
delete window.location;
|
||||
@@ -408,6 +412,20 @@ describe('CoursewareContainer', () => {
|
||||
afterEach(() => {
|
||||
window.location = location;
|
||||
});
|
||||
|
||||
it('should redirect to the sequence legacyWebUrl', async () => {
|
||||
const sequenceMetadata = Factory.build(
|
||||
'sequenceMetadata',
|
||||
{ is_time_limited: true }, // position index is 1-based and is converted to 0-based for activeUnitIndex
|
||||
{ courseId, unitBlocks, sequenceBlock },
|
||||
);
|
||||
setUpMockRequests({ sequenceMetadatas: [sequenceMetadata] });
|
||||
|
||||
history.push(`/c/${courseId}/${sequenceBlock.hash_key}/${unitBlocks[2].hash_key}`);
|
||||
await loadContainer();
|
||||
|
||||
expect(global.location.assign).toHaveBeenCalledWith(sequenceBlock.legacy_web_url);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -424,7 +442,7 @@ describe('CoursewareContainer', () => {
|
||||
|
||||
const { courseBlocks, sequenceBlocks, unitBlocks } = buildSimpleCourseBlocks(courseId, courseMetadata.name);
|
||||
setUpMockRequests({ courseBlocks, courseMetadata });
|
||||
history.push(`/course/${courseId}/${sequenceBlocks[0].id}/${unitBlocks[0].id}`);
|
||||
history.push(`/c/${courseId}/${sequenceBlocks[0].hash_key}/${unitBlocks[0].hash_key}`);
|
||||
return { courseMetadata, unitBlocks };
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,12 @@ export default () => {
|
||||
path={`${path}/courseware/:courseId/unit/:unitId`}
|
||||
component={CoursewareRedirect}
|
||||
/>
|
||||
<PageRoute
|
||||
path={`${path}/:courseId/:sequenceId/:unitId`}
|
||||
render={({ match }) => {
|
||||
global.location.assign(`/c/${match.params.courseId}/${match.params.sequenceId}/${match.params.unitId}`);
|
||||
}}
|
||||
/>
|
||||
<PageRoute
|
||||
path={`${path}/course-home/:courseId`}
|
||||
render={({ match }) => {
|
||||
|
||||
@@ -15,7 +15,6 @@ import NotificationTrigger from './NotificationTrigger';
|
||||
|
||||
import { useModel } from '../../generic/model-store';
|
||||
import useWindowSize, { responsiveBreakpoints } from '../../generic/tabs/useWindowSize';
|
||||
import { getLocalStorage, setLocalStorage } from '../../data/localStorage';
|
||||
|
||||
/** [MM-P2P] Experiment */
|
||||
import { initCoursewareMMP2P, MMP2PBlockModal } from '../../experiments/mm-p2p';
|
||||
@@ -61,22 +60,6 @@ function Course({
|
||||
if (notificationTrayVisible) { setNotificationTray(false); } else { setNotificationTray(true); }
|
||||
};
|
||||
|
||||
if (!getLocalStorage('notificationStatus')) {
|
||||
setLocalStorage('notificationStatus', 'active'); // Show red dot on notificationTrigger until seen
|
||||
}
|
||||
|
||||
if (!getLocalStorage('upgradeNotificationCurrentState')) {
|
||||
setLocalStorage('upgradeNotificationCurrentState', 'initialize');
|
||||
}
|
||||
|
||||
const [notificationStatus, setNotificationStatus] = useState(getLocalStorage('notificationStatus'));
|
||||
const [upgradeNotificationCurrentState, setupgradeNotificationCurrentState] = useState(getLocalStorage('upgradeNotificationCurrentState'));
|
||||
|
||||
const onNotificationSeen = () => {
|
||||
setNotificationStatus('inactive');
|
||||
setLocalStorage('notificationStatus', 'inactive');
|
||||
};
|
||||
|
||||
/** [MM-P2P] Experiment */
|
||||
const MMP2P = initCoursewareMMP2P(courseId, sequenceId, unitId);
|
||||
|
||||
@@ -98,9 +81,6 @@ function Course({
|
||||
<NotificationTrigger
|
||||
toggleNotificationTray={toggleNotificationTray}
|
||||
isNotificationTrayVisible={isNotificationTrayVisible}
|
||||
notificationStatus={notificationStatus}
|
||||
setNotificationStatus={setNotificationStatus}
|
||||
upgradeNotificationCurrentState={upgradeNotificationCurrentState}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -116,11 +96,6 @@ function Course({
|
||||
toggleNotificationTray={toggleNotificationTray}
|
||||
isNotificationTrayVisible={isNotificationTrayVisible}
|
||||
notificationTrayVisible={notificationTrayVisible}
|
||||
notificationStatus={notificationStatus}
|
||||
setNotificationStatus={setNotificationStatus}
|
||||
onNotificationSeen={onNotificationSeen}
|
||||
upgradeNotificationCurrentState={upgradeNotificationCurrentState}
|
||||
setupgradeNotificationCurrentState={setupgradeNotificationCurrentState}
|
||||
//* * [MM-P2P] Experiment */
|
||||
mmp2p={MMP2P}
|
||||
/>
|
||||
|
||||
@@ -91,31 +91,6 @@ describe('Course', () => {
|
||||
expect(notificationTrigger).not.toHaveClass('trigger-active');
|
||||
});
|
||||
|
||||
it('renders course breadcrumbs as expected', async () => {
|
||||
const courseMetadata = Factory.build('courseMetadata');
|
||||
const unitBlocks = Array.from({ length: 3 }).map(() => Factory.build(
|
||||
'block',
|
||||
{ type: 'vertical' },
|
||||
{ courseId: courseMetadata.id },
|
||||
));
|
||||
const testStore = await initializeTestStore({ courseMetadata, unitBlocks }, false);
|
||||
const { courseware, models } = testStore.getState();
|
||||
const { courseId, sequenceId } = courseware;
|
||||
const testData = {
|
||||
...mockData,
|
||||
courseId,
|
||||
sequenceId,
|
||||
unitId: Object.values(models.units)[1].id, // Corner cases are already covered in `Sequence` tests.
|
||||
};
|
||||
render(<Course {...testData} />, { store: testStore });
|
||||
|
||||
loadUnit();
|
||||
await waitFor(() => expect(screen.queryByText('Loading learning sequence...')).not.toBeInTheDocument());
|
||||
// expect the section and sequence "titles" to be loaded in as breadcrumb labels.
|
||||
expect(screen.getByText('cdabcdabcdabcdabcdabcdabcdabcd13')).toBeInTheDocument();
|
||||
expect(screen.getByText('cdabcdabcdabcdabcdabcdabcdabcd12')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('passes handlers to the sequence', async () => {
|
||||
const nextSequenceHandler = jest.fn();
|
||||
const previousSequenceHandler = jest.fn();
|
||||
|
||||
@@ -5,78 +5,29 @@ import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faHome } from '@fortawesome/free-solid-svg-icons';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Hyperlink, MenuItem, SelectMenu } from '@edx/paragon';
|
||||
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
|
||||
import {
|
||||
sendTrackingLogEvent,
|
||||
sendTrackEvent,
|
||||
} from '@edx/frontend-platform/analytics';
|
||||
import { useModel, useModels } from '../../generic/model-store';
|
||||
import { useModel } from '../../generic/model-store';
|
||||
|
||||
/** [MM-P2P] Experiment */
|
||||
import { MMP2PFlyoverTrigger } from '../../experiments/mm-p2p';
|
||||
|
||||
function CourseBreadcrumb({
|
||||
content, withSeparator,
|
||||
url, children, withSeparator, ...attrs
|
||||
}) {
|
||||
const defaultContent = content.filter(destination => destination.default)[0];
|
||||
const administrator = getAuthenticatedUser() ? getAuthenticatedUser().administrator : false;
|
||||
function logEvent(target) {
|
||||
const eventName = 'edx.ui.lms.jump_nav.selected';
|
||||
const payload = {
|
||||
target_name: target.label,
|
||||
id: target.id,
|
||||
current_id: defaultContent.id,
|
||||
widget_placement: 'breadcrumb',
|
||||
};
|
||||
sendTrackEvent(eventName, payload);
|
||||
sendTrackingLogEvent(eventName, payload);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{withSeparator && (
|
||||
<li className="mx-2 text-primary-500 text-truncate text-nowrap" role="presentation" aria-hidden>/</li>
|
||||
<li className="mx-2 text-primary-500" role="presentation" aria-hidden>/</li>
|
||||
)}
|
||||
|
||||
<li style={{
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{ getConfig().ENABLE_JUMPNAV !== 'true' || content.length < 2 || !administrator
|
||||
? (
|
||||
<a className="text-primary-500" href={defaultContent.url}>{defaultContent.label}
|
||||
</a>
|
||||
)
|
||||
: (
|
||||
<SelectMenu isLink defaultMessage={defaultContent.label}>
|
||||
{content.map(item => (
|
||||
<MenuItem
|
||||
as={Hyperlink}
|
||||
defaultSelected={item.default}
|
||||
destination={item.url}
|
||||
onClick={logEvent(item)}
|
||||
>
|
||||
{item.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</SelectMenu>
|
||||
)}
|
||||
|
||||
<li {...attrs}>
|
||||
<a className="text-primary-500" href={url}>{children}</a>
|
||||
</li>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
CourseBreadcrumb.propTypes = {
|
||||
content: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
default: PropTypes.bool,
|
||||
url: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
}),
|
||||
).isRequired,
|
||||
url: PropTypes.string.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
withSeparator: PropTypes.bool,
|
||||
};
|
||||
|
||||
@@ -92,56 +43,53 @@ export default function CourseBreadcrumbs({
|
||||
mmp2p,
|
||||
}) {
|
||||
const course = useModel('coursewareMeta', courseId);
|
||||
const sequence = useModel('sequences', sequenceId);
|
||||
const section = useModel('sections', sectionId);
|
||||
const courseStatus = useSelector(state => state.courseware.courseStatus);
|
||||
const sections = course ? Object.fromEntries(useModels('sections', course.sectionIds).map(section => [section.id, section])) : null;
|
||||
const possibleSequences = sections && sectionId ? sections[sectionId].sequenceIds : [];
|
||||
const sequences = Object.fromEntries(useModels('sequences', possibleSequences).map(sequence => [sequence.id, sequence]));
|
||||
const sequenceStatus = useSelector(state => state.courseware.sequenceStatus);
|
||||
|
||||
const links = useMemo(() => {
|
||||
const temp = [];
|
||||
if (courseStatus === 'loaded' && sequenceStatus === 'loaded') {
|
||||
temp.push(course.sectionIds.map(id => ({
|
||||
id,
|
||||
label: sections[id].title,
|
||||
default: (id === sectionId),
|
||||
// navigate to first sequence in section, (TODO: navigate to first incomplete sequence in section)
|
||||
url: `${getConfig().BASE_URL}/course/${courseId}/${sections[id].sequenceIds[0]}`,
|
||||
})));
|
||||
temp.push(sections[sectionId].sequenceIds.map(id => ({
|
||||
id,
|
||||
label: sequences[id].title,
|
||||
default: id === sequenceId,
|
||||
// first unit it section (TODO: navigate to first incomplete in sequence)
|
||||
url: `${getConfig().BASE_URL}/course/${courseId}/${sequences[id].id}/${sequences[id].unitIds[0]}`,
|
||||
})));
|
||||
return [section, sequence].filter(node => !!node).map((node) => ({
|
||||
id: node.id,
|
||||
label: node.title,
|
||||
url: `${getConfig().LMS_BASE_URL}/courses/${course.id}/course/#${node.id}`,
|
||||
}));
|
||||
}
|
||||
return temp;
|
||||
}, [courseStatus, sections, sequences]);
|
||||
return [];
|
||||
}, [courseStatus, sequenceStatus]);
|
||||
|
||||
return (
|
||||
<nav aria-label="breadcrumb" className="my-4 d-inline-block col-sm-10">
|
||||
<ol className="list-unstyled d-flex align-items-center m-0">
|
||||
<li>
|
||||
<a
|
||||
href={`${getConfig().LMS_BASE_URL}/courses/${courseId}/course/`}
|
||||
className="flex-shrink-0 text-primary"
|
||||
>
|
||||
<FontAwesomeIcon icon={faHome} className="mr-2" />
|
||||
<FormattedMessage
|
||||
id="learn.breadcrumb.navigation.course.home"
|
||||
description="The course home link in breadcrumbs nav"
|
||||
defaultMessage="Course"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
{links.map(content => (
|
||||
<CourseBreadcrumb
|
||||
content={content}
|
||||
withSeparator
|
||||
<ol className="list-unstyled d-flex m-0">
|
||||
<CourseBreadcrumb
|
||||
url={`${getConfig().LMS_BASE_URL}/courses/${course.id}/course/`}
|
||||
className="flex-shrink-0"
|
||||
>
|
||||
<FontAwesomeIcon icon={faHome} className="mr-2" />
|
||||
<FormattedMessage
|
||||
id="learn.breadcrumb.navigation.course.home"
|
||||
description="The course home link in breadcrumbs nav"
|
||||
defaultMessage="Course"
|
||||
/>
|
||||
</CourseBreadcrumb>
|
||||
{links.map(({ id, url, label }) => (
|
||||
<CourseBreadcrumb
|
||||
key={id}
|
||||
url={url}
|
||||
withSeparator
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</CourseBreadcrumb>
|
||||
))}
|
||||
|
||||
{/** [MM-P2P] Experiment */}
|
||||
{mmp2p.state && mmp2p.state.isEnabled && (
|
||||
{mmp2p.state.isEnabled && (
|
||||
<MMP2PFlyoverTrigger options={mmp2p} />
|
||||
)}
|
||||
</ol>
|
||||
@@ -164,6 +112,7 @@ CourseBreadcrumbs.propTypes = {
|
||||
CourseBreadcrumbs.defaultProps = {
|
||||
sectionId: null,
|
||||
sequenceId: null,
|
||||
|
||||
/** [MM-P2P] Experiment */
|
||||
mmp2p: {},
|
||||
};
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
import React from 'react';
|
||||
import { screen, render, fireEvent } from '@testing-library/react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { useModel, useModels } from '../../generic/model-store';
|
||||
import CourseBreadcrumbs from './CourseBreadcrumbs';
|
||||
|
||||
jest.mock('@edx/frontend-platform');
|
||||
jest.mock('react-redux');
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
|
||||
// Remove When Fully rolled out>>>
|
||||
jest.mock('../../generic/model-store');
|
||||
jest.mock('@edx/frontend-platform/auth');
|
||||
getConfig.mockImplementation(() => ({ ENABLE_JUMPNAV: 'true' }));
|
||||
getAuthenticatedUser.mockImplementation(() => ({ administrator: true }));
|
||||
// ^^^^Remove When Fully rolled out
|
||||
|
||||
useSelector.mockImplementation(() => 'loaded');
|
||||
useModels.mockImplementation((name) => {
|
||||
if (name === 'sections') {
|
||||
return [
|
||||
{
|
||||
courseId: 'course-v1:edX+DemoX+Demo_Course',
|
||||
id: 'block-v1:edX+DemoX+Demo_Course+type@chapter+block@d8a6192ade314473a78242dfeedfbf5b',
|
||||
sequenceIds: ['block-v1:edX+DemoX+Demo_Course+type@sequential+block@edx_introduction'],
|
||||
title: 'Introduction',
|
||||
},
|
||||
{
|
||||
courseId: 'course-v1:edX+DemoX+Demo_Course',
|
||||
id: 'block-v1:edX+DemoX+Demo_Course+type@chapter+block@interactive_demonstrations',
|
||||
sequenceIds: ['block-v1:edX+DemoX+Demo_Course+type@sequential+block@19a30717eff543078a5d94ae9d6c18a5',
|
||||
'block-v1:edX+DemoX+Demo_Course+type@sequential+block@basic_questions'],
|
||||
title: 'Example Week 1: Getting Started',
|
||||
},
|
||||
];
|
||||
}
|
||||
return [
|
||||
{
|
||||
id: 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@19a30717eff543078a5d94ae9d6c18a5',
|
||||
sectionId: 'block-v1:edX+DemoX+Demo_Course+type@chapter+block@interactive_demonstrations',
|
||||
title: 'Lesson 1 - Getting Started',
|
||||
unitIds: [
|
||||
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@867dddb6f55d410caaa9c1eb9c6743ec',
|
||||
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@4f6c1b4e316a419ab5b6bf30e6c708e9',
|
||||
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@3dc16db8d14842e38324e95d4030b8a0',
|
||||
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@4a1bba2a403f40bca5ec245e945b0d76',
|
||||
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@256f17a44983429fb1a60802203ee4e0',
|
||||
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@e3601c0abee6427d8c17e6d6f8fdddd1',
|
||||
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@134df56c516a4a0dbb24dd5facef746e',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@basic_questions',
|
||||
sectionId: 'block-v1:edX+DemoX+Demo_Course+type@chapter+block@interactive_demonstrations',
|
||||
title: 'Homework - Question Styles',
|
||||
unitIds: [
|
||||
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@2152d4a4aadc4cb0af5256394a3d1fc7',
|
||||
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@47dbd5f836544e61877a483c0b75606c',
|
||||
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@54bb9b142c6c4c22afc62bcb628f0e68',
|
||||
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical_0c92347a5c00',
|
||||
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical_1fef54c2b23b',
|
||||
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@2889db1677a549abb15eb4d886f95d1c',
|
||||
'block-v1:edX+DemoX+Demo_Course+type@vertical+block@e8a5cc2aed424838853defab7be45e42',
|
||||
],
|
||||
},
|
||||
];
|
||||
});
|
||||
useModel.mockImplementation(() => ({
|
||||
sectionIds: ['block-v1:edX+DemoX+Demo_Course+type@chapter+block@d8a6192ade314473a78242dfeedfbf5b',
|
||||
'block-v1:edX+DemoX+Demo_Course+type@chapter+block@interactive_demonstrations'],
|
||||
}));
|
||||
|
||||
describe('CourseBreadcrumbs', () => {
|
||||
jest.spyOn(React, 'useMemo').mockImplementation(() => [
|
||||
[
|
||||
{
|
||||
default: false,
|
||||
id: 'block-v1:edX+DemoX+Demo_Course+type@chapter+block@d8a6192ade314473a78242dfeedfbf5b',
|
||||
label: 'Introduction',
|
||||
url: 'http://localhost:2000/course/course-v1:edX+DemoX+Demo_Course/block-v1:edX+DemoX+Demo_Course+type@sequential+block@edx_introduction',
|
||||
},
|
||||
{
|
||||
default: true,
|
||||
id: 'block-v1:edX+DemoX+Demo_Course+type@chapter+block@interactive_demonstrations',
|
||||
label: 'Example Week 1: Getting Started',
|
||||
url: 'http://localhost:2000/course/course-v1:edX+DemoX+Demo_Course/block-v1:edX+DemoX+Demo_Course+type@sequential+block@19a30717eff543078a5d94ae9d6c18a5',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
id: 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@simulations', label: "Lesson 2 - Let's Get Interactive!", default: true, url: 'http://localhost:2000/course/course-v1:edX+DemoX+D…e@vertical+block@d0d804e8863c4a95a659c04d8a2b2bc0',
|
||||
},
|
||||
{
|
||||
id: 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@175e76c4951144a29d46211361266e0e', label: 'Homework - Essays', default: false, url: 'http://localhost:2000/course/course-v1:edX+DemoX+D…e@vertical+block@fb79dcbad35b466a8c6364f8ffee9050',
|
||||
},
|
||||
],
|
||||
]);
|
||||
render(
|
||||
<CourseBreadcrumbs
|
||||
courseId="course-v1:edX+DemoX+Demo_Course"
|
||||
sectionId="block-v1:edX+DemoX+Demo_Course+type@chapter+block@interactive_demonstrations"
|
||||
sequenceId="block-v1:edX+DemoX+Demo_Course+type@sequential+block@basic_questions"
|
||||
/>,
|
||||
);
|
||||
it('renders course breadcrumbs as expected, handles clicks', async () => {
|
||||
expect(screen.getByRole('navigation', { name: 'breadcrumb' })).toBeInTheDocument();
|
||||
expect(screen.queryAllByRole('button')).toHaveLength(2);
|
||||
const sectionButton = screen.getByText('Example Week 1: Getting Started');
|
||||
expect(screen.queryAllByRole('link')).toHaveLength(1);
|
||||
fireEvent.click(sectionButton);
|
||||
expect(screen.queryAllByRole('link')).toHaveLength(2);
|
||||
const menuItem = screen.queryAllByRole('link')[0];
|
||||
fireEvent.click(menuItem);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
@@ -12,7 +12,7 @@ import useWindowSize, { responsiveBreakpoints } from '../../generic/tabs/useWind
|
||||
import UpgradeNotification from '../../generic/upgrade-notification/UpgradeNotification';
|
||||
|
||||
function NotificationTray({
|
||||
intl, toggleNotificationTray, onNotificationSeen, upgradeNotificationCurrentState, setupgradeNotificationCurrentState,
|
||||
intl, toggleNotificationTray,
|
||||
}) {
|
||||
const {
|
||||
courseId,
|
||||
@@ -32,9 +32,6 @@ function NotificationTray({
|
||||
|
||||
const shouldDisplayFullScreen = useWindowSize().width < responsiveBreakpoints.large.minWidth;
|
||||
|
||||
// After three seconds, update notificationSeen (to hide red dot)
|
||||
useEffect(() => { setTimeout(onNotificationSeen, 3000); }, []);
|
||||
|
||||
return (
|
||||
<section className={classNames('notification-tray-container ml-0 ml-lg-4', { 'no-notification': !verifiedMode && !shouldDisplayFullScreen })} aria-label={intl.formatMessage(messages.notificationTray)}>
|
||||
{shouldDisplayFullScreen ? (
|
||||
@@ -67,8 +64,6 @@ function NotificationTray({
|
||||
timeOffsetMillis={timeOffsetMillis}
|
||||
courseId={courseId}
|
||||
org={org}
|
||||
upgradeNotificationCurrentState={upgradeNotificationCurrentState}
|
||||
setupgradeNotificationCurrentState={setupgradeNotificationCurrentState}
|
||||
/>
|
||||
) : <p className="notification-tray-content">{intl.formatMessage(messages.noNotificationsMessage)}</p>}
|
||||
</div>
|
||||
@@ -79,14 +74,10 @@ function NotificationTray({
|
||||
NotificationTray.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
toggleNotificationTray: PropTypes.func,
|
||||
onNotificationSeen: PropTypes.func,
|
||||
upgradeNotificationCurrentState: PropTypes.string.isRequired,
|
||||
setupgradeNotificationCurrentState: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
NotificationTray.defaultProps = {
|
||||
toggleNotificationTray: null,
|
||||
onNotificationSeen: null,
|
||||
};
|
||||
|
||||
export default injectIntl(NotificationTray);
|
||||
|
||||
@@ -1,31 +1,12 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { getLocalStorage, setLocalStorage } from '../../data/localStorage';
|
||||
|
||||
import NotificationIcon from './NotificationIcon';
|
||||
import messages from './messages';
|
||||
|
||||
function NotificationTrigger({
|
||||
intl, toggleNotificationTray, isNotificationTrayVisible, notificationStatus, setNotificationStatus,
|
||||
upgradeNotificationCurrentState,
|
||||
}) {
|
||||
/* Re-show a red dot beside the notification trigger for each of the 7 UpgradeNotification stages
|
||||
The upgradeNotificationCurrentState prop will be available after UpgradeNotification mounts. Once available,
|
||||
compare with the last state they've seen, and if it's different then set dot back to red */
|
||||
function UpdateUpgradeNotificationLastSeen() {
|
||||
if (upgradeNotificationCurrentState) {
|
||||
if (getLocalStorage('upgradeNotificationLastSeen') !== upgradeNotificationCurrentState) {
|
||||
setNotificationStatus('active');
|
||||
setLocalStorage('notificationStatus', 'active');
|
||||
setLocalStorage('upgradeNotificationLastSeen', upgradeNotificationCurrentState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => { UpdateUpgradeNotificationLastSeen(); });
|
||||
|
||||
function NotificationTrigger({ intl, toggleNotificationTray, isNotificationTrayVisible }) {
|
||||
return (
|
||||
<button
|
||||
className={classNames('notification-trigger-btn', { 'trigger-active': isNotificationTrayVisible() })}
|
||||
@@ -33,7 +14,8 @@ function NotificationTrigger({
|
||||
onClick={() => { toggleNotificationTray(); }}
|
||||
aria-label={intl.formatMessage(messages.openNotificationTrigger)}
|
||||
>
|
||||
<NotificationIcon status={notificationStatus} notificationColor="bg-danger-500" />
|
||||
{/* REV-2297 TODO: add logic for status "active" if red dot should display */}
|
||||
<NotificationIcon status="inactive" notificationColor="bg-danger-500" />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -41,10 +23,7 @@ function NotificationTrigger({
|
||||
NotificationTrigger.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
toggleNotificationTray: PropTypes.func.isRequired,
|
||||
notificationStatus: PropTypes.string.isRequired,
|
||||
setNotificationStatus: PropTypes.func.isRequired,
|
||||
isNotificationTrayVisible: PropTypes.func.isRequired,
|
||||
upgradeNotificationCurrentState: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(NotificationTrigger);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.notification-trigger-btn {
|
||||
border: 1px solid $light-400;
|
||||
background: none;
|
||||
margin-top: 1rem;
|
||||
margin-top: 0.625rem;
|
||||
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
||||
@@ -4,51 +4,27 @@ import {
|
||||
render, initializeTestStore, screen, fireEvent,
|
||||
} from '../../setupTest';
|
||||
import NotificationTrigger from './NotificationTrigger';
|
||||
import { getLocalStorage } from '../../data/localStorage';
|
||||
|
||||
describe('Notification Trigger', () => {
|
||||
let mockData;
|
||||
// let mockDataSameState;
|
||||
// let mockDataDifferentState;
|
||||
const courseMetadata = Factory.build('courseMetadata');
|
||||
|
||||
beforeEach(async () => {
|
||||
beforeAll(async () => {
|
||||
await initializeTestStore({ courseMetadata, excludeFetchCourse: true, excludeFetchSequence: true });
|
||||
mockData = {
|
||||
toggleNotificationTray: () => {},
|
||||
isNotificationTrayVisible: () => {},
|
||||
notificationStatus: 'active',
|
||||
setNotificationStatus: () => {},
|
||||
upgradeNotificationCurrentState: 'FPDdaysLeft',
|
||||
};
|
||||
});
|
||||
|
||||
it('renders notification trigger icon with red dot when notificationStatus is active', async () => {
|
||||
it('renders notification trigger with icon', async () => {
|
||||
const { container } = render(<NotificationTrigger {...mockData} />);
|
||||
expect(container).toBeInTheDocument();
|
||||
const buttonIcon = container.querySelectorAll('svg');
|
||||
expect(buttonIcon).toHaveLength(1);
|
||||
expect(screen.getByTestId('notification-dot')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders notification trigger icon WITHOUT red dot 3 seconds later', async () => {
|
||||
const { container } = render(<NotificationTrigger {...mockData} />);
|
||||
expect(container).toBeInTheDocument();
|
||||
jest.useFakeTimers();
|
||||
setTimeout(() => {
|
||||
expect(screen.queryByRole('notification-dot')).not.toBeInTheDocument();
|
||||
}, 3000);
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
it('renders notification trigger icon WITHOUT red dot within the same phase', async () => {
|
||||
const { container } = render(
|
||||
<NotificationTrigger {...mockData} upgradeNotificationCurrentState="sameState" upgradeNotificationLastSeen="sameState" />,
|
||||
);
|
||||
expect(container).toBeInTheDocument();
|
||||
const buttonIcon = container.querySelectorAll('svg');
|
||||
expect(buttonIcon).toHaveLength(1);
|
||||
expect(screen.queryByRole('notification-dot')).not.toBeInTheDocument();
|
||||
// REV-2297 TODO: update below test once the status=active or inactive is implemented
|
||||
// expect(screen.getByTestId('notification-dot')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles onClick event toggling the notification tray', async () => {
|
||||
@@ -64,16 +40,4 @@ describe('Notification Trigger', () => {
|
||||
fireEvent.click(notificationTrigger);
|
||||
expect(toggleNotificationTray).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
// rendering NotificationTrigger has the effect of calling UpdateUpgradeNotificationLastSeen()
|
||||
// Verify that local storage was updated accordingly
|
||||
it('we make the right updates when rendering a new phase (before -> after)', async () => {
|
||||
const { container } = render(
|
||||
<NotificationTrigger {...mockData} upgradeNotificationLastSeen="before" upgradeNotificationCurrentState="after" />,
|
||||
);
|
||||
expect(container).toBeInTheDocument();
|
||||
|
||||
expect(getLocalStorage('notificationStatus')).toBe('active');
|
||||
expect(getLocalStorage('upgradeNotificationLastSeen')).toBe('after');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,7 +40,7 @@ function CourseExit({ intl }) {
|
||||
} else if (mode === COURSE_EXIT_MODES.celebration) {
|
||||
body = (<CourseCelebration />);
|
||||
} else {
|
||||
return (<Redirect to={`/course/${courseId}`} />);
|
||||
return (<Redirect to={`/c/${courseId}`} />);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -94,7 +94,7 @@ describe('Course Exit Pages', () => {
|
||||
},
|
||||
});
|
||||
await fetchAndRender(<CourseExit />);
|
||||
expect(global.location.href).toEqual(`http://localhost/course/${defaultMetadata.id}`);
|
||||
expect(global.location.href).toEqual(`http://localhost/c/${defaultMetadata.id}`);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import { useModel } from '../../../generic/model-store';
|
||||
|
||||
import CourseLicense from '../course-license';
|
||||
import messages from './messages';
|
||||
import HiddenAfterDue from './hidden-after-due';
|
||||
import { SequenceNavigation, UnitNavigation } from './sequence-navigation';
|
||||
import SequenceContent from './SequenceContent';
|
||||
import NotificationTray from '../NotificationTray';
|
||||
@@ -41,17 +40,14 @@ function Sequence({
|
||||
toggleNotificationTray,
|
||||
notificationTrayVisible,
|
||||
isNotificationTrayVisible,
|
||||
notificationStatus,
|
||||
setNotificationStatus,
|
||||
onNotificationSeen,
|
||||
upgradeNotificationCurrentState,
|
||||
setupgradeNotificationCurrentState,
|
||||
mmp2p,
|
||||
}) {
|
||||
const course = useModel('coursewareMeta', courseId);
|
||||
const sequence = useModel('sequences', sequenceId);
|
||||
const unit = useModel('units', unitId);
|
||||
const sequenceStatus = useSelector(state => state.courseware.sequenceStatus);
|
||||
const specialExamsEnabledWaffleFlag = useSelector(state => state.courseware.specialExamsEnabledWaffleFlag);
|
||||
const proctoredExamsEnabledWaffleFlag = useSelector(state => state.courseware.proctoredExamsEnabledWaffleFlag);
|
||||
const shouldDisplayNotificationTrigger = useWindowSize().width < responsiveBreakpoints.small.minWidth;
|
||||
|
||||
const handleNext = () => {
|
||||
@@ -143,15 +139,29 @@ function Sequence({
|
||||
);
|
||||
}
|
||||
|
||||
if (sequenceStatus === 'loaded' && sequence.isHiddenAfterDue) {
|
||||
// Shouldn't even be here - these sequences are normally stripped out of the navigation.
|
||||
// But we are here, so render a notice instead of the normal content.
|
||||
return <HiddenAfterDue courseId={courseId} />;
|
||||
/*
|
||||
TODO: When the micro-frontend supports viewing special exams without redirecting to the legacy
|
||||
experience, we can remove this whole conditional. For now, though, we show the spinner here
|
||||
because we expect CoursewareContainer to be performing a redirect to the legacy experience while
|
||||
we're waiting. That redirect may take a few seconds, so we show the spinner in the meantime.
|
||||
*/
|
||||
if (sequenceStatus === 'loaded') {
|
||||
const shouldRedirectSpecialExams = sequence.isTimeLimited && !specialExamsEnabledWaffleFlag;
|
||||
const shouldRedirectProctoredExams = sequence.isProctored && specialExamsEnabledWaffleFlag
|
||||
&& !proctoredExamsEnabledWaffleFlag;
|
||||
|
||||
if (shouldRedirectSpecialExams || shouldRedirectProctoredExams) {
|
||||
return (
|
||||
<PageLoading
|
||||
srMessage={intl.formatMessage(messages['learn.loading.learning.sequence'])}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const gated = sequence && sequence.gatedContent !== undefined && sequence.gatedContent.gated;
|
||||
const goToCourseExitPage = () => {
|
||||
history.push(`/course/${courseId}/course-end`);
|
||||
history.push(`/c/${courseId}/course-end`);
|
||||
};
|
||||
|
||||
const defaultContent = (
|
||||
@@ -184,9 +194,6 @@ function Sequence({
|
||||
<NotificationTrigger
|
||||
toggleNotificationTray={toggleNotificationTray}
|
||||
isNotificationTrayVisible={isNotificationTrayVisible}
|
||||
notificationStatus={notificationStatus}
|
||||
setNotificationStatus={setNotificationStatus}
|
||||
upgradeNotificationCurrentState={upgradeNotificationCurrentState}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -222,10 +229,6 @@ function Sequence({
|
||||
<NotificationTray
|
||||
toggleNotificationTray={toggleNotificationTray}
|
||||
notificationTrayVisible={notificationTrayVisible}
|
||||
notificationStatus={notificationStatus}
|
||||
onNotificationSeen={onNotificationSeen}
|
||||
upgradeNotificationCurrentState={upgradeNotificationCurrentState}
|
||||
setupgradeNotificationCurrentState={setupgradeNotificationCurrentState}
|
||||
/>
|
||||
) : null }
|
||||
|
||||
@@ -241,12 +244,7 @@ function Sequence({
|
||||
if (sequenceStatus === 'loaded') {
|
||||
return (
|
||||
<div>
|
||||
<SequenceExamWrapper
|
||||
sequence={sequence}
|
||||
courseId={courseId}
|
||||
isStaff={course.isStaff}
|
||||
originalUserIsStaff={course.originalUserIsStaff}
|
||||
>
|
||||
<SequenceExamWrapper sequence={sequence} courseId={courseId} isStaff={course.isStaff}>
|
||||
{defaultContent}
|
||||
</SequenceExamWrapper>
|
||||
<CourseLicense license={course.license || undefined} />
|
||||
@@ -273,11 +271,6 @@ Sequence.propTypes = {
|
||||
toggleNotificationTray: PropTypes.func,
|
||||
notificationTrayVisible: PropTypes.bool,
|
||||
isNotificationTrayVisible: PropTypes.func,
|
||||
notificationStatus: PropTypes.string.isRequired,
|
||||
setNotificationStatus: PropTypes.func.isRequired,
|
||||
onNotificationSeen: PropTypes.func,
|
||||
upgradeNotificationCurrentState: PropTypes.string.isRequired,
|
||||
setupgradeNotificationCurrentState: PropTypes.func.isRequired,
|
||||
|
||||
/** [MM-P2P] Experiment */
|
||||
mmp2p: PropTypes.shape({
|
||||
@@ -299,7 +292,6 @@ Sequence.defaultProps = {
|
||||
toggleNotificationTray: null,
|
||||
notificationTrayVisible: null,
|
||||
isNotificationTrayVisible: null,
|
||||
onNotificationSeen: null,
|
||||
|
||||
/** [MM-P2P] Experiment */
|
||||
mmp2p: {
|
||||
|
||||
@@ -81,7 +81,11 @@ describe('Sequence', () => {
|
||||
expect(screen.queryByText('Loading locked content messaging...')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders correctly for hidden after due content', async () => {
|
||||
it('renders correctly for exam content', async () => {
|
||||
// Exams should NOT render in the Sequence. They should permanently show a spinner until the
|
||||
// application redirects away from the page. Note that this component is not responsible for
|
||||
// that redirect behavior, so there's no record of it here.
|
||||
// See CoursewareContainer.jsx "checkExamRedirect" function.
|
||||
const sequenceBlocks = [Factory.build(
|
||||
'block',
|
||||
{ type: 'sequential', children: [unitBlocks.map(block => block.id)] },
|
||||
@@ -89,7 +93,7 @@ describe('Sequence', () => {
|
||||
)];
|
||||
const sequenceMetadata = [Factory.build(
|
||||
'sequenceMetadata',
|
||||
{ is_hidden_after_due: true },
|
||||
{ is_time_limited: true },
|
||||
{ courseId: courseMetadata.id, unitBlocks, sequenceBlock: sequenceBlocks[0] },
|
||||
)];
|
||||
const testStore = await initializeTestStore(
|
||||
@@ -97,19 +101,15 @@ describe('Sequence', () => {
|
||||
courseMetadata, unitBlocks, sequenceBlocks, sequenceMetadata,
|
||||
}, false,
|
||||
);
|
||||
render(
|
||||
const { container } = render(
|
||||
<Sequence {...mockData} {...{ sequenceId: sequenceBlocks[0].id }} />,
|
||||
{ store: testStore },
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('The due date for this assignment has passed.')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByRole('link', { name: 'progress page' }))
|
||||
.toHaveAttribute('href', 'http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course_1/progress');
|
||||
|
||||
// No normal content or navigation should be rendered. Just the above alert.
|
||||
expect(screen.queryAllByRole('button').length).toEqual(0);
|
||||
// We expect that the sequence container isn't rendering at all.
|
||||
expect(container.querySelector('.sequence-container')).toBeNull();
|
||||
// But that we're seeing a nice spinner.
|
||||
expect(screen.queryByText('Loading learning sequence...')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays error message on sequence load failure', async () => {
|
||||
|
||||
@@ -89,9 +89,10 @@ function Unit({
|
||||
/** [MM-P2P] Experiment */
|
||||
mmp2p,
|
||||
}) {
|
||||
const unit = useModel('units', id);
|
||||
const { authenticatedUser } = useContext(AppContext);
|
||||
const view = authenticatedUser ? 'student_view' : 'public_view';
|
||||
let iframeUrl = `${getConfig().LMS_BASE_URL}/xblock/${id}?show_title=0&show_bookmark_button=0&recheck_access=1&view=${view}`;
|
||||
let iframeUrl = `${getConfig().LMS_BASE_URL}/xblock/${(unit.decoded_id || id)}?show_title=0&show_bookmark_button=0&recheck_access=1&view=${view}`;
|
||||
if (format) {
|
||||
iframeUrl += `&format=${format}`;
|
||||
}
|
||||
@@ -101,7 +102,6 @@ function Unit({
|
||||
const [modalOptions, setModalOptions] = useState({ open: false });
|
||||
const [shouldDisplayHonorCode, setShouldDisplayHonorCode] = useState(false);
|
||||
|
||||
const unit = useModel('units', id);
|
||||
const course = useModel('coursewareMeta', courseId);
|
||||
const {
|
||||
contentTypeGatingEnabled,
|
||||
|
||||
@@ -44,6 +44,7 @@ describe('Unit', () => {
|
||||
id: unit.id,
|
||||
courseId: courseMetadata.id,
|
||||
format: 'Homework',
|
||||
decoded_id: unit.decoded_id,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -53,7 +54,7 @@ describe('Unit', () => {
|
||||
const renderedUnit = screen.getByTitle(unit.display_name);
|
||||
expect(renderedUnit).toHaveAttribute('height', String(0));
|
||||
expect(renderedUnit).toHaveAttribute(
|
||||
'src', `http://localhost:18000/xblock/${mockData.id}?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view&format=${mockData.format}`,
|
||||
'src', `http://localhost:18000/xblock/${mockData.decoded_id}?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view&format=${mockData.format}`,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ function ContentLock({
|
||||
intl, courseId, prereqSectionName, prereqId, sequenceTitle,
|
||||
}) {
|
||||
const handleClick = useCallback(() => {
|
||||
history.push(`/course/${courseId}/${prereqId}`);
|
||||
history.push(`/c/${courseId}/${prereqId}`);
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -38,6 +38,6 @@ describe('Content Lock', () => {
|
||||
render(<ContentLock {...mockData} />);
|
||||
fireEvent.click(screen.getByRole('button'));
|
||||
|
||||
expect(history.push).toHaveBeenCalledWith(`/course/${mockData.courseId}/${mockData.prereqId}`);
|
||||
expect(history.push).toHaveBeenCalledWith(`/c/${mockData.courseId}/${mockData.prereqId}`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import { Alert, Hyperlink } from '@edx/paragon';
|
||||
import { Info } from '@edx/paragon/icons';
|
||||
|
||||
import { useModel } from '../../../../generic/model-store';
|
||||
|
||||
import messages from './messages';
|
||||
|
||||
function HiddenAfterDue({ courseId, intl }) {
|
||||
const { tabs } = useModel('coursewareMeta', courseId);
|
||||
|
||||
const progressTab = tabs.find(tab => tab.slug === 'progress');
|
||||
const progressLink = progressTab && progressTab.url && (
|
||||
<Hyperlink
|
||||
style={{ textDecoration: 'underline' }}
|
||||
destination={progressTab.url}
|
||||
className="text-reset"
|
||||
>
|
||||
{intl.formatMessage(messages.progressPage)}
|
||||
</Hyperlink>
|
||||
);
|
||||
|
||||
return (
|
||||
<Alert variant="info" icon={Info}>
|
||||
<h3>{intl.formatMessage(messages.header)}</h3>
|
||||
<p>
|
||||
{intl.formatMessage(messages.description)}
|
||||
{progressLink && (
|
||||
<>
|
||||
<br />
|
||||
<FormattedMessage
|
||||
id="learn.hiddenAfterDue.gradeAvailable"
|
||||
defaultMessage="If you have completed this assignment, your grade is available on the {progressPage}."
|
||||
values={{
|
||||
progressPage: progressLink,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
HiddenAfterDue.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
courseId: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(HiddenAfterDue);
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './HiddenAfterDue';
|
||||
@@ -1,23 +0,0 @@
|
||||
import { defineMessages } from '@edx/frontend-platform/i18n';
|
||||
|
||||
const messages = defineMessages({
|
||||
header: {
|
||||
id: 'learn.hiddenAfterDue.header',
|
||||
defaultMessage: 'The due date for this assignment has passed.',
|
||||
},
|
||||
description: {
|
||||
id: 'learn.hiddenAfterDue.description',
|
||||
defaultMessage: 'Because the due date has passed, this assignment is no longer available.',
|
||||
},
|
||||
gradeAvailable: {
|
||||
id: 'learn.hiddenAfterDue.gradeAvailable',
|
||||
defaultMessage: 'If you have completed this assignment, your grade is available on the {progressPage}.',
|
||||
},
|
||||
progressPage: {
|
||||
id: 'learn.hiddenAfterDue.progressPage',
|
||||
defaultMessage: 'progress page',
|
||||
description: 'This is the text for the link embedded in learn.hiddenAfterDue.gradeAvailable',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
@@ -13,7 +13,7 @@ function HonorCode({ intl, courseId }) {
|
||||
const siteName = getConfig().SITE_NAME;
|
||||
const honorCodeUrl = `${process.env.TERMS_OF_SERVICE_URL}#honor-code`;
|
||||
|
||||
const handleCancel = () => history.push(`/course/${courseId}/home`);
|
||||
const handleCancel = () => history.push(`/c/${courseId}/home`);
|
||||
|
||||
const handleAgree = () => {
|
||||
dispatch(saveIntegritySignature(courseId));
|
||||
|
||||
@@ -28,6 +28,6 @@ describe('Honor Code', () => {
|
||||
|
||||
const cancelButton = screen.getByText('Cancel');
|
||||
fireEvent.click(cancelButton);
|
||||
expect(history.push).toHaveBeenCalledWith(`/course/${mockData.courseId}/home`);
|
||||
expect(history.push).toHaveBeenCalledWith(`/c/${mockData.courseId}/home`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -113,7 +113,7 @@ function LockPaywall({
|
||||
<span className="fa-li"><FontAwesomeIcon icon={faCheck} /></span>
|
||||
<FormattedMessage
|
||||
id="gatedContent.paragraph.bulletOne"
|
||||
defaultMessage="Earn a {verifiedCertLink} of completion to showcase on your resumé"
|
||||
defaultMessage="Earn a {verifiedCertLink} of completion to showcase on your resume"
|
||||
values={{ verifiedCertLink }}
|
||||
className="bullet-text"
|
||||
/>
|
||||
@@ -149,7 +149,7 @@ function LockPaywall({
|
||||
|
||||
<div
|
||||
className={
|
||||
classNames('d-md-flex align-items-md-center text-right', {
|
||||
classNames('p-md-0 d-md-flex align-items-md-center text-right', {
|
||||
'col-md-5 mx-md-0': notificationTrayVisible, 'col-md-4 mx-md-3 justify-content-center': !notificationTrayVisible && !shouldDisplayGatedContentTwoColumnsHalf, 'col-md-11 justify-content-end': shouldDisplayGatedContentOneColumn && !shouldDisplayGatedContentTwoColumns, 'col-md-6 justify-content-center': shouldDisplayGatedContentTwoColumnsHalf,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
.alert-content.lock-paywall-container {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.lock-paywall-container svg {
|
||||
color: $primary-700;
|
||||
}
|
||||
|
||||
@@ -55,5 +55,8 @@ Factory.define('courseMetadata')
|
||||
linkedin_add_to_profile_url: null,
|
||||
related_programs: null,
|
||||
user_needs_integrity_signature: false,
|
||||
is_mfe_special_exams_enabled: false,
|
||||
is_mfe_proctored_exams_enabled: false,
|
||||
recommendations: null,
|
||||
mfe_short_url_is_active: true,
|
||||
});
|
||||
|
||||
@@ -36,6 +36,9 @@ Factory.define('sequenceMetadata')
|
||||
prereq_section_name: `${sequenceBlock.display_name}-prereq`,
|
||||
gated_section_name: sequenceBlock.display_name,
|
||||
}))
|
||||
|
||||
.attr('decoded_id', ['sequenceBlock'], sequenceBlock => sequenceBlock.decoded_id)
|
||||
.attr('hash_key', ['sequenceBlock'], sequenceBlock => sequenceBlock.hash_key)
|
||||
.attr('items', ['unitBlocks', 'sequenceBlock'], (unitBlocks, sequenceBlock) => unitBlocks.map(
|
||||
unitBlock => ({
|
||||
href: '',
|
||||
@@ -44,10 +47,12 @@ Factory.define('sequenceMetadata')
|
||||
bookmarked: unitBlock.bookmarked || false,
|
||||
path: `Chapter Display Name > ${sequenceBlock.display_name} > ${unitBlock.display_name}`,
|
||||
type: unitBlock.type,
|
||||
hash_key: unitBlock.hash_key,
|
||||
complete: unitBlock.complete || null,
|
||||
content: '',
|
||||
page_title: unitBlock.display_name,
|
||||
contains_content_type_gated_content: unitBlock.contains_content_type_gated_content,
|
||||
decoded_id: unitBlock.decoded_id,
|
||||
}),
|
||||
))
|
||||
.attrs({
|
||||
@@ -58,7 +63,6 @@ Factory.define('sequenceMetadata')
|
||||
save_position: true,
|
||||
prev_url: null,
|
||||
is_time_limited: false,
|
||||
is_hidden_after_due: false,
|
||||
show_completion: true,
|
||||
banner_text: null,
|
||||
format: 'Homework',
|
||||
|
||||
@@ -38,6 +38,7 @@ export function normalizeBlocks(courseId, blocks) {
|
||||
title: block.display_name,
|
||||
legacyWebUrl: block.legacy_web_url,
|
||||
unitIds: block.children || [],
|
||||
hash_key: block.hash_key,
|
||||
};
|
||||
break;
|
||||
case 'vertical':
|
||||
@@ -46,6 +47,7 @@ export function normalizeBlocks(courseId, blocks) {
|
||||
id: block.id,
|
||||
title: block.display_name,
|
||||
legacyWebUrl: block.legacy_web_url,
|
||||
hash_key: block.hash_key,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
@@ -87,7 +89,6 @@ export function normalizeBlocks(courseId, blocks) {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return models;
|
||||
}
|
||||
|
||||
@@ -219,7 +220,10 @@ function normalizeMetadata(metadata) {
|
||||
linkedinAddToProfileUrl: data.linkedin_add_to_profile_url,
|
||||
relatedPrograms: camelCaseObject(data.related_programs),
|
||||
userNeedsIntegritySignature: data.user_needs_integrity_signature,
|
||||
specialExamsEnabledWaffleFlag: data.is_mfe_special_exams_enabled,
|
||||
proctoredExamsEnabledWaffleFlag: data.is_mfe_proctored_exams_enabled,
|
||||
isMasquerading: data.original_user_is_staff && !data.is_staff,
|
||||
shortLinkFeatureFlag: data.mfe_short_url_is_active,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -251,12 +255,13 @@ function normalizeSequenceMetadata(sequence) {
|
||||
gatedContent: camelCaseObject(sequence.gated_content),
|
||||
isTimeLimited: sequence.is_time_limited,
|
||||
isProctored: sequence.is_proctored,
|
||||
isHiddenAfterDue: sequence.is_hidden_after_due,
|
||||
// Position comes back from the server 1-indexed. Adjust here.
|
||||
activeUnitIndex: sequence.position ? sequence.position - 1 : 0,
|
||||
saveUnitPosition: sequence.save_position,
|
||||
showCompletion: sequence.show_completion,
|
||||
allowProctoringOptOut: sequence.allow_proctoring_opt_out,
|
||||
hash_key: sequence.hash_key,
|
||||
decoded_id: sequence.decoded_id,
|
||||
},
|
||||
units: sequence.items.map(unit => ({
|
||||
id: unit.id,
|
||||
@@ -267,14 +272,14 @@ function normalizeSequenceMetadata(sequence) {
|
||||
contentType: unit.type,
|
||||
graded: unit.graded,
|
||||
containsContentTypeGatedContent: unit.contains_content_type_gated_content,
|
||||
decoded_id: unit.decoded_id,
|
||||
hash_key: unit.hash_key,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
export async function getSequenceMetadata(sequenceId) {
|
||||
const { data } = await getAuthenticatedHttpClient()
|
||||
.get(`${getConfig().LMS_BASE_URL}/api/courseware/sequence/${sequenceId}`, {});
|
||||
|
||||
return normalizeSequenceMetadata(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,180 +6,6 @@
|
||||
"name": "lms"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
"description": "a request to fetch tab",
|
||||
"providerState": "Tab data exists for course_id course-v1:edX+DemoX+Demo_Course",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/api/course_home/v1/course_metadata/course-v1:edX+DemoX+Demo_Course"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
},
|
||||
"body": {
|
||||
"can_show_upgrade_sock": false,
|
||||
"verified_mode": {
|
||||
"access_expiration_date": null,
|
||||
"currency": "USD",
|
||||
"currency_symbol": "$",
|
||||
"price": 149,
|
||||
"sku": "8CF08E5",
|
||||
"upgrade_url": "http://localhost:18130/basket/add/?sku=8CF08E5"
|
||||
},
|
||||
"can_load_courseware": true,
|
||||
"celebrations": {
|
||||
"first_section": false,
|
||||
"streak_length_to_celebrate": null,
|
||||
"streak_discount_enabled": false
|
||||
},
|
||||
"course_access": {
|
||||
"has_access": true,
|
||||
"error_code": null,
|
||||
"developer_message": null,
|
||||
"user_message": null,
|
||||
"additional_context_user_message": null,
|
||||
"user_fragment": null
|
||||
},
|
||||
"course_id": "course-v1:edX+DemoX+Demo_Course",
|
||||
"is_enrolled": true,
|
||||
"is_self_paced": false,
|
||||
"is_staff": true,
|
||||
"number": "DemoX",
|
||||
"org": "edX",
|
||||
"original_user_is_staff": true,
|
||||
"start": "2013-02-05T05:00:00Z",
|
||||
"tabs": [
|
||||
{
|
||||
"tab_id": "courseware",
|
||||
"title": "Course",
|
||||
"url": "http://localhost:2000/course/course-v1:edX+DemoX+Demo_Course/home"
|
||||
}
|
||||
],
|
||||
"title": "Demonstration Course",
|
||||
"username": "edx"
|
||||
},
|
||||
"matchingRules": {
|
||||
"$.body.can_show_upgrade_sock": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.verified_mode": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.can_load_courseware": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.celebrations": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.course_access.has_access": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.course_id": {
|
||||
"match": "regex",
|
||||
"regex": "[\\w\\-~.:]"
|
||||
},
|
||||
"$.body.is_enrolled": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.is_self_paced": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.is_staff": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.number": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.org": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.original_user_is_staff": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.start": {
|
||||
"match": "regex",
|
||||
"regex": "^(?:[1-9]\\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:Z|[+-][01]\\d:[0-5]\\d)$"
|
||||
},
|
||||
"$.body.tabs": {
|
||||
"min": 1
|
||||
},
|
||||
"$.body.tabs[*].*": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.title": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.username": {
|
||||
"match": "type"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "a request to fetch dates tab",
|
||||
"providerState": "course date blocks exist for course_id course-v1:edX+DemoX+Demo_Course",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/api/course_home/v1/dates/course-v1:edX+DemoX+Demo_Course"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
},
|
||||
"body": {
|
||||
"dates_banner_info": {
|
||||
"missed_deadlines": false,
|
||||
"content_type_gating_enabled": false,
|
||||
"missed_gated_content": false,
|
||||
"verified_upgrade_link": "http://localhost:18130/basket/add/?sku=8CF08E5"
|
||||
},
|
||||
"course_date_blocks": [
|
||||
{
|
||||
"assignment_type": null,
|
||||
"complete": null,
|
||||
"date": "2013-02-05T05:00:00Z",
|
||||
"date_type": "verified-upgrade-deadline",
|
||||
"description": "You are still eligible to upgrade to a Verified Certificate! Pursue it to highlight the knowledge and skills you gain in this course.",
|
||||
"learner_has_access": true,
|
||||
"link": "http://localhost:18130/basket/add/?sku=8CF08E5",
|
||||
"link_text": "Upgrade to Verified Certificate",
|
||||
"title": "Verification Upgrade Deadline",
|
||||
"extra_info": null,
|
||||
"first_component_block_id": ""
|
||||
}
|
||||
],
|
||||
"has_ended": false,
|
||||
"learner_is_full_access": true,
|
||||
"user_timezone": null
|
||||
},
|
||||
"matchingRules": {
|
||||
"$.body.dates_banner_info": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.course_date_blocks": {
|
||||
"min": 1
|
||||
},
|
||||
"$.body.course_date_blocks[*].*": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.course_date_blocks[*].date": {
|
||||
"match": "regex",
|
||||
"regex": "^(?:[1-9]\\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:Z|[+-][01]\\d:[0-5]\\d)$"
|
||||
},
|
||||
"$.body.course_date_blocks[*].date_type": {
|
||||
"match": "regex",
|
||||
"regex": "^(event|todays-date|course-start-date|course-end-date|assignment-due-date|course-expired-date|certificate-available-date|verified-upgrade-deadline|verification-deadline-date)$"
|
||||
},
|
||||
"$.body.has_ended": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.learner_is_full_access": {
|
||||
"match": "type"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "a request to get course blocks",
|
||||
"providerState": "Blocks data exists for course_id course-v1:edX+DemoX+Demo_Course",
|
||||
@@ -271,7 +97,7 @@
|
||||
],
|
||||
"user_timezone": null,
|
||||
"verified_mode": {
|
||||
"access_expiration_date": "2013-02-05T05:00:00Z",
|
||||
"access_expiration_date": null,
|
||||
"currency": "USD",
|
||||
"currency_symbol": "$",
|
||||
"price": 149,
|
||||
@@ -296,9 +122,9 @@
|
||||
},
|
||||
"marketing_url": null,
|
||||
"celebrations": {
|
||||
"first_section": false,
|
||||
"irst_section": false,
|
||||
"streak_length_to_celebrate": null,
|
||||
"streak_discount_enabled": false
|
||||
"streak_discount_experiment_enabled": false
|
||||
},
|
||||
"user_has_passing_grade": false,
|
||||
"course_exit_page_is_active": false,
|
||||
@@ -311,6 +137,8 @@
|
||||
"verify_identity_url": null,
|
||||
"verification_status": "none",
|
||||
"linkedin_add_to_profile_url": null,
|
||||
"is_mfe_special_exams_enabled": false,
|
||||
"is_mfe_proctored_exams_enabled": false,
|
||||
"user_needs_integrity_signature": false
|
||||
},
|
||||
"matchingRules": {
|
||||
@@ -404,10 +232,6 @@
|
||||
"$.body.verified_mode": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.verified_mode.access_expiration_date": {
|
||||
"match": "regex",
|
||||
"regex": "^(?:[1-9]\\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:Z|[+-][01]\\d:[0-5]\\d)$"
|
||||
},
|
||||
"$.body.show_calculator": {
|
||||
"match": "type"
|
||||
},
|
||||
@@ -423,16 +247,19 @@
|
||||
"$.body.course_access": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.course_access.has_access": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.notes.enabled": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.notes.visible": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.celebrations.first_section": {
|
||||
"$.body.celebrations.irst_section": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.celebrations.streak_discount_enabled": {
|
||||
"$.body.celebrations.streak_discount_experiment_enabled": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.user_has_passing_grade": {
|
||||
@@ -447,6 +274,12 @@
|
||||
"$.body.verification_status": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.is_mfe_special_exams_enabled": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.is_mfe_proctored_exams_enabled": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.user_needs_integrity_signature": {
|
||||
"match": "type"
|
||||
}
|
||||
@@ -481,7 +314,6 @@
|
||||
"item_id": "block-v1:edX+DemoX+Demo_Course+type@sequential+block@basic_questions",
|
||||
"is_time_limited": false,
|
||||
"is_proctored": false,
|
||||
"is_hidden_after_due": true,
|
||||
"position": null,
|
||||
"tag": "sequential",
|
||||
"banner_text": null,
|
||||
@@ -581,48 +413,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "a request to get Resume block",
|
||||
"providerState": "Resume block exists for course_id course-v1:edX+DemoX+Demo_Course",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"path": "/api/courseware/resume/course-v1:edX+DemoX+Demo_Course"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
},
|
||||
"body": {
|
||||
"block_id": "642fadf46d074aabb637f20af320fb31",
|
||||
"section_id": "642fadf46d074aabb637f20af320fb87",
|
||||
"unit_id": "642fadf46d074aabb637f20af320fb99"
|
||||
},
|
||||
"matchingRules": {
|
||||
"$.body.block_id": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.section_id": {
|
||||
"match": "type"
|
||||
},
|
||||
"$.body.unit_id": {
|
||||
"match": "type"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "a request to send activation email",
|
||||
"providerState": "A logged-in user may or may not be active",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"path": "/api/send_account_activation_email"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@@ -630,4 +420,4 @@
|
||||
"version": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,19 +4,9 @@ import { mergeConfig, getConfig } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
|
||||
|
||||
import {
|
||||
getCourseBlocks,
|
||||
getCourseMetadata,
|
||||
getSequenceMetadata,
|
||||
postSequencePosition,
|
||||
getBlockCompletion,
|
||||
getResumeBlock,
|
||||
sendActivationEmail,
|
||||
getCourseBlocks, getCourseMetadata, getSequenceMetadata, postSequencePosition, getBlockCompletion,
|
||||
} from '../api';
|
||||
|
||||
import { initializeMockApp } from '../../../setupTest';
|
||||
import {
|
||||
courseId, dateRegex, opaqueKeysRegex, sequenceId, usageId,
|
||||
} from '../../../pacts/constants';
|
||||
|
||||
const {
|
||||
somethingLike: like, term, boolean, string, eachLike, integer,
|
||||
@@ -25,13 +15,17 @@ const provider = new Pact({
|
||||
consumer: 'frontend-app-learning',
|
||||
provider: 'lms',
|
||||
log: path.resolve(process.cwd(), 'src/courseware/data/pact-tests/logs', 'pact.log'),
|
||||
dir: path.resolve(process.cwd(), 'src/pacts'),
|
||||
pactfileWriteMode: 'merge',
|
||||
dir: path.resolve(process.cwd(), 'src/courseware/data/pact-tests'),
|
||||
logLevel: 'DEBUG',
|
||||
cors: true,
|
||||
});
|
||||
|
||||
describe('Courseware Service', () => {
|
||||
const courseId = 'course-v1:edX+DemoX+Demo_Course';
|
||||
const sequenceId = 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@basic_questions';
|
||||
const usageId = 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@47dbd5f836544e61877a483c0b75606c';
|
||||
const dateRegex = '^(?:[1-9]\\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:Z|[+-][01]\\d:[0-5]\\d)$';
|
||||
const opaqueKeysRegex = '[\\w\\-~.:]';
|
||||
let authenticatedUser;
|
||||
beforeAll(async () => {
|
||||
initializeMockApp();
|
||||
@@ -63,7 +57,8 @@ describe('Courseware Service', () => {
|
||||
},
|
||||
willRespondWith: {
|
||||
status: 200,
|
||||
body: {
|
||||
body:
|
||||
{
|
||||
root: string('block-v1:edX+DemoX+Demo_Course+type@course+block@course'),
|
||||
blocks: like({
|
||||
'block-v1:edX+DemoX+Demo_Course+type@course+block@course': {
|
||||
@@ -110,7 +105,8 @@ describe('Courseware Service', () => {
|
||||
},
|
||||
willRespondWith: {
|
||||
status: 200,
|
||||
body: {
|
||||
body:
|
||||
{
|
||||
access_expiration: {
|
||||
expiration_date: term({
|
||||
generate: '2013-02-05T05:00:00Z',
|
||||
@@ -174,10 +170,7 @@ describe('Courseware Service', () => {
|
||||
}),
|
||||
user_timezone: null,
|
||||
verified_mode: like({
|
||||
access_expiration_date: term({
|
||||
generate: '2013-02-05T05:00:00Z',
|
||||
matcher: dateRegex,
|
||||
}),
|
||||
access_expiration_date: null,
|
||||
currency: 'USD',
|
||||
currency_symbol: '$',
|
||||
price: 149,
|
||||
@@ -189,7 +182,7 @@ describe('Courseware Service', () => {
|
||||
can_view_legacy_courseware: boolean(true),
|
||||
is_staff: boolean(true),
|
||||
course_access: like({
|
||||
has_access: true,
|
||||
has_access: boolean(true),
|
||||
error_code: null,
|
||||
developer_message: null,
|
||||
user_message: null,
|
||||
@@ -199,9 +192,9 @@ describe('Courseware Service', () => {
|
||||
notes: { enabled: boolean(false), visible: boolean(true) },
|
||||
marketing_url: null,
|
||||
celebrations: {
|
||||
first_section: boolean(false),
|
||||
irst_section: boolean(false),
|
||||
streak_length_to_celebrate: null,
|
||||
streak_discount_enabled: boolean(false),
|
||||
streak_discount_experiment_enabled: boolean(false),
|
||||
},
|
||||
user_has_passing_grade: boolean(false),
|
||||
course_exit_page_is_active: boolean(false),
|
||||
@@ -211,6 +204,8 @@ describe('Courseware Service', () => {
|
||||
verify_identity_url: null,
|
||||
verification_status: string('none'),
|
||||
linkedin_add_to_profile_url: null,
|
||||
is_mfe_special_exams_enabled: boolean(false),
|
||||
is_mfe_proctored_exams_enabled: boolean(false),
|
||||
user_needs_integrity_signature: boolean(false),
|
||||
},
|
||||
},
|
||||
@@ -256,28 +251,30 @@ describe('Courseware Service', () => {
|
||||
isStaff: true,
|
||||
license: 'all-rights-reserved',
|
||||
verifiedMode: {
|
||||
accessExpirationDate: '2013-02-05T05:00:00Z',
|
||||
accessExpirationDate: null,
|
||||
currency: 'USD',
|
||||
currencySymbol: '$',
|
||||
price: 149,
|
||||
sku: '8CF08E5',
|
||||
upgradeUrl: `${getConfig().ECOMMERCE_BASE_URL}/basket/add/?sku=8CF08E5`,
|
||||
},
|
||||
tabs: [{
|
||||
title: 'Course',
|
||||
slug: 'courseware',
|
||||
priority: 0,
|
||||
type: 'courseware',
|
||||
url: `${getConfig().BASE_URL}/course/course-v1:edX+DemoX+Demo_Course/home`,
|
||||
}],
|
||||
tabs: [
|
||||
{
|
||||
title: 'Course',
|
||||
slug: 'courseware',
|
||||
priority: 0,
|
||||
type: 'courseware',
|
||||
url: `${getConfig().BASE_URL}/course/course-v1:edX+DemoX+Demo_Course/home`,
|
||||
},
|
||||
],
|
||||
userTimezone: null,
|
||||
showCalculator: false,
|
||||
notes: { enabled: false, visible: true },
|
||||
marketingUrl: null,
|
||||
celebrations: {
|
||||
firstSection: false,
|
||||
irstSection: false,
|
||||
streakLengthToCelebrate: null,
|
||||
streakDiscountEnabled: false,
|
||||
streakDiscountExperimentEnabled: false,
|
||||
},
|
||||
userHasPassingGrade: false,
|
||||
courseExitPageIsActive: false,
|
||||
@@ -293,6 +290,8 @@ describe('Courseware Service', () => {
|
||||
linkedinAddToProfileUrl: null,
|
||||
relatedPrograms: null,
|
||||
userNeedsIntegritySignature: false,
|
||||
specialExamsEnabledWaffleFlag: false,
|
||||
proctoredExamsEnabledWaffleFlag: false,
|
||||
isMasquerading: false,
|
||||
};
|
||||
const response = await getCourseMetadata(courseId);
|
||||
@@ -312,7 +311,8 @@ describe('Courseware Service', () => {
|
||||
},
|
||||
willRespondWith: {
|
||||
status: 200,
|
||||
body: {
|
||||
body:
|
||||
{
|
||||
items: eachLike({
|
||||
content: '',
|
||||
page_title: 'Pointing on a Picture',
|
||||
@@ -327,7 +327,6 @@ describe('Courseware Service', () => {
|
||||
item_id: string('block-v1:edX+DemoX+Demo_Course+type@sequential+block@basic_questions'),
|
||||
is_time_limited: boolean(false),
|
||||
is_proctored: boolean(false),
|
||||
is_hidden_after_due: boolean(false),
|
||||
position: null,
|
||||
tag: boolean('sequential'),
|
||||
banner_text: null,
|
||||
@@ -364,22 +363,23 @@ describe('Courseware Service', () => {
|
||||
},
|
||||
isTimeLimited: false,
|
||||
isProctored: false,
|
||||
isHiddenAfterDue: false,
|
||||
activeUnitIndex: 0,
|
||||
saveUnitPosition: false,
|
||||
showCompletion: false,
|
||||
allowProctoringOptOut: undefined,
|
||||
},
|
||||
units: [{
|
||||
id: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@2152d4a4aadc4cb0af5256394a3d1fc7',
|
||||
sequenceId: 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@basic_questions',
|
||||
bookmarked: false,
|
||||
complete: undefined,
|
||||
title: 'Pointing on a Picture',
|
||||
contentType: 'problem',
|
||||
graded: true,
|
||||
containsContentTypeGatedContent: false,
|
||||
}],
|
||||
units: [
|
||||
{
|
||||
id: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@2152d4a4aadc4cb0af5256394a3d1fc7',
|
||||
sequenceId: 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@basic_questions',
|
||||
bookmarked: false,
|
||||
complete: undefined,
|
||||
title: 'Pointing on a Picture',
|
||||
contentType: 'problem',
|
||||
graded: true,
|
||||
containsContentTypeGatedContent: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
const response = await getSequenceMetadata(sequenceId);
|
||||
expect(response).toBeTruthy();
|
||||
@@ -399,7 +399,8 @@ describe('Courseware Service', () => {
|
||||
},
|
||||
willRespondWith: {
|
||||
status: 200,
|
||||
body: {
|
||||
body:
|
||||
{
|
||||
success: boolean(true),
|
||||
},
|
||||
},
|
||||
@@ -422,7 +423,8 @@ describe('Courseware Service', () => {
|
||||
},
|
||||
willRespondWith: {
|
||||
status: 200,
|
||||
body: {
|
||||
body:
|
||||
{
|
||||
complete: boolean(true),
|
||||
},
|
||||
},
|
||||
@@ -432,51 +434,4 @@ describe('Courseware Service', () => {
|
||||
expect(response).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When a request to get resume block is made', () => {
|
||||
it('returns block id, section id and unit id of the resume block', async () => {
|
||||
await provider.addInteraction({
|
||||
state: `Resume block exists for course_id ${courseId}`,
|
||||
uponReceiving: 'a request to get Resume block',
|
||||
withRequest: {
|
||||
method: 'GET',
|
||||
path: `/api/courseware/resume/${courseId}`,
|
||||
},
|
||||
willRespondWith: {
|
||||
status: 200,
|
||||
body: {
|
||||
block_id: string('642fadf46d074aabb637f20af320fb31'),
|
||||
section_id: string('642fadf46d074aabb637f20af320fb87'),
|
||||
unit_id: string('642fadf46d074aabb637f20af320fb99'),
|
||||
},
|
||||
},
|
||||
});
|
||||
const camelCaseResponse = {
|
||||
blockId: '642fadf46d074aabb637f20af320fb31',
|
||||
sectionId: '642fadf46d074aabb637f20af320fb87',
|
||||
unitId: '642fadf46d074aabb637f20af320fb99',
|
||||
};
|
||||
const response = await getResumeBlock(courseId);
|
||||
expect(response).toBeTruthy();
|
||||
expect(response).toEqual(camelCaseResponse);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When a request to send activation email is made', () => {
|
||||
it('returns status code 200', async () => {
|
||||
await provider.addInteraction({
|
||||
state: 'A logged-in user may or may not be active',
|
||||
uponReceiving: 'a request to send activation email',
|
||||
withRequest: {
|
||||
method: 'POST',
|
||||
path: '/api/send_account_activation_email',
|
||||
},
|
||||
willRespondWith: {
|
||||
status: 200,
|
||||
},
|
||||
});
|
||||
const response = await sendActivationEmail();
|
||||
expect(response).toEqual('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -254,8 +254,7 @@ describe('Data layer integration tests', () => {
|
||||
});
|
||||
|
||||
describe('Test checkBlockCompletion', () => {
|
||||
const getCompletionURL = `${getConfig().LMS_BASE_URL}/courses/${courseId}/xblock/${sequenceId}/handler/get_completion`;
|
||||
|
||||
const getCompletionURL = `${getConfig().LMS_BASE_URL}/courses/${courseId}/xblock/${sequenceMetadata.decoded_id}/handler/get_completion`;
|
||||
it('Should fail to check completion and log error', async () => {
|
||||
axiosMock.onPost(getCompletionURL).networkError();
|
||||
|
||||
@@ -283,7 +282,7 @@ describe('Data layer integration tests', () => {
|
||||
});
|
||||
|
||||
describe('Test saveSequencePosition', () => {
|
||||
const gotoPositionURL = `${getConfig().LMS_BASE_URL}/courses/${courseId}/xblock/${sequenceId}/handler/goto_position`;
|
||||
const gotoPositionURL = `${getConfig().LMS_BASE_URL}/courses/${courseId}/xblock/${sequenceMetadata.decoded_id}/handler/goto_position`;
|
||||
|
||||
it('Should change and revert sequence model activeUnitIndex in case of error', async () => {
|
||||
axiosMock.onPost(gotoPositionURL).networkError();
|
||||
|
||||
@@ -13,8 +13,20 @@ const slice = createSlice({
|
||||
courseId: null,
|
||||
sequenceStatus: 'loading',
|
||||
sequenceId: null,
|
||||
specialExamsEnabledWaffleFlag: false,
|
||||
proctoredExamsEnabledWaffleFlag: false,
|
||||
shortLinkFeatureFlag: false,
|
||||
},
|
||||
reducers: {
|
||||
setsSpecialExamsEnabled: (state, { payload }) => {
|
||||
state.specialExamsEnabledWaffleFlag = payload.specialExamsEnabledWaffleFlag;
|
||||
},
|
||||
setsProctoredExamsEnabled: (state, { payload }) => {
|
||||
state.proctoredExamsEnabledWaffleFlag = payload.proctoredExamsEnabledWaffleFlag;
|
||||
},
|
||||
setsShortLinkFeatureFlag: (state, { payload }) => {
|
||||
state.shortLinkFeatureFlag = payload.shortLinkFeatureFlag;
|
||||
},
|
||||
fetchCourseRequest: (state, { payload }) => {
|
||||
state.courseId = payload.courseId;
|
||||
state.courseStatus = LOADING;
|
||||
@@ -47,6 +59,9 @@ const slice = createSlice({
|
||||
});
|
||||
|
||||
export const {
|
||||
setsSpecialExamsEnabled,
|
||||
setsProctoredExamsEnabled,
|
||||
setsShortLinkFeatureFlag,
|
||||
fetchCourseRequest,
|
||||
fetchCourseSuccess,
|
||||
fetchCourseFailure,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user