Compare commits

..

14 Commits

Author SHA1 Message Date
Sofiane Bébert
edbbe42f3b Merge pull request #222 from eduNEXT/Jhony/gradebook_header_override_backport
feat: enabling header override
2021-12-20 17:20:03 +00:00
Jhony Avella
3ee7e40ecd feat: enabling header override
feat: new versions of package.json and lock

fix: tests after adding header component

Fixing tests

change package files

Signed-off-by: Jhony Avella <jhony.avella@edunext.co>
2021-12-20 12:10:27 -05:00
Sofiane Bébert
310d0844fd Merge pull request #221 from eduNEXT/Jhony/fix_course_id_extraction_backport
fix: build the course_id properly when MFE is deployed in a subdirectory
2021-12-20 17:08:42 +00:00
Jhony Avella
7fd06c2373 fix: build the course id properly when the MFE is deployed in a subdirectory 2021-12-20 11:58:26 -05:00
Sofiane Bébert
45b922d6e6 Merge pull request #218 from regisb/regisb/transifex
feat: add compatibility with transifex
2021-12-06 14:43:21 +00:00
Régis Behmo
12574a7561 feat: add compatibility with transifex
Now that the frontend-app-gradebook resource was added to Transifex, we can
start pulling strings fromt there. For now, the project contains very few
translated string.

Strings were pulled by running: make pull_translations

This is for https://github.com/openedx/build-test-release-wg/issues/107
2021-11-30 11:13:05 +01:00
Ned Batchelder
8f1c89a025 build: use the organization commitlint check 2021-10-07 13:55:57 -04:00
Ben Warzeski
a1de3a8612 feat: Bulk Management reorganization, phase 1 (#206)
* move GradesTab and BulkHistoryTab to views and control from container through redux

* add data logic for view control and import success toast

* add NetworkButton component for download/upload buttons

* remove download button from Bulk History view and update heading and help text

* add View control button to GradebookHeader if bulk management available

* remove FilterMenuToggle from SearchControls

* update BulkManagementControls to now include upload/download grades buttons

* add Import Success toast

* rename UserLabel to FilteredUsersLabel for clarity

* add InterventionsReport component

* update GradesView top-level component

* messageing update (separate messages into per-component files)

* style updates

* update test plan

* clean up css and add docstrings

* typo fix

* fix typo in bulk management view header
2021-08-26 10:37:35 -04:00
Nathan Sprenkle
4e26247ac3 docs: remove outdated screenshots (#211) 2021-08-19 13:49:29 -04:00
Ben Warzeski
f21e6da598 download bulk management history if showBulkManagement (#210)
* download bulk management history if showBulkManagement

* separate fetchBulkUpgrade call from fetchTracks

* 1.4.47
2021-08-09 10:49:04 -04:00
Nathan Sprenkle
650e9321b1 Simplify Bulk Management Config (#200)
* refactor: simplify bulk management enabling

Formerly, a course had to have bulk management enabled and have a master's
track. This painted us into a corner where we had to create a
workaround for enabling bulk management on non-master's track courses.
Instead, this relies only on an enabled flag from edx-platform (based on
a course waffle flag) which simplifies the enabling code here.

* feat: remove unneeded bulk management allow-list
2021-08-04 11:04:55 -04:00
Ben Warzeski
e8a8cca483 fix: add excludedCourseRoles to bulk-grades fetch (#209) 2021-08-04 09:46:51 -04:00
Ben Warzeski
f5d2a34660 Fix cohort url (#208)
* fix cohorts fetch url

* v1.4.45
2021-08-03 15:37:12 -04:00
Ben Warzeski
97d3a29a7f fix: add track back into lms api service calls. (#207)
* fix: add track back into lms api service calls.

* fix tests

* v1.4.44
2021-07-30 11:59:01 -04:00
164 changed files with 2851 additions and 1447 deletions

1
.env
View File

@@ -30,4 +30,3 @@ ENTERPRISE_MARKETING_URL=''
ENTERPRISE_MARKETING_UTM_SOURCE=''
ENTERPRISE_MARKETING_UTM_CAMPAIGN=''
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM=''
BULK_MANAGEMENT_SPECIAL_ACCESS_COURSE_IDS=''

View File

@@ -23,6 +23,7 @@ MARKETING_SITE_BASE_URL='http://localhost:18000'
SUPPORT_URL='http://localhost:18000/support'
CONTACT_URL='http://localhost:18000/contact'
OPEN_SOURCE_URL='http://localhost:18000/openedx'
ORDER_HISTORY_URL='http://localhost:1996/orders'
TERMS_OF_SERVICE_URL='http://localhost:18000/terms-of-service'
PRIVACY_POLICY_URL='http://localhost:18000/privacy-policy'
FACEBOOK_URL='https://www.facebook.com'
@@ -36,4 +37,3 @@ ENTERPRISE_MARKETING_URL='http://example.com'
ENTERPRISE_MARKETING_UTM_SOURCE='example.com'
ENTERPRISE_MARKETING_UTM_CAMPAIGN='example.com Referral'
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM='Footer'
BULK_MANAGEMENT_SPECIAL_ACCESS_COURSE_IDS=''

10
.github/workflows/commitlint.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
# Run commitlint on the commit messages in a pull request.
name: Lint Commit Messages
on:
- pull_request
jobs:
commitlint:
uses: edx/.github/.github/workflows/commitlint.yml@master

2
.gitignore vendored
View File

@@ -5,6 +5,8 @@ npm-debug.log
coverage
dist/
src/i18n/transifex_input.json
temp/babel-plugin-react-intl
### pyenv ###
.python-version

View File

@@ -6,3 +6,4 @@ file_filter = src/i18n/messages/<lang>.json
source_file = src/i18n/transifex_input.json
source_lang = en
type = KEYVALUEJSON

View File

@@ -22,19 +22,13 @@ For existing documentation see:
### What does this offer over the legacy gradebook?
![A screenshot of the grade listings](documentation/screenshots/grade-listings.png)
The micro-frontend offers a great deal more granularity when searching for problems, an easy interface for editing grades, an
audit trail for seeing who edited what grade and what reason they gave (if any) for doing so.
![Screenshot of the grade editing interface](documentation/screenshots/grade-editing.png)
UsageProblems can be filtered by student as in the traditional gradebook, but can also be filtered by scores to see who
scored within a certain range, and by assignment types (note: Not problem types, but categories like Exams or
Homework).
![Screenshot of the filtering options](documentation/screenshots/grade-filtering.png)
### What does the legacy gradebook offer that this project does not?
This project does not (yet, at least) create any graphs, which the traditional gradebook does. It also does not give

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 KiB

View File

@@ -68,33 +68,40 @@ Confirm the following workflows:
- [ ] Clicking "Save Grade" applies the override, shows the successful "grade has been edited" banner and updates score in grades table (may take a few seconds).
- [ ] Opening back up the "Edit Grades" modal shows the change as an entry in the override history table.
- [ ] *Masters only*: "Bulk Management" allows overriding grades in bulk.
- [ ] *Master's (or selectively-enabled) only*: "Bulk Management" allows overriding grades in bulk.
- Open a non-masters-track course.
- [ ] Verify that the "Bulk Management" tab does not appear.
- [ ] Verify that the "Bulk Management" button does not appear.
- [ ] Verify that the "Download Interventions" interface does not appear.
- Open a masters-track course.
- [ ] Verify that the "Bulk Management" tab appears to the right of the "Grades" tab.
- [ ] Verify that the "Bulk Management" button appears.
- Click the "Bulk Management" button. This downloads existing student/assignment info.
- [ ] Verify that the "Bulk Management History" button appears at the right of the header.
- [ ] Verify that the "Download Interventions" interface appears.
- [ ] Verify that the "Download Grades" button appears.
- [ ] Verify that the "Import Grades" button appears.
- Click the "Download Grades" button. This downloads existing student/assignment info.
- [ ] Open the downloaded CSV and verify that students and assignments in the file match applied filters/searches.
- Add values in the "new_override-{subsection-short-id}" columns for student grades to be overridden and save the CSV file.
- [ ] Clicking the "Bulk Management" tab shows the Bulk Management page.
- Navigate to Bulk Management History tab.
- [ ] Clicking the "ViewBulk Management History" tab shows the Bulk Management History view.
- [ ] The bulk management history table appears with columns: "Gradebook", "Download Summary", "Who", "When".
- [ ] Previous bulk management imports (if applicable) appear in the table.
- Add values in the "new_override-{subsection-short-id}" columns for student grades to be overridden and save the CSV file.
- Navigate back to Gradebook view
- Click the "Import Grades" button and select the modified CSV file.
- [ ] Verify that the "CSV processing" banner appears.
- Wait for processing to complete and reload the page. (Can take seconds to minutes depending on environment and size of the override.)
- Navigate back to the "Bulk Management" tab.
- [ ] Verify that Import Grades Success toast appears (and disappears after 5 seconds)
- Navigate back to the "Bulk Management History" view.
- [ ] Verify that a new entry appears in the results table indicating how many students were affected by the bulk grade change.
- Click the "Download Summary" link to see the summary of changes from the bulk grade changes.
- [ ] Verify that students are shown with modified subsections and actions: "No Action" for unchanged users, "Success" for successful overrides.
- [ ] *Masters only*: Interventions report shows student activity in the course.
- Open a non-masters-track course.
- [ ] Verify that the "Interventions" tab does not appear.
- [ ] Verify that the "Interventions" button does not appear.
- [ ] Verify that the "View Bulk Management History" button does not appear.
- [ ] Verify that the "Interventions" interface does not appear.
- [ ] Verify that the "Download Grades" and "Import Grades" buttons do not appear.
- Open a masters-track course.
- [ ] Verify that the "Interventions" tab appears to the right of the "Grades" tab.
- [ ] Verify that the "Interventions" button appears.
- Click on the "Interventions" button to generate a CSV students and activity info.
- [ ] Verify that the "View Bulk Management History" button appears at the right of the header.
- [ ] Verify that the "Interventions" interface appears.
- [ ] Verify that the "Download Grades" and "Import Grades" buttons appear.
- Click on the "Download Interventions" button to generate a CSV students and activity info.
- Open the interventions report and verify student info and activity info appear.

View File

@@ -14,7 +14,7 @@ Suggested resources:
- [Adding Exercises and Tools](https://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/grading/index.html)
- [Set the Assignment Type and Due Date for a Subsection](https://edx.readthedocs.io/projects/edx-partner-course-staff/en/latest/developing_course/course_subsections.html#set-the-assignment-type-and-due-date-for-a-subsection)
## Enable Gradebook and feature toggles for course
## Enable Gradebook for course
See README.md #Quickstart for more detailed instructions.
@@ -25,7 +25,13 @@ As an admin user, visit Django Admin (`{lms-url}/admin`) to modify features.
- [ ] Set name to `grades.assume_zero_grade_if_absent`, select "Active", and click "Save"
- In Waffle_Utils > Waffle flag course overrides:
- [ ] Add a new flag called `grades.writeable_gradebook`, select "Force On", and enable it for your course
- [ ] Add a new flag called `grades.bulk_management`, select "Force On", and enable it for your course
## Enable Bulk Management
Bulk Management is an added feature to allow modifying grades in bulk via CSV upload. Bulk Management is default enabled for Master's track courses but can be selectively enabled for other courses with a waffle flag following the steps below.
- In Waffle_Utils > Waffle flag course overrides:
- [ ] Add a new flag called `grades.bulk_management`, select "Force On", and enable it for your course.
## Create a Master's track for testing Master's-only features

988
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "@edx/frontend-app-gradebook",
"version": "1.4.43",
"version": "1.4.47",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -3241,56 +3241,90 @@
}
},
"@edx/frontend-component-footer": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-10.1.1.tgz",
"integrity": "sha512-NF64Dgznzy4A4nsfzC+HXAmtcmHwAAQCp17qtyUQ8a+LJhXUi/eXbrYhD8t0GboDHCI2HfRK2YOeiVJ3XjsNsw==",
"version": "10.1.6",
"resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-10.1.6.tgz",
"integrity": "sha512-ls9E2Lvy0WNaZEWA7Imxq/qrw9C0HiTZRh4mavzbJsBvuo9QwZh0OsA/jam5dQnf4IlgHwGwP8cIDEujdhQ/vg==",
"requires": {
"@fortawesome/fontawesome-svg-core": "1.2.32",
"@fortawesome/free-brands-svg-icons": "5.8.2",
"@fortawesome/free-regular-svg-icons": "5.8.2",
"@fortawesome/free-solid-svg-icons": "5.8.2",
"@fortawesome/react-fontawesome": "0.1.13"
"@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"
},
"dependencies": {
"@fortawesome/fontawesome-common-types": {
"version": "0.2.32",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.32.tgz",
"integrity": "sha512-ux2EDjKMpcdHBVLi/eWZynnPxs0BtFVXJkgHIxXRl+9ZFaHPvYamAfCzeeQFqHRjuJtX90wVnMRaMQAAlctz3w=="
"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=="
},
"@fortawesome/fontawesome-svg-core": {
"version": "1.2.32",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.32.tgz",
"integrity": "sha512-XjqyeLCsR/c/usUpdWcOdVtWFVjPbDFBTQkn2fQRrWhhUoxriQohO2RWDxLyUM8XpD+Zzg5xwJ8gqTYGDLeGaQ==",
"version": "1.2.36",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz",
"integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==",
"requires": {
"@fortawesome/fontawesome-common-types": "^0.2.32"
"@fortawesome/fontawesome-common-types": "^0.2.36"
}
},
"@fortawesome/free-brands-svg-icons": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.8.2.tgz",
"integrity": "sha512-nhEWctDOP6f+Ka10LXAFoF+6mtWidC2iQgTBGRGgydmhBtcIEwyxWVx5wQHa86A1zAMi5TnipDAYQs2qn7DD6A==",
"version": "5.15.4",
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.4.tgz",
"integrity": "sha512-f1witbwycL9cTENJegcmcZRYyawAFbm8+c6IirLmwbbpqz46wyjbQYLuxOc7weXFXfB7QR8/Vd2u5R3q6JYD9g==",
"requires": {
"@fortawesome/fontawesome-common-types": "^0.2.18"
"@fortawesome/fontawesome-common-types": "^0.2.36"
}
},
"@fortawesome/free-solid-svg-icons": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.8.2.tgz",
"integrity": "sha512-5tF6WOFlqqO95zY5ukSB6jliDa3jnk1p5L4K/a58ccDFsbjSkhfGuvZkRkeWxH8uMms81pZd6yQTwQrkedeJmg==",
"version": "5.15.4",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz",
"integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==",
"requires": {
"@fortawesome/fontawesome-common-types": "^0.2.18"
"@fortawesome/fontawesome-common-types": "^0.2.36"
}
},
"@fortawesome/react-fontawesome": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.13.tgz",
"integrity": "sha512-/HrLnIft5Ks2511Pz6TxHBIctC9QalVscAC64sufQ4sJH/sXaQlG3uR9LCu6VpEwkBemgcBLrz/QPNP/ddbjDg==",
"version": "0.1.15",
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.15.tgz",
"integrity": "sha512-/HFHdcoLESxxMkqZAcZ6RXDJ69pVApwdwRos/B2kiMWxDSAX2dFK8Er2/+rG+RsrzWB/dsAyjefLmemgmfE18g==",
"requires": {
"prop-types": "^15.7.2"
}
}
}
},
"@edx/frontend-component-header": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-2.2.5.tgz",
"integrity": "sha512-8ZfdQBgp5beLd3r+xIxpDQT4/eYggUrzkHe4iJk322lYqmjAEHio9jrLnHv7lW/B8ooH3qZNl+sJlT2UcWRxmg==",
"requires": {
"babel-polyfill": "6.26.0",
"react-responsive": "8.0.3",
"react-transition-group": "4.3.0"
},
"dependencies": {
"react-responsive": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.0.3.tgz",
"integrity": "sha512-F9VXyLao7O8XHXbLjQbIr4+mC6Zr0RDTwNjd7ixTmYEAyKyNanBkLkFchNaMZgszoSK6PgSs/3m/QDWw33/gpg==",
"requires": {
"hyphenate-style-name": "^1.0.0",
"matchmediaquery": "^0.3.0",
"prop-types": "^15.6.1",
"shallow-equal": "^1.1.0"
}
},
"react-transition-group": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz",
"integrity": "sha512-1qRV1ZuVSdxPlPf4O8t7inxUGpdyO5zG9IoNfJxSO0ImU2A1YWkEQvFPuIPZmMLkg5hYs7vv5mMOyfgSkvAwvw==",
"requires": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
}
}
}
},
"@edx/frontend-platform": {
"version": "1.9.5",
"resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-1.9.5.tgz",
@@ -3428,11 +3462,18 @@
}
},
"@fortawesome/free-regular-svg-icons": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.8.2.tgz",
"integrity": "sha512-499WODlsDcXA9hM+Y3ZqoWj1kKhVLCHS8PnJs3zEaoPr5W6yrE9Jnp3C9Hb9KpwnRMh3IRXZ3XqxyUaQFMMRFw==",
"version": "5.15.4",
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.4.tgz",
"integrity": "sha512-9VNNnU3CXHy9XednJ3wzQp6SwNwT3XaM26oS4Rp391GsxVYA+0oDR2J194YCIWf7jNRCYKjUCOduxdceLrx+xw==",
"requires": {
"@fortawesome/fontawesome-common-types": "^0.2.18"
"@fortawesome/fontawesome-common-types": "^0.2.36"
},
"dependencies": {
"@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=="
}
}
},
"@fortawesome/free-solid-svg-icons": {
@@ -6576,7 +6617,6 @@
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
"integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=",
"dev": true,
"requires": {
"babel-runtime": "^6.26.0",
"core-js": "^2.5.0",
@@ -6586,14 +6626,12 @@
"core-js": {
"version": "2.6.11",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
"integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==",
"dev": true
"integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="
},
"regenerator-runtime": {
"version": "0.10.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
"integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=",
"dev": true
"integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg="
}
}
},
@@ -6631,7 +6669,6 @@
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"dev": true,
"requires": {
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
@@ -6640,14 +6677,12 @@
"core-js": {
"version": "2.6.11",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
"integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==",
"dev": true
"integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="
},
"regenerator-runtime": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
"dev": true
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
}
}
},
@@ -18156,7 +18191,7 @@
"dependencies": {
"JSONStream": {
"version": "1.3.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
"dev": true,
"requires": {
@@ -18166,13 +18201,13 @@
},
"abbrev": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true
},
"agent-base": {
"version": "4.3.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
"dev": true,
"requires": {
@@ -18181,7 +18216,7 @@
},
"agentkeepalive": {
"version": "3.5.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==",
"dev": true,
"requires": {
@@ -18190,7 +18225,7 @@
},
"ajv": {
"version": "5.5.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
"dev": true,
"requires": {
@@ -18202,7 +18237,7 @@
},
"ansi-align": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=",
"dev": true,
"requires": {
@@ -18211,13 +18246,13 @@
},
"ansi-regex": {
"version": "2.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
@@ -18226,31 +18261,31 @@
},
"ansicolors": {
"version": "0.3.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=",
"dev": true
},
"ansistyles": {
"version": "0.1.3",
"resolved": "",
"resolved": false,
"integrity": "sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk=",
"dev": true
},
"aproba": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
"dev": true
},
"archy": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=",
"dev": true
},
"are-we-there-yet": {
"version": "1.1.4",
"resolved": "",
"resolved": false,
"integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
"dev": true,
"requires": {
@@ -18260,7 +18295,7 @@
"dependencies": {
"readable-stream": {
"version": "2.3.6",
"resolved": "",
"resolved": false,
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -18275,7 +18310,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
@@ -18286,13 +18321,13 @@
},
"asap": {
"version": "2.0.6",
"resolved": "",
"resolved": false,
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
"dev": true
},
"asn1": {
"version": "0.2.4",
"resolved": "",
"resolved": false,
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
"dev": true,
"requires": {
@@ -18301,37 +18336,37 @@
},
"assert-plus": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
},
"asynckit": {
"version": "0.4.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
"dev": true
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
"dev": true
},
"aws4": {
"version": "1.8.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
"dev": true
},
"balanced-match": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
"dev": true,
"optional": true,
@@ -18341,7 +18376,7 @@
},
"bin-links": {
"version": "1.1.8",
"resolved": "",
"resolved": false,
"integrity": "sha512-KgmVfx+QqggqP9dA3iIc5pA4T1qEEEL+hOhOhNPaUm77OTrJoOXE/C05SJLNJe6m/2wUK7F1tDSou7n5TfCDzQ==",
"dev": true,
"requires": {
@@ -18355,13 +18390,13 @@
},
"bluebird": {
"version": "3.5.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==",
"dev": true
},
"boxen": {
"version": "1.3.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==",
"dev": true,
"requires": {
@@ -18376,7 +18411,7 @@
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "",
"resolved": false,
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
@@ -18386,31 +18421,31 @@
},
"buffer-from": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==",
"dev": true
},
"builtins": {
"version": "1.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=",
"dev": true
},
"byline": {
"version": "5.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=",
"dev": true
},
"byte-size": {
"version": "5.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw==",
"dev": true
},
"cacache": {
"version": "12.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==",
"dev": true,
"requires": {
@@ -18433,31 +18468,31 @@
},
"call-limit": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-5twvci5b9eRBw2wCfPtN0GmlR2/gadZqyFpPhOK6CvMFoFgA+USnZ6Jpu1lhG9h85pQ3Ouil3PfXWRD4EUaRiQ==",
"dev": true
},
"camelcase": {
"version": "4.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
"dev": true
},
"capture-stack-trace": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=",
"dev": true
},
"caseless": {
"version": "0.12.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
"dev": true
},
"chalk": {
"version": "2.4.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
"dev": true,
"requires": {
@@ -18468,19 +18503,19 @@
},
"chownr": {
"version": "1.1.4",
"resolved": "",
"resolved": false,
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
"dev": true
},
"ci-info": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
"dev": true
},
"cidr-regex": {
"version": "2.0.10",
"resolved": "",
"resolved": false,
"integrity": "sha512-sB3ogMQXWvreNPbJUZMRApxuRYd+KoIo4RGQ81VatjmMW6WJPo+IJZ2846FGItr9VzKo5w7DXzijPLGtSd0N3Q==",
"dev": true,
"requires": {
@@ -18489,13 +18524,13 @@
},
"cli-boxes": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=",
"dev": true
},
"cli-columns": {
"version": "3.1.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-ZzLZcpee/CrkRKHwjgj6E5yWoY4=",
"dev": true,
"requires": {
@@ -18505,7 +18540,7 @@
},
"cli-table3": {
"version": "0.5.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
"dev": true,
"requires": {
@@ -18516,7 +18551,7 @@
},
"cliui": {
"version": "5.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
"dev": true,
"requires": {
@@ -18527,19 +18562,19 @@
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"string-width": {
"version": "3.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
@@ -18550,7 +18585,7 @@
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
@@ -18561,13 +18596,13 @@
},
"clone": {
"version": "1.0.4",
"resolved": "",
"resolved": false,
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
"dev": true
},
"cmd-shim": {
"version": "3.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-DtGg+0xiFhQIntSBRzL2fRQBnmtAVwXIDo4Qq46HPpObYquxMaZS4sb82U9nH91qJrlosC1wa9gwr0QyL/HypA==",
"dev": true,
"requires": {
@@ -18577,19 +18612,19 @@
},
"co": {
"version": "4.6.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
"dev": true
},
"code-point-at": {
"version": "1.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true
},
"color-convert": {
"version": "1.9.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
"dev": true,
"requires": {
@@ -18598,20 +18633,20 @@
},
"color-name": {
"version": "1.1.3",
"resolved": "",
"resolved": false,
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
"colors": {
"version": "1.3.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==",
"dev": true,
"optional": true
},
"columnify": {
"version": "1.5.4",
"resolved": "",
"resolved": false,
"integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=",
"dev": true,
"requires": {
@@ -18621,7 +18656,7 @@
},
"combined-stream": {
"version": "1.0.6",
"resolved": "",
"resolved": false,
"integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
"dev": true,
"requires": {
@@ -18630,13 +18665,13 @@
},
"concat-map": {
"version": "0.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"concat-stream": {
"version": "1.6.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
"dev": true,
"requires": {
@@ -18648,7 +18683,7 @@
"dependencies": {
"readable-stream": {
"version": "2.3.6",
"resolved": "",
"resolved": false,
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -18663,7 +18698,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
@@ -18674,7 +18709,7 @@
},
"config-chain": {
"version": "1.1.12",
"resolved": "",
"resolved": false,
"integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==",
"dev": true,
"requires": {
@@ -18684,7 +18719,7 @@
},
"configstore": {
"version": "3.1.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==",
"dev": true,
"requires": {
@@ -18698,13 +18733,13 @@
},
"console-control-strings": {
"version": "1.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"dev": true
},
"copy-concurrently": {
"version": "1.0.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
"dev": true,
"requires": {
@@ -18718,13 +18753,13 @@
"dependencies": {
"aproba": {
"version": "1.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"dev": true
},
"iferr": {
"version": "0.1.5",
"resolved": "",
"resolved": false,
"integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
"dev": true
}
@@ -18732,13 +18767,13 @@
},
"core-util-is": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true
},
"create-error-class": {
"version": "3.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
"dev": true,
"requires": {
@@ -18747,7 +18782,7 @@
},
"cross-spawn": {
"version": "5.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
"dev": true,
"requires": {
@@ -18758,7 +18793,7 @@
"dependencies": {
"lru-cache": {
"version": "4.1.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
"dev": true,
"requires": {
@@ -18768,7 +18803,7 @@
},
"yallist": {
"version": "2.1.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
"dev": true
}
@@ -18776,19 +18811,19 @@
},
"crypto-random-string": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=",
"dev": true
},
"cyclist": {
"version": "0.2.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
"dev": true
},
"dashdash": {
"version": "1.14.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"dev": true,
"requires": {
@@ -18797,7 +18832,7 @@
},
"debug": {
"version": "3.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
@@ -18806,7 +18841,7 @@
"dependencies": {
"ms": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
@@ -18814,31 +18849,31 @@
},
"debuglog": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=",
"dev": true
},
"decamelize": {
"version": "1.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
"dev": true
},
"decode-uri-component": {
"version": "0.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
"dev": true
},
"deep-extend": {
"version": "0.6.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"dev": true
},
"defaults": {
"version": "1.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
"dev": true,
"requires": {
@@ -18847,7 +18882,7 @@
},
"define-properties": {
"version": "1.1.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"dev": true,
"requires": {
@@ -18856,31 +18891,31 @@
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true
},
"delegates": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"dev": true
},
"detect-indent": {
"version": "5.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=",
"dev": true
},
"detect-newline": {
"version": "2.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=",
"dev": true
},
"dezalgo": {
"version": "1.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=",
"dev": true,
"requires": {
@@ -18890,7 +18925,7 @@
},
"dot-prop": {
"version": "4.2.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==",
"dev": true,
"requires": {
@@ -18899,19 +18934,19 @@
},
"dotenv": {
"version": "5.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==",
"dev": true
},
"duplexer3": {
"version": "0.1.4",
"resolved": "",
"resolved": false,
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=",
"dev": true
},
"duplexify": {
"version": "3.6.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==",
"dev": true,
"requires": {
@@ -18923,7 +18958,7 @@
"dependencies": {
"readable-stream": {
"version": "2.3.6",
"resolved": "",
"resolved": false,
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -18938,7 +18973,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
@@ -18949,7 +18984,7 @@
},
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
"dev": true,
"optional": true,
@@ -18960,19 +18995,19 @@
},
"editor": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I=",
"dev": true
},
"emoji-regex": {
"version": "7.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
"dev": true
},
"encoding": {
"version": "0.1.12",
"resolved": "",
"resolved": false,
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
"dev": true,
"requires": {
@@ -18981,7 +19016,7 @@
},
"end-of-stream": {
"version": "1.4.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
"dev": true,
"requires": {
@@ -18990,19 +19025,19 @@
},
"env-paths": {
"version": "2.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==",
"dev": true
},
"err-code": {
"version": "1.1.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=",
"dev": true
},
"errno": {
"version": "0.1.7",
"resolved": "",
"resolved": false,
"integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
"dev": true,
"requires": {
@@ -19011,7 +19046,7 @@
},
"es-abstract": {
"version": "1.12.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==",
"dev": true,
"requires": {
@@ -19024,7 +19059,7 @@
},
"es-to-primitive": {
"version": "1.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
"dev": true,
"requires": {
@@ -19035,13 +19070,13 @@
},
"es6-promise": {
"version": "4.2.8",
"resolved": "",
"resolved": false,
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
"dev": true
},
"es6-promisify": {
"version": "5.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
"dev": true,
"requires": {
@@ -19050,13 +19085,13 @@
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "",
"resolved": false,
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"execa": {
"version": "0.7.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
"dev": true,
"requires": {
@@ -19071,7 +19106,7 @@
"dependencies": {
"get-stream": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
"dev": true
}
@@ -19079,43 +19114,43 @@
},
"extend": {
"version": "3.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"dev": true
},
"extsprintf": {
"version": "1.3.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
"dev": true
},
"fast-deep-equal": {
"version": "1.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
"dev": true
},
"fast-json-stable-stringify": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
"dev": true
},
"figgy-pudding": {
"version": "3.5.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==",
"dev": true
},
"find-npm-prefix": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA==",
"dev": true
},
"flush-write-stream": {
"version": "1.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==",
"dev": true,
"requires": {
@@ -19125,7 +19160,7 @@
"dependencies": {
"readable-stream": {
"version": "2.3.6",
"resolved": "",
"resolved": false,
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -19140,7 +19175,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
@@ -19151,13 +19186,13 @@
},
"forever-agent": {
"version": "0.6.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
"dev": true
},
"form-data": {
"version": "2.3.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
"dev": true,
"requires": {
@@ -19168,7 +19203,7 @@
},
"from2": {
"version": "2.3.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
"dev": true,
"requires": {
@@ -19178,7 +19213,7 @@
"dependencies": {
"readable-stream": {
"version": "2.3.6",
"resolved": "",
"resolved": false,
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -19193,7 +19228,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
@@ -19204,7 +19239,7 @@
},
"fs-minipass": {
"version": "1.2.7",
"resolved": "",
"resolved": false,
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
"dev": true,
"requires": {
@@ -19213,7 +19248,7 @@
"dependencies": {
"minipass": {
"version": "2.9.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
"dev": true,
"requires": {
@@ -19225,7 +19260,7 @@
},
"fs-vacuum": {
"version": "1.2.10",
"resolved": "",
"resolved": false,
"integrity": "sha1-t2Kb7AekAxolSP35n17PHMizHjY=",
"dev": true,
"requires": {
@@ -19236,7 +19271,7 @@
},
"fs-write-stream-atomic": {
"version": "1.0.10",
"resolved": "",
"resolved": false,
"integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
"dev": true,
"requires": {
@@ -19248,13 +19283,13 @@
"dependencies": {
"iferr": {
"version": "0.1.5",
"resolved": "",
"resolved": false,
"integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
"dev": true
},
"readable-stream": {
"version": "2.3.6",
"resolved": "",
"resolved": false,
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -19269,7 +19304,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
@@ -19280,19 +19315,19 @@
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"gauge": {
"version": "2.7.4",
"resolved": "",
"resolved": false,
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"dev": true,
"requires": {
@@ -19308,13 +19343,13 @@
"dependencies": {
"aproba": {
"version": "1.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"dev": true
},
"string-width": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
"requires": {
@@ -19327,13 +19362,13 @@
},
"genfun": {
"version": "5.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==",
"dev": true
},
"gentle-fs": {
"version": "2.3.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-OlwBBwqCFPcjm33rF2BjW+Pr6/ll2741l+xooiwTCeaX2CA1ZuclavyMBe0/KlR21/XGsgY6hzEQZ15BdNa13Q==",
"dev": true,
"requires": {
@@ -19352,13 +19387,13 @@
"dependencies": {
"aproba": {
"version": "1.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"dev": true
},
"iferr": {
"version": "0.1.5",
"resolved": "",
"resolved": false,
"integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
"dev": true
}
@@ -19366,13 +19401,13 @@
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true
},
"get-stream": {
"version": "4.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
"dev": true,
"requires": {
@@ -19381,7 +19416,7 @@
},
"getpass": {
"version": "0.1.7",
"resolved": "",
"resolved": false,
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"dev": true,
"requires": {
@@ -19390,7 +19425,7 @@
},
"glob": {
"version": "7.1.6",
"resolved": "",
"resolved": false,
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dev": true,
"requires": {
@@ -19404,7 +19439,7 @@
},
"global-dirs": {
"version": "0.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
"dev": true,
"requires": {
@@ -19413,7 +19448,7 @@
},
"got": {
"version": "6.7.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
"dev": true,
"requires": {
@@ -19432,7 +19467,7 @@
"dependencies": {
"get-stream": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
"dev": true
}
@@ -19440,19 +19475,19 @@
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "",
"resolved": false,
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"har-schema": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
"dev": true
},
"har-validator": {
"version": "5.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==",
"dev": true,
"requires": {
@@ -19462,7 +19497,7 @@
},
"has": {
"version": "1.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
@@ -19471,37 +19506,37 @@
},
"has-flag": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"has-symbols": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
"dev": true
},
"has-unicode": {
"version": "2.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
"dev": true
},
"hosted-git-info": {
"version": "2.8.8",
"resolved": "",
"resolved": false,
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
"dev": true
},
"http-cache-semantics": {
"version": "3.8.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==",
"dev": true
},
"http-proxy-agent": {
"version": "2.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
"dev": true,
"requires": {
@@ -19511,7 +19546,7 @@
},
"http-signature": {
"version": "1.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"dev": true,
"requires": {
@@ -19522,7 +19557,7 @@
},
"https-proxy-agent": {
"version": "2.2.4",
"resolved": "",
"resolved": false,
"integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
"dev": true,
"requires": {
@@ -19532,7 +19567,7 @@
},
"humanize-ms": {
"version": "1.2.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
"dev": true,
"requires": {
@@ -19541,7 +19576,7 @@
},
"iconv-lite": {
"version": "0.4.23",
"resolved": "",
"resolved": false,
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
"dev": true,
"requires": {
@@ -19550,13 +19585,13 @@
},
"iferr": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-9AfeLfji44r5TKInjhz3W9DyZI1zR1JAf2hVBMGhddAKPqBsupb89jGfbCTHIGZd6fGZl9WlHdn4AObygyMKwg==",
"dev": true
},
"ignore-walk": {
"version": "3.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
"dev": true,
"requires": {
@@ -19565,25 +19600,25 @@
},
"import-lazy": {
"version": "2.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=",
"dev": true
},
"imurmurhash": {
"version": "0.1.4",
"resolved": "",
"resolved": false,
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"dev": true
},
"infer-owner": {
"version": "1.0.4",
"resolved": "",
"resolved": false,
"integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
"dev": true
},
"inflight": {
"version": "1.0.6",
"resolved": "",
"resolved": false,
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"requires": {
@@ -19593,19 +19628,19 @@
},
"inherits": {
"version": "2.0.4",
"resolved": "",
"resolved": false,
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"ini": {
"version": "1.3.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true
},
"init-package-json": {
"version": "1.10.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==",
"dev": true,
"requires": {
@@ -19621,25 +19656,25 @@
},
"ip": {
"version": "1.1.5",
"resolved": "",
"resolved": false,
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
"dev": true
},
"ip-regex": {
"version": "2.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
"dev": true
},
"is-callable": {
"version": "1.1.4",
"resolved": "",
"resolved": false,
"integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
"dev": true
},
"is-ci": {
"version": "1.2.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
"dev": true,
"requires": {
@@ -19648,7 +19683,7 @@
"dependencies": {
"ci-info": {
"version": "1.6.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==",
"dev": true
}
@@ -19656,7 +19691,7 @@
},
"is-cidr": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-8Xnnbjsb0x462VoYiGlhEi+drY8SFwrHiSYuzc/CEwco55vkehTaxAyIjEdpi3EMvLPPJAJi9FlzP+h+03gp0Q==",
"dev": true,
"requires": {
@@ -19665,13 +19700,13 @@
},
"is-date-object": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
"dev": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true,
"requires": {
@@ -19680,7 +19715,7 @@
},
"is-installed-globally": {
"version": "0.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=",
"dev": true,
"requires": {
@@ -19690,19 +19725,19 @@
},
"is-npm": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=",
"dev": true
},
"is-obj": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
"dev": true
},
"is-path-inside": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
"dev": true,
"requires": {
@@ -19711,13 +19746,13 @@
},
"is-redirect": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=",
"dev": true
},
"is-regex": {
"version": "1.0.4",
"resolved": "",
"resolved": false,
"integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
"dev": true,
"requires": {
@@ -19726,19 +19761,19 @@
},
"is-retry-allowed": {
"version": "1.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==",
"dev": true
},
"is-stream": {
"version": "1.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
"dev": true
},
"is-symbol": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
"dev": true,
"requires": {
@@ -19747,68 +19782,68 @@
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
"dev": true
},
"isarray": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true
},
"isexe": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"dev": true
},
"isstream": {
"version": "0.1.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
"dev": true
},
"jsbn": {
"version": "0.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
"dev": true,
"optional": true
},
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
"dev": true
},
"json-schema": {
"version": "0.2.3",
"resolved": "",
"resolved": false,
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
"dev": true
},
"json-schema-traverse": {
"version": "0.3.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
"dev": true
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
"dev": true
},
"jsonparse": {
"version": "1.3.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
"dev": true
},
"jsprim": {
"version": "1.4.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
"dev": true,
"requires": {
@@ -19820,7 +19855,7 @@
},
"latest-version": {
"version": "3.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=",
"dev": true,
"requires": {
@@ -19829,13 +19864,13 @@
},
"lazy-property": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-hN3Es3Bnm6i9TNz6TAa0PVcREUc=",
"dev": true
},
"libcipm": {
"version": "4.0.8",
"resolved": "",
"resolved": false,
"integrity": "sha512-IN3hh2yDJQtZZ5paSV4fbvJg4aHxCCg5tcZID/dSVlTuUiWktsgaldVljJv6Z5OUlYspx6xQkbR0efNodnIrOA==",
"dev": true,
"requires": {
@@ -19858,7 +19893,7 @@
},
"libnpm": {
"version": "3.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-d7jU5ZcMiTfBqTUJVZ3xid44fE5ERBm9vBnmhp2ECD2Ls+FNXWxHSkO7gtvrnbLO78gwPdNPz1HpsF3W4rjkBQ==",
"dev": true,
"requires": {
@@ -19886,7 +19921,7 @@
},
"libnpmaccess": {
"version": "3.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-01512AK7MqByrI2mfC7h5j8N9V4I7MHJuk9buo8Gv+5QgThpOgpjB7sQBDDkeZqRteFb1QM/6YNdHfG7cDvfAQ==",
"dev": true,
"requires": {
@@ -19898,7 +19933,7 @@
},
"libnpmconfig": {
"version": "1.2.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==",
"dev": true,
"requires": {
@@ -19909,7 +19944,7 @@
"dependencies": {
"find-up": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
@@ -19918,7 +19953,7 @@
},
"locate-path": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
@@ -19928,7 +19963,7 @@
},
"p-limit": {
"version": "2.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
"dev": true,
"requires": {
@@ -19937,7 +19972,7 @@
},
"p-locate": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
@@ -19946,7 +19981,7 @@
},
"p-try": {
"version": "2.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
}
@@ -19954,7 +19989,7 @@
},
"libnpmhook": {
"version": "5.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-UdNLMuefVZra/wbnBXECZPefHMGsVDTq5zaM/LgKNE9Keyl5YXQTnGAzEo+nFOpdRqTWI9LYi4ApqF9uVCCtuA==",
"dev": true,
"requires": {
@@ -19966,7 +20001,7 @@
},
"libnpmorg": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-0sRUXLh+PLBgZmARvthhYXQAWn0fOsa6T5l3JSe2n9vKG/lCVK4nuG7pDsa7uMq+uTt2epdPK+a2g6btcY11Ww==",
"dev": true,
"requires": {
@@ -19978,7 +20013,7 @@
},
"libnpmpublish": {
"version": "1.1.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-2yIwaXrhTTcF7bkJKIKmaCV9wZOALf/gsTDxVSu/Gu/6wiG3fA8ce8YKstiWKTxSFNC0R7isPUb6tXTVFZHt2g==",
"dev": true,
"requires": {
@@ -19995,7 +20030,7 @@
},
"libnpmsearch": {
"version": "2.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-VTBbV55Q6fRzTdzziYCr64+f8AopQ1YZ+BdPOv16UegIEaE8C0Kch01wo4s3kRTFV64P121WZJwgmBwrq68zYg==",
"dev": true,
"requires": {
@@ -20006,7 +20041,7 @@
},
"libnpmteam": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-p420vM28Us04NAcg1rzgGW63LMM6rwe+6rtZpfDxCcXxM0zUTLl7nPFEnRF3JfFBF5skF/yuZDUthTsHgde8QA==",
"dev": true,
"requires": {
@@ -20018,7 +20053,7 @@
},
"libnpx": {
"version": "10.2.4",
"resolved": "",
"resolved": false,
"integrity": "sha512-BPc0D1cOjBeS8VIBKUu5F80s6njm0wbVt7CsGMrIcJ+SI7pi7V0uVPGpEMH9H5L8csOcclTxAXFE2VAsJXUhfA==",
"dev": true,
"requires": {
@@ -20034,7 +20069,7 @@
},
"lock-verify": {
"version": "2.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-vcLpxnGvrqisKvLQ2C2v0/u7LVly17ak2YSgoK4PrdsYBXQIax19vhKiLfvKNFx7FRrpTnitrpzF/uuCMuorIg==",
"dev": true,
"requires": {
@@ -20044,7 +20079,7 @@
},
"lockfile": {
"version": "1.0.4",
"resolved": "",
"resolved": false,
"integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==",
"dev": true,
"requires": {
@@ -20053,13 +20088,13 @@
},
"lodash._baseindexof": {
"version": "3.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=",
"dev": true
},
"lodash._baseuniq": {
"version": "4.6.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg=",
"dev": true,
"requires": {
@@ -20069,19 +20104,19 @@
},
"lodash._bindcallback": {
"version": "3.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=",
"dev": true
},
"lodash._cacheindexof": {
"version": "3.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=",
"dev": true
},
"lodash._createcache": {
"version": "3.1.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=",
"dev": true,
"requires": {
@@ -20090,61 +20125,61 @@
},
"lodash._createset": {
"version": "4.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=",
"dev": true
},
"lodash._getnative": {
"version": "3.9.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=",
"dev": true
},
"lodash._root": {
"version": "3.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=",
"dev": true
},
"lodash.clonedeep": {
"version": "4.5.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
"dev": true
},
"lodash.restparam": {
"version": "3.6.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=",
"dev": true
},
"lodash.union": {
"version": "4.6.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=",
"dev": true
},
"lodash.uniq": {
"version": "4.5.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
"dev": true
},
"lodash.without": {
"version": "4.4.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=",
"dev": true
},
"lowercase-keys": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
"dev": true
},
"lru-cache": {
"version": "5.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"requires": {
@@ -20153,7 +20188,7 @@
},
"make-dir": {
"version": "1.3.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
"dev": true,
"requires": {
@@ -20162,7 +20197,7 @@
},
"make-fetch-happen": {
"version": "5.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==",
"dev": true,
"requires": {
@@ -20181,19 +20216,19 @@
},
"meant": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-KN+1uowN/NK+sT/Lzx7WSGIj2u+3xe5n2LbwObfjOhPZiA+cCfCm6idVl0RkEfjThkw5XJ96CyRcanq6GmKtUg==",
"dev": true
},
"mime-db": {
"version": "1.35.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==",
"dev": true
},
"mime-types": {
"version": "2.1.19",
"resolved": "",
"resolved": false,
"integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==",
"dev": true,
"requires": {
@@ -20202,7 +20237,7 @@
},
"minimatch": {
"version": "3.0.4",
"resolved": "",
"resolved": false,
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
@@ -20211,13 +20246,13 @@
},
"minimist": {
"version": "1.2.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"minizlib": {
"version": "1.3.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
"dev": true,
"requires": {
@@ -20226,7 +20261,7 @@
"dependencies": {
"minipass": {
"version": "2.9.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
"dev": true,
"requires": {
@@ -20238,7 +20273,7 @@
},
"mississippi": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
"dev": true,
"requires": {
@@ -20256,7 +20291,7 @@
},
"mkdirp": {
"version": "0.5.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
@@ -20265,7 +20300,7 @@
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
}
@@ -20273,7 +20308,7 @@
},
"move-concurrently": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
"dev": true,
"requires": {
@@ -20287,7 +20322,7 @@
"dependencies": {
"aproba": {
"version": "1.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"dev": true
}
@@ -20295,19 +20330,19 @@
},
"ms": {
"version": "2.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
},
"mute-stream": {
"version": "0.0.7",
"resolved": "",
"resolved": false,
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
"dev": true
},
"node-fetch-npm": {
"version": "2.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==",
"dev": true,
"requires": {
@@ -20318,7 +20353,7 @@
},
"node-gyp": {
"version": "5.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw==",
"dev": true,
"requires": {
@@ -20337,7 +20372,7 @@
},
"nopt": {
"version": "4.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
"dev": true,
"requires": {
@@ -20347,7 +20382,7 @@
},
"normalize-package-data": {
"version": "2.5.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
"dev": true,
"requires": {
@@ -20359,7 +20394,7 @@
"dependencies": {
"resolve": {
"version": "1.10.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
"dev": true,
"requires": {
@@ -20370,7 +20405,7 @@
},
"npm-audit-report": {
"version": "1.3.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-8nH/JjsFfAWMvn474HB9mpmMjrnKb1Hx/oTAdjv4PT9iZBvBxiZ+wtDUapHCJwLqYGQVPaAfs+vL5+5k9QndXw==",
"dev": true,
"requires": {
@@ -20380,7 +20415,7 @@
},
"npm-bundled": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
"dev": true,
"requires": {
@@ -20389,13 +20424,13 @@
},
"npm-cache-filename": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-3tMGxbC/yHCp6fr4I7xfKD4FrhE=",
"dev": true
},
"npm-install-checks": {
"version": "3.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-E4kzkyZDIWoin6uT5howP8VDvkM+E8IQDcHAycaAxMbwkqhIg5eEYALnXOl3Hq9MrkdQB/2/g1xwBINXdKSRkg==",
"dev": true,
"requires": {
@@ -20404,7 +20439,7 @@
},
"npm-lifecycle": {
"version": "3.1.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g==",
"dev": true,
"requires": {
@@ -20420,19 +20455,19 @@
},
"npm-logical-tree": {
"version": "1.2.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-AJI/qxDB2PWI4LG1CYN579AY1vCiNyWfkiquCsJWqntRu/WwimVrC8yXeILBFHDwxfOejxewlmnvW9XXjMlYIg==",
"dev": true
},
"npm-normalize-package-bin": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
"dev": true
},
"npm-package-arg": {
"version": "6.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==",
"dev": true,
"requires": {
@@ -20444,7 +20479,7 @@
},
"npm-packlist": {
"version": "1.4.8",
"resolved": "",
"resolved": false,
"integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
"dev": true,
"requires": {
@@ -20455,7 +20490,7 @@
},
"npm-pick-manifest": {
"version": "3.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==",
"dev": true,
"requires": {
@@ -20466,7 +20501,7 @@
},
"npm-profile": {
"version": "4.0.4",
"resolved": "",
"resolved": false,
"integrity": "sha512-Ta8xq8TLMpqssF0H60BXS1A90iMoM6GeKwsmravJ6wYjWwSzcYBTdyWa3DZCYqPutacBMEm7cxiOkiIeCUAHDQ==",
"dev": true,
"requires": {
@@ -20477,7 +20512,7 @@
},
"npm-registry-fetch": {
"version": "4.0.7",
"resolved": "",
"resolved": false,
"integrity": "sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ==",
"dev": true,
"requires": {
@@ -20492,7 +20527,7 @@
"dependencies": {
"safe-buffer": {
"version": "5.2.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true
}
@@ -20500,7 +20535,7 @@
},
"npm-run-path": {
"version": "2.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
"dev": true,
"requires": {
@@ -20509,13 +20544,13 @@
},
"npm-user-validate": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-jOyg9c6gTU6TUZ73LQVXp1Ei6VE=",
"dev": true
},
"npmlog": {
"version": "4.1.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"dev": true,
"requires": {
@@ -20527,31 +20562,31 @@
},
"number-is-nan": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true
},
"oauth-sign": {
"version": "0.9.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
"dev": true
},
"object-assign": {
"version": "4.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true
},
"object-keys": {
"version": "1.0.12",
"resolved": "",
"resolved": false,
"integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==",
"dev": true
},
"object.getownpropertydescriptors": {
"version": "2.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
"dev": true,
"requires": {
@@ -20561,7 +20596,7 @@
},
"once": {
"version": "1.4.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
@@ -20570,25 +20605,25 @@
},
"opener": {
"version": "1.5.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==",
"dev": true
},
"os-homedir": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"dev": true
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true
},
"osenv": {
"version": "0.1.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"dev": true,
"requires": {
@@ -20598,13 +20633,13 @@
},
"p-finally": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
"dev": true
},
"package-json": {
"version": "4.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=",
"dev": true,
"requires": {
@@ -20616,7 +20651,7 @@
},
"pacote": {
"version": "9.5.12",
"resolved": "",
"resolved": false,
"integrity": "sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ==",
"dev": true,
"requires": {
@@ -20654,7 +20689,7 @@
"dependencies": {
"minipass": {
"version": "2.9.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
"dev": true,
"requires": {
@@ -20666,7 +20701,7 @@
},
"parallel-transform": {
"version": "1.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
"dev": true,
"requires": {
@@ -20677,7 +20712,7 @@
"dependencies": {
"readable-stream": {
"version": "2.3.6",
"resolved": "",
"resolved": false,
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -20692,7 +20727,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
@@ -20703,67 +20738,67 @@
},
"path-exists": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"path-is-inside": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
"dev": true
},
"path-key": {
"version": "2.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
"dev": true
},
"path-parse": {
"version": "1.0.6",
"resolved": "",
"resolved": false,
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
},
"performance-now": {
"version": "2.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
"dev": true
},
"pify": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
},
"prepend-http": {
"version": "1.0.4",
"resolved": "",
"resolved": false,
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
"dev": true
},
"process-nextick-args": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
"dev": true
},
"promise-inflight": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
"dev": true
},
"promise-retry": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=",
"dev": true,
"requires": {
@@ -20773,7 +20808,7 @@
"dependencies": {
"retry": {
"version": "0.10.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=",
"dev": true
}
@@ -20781,7 +20816,7 @@
},
"promzard": {
"version": "0.3.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=",
"dev": true,
"requires": {
@@ -20790,13 +20825,13 @@
},
"proto-list": {
"version": "1.2.4",
"resolved": "",
"resolved": false,
"integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=",
"dev": true
},
"protoduck": {
"version": "5.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==",
"dev": true,
"requires": {
@@ -20805,25 +20840,25 @@
},
"prr": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
"dev": true
},
"pseudomap": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
"dev": true
},
"psl": {
"version": "1.1.29",
"resolved": "",
"resolved": false,
"integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==",
"dev": true
},
"pump": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dev": true,
"requires": {
@@ -20833,7 +20868,7 @@
},
"pumpify": {
"version": "1.5.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
"dev": true,
"requires": {
@@ -20844,7 +20879,7 @@
"dependencies": {
"pump": {
"version": "2.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
"dev": true,
"requires": {
@@ -20856,25 +20891,25 @@
},
"punycode": {
"version": "1.4.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
"dev": true
},
"qrcode-terminal": {
"version": "0.12.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==",
"dev": true
},
"qs": {
"version": "6.5.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
"dev": true
},
"query-string": {
"version": "6.8.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-J3Qi8XZJXh93t2FiKyd/7Ec6GNifsjKXUsVFkSBj/kjLsDylWhnCz4NT1bkPcKotttPW+QbKGqqPH8OoI2pdqw==",
"dev": true,
"requires": {
@@ -20885,13 +20920,13 @@
},
"qw": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-77/cdA+a0FQwRCassYNBLMi5ltQ=",
"dev": true
},
"rc": {
"version": "1.2.8",
"resolved": "",
"resolved": false,
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"dev": true,
"requires": {
@@ -20903,7 +20938,7 @@
},
"read": {
"version": "1.0.7",
"resolved": "",
"resolved": false,
"integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=",
"dev": true,
"requires": {
@@ -20912,7 +20947,7 @@
},
"read-cmd-shim": {
"version": "1.0.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-v5yCqQ/7okKoZZkBQUAfTsQ3sVJtXdNfbPnI5cceppoxEVLYA3k+VtV2omkeo8MS94JCy4fSiUwlRBAwCVRPUA==",
"dev": true,
"requires": {
@@ -20921,7 +20956,7 @@
},
"read-installed": {
"version": "4.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha1-/5uLZ/GH0eTCm5/rMfayI6zRkGc=",
"dev": true,
"requires": {
@@ -20936,7 +20971,7 @@
},
"read-package-json": {
"version": "2.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==",
"dev": true,
"requires": {
@@ -20949,7 +20984,7 @@
},
"read-package-tree": {
"version": "5.3.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==",
"dev": true,
"requires": {
@@ -20960,7 +20995,7 @@
},
"readable-stream": {
"version": "3.6.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"requires": {
@@ -20971,7 +21006,7 @@
},
"readdir-scoped-modules": {
"version": "1.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==",
"dev": true,
"requires": {
@@ -20983,7 +21018,7 @@
},
"registry-auth-token": {
"version": "3.4.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==",
"dev": true,
"requires": {
@@ -20993,7 +21028,7 @@
},
"registry-url": {
"version": "3.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
"dev": true,
"requires": {
@@ -21002,7 +21037,7 @@
},
"request": {
"version": "2.88.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
"dev": true,
"requires": {
@@ -21030,31 +21065,31 @@
},
"require-directory": {
"version": "2.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"dev": true
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"dev": true
},
"resolve-from": {
"version": "4.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
},
"retry": {
"version": "0.12.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
"dev": true
},
"rimraf": {
"version": "2.7.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"dev": true,
"requires": {
@@ -21063,7 +21098,7 @@
},
"run-queue": {
"version": "1.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
"dev": true,
"requires": {
@@ -21072,7 +21107,7 @@
"dependencies": {
"aproba": {
"version": "1.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"dev": true
}
@@ -21080,25 +21115,25 @@
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
},
"semver-diff": {
"version": "2.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
"dev": true,
"requires": {
@@ -21107,13 +21142,13 @@
},
"set-blocking": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"dev": true
},
"sha": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-DOYnM37cNsLNSGIG/zZWch5CKIRNoLdYUQTQlcgkRkoYIUwDYjqDyye16YcDZg/OPdcbUgTKMjc4SY6TB7ZAPw==",
"dev": true,
"requires": {
@@ -21122,7 +21157,7 @@
},
"shebang-command": {
"version": "1.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
"dev": true,
"requires": {
@@ -21131,31 +21166,31 @@
},
"shebang-regex": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
"dev": true
},
"signal-exit": {
"version": "3.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true
},
"slide": {
"version": "1.1.6",
"resolved": "",
"resolved": false,
"integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=",
"dev": true
},
"smart-buffer": {
"version": "4.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==",
"dev": true
},
"socks": {
"version": "2.3.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==",
"dev": true,
"requires": {
@@ -21165,7 +21200,7 @@
},
"socks-proxy-agent": {
"version": "4.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==",
"dev": true,
"requires": {
@@ -21175,7 +21210,7 @@
"dependencies": {
"agent-base": {
"version": "4.2.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
"dev": true,
"requires": {
@@ -21186,13 +21221,13 @@
},
"sorted-object": {
"version": "2.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-fWMfS9OnmKJK8d/8+/6DM3pd9fw=",
"dev": true
},
"sorted-union-stream": {
"version": "2.1.3",
"resolved": "",
"resolved": false,
"integrity": "sha1-x3lMfgd4gAUv9xqNSi27Sppjisc=",
"dev": true,
"requires": {
@@ -21202,7 +21237,7 @@
"dependencies": {
"from2": {
"version": "1.3.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-iEE7qqX5pZfP3pIh2GmGzTwGHf0=",
"dev": true,
"requires": {
@@ -21212,13 +21247,13 @@
},
"isarray": {
"version": "0.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
"dev": true
},
"readable-stream": {
"version": "1.1.14",
"resolved": "",
"resolved": false,
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"dev": true,
"requires": {
@@ -21230,7 +21265,7 @@
},
"string_decoder": {
"version": "0.10.31",
"resolved": "",
"resolved": false,
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
"dev": true
}
@@ -21238,7 +21273,7 @@
},
"spdx-correct": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==",
"dev": true,
"requires": {
@@ -21248,13 +21283,13 @@
},
"spdx-exceptions": {
"version": "2.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==",
"dev": true
},
"spdx-expression-parse": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
"dev": true,
"requires": {
@@ -21264,19 +21299,19 @@
},
"spdx-license-ids": {
"version": "3.0.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
"dev": true
},
"split-on-first": {
"version": "1.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
"dev": true
},
"sshpk": {
"version": "1.14.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=",
"dev": true,
"requires": {
@@ -21293,7 +21328,7 @@
},
"ssri": {
"version": "6.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
"dev": true,
"requires": {
@@ -21302,7 +21337,7 @@
},
"stream-each": {
"version": "1.2.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==",
"dev": true,
"requires": {
@@ -21312,7 +21347,7 @@
},
"stream-iterate": {
"version": "1.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-K9fHcpbBcCpGSIuK1B95hl7s1OE=",
"dev": true,
"requires": {
@@ -21322,7 +21357,7 @@
"dependencies": {
"readable-stream": {
"version": "2.3.6",
"resolved": "",
"resolved": false,
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -21337,7 +21372,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
@@ -21348,19 +21383,19 @@
},
"stream-shift": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
"dev": true
},
"strict-uri-encode": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=",
"dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dev": true,
"requires": {
@@ -21370,19 +21405,19 @@
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"requires": {
@@ -21393,7 +21428,7 @@
},
"string_decoder": {
"version": "1.3.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"requires": {
@@ -21402,7 +21437,7 @@
"dependencies": {
"safe-buffer": {
"version": "5.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
"dev": true
}
@@ -21410,13 +21445,13 @@
},
"stringify-package": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==",
"dev": true
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -21425,19 +21460,19 @@
},
"strip-eof": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
"dev": true
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true
},
"supports-color": {
"version": "5.4.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"dev": true,
"requires": {
@@ -21446,7 +21481,7 @@
},
"tar": {
"version": "4.4.13",
"resolved": "",
"resolved": false,
"integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
"dev": true,
"requires": {
@@ -21461,7 +21496,7 @@
"dependencies": {
"minipass": {
"version": "2.9.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
"dev": true,
"requires": {
@@ -21473,7 +21508,7 @@
},
"term-size": {
"version": "1.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=",
"dev": true,
"requires": {
@@ -21482,19 +21517,19 @@
},
"text-table": {
"version": "0.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
"dev": true
},
"through": {
"version": "2.3.8",
"resolved": "",
"resolved": false,
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true
},
"through2": {
"version": "2.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
"dev": true,
"requires": {
@@ -21504,7 +21539,7 @@
"dependencies": {
"readable-stream": {
"version": "2.3.6",
"resolved": "",
"resolved": false,
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -21519,7 +21554,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
@@ -21530,19 +21565,19 @@
},
"timed-out": {
"version": "4.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=",
"dev": true
},
"tiny-relative-date": {
"version": "1.3.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A==",
"dev": true
},
"tough-cookie": {
"version": "2.4.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
"dev": true,
"requires": {
@@ -21552,7 +21587,7 @@
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"dev": true,
"requires": {
@@ -21561,32 +21596,32 @@
},
"tweetnacl": {
"version": "0.14.5",
"resolved": "",
"resolved": false,
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"dev": true,
"optional": true
},
"typedarray": {
"version": "0.0.6",
"resolved": "",
"resolved": false,
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
"dev": true
},
"uid-number": {
"version": "0.0.6",
"resolved": "",
"resolved": false,
"integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=",
"dev": true
},
"umask": {
"version": "1.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=",
"dev": true
},
"unique-filename": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
"dev": true,
"requires": {
@@ -21595,7 +21630,7 @@
},
"unique-slug": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=",
"dev": true,
"requires": {
@@ -21604,7 +21639,7 @@
},
"unique-string": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
"dev": true,
"requires": {
@@ -21613,19 +21648,19 @@
},
"unpipe": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
"dev": true
},
"unzip-response": {
"version": "2.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=",
"dev": true
},
"update-notifier": {
"version": "2.5.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==",
"dev": true,
"requires": {
@@ -21643,7 +21678,7 @@
},
"url-parse-lax": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
"dev": true,
"requires": {
@@ -21652,19 +21687,19 @@
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true
},
"util-extend": {
"version": "1.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8=",
"dev": true
},
"util-promisify": {
"version": "2.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=",
"dev": true,
"requires": {
@@ -21673,13 +21708,13 @@
},
"uuid": {
"version": "3.3.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==",
"dev": true
},
"validate-npm-package-license": {
"version": "3.0.4",
"resolved": "",
"resolved": false,
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
"dev": true,
"requires": {
@@ -21689,7 +21724,7 @@
},
"validate-npm-package-name": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=",
"dev": true,
"requires": {
@@ -21698,7 +21733,7 @@
},
"verror": {
"version": "1.10.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
"dev": true,
"requires": {
@@ -21709,7 +21744,7 @@
},
"wcwidth": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=",
"dev": true,
"requires": {
@@ -21718,7 +21753,7 @@
},
"which": {
"version": "1.3.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"dev": true,
"requires": {
@@ -21727,13 +21762,13 @@
},
"which-module": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
"dev": true
},
"wide-align": {
"version": "1.1.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
"dev": true,
"requires": {
@@ -21742,7 +21777,7 @@
"dependencies": {
"string-width": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
"requires": {
@@ -21755,7 +21790,7 @@
},
"widest-line": {
"version": "2.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==",
"dev": true,
"requires": {
@@ -21764,7 +21799,7 @@
},
"worker-farm": {
"version": "1.7.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
"dev": true,
"requires": {
@@ -21773,7 +21808,7 @@
},
"wrap-ansi": {
"version": "5.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
"dev": true,
"requires": {
@@ -21784,19 +21819,19 @@
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"string-width": {
"version": "3.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
@@ -21807,7 +21842,7 @@
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
@@ -21818,13 +21853,13 @@
},
"wrappy": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
"write-file-atomic": {
"version": "2.4.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==",
"dev": true,
"requires": {
@@ -21835,31 +21870,31 @@
},
"xdg-basedir": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=",
"dev": true
},
"xtend": {
"version": "4.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
"dev": true
},
"y18n": {
"version": "4.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
},
"yallist": {
"version": "3.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
"dev": true
},
"yargs": {
"version": "14.2.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==",
"dev": true,
"requires": {
@@ -21878,13 +21913,13 @@
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"find-up": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
@@ -21893,13 +21928,13 @@
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"locate-path": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
@@ -21909,7 +21944,7 @@
},
"p-limit": {
"version": "2.3.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
@@ -21918,7 +21953,7 @@
},
"p-locate": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
@@ -21927,13 +21962,13 @@
},
"p-try": {
"version": "2.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
"string-width": {
"version": "3.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
@@ -21944,7 +21979,7 @@
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
@@ -21955,7 +21990,7 @@
},
"yargs-parser": {
"version": "15.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==",
"dev": true,
"requires": {
@@ -21965,7 +22000,7 @@
"dependencies": {
"camelcase": {
"version": "5.3.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true
}
@@ -26461,6 +26496,11 @@
"kind-of": "^6.0.2"
}
},
"shallow-equal": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
"integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA=="
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "@edx/frontend-app-gradebook",
"version": "1.4.43",
"version": "1.4.47",
"description": "edx editable gradebook-ui to manipulate grade overrides on subsections",
"repository": {
"type": "git",
@@ -28,7 +28,8 @@
},
"dependencies": {
"@edx/brand": "npm:@edx/brand-edx.org@^1.3.2",
"@edx/frontend-component-footer": "10.1.1",
"@edx/frontend-component-footer": "10.1.6",
"@edx/frontend-component-header": "2.2.5",
"@edx/frontend-platform": "1.9.5",
"@edx/paragon": "14.16.4",
"@fortawesome/fontawesome-svg-core": "^1.2.25",

View File

@@ -1,36 +1,34 @@
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { Provider } from 'react-redux';
import { IntlProvider } from 'react-intl';
import { AppProvider } from '@edx/frontend-platform/react';
import Footer from '@edx/frontend-component-footer';
import Header from '@edx/frontend-component-header';
import { routePath } from 'data/constants/app';
import store from 'data/store';
import GradebookPage from 'containers/GradebookPage';
import EdxHeader from 'components/EdxHeader';
import './App.scss';
const App = () => (
<IntlProvider locale="en">
<Provider store={store}>
<Router>
<div>
<EdxHeader />
<main>
<Switch>
<Route
exact
path={routePath}
component={GradebookPage}
/>
</Switch>
</main>
<Footer logo={process.env.LOGO_POWERED_BY_OPEN_EDX_URL_SVG} />
</div>
</Router>
</Provider>
</IntlProvider>
<AppProvider store={store}>
<Router>
<div>
<Header />
<main>
<Switch>
<Route
exact
path={routePath}
component={GradebookPage}
/>
</Switch>
</main>
<Footer logo={process.env.LOGO_POWERED_BY_OPEN_EDX_URL_SVG} />
</div>
</Router>
</AppProvider>
);
export default App;

View File

@@ -9,8 +9,10 @@ $fa-font-path: "~font-awesome/fonts";
$input-focus-box-shadow: $input-box-shadow; // hack to get upgrade to paragon 4.0.0 to work
@import "~@edx/frontend-component-header/dist/index";
@import "~@edx/frontend-component-footer/dist/_footer";
@import "./components/GradesTab/GradesTab";
@import "./components/GradesView/GradesView";
@import "./components/BulkManagementHistoryView/BulkManagementHistoryView";
@import "./components/WithSidebar/WithSidebar";
@import "./components/GradebookFilters/GradebookFilters";

View File

@@ -2,15 +2,14 @@ import React from 'react';
import { shallow } from 'enzyme';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { Provider } from 'react-redux';
import { IntlProvider } from 'react-intl';
import { AppProvider } from '@edx/frontend-platform/react';
import Footer from '@edx/frontend-component-footer';
import Header from '@edx/frontend-component-header';
import { routePath } from 'data/constants/app';
import store from 'data/store';
import GradebookPage from 'containers/GradebookPage';
import EdxHeader from 'components/EdxHeader';
import App from './App';
@@ -19,11 +18,8 @@ jest.mock('react-router-dom', () => ({
Route: () => 'Route',
Switch: () => 'Switch',
}));
jest.mock('react-redux', () => ({
Provider: () => 'Provider',
}));
jest.mock('react-intl', () => ({
IntlProvider: () => 'IntlProvider',
jest.mock('@edx/frontend-platform/react', () => ({
AppProvider: () => 'AppProvider',
}));
jest.mock('data/constants/app', () => ({
routePath: '/:courseId',
@@ -31,7 +27,7 @@ jest.mock('data/constants/app', () => ({
jest.mock('@edx/frontend-component-footer', () => 'Footer');
jest.mock('data/store', () => 'testStore');
jest.mock('containers/GradebookPage', () => 'GradebookPage');
jest.mock('components/EdxHeader', () => 'EdxHeader');
jest.mock('@edx/frontend-component-header', () => 'Header');
const logo = 'fakeLogo.png';
let el;
@@ -45,28 +41,20 @@ describe('App router component', () => {
beforeEach(() => {
process.env.LOGO_POWERED_BY_OPEN_EDX_URL_SVG = logo;
el = shallow(<App />);
router = el.childAt(0).childAt(0);
router = el.childAt(0);
});
describe('IntlProvider', () => {
test('outer-wrapper component', () => {
expect(el.type()).toBe(IntlProvider);
});
test('"en" locale', () => {
expect(el.props().locale).toEqual('en');
});
});
describe('Provider, inside IntlProvider', () => {
test('first child, passed the redux store props', () => {
expect(el.childAt(0).type()).toBe(Provider);
expect(el.childAt(0).props().store).toEqual(store);
describe('AppProvider', () => {
test('AppProvider is the parent component, passed the redux store props', () => {
expect(el.type()).toBe(AppProvider);
expect(el.props().store).toEqual(store);
});
});
describe('Router', () => {
test('first child of Provider', () => {
test('first child of AppProvider', () => {
expect(router.type()).toBe(Router);
});
test('EdxHeader is above/outside-of the routing', () => {
expect(router.childAt(0).childAt(0).type()).toBe(EdxHeader);
test('Header is above/outside-of the routing', () => {
expect(router.childAt(0).childAt(0).type()).toBe(Header);
expect(router.childAt(0).childAt(1).type()).toBe('main');
});
test('Routing - GradebookPage is only route', () => {

View File

@@ -1,27 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`App router component snapshot 1`] = `
<IntlProvider
locale="en"
<AppProvider
store="testStore"
>
<Provider
store="testStore"
>
<BrowserRouter>
<div>
<EdxHeader />
<main>
<Switch>
<Route
component="GradebookPage"
exact={true}
path="/:courseId"
/>
</Switch>
</main>
<Footer />
</div>
</BrowserRouter>
</Provider>
</IntlProvider>
<BrowserRouter>
<div>
<Header />
<main>
<Switch>
<Route
component="GradebookPage"
exact={true}
path="/:courseId"
/>
</Switch>
</main>
<Footer />
</div>
</BrowserRouter>
</AppProvider>
`;

View File

@@ -0,0 +1,6 @@
.bulk-management-history-view {
.help-text {
margin-bottom: 40px;
max-width: 70%;
}
}

View File

@@ -3,14 +3,12 @@ import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { Table } from '@edx/paragon';
import { bulkManagementColumns } from 'data/constants/app';
import selectors from 'data/selectors';
import ResultsSummary from './ResultsSummary';
import messages from './messages';
export const mapHistoryRows = ({
resultsSummary,
@@ -33,12 +31,6 @@ export const HistoryTable = ({
bulkManagementHistory,
}) => (
<>
<p>
<FormattedMessage {...messages.hint1} />
<br />
<FormattedMessage {...messages.hint2} />
</p>
<Table
data={bulkManagementHistory.map(mapHistoryRows)}
hasFixedColumnWidths

View File

@@ -2,14 +2,12 @@
import React from 'react';
import { shallow } from 'enzyme';
import { Table } from '@edx/paragon';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import selectors from 'data/selectors';
import { bulkManagementColumns } from 'data/constants/app';
import ResultsSummary from './ResultsSummary';
import { HistoryTable, mapStateToProps } from './HistoryTable';
import messages from './messages';
jest.mock('@edx/frontend-platform/i18n', () => ({
defineMessages: m => m,
@@ -58,19 +56,9 @@ describe('HistoryTable', () => {
beforeEach(() => {
el = shallow(<HistoryTable {...props} />);
});
const snapshotSegments = [
'hints display',
'formatted table',
];
test(`snapshot - loads ${snapshotSegments.join(', ')}`, () => {
test('snapshot - loads formatted table', () => {
expect(el).toMatchSnapshot();
});
test('hints with break in between', () => {
const hints = el.find('p');
expect(hints.childAt(0).getElement()).toEqual(<FormattedMessage {...messages.hint1} />);
expect(hints.childAt(1).is('br')).toEqual(true);
expect(hints.childAt(2).getElement()).toEqual(<FormattedMessage {...messages.hint2} />);
});
describe('history table', () => {
let table;
beforeEach(() => {

View File

@@ -15,7 +15,7 @@ exports[`BulkManagementAlerts component no errer, no upload success snapshot - b
<FormattedMessage
defaultMessage="CSV processing. File uploads may take several minutes to complete."
description="Success Dialog message in BulkManagement Tab File Upload Form"
id="gradebook.BulkManagementTab.successDialog"
id="gradebook.BulkManagementHistoryView.successDialog"
/>
</Alert>
</Fragment>
@@ -38,7 +38,7 @@ exports[`BulkManagementAlerts component no errer, no upload success snapshot - d
<FormattedMessage
defaultMessage="CSV processing. File uploads may take several minutes to complete."
description="Success Dialog message in BulkManagement Tab File Upload Form"
id="gradebook.BulkManagementTab.successDialog"
id="gradebook.BulkManagementHistoryView.successDialog"
/>
</Alert>
</Fragment>

View File

@@ -41,21 +41,8 @@ Array [
]
`;
exports[`HistoryTable component snapshot snapshot - loads hints display, formatted table 1`] = `
exports[`HistoryTable component snapshot snapshot - loads formatted table 1`] = `
<Fragment>
<p>
<FormattedMessage
defaultMessage="Results appear in the table below."
description="Hint text on BulkManagement Tab History Table"
id="gradebook.BulkManagementTab.hint1"
/>
<br />
<FormattedMessage
defaultMessage="Grade processing may take a few seconds."
description="Hint text on BulkManagement Tab History Table"
id="gradebook.BulkManagementTab.hint2"
/>
</p>
<Table
className="table-striped"
columns={

View File

@@ -0,0 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BulkManagementHistoryView component snapshot snapshot - loads heading from messages.BulkManagementHistoryView.heading, <BulkManagementAlerts />, <HistoryTable /> 1`] = `
<div
className="bulk-management-history-view"
>
<h4>
<FormattedMessage
defaultMessage="Bulk Management History"
description="Heading text for BulkManagement History Tab"
id="gradebook.BulkManagementHistoryView.heading"
/>
</h4>
<p
className="help-text"
>
<FormattedMessage
defaultMessage="Below is a log of previous grade imports. To download a CSV of your gradebook and import grades for override, return to the Gradebook. Please note, after importing grades, it may take a few seconds to process the override."
description="Bulk Management History View help text"
id="gradebook.BulkManagementHistoryView"
/>
</p>
<BulkManagementAlerts />
<HistoryTable />
</div>
`;

View File

@@ -4,20 +4,21 @@ import { FormattedMessage } from '@edx/frontend-platform/i18n';
import messages from './messages';
import BulkManagementAlerts from './BulkManagementAlerts';
import FileUploadForm from './FileUploadForm';
import HistoryTable from './HistoryTable';
/**
* <BulkManagementTab />
* <BulkManagementHistoryView />
* top-level view for managing uploads of bulk management override csvs.
*/
export const BulkManagementTab = () => (
<div>
<h4><FormattedMessage {...(messages.heading)} /></h4>
export const BulkManagementHistoryView = () => (
<div className="bulk-management-history-view">
<h4><FormattedMessage {...messages.heading} /></h4>
<p className="help-text">
<FormattedMessage {...messages.helpText} />
</p>
<BulkManagementAlerts />
<FileUploadForm />
<HistoryTable />
</div>
);
export default BulkManagementTab;
export default BulkManagementHistoryView;

View File

@@ -3,27 +3,24 @@ import React from 'react';
import { shallow } from 'enzyme';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { BulkManagementTab } from '.';
import { BulkManagementHistoryView } from '.';
import BulkManagementAlerts from './BulkManagementAlerts';
import FileUploadForm from './FileUploadForm';
import HistoryTable from './HistoryTable';
import messages from './messages';
jest.mock('./BulkManagementAlerts', () => 'BulkManagementAlerts');
jest.mock('./FileUploadForm', () => 'FileUploadForm');
jest.mock('./HistoryTable', () => 'HistoryTable');
describe('BulkManagementTab', () => {
describe('BulkManagementHistoryView', () => {
describe('component', () => {
let el;
beforeEach(() => {
el = shallow(<BulkManagementTab />);
el = shallow(<BulkManagementHistoryView />);
});
describe('snapshot', () => {
const snapshotSegments = [
'heading from messages.BulkManagementTab.heading',
'heading from messages.BulkManagementHistoryView.heading',
'<BulkManagementAlerts />',
'<FileUploadForm />',
'<HistoryTable />',
];
test(`snapshot - loads ${snapshotSegments.join(', ')}`, () => {
@@ -39,8 +36,7 @@ describe('BulkManagementTab', () => {
});
test('heading, then alerts, then upload form, then table', () => {
expect(el.childAt(0).is('h4')).toEqual(true);
expect(el.childAt(1).is(BulkManagementAlerts)).toEqual(true);
expect(el.childAt(2).is(FileUploadForm)).toEqual(true);
expect(el.childAt(2).is(BulkManagementAlerts)).toEqual(true);
expect(el.childAt(3).is(HistoryTable)).toEqual(true);
});
});

View File

@@ -0,0 +1,21 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
heading: {
id: 'gradebook.BulkManagementHistoryView.heading',
defaultMessage: 'Bulk Management History',
description: 'Heading text for BulkManagement History Tab',
},
helpText: {
id: 'gradebook.BulkManagementHistoryView',
defaultMessage: 'Below is a log of previous grade imports. To download a CSV of your gradebook and import grades for override, return to the Gradebook. Please note, after importing grades, it may take a few seconds to process the override.',
description: 'Bulk Management History View help text',
},
successDialog: {
id: 'gradebook.BulkManagementHistoryView.successDialog',
defaultMessage: 'CSV processing. File uploads may take several minutes to complete.',
description: 'Success Dialog message in BulkManagement Tab File Upload Form',
},
});
export default messages;

View File

@@ -1,16 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BulkManagementTab component snapshot snapshot - loads heading from messages.BulkManagementTab.heading, <BulkManagementAlerts />, <FileUploadForm />, <HistoryTable /> 1`] = `
<div>
<h4>
<FormattedMessage
defaultMessage="Use this feature by downloading a CSV for bulk management, overriding grades locally, and coming back here to upload."
description="Heading text for BulkManagement Tab"
id="gradebook.BulkManagementTab.heading"
/>
</h4>
<BulkManagementAlerts />
<FileUploadForm />
<HistoryTable />
</div>
`;

View File

@@ -1,36 +0,0 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
csvUploadLabel: {
id: 'gradebook.BulkManagementTab.csvUploadLabel',
defaultMessage: 'Upload Grade CSV',
description: 'Button in BulkManagementTab Alerts',
},
heading: {
id: 'gradebook.BulkManagementTab.heading',
defaultMessage: 'Use this feature by downloading a CSV for bulk management, overriding grades locally, and coming back here to upload.',
description: 'Heading text for BulkManagement Tab',
},
hint1: {
id: 'gradebook.BulkManagementTab.hint1',
defaultMessage: 'Results appear in the table below.',
description: 'Hint text on BulkManagement Tab History Table',
},
hint2: {
id: 'gradebook.BulkManagementTab.hint2',
defaultMessage: 'Grade processing may take a few seconds.',
description: 'Hint text on BulkManagement Tab History Table',
},
importBtnText: {
id: 'gradebook.BulkManagementTab.importBtnText',
defaultMessage: 'Import Grades',
description: 'Button in BulkManagement Tab File Upload Form',
},
successDialog: {
id: 'gradebook.BulkManagementTab.successDialog',
defaultMessage: 'CSV processing. File uploads may take several minutes to complete.',
description: 'Success Dialog message in BulkManagement Tab File Upload Form',
},
});
export default messages;

View File

@@ -26,9 +26,13 @@ exports[`GradebookHeader component snapshots default values (grades frozen, cann
id="gradebook.GradebookHeader.appLabel"
/>
</h1>
<h3>
fakeID
</h3>
<div
className="subtitle-row d-flex justify-content-between align-items-center"
>
<h3>
fakeID
</h3>
</div>
<div
className="alert alert-warning"
role="alert"
@@ -68,9 +72,13 @@ exports[`GradebookHeader component snapshots grades frozen, can view. grades fro
id="gradebook.GradebookHeader.appLabel"
/>
</h1>
<h3>
fakeID
</h3>
<div
className="subtitle-row d-flex justify-content-between align-items-center"
>
<h3>
fakeID
</h3>
</div>
<div
className="alert alert-warning"
role="alert"
@@ -110,9 +118,13 @@ exports[`GradebookHeader component snapshots grades frozen, cannot view unauthor
id="gradebook.GradebookHeader.appLabel"
/>
</h1>
<h3>
fakeID
</h3>
<div
className="subtitle-row d-flex justify-content-between align-items-center"
>
<h3>
fakeID
</h3>
</div>
<div
className="alert alert-warning"
role="alert"
@@ -135,3 +147,115 @@ exports[`GradebookHeader component snapshots grades frozen, cannot view unauthor
</div>
</div>
`;
exports[`GradebookHeader component snapshots show bulk management, active view is bulkManagementHistory view toggle view button to grades 1`] = `
<div
className="gradebook-header"
>
<a
className="mb-3"
href="http://localhost:18000/courses/fakeID/instructor"
>
<span
aria-hidden="true"
>
&lt;&lt;
</span>
<FormattedMessage
defaultMessage="Back to Dashboard"
description="Button text to take user back to LMS dashboard in Gradebook Header"
id="gradebook.GradebookHeader.backButton"
/>
</a>
<h1>
<FormattedMessage
defaultMessage="Gradebook"
description="Top-level app title in Gradebook Header component"
id="gradebook.GradebookHeader.appLabel"
/>
</h1>
<div
className="subtitle-row d-flex justify-content-between align-items-center"
>
<h3>
fakeID
</h3>
<Button
onClick={[MockFunction this.handleToggleViewClick]}
variant="tertiary"
>
<FormattedMessage
defaultMessage="Return to Gradebook"
description="Button text for button navigating to Grades view."
id="gradebook.GradebookHeader.toGradesView"
/>
</Button>
</div>
<div
className="alert alert-warning"
role="alert"
>
<FormattedMessage
defaultMessage="You are not authorized to view the gradebook for this course."
description="Warning message in Gradebook Header when user is not allowed to view the app"
id="gradebook.GradebookHeader.unauthorizedWarning"
/>
</div>
</div>
`;
exports[`GradebookHeader component snapshots show bulk management, active view is grades view toggle view button to activity log 1`] = `
<div
className="gradebook-header"
>
<a
className="mb-3"
href="http://localhost:18000/courses/fakeID/instructor"
>
<span
aria-hidden="true"
>
&lt;&lt;
</span>
<FormattedMessage
defaultMessage="Back to Dashboard"
description="Button text to take user back to LMS dashboard in Gradebook Header"
id="gradebook.GradebookHeader.backButton"
/>
</a>
<h1>
<FormattedMessage
defaultMessage="Gradebook"
description="Top-level app title in Gradebook Header component"
id="gradebook.GradebookHeader.appLabel"
/>
</h1>
<div
className="subtitle-row d-flex justify-content-between align-items-center"
>
<h3>
fakeID
</h3>
<Button
onClick={[MockFunction this.handleToggleViewClick]}
variant="tertiary"
>
<FormattedMessage
defaultMessage="View Bulk Management History"
description="Button text for button navigating to Bulk Managment Activity Log"
id="gradebook.GradebookHeader.toActivityLogButton"
/>
</Button>
</div>
<div
className="alert alert-warning"
role="alert"
>
<FormattedMessage
defaultMessage="You are not authorized to view the gradebook for this course."
description="Warning message in Gradebook Header when user is not allowed to view the app"
id="gradebook.GradebookHeader.unauthorizedWarning"
/>
</div>
</div>
`;

View File

@@ -3,17 +3,36 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { Button } from '@edx/paragon';
import { configuration } from 'config';
import { views } from 'data/constants/app';
import actions from 'data/actions';
import selectors from 'data/selectors';
import messages from './messages';
export class GradebookHeader extends React.Component {
constructor(props) {
super(props);
this.handleToggleViewClick = this.handleToggleViewClick.bind(this);
}
get toggleViewMessage() {
return this.props.activeView === views.grades
? messages.toActivityLog
: messages.toGradesView;
}
lmsInstructorDashboardUrl = courseId => (
`${configuration.LMS_BASE_URL}/courses/${courseId}/instructor`
);
handleToggleViewClick() {
const newView = this.props.activeView === views.grades ? views.bulkManagementHistory : views.grades;
this.props.setView(newView);
}
render() {
return (
<div className="gradebook-header">
@@ -27,7 +46,17 @@ export class GradebookHeader extends React.Component {
<h1>
<FormattedMessage {...messages.gradebook} />
</h1>
<h3>{this.props.courseId}</h3>
<div className="subtitle-row d-flex justify-content-between align-items-center">
<h3>{this.props.courseId}</h3>
{ this.props.showBulkManagement && (
<Button
variant="tertiary"
onClick={this.handleToggleViewClick}
>
<FormattedMessage {...this.toggleViewMessage} />
</Button>
)}
</div>
{this.props.areGradesFrozen
&& (
<div className="alert alert-warning" role="alert">
@@ -49,19 +78,29 @@ GradebookHeader.defaultProps = {
courseId: '',
areGradesFrozen: false,
canUserViewGradebook: false,
showBulkManagement: false,
};
GradebookHeader.propTypes = {
// redux
activeView: PropTypes.string.isRequired,
courseId: PropTypes.string,
areGradesFrozen: PropTypes.bool,
canUserViewGradebook: PropTypes.bool,
setView: PropTypes.func.isRequired,
showBulkManagement: PropTypes.bool,
};
export const mapStateToProps = (state) => ({
activeView: selectors.app.activeView(state),
courseId: selectors.app.courseId(state),
areGradesFrozen: selectors.assignmentTypes.areGradesFrozen(state),
canUserViewGradebook: selectors.roles.canUserViewGradebook(state),
showBulkManagement: selectors.root.showBulkManagement(state),
});
export default connect(mapStateToProps)(GradebookHeader);
export const mapDispatchToProps = {
setView: actions.app.setView,
};
export default connect(mapStateToProps, mapDispatchToProps)(GradebookHeader);

View File

@@ -21,6 +21,16 @@ const messages = defineMessages({
defaultMessage: 'You are not authorized to view the gradebook for this course.',
description: 'Warning message in Gradebook Header when user is not allowed to view the app',
},
toActivityLog: {
id: 'gradebook.GradebookHeader.toActivityLogButton',
defaultMessage: 'View Bulk Management History',
description: 'Button text for button navigating to Bulk Managment Activity Log',
},
toGradesView: {
id: 'gradebook.GradebookHeader.toGradesView',
defaultMessage: 'Return to Gradebook',
description: 'Button text for button navigating to Grades view.',
},
});
export default messages;

View File

@@ -1,42 +1,119 @@
import React from 'react';
import { shallow } from 'enzyme';
import { Button } from '@edx/paragon';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import actions from 'data/actions';
import selectors from 'data/selectors';
import { GradebookHeader, mapStateToProps } from '.';
import { views } from 'data/constants/app';
import messages from './messages';
import { GradebookHeader, mapDispatchToProps, mapStateToProps } from '.';
jest.mock('@edx/frontend-platform/i18n', () => ({
defineMessages: messages => messages,
FormattedMessage: 'FormattedMessage',
jest.mock('@edx/paragon', () => ({
Button: () => 'Button',
}));
jest.mock('@edx/frontend-platform/i18n', () => ({
defineMessages: m => m,
FormattedMessage: () => 'FormattedMessage',
}));
jest.mock('data/actions', () => ({
__esModule: true,
default: {
app: { setView: jest.fn() },
},
}));
jest.mock('data/selectors', () => ({
__esModule: true,
default: {
app: { courseId: jest.fn(state => ({ courseId: state })) },
app: {
activeView: jest.fn(state => ({ aciveView: state })),
courseId: jest.fn(state => ({ courseId: state })),
},
assignmentTypes: { areGradesFrozen: jest.fn(state => ({ areGradesFrozen: state })) },
roles: { canUserViewGradebook: jest.fn(state => ({ canUserViewGradebook: state })) },
root: { showBulkManagement: jest.fn(state => ({ showBulkManagement: state })) },
},
}));
const courseId = 'fakeID';
describe('GradebookHeader component', () => {
const props = {
activeView: views.grades,
areGradesFrozen: false,
canUserViewGradebook: false,
courseId,
showBulkManagement: false,
};
beforeEach(() => {
props.setView = jest.fn();
});
describe('snapshots', () => {
let el;
beforeEach(() => {
el = shallow(<GradebookHeader {...props} />);
el.instance().handleToggleViewClick = jest.fn().mockName('this.handleToggleViewClick');
});
describe('default values (grades frozen, cannot view).', () => {
test('unauthorized warning, but no grades frozen warning', () => {
const props = { courseId, areGradesFrozen: false, canUserViewGradebook: false };
expect(shallow(<GradebookHeader {...props} />)).toMatchSnapshot();
expect(el.instance().render()).toMatchSnapshot();
});
});
describe('grades frozen, cannot view', () => {
test('unauthorized warning, and grades frozen warning.', () => {
const props = { courseId, areGradesFrozen: true, canUserViewGradebook: false };
expect(shallow(<GradebookHeader {...props} />)).toMatchSnapshot();
el.setProps({ areGradesFrozen: true });
expect(el.instance().render()).toMatchSnapshot();
});
});
describe('grades frozen, can view.', () => {
test('grades frozen warning but no unauthorized warning', () => {
const props = { courseId, areGradesFrozen: true, canUserViewGradebook: true };
expect(shallow(<GradebookHeader {...props} />)).toMatchSnapshot();
el.setProps({ areGradesFrozen: true, canUserViewGradebook: true });
expect(el.instance().render()).toMatchSnapshot();
});
});
describe('show bulk management, active view is grades view', () => {
test('toggle view button to activity log', () => {
el.setProps({ showBulkManagement: true });
expect(el.find(Button).getElement()).toEqual((
<Button
variant="tertiary"
onClick={el.instance().handleToggleViewClick}
>
<FormattedMessage {...messages.toActivityLog} />
</Button>
));
expect(el.instance().render()).toMatchSnapshot();
});
});
describe('show bulk management, active view is bulkManagementHistory view', () => {
test('toggle view button to grades', () => {
el.setProps({ showBulkManagement: true, activeView: views.bulkManagementHistory });
expect(el.find(Button).getElement()).toEqual((
<Button
variant="tertiary"
onClick={el.instance().handleToggleViewClick}
>
<FormattedMessage {...messages.toGradesView} />
</Button>
));
expect(el.instance().render()).toMatchSnapshot();
});
});
});
describe('behavior', () => {
let el;
beforeEach(() => {
el = shallow(<GradebookHeader {...props} />);
});
describe('handleToggleViewClick', () => {
test('calls setView with activity view if activeView is grades', () => {
el.instance().handleToggleViewClick();
expect(props.setView).toHaveBeenCalledWith(views.bulkManagementHistory);
});
test('calls setView with grades view if activeView is bulkManagementHistory', () => {
el.setProps({ activeView: views.bulkManagementHistory });
el.instance().handleToggleViewClick();
expect(props.setView).toHaveBeenCalledWith(views.grades);
});
});
});
@@ -46,6 +123,9 @@ describe('GradebookHeader component', () => {
beforeEach(() => {
mapped = mapStateToProps(testState);
});
test('activeView from app.activeView', () => {
expect(mapped.activeView).toEqual(selectors.app.activeView(testState));
});
test('courseId from app.courseId', () => {
expect(mapped.courseId).toEqual(selectors.app.courseId(testState));
});
@@ -59,5 +139,14 @@ describe('GradebookHeader component', () => {
mapped.canUserViewGradebook,
).toEqual(selectors.roles.canUserViewGradebook(testState));
});
test('showBulkManagement from root showBulkManagement selector', () => {
expect(mapped.showBulkManagement).toEqual(selectors.root.showBulkManagement(testState));
});
});
describe('mapDispatchToProps', () => {
test('setView from actions.app.setView', () => {
expect(mapDispatchToProps.setView).toEqual(actions.app.setView);
});
});
});

View File

@@ -1,108 +0,0 @@
/* eslint-disable react/sort-comp, react/button-has-type */
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { StatefulButton, Icon } from '@edx/paragon';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { StrictDict } from 'utils';
import actions from 'data/actions';
import selectors from 'data/selectors';
import messages from './messages';
export const basicButtonProps = () => ({
variant: 'outline-primary',
icons: {
default: <Icon className="fa fa-download mr-2" />,
pending: <Icon className="fa fa-spinner fa-spin mr-2" />,
},
disabledStates: ['pending'],
className: 'ml-2',
});
export const buttonStates = StrictDict({
pending: 'pending',
default: 'default',
});
/**
* <BulkManagementControls />
* Provides download buttons for Bulk Management and Intervention reports, only if
* showBulkManagement is set in redus.
*/
export class BulkManagementControls extends React.Component {
constructor(props) {
super(props);
this.buttonProps = this.buttonProps.bind(this);
this.handleClickDownloadInterventions = this.handleClickDownloadInterventions.bind(this);
this.handleClickExportGrades = this.handleClickExportGrades.bind(this);
}
buttonProps(label) {
return {
labels: { default: label, pending: label },
state: this.props.showSpinner ? 'pending' : 'default',
...basicButtonProps(),
};
}
handleClickDownloadInterventions() {
this.props.downloadInterventionReport();
window.location.assign(this.props.interventionExportUrl);
}
// At present, we don't store label and value in google analytics. By setting the label
// property of the below events, I want to verify that we can set the label of google anlatyics
// The following properties of a google analytics event are:
// category (used), name(used), label(not used), value(not used)
handleClickExportGrades() {
this.props.downloadBulkGradesReport();
window.location.assign(this.props.gradeExportUrl);
}
render() {
return this.props.showBulkManagement && (
<div>
<StatefulButton
{...this.buttonProps(<FormattedMessage {...messages.bulkManagement} />)}
onClick={this.handleClickExportGrades}
/>
<StatefulButton
{...this.buttonProps(<FormattedMessage {...messages.interventions} />)}
onClick={this.handleClickDownloadInterventions}
/>
</div>
);
}
}
BulkManagementControls.defaultProps = {
showBulkManagement: false,
showSpinner: false,
};
BulkManagementControls.propTypes = {
// redux
downloadBulkGradesReport: PropTypes.func.isRequired,
downloadInterventionReport: PropTypes.func.isRequired,
gradeExportUrl: PropTypes.string.isRequired,
interventionExportUrl: PropTypes.string.isRequired,
showSpinner: PropTypes.bool,
showBulkManagement: PropTypes.bool,
};
export const mapStateToProps = (state) => ({
gradeExportUrl: selectors.root.gradeExportUrl(state),
interventionExportUrl: selectors.root.interventionExportUrl(state),
showBulkManagement: selectors.root.showBulkManagement(state),
showSpinner: selectors.root.shouldShowSpinner(state),
});
export const mapDispatchToProps = {
downloadBulkGradesReport: actions.grades.downloadReport.bulkGrades,
downloadInterventionReport: actions.grades.downloadReport.intervention,
};
export default connect(mapStateToProps, mapDispatchToProps)(BulkManagementControls);

View File

@@ -1,56 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SearchControls Component Snapshots basic snapshot 1`] = `
<React.Fragment>
<h4>
<FormattedMessage
defaultMessage="Step 1: Filter the Grade Report"
description="Filter controls container heading string"
id="gradebook.GradesTab.filterHeading"
/>
</h4>
<div
className="d-flex justify-content-between"
>
<Button
className="btn-primary align-self-start"
id="edit-filters-btn"
onClick={[MockFunction toggleFilterDrawer]}
>
<Icon
className="fa fa-filter"
/>
<FormattedMessage
defaultMessage="Edit Filters"
description="Button text on Grades tab to open/close the Filters tab"
id="gradebook.GradesTab.editFilterLabel"
/>
</Button>
<div>
<SearchField
inputLabel={
<FormattedMessage
defaultMessage="Search for a learner"
description="Search description label"
id="gradebook.GradesTab.search.label"
/>
}
onChange={[MockFunction onChange]}
onClear={[MockFunction onClear]}
onSubmit={[MockFunction fetchGrades]}
value="alice"
/>
<small
className="form-text text-muted search-help-text"
>
<FormattedMessage
defaultMessage="Search by username, email, or student key"
description="Search hint label"
id="gradebook.GradesTab.search.hint"
/>
</small>
</div>
</div>
</React.Fragment>
`;

View File

@@ -1,76 +0,0 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
bulkManagement: {
id: 'gradebook.GradesTab.BulkManagementControls.bulkManagementLabel',
defaultMessage: 'Bulk Management',
description: 'Button text for bulk grades download control in GradesTab',
},
interventions: {
id: 'gradebook.GradesTab.BulkManagementControls.interventionsLabel',
defaultMessage: 'Interventions',
description: 'Button text for intervention report download control in GradesTab',
},
scoreView: {
id: 'gradebook.GradesTab.scoreViewLabel',
defaultMessage: 'Score View',
description: 'Score format select dropdown label',
},
absolute: {
id: 'gradebook.GradesTab.absoluteOption',
defaultMessage: 'Absolute',
description: 'Score format select dropdown option',
},
percent: {
id: 'gradebook.GradesTab.percentOption',
defaultMessage: 'Percent',
description: 'Score format select dropdown option',
},
filterStepHeading: {
id: 'gradebook.GradesTab.filterHeading',
defaultMessage: 'Step 1: Filter the Grade Report',
description: 'Filter controls container heading string',
},
editFilters: {
id: 'gradebook.GradesTab.editFilterLabel',
defaultMessage: 'Edit Filters',
description: 'Button text on Grades tab to open/close the Filters tab',
},
searchLabel: {
id: 'gradebook.GradesTab.search.label',
defaultMessage: 'Search for a learner',
description: 'Search description label',
},
searchHint: {
id: 'gradebook.GradesTab.search.hint',
defaultMessage: 'Search by username, email, or student key',
description: 'Search hint label',
},
editSuccessAlert: {
id: 'gradebook.GradesTab.editSuccessAlert',
defaultMessage: 'The grade has been successfully edited. You may see a slight delay before updates appear in the Gradebook.',
description: 'Alert text for successful edit action',
},
maxGradeInvalid: {
id: 'gradebook.GradesTab.maxCourseGradeInvalid',
defaultMessage: 'Maximum course grade must be between 0 and 100',
description: 'Alert text for invalid maximum course grade',
},
minGradeInvalid: {
id: 'gradebook.GradesTab.minCourseGradeInvalid',
defaultMessage: 'Minimum course grade must be between 0 and 100',
description: 'Alert text for invalid minimum course grade',
},
gradebookStepHeading: {
id: 'gradebook.GradesTab.gradebookStepHeading',
defaultMessage: 'Step 2: View or Modify Individual Grades',
description: 'Alert text for invalid minimum course grade',
},
mastersHint: {
id: 'gradebook.GradesTab.mastersHint',
defaultMessage: "available for learners in the Master's track only",
description: 'Masters feature availability hint on Grades Tab',
},
});
export default messages;

View File

@@ -0,0 +1,71 @@
/* eslint-disable react/sort-comp, react/button-has-type */
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { views } from 'data/constants/app';
import actions from 'data/actions';
import selectors from 'data/selectors';
import NetworkButton from 'components/NetworkButton';
import ImportGradesButton from './ImportGradesButton';
import messages from './BulkManagementControls.messages';
/**
* <BulkManagementControls />
* Provides download buttons for Bulk Management and Intervention reports, only if
* showBulkManagement is set in redus.
*/
export class BulkManagementControls extends React.Component {
constructor(props) {
super(props);
this.handleClickExportGrades = this.handleClickExportGrades.bind(this);
this.handleViewActivityLog = this.handleViewActivityLog.bind(this);
}
handleClickExportGrades() {
this.props.downloadBulkGradesReport();
window.location.assign(this.props.gradeExportUrl);
}
handleViewActivityLog() {
this.props.setView(views.bulkManagementHistory);
}
render() {
return this.props.showBulkManagement && (
<div className="d-flex">
<NetworkButton
label={messages.downloadGradesBtn}
onClick={this.handleClickExportGrades}
/>
<ImportGradesButton />
</div>
);
}
}
BulkManagementControls.defaultProps = {
showBulkManagement: false,
};
BulkManagementControls.propTypes = {
// redux
downloadBulkGradesReport: PropTypes.func.isRequired,
gradeExportUrl: PropTypes.string.isRequired,
showBulkManagement: PropTypes.bool,
setView: PropTypes.func.isRequired,
};
export const mapStateToProps = (state) => ({
gradeExportUrl: selectors.root.gradeExportUrl(state),
showBulkManagement: selectors.root.showBulkManagement(state),
});
export const mapDispatchToProps = {
downloadBulkGradesReport: actions.grades.downloadReport.bulkGrades,
setView: actions.app.setView,
};
export default connect(mapStateToProps, mapDispatchToProps)(BulkManagementControls);

View File

@@ -0,0 +1,11 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
downloadGradesBtn: {
id: 'gradebook.GradesView.BulkManagementControls.bulkManagementLabel',
defaultMessage: 'Download Grades',
description: 'Button text for bulk grades download control in GradesView',
},
});
export default messages;

View File

@@ -3,15 +3,16 @@ import { shallow } from 'enzyme';
import actions from 'data/actions';
import selectors from 'data/selectors';
import { views } from 'data/constants/app';
import {
BulkManagementControls,
basicButtonProps,
buttonStates,
mapStateToProps,
mapDispatchToProps,
} from './BulkManagementControls';
jest.mock('./ImportGradesButton', () => 'ImportGradesButton');
jest.mock('components/NetworkButton', () => 'NetworkButton');
jest.mock('data/selectors', () => ({
__esModule: true,
default: {
@@ -19,13 +20,13 @@ jest.mock('data/selectors', () => ({
gradeExportUrl: (state) => ({ gradeExportUrl: state }),
interventionExportUrl: (state) => ({ interventionExportUrl: state }),
showBulkManagement: (state) => ({ showBulkManagement: state }),
shouldShowSpinner: (state) => ({ showSpinner: state }),
},
},
}));
jest.mock('data/actions', () => ({
__esModule: true,
default: {
app: { setView: jest.fn() },
grades: {
downloadReport: {
bulkGrades: jest.fn(),
@@ -47,22 +48,12 @@ describe('BulkManagementControls', () => {
...props,
downloadBulkGradesReport: jest.fn(),
downloadInterventionReport: jest.fn(),
setView: jest.fn(),
};
});
test('snapshot - empty if showBulkManagement is not truthy', () => {
expect(shallow(<BulkManagementControls {...props} />)).toEqual({});
});
test('snapshot - buttonProps for each button ("Bulk Management" and "Interventions")', () => {
el = shallow(<BulkManagementControls {...props} showBulkManagement />);
jest.spyOn(el.instance(), 'buttonProps').mockImplementation(
value => ({ buttonProps: value }),
);
jest.spyOn(el.instance(), 'handleClickExportGrades').mockName('this.handleClickExportGrades');
jest.spyOn(
el.instance(),
'handleClickDownloadInterventions',
).mockName('this.handleClickDownloadInterventions');
});
describe('behavior', () => {
const oldWindowLocation = window.location;
@@ -87,37 +78,10 @@ describe('BulkManagementControls', () => {
// restore `window.location` to the `jsdom` `Location` object
window.location = oldWindowLocation;
});
describe('buttonProps', () => {
test('loads default and pending labels based on passed string', () => {
const label = 'Fake Label';
const { labels, state, ...rest } = el.instance().buttonProps(label);
expect(rest).toEqual(basicButtonProps());
expect(labels).toEqual({ default: label, pending: label });
});
test('loads pending state if props.showSpinner', () => {
const label = 'Fake Label';
el.setProps({ showSpinner: true });
const { labels, state, ...rest } = el.instance().buttonProps(label);
expect(state).toEqual(buttonStates.pending);
expect(rest).toEqual(basicButtonProps());
});
test('loads default state if not props.showSpinner', () => {
const label = 'Fake Label';
const { labels, state, ...rest } = el.instance().buttonProps(label);
expect(state).toEqual(buttonStates.default);
expect(rest).toEqual(basicButtonProps());
});
});
describe('handleClickDownloadInterventions', () => {
const assertions = [
'calls props.downloadInterventionReport',
'sets location to props.interventionsExportUrl',
];
it(assertions.join(' and '), () => {
el.instance().handleClickDownloadInterventions();
expect(props.downloadInterventionReport).toHaveBeenCalled();
expect(window.location.assign).toHaveBeenCalledWith(props.interventionExportUrl);
describe('handleViewActivityLog', () => {
it('calls props.setView(views.bulkManagementHistory)', () => {
el.instance().handleViewActivityLog();
expect(props.setView).toHaveBeenCalledWith(views.bulkManagementHistory);
});
});
describe('handleClickExportGrades', () => {
@@ -143,15 +107,9 @@ describe('BulkManagementControls', () => {
test('gradeExportUrl from root.gradeExportUrl', () => {
expect(mapped.gradeExportUrl).toEqual(selectors.root.gradeExportUrl(testState));
});
test('interventionExportUrl from root.interventionExportUrl', () => {
expect(mapped.interventionExportUrl).toEqual(selectors.root.interventionExportUrl(testState));
});
test('showBulkManagement from root.showBulkManagement', () => {
expect(mapped.showBulkManagement).toEqual(selectors.root.showBulkManagement(testState));
});
test('showSpinner from root.shouldShowSpinner', () => {
expect(mapped.showSpinner).toEqual(selectors.root.shouldShowSpinner(testState));
});
});
describe('mapDispatchToProps', () => {
test('downloadBulkGradesReport from actions.grades.downloadReport.bulkGrades', () => {
@@ -159,10 +117,8 @@ describe('BulkManagementControls', () => {
mapDispatchToProps.downloadBulkGradesReport,
).toEqual(actions.grades.downloadReport.bulkGrades);
});
test('downloadInterventionReport from actions.grades.downloadReport.invervention', () => {
expect(
mapDispatchToProps.downloadInterventionReport,
).toEqual(actions.grades.downloadReport.intervention);
test('setView from actions.app.setView', () => {
expect(mapDispatchToProps.setView).toEqual(actions.app.setView);
});
});
});

View File

@@ -9,7 +9,7 @@ exports[`OverrideTable Component snapshots basic snapshot shows a row for each e
"label": <FormattedMessage
defaultMessage="Date"
description="Edit Modal Override Table Date column header"
id="gradebook.GradesTab.EditModal.Overrides.dateHeader"
id="gradebook.GradesView.EditModal.Overrides.dateHeader"
/>,
},
Object {
@@ -17,7 +17,7 @@ exports[`OverrideTable Component snapshots basic snapshot shows a row for each e
"label": <FormattedMessage
defaultMessage="Grader"
description="Edit Modal Override Table Grader column header"
id="gradebook.GradesTab.EditModal.Overrides.graderHeader"
id="gradebook.GradesView.EditModal.Overrides.graderHeader"
/>,
},
Object {
@@ -25,7 +25,7 @@ exports[`OverrideTable Component snapshots basic snapshot shows a row for each e
"label": <FormattedMessage
defaultMessage="Reason"
description="Edit Modal Override Table Reason column header"
id="gradebook.GradesTab.EditModal.Overrides.reasonHeader"
id="gradebook.GradesView.EditModal.Overrides.reasonHeader"
/>,
},
Object {
@@ -33,7 +33,7 @@ exports[`OverrideTable Component snapshots basic snapshot shows a row for each e
"label": <FormattedMessage
defaultMessage="Adjusted grade"
description="Edit Modal Override Table Adjusted grade column header"
id="gradebook.GradesTab.EditModal.Overrides.adjustedGradeHeader"
id="gradebook.GradesView.EditModal.Overrides.adjustedGradeHeader"
/>,
},
]

View File

@@ -2,22 +2,22 @@ import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
adjustedGradeHeader: {
id: 'gradebook.GradesTab.EditModal.Overrides.adjustedGradeHeader',
id: 'gradebook.GradesView.EditModal.Overrides.adjustedGradeHeader',
defaultMessage: 'Adjusted grade',
description: 'Edit Modal Override Table Adjusted grade column header',
},
dateHeader: {
id: 'gradebook.GradesTab.EditModal.Overrides.dateHeader',
id: 'gradebook.GradesView.EditModal.Overrides.dateHeader',
defaultMessage: 'Date',
description: 'Edit Modal Override Table Date column header',
},
graderHeader: {
id: 'gradebook.GradesTab.EditModal.Overrides.graderHeader',
id: 'gradebook.GradesView.EditModal.Overrides.graderHeader',
defaultMessage: 'Grader',
description: 'Edit Modal Override Table Grader column header',
},
reasonHeader: {
id: 'gradebook.GradesTab.EditModal.Overrides.reasonHeader',
id: 'gradebook.GradesView.EditModal.Overrides.reasonHeader',
defaultMessage: 'Reason',
description: 'Edit Modal Override Table Reason column header',
},

View File

@@ -8,7 +8,7 @@ exports[`ModalHeaders Component snapshots gradeOverrideHistoryError is and empty
<FormattedMessage
defaultMessage="Assignment"
description="Edit Modal Assignment header"
id="gradebook.GradesTab.EditModal.headers.assignment"
id="gradebook.GradesView.EditModal.headers.assignment"
/>
}
value="Qwerty"
@@ -19,7 +19,7 @@ exports[`ModalHeaders Component snapshots gradeOverrideHistoryError is and empty
<FormattedMessage
defaultMessage="Student"
description="Edit Modal Student header"
id="gradebook.GradesTab.EditModal.headers.student"
id="gradebook.GradesView.EditModal.headers.student"
/>
}
value="Uiop"
@@ -30,7 +30,7 @@ exports[`ModalHeaders Component snapshots gradeOverrideHistoryError is and empty
<FormattedMessage
defaultMessage="Original Grade"
description="Edit Modal Original Grade header"
id="gradebook.GradesTab.EditModal.headers.originalGrade"
id="gradebook.GradesView.EditModal.headers.originalGrade"
/>
}
value={20}
@@ -41,7 +41,7 @@ exports[`ModalHeaders Component snapshots gradeOverrideHistoryError is and empty
<FormattedMessage
defaultMessage="Current Grade"
description="Edit Modal Current Grade header"
id="gradebook.GradesTab.EditModal.headers.currentGrade"
id="gradebook.GradesView.EditModal.headers.currentGrade"
/>
}
value={2}
@@ -57,7 +57,7 @@ exports[`ModalHeaders Component snapshots gradeOverrideHistoryError is empty and
<FormattedMessage
defaultMessage="Assignment"
description="Edit Modal Assignment header"
id="gradebook.GradesTab.EditModal.headers.assignment"
id="gradebook.GradesView.EditModal.headers.assignment"
/>
}
value="Qwerty"
@@ -68,7 +68,7 @@ exports[`ModalHeaders Component snapshots gradeOverrideHistoryError is empty and
<FormattedMessage
defaultMessage="Student"
description="Edit Modal Student header"
id="gradebook.GradesTab.EditModal.headers.student"
id="gradebook.GradesView.EditModal.headers.student"
/>
}
value="Uiop"
@@ -79,7 +79,7 @@ exports[`ModalHeaders Component snapshots gradeOverrideHistoryError is empty and
<FormattedMessage
defaultMessage="Original Grade"
description="Edit Modal Original Grade header"
id="gradebook.GradesTab.EditModal.headers.originalGrade"
id="gradebook.GradesView.EditModal.headers.originalGrade"
/>
}
value={20}
@@ -90,7 +90,7 @@ exports[`ModalHeaders Component snapshots gradeOverrideHistoryError is empty and
<FormattedMessage
defaultMessage="Current Grade"
description="Edit Modal Current Grade header"
id="gradebook.GradesTab.EditModal.headers.currentGrade"
id="gradebook.GradesView.EditModal.headers.currentGrade"
/>
}
value={2}

View File

@@ -16,14 +16,14 @@ exports[`EditMoal Component snapshots gradeOverrideHistoryError is and empty and
<FormattedMessage
defaultMessage="Showing most recent actions (max 5). To see more, please contact support"
description="Edit Modal visibility hint message"
id="gradebook.GradesTab.EditModal.contactSupport"
id="gradebook.GradesView.EditModal.contactSupport"
/>
</div>
<div>
<FormattedMessage
defaultMessage="Note: Once you save, your changes will be visible to students."
description="Edit Modal saved changes effect hint"
id="gradebook.GradesTab.EditModal.saveVisibility"
id="gradebook.GradesView.EditModal.saveVisibility"
/>
</div>
</div>
@@ -37,7 +37,7 @@ exports[`EditMoal Component snapshots gradeOverrideHistoryError is and empty and
<FormattedMessage
defaultMessage="Save Grades"
description="Edit Modal Save button label"
id="gradebook.GradesTab.EditModal.saveGrade"
id="gradebook.GradesView.EditModal.saveGrade"
/>
</Button>,
]
@@ -46,7 +46,7 @@ exports[`EditMoal Component snapshots gradeOverrideHistoryError is and empty and
<FormattedMessage
defaultMessage="Cancel"
description="Edit Modal close button text"
id="gradebook.GradesTab.EditModal.closeText"
id="gradebook.GradesView.EditModal.closeText"
/>
}
onClose={[MockFunction this.closeAssignmentModal]}
@@ -55,7 +55,7 @@ exports[`EditMoal Component snapshots gradeOverrideHistoryError is and empty and
<FormattedMessage
defaultMessage="Edit Grades"
description="Edit Modal title"
id="gradebook.GradesTab.EditModal.title"
id="gradebook.GradesView.EditModal.title"
/>
}
/>
@@ -77,14 +77,14 @@ exports[`EditMoal Component snapshots gradeOverrideHistoryError is empty and ope
<FormattedMessage
defaultMessage="Showing most recent actions (max 5). To see more, please contact support"
description="Edit Modal visibility hint message"
id="gradebook.GradesTab.EditModal.contactSupport"
id="gradebook.GradesView.EditModal.contactSupport"
/>
</div>
<div>
<FormattedMessage
defaultMessage="Note: Once you save, your changes will be visible to students."
description="Edit Modal saved changes effect hint"
id="gradebook.GradesTab.EditModal.saveVisibility"
id="gradebook.GradesView.EditModal.saveVisibility"
/>
</div>
</div>
@@ -98,7 +98,7 @@ exports[`EditMoal Component snapshots gradeOverrideHistoryError is empty and ope
<FormattedMessage
defaultMessage="Save Grades"
description="Edit Modal Save button label"
id="gradebook.GradesTab.EditModal.saveGrade"
id="gradebook.GradesView.EditModal.saveGrade"
/>
</Button>,
]
@@ -107,7 +107,7 @@ exports[`EditMoal Component snapshots gradeOverrideHistoryError is empty and ope
<FormattedMessage
defaultMessage="Cancel"
description="Edit Modal close button text"
id="gradebook.GradesTab.EditModal.closeText"
id="gradebook.GradesView.EditModal.closeText"
/>
}
onClose={[MockFunction this.closeAssignmentModal]}
@@ -116,7 +116,7 @@ exports[`EditMoal Component snapshots gradeOverrideHistoryError is empty and ope
<FormattedMessage
defaultMessage="Edit Grades"
description="Edit Modal title"
id="gradebook.GradesTab.EditModal.title"
id="gradebook.GradesView.EditModal.title"
/>
}
/>

View File

@@ -2,47 +2,47 @@ import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
assignmentHeader: {
id: 'gradebook.GradesTab.EditModal.headers.assignment',
id: 'gradebook.GradesView.EditModal.headers.assignment',
defaultMessage: 'Assignment',
description: 'Edit Modal Assignment header',
},
currentGradeHeader: {
id: 'gradebook.GradesTab.EditModal.headers.currentGrade',
id: 'gradebook.GradesView.EditModal.headers.currentGrade',
defaultMessage: 'Current Grade',
description: 'Edit Modal Current Grade header',
},
originalGradeHeader: {
id: 'gradebook.GradesTab.EditModal.headers.originalGrade',
id: 'gradebook.GradesView.EditModal.headers.originalGrade',
defaultMessage: 'Original Grade',
description: 'Edit Modal Original Grade header',
},
studentHeader: {
id: 'gradebook.GradesTab.EditModal.headers.student',
id: 'gradebook.GradesView.EditModal.headers.student',
defaultMessage: 'Student',
description: 'Edit Modal Student header',
},
title: {
id: 'gradebook.GradesTab.EditModal.title',
id: 'gradebook.GradesView.EditModal.title',
defaultMessage: 'Edit Grades',
description: 'Edit Modal title',
},
closeText: {
id: 'gradebook.GradesTab.EditModal.closeText',
id: 'gradebook.GradesView.EditModal.closeText',
defaultMessage: 'Cancel',
description: 'Edit Modal close button text',
},
visibility: {
id: 'gradebook.GradesTab.EditModal.contactSupport',
id: 'gradebook.GradesView.EditModal.contactSupport',
defaultMessage: 'Showing most recent actions (max 5). To see more, please contact support',
description: 'Edit Modal visibility hint message',
},
saveVisibility: {
id: 'gradebook.GradesTab.EditModal.saveVisibility',
id: 'gradebook.GradesView.EditModal.saveVisibility',
defaultMessage: 'Note: Once you save, your changes will be visible to students.',
description: 'Edit Modal saved changes effect hint',
},
saveGrade: {
id: 'gradebook.GradesTab.EditModal.saveGrade',
id: 'gradebook.GradesView.EditModal.saveGrade',
defaultMessage: 'Save Grades',
description: 'Edit Modal Save button label',
},

View File

@@ -0,0 +1,37 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Button, Icon } from '@edx/paragon';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import thunkActions from 'data/thunkActions';
import messages from './FilterMenuToggle.messages';
/**
* Controls for filtering the GradebookTable. Contains the "Edit Filters" button for opening the filter drawer
* as well as the search box for searching by username/email.
*/
export const FilterMenuToggle = ({ toggleFilterDrawer }) => (
<Button
id="edit-filters-btn"
className="btn-primary align-self-start"
onClick={toggleFilterDrawer}
>
<Icon className="fa fa-filter" /> <FormattedMessage {...messages.editFilters} />
</Button>
);
FilterMenuToggle.propTypes = {
// From Redux
toggleFilterDrawer: PropTypes.func.isRequired,
};
export const mapStateToProps = () => ({});
export const mapDispatchToProps = {
toggleFilterDrawer: thunkActions.app.filterMenu.toggle,
};
export default connect(mapStateToProps, mapDispatchToProps)(FilterMenuToggle);

View File

@@ -0,0 +1,11 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
editFilters: {
id: 'gradebook.GradesView.editFilterLabel',
defaultMessage: 'Edit Filters',
description: 'Button text on Grades tab to open/close the Filters tab',
},
});
export default messages;

View File

@@ -0,0 +1,42 @@
import React from 'react';
import { shallow } from 'enzyme';
import thunkActions from 'data/thunkActions';
import { FilterMenuToggle, mapDispatchToProps, mapStateToProps } from './FilterMenuToggle';
jest.mock('@edx/paragon', () => ({
Button: () => 'Button',
Icon: () => 'Icon',
}));
jest.mock('data/thunkActions', () => ({
__esModule: true,
default: {
app: {
filterMenu: { toggle: jest.fn() },
},
},
}));
describe('FilterMenuToggle component', () => {
describe('snapshots', () => {
test('basic snapshot', () => {
const toggleFilterDrawer = jest.fn().mockName('this.props.toggleFilterDrawer');
expect(shallow((
<FilterMenuToggle {...{ toggleFilterDrawer }} />
))).toMatchSnapshot();
});
});
describe('mapStateToProps', () => {
test('does not connect any selectors', () => {
expect(mapStateToProps({ test: 'state' })).toEqual({});
});
});
describe('mapDispatchToProps', () => {
test('toggleFilterDrawer from thunkActions.app.filterMenu.toggle', () => {
expect(mapDispatchToProps.toggleFilterDrawer).toEqual(
thunkActions.app.filterMenu.toggle,
);
});
});
});

View File

@@ -0,0 +1,44 @@
/* eslint-disable react/sort-comp, react/button-has-type, import/no-named-as-default */
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import selectors from 'data/selectors';
/**
* <FilteredUsersLabel />
* Simple label component displaying the filtered and total users shown
*/
export const FilteredUsersLabel = ({
filteredUsersCount,
totalUsersCount,
}) => {
if (!totalUsersCount) {
return null;
}
const bold = (val) => (<span className="font-weight-bold">{val}</span>);
return (
<FormattedMessage
id="gradebook.GradesTab.usersVisibilityLabel'"
defaultMessage="Showing {filteredUsers} of {totalUsers} total learners"
description="Users visibility label"
values={{
filteredUsers: bold(filteredUsersCount),
totalUsers: bold(totalUsersCount),
}}
/>
);
};
FilteredUsersLabel.propTypes = {
filteredUsersCount: PropTypes.number.isRequired,
totalUsersCount: PropTypes.number.isRequired,
};
export const mapStateToProps = (state) => ({
totalUsersCount: selectors.grades.totalUsersCount(state),
filteredUsersCount: selectors.grades.filteredUsersCount(state),
});
export default connect(mapStateToProps)(FilteredUsersLabel);

View File

@@ -0,0 +1,46 @@
import React from 'react';
import { shallow } from 'enzyme';
import selectors from 'data/selectors';
import { FilteredUsersLabel, mapStateToProps } from './FilteredUsersLabel';
jest.mock('@edx/paragon', () => ({
Icon: () => 'Icon',
}));
jest.mock('data/selectors', () => ({
__esModule: true,
default: {
grades: {
filteredUsersCount: state => ({ filteredUsersCount: state }),
totalUsersCount: state => ({ totalUsersCount: state }),
},
},
}));
describe('FilteredUsersLabel', () => {
describe('component', () => {
const props = {
filteredUsersCount: 23,
totalUsersCount: 140,
};
it('does not render if totalUsersCount is falsey', () => {
expect(shallow(<FilteredUsersLabel {...props} totalUsersCount={0} />)).toEqual({});
});
test('snapshot - displays label with number of filtered users out of total', () => {
expect(shallow(<FilteredUsersLabel {...props} />)).toMatchSnapshot();
});
});
describe('mapStateToProps', () => {
const testState = { a: 'nice', day: 'for', some: 'rain' };
let mapped;
beforeEach(() => {
mapped = mapStateToProps(testState);
});
test('filteredUsersCount from grades.filteredUsersCount', () => {
expect(mapped.filteredUsersCount).toEqual(selectors.grades.filteredUsersCount(testState));
});
test('totalUsersCount from grades.totalUsersCount', () => {
expect(mapped.totalUsersCount).toEqual(selectors.grades.totalUsersCount(testState));
});
});
});

View File

@@ -7,7 +7,7 @@ exports[`LabelReplacements TotalGradeLabelReplacement displays overlay tooltip 1
<FormattedMessage
defaultMessage="Total Grade values are always displayed as a percentage"
description="Gradebook table message that total grades are displayed in percent format"
id="gradebook.GradesTab.table.totalGradePercentage"
id="gradebook.GradesView.table.totalGradePercentage"
/>
</Tooltip>
`;
@@ -23,7 +23,7 @@ exports[`LabelReplacements TotalGradeLabelReplacement snapshot 1`] = `
<FormattedMessage
defaultMessage="Total Grade values are always displayed as a percentage"
description="Gradebook table message that total grades are displayed in percent format"
id="gradebook.GradesTab.table.totalGradePercentage"
id="gradebook.GradesView.table.totalGradePercentage"
/>
</Tooltip>
}
@@ -39,7 +39,7 @@ exports[`LabelReplacements TotalGradeLabelReplacement snapshot 1`] = `
<FormattedMessage
defaultMessage="Total Grade (%)"
description="Gradebook table total grade column header"
id="gradebook.GradesTab.table.headings.totalGrade"
id="gradebook.GradesView.table.headings.totalGrade"
/>
<div
id="courseGradeTooltipIcon"
@@ -50,7 +50,7 @@ exports[`LabelReplacements TotalGradeLabelReplacement snapshot 1`] = `
<FormattedMessage
defaultMessage="Total Grade values are always displayed as a percentage"
description="Gradebook table message that total grades are displayed in percent format"
id="gradebook.GradesTab.table.totalGradePercentage"
id="gradebook.GradesView.table.totalGradePercentage"
/>
}
/>
@@ -66,7 +66,7 @@ exports[`LabelReplacements UsernameLabelReplacement snapshot 1`] = `
<FormattedMessage
defaultMessage="Username"
description="Gradebook table username column header"
id="gradebook.GradesTab.table.headings.username"
id="gradebook.GradesView.table.headings.username"
/>
</div>
<div
@@ -75,7 +75,7 @@ exports[`LabelReplacements UsernameLabelReplacement snapshot 1`] = `
<FormattedMessage
defaultMessage="Student Key*"
description="Gradebook table Student Key label"
id="gradebook.GradesTab.table.labels.studentKey"
id="gradebook.GradesView.table.labels.studentKey"
/>
</div>
</div>

View File

@@ -19,7 +19,7 @@ exports[`GradebookTable component snapshot - fields1 and 2 between email and tot
"label": <FormattedMessage
defaultMessage="Email"
description="Gradebook table email column header"
id="gradebook.GradesTab.table.headings.email"
id="gradebook.GradesView.table.headings.email"
/>,
},
Object {

View File

@@ -2,32 +2,32 @@ import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
emailHeading: {
id: 'gradebook.GradesTab.table.headings.email',
id: 'gradebook.GradesView.table.headings.email',
defaultMessage: 'Email',
description: 'Gradebook table email column header',
},
totalGradeHeading: {
id: 'gradebook.GradesTab.table.headings.totalGrade',
id: 'gradebook.GradesView.table.headings.totalGrade',
defaultMessage: 'Total Grade (%)',
description: 'Gradebook table total grade column header',
},
usernameHeading: {
id: 'gradebook.GradesTab.table.headings.username',
id: 'gradebook.GradesView.table.headings.username',
defaultMessage: 'Username',
description: 'Gradebook table username column header',
},
studentKeyLabel: {
id: 'gradebook.GradesTab.table.labels.studentKey',
id: 'gradebook.GradesView.table.labels.studentKey',
defaultMessage: 'Student Key*',
description: 'Gradebook table Student Key label',
},
usernameLabel: {
id: 'gradebook.GradesTab.table.labels.username',
id: 'gradebook.GradesView.table.labels.username',
defaultMessage: 'Username',
description: 'Gradebook table username label',
},
totalGradePercentage: {
id: 'gradebook.GradesTab.table.totalGradePercentage',
id: 'gradebook.GradesView.table.totalGradePercentage',
defaultMessage: 'Total Grade values are always displayed as a percentage',
description: 'Gradebook table message that total grades are displayed in percent format',
},

View File

@@ -26,7 +26,16 @@
}
h4 {
font-weight: bold;
margin-top: 2rem;
margin-top: 0rem;
}
.import-grades-btn {
margin-left: 20px;
}
.intervention-report-description: {
margin-right: 40px;
}
h4.step-message-1 {
margin-top: 40px;
}
}

View File

@@ -6,7 +6,6 @@ import { connect } from 'react-redux';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import {
Button,
Form,
FormControl,
FormGroup,
@@ -14,14 +13,15 @@ import {
import selectors from 'data/selectors';
import thunkActions from 'data/thunkActions';
import messages from './messages';
import NetworkButton from 'components/NetworkButton';
import messages from './ImportGradesButton.messages';
/**
* <FileUploadForm />
* <ImportGradesButton />
* File-type input wrapped with hidden control such that when a valid file is
* added, it is automattically uploaded.
*/
export class FileUploadForm extends React.Component {
export class ImportGradesButton extends React.Component {
constructor(props) {
super(props);
this.fileInputRef = React.createRef();
@@ -49,7 +49,7 @@ export class FileUploadForm extends React.Component {
handleFileInputChange() {
return this.hasFile && (
this.props.submitFileUploadFormData(this.formData).then(
this.props.submitImportGradesButtonData(this.formData).then(
() => { this.fileInput.value = null; },
)
);
@@ -71,17 +71,20 @@ export class FileUploadForm extends React.Component {
</FormGroup>
</Form>
<Button variant="primary" onClick={this.handleClickImportGrades}>
<FormattedMessage {...messages.importBtnText} />
</Button>
<NetworkButton
className="import-grades-btn"
label={messages.importGradesBtnText}
onClick={this.handleClickImportGrades}
import
/>
</>
);
}
}
FileUploadForm.propTypes = {
ImportGradesButton.propTypes = {
// redux
gradeExportUrl: PropTypes.string.isRequired,
submitFileUploadFormData: PropTypes.func.isRequired,
submitImportGradesButtonData: PropTypes.func.isRequired,
};
export const mapStateToProps = (state) => ({
@@ -89,7 +92,7 @@ export const mapStateToProps = (state) => ({
});
export const mapDispatchToProps = {
submitFileUploadFormData: thunkActions.grades.submitFileUploadFormData,
submitImportGradesButtonData: thunkActions.grades.submitImportGradesButtonData,
};
export default connect(mapStateToProps, mapDispatchToProps)(FileUploadForm);
export default connect(mapStateToProps, mapDispatchToProps)(ImportGradesButton);

View File

@@ -0,0 +1,16 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
csvUploadLabel: {
id: 'gradebook.BulkManagementHistoryView.csvUploadLabel',
defaultMessage: 'Upload Grade CSV',
description: 'Button in BulkManagementHistoryView Alerts',
},
importGradesBtnText: {
id: 'gradebook.GradesView.importGradesBtnText',
defaultMessage: 'Import Grades',
description: 'Button in BulkManagement Tab File Upload Form',
},
});
export default messages;

View File

@@ -3,23 +3,24 @@ import React from 'react';
import { shallow } from 'enzyme';
import TestRenderer from 'react-test-renderer';
import {
Button,
Form,
FormControl,
FormGroup,
} from '@edx/paragon';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import NetworkButton from 'components/NetworkButton';
import selectors from 'data/selectors';
import thunkActions from 'data/thunkActions';
import { FileUploadForm, mapStateToProps, mapDispatchToProps } from './FileUploadForm';
import { ImportGradesButton, mapStateToProps, mapDispatchToProps } from './ImportGradesButton';
import messages from './messages';
import messages from './ImportGradesButton.messages';
jest.mock('@edx/frontend-platform/i18n', () => ({
defineMessages: m => m,
FormattedMessage: () => 'FormattedMessage',
}));
jest.mock('components/NetworkButton', () => 'NetworkButton');
jest.mock('data/selectors', () => ({
__esModule: true,
default: {
@@ -34,16 +35,14 @@ jest.mock('data/selectors', () => ({
jest.mock('data/thunkActions', () => ({
__esModule: true,
default: {
grades: { submitFileUploadFormData: jest.fn() },
grades: { submitImportGradesButtonData: jest.fn() },
},
}));
jest.mock('./BulkManagementAlerts', () => 'BulkManagementAlerts');
jest.mock('./ResultsSummary', () => 'ResultsSummary');
const mockRef = { click: jest.fn(), files: [] };
describe('FileUploadForm', () => {
describe('ImportGradesButton', () => {
beforeEach(() => {
mockRef.click.mockClear();
});
@@ -54,7 +53,7 @@ describe('FileUploadForm', () => {
beforeEach(() => {
props = {
gradeExportUrl: 'fakeUrl',
submitFileUploadFormData: jest.fn(),
submitImportGradesButtonData: jest.fn(),
};
});
describe('snapshot', () => {
@@ -64,12 +63,11 @@ describe('FileUploadForm', () => {
];
test(`snapshot - loads ${snapshotSegments.join(', ')}`, () => {
jest.mock('@edx/paragon', () => ({
Button: () => 'Button',
Form: () => 'Form',
FormControl: () => 'FormControl',
FormGroup: () => 'FormGroup',
}));
el = shallow(<FileUploadForm {...props} />);
el = shallow(<ImportGradesButton {...props} />);
el.instance().handleFileInputChange = jest.fn().mockName('this.handleFileInputChange');
el.instance().fileInputRef = jest.fn().mockName('this.fileInputRef');
el.instance().handleClickImportGrades = jest.fn().mockName('this.handleClickImportGrades');
@@ -80,7 +78,7 @@ describe('FileUploadForm', () => {
describe('render', () => {
beforeEach(() => {
el = TestRenderer.create(
<FileUploadForm {...props} />,
<ImportGradesButton {...props} />,
{ createNodeMock: () => mockRef },
);
inst = el.root;
@@ -119,21 +117,21 @@ describe('FileUploadForm', () => {
describe('import button', () => {
let btn;
beforeEach(() => {
btn = inst.findByType(Button);
btn = inst.findByType(NetworkButton);
});
test('handleClickImportGrade on click', () => {
expect(btn.props.onClick).toEqual(el.getInstance().handleClickImportGrades);
});
test('text from messages.importBtn', () => {
const messageEl = btn.findByType(FormattedMessage);
expect(messageEl.props).toEqual(messages.importBtnText);
test('label from messages.importGradesBtnText and import true', () => {
expect(btn.props.label).toEqual(messages.importGradesBtnText);
expect(btn.props.import).toEqual(true);
});
});
});
describe('fileInput helper', () => {
test('links to fileInputRef.current', () => {
el = TestRenderer.create(
<FileUploadForm {...props} />,
<ImportGradesButton {...props} />,
{ createNodeMock: () => mockRef },
);
expect(el.getInstance().fileInput).not.toEqual(undefined);
@@ -144,7 +142,7 @@ describe('FileUploadForm', () => {
let fileInput;
beforeEach(() => {
el = TestRenderer.create(
<FileUploadForm {...props} />,
<ImportGradesButton {...props} />,
{ createNodeMock: () => mockRef },
);
fileInput = jest.spyOn(el.getInstance(), 'fileInput', 'get');
@@ -164,17 +162,17 @@ describe('FileUploadForm', () => {
it('does nothing if file input has not loaded with files', () => {
fileInput.mockReturnValue(null);
el.getInstance().handleFileInputChange();
expect(props.submitFileUploadFormData).not.toHaveBeenCalled();
expect(props.submitImportGradesButtonData).not.toHaveBeenCalled();
fileInput.mockReturnValue({ files: [] });
el.getInstance().handleFileInputChange();
expect(props.submitFileUploadFormData).not.toHaveBeenCalled();
expect(props.submitImportGradesButtonData).not.toHaveBeenCalled();
});
it('calls submitFileUploadFormData and then clears fileInput if has files', () => {
it('calls submitImportGradesButtonData and then clears fileInput if has files', () => {
fileInput.mockReturnValue({ files: ['some', 'files'], value: 'a value' });
const formData = { fake: 'form data' };
jest.spyOn(el.getInstance(), 'formData', 'get').mockReturnValue(formData);
const submit = jest.fn(() => ({ then: (thenCB) => { thenCB(); } }));
el.update(<FileUploadForm {...props} submitFileUploadFormData={submit} />);
el.update(<ImportGradesButton {...props} submitImportGradesButtonData={submit} />);
el.getInstance().handleFileInputChange();
expect(submit).toHaveBeenCalledWith(formData);
expect(el.getInstance().fileInput.value).toEqual(null);
@@ -205,10 +203,10 @@ describe('FileUploadForm', () => {
});
describe('mapDispatchToProps', () => {
test('submitFileUploadFormData from thunkActions.grades', () => {
test('submitImportGradesButtonData from thunkActions.grades', () => {
expect(
mapDispatchToProps.submitFileUploadFormData,
).toEqual(thunkActions.grades.submitFileUploadFormData);
mapDispatchToProps.submitImportGradesButtonData,
).toEqual(thunkActions.grades.submitImportGradesButtonData);
});
});
});

View File

@@ -0,0 +1,72 @@
/* eslint-disable react/sort-comp, react/button-has-type */
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Toast } from '@edx/paragon';
import {
injectIntl,
intlShape,
} from '@edx/frontend-platform/i18n';
import selectors from 'data/selectors';
import actions from 'data/actions';
import { views } from 'data/constants/app';
import messages from './ImportSuccessToast.messages';
/**
* <ImportSuccessToast />
* Toast component triggered by successful grade upload.
* Provides a link to view the Bulk Management History tab.
*/
export class ImportSuccessToast extends React.Component {
constructor(props) {
super(props);
this.onClose = this.onClose.bind(this);
this.handleShowHistoryView = this.handleShowHistoryView.bind(this);
}
onClose() {
this.props.setShow(false);
}
handleShowHistoryView() {
this.props.setAppView(views.bulkManagementHistory);
this.onClose();
}
render() {
return (
<Toast
action={{
label: this.props.intl.formatMessage(messages.showHistoryViewBtn),
onClick: this.handleShowHistoryView,
}}
onClose={this.onClose}
show={this.props.show}
>
{this.props.intl.formatMessage(messages.description)}
</Toast>
);
}
}
ImportSuccessToast.propTypes = {
// injected
intl: intlShape.isRequired,
// redux
show: PropTypes.bool.isRequired,
setAppView: PropTypes.func.isRequired,
setShow: PropTypes.func.isRequired,
};
export const mapStateToProps = (state) => ({
show: selectors.app.showImportSuccessToast(state),
});
export const mapDispatchToProps = {
setAppView: actions.app.setView,
setShow: actions.app.setShowImportSuccessToast,
};
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ImportSuccessToast));

View File

@@ -0,0 +1,16 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
description: {
id: 'gradebook.GradesView.ImportSuccessToast.description',
defaultMessage: 'Import Successful! Grades will be updated momentarily.',
description: 'Import Success Toast description',
},
showHistoryViewBtn: {
id: 'gradebook.GradesView.ImportSuccessToast.showHistoryViewBtn',
defaultMessage: 'View Activity Log',
description: 'Button text for action that loads Bulk Management Activity Log view',
},
});
export default messages;

View File

@@ -0,0 +1,110 @@
import React from 'react';
import { shallow } from 'enzyme';
import selectors from 'data/selectors';
import actions from 'data/actions';
import { views } from 'data/constants/app';
import {
ImportSuccessToast,
mapStateToProps,
mapDispatchToProps,
} from './ImportSuccessToast';
import messages from './ImportSuccessToast.messages';
jest.mock('@edx/paragon', () => ({
Toast: () => 'Toast',
}));
jest.mock('data/selectors', () => ({
__esModule: true,
default: {
app: {
showImportSuccessToast: (state) => ({ showImportSuccessToast: state }),
},
},
}));
jest.mock('data/actions', () => ({
__esModule: true,
default: {
app: {
setView: jest.fn(),
setShow: jest.fn(),
},
},
}));
describe('ImportSuccessToast component', () => {
describe('snapshots', () => {
let el;
let props = {
show: true,
};
beforeEach(() => {
props = {
...props,
intl: { formatMessage: (msg) => msg.defaultMessage },
setAppView: jest.fn(),
setShow: jest.fn(),
};
el = shallow(<ImportSuccessToast {...props} />);
});
test('snapshot', () => {
el.instance().handleShowHistoryView = jest.fn().mockName('handleShowHistoryView');
el.instance().onClose = jest.fn().mockName('onClose');
expect(el).toMatchSnapshot();
});
describe('Toast props', () => {
let toastProps;
beforeEach(() => {
toastProps = el.props();
});
test('action has translated label and onClick from this.handleShowHistoryView', () => {
expect(toastProps.action).toEqual({
label: props.intl.formatMessage(messages.showHistoryViewBtn),
onClick: el.instance().handleShowHistoryView,
});
});
test('onClose from this.onClose method', () => {
expect(toastProps.onClose).toEqual(el.instance().onClose);
});
test('show from show prop', () => {
expect(toastProps.show).toEqual(props.show);
el.setProps({ show: false });
expect(el.props().show).toEqual(false);
});
});
describe('onClose', () => {
it('calls props.setShow(false)', () => {
el.instance().onClose();
expect(props.setShow).toHaveBeenCalledWith(false);
});
});
describe('handleShowHistoryView', () => {
it('calls setAppView with views.bulkManagementHistory and this.onClose', () => {
el.instance().onClose = jest.fn();
el.instance().handleShowHistoryView();
expect(props.setAppView).toHaveBeenCalledWith(views.bulkManagementHistory);
expect(el.instance().onClose).toHaveBeenCalled();
});
});
});
describe('behavior', () => {
});
describe('mapStateToProps', () => {
const testState = { somewhere: 'over', the: 'rainbow' };
const mapped = mapStateToProps(testState);
test('show from app showImportSuccessToast selector', () => {
expect(mapped.show).toEqual(
selectors.app.showImportSuccessToast(testState),
);
});
});
describe('mapDispatchToProps', () => {
test('setAppView from actions.app.setView', () => {
expect(mapDispatchToProps.setAppView).toEqual(actions.app.setView);
});
test('setShow from actions.setShowImportSuccessToast', () => {
expect(mapDispatchToProps.setShow).toEqual(actions.app.setShowImportSuccessToast);
});
});
});

View File

@@ -0,0 +1,72 @@
/* eslint-disable react/sort-comp, react/button-has-type */
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import actions from 'data/actions';
import selectors from 'data/selectors';
import NetworkButton from 'components/NetworkButton';
import messages from './InterventionsReport.messages';
/**
* <InterventionsReport />
* Provides download buttons for Bulk Management and Intervention reports, only if
* showBulkManagement is set in redus.
*/
export class InterventionsReport extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.downloadInterventionReport();
window.location.assign(this.props.interventionExportUrl);
}
render() {
return this.props.showBulkManagement && (
<div>
<h4 className="mt-0">
<FormattedMessage {...messages.title} />
</h4>
<div
className="d-flex justify-content-between align-items-center"
>
<div className="intervention-report-description">
<FormattedMessage {...messages.description} />
</div>
<NetworkButton
label={messages.downloadBtn}
onClick={this.handleClick}
/>
</div>
</div>
);
}
}
InterventionsReport.defaultProps = {
showBulkManagement: false,
};
InterventionsReport.propTypes = {
// redux
downloadInterventionReport: PropTypes.func.isRequired,
interventionExportUrl: PropTypes.string.isRequired,
showBulkManagement: PropTypes.bool,
};
export const mapStateToProps = (state) => ({
interventionExportUrl: selectors.root.interventionExportUrl(state),
showBulkManagement: selectors.root.showBulkManagement(state),
});
export const mapDispatchToProps = {
downloadInterventionReport: actions.grades.downloadReport.intervention,
};
export default connect(mapStateToProps, mapDispatchToProps)(InterventionsReport);

View File

@@ -0,0 +1,21 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
title: {
id: 'gradebook.GradesView.InterventionsReport.title',
defaultMessage: 'Interventions Report',
description: 'Intervention report subsection label',
},
description: {
id: 'gradebook.GradesView.InterventionsReport.description',
defaultMessage: 'Need to find students who may be falling behind? Download the interventions report to obtain engagement metrics such as section attempts and visits.',
description: 'Intervention report subsection description',
},
downloadBtn: {
id: 'gradebook.GradesView.InterventionsReport.downloadBtn',
defaultMessage: 'Download Interventions',
description: 'Button text for intervention report download control in GradesView',
},
});
export default messages;

View File

@@ -0,0 +1,107 @@
import React from 'react';
import { shallow } from 'enzyme';
import selectors from 'data/selectors';
import actions from 'data/actions';
import {
InterventionsReport,
mapStateToProps,
mapDispatchToProps,
} from './InterventionsReport';
jest.mock('@edx/paragon', () => ({
Toast: () => 'Toast',
}));
jest.mock('components/NetworkButton', () => 'NetworkButton');
jest.mock('data/selectors', () => ({
__esModule: true,
default: {
root: {
interventionExportUrl: (state) => ({ interventionExportUrl: state }),
showBulkManagement: (state) => ({ showBulkManagement: state }),
},
},
}));
jest.mock('data/actions', () => ({
__esModule: true,
default: {
grades: {
downloadReport: { intervention: jest.fn() },
},
},
}));
describe('InterventionsReport component', () => {
let el;
let props = {
interventionExportUrl: 'url.for.exporting.interventions',
showBulkManagement: true,
};
let location;
beforeAll(() => {
location = window.location;
});
beforeEach(() => {
delete window.location;
window.location = Object.defineProperties(
{},
{
...Object.getOwnPropertyDescriptors(location),
assign: { configurable: true, value: jest.fn() },
},
);
props = {
...props,
downloadInterventionReport: jest.fn(),
};
});
afterAll(() => {
window.location = location;
});
describe('snapshots', () => {
beforeEach(() => {
el = shallow(<InterventionsReport {...props} />);
});
test('snapshot', () => {
el.instance().handleClick = jest.fn().mockName('handleClick');
expect(el.instance().render()).toMatchSnapshot();
});
test('returns empty if props.showBulkManagement is false', () => {
el.setProps({ showBulkManagement: false });
expect(el.instance().render()).toEqual(false);
});
});
describe('behavior', () => {
beforeEach(() => {
el = shallow(<InterventionsReport {...props} />);
});
describe('handleClick', () => {
it('calls props.downloadInterventionReport and navigates to props.interventionExportUrl', () => {
el.instance().handleClick();
expect(props.downloadInterventionReport).toHaveBeenCalled();
});
});
});
describe('mapStateToProps', () => {
const testState = { somewhere: 'over', the: 'rainbow' };
const mapped = mapStateToProps(testState);
test('interventionExportUrl from root interventionExportUrl selector', () => {
expect(mapped.interventionExportUrl).toEqual(
selectors.root.interventionExportUrl(testState),
);
});
test('showBulkManagement from root showBulkManagement selector', () => {
expect(mapped.showBulkManagement).toEqual(
selectors.root.showBulkManagement(testState),
);
});
});
describe('mapDispatchToProps', () => {
test('downloadInterventionReport from actions.grades.downloadReport.intervention', () => {
expect(mapDispatchToProps.downloadInterventionReport).toEqual(
actions.grades.downloadReport.intervention,
);
});
});
});

View File

@@ -22,7 +22,7 @@ exports[`PageButtons component snapshots buttons enabled with both endpoints pro
<FormattedMessage
defaultMessage="Previous Page"
description="Grades tab Previous Page button text"
id="gradebook.GradesTab.PageButtons.prevPage"
id="gradebook.GradesView.PageButtons.prevPage"
/>
</Button>
<Button
@@ -38,7 +38,7 @@ exports[`PageButtons component snapshots buttons enabled with both endpoints pro
<FormattedMessage
defaultMessage="Next Page"
description="Grades tab Next Page button text"
id="gradebook.GradesTab.PageButtons.nextPage"
id="gradebook.GradesView.PageButtons.nextPage"
/>
</Button>
</div>
@@ -66,7 +66,7 @@ exports[`PageButtons component snapshots nextPage disabled if not provided 1`] =
<FormattedMessage
defaultMessage="Previous Page"
description="Grades tab Previous Page button text"
id="gradebook.GradesTab.PageButtons.prevPage"
id="gradebook.GradesView.PageButtons.prevPage"
/>
</Button>
<Button
@@ -82,7 +82,7 @@ exports[`PageButtons component snapshots nextPage disabled if not provided 1`] =
<FormattedMessage
defaultMessage="Next Page"
description="Grades tab Next Page button text"
id="gradebook.GradesTab.PageButtons.nextPage"
id="gradebook.GradesView.PageButtons.nextPage"
/>
</Button>
</div>
@@ -110,7 +110,7 @@ exports[`PageButtons component snapshots prevPage disabled if not provided 1`] =
<FormattedMessage
defaultMessage="Previous Page"
description="Grades tab Previous Page button text"
id="gradebook.GradesTab.PageButtons.prevPage"
id="gradebook.GradesView.PageButtons.prevPage"
/>
</Button>
<Button
@@ -126,7 +126,7 @@ exports[`PageButtons component snapshots prevPage disabled if not provided 1`] =
<FormattedMessage
defaultMessage="Next Page"
description="Grades tab Next Page button text"
id="gradebook.GradesTab.PageButtons.nextPage"
id="gradebook.GradesView.PageButtons.nextPage"
/>
</Button>
</div>

Some files were not shown because too many files have changed in this diff Show More