From 8771b1f9baef41aa2d62eabcd8124a1cd742417b Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Thu, 18 Oct 2018 14:38:06 +0200
Subject: [PATCH 01/21] better error messages when creating feedback

---
 frontend/src/store/modules/submission-notes.ts | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts
index 4dfd9d29..7d5ba093 100644
--- a/frontend/src/store/modules/submission-notes.ts
+++ b/frontend/src/store/modules/submission-notes.ts
@@ -141,14 +141,17 @@ async function submitFeedback ({state}: BareActionContext<SubmissionNotesState,
     isFinal: isFinal,
     ofSubmission: state.submission.pk
   }
-  if (Object.keys(state.updatedFeedback.feedbackLines || {}).length > 0) {
-    feedback.feedbackLines = state.updatedFeedback.feedbackLines
-  }
   if (state.origFeedback.score === undefined && state.updatedFeedback.score === undefined) {
     throw new Error('You need to give a score.')
-  } else if (state.updatedFeedback.score !== undefined) {
-    feedback.score = state.updatedFeedback.score
+  } else {
+    feedback.score = state.updatedFeedback.score || state.origFeedback.score || 0
+  }
+  if (Object.keys(state.updatedFeedback.feedbackLines || {}).length > 0) {
+    feedback.feedbackLines = state.updatedFeedback.feedbackLines
+  } else if (feedback.score! < SubmissionNotes.submissionType.fullScore!) {
+    throw new Error('You need to provide a reason for a reduced score.')
   }
+  // delete those comments that have been marked for deletion
   await SubmissionNotes.deleteComments()
   if (!state.hasOrigFeedback) {
     return api.submitFeedbackForAssignment({feedback})
-- 
GitLab


From 7509a80803999aa845b1fb060e1fd471ab196b7f Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Tue, 23 Oct 2018 19:59:59 +0200
Subject: [PATCH 02/21] Added factory-boy dependency and started writing more
 e2e test

---
 .../src/components/CorrectionStatistics.vue   |  2 +-
 functional_tests/test_front_pages.py          | 47 +++++++++
 functional_tests/util.py                      | 23 +++++
 requirements.dev.txt                          |  2 +
 util/factory_boys.py                          | 99 +++++++++++++++++++
 5 files changed, 172 insertions(+), 1 deletion(-)
 create mode 100644 functional_tests/test_front_pages.py
 create mode 100644 util/factory_boys.py

diff --git a/frontend/src/components/CorrectionStatistics.vue b/frontend/src/components/CorrectionStatistics.vue
index ecd499f2..59a31183 100644
--- a/frontend/src/components/CorrectionStatistics.vue
+++ b/frontend/src/components/CorrectionStatistics.vue
@@ -1,5 +1,5 @@
 <template>
-    <v-card class="py-2">
+    <v-card class="py-2" id="correction-statistics">
       <v-card-title>
         <span class="title">Statistics</span>
       </v-card-title>
diff --git a/functional_tests/test_front_pages.py b/functional_tests/test_front_pages.py
new file mode 100644
index 00000000..cb8f987b
--- /dev/null
+++ b/functional_tests/test_front_pages.py
@@ -0,0 +1,47 @@
+import os
+import time
+
+from django.test import LiveServerTestCase
+from selenium import webdriver
+from selenium.webdriver.common.keys import Keys
+from selenium.webdriver.firefox.options import Options
+
+from core.models import UserAccount
+from functional_tests.util import get_frontend_url, login, create_browser
+from util.factories import make_test_data
+from util import factory_boys as fact
+
+LiveServerTestCase.port = int(os.environ.get('LIVE_SERVER_PORT', 0))
+
+
+class FrontPageTestsTutorReviewer:
+    def _login(self):
+        login(self.browser, self.live_server_url, self.username, self.password)
+
+    def test_statistics_are_shown(self):
+        self._login()
+        statistics = self.browser.find_element_by_id('correction-statistics')
+
+
+class FrontPageTestsTutor(LiveServerTestCase, FrontPageTestsTutorReviewer):
+    def setUp(self):
+        self.live_server_url = get_frontend_url(self.live_server_url)
+        self.browser = create_browser()
+        self.username = 'tutor'
+        self.password = 'p'
+        fact.UserAccountFactory(username=self.username, password=self.password)
+
+    def tearDown(self):
+        self.browser.quit()
+
+
+class FrontPageTestsReviewer(LiveServerTestCase, FrontPageTestsTutorReviewer):
+    def setUp(self):
+        self.live_server_url = get_frontend_url(self.live_server_url)
+        self.browser = create_browser()
+        self.username = 'reviewer'
+        self.password = 'p'
+        fact.UserAccountFactory(username=self.username, password=self.password)
+
+    def tearDown(self):
+        self.browser.quit()
diff --git a/functional_tests/util.py b/functional_tests/util.py
index 2e67d9f0..178ddd25 100644
--- a/functional_tests/util.py
+++ b/functional_tests/util.py
@@ -1,5 +1,28 @@
 import os
+import time
+
+from selenium import webdriver
+from selenium.webdriver.common.keys import Keys
+from selenium.webdriver.firefox.options import Options
 
 
 def get_frontend_url(live_server_url):
     return os.environ.get('FRONTEND_URL', live_server_url)
+
+
+def create_browser():
+    options = Options()
+    options.headless = bool(os.environ.get('HEADLESS_TESTS', False))
+    browser = webdriver.Firefox(options=options)
+    browser.implicitly_wait(5)
+    return browser
+
+
+def login(browser, live_server_url, username, password='p'):
+    browser.get(live_server_url)
+    username_input = browser.find_element_by_xpath('//input[@aria-label="Username"]')
+    username_input.send_keys(username)
+    password_input = browser.find_element_by_xpath('//input[@aria-label="Password"]')
+    password_input.send_keys(password)
+    browser.find_element_by_xpath('//button[@type="submit"]').send_keys(Keys.ENTER)
+    time.sleep(1)
diff --git a/requirements.dev.txt b/requirements.dev.txt
index c4009fe1..27871cf0 100644
--- a/requirements.dev.txt
+++ b/requirements.dev.txt
@@ -3,3 +3,5 @@ pre-commit~=1.4.1
 pytest-cov~=2.5.1
 pytest-django~=3.1.2
 selenium~=3.14.1
+factory-boy~=2.11.1
+Faker~=0.9.2
diff --git a/util/factory_boys.py b/util/factory_boys.py
new file mode 100644
index 00000000..38d21ed3
--- /dev/null
+++ b/util/factory_boys.py
@@ -0,0 +1,99 @@
+import factory
+from factory.django import DjangoModelFactory
+from faker import Faker
+from core import models
+
+fake = Faker()
+fake.seed(42)
+
+
+class ExamTypeFactory(DjangoModelFactory):
+    class Meta:
+        model = models.ExamType
+    module_reference = 'B.Inf.4242 Test Module'
+    total_score = 90
+    pass_score = 45
+    pass_only = False
+
+
+class SubmissionTypeFactory(DjangoModelFactory):
+    class Meta:
+        model = models.SubmissionType
+    name = factory.Sequence(lambda n: f"[${n}] Example submission type ")
+    full_score = 15
+    description = '<h1>This</h1> is a <b>description</b> containing html'
+    solution = 'This usually contains commented code'
+    programming_language = models.SubmissionType.C
+
+
+class UserAccountFactory(DjangoModelFactory):
+    class Meta:
+        model = models.UserAccount
+
+    role = models.UserAccount.REVIEWER
+    fullname = fake.name()
+    username = fake.user_name()
+    password = factory.PostGenerationMethodCall('set_password', 'p')
+
+
+class StudentInfoFactory(DjangoModelFactory):
+    class Meta:
+        model = models.StudentInfo
+
+    exam = factory.SubFactory(ExamTypeFactory)
+    user = factory.SubFactory(UserAccountFactory)
+
+
+class TestFactory(DjangoModelFactory):
+    class Meta:
+        model = models.Test
+
+    name = 'EmptyTest'
+    label = 'Epmty'
+    annotation = 'This is an annotation'
+
+
+class SubmissionFactory(DjangoModelFactory):
+    class Meta:
+        model = models.Submission
+
+    text = """#include<stdio.h>
+
+int main() {
+    printf("Hello World\n");
+    return 0;
+}
+"""
+    type = factory.SubFactory(SubmissionTypeFactory)
+    student = factory.SubFactory(StudentInfoFactory)
+
+
+class FeedbackFactory(DjangoModelFactory):
+    class Meta:
+        model = models.Feedback
+
+    of_submission = factory.SubFactory(SubmissionTypeFactory)
+
+
+class FeedbackCommentFactory(DjangoModelFactory):
+    class Meta:
+        model = models.FeedbackComment
+
+    text = 'Well, this is bad...'
+    of_tutor = factory.SubFactory(UserAccountFactory)
+    of_feedback = factory.SubFactory(FeedbackFactory)
+
+
+class SubmissionSubscriptionFactory(DjangoModelFactory):
+    class Meta:
+        model = models.SubmissionSubscription
+
+    owner = factory.SubFactory(UserAccountFactory)
+
+
+class TutorSubmissionAssignmentFactory(DjangoModelFactory):
+    class Meta:
+        model = models.TutorSubmissionAssignment
+
+    submission = factory.SubFactory(SubmissionFactory)
+    subscription = factory.SubFactory(SubmissionSubscriptionFactory)
-- 
GitLab


From 96df55b8a25a9947c90b27dcd6b864ca342aaff4 Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Wed, 24 Oct 2018 11:09:49 +0200
Subject: [PATCH 03/21] Clarification in README and importer

---
 README.md        | 10 +++++-----
 util/importer.py |  6 +++---
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index d58e49e5..f20e563d 100644
--- a/README.md
+++ b/README.md
@@ -193,7 +193,7 @@ You need the following:
     generated by [hektor](https://gitlab.gwdg.de/j.michal/hektor)
 2. A .csv file where the columns are: id, name, score, (file suffix). No
    suffix defaults to .c  
-   Supported suffixes: .c , .java , .hs , .s (for mips)  
+   Supported suffixes: .c , .java , .hs , .s or .asm (for mips)  
    Important: The name values must be the same as the ones that are contained in
    the export file file from 1.
     Example:
@@ -204,16 +204,16 @@ You need the following:
     a03, Gamma Ray, 20
     ```
 3. A path to a directory with sample solutions named
-    <id>-lsg.c (same id as in 2.)
+    <id>.c (same id as in 2.)
 4. A path to a directory containing HTML files with an accurate
     description of the task. File name pattern has to be: <id>.html (same id as in 2.)
     ```commandline
     $ tree -L 2
     .
     ├── code-lsg
-    │   ├── a01-lsg.c
-    │   ├── a02-lsg.c
-    │   └── a03-lsg.c
+    │   ├── a01.c
+    │   ├── a02.java
+    │   └── a03.hs
     └── html
         ├── a01.html
         ├── a02.html
diff --git a/util/importer.py b/util/importer.py
index 705549d8..a49641ce 100644
--- a/util/importer.py
+++ b/util/importer.py
@@ -196,9 +196,9 @@ def do_load_submission_types():
         $ tree -L 2
         .
         ├── code-lsg
-        │   ├── a01-lsg.c
-        │   ├── a02-lsg.c
-        │   └── a03-lsg.c
+        │   ├── a01.c
+        │   ├── a02.java
+        │   └── a03.hs
         └── html
             ├── a01.html
             ├── a02.html
-- 
GitLab


From 83ff19ab367d9a336c4f7299811e7fee788344aa Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Wed, 24 Oct 2018 11:33:46 +0200
Subject: [PATCH 04/21] Added sidebar tests

---
 functional_tests/test_front_pages.py | 48 ++++++++++++++++++++++++----
 1 file changed, 41 insertions(+), 7 deletions(-)

diff --git a/functional_tests/test_front_pages.py b/functional_tests/test_front_pages.py
index cb8f987b..5aa9c73c 100644
--- a/functional_tests/test_front_pages.py
+++ b/functional_tests/test_front_pages.py
@@ -1,26 +1,28 @@
 import os
-import time
+from typing import Sequence
 
 from django.test import LiveServerTestCase
 from selenium import webdriver
-from selenium.webdriver.common.keys import Keys
-from selenium.webdriver.firefox.options import Options
+from selenium.webdriver.remote.webelement import WebElement
 
-from core.models import UserAccount
 from functional_tests.util import get_frontend_url, login, create_browser
-from util.factories import make_test_data
 from util import factory_boys as fact
 
 LiveServerTestCase.port = int(os.environ.get('LIVE_SERVER_PORT', 0))
 
 
-class FrontPageTestsTutorReviewer:
+class FrontPageTestsTutorReviewer():
+    def __init__(self):
+        self.browser: webdriver.Firefox = None
+
     def _login(self):
         login(self.browser, self.live_server_url, self.username, self.password)
 
     def test_statistics_are_shown(self):
         self._login()
-        statistics = self.browser.find_element_by_id('correction-statistics')
+        statistcs = self.browser.find_element_by_id('correction-statistics')
+        title = statistcs.find_element_by_class_name('title')
+        self.assertEqual('Statistics', title.text)
 
 
 class FrontPageTestsTutor(LiveServerTestCase, FrontPageTestsTutorReviewer):
@@ -30,10 +32,24 @@ class FrontPageTestsTutor(LiveServerTestCase, FrontPageTestsTutorReviewer):
         self.username = 'tutor'
         self.password = 'p'
         fact.UserAccountFactory(username=self.username, password=self.password)
+        fact.SubmissionFactory()
 
     def tearDown(self):
         self.browser.quit()
 
+    def test_side_bar_contains_correct_items(self):
+        self._login()
+        drawer = self.browser.find_element_by_class_name('v-navigation-drawer')
+        links = extract_hrefs(drawer.find_elements_by_tag_name('a'))
+        self.assertTrue(all(link in links for link in ['#/home', '#/feedback']))
+        task_title = drawer.find_element_by_class_name('v-toolbar__title')
+        self.assertEqual('Tasks', task_title.text)
+        footer = drawer.find_element_by_class_name('sidebar-footer')
+        feedback_link = footer.find_element_by_css_selector('a.feedback-link')
+        self.assertEqual('Give us Feedback!', feedback_link.text)
+        self.assertEqual('https://gitlab.gwdg.de/j.michal/grady/issues',
+                         feedback_link.get_attribute('href'))
+
 
 class FrontPageTestsReviewer(LiveServerTestCase, FrontPageTestsTutorReviewer):
     def setUp(self):
@@ -45,3 +61,21 @@ class FrontPageTestsReviewer(LiveServerTestCase, FrontPageTestsTutorReviewer):
 
     def tearDown(self):
         self.browser.quit()
+
+    def test_side_bar_contains_correct_items(self):
+        self._login()
+        drawer = self.browser.find_element_by_class_name('v-navigation-drawer')
+        links = extract_hrefs(drawer.find_elements_by_tag_name('a'))
+        self.assertTrue(all(link in links for link in
+                            ['#/home', '#/feedback', '#/student-overview', '#/tutor-overview']))
+        task_title = drawer.find_element_by_class_name('v-toolbar__title')
+        self.assertEqual('Tasks', task_title.text)
+        footer = drawer.find_element_by_class_name('sidebar-footer')
+        feedback_link = footer.find_element_by_css_selector('a.feedback-link')
+        self.assertEqual('Give us Feedback!', feedback_link.text)
+        self.assertEqual('https://gitlab.gwdg.de/j.michal/grady/issues',
+                         feedback_link.get_attribute('href'))
+
+
+def extract_hrefs(web_elements: Sequence[WebElement]):
+    return [el.get_attribute('href') for el in web_elements]
-- 
GitLab


From 9c17d435e1d069a37f1de92095c1d14a84040fcb Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Thu, 13 Dec 2018 14:30:04 +0100
Subject: [PATCH 05/21] # This is a combination of 3 commits. # The first
 commit's message is: Added tests for Frontpages

# The 2nd commit message will be skipped:

#	Added tests for Frontpages

# The 3rd commit message will be skipped:

#	Fixed tests failing in ci
#
#	Possibly due to weak password, since CI tests use live settings
---
 Makefile                                      |  3 +
 .../subscriptions/SubscriptionList.vue        |  2 +-
 functional_tests/test_front_pages.py          | 97 ++++++++++++-------
 functional_tests/test_login_page.py           |  3 -
 functional_tests/util.py                      | 17 +++-
 util/factory_boys.py                          | 13 ++-
 6 files changed, 87 insertions(+), 48 deletions(-)

diff --git a/Makefile b/Makefile
index 9aa05e46..3b60ef1f 100644
--- a/Makefile
+++ b/Makefile
@@ -30,6 +30,9 @@ install:
 test:
 	DJANGO_SETTINGS_MODULE=grady.settings pytest
 
+teste2e:
+	cd frontend && yarn build && cp dist/index.html ../core/templates && cd .. && python util/format_index.py && python manage.py collectstatic --no-input && HEADLESS_TESTS=$(headless) pytest --ds=grady.settings $(path); git checkout core/templates/index.html
+
 coverage:
 	DJANGO_SETTINGS_MODULE=grady.settings pytest --cov
 	coverage html
diff --git a/frontend/src/components/subscriptions/SubscriptionList.vue b/frontend/src/components/subscriptions/SubscriptionList.vue
index 0edd3ced..316b1b0e 100644
--- a/frontend/src/components/subscriptions/SubscriptionList.vue
+++ b/frontend/src/components/subscriptions/SubscriptionList.vue
@@ -1,5 +1,5 @@
 <template>
-  <v-card>
+  <v-card name='subscription-list'>
     <v-toolbar color="teal" :dense="sidebar">
       <v-toolbar-side-icon><v-icon>assignment</v-icon></v-toolbar-side-icon>
       <v-toolbar-title v-if="showDetail" style="min-width: fit-content;">
diff --git a/functional_tests/test_front_pages.py b/functional_tests/test_front_pages.py
index 5aa9c73c..70e8dc08 100644
--- a/functional_tests/test_front_pages.py
+++ b/functional_tests/test_front_pages.py
@@ -1,38 +1,62 @@
-import os
-from typing import Sequence
+import time
 
 from django.test import LiveServerTestCase
 from selenium import webdriver
-from selenium.webdriver.remote.webelement import WebElement
 
-from functional_tests.util import get_frontend_url, login, create_browser
+from core import models
+from core.models import UserAccount
+from functional_tests.util import login, create_browser, extract_hrefs_hashes
 from util import factory_boys as fact
 
-LiveServerTestCase.port = int(os.environ.get('LIVE_SERVER_PORT', 0))
 
-
-class FrontPageTestsTutorReviewer():
-    def __init__(self):
-        self.browser: webdriver.Firefox = None
-
-    def _login(self):
-        login(self.browser, self.live_server_url, self.username, self.password)
-
-    def test_statistics_are_shown(self):
-        self._login()
-        statistcs = self.browser.find_element_by_id('correction-statistics')
-        title = statistcs.find_element_by_class_name('title')
-        self.assertEqual('Statistics', title.text)
-
-
-class FrontPageTestsTutor(LiveServerTestCase, FrontPageTestsTutorReviewer):
+class UntestedParent:
+    class FrontPageTestsTutorReviewer(LiveServerTestCase):
+        browser: webdriver.Firefox = None
+        username = None
+        password = None
+        role = None
+
+        def setUp(self):
+            fact.SubmissionFactory.create_batch(4)
+
+        def _login(self):
+            login(self.browser, self.live_server_url, self.username, self.password)
+
+        def test_statistics_are_shown(self):
+            self._login()
+            statistics = self.browser.find_element_by_id('correction-statistics')
+            title = statistics.find_element_by_class_name('title')
+            self.assertEqual('Statistics', title.text)
+
+        def test_available_tasks_are_shown(self):
+            self._login()
+            tasks = self.browser.find_element_by_name('subscription-list')
+            title = tasks.find_element_by_class_name('v-toolbar__title')
+            self.assertEqual('Tasks', title.text)
+            time.sleep(8)
+            subscription_links = extract_hrefs_hashes(
+                tasks.find_elements_by_tag_name('a')
+            )
+            subscriptions = models.SubmissionSubscription.objects.filter(
+                owner__username=self.username,
+                deactivated=False,
+                feedback_stage=models.SubmissionSubscription.FEEDBACK_CREATION
+            ).exclude(query_type=models.SubmissionSubscription.RANDOM)
+            for sub in subscriptions:
+                self.assertIn(f'/subscription/{sub.pk}', subscription_links)
+
+
+class FrontPageTestsTutor(UntestedParent.FrontPageTestsTutorReviewer):
     def setUp(self):
-        self.live_server_url = get_frontend_url(self.live_server_url)
+        super().setUp()
         self.browser = create_browser()
         self.username = 'tutor'
         self.password = 'p'
-        fact.UserAccountFactory(username=self.username, password=self.password)
-        fact.SubmissionFactory()
+        self.role = UserAccount.TUTOR
+        fact.UserAccountFactory(
+            username=self.username,
+            password=self.password
+        )
 
     def tearDown(self):
         self.browser.quit()
@@ -40,8 +64,9 @@ class FrontPageTestsTutor(LiveServerTestCase, FrontPageTestsTutorReviewer):
     def test_side_bar_contains_correct_items(self):
         self._login()
         drawer = self.browser.find_element_by_class_name('v-navigation-drawer')
-        links = extract_hrefs(drawer.find_elements_by_tag_name('a'))
-        self.assertTrue(all(link in links for link in ['#/home', '#/feedback']))
+        links = extract_hrefs_hashes(drawer.find_elements_by_tag_name('a'))
+        print(links)
+        self.assertTrue(all(link in links for link in ['/home', '/feedback']))
         task_title = drawer.find_element_by_class_name('v-toolbar__title')
         self.assertEqual('Tasks', task_title.text)
         footer = drawer.find_element_by_class_name('sidebar-footer')
@@ -51,13 +76,19 @@ class FrontPageTestsTutor(LiveServerTestCase, FrontPageTestsTutorReviewer):
                          feedback_link.get_attribute('href'))
 
 
-class FrontPageTestsReviewer(LiveServerTestCase, FrontPageTestsTutorReviewer):
+class FrontPageTestsReviewer(UntestedParent.FrontPageTestsTutorReviewer):
     def setUp(self):
-        self.live_server_url = get_frontend_url(self.live_server_url)
+        super().setUp()
+        self.browser = create_browser()
         self.browser = create_browser()
         self.username = 'reviewer'
         self.password = 'p'
-        fact.UserAccountFactory(username=self.username, password=self.password)
+        self.role = UserAccount.REVIEWER
+        fact.UserAccountFactory(
+            username=self.username,
+            password=self.password,
+            role=self.role
+        )
 
     def tearDown(self):
         self.browser.quit()
@@ -65,9 +96,9 @@ class FrontPageTestsReviewer(LiveServerTestCase, FrontPageTestsTutorReviewer):
     def test_side_bar_contains_correct_items(self):
         self._login()
         drawer = self.browser.find_element_by_class_name('v-navigation-drawer')
-        links = extract_hrefs(drawer.find_elements_by_tag_name('a'))
+        links = extract_hrefs_hashes(drawer.find_elements_by_tag_name('a'))
         self.assertTrue(all(link in links for link in
-                            ['#/home', '#/feedback', '#/student-overview', '#/tutor-overview']))
+                            ['/home', '/feedback', '/student-overview', '/tutor-overview']))
         task_title = drawer.find_element_by_class_name('v-toolbar__title')
         self.assertEqual('Tasks', task_title.text)
         footer = drawer.find_element_by_class_name('sidebar-footer')
@@ -75,7 +106,3 @@ class FrontPageTestsReviewer(LiveServerTestCase, FrontPageTestsTutorReviewer):
         self.assertEqual('Give us Feedback!', feedback_link.text)
         self.assertEqual('https://gitlab.gwdg.de/j.michal/grady/issues',
                          feedback_link.get_attribute('href'))
-
-
-def extract_hrefs(web_elements: Sequence[WebElement]):
-    return [el.get_attribute('href') for el in web_elements]
diff --git a/functional_tests/test_login_page.py b/functional_tests/test_login_page.py
index b564cc76..21907957 100644
--- a/functional_tests/test_login_page.py
+++ b/functional_tests/test_login_page.py
@@ -7,7 +7,6 @@ from selenium.webdriver.common.keys import Keys
 from selenium.webdriver.firefox.options import Options
 
 from core.models import UserAccount
-from functional_tests.util import get_frontend_url
 from util.factories import make_test_data
 
 
@@ -16,7 +15,6 @@ LiveServerTestCase.port = int(os.environ.get('LIVE_SERVER_PORT', 0))
 
 class LoginPageTest(LiveServerTestCase):
     def setUp(self):
-        self.live_server_url = get_frontend_url(self.live_server_url)
         options = Options()
         # funnily the method is marked deprecated but the alternative setter is not working...
         options.headless = bool(os.environ.get('HEADLESS_TESTS', False))
@@ -134,4 +132,3 @@ class LoginPageTest(LiveServerTestCase):
         tutor = UserAccount.objects.get(username=username)
         self.assertEqual(UserAccount.TUTOR, tutor.role)
         self.assertFalse(tutor.is_active, "Tutors should be inactive after registered")
-
diff --git a/functional_tests/util.py b/functional_tests/util.py
index 178ddd25..c1ffba6f 100644
--- a/functional_tests/util.py
+++ b/functional_tests/util.py
@@ -1,13 +1,12 @@
 import os
 import time
+from itertools import islice
+from typing import Sequence
 
 from selenium import webdriver
 from selenium.webdriver.common.keys import Keys
 from selenium.webdriver.firefox.options import Options
-
-
-def get_frontend_url(live_server_url):
-    return os.environ.get('FRONTEND_URL', live_server_url)
+from selenium.webdriver.remote.webelement import WebElement
 
 
 def create_browser():
@@ -26,3 +25,13 @@ def login(browser, live_server_url, username, password='p'):
     password_input.send_keys(password)
     browser.find_element_by_xpath('//button[@type="submit"]').send_keys(Keys.ENTER)
     time.sleep(1)
+
+
+def nth(iterable, n, default=None):
+    "Returns the nth item or a default value"
+    return next(islice(iterable, n, None), default)
+
+
+def extract_hrefs_hashes(web_elements: Sequence[WebElement]):
+    return [nth(el.get_attribute('href').split('#'), 1, '')
+            for el in web_elements if el.get_attribute('href')]
diff --git a/util/factory_boys.py b/util/factory_boys.py
index 38d21ed3..4de7d9ed 100644
--- a/util/factory_boys.py
+++ b/util/factory_boys.py
@@ -10,6 +10,8 @@ fake.seed(42)
 class ExamTypeFactory(DjangoModelFactory):
     class Meta:
         model = models.ExamType
+        django_get_or_create = ('module_reference',)
+
     module_reference = 'B.Inf.4242 Test Module'
     total_score = 90
     pass_score = 45
@@ -29,11 +31,12 @@ class SubmissionTypeFactory(DjangoModelFactory):
 class UserAccountFactory(DjangoModelFactory):
     class Meta:
         model = models.UserAccount
+        django_get_or_create = ('username',)
 
-    role = models.UserAccount.REVIEWER
-    fullname = fake.name()
-    username = fake.user_name()
-    password = factory.PostGenerationMethodCall('set_password', 'p')
+    role = models.UserAccount.TUTOR
+    fullname = fake.name
+    username = fake.user_name
+    password = factory.PostGenerationMethodCall('set_password', 'redrum-is-murder-reversed')
 
 
 class StudentInfoFactory(DjangoModelFactory):
@@ -49,7 +52,7 @@ class TestFactory(DjangoModelFactory):
         model = models.Test
 
     name = 'EmptyTest'
-    label = 'Epmty'
+    label = 'Empty'
     annotation = 'This is an annotation'
 
 
-- 
GitLab


From 85378627e5cd790a0627cf815e0a65c11aaef4c3 Mon Sep 17 00:00:00 2001
From: Dominik Seeger <dominik.seeger@gmx.net>
Date: Thu, 13 Dec 2018 19:22:36 +0100
Subject: [PATCH 06/21] added e2e tests for exporting student data

added instance export e2e test

Swapped Logout button and slot postion

Logout is now always a the far left

e2e tests are now using one browser instance per TestClass

Statistics endpoint now checks if there are submissiontypes available

Fixed race cond. in e2e test and bug in stats endpoint

Added function_tests to flake8 check

Added id "logout" to logout button

Using --cache-from in build step

Increased implicit wait for e2e test
---
 .gitlab-ci.yml                                |   15 +-
 core/views/common_views.py                    |    4 +-
 frontend/package.json                         |   27 +-
 frontend/src/api.ts                           |    4 +-
 frontend/src/components/BaseLayout.vue        |    2 +-
 frontend/src/components/export/DataExport.vue |   35 +-
 .../src/components/export/ExportDialog.vue    |   12 +-
 .../src/components/export/InstanceExport.vue  |   21 +-
 .../feedback_list/FeedbackTable.vue           |   21 +-
 frontend/src/models.ts                        |    1 -
 frontend/src/store/modules/authentication.ts  |    6 +-
 .../src/store/modules/submission-notes.ts     |   20 +-
 frontend/src/store/modules/subscriptions.ts   |    2 +-
 frontend/src/store/store.ts                   |   31 +-
 frontend/src/util/helpers.ts                  |   36 +-
 frontend/tests/unit/.eslintrc.js              |    3 -
 frontend/tests/unit/autologout.spec.ts        |   33 +
 frontend/tsconfig.json                        |    7 +-
 frontend/vue.config.js                        |    2 +-
 frontend/yarn.lock                            | 2353 ++++++++++++-----
 functional_tests/test_export_modal.py         |  117 +
 functional_tests/test_front_pages.py          |   29 +-
 functional_tests/test_login_page.py           |   23 +-
 functional_tests/util.py                      |   14 +-
 util/factory_boys.py                          |    2 +-
 25 files changed, 2059 insertions(+), 761 deletions(-)
 create mode 100644 frontend/tests/unit/autologout.spec.ts
 create mode 100644 functional_tests/test_export_modal.py

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index aebff399..84953ada 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -7,8 +7,8 @@ stages:
   - staging
 
 variables:
-  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
-
+  CONTAINER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
+  DOCKER_DRIVER: overlay2
 
 # ========================== Build Testing section =========================== #
 build_test_env:
@@ -78,7 +78,7 @@ test_flake8:
   <<: *test_definition_virtualenv
   stage: test
   script:
-    - flake8 --exclude=migrations core util
+    - flake8 --exclude=migrations core util functional_tests
 
 # ----------------------------- Frontend subsection -------------------------- #
 .test_template_frontend: &test_definition_frontend
@@ -114,9 +114,12 @@ build_backend:
     DOCKER_DRIVER: overlay2
   script:
     - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
-    - docker build -t $IMAGE_TAG .
-    - docker tag $IMAGE_TAG $IMAGE_TAG-$CI_COMMIT_SHA
-    - docker push $IMAGE_TAG
+    - docker pull "$CONTAINER_IMAGE-base" || true
+    - docker build --cache-from "$CONTAINER_IMAGE-base" -t "$CONTAINER_IMAGE-base" --target node .
+    - docker pull $CONTAINER_IMAGE || true
+    - docker build --cache-from $CONTAINER_IMAGE --cache-from "$CONTAINER_IMAGE-base" -t $CONTAINER_IMAGE .
+    - docker push "$CONTAINER_IMAGE-base"
+    - docker push $CONTAINER_IMAGE
   tags:
     - docker
 
diff --git a/core/views/common_views.py b/core/views/common_views.py
index 4444f9e8..a7fd17e7 100644
--- a/core/views/common_views.py
+++ b/core/views/common_views.py
@@ -132,9 +132,11 @@ class SubmissionTypeApiView(viewsets.ReadOnlyModelViewSet):
 class StatisticsEndpoint(viewsets.ViewSet):
 
     def list(self, request, *args, **kwargs):
+        first_sub_type = models.SubmissionType.objects.first()
+
         return Response({
             'submissions_per_type':
-            models.SubmissionType.objects.first().submissions.count(),
+                first_sub_type.submissions.count() if first_sub_type is not None else 0,
 
             'submissions_per_student':
             models.SubmissionType.objects.count(),
diff --git a/frontend/package.json b/frontend/package.json
index 4447f55e..78a12a64 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,15 +1,15 @@
 {
   "name": "frontend",
   "version": "0.1.0",
+  "private": true,
   "description": "Vue.js frontend for Grady",
   "author": "robinwilliam.hundt <robinwilliam.hundt@stud.uni-goettingen.de>",
-  "private": true,
   "scripts": {
     "serve": "vue-cli-service serve",
     "build": "vue-cli-service build",
     "lint": "vue-cli-service lint",
-    "test:unit": "vue-cli-service test:unit",
-    "test:e2e": "vue-cli-service test:e2e"
+    "test:e2e": "vue-cli-service test:e2e",
+    "test:unit": "vue-cli-service test:unit --require mock-local-storage"
   },
   "dependencies": {
     "axios": "^0.18.0",
@@ -28,18 +28,23 @@
   "devDependencies": {
     "@types/chai": "^4.1.0",
     "@types/highlight.js": "^9.12.3",
-    "@types/mocha": "^5.2.4",
+    "@types/mocha": "^5.2.5",
     "@types/nightwatch": "^0.9.8",
+    "@types/sinon": "^7.0.2",
     "@vue/cli-plugin-e2e-nightwatch": "^3.0.0-rc.10",
     "@vue/cli-plugin-eslint": "^3.0.0-rc.10",
-    "@vue/cli-plugin-typescript": "^3.0.0-rc.10",
-    "@vue/cli-plugin-unit-mocha": "^3.0.0-rc.10",
-    "@vue/cli-service": "^3.0.0-rc.10",
+    "@vue/cli-plugin-typescript": "^3.2.0",
+    "@vue/cli-plugin-unit-mocha": "^3.2.0",
+    "@vue/cli-service": "^3.2.0",
     "@vue/eslint-config-standard": "^3.0.0-rc.10",
-    "@vue/eslint-config-typescript": "^3.0.0-rc.10",
-    "@vue/test-utils": "^1.0.0-beta.20",
-    "chai": "^4.1.2",
-    "typescript": "^3.0.0",
+    "@vue/eslint-config-typescript": "^3.2.0",
+    "@vue/test-utils": "^1.0.0-beta.27",
+    "chai": "^4.2.0",
+    "mocha": "^5.2.0",
+    "mocha-webpack": "^1.1.0",
+    "mock-local-storage": "^1.1.8",
+    "sinon": "^7.2.2",
+    "typescript": "^3.2.2",
     "vue-template-compiler": "^2.5.16"
   }
 }
diff --git a/frontend/src/api.ts b/frontend/src/api.ts
index e06a4111..767736b5 100644
--- a/frontend/src/api.ts
+++ b/frontend/src/api.ts
@@ -25,7 +25,7 @@ let ax: AxiosInstance = axios.create({
   baseURL: getInstanceBaseUrl()
 })
 {
-  let token = sessionStorage.getItem('token')
+  let token = window.sessionStorage.getItem('token')
   if (token) {
     ax.defaults.headers['Authorization'] = `JWT ${token}`
   }
@@ -221,8 +221,6 @@ export async function fetchStudentExportData (options: StudentExportOptions): Pr
   return (await ax.post('/api/export/json/', options)).data
 }
 
-
-
 // Note, this interface does not represent all of the returned data,
 // but only the fields which have to be transformed for deanonymisation
 export interface InstanceExportData {
diff --git a/frontend/src/components/BaseLayout.vue b/frontend/src/components/BaseLayout.vue
index fd29f7e2..b5407e46 100644
--- a/frontend/src/components/BaseLayout.vue
+++ b/frontend/src/components/BaseLayout.vue
@@ -77,8 +77,8 @@
           <user-options class="mt-1"/>
         </v-menu>
       </div>
-      <v-btn color="blue darken-1" @click.native="logout">Logout</v-btn>
       <slot name="toolbar-right"></slot>
+      <v-btn color="blue darken-1" id="logout" @click.native="logout">Logout</v-btn>
     </v-toolbar>
   </div>
 </template>
diff --git a/frontend/src/components/export/DataExport.vue b/frontend/src/components/export/DataExport.vue
index 59c7ee32..4bed957d 100644
--- a/frontend/src/components/export/DataExport.vue
+++ b/frontend/src/components/export/DataExport.vue
@@ -1,6 +1,6 @@
 <template>
   <v-dialog v-model="exportDialog" max-width="31vw" @update:returnValue="hide">
-    <v-card>
+    <v-card id="data-export-modal">
       <v-card-title class="title">
         Student Data Export
       </v-card-title>
@@ -28,7 +28,7 @@
               </span>
             </v-tooltip>
           </v-flex>
-          <v-flex xs3 offset-xs1>
+          <v-flex xs3 offset-xs1 id="type-select">
             <v-select
               label="Export file format"
               :items="availableExportTypes"
@@ -42,7 +42,7 @@
             @click="exportDialog = false"
           >close</v-btn>
           <v-spacer/>
-          <v-btn flat outline @click="getExportFile"
+          <v-btn id="export-data-download-btn" flat outline @click="getExportFile"
           >{{mapFile || mapFileLoaded ? 'Download and apply mapping' : 'Download without mapping'}}</v-btn>
         </v-card-actions>
       </v-card-text>
@@ -51,20 +51,18 @@
 </template>
 
 <script lang="ts">
-import {Vue, Component, Mixins} from 'vue-property-decorator'
+import { Vue, Component, Mixins } from 'vue-property-decorator'
 import { getters } from '@/store/getters'
 import ax, { StudentExportItem, fetchStudentExportData } from '@/api'
 import FileSelect from '@/components/util/FileSelect.vue'
 import { mutations as mut } from '@/store/mutations'
 import { parseCSVMapMixin } from '@/components/mixins/mixins'
 
-
 enum ExportType {
   JSON = 'JSON',
   CSV = 'CSV'
 }
 
-
 @Component({
   components: { FileSelect }
 })
@@ -93,20 +91,21 @@ export default class DataExport extends Mixins(parseCSVMapMixin) {
   readMapFileAndCommit () {
     const fileReader = new FileReader()
     return new Promise((resolve, reject) => {
+      // @ts-ignore
       fileReader.onload = event => {
         // @ts-ignore typings of EventTarget seem to be wrong
         const studentMap = this.parseCSVMap(event.target.result)
         mut.SET_STUDENT_MAP(studentMap)
         resolve()
-      },
+      }
       fileReader.onerror = () => {
-        fileReader.abort();
-        reject(new Error("Problem parsing input file."));
+        fileReader.abort()
+        reject(new Error('Problem parsing input file.'))
       }
 
       if (!this.mapFile) {
-        reject(new Error("Can only call" +
-          " readMapFileAndCommit when mapFile is not undefined"))
+        reject(new Error('Can only call' +
+          ' readMapFileAndCommit when mapFile is not undefined'))
       } else {
         fileReader.readAsText(this.mapFile)
       }
@@ -132,8 +131,8 @@ export default class DataExport extends Mixins(parseCSVMapMixin) {
     }, '')
     headerLine += Object.values(studentExport[0].Scores)
       .reduce((acc: string, curr) => {
-      return `${acc};${curr.type}`
-    }, '')
+        return `${acc};${curr.type}`
+      }, '')
 
     const lines = studentExport.map(student => {
       const normalFields = Object.values(student).reduce((acc: string, curr): string => {
@@ -168,16 +167,16 @@ export default class DataExport extends Mixins(parseCSVMapMixin) {
   }
 
   optionalConvertAndCreatePopup (studentData: StudentExportItem[]) {
-    const convertedData = this.exportType === ExportType.CSV ?
-      this.jsonToCSV(studentData) : studentData
+    const convertedData = this.exportType === ExportType.CSV
+      ? this.jsonToCSV(studentData) : studentData
 
     this.createDownloadPopup(convertedData, this.exportType)
   }
 
   async getMappedExportFile (studentData: StudentExportItem[]) {
     if (!this.mapFile && !this.mapFileLoaded) {
-      throw new Error("Either mapFile must be selected or already loaded "+
-                      "to call getMappedExportFile")
+      throw new Error('Either mapFile must be selected or already loaded ' +
+                      'to call getMappedExportFile')
     }
     if (this.mapFile) {
       await this.readMapFileAndCommit()
@@ -187,7 +186,7 @@ export default class DataExport extends Mixins(parseCSVMapMixin) {
   }
 
   async getExportFile () {
-    const studentData = await fetchStudentExportData({setPasswords: this.setPasswords})
+    const studentData = await fetchStudentExportData({ setPasswords: this.setPasswords })
 
     if (this.mapFile || this.mapFileLoaded) {
       this.getMappedExportFile(studentData)
diff --git a/frontend/src/components/export/ExportDialog.vue b/frontend/src/components/export/ExportDialog.vue
index 8027a44c..54ea87b7 100644
--- a/frontend/src/components/export/ExportDialog.vue
+++ b/frontend/src/components/export/ExportDialog.vue
@@ -2,15 +2,15 @@
   <div>
     <v-menu>
       <v-tooltip bottom slot="activator">
-        <v-btn :color="exportColor" slot="activator">
+        <v-btn id="export-btn" :color="exportColor" slot="activator">
           export
           <v-icon>file_download</v-icon>
         </v-btn>
-        <span v-if="corrected">All submissions have been corrected!</span>
-        <span v-else>UNCORRECTED submissions left! Export will be incomplete.</span>
+        <span id="corrected-tooltip" v-if="corrected">All submissions have been corrected!</span>
+        <span id="uncorrected-tooltip" v-else>UNCORRECTED submissions left! Export will be incomplete.</span>
       </v-tooltip>
       <v-list>
-        <v-list-tile v-for="(item, i) in menuItems" :key="i" @click="item.action">{{item.display}}</v-list-tile>
+        <v-list-tile :id="'export-list' + i" v-for="(item, i) in menuItems" :key="i" @click="item.action">{{item.display}}</v-list-tile>
       </v-list>
     </v-menu>
     <component v-if="displayComponent" :is="displayComponent" @hide="displayComponent = null"/>
@@ -51,8 +51,8 @@ export default class ExportDialog extends Vue {
 
     // apparently `this` is not the same when used within a
     // closure when defining data and within a method
-    setDisplayComponent(component: any) {
-        this.displayComponent = component
+    setDisplayComponent (component: any) {
+      this.displayComponent = component
     }
 }
 </script>
diff --git a/frontend/src/components/export/InstanceExport.vue b/frontend/src/components/export/InstanceExport.vue
index 8045bd22..91c63ba6 100644
--- a/frontend/src/components/export/InstanceExport.vue
+++ b/frontend/src/components/export/InstanceExport.vue
@@ -1,6 +1,6 @@
 <template>
   <v-dialog v-model="exportDialog" max-width="31vw" @update:returnValue="hide">
-    <v-card>
+    <v-card id="instance-export-modal">
       <v-card-title class="title">
         Instance Data Export
       </v-card-title>
@@ -20,7 +20,7 @@
             @click="exportDialog = false"
           >close</v-btn>
           <v-spacer/>
-          <v-btn flat outline @click="getExportFile"
+          <v-btn id="instance-export-dl" flat outline @click="getExportFile"
           >{{mapFile || mapFileLoaded ? 'Download and apply mapping' : 'Download without mapping'}}</v-btn>
         </v-card-actions>
       </v-card-text>
@@ -29,14 +29,13 @@
 </template>
 
 <script lang="ts">
-import {Vue, Component, Mixins} from 'vue-property-decorator'
+import { Vue, Component, Mixins } from 'vue-property-decorator'
 import { getters } from '@/store/getters'
 import ax, { StudentExportItem, fetchStudentExportData, fetchInstanceExportData, InstanceExportData } from '@/api'
 import FileSelect from '@/components/util/FileSelect.vue'
 import { mutations as mut } from '@/store/mutations'
 import { parseCSVMapMixin } from '@/components/mixins/mixins'
 
-
 @Component({
   components: { FileSelect }
 })
@@ -65,15 +64,15 @@ export default class DataExport extends Mixins(parseCSVMapMixin) {
         const studentMap = this.parseCSVMap(event.target.result)
         mut.SET_STUDENT_MAP(studentMap)
         resolve()
-      },
+      }
       fileReader.onerror = () => {
-        fileReader.abort();
-        reject(new Error("Problem parsing input file."));
+        fileReader.abort()
+        reject(new Error('Problem parsing input file.'))
       }
 
       if (!this.mapFile) {
-        reject(new Error("Can only call" +
-          " readMapFileAndCommit when mapFile is not undefined"))
+        reject(new Error('Can only call' +
+          ' readMapFileAndCommit when mapFile is not undefined'))
       } else {
         fileReader.readAsText(this.mapFile)
       }
@@ -107,8 +106,8 @@ export default class DataExport extends Mixins(parseCSVMapMixin) {
 
   async getMappedExportFile (studentData: InstanceExportData) {
     if (!this.mapFile && !this.mapFileLoaded) {
-      throw new Error("Either mapFile must be selected or already loaded "+
-                      "to call getMappedExportFile")
+      throw new Error('Either mapFile must be selected or already loaded ' +
+                      'to call getMappedExportFile')
     }
     if (this.mapFile) {
       await this.readMapFileAndCommit()
diff --git a/frontend/src/components/feedback_list/FeedbackTable.vue b/frontend/src/components/feedback_list/FeedbackTable.vue
index 445173ef..afb1fc62 100644
--- a/frontend/src/components/feedback_list/FeedbackTable.vue
+++ b/frontend/src/components/feedback_list/FeedbackTable.vue
@@ -38,15 +38,15 @@
 </template>
 
 <script lang="ts">
-import {mapState, mapGetters} from 'vuex'
-import {Component, Prop, Vue} from 'vue-property-decorator'
-import {getObjectValueByPath} from '@/util/helpers'
+import { mapState, mapGetters } from 'vuex'
+import { Component, Prop, Vue } from 'vue-property-decorator'
+import { getObjectValueByPath } from '@/util/helpers'
 import FeedbackSearchOptions from '@/components/feedback_list/FeedbackSearchOptions.vue'
 import { FeedbackSearchOptions as OptionsModule } from '@/store/modules/feedback_list/feedback-search-options'
-import { FeedbackTable as FeedbackModule, FeedbackHistoryItem} from '@/store/modules/feedback_list/feedback-table'
-import { Subscription, Feedback } from '@/models';
-import { actions } from '@/store/actions';
-import { getters } from '@/store/getters';
+import { FeedbackTable as FeedbackModule, FeedbackHistoryItem } from '@/store/modules/feedback_list/feedback-table'
+import { Subscription, Feedback } from '@/models'
+import { actions } from '@/store/actions'
+import { getters } from '@/store/getters'
 
 @Component({
   components: {
@@ -98,17 +98,16 @@ export default class FeedbackTable extends Vue {
     }
     const associatedTutors = this.stageFilterString === 'all'
       ? Object.values(feedback.history).filter(histEntry => !!histEntry).map(histEntry => histEntry!.owner)
-      : feedback.history[<Subscription.FeedbackStageEnum> this.stageFilterString] ?
-      [feedback.history[<Subscription.FeedbackStageEnum> this.stageFilterString]!.owner] : []
+      : feedback.history[<Subscription.FeedbackStageEnum> this.stageFilterString]
+        ? [feedback.history[<Subscription.FeedbackStageEnum> this.stageFilterString]!.owner] : []
     return this.filterByTutors.length === 0 ||
         associatedTutors.some(tutor => this.filterByTutors.includes(tutor))
-
   }
   showSubmission (submissionPk: string) {
     this.$router.push(`/feedback/${submissionPk}`)
   }
   prefetchSubmission (submissionPk: string) {
-    actions.getSubmissionFeedbackTest({pk: submissionPk})
+    actions.getSubmissionFeedbackTest({ pk: submissionPk })
   }
   prefetchFilteredItems (items: Feedback[]) {
     if (items.length < this.prefetchWhenLessItems) {
diff --git a/frontend/src/models.ts b/frontend/src/models.ts
index ce4b7e97..c94d46fa 100644
--- a/frontend/src/models.ts
+++ b/frontend/src/models.ts
@@ -198,7 +198,6 @@ export interface Credentials {
     password: string
 }
 
-
 export interface JSONWebToken {
     token: string
 }
diff --git a/frontend/src/store/modules/authentication.ts b/frontend/src/store/modules/authentication.ts
index a8cbc331..f5aa875d 100644
--- a/frontend/src/store/modules/authentication.ts
+++ b/frontend/src/store/modules/authentication.ts
@@ -19,7 +19,7 @@ export interface AuthState {
 }
 function initialState (): AuthState {
   return {
-    token: sessionStorage.getItem('token') || '',
+    token: window.sessionStorage.getItem('token') || '',
     lastTokenRefreshTry: Date.now(),
     refreshingToken: false,
     jwtTimeDelta: 0,
@@ -59,7 +59,7 @@ function SET_MESSAGE (state: AuthState, message: string) {
   state.message = message
 }
 function SET_JWT_TOKEN (state: AuthState, token: string) {
-  sessionStorage.setItem('token', token)
+  window.sessionStorage.setItem('token', token)
   state.token = token
 }
 function SET_JWT_TIME_DELTA (state: AuthState, timeDelta: number) {
@@ -75,7 +75,7 @@ function SET_LAST_TOKEN_REFRESH_TRY (state: AuthState) {
   state.lastTokenRefreshTry = Date.now()
 }
 function RESET_STATE (state: AuthState) {
-  sessionStorage.setItem('token', '')
+  window.sessionStorage.setItem('token', '')
   Object.assign(state, initialState())
 }
 
diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts
index 7d5ba093..3c1f1376 100644
--- a/frontend/src/store/modules/submission-notes.ts
+++ b/frontend/src/store/modules/submission-notes.ts
@@ -1,10 +1,10 @@
 import Vue from 'vue'
 import * as hljs from 'highlight.js'
 import * as api from '@/api'
-import {nameSpacer} from '@/util/helpers'
-import {Feedback, FeedbackComment, Submission, SubmissionNoType} from '@/models'
-import {RootState} from '@/store/store'
-import {Module} from 'vuex'
+import { nameSpacer } from '@/util/helpers'
+import { Feedback, FeedbackComment, Submission, SubmissionNoType } from '@/models'
+import { RootState } from '@/store/store'
+import { Module } from 'vuex'
 import { getStoreBuilder, BareActionContext } from 'vuex-typex'
 
 export const subNotesNamespace = nameSpacer('submissionNotes/')
@@ -100,7 +100,7 @@ function SET_SHOW_FEEDBACK (state: SubmissionNotesState, val: boolean) {
 function UPDATE_FEEDBACK_LINE (state: SubmissionNotesState, feedback: {lineNo: number, comment: Partial<FeedbackComment>}) {
   // explicit .toString() on lineNo is necessary because of Vue.set typings
   if (state.updatedFeedback.feedbackLines) {
-      Vue.set(state.updatedFeedback.feedbackLines, feedback.lineNo.toString(), feedback.comment)
+    Vue.set(state.updatedFeedback.feedbackLines, feedback.lineNo.toString(), feedback.comment)
   }
 }
 function UPDATE_FEEDBACK_SCORE (state: SubmissionNotesState, score: number) {
@@ -112,7 +112,7 @@ function DELETE_FEEDBACK_LINE (state: SubmissionNotesState, lineNo: number) {
     Vue.delete(state.updatedFeedback.feedbackLines, <string><any>lineNo)
   }
 }
-function TOGGLE_EDITOR_ON_LINE (state: SubmissionNotesState, {lineNo, comment}: {lineNo: number, comment: FeedbackComment}) {
+function TOGGLE_EDITOR_ON_LINE (state: SubmissionNotesState, { lineNo, comment }: {lineNo: number, comment: FeedbackComment}) {
   Vue.set(state.ui.selectedCommentOnLine, <string><any>lineNo, comment)
   Vue.set(state.ui.showEditorOnLine, <string><any> lineNo, !state.ui.showEditorOnLine[lineNo])
 }
@@ -129,14 +129,14 @@ function RESET_STATE (state: SubmissionNotesState) {
   Object.assign(state, initialState())
 }
 
-async function deleteComments ({state}: BareActionContext<SubmissionNotesState, RootState>) {
+async function deleteComments ({ state }: BareActionContext<SubmissionNotesState, RootState>) {
   return Promise.all(
     Object.values(state.commentsMarkedForDeletion).map(comment => {
       return api.deleteComment(comment)
     })
   )
 }
-async function submitFeedback ({state}: BareActionContext<SubmissionNotesState, RootState>, {isFinal = false}) {
+async function submitFeedback ({ state }: BareActionContext<SubmissionNotesState, RootState>, { isFinal = false }) {
   let feedback: Partial<Feedback> = {
     isFinal: isFinal,
     ofSubmission: state.submission.pk
@@ -154,10 +154,10 @@ async function submitFeedback ({state}: BareActionContext<SubmissionNotesState,
   // delete those comments that have been marked for deletion
   await SubmissionNotes.deleteComments()
   if (!state.hasOrigFeedback) {
-    return api.submitFeedbackForAssignment({feedback})
+    return api.submitFeedbackForAssignment({ feedback })
   } else {
     feedback.pk = state.origFeedback.pk
-    return api.submitUpdatedFeedback(<{feedback: Feedback}> {feedback})
+    return api.submitUpdatedFeedback(<{feedback: Feedback}> { feedback })
   }
 }
 
diff --git a/frontend/src/store/modules/subscriptions.ts b/frontend/src/store/modules/subscriptions.ts
index 2d43594b..83c52ccf 100644
--- a/frontend/src/store/modules/subscriptions.ts
+++ b/frontend/src/store/modules/subscriptions.ts
@@ -1,6 +1,6 @@
 import Vue from 'vue'
 import * as api from '@/api'
-import { cartesian, flatten, handleError, once } from '@/util/helpers'
+import { cartesian, flatten, once } from '@/util/helpers'
 import { Assignment, Subscription } from '@/models'
 import { ActionContext, Module } from 'vuex'
 import { RootState } from '@/store/store'
diff --git a/frontend/src/store/store.ts b/frontend/src/store/store.ts
index b2e63118..e8f940af 100644
--- a/frontend/src/store/store.ts
+++ b/frontend/src/store/store.ts
@@ -1,34 +1,39 @@
 import Vuex from 'vuex'
 import Vue from 'vue'
 import createPersistedState from 'vuex-persistedstate'
-import {getStoreBuilder} from 'vuex-typex'
-
+import { getStoreBuilder } from 'vuex-typex'
 
+// @ts-ignore
 import './modules/ui'
+// @ts-ignore
 import './modules/authentication'
+// @ts-ignore
 import './modules/feedback_list/feedback-search-options'
+// @ts-ignore
 import './modules/feedback_list/feedback-table'
+// @ts-ignore
 import './modules/subscriptions'
+// @ts-ignore
 import './modules/submission-notes'
+// @ts-ignore
 import './modules/student-page'
+// @ts-ignore
 import './modules/tutor-overview'
 
 import './mutations'
 import './actions'
 import './getters'
 
+import { UIState } from './modules/ui'
+import { AuthState } from './modules/authentication'
+import { FeedbackSearchOptionsState } from './modules/feedback_list/feedback-search-options'
+import { SubscriptionsState } from './modules/subscriptions'
+import { FeedbackTableState } from './modules/feedback_list/feedback-table'
+import { SubmissionNotesState } from './modules/submission-notes'
+import { StudentPageState } from './modules/student-page'
+import { TutorOverviewState, TutorOverview } from './modules/tutor-overview'
 
-import {UIState} from './modules/ui'
-import {AuthState} from './modules/authentication'
-import {FeedbackSearchOptionsState} from './modules/feedback_list/feedback-search-options'
-import {SubscriptionsState} from './modules/subscriptions'
-import {FeedbackTableState} from './modules/feedback_list/feedback-table'
-import {SubmissionNotesState} from './modules/submission-notes'
-import {StudentPageState} from './modules/student-page'
-import {TutorOverviewState, TutorOverview} from './modules/tutor-overview'
-
-
-import {lastInteraction} from '@/store/plugins/lastInteractionPlugin'
+import { lastInteraction } from '@/store/plugins/lastInteractionPlugin'
 import {
   Exam, Feedback,
   Statistics,
diff --git a/frontend/src/util/helpers.ts b/frontend/src/util/helpers.ts
index dae1e7e9..07e06414 100644
--- a/frontend/src/util/helpers.ts
+++ b/frontend/src/util/helpers.ts
@@ -1,6 +1,6 @@
-import {AxiosError} from "axios"
-import {Dispatch} from "vuex"
-import {MutationHandler} from "vuex-typex"
+import { AxiosError } from 'axios'
+import { Dispatch } from 'vuex'
+import { MutationHandler } from 'vuex-typex'
 
 export function nameSpacer (namespace: string) {
   return function (commitType: string) {
@@ -40,15 +40,15 @@ interface GetSetPair<P> {
  * @returns {*}
  */
 export function createComputedGetterSetter<P> (
-  {path, mutation, namespace}:
+  { path, mutation, namespace }:
   {path: string, mutation: string | ((payload?: P) => void), namespace:string}): GetSetPair<P> {
   return {
     get (): any {
       return getObjectValueByPath((this as any).$store.state, path)
     },
     set (val: P): void {
-      if (typeof mutation === "string") {
-          (this as any).$store.commit(`${namespace ? namespace + '/' : ''}${mutation}`, val)
+      if (typeof mutation === 'string') {
+        (this as any).$store.commit(`${namespace ? namespace + '/' : ''}${mutation}`, val)
       } else {
         mutation(val)
       }
@@ -74,13 +74,13 @@ interface MappedState {
  * @param items array that contains objects {name, path, mutation}
  */
 export function mapStateToComputedGetterSetter (
-  {namespace = '', pathPrefix = '', items = []}:
+  { namespace = '', pathPrefix = '', items = [] }:
   {namespace: string, pathPrefix: string, items: StateMapperItem[]}): MappedState {
   return items.reduce((acc: MappedState, curr) => {
     // if no path is give, use name
     const itemPath = curr.path || curr.name
     const path = pathPrefix ? `${pathPrefix}.${itemPath}` : itemPath
-    acc[curr.name] = createComputedGetterSetter({mutation: curr.mutation, path, namespace})
+    acc[curr.name] = createComputedGetterSetter({ mutation: curr.mutation, path, namespace })
     return acc
   }, {})
 }
@@ -88,7 +88,7 @@ export function mapStateToComputedGetterSetter (
 // thanks to rsp
 // https://stackoverflow.com/questions/12303989/cartesian-product-of-multiple-arrays-in-javascript/43053803#43053803
 function cartesianHelper (a: Array<any>, b: Array<any>): Array<Array<any>> {
-    return ([] as Array<any>).concat(...a.map((a: any) => b.map((b: any) => [].concat(a, b))))
+  return ([] as Array<any>).concat(...a.map((a: any) => b.map((b: any) => [].concat(a, b))))
 }
 export function cartesian (a: Array<any>, b?: Array<any>, ...c: Array<Array<any>>): Array<Array<any>> {
   return b ? cartesian(cartesianHelper(a, b), ...c) : a
@@ -121,22 +121,6 @@ export function once (fn: Function, context?: object): OnceFunc {
     }
     return result
   }
-  wrapped.reset = function () {result = undefined}
+  wrapped.reset = function () { result = undefined }
   return wrapped
 }
-
-export function handleError (err: Error, dispatch: Dispatch, fallbackMsg: string): any
-export function handleError (err: AxiosError, dispatch: Dispatch, fallbackMsg: string): any
-export function handleError (err: any, dispatch: Dispatch, fallbackMsg: string): any {
-  if (err.response) {
-    if (err.response.status === 401) {
-      dispatch('logout', 'You\'ve been logged out')
-    } else {
-      throw err
-    }
-  } else {
-    if (fallbackMsg) {
-      throw new Error(fallbackMsg)
-    }
-  }
-}
diff --git a/frontend/tests/unit/.eslintrc.js b/frontend/tests/unit/.eslintrc.js
index 8038afe9..8f4efbda 100644
--- a/frontend/tests/unit/.eslintrc.js
+++ b/frontend/tests/unit/.eslintrc.js
@@ -1,8 +1,5 @@
 module.exports = {
   env: {
     mocha: true
-  },
-  rules: {
-    'import/no-extraneous-dependencies': 'off'
   }
 }
diff --git a/frontend/tests/unit/autologout.spec.ts b/frontend/tests/unit/autologout.spec.ts
new file mode 100644
index 00000000..823f4815
--- /dev/null
+++ b/frontend/tests/unit/autologout.spec.ts
@@ -0,0 +1,33 @@
+import Vuex from 'vuex'
+import { mount, createLocalVue } from '@vue/test-utils'
+import AutoLogout from '../../src/components/AutoLogout.vue'
+import sinon from 'sinon'
+import { Authentication } from '@/store/modules/authentication'
+
+import chai from 'chai'
+chai.should()
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('Auto Logout Unit Tests', () => {
+  afterEach(() => {
+    sinon.restore()
+  })
+
+  it('should be hidden by default', () => {
+    let store = new Vuex.Store({})
+    let wrapper = mount(AutoLogout, { localVue, store })
+    wrapper.vm.$data.logoutDialog.should.equal(false)
+    wrapper.html().should.contain('<div class="v-dialog v-dialog--persistent" style="max-width: 30%; display: none;">')
+  })
+  it('should be visible when logoutDialog is set to true', () => {
+    let spy = sinon.spy()
+    sinon.replace(Authentication, 'refreshJWT', spy)
+    let store = new Vuex.Store({})
+    let wrapper = mount(AutoLogout, { localVue, store })
+    // @ts-ignore
+    wrapper.vm.continueWork()
+    spy.called.should.equal(true)
+  })
+})
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
index 38bf9405..951dc8fc 100644
--- a/frontend/tsconfig.json
+++ b/frontend/tsconfig.json
@@ -10,6 +10,11 @@
     "esModuleInterop": true,
     "sourceMap": true,
     "baseUrl": ".",
+    "types": [
+      "webpack-env",
+      "mocha",
+      "chai"
+    ],
     "paths": {
       "@/*": [
         "src/*"
@@ -34,4 +39,4 @@
   "exclude": [
     "node_modules"
   ]
-}
+}
\ No newline at end of file
diff --git a/frontend/vue.config.js b/frontend/vue.config.js
index f470691e..74cd1213 100644
--- a/frontend/vue.config.js
+++ b/frontend/vue.config.js
@@ -17,7 +17,7 @@ module.exports = {
 
     // keep_fnames ist set to true because vuex-typex is dependant on the function names
     if (process.env.NODE_ENV === 'production') {
-      config.optimization.minimizer[0].options.uglifyOptions.keep_fnames = true
+      config.optimization.minimizer[0].options.terserOptions.keep_fnames = true
     }
   }
 }
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index b04e7aa7..27374204 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -2,80 +2,92 @@
 # yarn lockfile v1
 
 
-"@babel/code-frame@7.0.0-beta.44":
-  version "7.0.0-beta.44"
-  resolved "http://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz#2a02643368de80916162be70865c97774f3adbd9"
+"@babel/code-frame@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8"
+  integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==
   dependencies:
-    "@babel/highlight" "7.0.0-beta.44"
+    "@babel/highlight" "^7.0.0"
 
-"@babel/generator@7.0.0-beta.44":
-  version "7.0.0-beta.44"
-  resolved "http://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.44.tgz#c7e67b9b5284afcf69b309b50d7d37f3e5033d42"
+"@babel/generator@^7.2.2":
+  version "7.2.2"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.2.2.tgz#18c816c70962640eab42fe8cae5f3947a5c65ccc"
+  integrity sha512-I4o675J/iS8k+P38dvJ3IBGqObLXyQLTxtrR4u9cSUJOURvafeEWb/pFMOTwtNrmq73mJzyF6ueTbO1BtN0Zeg==
   dependencies:
-    "@babel/types" "7.0.0-beta.44"
+    "@babel/types" "^7.2.2"
     jsesc "^2.5.1"
-    lodash "^4.2.0"
+    lodash "^4.17.10"
     source-map "^0.5.0"
     trim-right "^1.0.1"
 
-"@babel/helper-function-name@7.0.0-beta.44":
-  version "7.0.0-beta.44"
-  resolved "http://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz#e18552aaae2231100a6e485e03854bc3532d44dd"
+"@babel/helper-function-name@^7.1.0":
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53"
+  integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==
   dependencies:
-    "@babel/helper-get-function-arity" "7.0.0-beta.44"
-    "@babel/template" "7.0.0-beta.44"
-    "@babel/types" "7.0.0-beta.44"
+    "@babel/helper-get-function-arity" "^7.0.0"
+    "@babel/template" "^7.1.0"
+    "@babel/types" "^7.0.0"
 
-"@babel/helper-get-function-arity@7.0.0-beta.44":
-  version "7.0.0-beta.44"
-  resolved "http://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz#d03ca6dd2b9f7b0b1e6b32c56c72836140db3a15"
+"@babel/helper-get-function-arity@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3"
+  integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==
   dependencies:
-    "@babel/types" "7.0.0-beta.44"
+    "@babel/types" "^7.0.0"
 
-"@babel/helper-split-export-declaration@7.0.0-beta.44":
-  version "7.0.0-beta.44"
-  resolved "http://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz#c0b351735e0fbcb3822c8ad8db4e583b05ebd9dc"
+"@babel/helper-split-export-declaration@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz#3aae285c0311c2ab095d997b8c9a94cad547d813"
+  integrity sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==
   dependencies:
-    "@babel/types" "7.0.0-beta.44"
+    "@babel/types" "^7.0.0"
 
-"@babel/highlight@7.0.0-beta.44":
-  version "7.0.0-beta.44"
-  resolved "http://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.44.tgz#18c94ce543916a80553edcdcf681890b200747d5"
+"@babel/highlight@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4"
+  integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==
   dependencies:
     chalk "^2.0.0"
     esutils "^2.0.2"
-    js-tokens "^3.0.0"
-
-"@babel/template@7.0.0-beta.44":
-  version "7.0.0-beta.44"
-  resolved "http://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f"
-  dependencies:
-    "@babel/code-frame" "7.0.0-beta.44"
-    "@babel/types" "7.0.0-beta.44"
-    babylon "7.0.0-beta.44"
-    lodash "^4.2.0"
-
-"@babel/traverse@7.0.0-beta.44":
-  version "7.0.0-beta.44"
-  resolved "http://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.44.tgz#a970a2c45477ad18017e2e465a0606feee0d2966"
-  dependencies:
-    "@babel/code-frame" "7.0.0-beta.44"
-    "@babel/generator" "7.0.0-beta.44"
-    "@babel/helper-function-name" "7.0.0-beta.44"
-    "@babel/helper-split-export-declaration" "7.0.0-beta.44"
-    "@babel/types" "7.0.0-beta.44"
-    babylon "7.0.0-beta.44"
-    debug "^3.1.0"
+    js-tokens "^4.0.0"
+
+"@babel/parser@^7.0.0", "@babel/parser@^7.2.2", "@babel/parser@^7.2.3":
+  version "7.2.3"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.2.3.tgz#32f5df65744b70888d17872ec106b02434ba1489"
+  integrity sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA==
+
+"@babel/template@^7.1.0":
+  version "7.2.2"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907"
+  integrity sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    "@babel/parser" "^7.2.2"
+    "@babel/types" "^7.2.2"
+
+"@babel/traverse@^7.0.0":
+  version "7.2.3"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.2.3.tgz#7ff50cefa9c7c0bd2d81231fdac122f3957748d8"
+  integrity sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    "@babel/generator" "^7.2.2"
+    "@babel/helper-function-name" "^7.1.0"
+    "@babel/helper-split-export-declaration" "^7.0.0"
+    "@babel/parser" "^7.2.3"
+    "@babel/types" "^7.2.2"
+    debug "^4.1.0"
     globals "^11.1.0"
-    invariant "^2.2.0"
-    lodash "^4.2.0"
+    lodash "^4.17.10"
 
-"@babel/types@7.0.0-beta.44":
-  version "7.0.0-beta.44"
-  resolved "http://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.44.tgz#6b1b164591f77dec0a0342aca995f2d046b3a757"
+"@babel/types@^7.0.0", "@babel/types@^7.2.2":
+  version "7.2.2"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.2.2.tgz#44e10fc24e33af524488b716cdaee5360ea8ed1e"
+  integrity sha512-fKCuD6UFUMkR541eDWL+2ih/xFZBXPOg/7EQFeTluMDebfqR4jrpaCjLhkWlQS4hT6nRa2PMEgXKbRB5/H2fpg==
   dependencies:
     esutils "^2.0.2"
-    lodash "^4.2.0"
+    lodash "^4.17.10"
     to-fast-properties "^2.0.0"
 
 "@intervolga/optimize-cssnano-plugin@^1.0.5":
@@ -97,29 +109,60 @@
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.2.tgz#54c5a964462be3d4d78af631363c18d6fa91ac26"
 
+"@sinonjs/commons@^1.0.2", "@sinonjs/commons@^1.2.0":
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.3.0.tgz#50a2754016b6f30a994ceda6d9a0a8c36adda849"
+  integrity sha512-j4ZwhaHmwsCb4DlDOIWnI5YyKDNMoNThsmwEpfHx6a1EpsGZ9qYLxP++LMlmBRjtGptGHFsGItJ768snllFWpA==
+  dependencies:
+    type-detect "4.0.8"
+
+"@sinonjs/formatio@^3.1.0":
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.1.0.tgz#6ac9d1eb1821984d84c4996726e45d1646d8cce5"
+  integrity sha512-ZAR2bPHOl4Xg6eklUGpsdiIJ4+J1SNag1DHHrG/73Uz/nVwXqjgUtRPLoS+aVyieN9cSbc0E4LsU984tWcDyNg==
+  dependencies:
+    "@sinonjs/samsam" "^2 || ^3"
+
+"@sinonjs/samsam@^2 || ^3", "@sinonjs/samsam@^3.0.2":
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.0.2.tgz#304fb33bd5585a0b2df8a4c801fcb47fa84d8e43"
+  integrity sha512-m08g4CS3J6lwRQk1pj1EO+KEVWbrbXsmi9Pw0ySmrIbcVxVaedoFgLvFsV8wHLwh01EpROVz3KvVcD1Jmks9FQ==
+  dependencies:
+    "@sinonjs/commons" "^1.0.2"
+    array-from "^2.1.1"
+    lodash.get "^4.4.2"
+
 "@types/chai@^4.1.0":
-  version "4.1.6"
-  resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.6.tgz#1eb26c040e3a84205b1008ad55c800e5e8a94e34"
+  version "4.1.7"
+  resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.7.tgz#1b8e33b61a8c09cbe1f85133071baa0dbf9fa71a"
+  integrity sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==
 
 "@types/highlight.js@^9.12.3":
   version "9.12.3"
   resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.3.tgz#b672cfaac25cbbc634a0fd92c515f66faa18dbca"
 
-"@types/mocha@^5.2.4":
+"@types/mocha@^5.2.5":
   version "5.2.5"
   resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.5.tgz#8a4accfc403c124a0bafe8a9fc61a05ec1032073"
+  integrity sha512-lAVp+Kj54ui/vLUFxsJTMtWvZraZxum3w3Nwkble2dNuV5VnPA+Mi2oGX9XYJAaIvZi3tn3cbjS/qcJXRb6Bww==
 
 "@types/nightwatch@^0.9.8":
   version "0.9.9"
   resolved "https://registry.yarnpkg.com/@types/nightwatch/-/nightwatch-0.9.9.tgz#87927decb3e1bbf2cf4dce436cb4089d23909024"
 
+"@types/sinon@^7.0.2":
+  version "7.0.2"
+  resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.0.2.tgz#234c03dd39bfa97616b28215caea3de043c63310"
+  integrity sha512-YvJOqPk4kh1eQyxuASDD4MDK27XWAhtw6hJ7rRayEOkkTpZkqDWpDb4OjLVzFGdapOuUgZdnqO+71Q3utCJtcA==
+
 "@types/webpack-env@^1.13.6":
   version "1.13.6"
   resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.6.tgz#128d1685a7c34d31ed17010fc87d6a12c1de6976"
 
-"@vue/cli-overlay@^3.0.4":
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/@vue/cli-overlay/-/cli-overlay-3.0.4.tgz#6d5640021906d35e5572c186c3f35b4e7a93c247"
+"@vue/cli-overlay@^3.2.0":
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/@vue/cli-overlay/-/cli-overlay-3.2.0.tgz#bb5d781914bb5af97d92410babbaa3720707b728"
+  integrity sha512-RKMSfgTtRs4VOXQhmbrNZJaCCheshebji9NcPNGyXzukCMBtoAbu3cG9HxizCSUA//oFFAdPP5BGeHvv0cpu/A==
 
 "@vue/cli-plugin-e2e-nightwatch@^3.0.0-rc.10":
   version "3.0.4"
@@ -133,61 +176,67 @@
     selenium-server "^3.13.0"
 
 "@vue/cli-plugin-eslint@^3.0.0-rc.10":
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-eslint/-/cli-plugin-eslint-3.0.4.tgz#0c62607c44a3b63fc730fdc0dd0ad5165bc559f6"
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-eslint/-/cli-plugin-eslint-3.2.1.tgz#4dc353add93023363bf4c6b347e64472d1f2f432"
+  integrity sha512-Z/eQw18FjTypMMryNg8WCYJxEBmSAtnzukRWWNFwqNnh2zM/2J6yR4dYhsyjNtMEMUOnQsAsJnqgw45rLu8sJg==
   dependencies:
-    "@vue/cli-shared-utils" "^3.0.4"
-    babel-eslint "^8.2.5"
+    "@vue/cli-shared-utils" "^3.2.0"
+    babel-eslint "^10.0.1"
     eslint "^4.19.1"
-    eslint-loader "^2.0.0"
-    eslint-plugin-vue "^4.5.0"
+    eslint-loader "^2.1.1"
+    eslint-plugin-vue "^4.7.1"
+    globby "^8.0.1"
 
-"@vue/cli-plugin-typescript@^3.0.0-rc.10":
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-typescript/-/cli-plugin-typescript-3.0.4.tgz#f09dab3833593b805534afbcadd9146bd848be31"
+"@vue/cli-plugin-typescript@^3.2.0":
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-typescript/-/cli-plugin-typescript-3.2.0.tgz#074dd7e7e65ac79c76bcd45cd64fd9a163f5b0fd"
+  integrity sha512-zv9N92mMyidK3+0lyJhIimeMjqIv0SZg8Nkie4xDBEKyliF3KmGLwDstLft7rBm44yqppEROjLMVOxJkuwUVNw==
   dependencies:
     "@types/webpack-env" "^1.13.6"
-    "@vue/cli-shared-utils" "^3.0.4"
-    fork-ts-checker-webpack-plugin "^0.4.4"
+    "@vue/cli-shared-utils" "^3.2.0"
+    fork-ts-checker-webpack-plugin "^0.5.0"
     globby "^8.0.1"
-    ts-loader "^4.4.2"
-    tslint "^5.10.0"
+    ts-loader "^5.3.1"
+    tslint "^5.11.0"
 
-"@vue/cli-plugin-unit-mocha@^3.0.0-rc.10":
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-unit-mocha/-/cli-plugin-unit-mocha-3.0.4.tgz#8e350537a270edb36c25e0673e2180ecc973770b"
+"@vue/cli-plugin-unit-mocha@^3.2.0":
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-unit-mocha/-/cli-plugin-unit-mocha-3.2.0.tgz#76828eae415ffe949c7fbfcad8e3b7d6a2e991c6"
+  integrity sha512-lT7arkoJFiTBJMIqt5IKFDY0VAoVOf7ZksAlp4WeD3Rwimh/q4tp9yMeNW4x92oxulbQFszkbtoTs5+ARzbN5w==
   dependencies:
-    "@vue/cli-shared-utils" "^3.0.4"
-    jsdom "^11.11.0"
+    "@vue/cli-shared-utils" "^3.2.0"
+    jsdom "^13.0.0"
     jsdom-global "^3.0.2"
     mocha "^5.2.0"
     mocha-webpack "^2.0.0-beta.0"
 
-"@vue/cli-service@^3.0.0-rc.10":
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/@vue/cli-service/-/cli-service-3.0.4.tgz#0084569470a742a3c9adf32e8815c4760eaafd96"
+"@vue/cli-service@^3.2.0":
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/@vue/cli-service/-/cli-service-3.2.0.tgz#96e8ee7b33a911ab71710c126ab55cd64c2a4c51"
+  integrity sha512-HTiaz1IBXV/JCfrmyhHJvDOYpPTBK0uQAekSVRTt5AddCeOV68ktdUqHbDe3VPDfrWFA5x3d3kIHlJd6WK31mA==
   dependencies:
     "@intervolga/optimize-cssnano-plugin" "^1.0.5"
-    "@vue/cli-overlay" "^3.0.4"
-    "@vue/cli-shared-utils" "^3.0.4"
+    "@vue/cli-overlay" "^3.2.0"
+    "@vue/cli-shared-utils" "^3.2.0"
     "@vue/preload-webpack-plugin" "^1.1.0"
     "@vue/web-component-wrapper" "^1.2.0"
-    acorn "^5.7.1"
+    acorn "^6.0.4"
+    acorn-walk "^6.1.1"
     address "^1.0.3"
     autoprefixer "^8.6.5"
-    cache-loader "^1.2.2"
+    cache-loader "^1.2.5"
     case-sensitive-paths-webpack-plugin "^2.1.2"
     chalk "^2.4.1"
     clipboardy "^1.2.3"
     cliui "^4.1.0"
-    copy-webpack-plugin "^4.5.2"
-    css-loader "^1.0.0"
-    cssnano "^4.0.0"
-    debug "^3.1.0"
+    copy-webpack-plugin "^4.6.0"
+    css-loader "^1.0.1"
+    cssnano "^4.1.7"
+    debug "^4.1.0"
     escape-string-regexp "^1.0.5"
-    file-loader "^1.1.11"
+    file-loader "^2.0.0"
     friendly-errors-webpack-plugin "^1.7.0"
-    fs-extra "^6.0.1"
+    fs-extra "^7.0.1"
     globby "^8.0.1"
     hash-sum "^1.0.2"
     html-webpack-plugin "^3.2.0"
@@ -195,34 +244,35 @@
     lodash.defaultsdeep "^4.6.0"
     lodash.mapvalues "^4.6.0"
     lodash.transform "^4.6.0"
-    mini-css-extract-plugin "^0.4.1"
+    mini-css-extract-plugin "^0.4.5"
     minimist "^1.2.0"
-    ora "^2.1.0"
-    portfinder "^1.0.13"
-    postcss-loader "^2.1.6"
+    ora "^3.0.0"
+    portfinder "^1.0.19"
+    postcss-loader "^3.0.0"
     read-pkg "^4.0.1"
-    semver "^5.5.0"
+    semver "^5.6.0"
     slash "^2.0.0"
     source-map-url "^0.4.0"
-    ssri "^6.0.0"
+    ssri "^6.0.1"
     string.prototype.padend "^3.0.0"
-    thread-loader "^1.1.5"
-    uglifyjs-webpack-plugin "^1.2.7"
-    url-loader "^1.1.0"
+    terser-webpack-plugin "^1.1.0"
+    thread-loader "^1.2.0"
+    url-loader "^1.1.2"
     vue-loader "^15.4.2"
-    webpack "^4.15.1"
-    webpack-bundle-analyzer "^2.13.1"
-    webpack-chain "^4.8.0"
-    webpack-dev-server "^3.1.4"
-    webpack-merge "^4.1.3"
+    webpack "^4.26.1"
+    webpack-bundle-analyzer "^3.0.3"
+    webpack-chain "^4.11.0"
+    webpack-dev-server "^3.1.10"
+    webpack-merge "^4.1.4"
     yorkie "^2.0.0"
 
-"@vue/cli-shared-utils@^3.0.4":
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/@vue/cli-shared-utils/-/cli-shared-utils-3.0.4.tgz#055a6eec7dd5d0392bec1547a0199ccc008ccc06"
+"@vue/cli-shared-utils@^3.0.4", "@vue/cli-shared-utils@^3.2.0":
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/@vue/cli-shared-utils/-/cli-shared-utils-3.2.0.tgz#504037063c2a4a346dc35c9652bd3863da4816e7"
+  integrity sha512-FCX5ABFg5pWhomyXLpCaogJktMvjsS5d4Mn5BfvqcJxCvzOX6ze8ihFK3u//XMeM78dOFpHSjxnRSvHtkEwgsg==
   dependencies:
     chalk "^2.4.1"
-    execa "^0.10.0"
+    execa "^1.0.0"
     joi "^13.0.0"
     launch-editor "^2.2.1"
     lru-cache "^4.1.3"
@@ -249,8 +299,9 @@
     vue-template-es2015-compiler "^1.6.0"
 
 "@vue/eslint-config-standard@^3.0.0-rc.10":
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/@vue/eslint-config-standard/-/eslint-config-standard-3.0.4.tgz#e7bb75f880d9996bbc5185f6733df95fb44282d2"
+  version "3.0.5"
+  resolved "https://registry.yarnpkg.com/@vue/eslint-config-standard/-/eslint-config-standard-3.0.5.tgz#7d6ae809eaec90993c6033d9543f48f687b9ebf1"
+  integrity sha512-qijT6OWUgsCO/MTC9Qpw4JtRL01cX0+kIikU0bXQxWXyh8WfNnfvxPouo7tKf7W28qqqiGmwDUnbvTB50SLcTw==
   dependencies:
     eslint-config-standard "^12.0.0-alpha.0"
     eslint-plugin-import "^2.11.0"
@@ -258,150 +309,171 @@
     eslint-plugin-promise "^3.7.0"
     eslint-plugin-standard "^3.1.0"
 
-"@vue/eslint-config-typescript@^3.0.0-rc.10":
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/@vue/eslint-config-typescript/-/eslint-config-typescript-3.0.4.tgz#574ad86675273bfcc846ce276a6200d3a071595e"
+"@vue/eslint-config-typescript@^3.2.0":
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/@vue/eslint-config-typescript/-/eslint-config-typescript-3.2.0.tgz#cbb19f11b74149f49febb52c92167458a22ba002"
+  integrity sha512-rDcOpHpxVOyKVe5kaxV63UOQulYvIAwpX0HZO4+rEnDoxkKtS9iAw2VeaamvrYp+TCFMsguo3CsGg0leu1xWAg==
   dependencies:
-    eslint-plugin-typescript "^0.12.0"
-    typescript-eslint-parser "^18.0.0"
+    eslint-plugin-typescript "^0.14.0"
+    typescript-eslint-parser "^21.0.1"
 
 "@vue/preload-webpack-plugin@^1.1.0":
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.0.tgz#d768dba004261c029b53a77c5ea2d5f9ee4f3cce"
 
-"@vue/test-utils@^1.0.0-beta.20":
-  version "1.0.0-beta.25"
-  resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.0.0-beta.25.tgz#4703076de3076bac42cdd242cd53e6fb8752ed8c"
+"@vue/test-utils@^1.0.0-beta.27":
+  version "1.0.0-beta.27"
+  resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.0.0-beta.27.tgz#7e5f7b7180c00e28a4ca55c0ff0a7e754377fdb2"
+  integrity sha512-Lzrd4ZBkS70Tl8JbXbDrN/NcSaH9aZT6+7emU3QhTJ+CrorJpyFDA1dkvSIhH+rDTs8sHFbGeXjXV/qorXxtRw==
   dependencies:
+    dom-event-types "^1.0.0"
     lodash "^4.17.4"
 
 "@vue/web-component-wrapper@^1.2.0":
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/@vue/web-component-wrapper/-/web-component-wrapper-1.2.0.tgz#bb0e46f1585a7e289b4ee6067dcc5a6ae62f1dd1"
 
-"@webassemblyjs/ast@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.8.tgz#f31f480debeef957f01b623f27eabc695fa4fe8f"
-  dependencies:
-    "@webassemblyjs/helper-module-context" "1.7.8"
-    "@webassemblyjs/helper-wasm-bytecode" "1.7.8"
-    "@webassemblyjs/wast-parser" "1.7.8"
-
-"@webassemblyjs/floating-point-hex-parser@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.8.tgz#1b3ed0e27e384032254e9322fc646dd3e70ef1b9"
-
-"@webassemblyjs/helper-api-error@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.8.tgz#a2b49c11f615e736f815ec927f035dcfa690d572"
-
-"@webassemblyjs/helper-buffer@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.8.tgz#3fc66bfa09c1c60e824cf3d5887826fac062877d"
-
-"@webassemblyjs/helper-code-frame@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.8.tgz#cc5a7e9522b70e7580df056dfd34020cf29645b0"
-  dependencies:
-    "@webassemblyjs/wast-printer" "1.7.8"
-
-"@webassemblyjs/helper-fsm@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.8.tgz#fe4607430af466912797c21acafd3046080182ea"
-
-"@webassemblyjs/helper-module-context@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.8.tgz#3c2e7ee93d14ff4768ba66fb1be42fdc9dc7160a"
-
-"@webassemblyjs/helper-wasm-bytecode@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.8.tgz#89bdb78cd6dd5209ae2ed2925de78d0f0e00b6f0"
-
-"@webassemblyjs/helper-wasm-section@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.8.tgz#c68ef7d26a6fc12421b2e6e56f9bc810dfb33e87"
-  dependencies:
-    "@webassemblyjs/ast" "1.7.8"
-    "@webassemblyjs/helper-buffer" "1.7.8"
-    "@webassemblyjs/helper-wasm-bytecode" "1.7.8"
-    "@webassemblyjs/wasm-gen" "1.7.8"
-
-"@webassemblyjs/ieee754@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.7.8.tgz#1f37974b13cb486a9237e73ce04cac7a2f1265ed"
+"@webassemblyjs/ast@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace"
+  integrity sha512-ZEzy4vjvTzScC+SH8RBssQUawpaInUdMTYwYYLh54/s8TuT0gBLuyUnppKsVyZEi876VmmStKsUs28UxPgdvrA==
+  dependencies:
+    "@webassemblyjs/helper-module-context" "1.7.11"
+    "@webassemblyjs/helper-wasm-bytecode" "1.7.11"
+    "@webassemblyjs/wast-parser" "1.7.11"
+
+"@webassemblyjs/floating-point-hex-parser@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz#a69f0af6502eb9a3c045555b1a6129d3d3f2e313"
+  integrity sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg==
+
+"@webassemblyjs/helper-api-error@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz#c7b6bb8105f84039511a2b39ce494f193818a32a"
+  integrity sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg==
+
+"@webassemblyjs/helper-buffer@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz#3122d48dcc6c9456ed982debe16c8f37101df39b"
+  integrity sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w==
+
+"@webassemblyjs/helper-code-frame@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz#cf8f106e746662a0da29bdef635fcd3d1248364b"
+  integrity sha512-T8ESC9KMXFTXA5urJcyor5cn6qWeZ4/zLPyWeEXZ03hj/x9weSokGNkVCdnhSabKGYWxElSdgJ+sFa9G/RdHNw==
+  dependencies:
+    "@webassemblyjs/wast-printer" "1.7.11"
+
+"@webassemblyjs/helper-fsm@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz#df38882a624080d03f7503f93e3f17ac5ac01181"
+  integrity sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A==
+
+"@webassemblyjs/helper-module-context@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz#d874d722e51e62ac202476935d649c802fa0e209"
+  integrity sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg==
+
+"@webassemblyjs/helper-wasm-bytecode@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz#dd9a1e817f1c2eb105b4cf1013093cb9f3c9cb06"
+  integrity sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ==
+
+"@webassemblyjs/helper-wasm-section@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz#9c9ac41ecf9fbcfffc96f6d2675e2de33811e68a"
+  integrity sha512-8ZRY5iZbZdtNFE5UFunB8mmBEAbSI3guwbrsCl4fWdfRiAcvqQpeqd5KHhSWLL5wuxo53zcaGZDBU64qgn4I4Q==
+  dependencies:
+    "@webassemblyjs/ast" "1.7.11"
+    "@webassemblyjs/helper-buffer" "1.7.11"
+    "@webassemblyjs/helper-wasm-bytecode" "1.7.11"
+    "@webassemblyjs/wasm-gen" "1.7.11"
+
+"@webassemblyjs/ieee754@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz#c95839eb63757a31880aaec7b6512d4191ac640b"
+  integrity sha512-Mmqx/cS68K1tSrvRLtaV/Lp3NZWzXtOHUW2IvDvl2sihAwJh4ACE0eL6A8FvMyDG9abes3saB6dMimLOs+HMoQ==
   dependencies:
     "@xtuc/ieee754" "^1.2.0"
 
-"@webassemblyjs/leb128@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.7.8.tgz#1bee83426819192db2ea1a234b84c7ebc6d34c1f"
+"@webassemblyjs/leb128@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.7.11.tgz#d7267a1ee9c4594fd3f7e37298818ec65687db63"
+  integrity sha512-vuGmgZjjp3zjcerQg+JA+tGOncOnJLWVkt8Aze5eWQLwTQGNgVLcyOTqgSCxWTR4J42ijHbBxnuRaL1Rv7XMdw==
   dependencies:
     "@xtuc/long" "4.2.1"
 
-"@webassemblyjs/utf8@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.7.8.tgz#2b489d5cf43e0aebb93d8e2d792aff9879c61f05"
-
-"@webassemblyjs/wasm-edit@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.8.tgz#f8bdbe7088718eca27b1c349bb7c06b8a457950c"
-  dependencies:
-    "@webassemblyjs/ast" "1.7.8"
-    "@webassemblyjs/helper-buffer" "1.7.8"
-    "@webassemblyjs/helper-wasm-bytecode" "1.7.8"
-    "@webassemblyjs/helper-wasm-section" "1.7.8"
-    "@webassemblyjs/wasm-gen" "1.7.8"
-    "@webassemblyjs/wasm-opt" "1.7.8"
-    "@webassemblyjs/wasm-parser" "1.7.8"
-    "@webassemblyjs/wast-printer" "1.7.8"
-
-"@webassemblyjs/wasm-gen@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.8.tgz#7e8abf1545eae74ac6781d545c034af3cfd0c7d5"
-  dependencies:
-    "@webassemblyjs/ast" "1.7.8"
-    "@webassemblyjs/helper-wasm-bytecode" "1.7.8"
-    "@webassemblyjs/ieee754" "1.7.8"
-    "@webassemblyjs/leb128" "1.7.8"
-    "@webassemblyjs/utf8" "1.7.8"
-
-"@webassemblyjs/wasm-opt@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.8.tgz#7ada6e211914728fce02ff0ff9c344edc6d41f26"
-  dependencies:
-    "@webassemblyjs/ast" "1.7.8"
-    "@webassemblyjs/helper-buffer" "1.7.8"
-    "@webassemblyjs/wasm-gen" "1.7.8"
-    "@webassemblyjs/wasm-parser" "1.7.8"
-
-"@webassemblyjs/wasm-parser@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.8.tgz#dac47c291fb6a3e63529aecd647592cd34afbf94"
-  dependencies:
-    "@webassemblyjs/ast" "1.7.8"
-    "@webassemblyjs/helper-api-error" "1.7.8"
-    "@webassemblyjs/helper-wasm-bytecode" "1.7.8"
-    "@webassemblyjs/ieee754" "1.7.8"
-    "@webassemblyjs/leb128" "1.7.8"
-    "@webassemblyjs/utf8" "1.7.8"
-
-"@webassemblyjs/wast-parser@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.7.8.tgz#f8aab9a450c048c1f9537695c89faeb92fabfba5"
-  dependencies:
-    "@webassemblyjs/ast" "1.7.8"
-    "@webassemblyjs/floating-point-hex-parser" "1.7.8"
-    "@webassemblyjs/helper-api-error" "1.7.8"
-    "@webassemblyjs/helper-code-frame" "1.7.8"
-    "@webassemblyjs/helper-fsm" "1.7.8"
+"@webassemblyjs/utf8@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.7.11.tgz#06d7218ea9fdc94a6793aa92208160db3d26ee82"
+  integrity sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA==
+
+"@webassemblyjs/wasm-edit@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz#8c74ca474d4f951d01dbae9bd70814ee22a82005"
+  integrity sha512-FUd97guNGsCZQgeTPKdgxJhBXkUbMTY6hFPf2Y4OedXd48H97J+sOY2Ltaq6WGVpIH8o/TGOVNiVz/SbpEMJGg==
+  dependencies:
+    "@webassemblyjs/ast" "1.7.11"
+    "@webassemblyjs/helper-buffer" "1.7.11"
+    "@webassemblyjs/helper-wasm-bytecode" "1.7.11"
+    "@webassemblyjs/helper-wasm-section" "1.7.11"
+    "@webassemblyjs/wasm-gen" "1.7.11"
+    "@webassemblyjs/wasm-opt" "1.7.11"
+    "@webassemblyjs/wasm-parser" "1.7.11"
+    "@webassemblyjs/wast-printer" "1.7.11"
+
+"@webassemblyjs/wasm-gen@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz#9bbba942f22375686a6fb759afcd7ac9c45da1a8"
+  integrity sha512-U/KDYp7fgAZX5KPfq4NOupK/BmhDc5Kjy2GIqstMhvvdJRcER/kUsMThpWeRP8BMn4LXaKhSTggIJPOeYHwISA==
+  dependencies:
+    "@webassemblyjs/ast" "1.7.11"
+    "@webassemblyjs/helper-wasm-bytecode" "1.7.11"
+    "@webassemblyjs/ieee754" "1.7.11"
+    "@webassemblyjs/leb128" "1.7.11"
+    "@webassemblyjs/utf8" "1.7.11"
+
+"@webassemblyjs/wasm-opt@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz#b331e8e7cef8f8e2f007d42c3a36a0580a7d6ca7"
+  integrity sha512-XynkOwQyiRidh0GLua7SkeHvAPXQV/RxsUeERILmAInZegApOUAIJfRuPYe2F7RcjOC9tW3Cb9juPvAC/sCqvg==
+  dependencies:
+    "@webassemblyjs/ast" "1.7.11"
+    "@webassemblyjs/helper-buffer" "1.7.11"
+    "@webassemblyjs/wasm-gen" "1.7.11"
+    "@webassemblyjs/wasm-parser" "1.7.11"
+
+"@webassemblyjs/wasm-parser@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz#6e3d20fa6a3519f6b084ef9391ad58211efb0a1a"
+  integrity sha512-6lmXRTrrZjYD8Ng8xRyvyXQJYUQKYSXhJqXOBLw24rdiXsHAOlvw5PhesjdcaMadU/pyPQOJ5dHreMjBxwnQKg==
+  dependencies:
+    "@webassemblyjs/ast" "1.7.11"
+    "@webassemblyjs/helper-api-error" "1.7.11"
+    "@webassemblyjs/helper-wasm-bytecode" "1.7.11"
+    "@webassemblyjs/ieee754" "1.7.11"
+    "@webassemblyjs/leb128" "1.7.11"
+    "@webassemblyjs/utf8" "1.7.11"
+
+"@webassemblyjs/wast-parser@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz#25bd117562ca8c002720ff8116ef9072d9ca869c"
+  integrity sha512-lEyVCg2np15tS+dm7+JJTNhNWq9yTZvi3qEhAIIOaofcYlUp0UR5/tVqOwa/gXYr3gjwSZqw+/lS9dscyLelbQ==
+  dependencies:
+    "@webassemblyjs/ast" "1.7.11"
+    "@webassemblyjs/floating-point-hex-parser" "1.7.11"
+    "@webassemblyjs/helper-api-error" "1.7.11"
+    "@webassemblyjs/helper-code-frame" "1.7.11"
+    "@webassemblyjs/helper-fsm" "1.7.11"
     "@xtuc/long" "4.2.1"
 
-"@webassemblyjs/wast-printer@1.7.8":
-  version "1.7.8"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.7.8.tgz#e7e965782c1912f6a965f14a53ff43d8ad0403a5"
+"@webassemblyjs/wast-printer@1.7.11":
+  version "1.7.11"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz#c4245b6de242cb50a2cc950174fdbf65c78d7813"
+  integrity sha512-m5vkAsuJ32QpkdkDOUPGSltrg8Cuk3KBx4YrmAGQwCZPRdUHXxG4phIOuuycLemHFr74sWL9Wthqss4fzdzSwg==
   dependencies:
-    "@webassemblyjs/ast" "1.7.8"
-    "@webassemblyjs/wast-parser" "1.7.8"
+    "@webassemblyjs/ast" "1.7.11"
+    "@webassemblyjs/wast-parser" "1.7.11"
     "@xtuc/long" "4.2.1"
 
 "@xtuc/ieee754@^1.2.0":
@@ -415,6 +487,7 @@
 abab@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f"
+  integrity sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==
 
 abbrev@1:
   version "1.1.1"
@@ -433,34 +506,39 @@ acorn-dynamic-import@^3.0.0:
   dependencies:
     acorn "^5.0.0"
 
-acorn-globals@^4.1.0:
+acorn-globals@^4.3.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.0.tgz#e3b6f8da3c1552a95ae627571f7dd6923bb54103"
+  integrity sha512-hMtHj3s5RnuhvHPowpBYvJVj3rAar82JiDQHvGs1zO0l10ocX/xEdBShNHTJaboucJUsScghp74pH3s7EnHHQw==
   dependencies:
     acorn "^6.0.1"
     acorn-walk "^6.0.1"
 
 acorn-jsx@^3.0.0:
   version "3.0.1"
-  resolved "http://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
+  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
+  integrity sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=
   dependencies:
     acorn "^3.0.4"
 
-acorn-walk@^6.0.1:
-  version "6.1.0"
-  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.0.tgz#c957f4a1460da46af4a0388ce28b4c99355b0cbc"
+acorn-walk@^6.0.1, acorn-walk@^6.1.1:
+  version "6.1.1"
+  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913"
+  integrity sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==
 
 acorn@^3.0.4:
   version "3.3.0"
-  resolved "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
+  integrity sha1-ReN/s56No/JbruP/U2niu18iAXo=
 
-acorn@^5.0.0, acorn@^5.3.0, acorn@^5.5.0, acorn@^5.5.3, acorn@^5.6.2, acorn@^5.7.1:
+acorn@^5.0.0, acorn@^5.5.0, acorn@^5.6.2, acorn@^5.7.3:
   version "5.7.3"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
 
-acorn@^6.0.1:
-  version "6.0.2"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.2.tgz#6a459041c320ab17592c6317abbfdf4bbaa98ca4"
+acorn@^6.0.1, acorn@^6.0.4:
+  version "6.0.4"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.4.tgz#77377e7353b72ec5104550aa2d2097a2fd40b754"
+  integrity sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==
 
 address@^1.0.3:
   version "1.0.3"
@@ -480,6 +558,7 @@ ajv-errors@^1.0.0:
 ajv-keywords@^2.1.0:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762"
+  integrity sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=
 
 ajv-keywords@^3.1.0:
   version "3.2.0"
@@ -488,6 +567,7 @@ ajv-keywords@^3.1.0:
 ajv@^5.2.3, ajv@^5.3.0:
   version "5.5.2"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
+  integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=
   dependencies:
     co "^4.6.0"
     fast-deep-equal "^1.0.0"
@@ -503,6 +583,16 @@ ajv@^6.1.0:
     json-schema-traverse "^0.4.1"
     uri-js "^4.2.2"
 
+ajv@^6.5.5:
+  version "6.6.2"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.6.2.tgz#caceccf474bf3fc3ce3b147443711a24063cc30d"
+  integrity sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==
+  dependencies:
+    fast-deep-equal "^2.0.1"
+    fast-json-stable-stringify "^2.0.0"
+    json-schema-traverse "^0.4.1"
+    uri-js "^4.2.2"
+
 alphanum-sort@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
@@ -514,6 +604,7 @@ ansi-colors@^3.0.0:
 ansi-escapes@^3.0.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30"
+  integrity sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==
 
 ansi-html@0.0.7:
   version "0.0.7"
@@ -522,21 +613,33 @@ ansi-html@0.0.7:
 ansi-regex@^2.0.0:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+  integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
 
 ansi-regex@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
+  integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
 
 ansi-styles@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+  integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
 
 ansi-styles@^3.2.1:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+  integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
   dependencies:
     color-convert "^1.9.0"
 
+anymatch@^1.3.0:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a"
+  integrity sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==
+  dependencies:
+    micromatch "^2.1.5"
+    normalize-path "^2.0.0"
+
 anymatch@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@@ -562,14 +665,22 @@ are-we-there-yet@~1.1.2:
 argparse@^1.0.7:
   version "1.0.10"
   resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+  integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
   dependencies:
     sprintf-js "~1.0.2"
 
+arr-diff@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf"
+  integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=
+  dependencies:
+    arr-flatten "^1.0.1"
+
 arr-diff@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
 
-arr-flatten@^1.1.0:
+arr-flatten@^1.0.1, arr-flatten@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
 
@@ -580,10 +691,12 @@ arr-union@^3.1.0:
 array-equal@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
+  integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=
 
 array-filter@~0.0.0:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec"
+  integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw=
 
 array-flatten@1.1.1:
   version "1.1.1"
@@ -593,29 +706,43 @@ array-flatten@^2.1.0:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.1.tgz#426bb9da84090c1838d812c8150af20a8331e296"
 
+array-from@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195"
+  integrity sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=
+
 array-map@~0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662"
+  integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=
 
 array-reduce@~0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b"
+  integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=
 
 array-union@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
+  integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=
   dependencies:
     array-uniq "^1.0.1"
 
 array-uniq@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
+  integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=
+
+array-unique@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53"
+  integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=
 
 array-unique@^0.3.2:
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
 
-arrify@^1.0.0, arrify@^1.0.1:
+arrify@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
 
@@ -630,12 +757,14 @@ asn1.js@^4.0.0:
 asn1@~0.2.3:
   version "0.2.4"
   resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
+  integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
   dependencies:
     safer-buffer "~2.1.0"
 
 assert-plus@1.0.0, assert-plus@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+  integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
 
 assert@^1.1.1:
   version "1.4.1"
@@ -674,12 +803,14 @@ async@^1.5.2:
 async@^2.3.0:
   version "2.6.1"
   resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
+  integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==
   dependencies:
     lodash "^4.17.10"
 
 asynckit@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+  integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
 
 atob@^2.1.1:
   version "2.1.2"
@@ -699,10 +830,12 @@ autoprefixer@^8.6.5:
 aws-sign2@~0.7.0:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
+  integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
 
 aws4@^1.8.0:
   version "1.8.0"
   resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
+  integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
 
 axios@^0.18.0:
   version "0.18.0"
@@ -714,36 +847,36 @@ axios@^0.18.0:
 babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
+  integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=
   dependencies:
     chalk "^1.1.3"
     esutils "^2.0.2"
     js-tokens "^3.0.2"
 
-babel-eslint@^8.2.5:
-  version "8.2.6"
-  resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.6.tgz#6270d0c73205628067c0f7ae1693a9e797acefd9"
+babel-eslint@^10.0.1:
+  version "10.0.1"
+  resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.1.tgz#919681dc099614cd7d31d45c8908695092a1faed"
+  integrity sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==
   dependencies:
-    "@babel/code-frame" "7.0.0-beta.44"
-    "@babel/traverse" "7.0.0-beta.44"
-    "@babel/types" "7.0.0-beta.44"
-    babylon "7.0.0-beta.44"
+    "@babel/code-frame" "^7.0.0"
+    "@babel/parser" "^7.0.0"
+    "@babel/traverse" "^7.0.0"
+    "@babel/types" "^7.0.0"
     eslint-scope "3.7.1"
     eslint-visitor-keys "^1.0.0"
 
 babel-runtime@^6.18.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
+  integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
   dependencies:
     core-js "^2.4.0"
     regenerator-runtime "^0.11.0"
 
-babylon@7.0.0-beta.44:
-  version "7.0.0-beta.44"
-  resolved "http://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz#89159e15e6e30c5096e22d738d8c0af8a0e8ca1d"
-
 balanced-match@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+  integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
 
 base64-js@^1.0.2:
   version "1.3.0"
@@ -768,20 +901,24 @@ batch@0.6.1:
 bcrypt-pbkdf@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
+  integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
   dependencies:
     tweetnacl "^0.14.3"
 
-bfj-node4@^5.2.0:
-  version "5.3.1"
-  resolved "https://registry.yarnpkg.com/bfj-node4/-/bfj-node4-5.3.1.tgz#e23d8b27057f1d0214fc561142ad9db998f26830"
+bfj@^6.1.1:
+  version "6.1.1"
+  resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.1.tgz#05a3b7784fbd72cfa3c22e56002ef99336516c48"
+  integrity sha512-+GUNvzHR4nRyGybQc2WpNJL4MJazMuvf92ueIyA0bIkPRwhhQu3IfZQ2PSoVPpCBJfmoSdOxu5rnotfFLlvYRQ==
   dependencies:
     bluebird "^3.5.1"
     check-types "^7.3.0"
+    hoopy "^0.1.2"
     tryer "^1.0.0"
 
 big.js@^3.1.3:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e"
+  integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==
 
 binary-extensions@^1.0.0:
   version "1.12.0"
@@ -810,6 +947,22 @@ body-parser@1.18.2:
     raw-body "2.3.2"
     type-is "~1.6.15"
 
+body-parser@1.18.3:
+  version "1.18.3"
+  resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4"
+  integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=
+  dependencies:
+    bytes "3.0.0"
+    content-type "~1.0.4"
+    debug "2.6.9"
+    depd "~1.1.2"
+    http-errors "~1.6.3"
+    iconv-lite "0.4.23"
+    on-finished "~2.3.0"
+    qs "6.5.2"
+    raw-body "2.3.3"
+    type-is "~1.6.16"
+
 bonjour@^3.5.0:
   version "3.5.0"
   resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5"
@@ -832,6 +985,15 @@ brace-expansion@^1.0.0, brace-expansion@^1.1.7:
     balanced-match "^1.0.0"
     concat-map "0.0.1"
 
+braces@^1.8.2:
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
+  integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=
+  dependencies:
+    expand-range "^1.8.1"
+    preserve "^0.2.0"
+    repeat-element "^1.1.2"
+
 braces@^2.3.0, braces@^2.3.1:
   version "2.3.2"
   resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
@@ -854,6 +1016,7 @@ brorand@^1.0.1:
 browser-process-hrtime@^0.1.2:
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4"
+  integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==
 
 browser-stdout@1.3.0:
   version "1.3.0"
@@ -862,6 +1025,7 @@ browser-stdout@1.3.0:
 browser-stdout@1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
+  integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
 
 browserify-aes@^1.0.0, browserify-aes@^1.0.4:
   version "1.2.0"
@@ -934,6 +1098,7 @@ browserslist@^4.0.0:
 buffer-from@^1.0.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
+  integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
 
 buffer-indexof@^1.0.0:
   version "1.1.1"
@@ -981,6 +1146,26 @@ cacache@^10.0.4:
     unique-filename "^1.1.0"
     y18n "^4.0.0"
 
+cacache@^11.0.2:
+  version "11.3.1"
+  resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.1.tgz#d09d25f6c4aca7a6d305d141ae332613aa1d515f"
+  integrity sha512-2PEw4cRRDu+iQvBTTuttQifacYjLPhET+SYO/gEFMy8uhi+jlJREDAjSF5FWSdV/Aw5h18caHA7vMTw2c+wDzA==
+  dependencies:
+    bluebird "^3.5.1"
+    chownr "^1.0.1"
+    figgy-pudding "^3.1.0"
+    glob "^7.1.2"
+    graceful-fs "^4.1.11"
+    lru-cache "^4.1.3"
+    mississippi "^3.0.0"
+    mkdirp "^0.5.1"
+    move-concurrently "^1.0.1"
+    promise-inflight "^1.0.1"
+    rimraf "^2.6.2"
+    ssri "^6.0.0"
+    unique-filename "^1.1.0"
+    y18n "^4.0.0"
+
 cache-base@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
@@ -995,9 +1180,10 @@ cache-base@^1.0.1:
     union-value "^1.0.0"
     unset-value "^1.0.0"
 
-cache-loader@^1.2.2:
-  version "1.2.2"
-  resolved "https://registry.yarnpkg.com/cache-loader/-/cache-loader-1.2.2.tgz#6d5c38ded959a09cc5d58190ab5af6f73bd353f5"
+cache-loader@^1.2.5:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/cache-loader/-/cache-loader-1.2.5.tgz#9ab15b0ae5f546f376083a695fc1a75f546cb266"
+  integrity sha512-enWKEQ4kO3YreDFd7AtVRjtJBmNiqh/X9hVDReu0C4qm8gsGmySkwuWtdc+N5O+vq5FzxL1mIZc30NyXCB7o/Q==
   dependencies:
     loader-utils "^1.1.0"
     mkdirp "^0.5.1"
@@ -1011,12 +1197,14 @@ call-me-maybe@^1.0.1:
 caller-path@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
+  integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=
   dependencies:
     callsites "^0.2.0"
 
 callsites@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
+  integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=
 
 camel-case@3.0.x:
   version "3.0.0"
@@ -1025,6 +1213,11 @@ camel-case@3.0.x:
     no-case "^2.2.0"
     upper-case "^1.1.1"
 
+camelcase@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
+  integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo=
+
 camelcase@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
@@ -1049,6 +1242,7 @@ case-sensitive-paths-webpack-plugin@^2.1.2:
 caseless@~0.12.0:
   version "0.12.0"
   resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+  integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
 
 chai-nightwatch@~0.1.x:
   version "0.1.1"
@@ -1057,9 +1251,10 @@ chai-nightwatch@~0.1.x:
     assertion-error "1.0.0"
     deep-eql "0.1.3"
 
-chai@^4.1.2:
+chai@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5"
+  integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==
   dependencies:
     assertion-error "^1.1.0"
     check-error "^1.0.2"
@@ -1070,7 +1265,8 @@ chai@^4.1.2:
 
 chalk@^1.1.3:
   version "1.1.3"
-  resolved "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+  integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
   dependencies:
     ansi-styles "^2.2.1"
     escape-string-regexp "^1.0.2"
@@ -1081,6 +1277,7 @@ chalk@^1.1.3:
 chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1:
   version "2.4.1"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
+  integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==
   dependencies:
     ansi-styles "^3.2.1"
     escape-string-regexp "^1.0.5"
@@ -1089,6 +1286,7 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4
 chardet@^0.4.0:
   version "0.4.2"
   resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
+  integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=
 
 check-error@^1.0.2:
   version "1.0.2"
@@ -1098,6 +1296,22 @@ check-types@^7.3.0:
   version "7.4.0"
   resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.4.0.tgz#0378ec1b9616ec71f774931a3c6516fad8c152f4"
 
+chokidar@^1.6.1:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
+  integrity sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=
+  dependencies:
+    anymatch "^1.3.0"
+    async-each "^1.0.0"
+    glob-parent "^2.0.0"
+    inherits "^2.0.1"
+    is-binary-path "^1.0.0"
+    is-glob "^2.0.0"
+    path-is-absolute "^1.0.0"
+    readdirp "^2.0.0"
+  optionalDependencies:
+    fsevents "^1.0.0"
+
 chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.4:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26"
@@ -1151,6 +1365,7 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
 circular-json@^0.3.1:
   version "0.3.3"
   resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
+  integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==
 
 class-utils@^0.3.5:
   version "0.3.6"
@@ -1170,16 +1385,19 @@ clean-css@4.2.x:
 cli-cursor@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
+  integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
   dependencies:
     restore-cursor "^2.0.0"
 
 cli-spinners@^1.1.0:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a"
+  integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==
 
 cli-width@^2.0.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
+  integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=
 
 clipboardy@^1.2.3:
   version "1.2.3"
@@ -1188,6 +1406,15 @@ clipboardy@^1.2.3:
     arch "^2.1.0"
     execa "^0.8.0"
 
+cliui@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
+  integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=
+  dependencies:
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+    wrap-ansi "^2.0.0"
+
 cliui@^4.0.0, cliui@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49"
@@ -1199,10 +1426,12 @@ cliui@^4.0.0, cliui@^4.1.0:
 clone@^1.0.2:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
+  integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
 
 co@^4.6.0:
   version "4.6.0"
   resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+  integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
 
 co@~3.0.6:
   version "3.0.6"
@@ -1228,12 +1457,14 @@ collection-visit@^1.0.0:
 color-convert@^1.9.0, color-convert@^1.9.1:
   version "1.9.3"
   resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+  integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
   dependencies:
     color-name "1.1.3"
 
 color-name@1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+  integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
 
 color-name@^1.0.0:
   version "1.1.4"
@@ -1257,21 +1488,17 @@ colors@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
 
-combined-stream@1.0.6:
-  version "1.0.6"
-  resolved "http://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818"
-  dependencies:
-    delayed-stream "~1.0.0"
-
-combined-stream@~1.0.6:
+combined-stream@^1.0.6, combined-stream@~1.0.6:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828"
+  integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==
   dependencies:
     delayed-stream "~1.0.0"
 
 commander@2.15.1:
   version "2.15.1"
-  resolved "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
+  integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==
 
 commander@2.17.x, commander@~2.17.1:
   version "2.17.1"
@@ -1283,17 +1510,19 @@ commander@2.9.0:
   dependencies:
     graceful-readlink ">= 1.0.0"
 
-commander@^2.12.1, commander@^2.13.0:
+commander@^2.12.1:
   version "2.18.0"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970"
 
-commander@~2.13.0:
-  version "2.13.0"
-  resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
+commander@^2.18.0:
+  version "2.19.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
+  integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
 
 commondir@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+  integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
 
 component-emitter@^1.2.1:
   version "1.2.1"
@@ -1320,6 +1549,7 @@ compression@^1.5.2:
 concat-map@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
 
 concat-stream@1.6.2, concat-stream@^1.5.0, concat-stream@^1.6.0:
   version "1.6.2"
@@ -1357,6 +1587,7 @@ constants-browserify@^1.0.0:
 contains-path@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a"
+  integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=
 
 content-disposition@0.5.2:
   version "0.5.2"
@@ -1389,9 +1620,10 @@ copy-descriptor@^0.1.0:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
 
-copy-webpack-plugin@^4.5.2:
-  version "4.5.2"
-  resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.5.2.tgz#d53444a8fea2912d806e78937390ddd7e632ee5c"
+copy-webpack-plugin@^4.6.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz#e7f40dd8a68477d405dd1b7a854aae324b158bae"
+  integrity sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==
   dependencies:
     cacache "^10.0.4"
     find-cache-dir "^1.0.0"
@@ -1402,6 +1634,11 @@ copy-webpack-plugin@^4.5.2:
     p-limit "^1.0.0"
     serialize-javascript "^1.4.0"
 
+core-js@^0.8.3:
+  version "0.8.4"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-0.8.4.tgz#c22665f1e0d1b9c3c5e1b08dabd1f108695e4fcf"
+  integrity sha1-wiZl8eDRucPF4bCNq9HxCGleT88=
+
 core-js@^2.4.0:
   version "2.5.7"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
@@ -1409,6 +1646,7 @@ core-js@^2.4.0:
 core-util-is@1.0.2, core-util-is@~1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+  integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
 
 cosmiconfig@^4.0.0:
   version "4.0.0"
@@ -1466,6 +1704,7 @@ cross-spawn@^5.0.1, cross-spawn@^5.1.0:
 cross-spawn@^6.0.0:
   version "6.0.5"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
+  integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
   dependencies:
     nice-try "^1.0.4"
     path-key "^2.0.1"
@@ -1500,15 +1739,16 @@ css-declaration-sorter@^4.0.1:
     postcss "^7.0.1"
     timsort "^0.3.0"
 
-css-loader@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-1.0.0.tgz#9f46aaa5ca41dbe31860e3b62b8e23c42916bf56"
+css-loader@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-1.0.1.tgz#6885bb5233b35ec47b006057da01cc640b6b79fe"
+  integrity sha512-+ZHAZm/yqvJ2kDtPne3uX0C+Vr3Zn5jFn2N4HywtS5ujwvsVkyg0VArEXpl3BgczDA8anieki1FIzhchX4yrDw==
   dependencies:
     babel-code-frame "^6.26.0"
     css-selector-tokenizer "^0.7.0"
     icss-utils "^2.1.0"
     loader-utils "^1.0.2"
-    lodash.camelcase "^4.3.0"
+    lodash "^4.17.11"
     postcss "^6.0.23"
     postcss-modules-extract-imports "^1.2.0"
     postcss-modules-local-by-default "^1.2.0"
@@ -1577,6 +1817,11 @@ cssesc@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
 
+cssesc@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703"
+  integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==
+
 cssnano-preset-default@^4.0.0, cssnano-preset-default@^4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.2.tgz#1de3f27e73b7f0fbf87c1d7fd7a63ae980ac3774"
@@ -1612,6 +1857,42 @@ cssnano-preset-default@^4.0.0, cssnano-preset-default@^4.0.2:
     postcss-svgo "^4.0.1"
     postcss-unique-selectors "^4.0.1"
 
+cssnano-preset-default@^4.0.5:
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.5.tgz#d1756c0259d98ad311e601ba76e95c60f6771ac1"
+  integrity sha512-f1uhya0ZAjPYtDD58QkBB0R+uYdzHPei7cDxJyQQIHt5acdhyGXaSXl2nDLzWHLwGFbZcHxQtkJS8mmNwnxTvw==
+  dependencies:
+    css-declaration-sorter "^4.0.1"
+    cssnano-util-raw-cache "^4.0.1"
+    postcss "^7.0.0"
+    postcss-calc "^7.0.0"
+    postcss-colormin "^4.0.2"
+    postcss-convert-values "^4.0.1"
+    postcss-discard-comments "^4.0.1"
+    postcss-discard-duplicates "^4.0.2"
+    postcss-discard-empty "^4.0.1"
+    postcss-discard-overridden "^4.0.1"
+    postcss-merge-longhand "^4.0.9"
+    postcss-merge-rules "^4.0.2"
+    postcss-minify-font-values "^4.0.2"
+    postcss-minify-gradients "^4.0.1"
+    postcss-minify-params "^4.0.1"
+    postcss-minify-selectors "^4.0.1"
+    postcss-normalize-charset "^4.0.1"
+    postcss-normalize-display-values "^4.0.1"
+    postcss-normalize-positions "^4.0.1"
+    postcss-normalize-repeat-style "^4.0.1"
+    postcss-normalize-string "^4.0.1"
+    postcss-normalize-timing-functions "^4.0.1"
+    postcss-normalize-unicode "^4.0.1"
+    postcss-normalize-url "^4.0.1"
+    postcss-normalize-whitespace "^4.0.1"
+    postcss-ordered-values "^4.1.1"
+    postcss-reduce-initial "^4.0.2"
+    postcss-reduce-transforms "^4.0.1"
+    postcss-svgo "^4.0.1"
+    postcss-unique-selectors "^4.0.1"
+
 cssnano-util-get-arguments@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f"
@@ -1639,19 +1920,31 @@ cssnano@^4.0.0:
     is-resolvable "^1.0.0"
     postcss "^7.0.0"
 
+cssnano@^4.1.7:
+  version "4.1.7"
+  resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.7.tgz#0bf112294bec103ab5f68d3f805732c8325a0b1b"
+  integrity sha512-AiXL90l+MDuQmRNyypG2P7ux7K4XklxYzNNUd5HXZCNcH8/N9bHPcpN97v8tXgRVeFL/Ed8iP8mVmAAu0ZpT7A==
+  dependencies:
+    cosmiconfig "^5.0.0"
+    cssnano-preset-default "^4.0.5"
+    is-resolvable "^1.0.0"
+    postcss "^7.0.0"
+
 csso@^3.5.0:
   version "3.5.1"
   resolved "https://registry.yarnpkg.com/csso/-/csso-3.5.1.tgz#7b9eb8be61628973c1b261e169d2f024008e758b"
   dependencies:
     css-tree "1.0.0-alpha.29"
 
-cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
+cssom@0.3.x, cssom@^0.3.4:
   version "0.3.4"
   resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.4.tgz#8cd52e8a3acfd68d3aed38ee0a640177d2f9d797"
+  integrity sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==
 
-cssstyle@^1.0.0:
+cssstyle@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.1.1.tgz#18b038a9c44d65f7a8e428a653b9f6fe42faf5fb"
+  integrity sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog==
   dependencies:
     cssom "0.3.x"
 
@@ -1662,6 +1955,7 @@ cyclist@~0.2.2:
 dashdash@^1.12.0:
   version "1.14.1"
   resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+  integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
   dependencies:
     assert-plus "^1.0.0"
 
@@ -1669,12 +1963,13 @@ data-uri-to-buffer@1:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835"
 
-data-urls@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.0.1.tgz#d416ac3896918f29ca84d81085bc3705834da579"
+data-urls@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe"
+  integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==
   dependencies:
     abab "^2.0.0"
-    whatwg-mimetype "^2.1.0"
+    whatwg-mimetype "^2.2.0"
     whatwg-url "^7.0.0"
 
 date-now@^0.1.4:
@@ -1684,8 +1979,9 @@ date-now@^0.1.4:
 de-indent@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
+  integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=
 
-debug@2, debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9:
+debug@2, debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
   version "2.6.9"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
   dependencies:
@@ -1703,9 +1999,17 @@ debug@3.1.0, debug@=3.1.0:
   dependencies:
     ms "2.0.0"
 
-debug@^3.1.0:
-  version "3.2.5"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.5.tgz#c2418fbfd7a29f4d4f70ff4cea604d4b64c46407"
+debug@^3.1.0, debug@^3.2.5:
+  version "3.2.6"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
+  integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
+  dependencies:
+    ms "^2.1.1"
+
+debug@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87"
+  integrity sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==
   dependencies:
     ms "^2.1.1"
 
@@ -1746,6 +2050,7 @@ deep-extend@^0.6.0:
 deep-is@~0.1.3:
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+  integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
 
 deepmerge@^1.5.2:
   version "1.5.2"
@@ -1765,12 +2070,14 @@ default-gateway@^2.6.0:
 defaults@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
+  integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=
   dependencies:
     clone "^1.0.2"
 
 define-properties@^1.1.2:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
+  integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
   dependencies:
     object-keys "^1.0.12"
 
@@ -1801,18 +2108,6 @@ degenerator@~1.0.2:
     escodegen "1.x.x"
     esprima "3.x.x"
 
-del@^2.0.2:
-  version "2.2.2"
-  resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
-  dependencies:
-    globby "^5.0.0"
-    is-path-cwd "^1.0.0"
-    is-path-in-cwd "^1.0.0"
-    object-assign "^4.0.1"
-    pify "^2.0.0"
-    pinkie-promise "^2.0.0"
-    rimraf "^2.2.8"
-
 del@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
@@ -1827,6 +2122,7 @@ del@^3.0.0:
 delayed-stream@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+  integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
 
 delegates@^1.0.0:
   version "1.0.0"
@@ -1863,7 +2159,7 @@ diff@1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf"
 
-diff@3.5.0, diff@^3.2.0:
+diff@3.5.0, diff@^3.2.0, diff@^3.5.0:
   version "3.5.0"
   resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
 
@@ -1902,6 +2198,7 @@ dns-txt@^2.0.2:
 doctrine@1.5.0:
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
+  integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=
   dependencies:
     esutils "^2.0.2"
     isarray "^1.0.0"
@@ -1909,6 +2206,7 @@ doctrine@1.5.0:
 doctrine@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
+  integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==
   dependencies:
     esutils "^2.0.2"
 
@@ -1918,6 +2216,11 @@ dom-converter@~0.1:
   dependencies:
     utila "~0.3"
 
+dom-event-types@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/dom-event-types/-/dom-event-types-1.0.0.tgz#5830a0a29e1bf837fe50a70cd80a597232813cae"
+  integrity sha512-2G2Vwi2zXTHBGqXHsJ4+ak/iP0N8Ar+G8a7LiD2oup5o4sQWytwqqrZu/O6hIMV0KMID2PL69OhpshLO0n7UJQ==
+
 dom-serializer@0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
@@ -1925,6 +2228,11 @@ dom-serializer@0:
     domelementtype "~1.1.1"
     entities "~1.1.1"
 
+dom-walk@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018"
+  integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=
+
 domain-browser@^1.1.1:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
@@ -1940,6 +2248,7 @@ domelementtype@~1.1.1:
 domexception@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
+  integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==
   dependencies:
     webidl-conversions "^4.0.2"
 
@@ -1991,10 +2300,12 @@ duplexify@^3.4.2, duplexify@^3.6.0:
 easy-stack@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/easy-stack/-/easy-stack-1.0.0.tgz#12c91b3085a37f0baa336e9486eac4bf94e3e788"
+  integrity sha1-EskbMIWjfwuqM26UhurEv5Tj54g=
 
 ecc-jsbn@~0.1.1:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
+  integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
   dependencies:
     jsbn "~0.1.0"
     safer-buffer "^2.1.0"
@@ -2007,9 +2318,10 @@ ejs@2.5.7:
   version "2.5.7"
   resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a"
 
-ejs@^2.5.7:
+ejs@^2.6.1:
   version "2.6.1"
   resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0"
+  integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==
 
 electron-to-chromium@^1.3.47, electron-to-chromium@^1.3.62:
   version "1.3.72"
@@ -2030,6 +2342,7 @@ elliptic@^6.0.0:
 emojis-list@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
+  integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
 
 encodeurl@~1.0.2:
   version "1.0.2"
@@ -2074,6 +2387,7 @@ error-stack-parser@^2.0.0:
 es-abstract@^1.4.3, es-abstract@^1.5.1, es-abstract@^1.6.1:
   version "1.12.0"
   resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165"
+  integrity sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==
   dependencies:
     es-to-primitive "^1.1.1"
     function-bind "^1.1.1"
@@ -2084,6 +2398,7 @@ es-abstract@^1.4.3, es-abstract@^1.5.1, es-abstract@^1.6.1:
 es-to-primitive@^1.1.1:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377"
+  integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==
   dependencies:
     is-callable "^1.1.4"
     is-date-object "^1.0.1"
@@ -2097,7 +2412,7 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
 
-escodegen@1.x.x, escodegen@^1.9.1:
+escodegen@1.x.x, escodegen@^1.11.0:
   version "1.11.0"
   resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589"
   dependencies:
@@ -2111,17 +2426,20 @@ escodegen@1.x.x, escodegen@^1.9.1:
 eslint-config-standard@^12.0.0-alpha.0:
   version "12.0.0"
   resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz#638b4c65db0bd5a41319f96bba1f15ddad2107d9"
+  integrity sha512-COUz8FnXhqFitYj4DTqHzidjIL/t4mumGZto5c7DrBpvWoie+Sn3P4sLEzUGeYhRElWuFEf8K1S1EfvD1vixCQ==
 
 eslint-import-resolver-node@^0.3.1:
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a"
+  integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==
   dependencies:
     debug "^2.6.9"
     resolve "^1.5.0"
 
-eslint-loader@^2.0.0:
+eslint-loader@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-2.1.1.tgz#2a9251523652430bfdd643efdb0afc1a2a89546a"
+  integrity sha512-1GrJFfSevQdYpoDzx8mEE2TDWsb/zmFuY09l6hURg1AeFIKQOvZ+vH0UPjzmd1CZIbfTV5HUkMeBmFiDBkgIsQ==
   dependencies:
     loader-fs-cache "^1.0.0"
     loader-utils "^1.0.2"
@@ -2132,6 +2450,7 @@ eslint-loader@^2.0.0:
 eslint-module-utils@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz#b270362cd88b1a48ad308976ce7fa54e98411746"
+  integrity sha1-snA2LNiLGkitMIl2zn+lTphBF0Y=
   dependencies:
     debug "^2.6.8"
     pkg-dir "^1.0.0"
@@ -2139,6 +2458,7 @@ eslint-module-utils@^2.2.0:
 eslint-plugin-import@^2.11.0:
   version "2.14.0"
   resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz#6b17626d2e3e6ad52cfce8807a845d15e22111a8"
+  integrity sha512-FpuRtniD/AY6sXByma2Wr0TXvXJ4nA/2/04VPlfpmUDPOpOY264x+ILiwnrk/k4RINgDAyFZByxqPUbSQ5YE7g==
   dependencies:
     contains-path "^0.1.0"
     debug "^2.6.8"
@@ -2154,6 +2474,7 @@ eslint-plugin-import@^2.11.0:
 eslint-plugin-node@^6.0.1:
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-6.0.1.tgz#bf19642298064379315d7a4b2a75937376fa05e4"
+  integrity sha512-Q/Cc2sW1OAISDS+Ji6lZS2KV4b7ueA/WydVWd1BECTQwVvfQy5JAi3glhINoKzoMnfnuRgNP+ZWKrGAbp3QDxw==
   dependencies:
     ignore "^3.3.6"
     minimatch "^3.0.4"
@@ -2163,26 +2484,31 @@ eslint-plugin-node@^6.0.1:
 eslint-plugin-promise@^3.7.0:
   version "3.8.0"
   resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.8.0.tgz#65ebf27a845e3c1e9d6f6a5622ddd3801694b621"
+  integrity sha512-JiFL9UFR15NKpHyGii1ZcvmtIqa3UTwiDAGb8atSffe43qJ3+1czVGN6UtkklpcJ2DVnqvTMzEKRaJdBkAL2aQ==
 
 eslint-plugin-standard@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz#2a9e21259ba4c47c02d53b2d0c9135d4b1022d47"
+  integrity sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w==
 
-eslint-plugin-typescript@^0.12.0:
-  version "0.12.0"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-typescript/-/eslint-plugin-typescript-0.12.0.tgz#e23d58cb27fe28e89fc641a1f20e8d862cb99aef"
+eslint-plugin-typescript@^0.14.0:
+  version "0.14.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-typescript/-/eslint-plugin-typescript-0.14.0.tgz#068549c3f4c7f3f85d88d398c29fa96bf500884c"
+  integrity sha512-2u1WnnDF2mkWWgU1lFQ2RjypUlmRoBEvQN02y9u+IL12mjWlkKFGEBnVsjs9Y8190bfPQCvWly1c2rYYUSOxWw==
   dependencies:
     requireindex "~1.1.0"
 
-eslint-plugin-vue@^4.5.0:
+eslint-plugin-vue@^4.7.1:
   version "4.7.1"
   resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-4.7.1.tgz#c829b9fc62582c1897b5a0b94afd44ecca511e63"
+  integrity sha512-esETKhVMI7Vdli70Wt4bvAwnZBJeM0pxVX9Yb0wWKxdCJc2EADalVYK/q2FzMw8oKN0wPMdqVCKS8kmR89recA==
   dependencies:
     vue-eslint-parser "^2.0.3"
 
 eslint-scope@3.7.1:
   version "3.7.1"
   resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
+  integrity sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=
   dependencies:
     esrecurse "^4.1.0"
     estraverse "^4.1.1"
@@ -2190,6 +2516,7 @@ eslint-scope@3.7.1:
 eslint-scope@^3.7.1:
   version "3.7.3"
   resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535"
+  integrity sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==
   dependencies:
     esrecurse "^4.1.0"
     estraverse "^4.1.1"
@@ -2204,10 +2531,12 @@ eslint-scope@^4.0.0:
 eslint-visitor-keys@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
+  integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==
 
 eslint@^4.19.1:
   version "4.19.1"
-  resolved "http://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300"
+  integrity sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==
   dependencies:
     ajv "^5.3.0"
     babel-code-frame "^6.22.0"
@@ -2251,6 +2580,7 @@ eslint@^4.19.1:
 espree@^3.5.2, espree@^3.5.4:
   version "3.5.4"
   resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7"
+  integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==
   dependencies:
     acorn "^5.5.0"
     acorn-jsx "^3.0.0"
@@ -2262,16 +2592,19 @@ esprima@3.x.x, esprima@^3.1.3:
 esprima@^4.0.0:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+  integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
 
 esquery@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708"
+  integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==
   dependencies:
     estraverse "^4.0.0"
 
 esrecurse@^4.1.0:
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
+  integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==
   dependencies:
     estraverse "^4.1.0"
 
@@ -2282,6 +2615,7 @@ estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
 esutils@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
+  integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=
 
 etag@~1.8.1:
   version "1.8.1"
@@ -2290,6 +2624,7 @@ etag@~1.8.1:
 event-pubsub@4.3.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/event-pubsub/-/event-pubsub-4.3.0.tgz#f68d816bc29f1ec02c539dc58c8dd40ce72cb36e"
+  integrity sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==
 
 eventemitter3@^3.0.0:
   version "3.1.0"
@@ -2299,11 +2634,12 @@ events@^1.0.0:
   version "1.1.1"
   resolved "http://registry.npmjs.org/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
 
-eventsource@0.1.6:
-  version "0.1.6"
-  resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232"
+eventsource@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0"
+  integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==
   dependencies:
-    original ">=0.0.5"
+    original "^1.0.0"
 
 evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
   version "1.0.3"
@@ -2315,6 +2651,7 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
 execa@^0.10.0:
   version "0.10.0"
   resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50"
+  integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==
   dependencies:
     cross-spawn "^6.0.0"
     get-stream "^3.0.0"
@@ -2327,6 +2664,7 @@ execa@^0.10.0:
 execa@^0.7.0:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
+  integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=
   dependencies:
     cross-spawn "^5.0.1"
     get-stream "^3.0.0"
@@ -2348,6 +2686,26 @@ execa@^0.8.0:
     signal-exit "^3.0.0"
     strip-eof "^1.0.0"
 
+execa@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
+  integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
+  dependencies:
+    cross-spawn "^6.0.0"
+    get-stream "^4.0.0"
+    is-stream "^1.1.0"
+    npm-run-path "^2.0.0"
+    p-finally "^1.0.0"
+    signal-exit "^3.0.0"
+    strip-eof "^1.0.0"
+
+expand-brackets@^0.1.4:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
+  integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=
+  dependencies:
+    is-posix-bracket "^0.1.0"
+
 expand-brackets@^2.1.4:
   version "2.1.4"
   resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
@@ -2360,6 +2718,13 @@ expand-brackets@^2.1.4:
     snapdragon "^0.8.1"
     to-regex "^3.0.1"
 
+expand-range@^1.8.1:
+  version "1.8.2"
+  resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337"
+  integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=
+  dependencies:
+    fill-range "^2.1.0"
+
 express@^4.16.2:
   version "4.16.3"
   resolved "http://registry.npmjs.org/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53"
@@ -2395,6 +2760,42 @@ express@^4.16.2:
     utils-merge "1.0.1"
     vary "~1.1.2"
 
+express@^4.16.3:
+  version "4.16.4"
+  resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e"
+  integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==
+  dependencies:
+    accepts "~1.3.5"
+    array-flatten "1.1.1"
+    body-parser "1.18.3"
+    content-disposition "0.5.2"
+    content-type "~1.0.4"
+    cookie "0.3.1"
+    cookie-signature "1.0.6"
+    debug "2.6.9"
+    depd "~1.1.2"
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    etag "~1.8.1"
+    finalhandler "1.1.1"
+    fresh "0.5.2"
+    merge-descriptors "1.0.1"
+    methods "~1.1.2"
+    on-finished "~2.3.0"
+    parseurl "~1.3.2"
+    path-to-regexp "0.1.7"
+    proxy-addr "~2.0.4"
+    qs "6.5.2"
+    range-parser "~1.2.0"
+    safe-buffer "5.1.2"
+    send "0.16.2"
+    serve-static "1.13.2"
+    setprototypeof "1.1.0"
+    statuses "~1.4.0"
+    type-is "~1.6.16"
+    utils-merge "1.0.1"
+    vary "~1.1.2"
+
 extend-shallow@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
@@ -2414,12 +2815,20 @@ extend@3, extend@~3.0.0, extend@~3.0.2:
 
 external-editor@^2.0.4:
   version "2.2.0"
-  resolved "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5"
+  resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5"
+  integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==
   dependencies:
     chardet "^0.4.0"
     iconv-lite "^0.4.17"
     tmp "^0.0.33"
 
+extglob@^0.3.1:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
+  integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=
+  dependencies:
+    is-extglob "^1.0.0"
+
 extglob@^2.0.4:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
@@ -2445,14 +2854,17 @@ extract-zip@^1.6.7:
 extsprintf@1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
+  integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
 
 extsprintf@^1.2.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
+  integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
 
 fast-deep-equal@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
+  integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=
 
 fast-deep-equal@^2.0.1:
   version "2.0.1"
@@ -2472,10 +2884,12 @@ fast-glob@^2.0.2:
 fast-json-stable-stringify@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
+  integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
 
 fast-levenshtein@~2.0.4:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+  integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
 
 fastparse@^1.1.1:
   version "1.1.1"
@@ -2487,9 +2901,10 @@ faye-websocket@^0.10.0:
   dependencies:
     websocket-driver ">=0.5.1"
 
-faye-websocket@~0.11.0:
+faye-websocket@~0.11.1:
   version "0.11.1"
   resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.1.tgz#f0efe18c4f56e4f40afc7e06c719fd5ee6188f38"
+  integrity sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=
   dependencies:
     websocket-driver ">=0.5.1"
 
@@ -2499,37 +2914,57 @@ fd-slicer@~1.0.1:
   dependencies:
     pend "~1.2.0"
 
-figgy-pudding@^3.5.1:
+figgy-pudding@^3.1.0, figgy-pudding@^3.5.1:
   version "3.5.1"
   resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
 
 figures@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
+  integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=
   dependencies:
     escape-string-regexp "^1.0.5"
 
 file-entry-cache@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361"
+  integrity sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=
   dependencies:
     flat-cache "^1.2.1"
     object-assign "^4.0.1"
 
-file-loader@^1.1.11:
-  version "1.1.11"
-  resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-1.1.11.tgz#6fe886449b0f2a936e43cabaac0cdbfb369506f8"
+file-loader@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-2.0.0.tgz#39749c82f020b9e85901dcff98e8004e6401cfde"
+  integrity sha512-YCsBfd1ZGCyonOKLxPiKPdu+8ld9HAaMEvJewzz+b2eTF7uL5Zm/HdBF6FjCrpCMRq25Mi0U1gl4pwn2TlH7hQ==
   dependencies:
     loader-utils "^1.0.2"
-    schema-utils "^0.4.5"
+    schema-utils "^1.0.0"
 
 file-uri-to-path@1:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
 
-filesize@^3.5.11:
+filename-regex@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
+  integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=
+
+filesize@^3.6.1:
   version "3.6.1"
   resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317"
+  integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==
+
+fill-range@^2.1.0:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565"
+  integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==
+  dependencies:
+    is-number "^2.1.0"
+    isobject "^2.0.0"
+    randomatic "^3.0.0"
+    repeat-element "^1.1.2"
+    repeat-string "^1.5.2"
 
 fill-range@^4.0.0:
   version "4.0.0"
@@ -2555,6 +2990,7 @@ finalhandler@1.1.1:
 find-cache-dir@^0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9"
+  integrity sha1-yN765XyKUqinhPnjHFfHQumToLk=
   dependencies:
     commondir "^1.0.1"
     mkdirp "^0.5.1"
@@ -2568,9 +3004,19 @@ find-cache-dir@^1.0.0:
     make-dir "^1.0.0"
     pkg-dir "^2.0.0"
 
+find-cache-dir@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.0.0.tgz#4c1faed59f45184530fb9d7fa123a4d04a98472d"
+  integrity sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==
+  dependencies:
+    commondir "^1.0.1"
+    make-dir "^1.0.0"
+    pkg-dir "^3.0.0"
+
 find-up@^1.0.0:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
+  integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=
   dependencies:
     path-exists "^2.0.0"
     pinkie-promise "^2.0.0"
@@ -2588,12 +3034,13 @@ find-up@^3.0.0:
     locate-path "^3.0.0"
 
 flat-cache@^1.2.1:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481"
+  version "1.3.4"
+  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.4.tgz#2c2ef77525cc2929007dfffa1dd314aa9c9dee6f"
+  integrity sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==
   dependencies:
     circular-json "^0.3.1"
-    del "^2.0.2"
     graceful-fs "^4.1.2"
+    rimraf "~2.6.2"
     write "^0.2.1"
 
 flatten@^1.0.2:
@@ -2619,35 +3066,41 @@ follow-redirects@^1.3.0:
   dependencies:
     debug "^3.1.0"
 
-for-in@^1.0.2:
+for-in@^1.0.1, for-in@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
 
+for-own@^0.1.4:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce"
+  integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=
+  dependencies:
+    for-in "^1.0.1"
+
 forever-agent@~0.6.1:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+  integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
 
-fork-ts-checker-webpack-plugin@^0.4.4:
-  version "0.4.9"
-  resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-0.4.9.tgz#78607899d4411fdc6faeca5b4db7654c9d8d28a2"
+fork-ts-checker-webpack-plugin@^0.5.0:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-0.5.2.tgz#a73b3630bd0a69409a6e4824e54c03a62fe82d8f"
+  integrity sha512-a5IG+xXyKnpruI0CP/anyRLAoxWtp3lzdG6flxicANnoSzz64b12dJ7ASAVRrI2OaWwZR2JyBaMHFQqInhWhIw==
   dependencies:
     babel-code-frame "^6.22.0"
     chalk "^2.4.1"
     chokidar "^2.0.4"
-    lodash.endswith "^4.2.1"
-    lodash.isfunction "^3.0.8"
-    lodash.isstring "^4.0.1"
-    lodash.startswith "^4.2.1"
+    micromatch "^3.1.10"
     minimatch "^3.0.4"
-    resolve "^1.5.0"
     tapable "^1.0.0"
 
 form-data@~2.3.2:
-  version "2.3.2"
-  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099"
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
+  integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
   dependencies:
     asynckit "^0.4.0"
-    combined-stream "1.0.6"
+    combined-stream "^1.0.6"
     mime-types "^2.1.12"
 
 forwarded@~0.1.2:
@@ -2679,9 +3132,10 @@ from2@^2.1.0:
     inherits "^2.0.1"
     readable-stream "^2.0.0"
 
-fs-extra@^6.0.1:
-  version "6.0.1"
-  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-6.0.1.tgz#8abc128f7946e310135ddc93b98bddb410e7a34b"
+fs-extra@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
+  integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
   dependencies:
     graceful-fs "^4.1.2"
     jsonfile "^4.0.0"
@@ -2705,10 +3159,12 @@ fs-write-stream-atomic@^1.0.8:
 fs.realpath@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+  integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
 
-fsevents@^1.2.2:
+fsevents@^1.0.0, fsevents@^1.2.2:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426"
+  integrity sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==
   dependencies:
     nan "^2.9.2"
     node-pre-gyp "^0.10.0"
@@ -2723,10 +3179,12 @@ ftp@~0.3.10:
 function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+  integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
 
 functional-red-black-tree@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+  integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
 
 gauge@~2.7.3:
   version "2.7.4"
@@ -2752,6 +3210,14 @@ get-func-name@^2.0.0:
 get-stream@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
+  integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
+
+get-stream@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
+  integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
+  dependencies:
+    pump "^3.0.0"
 
 get-uri@2:
   version "2.0.2"
@@ -2771,9 +3237,25 @@ get-value@^2.0.3, get-value@^2.0.6:
 getpass@^0.1.1:
   version "0.1.7"
   resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+  integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
   dependencies:
     assert-plus "^1.0.0"
 
+glob-base@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
+  integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=
+  dependencies:
+    glob-parent "^2.0.0"
+    is-glob "^2.0.0"
+
+glob-parent@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
+  integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=
+  dependencies:
+    is-glob "^2.0.0"
+
 glob-parent@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
@@ -2799,6 +3281,7 @@ glob@7.0.5:
 glob@7.1.2:
   version "7.1.2"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
+  integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==
   dependencies:
     fs.realpath "^1.0.0"
     inflight "^1.0.4"
@@ -2810,6 +3293,7 @@ glob@7.1.2:
 glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
   version "7.1.3"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
+  integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
   dependencies:
     fs.realpath "^1.0.0"
     inflight "^1.0.4"
@@ -2818,20 +3302,18 @@ glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
-globals@^11.0.1, globals@^11.1.0:
-  version "11.7.0"
-  resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673"
-
-globby@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
+global@^4.3.2:
+  version "4.3.2"
+  resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f"
+  integrity sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=
   dependencies:
-    array-union "^1.0.1"
-    arrify "^1.0.0"
-    glob "^7.0.3"
-    object-assign "^4.0.1"
-    pify "^2.0.0"
-    pinkie-promise "^2.0.0"
+    min-document "^2.19.0"
+    process "~0.5.1"
+
+globals@^11.0.1, globals@^11.1.0:
+  version "11.9.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-11.9.0.tgz#bde236808e987f290768a93d065060d78e6ab249"
+  integrity sha512-5cJVtyXWH8PiJPVLZzzoIizXx944O4OmRro5MWKx5fT4MgcN7OfaMutPeaTdJCCURwbWdhhcCWcKIffPnmTzBg==
 
 globby@^6.1.0:
   version "6.1.0"
@@ -2866,10 +3348,15 @@ globby@^8.0.1:
     pify "^3.0.0"
     slash "^1.0.0"
 
-graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6:
+graceful-fs@^4.1.11, graceful-fs@^4.1.6:
   version "4.1.11"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
 
+graceful-fs@^4.1.2:
+  version "4.1.15"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
+  integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
+
 "graceful-readlink@>= 1.0.0":
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
@@ -2877,14 +3364,16 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6:
 growl@1.10.5:
   version "1.10.5"
   resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
+  integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
 
 growl@1.9.2:
   version "1.9.2"
   resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f"
 
-gzip-size@^4.1.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-4.1.0.tgz#8ae096257eabe7d69c45be2b67c448124ffb517c"
+gzip-size@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.0.0.tgz#a55ecd99222f4c48fd8c01c625ce3b349d0a0e80"
+  integrity sha512-5iI7omclyqrnWw4XbXAmGhPsABkSIDQonv2K0h61lybgofWa6iZyvrI3r2zsJH4P8Nb64fFVzlvfhs0g7BBxAA==
   dependencies:
     duplexer "^0.1.1"
     pify "^3.0.0"
@@ -2896,17 +3385,20 @@ handle-thing@^1.2.5:
 har-schema@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
+  integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
 
 har-validator@~5.1.0:
-  version "5.1.0"
-  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.0.tgz#44657f5688a22cfd4b72486e81b3a3fb11742c29"
+  version "5.1.3"
+  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
+  integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
   dependencies:
-    ajv "^5.3.0"
+    ajv "^6.5.5"
     har-schema "^2.0.0"
 
 has-ansi@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+  integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=
   dependencies:
     ansi-regex "^2.0.0"
 
@@ -2917,10 +3409,12 @@ has-flag@^1.0.0:
 has-flag@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+  integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
 
 has-symbols@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44"
+  integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=
 
 has-unicode@^2.0.0:
   version "2.0.1"
@@ -2956,6 +3450,7 @@ has-values@^1.0.0:
 has@^1.0.0, has@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+  integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
   dependencies:
     function-bind "^1.1.1"
 
@@ -2977,17 +3472,23 @@ hash.js@^1.0.0, hash.js@^1.0.3:
     inherits "^2.0.3"
     minimalistic-assert "^1.0.1"
 
-he@1.1.1, he@1.1.x, he@^1.1.0:
+he@1.1.1, he@1.1.x:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
 
+he@^1.1.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
+  integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+
 hex-color-regex@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
 
 highlight.js@^9.12.0:
-  version "9.12.0"
-  resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e"
+  version "9.13.1"
+  resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e"
+  integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A==
 
 hmac-drbg@^1.0.0:
   version "1.0.1"
@@ -3000,10 +3501,22 @@ hmac-drbg@^1.0.0:
 hoek@5.x.x:
   version "5.0.4"
   resolved "https://registry.yarnpkg.com/hoek/-/hoek-5.0.4.tgz#0f7fa270a1cafeb364a4b2ddfaa33f864e4157da"
+  integrity sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w==
+
+hoek@6.x.x:
+  version "6.1.2"
+  resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.1.2.tgz#99e6d070561839de74ee427b61aa476bd6bddfd6"
+  integrity sha512-6qhh/wahGYZHFSFw12tBbJw5fsAhhwrrG/y3Cs0YMTv2WzMnL0oLPnQJjv1QJvEfylRSOFuP+xCu+tdx0tD16Q==
+
+hoopy@^0.1.2:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d"
+  integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==
 
 hosted-git-info@^2.1.4:
   version "2.7.1"
   resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
+  integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==
 
 hpack.js@^2.1.6:
   version "2.1.6"
@@ -3029,6 +3542,7 @@ html-comment-regex@^1.1.0:
 html-encoding-sniffer@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8"
+  integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==
   dependencies:
     whatwg-encoding "^1.0.1"
 
@@ -3082,7 +3596,7 @@ http-errors@1.6.2:
     setprototypeof "1.0.3"
     statuses ">= 1.3.1 < 2"
 
-http-errors@1.6.3, http-errors@~1.6.2:
+http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3:
   version "1.6.3"
   resolved "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
   dependencies:
@@ -3123,6 +3637,7 @@ http-proxy@^1.16.2:
 http-signature@~1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
+  integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
   dependencies:
     assert-plus "^1.0.0"
     jsprim "^1.2.2"
@@ -3206,6 +3721,7 @@ import-local@^2.0.0:
 imurmurhash@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+  integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
 
 indexes-of@^1.0.1:
   version "1.0.1"
@@ -3218,6 +3734,7 @@ indexof@0.0.1:
 inflight@^1.0.4:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
   dependencies:
     once "^1.3.0"
     wrappy "1"
@@ -3233,10 +3750,12 @@ inherits@2.0.1:
 ini@~1.3.0:
   version "1.3.5"
   resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
+  integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
 
 inquirer@^3.0.6:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
+  integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==
   dependencies:
     ansi-escapes "^3.0.0"
     chalk "^2.0.0"
@@ -3263,12 +3782,7 @@ internal-ip@^3.0.1:
 interpret@^1.0.1:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
-
-invariant@^2.2.0:
-  version "2.2.4"
-  resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
-  dependencies:
-    loose-envify "^1.0.0"
+  integrity sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=
 
 invert-kv@^1.0.0:
   version "1.0.0"
@@ -3317,6 +3831,7 @@ is-accessor-descriptor@^1.0.0:
 is-arrayish@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+  integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
 
 is-arrayish@^0.3.1:
   version "0.3.2"
@@ -3334,13 +3849,15 @@ is-buffer@^1.1.5:
 
 is-builtin-module@^1.0.0:
   version "1.0.0"
-  resolved "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
+  resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
+  integrity sha1-VAVy0096wxGfj3bDDLwbHgN6/74=
   dependencies:
     builtin-modules "^1.0.0"
 
 is-callable@^1.1.3, is-callable@^1.1.4:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
+  integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==
 
 is-ci@^1.0.10:
   version "1.2.1"
@@ -3374,6 +3891,7 @@ is-data-descriptor@^1.0.0:
 is-date-object@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
+  integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=
 
 is-descriptor@^0.1.0:
   version "0.1.6"
@@ -3395,6 +3913,18 @@ is-directory@^0.3.1:
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
 
+is-dotfile@^1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
+  integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=
+
+is-equal-shallow@^0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534"
+  integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=
+  dependencies:
+    is-primitive "^2.0.0"
+
 is-extendable@^0.1.0, is-extendable@^0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
@@ -3405,6 +3935,11 @@ is-extendable@^1.0.1:
   dependencies:
     is-plain-object "^2.0.4"
 
+is-extglob@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
+  integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=
+
 is-extglob@^2.1.0, is-extglob@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@@ -3418,6 +3953,14 @@ is-fullwidth-code-point@^1.0.0:
 is-fullwidth-code-point@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+  integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
+
+is-glob@^2.0.0, is-glob@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
+  integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=
+  dependencies:
+    is-extglob "^1.0.0"
 
 is-glob@^3.1.0:
   version "3.1.0"
@@ -3431,12 +3974,24 @@ is-glob@^4.0.0:
   dependencies:
     is-extglob "^2.1.1"
 
+is-number@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
+  integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=
+  dependencies:
+    kind-of "^3.0.2"
+
 is-number@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
   dependencies:
     kind-of "^3.0.2"
 
+is-number@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
+  integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==
+
 is-obj@^1.0.0:
   version "1.0.1"
   resolved "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
@@ -3444,16 +3999,19 @@ is-obj@^1.0.0:
 is-path-cwd@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
+  integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=
 
 is-path-in-cwd@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52"
+  integrity sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==
   dependencies:
     is-path-inside "^1.0.0"
 
 is-path-inside@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
+  integrity sha1-jvW33lBDej/cprToZe96pVy0gDY=
   dependencies:
     path-is-inside "^1.0.1"
 
@@ -3463,23 +4021,37 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
   dependencies:
     isobject "^3.0.1"
 
+is-posix-bracket@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
+  integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=
+
+is-primitive@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
+  integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU=
+
 is-promise@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
+  integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
 
 is-regex@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
+  integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=
   dependencies:
     has "^1.0.1"
 
 is-resolvable@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
+  integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
 
 is-stream@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+  integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
 
 is-svg@^3.0.0:
   version "3.0.0"
@@ -3490,12 +4062,19 @@ is-svg@^3.0.0:
 is-symbol@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38"
+  integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==
   dependencies:
     has-symbols "^1.0.0"
 
 is-typedarray@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+  integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
+
+is-utf8@^0.2.0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
+  integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
 
 is-windows@^1.0.2:
   version "1.0.2"
@@ -3504,6 +4083,7 @@ is-windows@^1.0.2:
 is-wsl@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
+  integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
 
 isarray@0.0.1:
   version "0.0.1"
@@ -3512,16 +4092,19 @@ isarray@0.0.1:
 isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+  integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
 
 isemail@3.x.x:
-  version "3.1.3"
-  resolved "https://registry.yarnpkg.com/isemail/-/isemail-3.1.3.tgz#64f37fc113579ea12523165c3ebe3a71a56ce571"
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/isemail/-/isemail-3.2.0.tgz#59310a021931a9fb06bbb51e155ce0b3f236832c"
+  integrity sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==
   dependencies:
     punycode "2.x.x"
 
 isexe@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+  integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
 
 isobject@^2.0.0:
   version "2.1.0"
@@ -3536,6 +4119,7 @@ isobject@^3.0.0, isobject@^3.0.1:
 isstream@~0.1.2:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+  integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
 
 javascript-stringify@^1.6.0:
   version "1.6.0"
@@ -3544,6 +4128,7 @@ javascript-stringify@^1.6.0:
 joi@^13.0.0:
   version "13.7.0"
   resolved "https://registry.yarnpkg.com/joi/-/joi-13.7.0.tgz#cfd85ebfe67e8a1900432400b4d03bbd93fb879f"
+  integrity sha512-xuY5VkHfeOYK3Hdi91ulocfuFopwgbSORmIwzcwHKESQhC7w1kD5jaVSPnqDxS2I8t3RZ9omCKAxNwXN5zG1/Q==
   dependencies:
     hoek "5.x.x"
     isemail "3.x.x"
@@ -3552,20 +4137,24 @@ joi@^13.0.0:
 js-message@1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/js-message/-/js-message-1.0.5.tgz#2300d24b1af08e89dd095bc1a4c9c9cfcb892d15"
+  integrity sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=
 
 js-queue@2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/js-queue/-/js-queue-2.0.0.tgz#362213cf860f468f0125fc6c96abc1742531f948"
+  integrity sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=
   dependencies:
     easy-stack "^1.0.0"
 
-js-tokens@^3.0.0, js-tokens@^3.0.2:
+js-tokens@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
+  integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
 
-"js-tokens@^3.0.0 || ^4.0.0":
+js-tokens@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
 
 js-yaml@^3.12.0, js-yaml@^3.7.0, js-yaml@^3.9.0, js-yaml@^3.9.1:
   version "3.12.0"
@@ -3577,45 +4166,49 @@ js-yaml@^3.12.0, js-yaml@^3.7.0, js-yaml@^3.9.0, js-yaml@^3.9.1:
 jsbn@~0.1.0:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+  integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
 
 jsdom-global@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/jsdom-global/-/jsdom-global-3.0.2.tgz#6bd299c13b0c4626b2da2c0393cd4385d606acb9"
+  integrity sha1-a9KZwTsMRiay2iwDk81DhdYGrLk=
 
-jsdom@^11.11.0:
-  version "11.12.0"
-  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8"
+jsdom@^13.0.0:
+  version "13.1.0"
+  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-13.1.0.tgz#fa7356f0cc8111d0f1077cb7800d06f22f1d66c7"
+  integrity sha512-C2Kp0qNuopw0smXFaHeayvharqF3kkcNqlcIlSX71+3XrsOFwkEPLt/9f5JksMmaul2JZYIQuY+WTpqHpQQcLg==
   dependencies:
     abab "^2.0.0"
-    acorn "^5.5.3"
-    acorn-globals "^4.1.0"
+    acorn "^6.0.4"
+    acorn-globals "^4.3.0"
     array-equal "^1.0.0"
-    cssom ">= 0.3.2 < 0.4.0"
-    cssstyle "^1.0.0"
-    data-urls "^1.0.0"
+    cssom "^0.3.4"
+    cssstyle "^1.1.1"
+    data-urls "^1.1.0"
     domexception "^1.0.1"
-    escodegen "^1.9.1"
+    escodegen "^1.11.0"
     html-encoding-sniffer "^1.0.2"
-    left-pad "^1.3.0"
-    nwsapi "^2.0.7"
-    parse5 "4.0.0"
+    nwsapi "^2.0.9"
+    parse5 "5.1.0"
     pn "^1.1.0"
-    request "^2.87.0"
+    request "^2.88.0"
     request-promise-native "^1.0.5"
-    sax "^1.2.4"
+    saxes "^3.1.4"
     symbol-tree "^3.2.2"
-    tough-cookie "^2.3.4"
+    tough-cookie "^2.5.0"
     w3c-hr-time "^1.0.1"
+    w3c-xmlserializer "^1.0.1"
     webidl-conversions "^4.0.2"
-    whatwg-encoding "^1.0.3"
-    whatwg-mimetype "^2.1.0"
-    whatwg-url "^6.4.1"
-    ws "^5.2.0"
+    whatwg-encoding "^1.0.5"
+    whatwg-mimetype "^2.3.0"
+    whatwg-url "^7.0.0"
+    ws "^6.1.2"
     xml-name-validator "^3.0.0"
 
 jsesc@^2.5.1:
-  version "2.5.1"
-  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe"
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
+  integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
 
 jsesc@~0.5.0:
   version "0.5.0"
@@ -3628,6 +4221,7 @@ json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
 json-schema-traverse@^0.3.0:
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
+  integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=
 
 json-schema-traverse@^0.4.1:
   version "0.4.1"
@@ -3636,14 +4230,17 @@ json-schema-traverse@^0.4.1:
 json-schema@0.2.3:
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
+  integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
 
 json-stable-stringify-without-jsonify@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+  integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
 
 json-stringify-safe@~5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+  integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
 
 json3@3.3.2, json3@^3.3.2:
   version "3.3.2"
@@ -3651,7 +4248,8 @@ json3@3.3.2, json3@^3.3.2:
 
 json5@^0.5.0:
   version "0.5.1"
-  resolved "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
+  integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=
 
 jsonfile@^4.0.0:
   version "4.0.0"
@@ -3662,16 +4260,23 @@ jsonfile@^4.0.0:
 jsonify@~0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
+  integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
 
 jsprim@^1.2.2:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
+  integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
   dependencies:
     assert-plus "1.0.0"
     extsprintf "1.3.0"
     json-schema "0.2.3"
     verror "1.10.0"
 
+just-extend@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.0.2.tgz#f3f47f7dfca0f989c55410a7ebc8854b07108afc"
+  integrity sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==
+
 kew@^0.7.0:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b"
@@ -3709,6 +4314,7 @@ launch-editor-middleware@^2.2.1:
 launch-editor@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.2.1.tgz#871b5a3ee39d6680fcc26d37930b6eeda89db0ca"
+  integrity sha512-On+V7K2uZK6wK7x691ycSUbLD/FyKKelArkbaAMSSJU8JmqmhwN2+mnJDNINuJWSrh2L0kDk+ZQtbC/gOWUwLw==
   dependencies:
     chalk "^2.3.0"
     shell-quote "^1.6.1"
@@ -3725,20 +4331,29 @@ lcid@^2.0.0:
   dependencies:
     invert-kv "^2.0.0"
 
-left-pad@^1.3.0:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e"
-
 levn@^0.3.0, levn@~0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
+  integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
   dependencies:
     prelude-ls "~1.1.2"
     type-check "~0.3.2"
 
+load-json-file@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
+  integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=
+  dependencies:
+    graceful-fs "^4.1.2"
+    parse-json "^2.2.0"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+    strip-bom "^2.0.0"
+
 load-json-file@^2.0.0:
   version "2.0.0"
-  resolved "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
+  resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
+  integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=
   dependencies:
     graceful-fs "^4.1.2"
     parse-json "^2.2.0"
@@ -3748,6 +4363,7 @@ load-json-file@^2.0.0:
 loader-fs-cache@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz#56e0bf08bd9708b26a765b68509840c8dec9fdbc"
+  integrity sha1-VuC/CL2XCLJqdltoUJhAyN7J/bw=
   dependencies:
     find-cache-dir "^0.1.1"
     mkdirp "0.5.1"
@@ -3768,6 +4384,7 @@ loader-utils@^0.2.16:
 loader-utils@^1.0.2, loader-utils@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd"
+  integrity sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=
   dependencies:
     big.js "^3.1.3"
     emojis-list "^2.0.0"
@@ -3776,6 +4393,7 @@ loader-utils@^1.0.2, loader-utils@^1.1.0:
 locate-path@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
+  integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=
   dependencies:
     p-locate "^2.0.0"
     path-exists "^3.0.0"
@@ -3845,9 +4463,10 @@ lodash._stack@^4.0.0:
   version "4.1.3"
   resolved "https://registry.yarnpkg.com/lodash._stack/-/lodash._stack-4.1.3.tgz#751aa76c1b964b047e76d14fc72a093fcb5e2dd0"
 
-lodash.camelcase@^4.3.0:
-  version "4.3.0"
-  resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
+lodash.assign@^4.0.3, lodash.assign@^4.0.6:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
+  integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=
 
 lodash.clone@3.0.3:
   version "3.0.3"
@@ -3884,9 +4503,10 @@ lodash.defaultsdeep@^4.6.0:
   version "4.6.0"
   resolved "https://registry.yarnpkg.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.0.tgz#bec1024f85b1bd96cbea405b23c14ad6443a6f81"
 
-lodash.endswith@^4.2.1:
-  version "4.2.1"
-  resolved "https://registry.yarnpkg.com/lodash.endswith/-/lodash.endswith-4.2.1.tgz#fed59ac1738ed3e236edd7064ec456448b37bc09"
+lodash.get@^4.4.2:
+  version "4.4.2"
+  resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
+  integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
 
 lodash.isarguments@^3.0.0:
   version "3.1.0"
@@ -3896,18 +4516,10 @@ lodash.isarray@^3.0.0:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
 
-lodash.isfunction@^3.0.8:
-  version "3.0.9"
-  resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
-
 lodash.isplainobject@^4.0.0:
   version "4.0.6"
   resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
 
-lodash.isstring@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
-
 lodash.keys@^3.0.0:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
@@ -3939,10 +4551,7 @@ lodash.rest@^4.0.0:
 lodash.sortby@^4.7.0:
   version "4.7.0"
   resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
-
-lodash.startswith@^4.2.1:
-  version "4.2.1"
-  resolved "https://registry.yarnpkg.com/lodash.startswith/-/lodash.startswith-4.2.1.tgz#c598c4adce188a27e53145731cdc6c0e7177600c"
+  integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
 
 lodash.transform@^4.6.0:
   version "4.6.0"
@@ -3956,13 +4565,15 @@ lodash.uniq@^4.5.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
 
-lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0:
+lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0:
   version "4.17.11"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
+  integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
 
 log-symbols@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
+  integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==
   dependencies:
     chalk "^2.0.1"
 
@@ -3970,17 +4581,29 @@ loglevel@^1.4.1:
   version "1.6.1"
   resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa"
 
-loose-envify@^1.0.0:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
-  dependencies:
-    js-tokens "^3.0.0 || ^4.0.0"
+lolex@^2.3.2:
+  version "2.7.5"
+  resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.7.5.tgz#113001d56bfc7e02d56e36291cc5c413d1aa0733"
+  integrity sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==
+
+lolex@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/lolex/-/lolex-3.0.0.tgz#f04ee1a8aa13f60f1abd7b0e8f4213ec72ec193e"
+  integrity sha512-hcnW80h3j2lbUfFdMArd5UPA/vxZJ+G8vobd+wg3nVEQA0EigStbYcrG030FJxL6xiDDPEkoMatV9xIh5OecQQ==
 
 lower-case@^1.1.1:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
 
-lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@^4.1.2, lru-cache@^4.1.3:
+lru-cache@^4.0.1, lru-cache@^4.1.3:
+  version "4.1.5"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
+  integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
+  dependencies:
+    pseudomap "^1.0.2"
+    yallist "^2.1.2"
+
+lru-cache@^4.1.1, lru-cache@^4.1.2:
   version "4.1.3"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c"
   dependencies:
@@ -4013,6 +4636,11 @@ map-visit@^1.0.0:
   dependencies:
     object-visit "^1.0.0"
 
+math-random@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.1.tgz#8b3aac588b8a66e4975e3cdea67f7bb329601fac"
+  integrity sha1-izqsWIuKZuSXXjzepn97sylgH6w=
+
 md5.js@^1.3.4:
   version "1.3.4"
   resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d"
@@ -4031,6 +4659,7 @@ media-typer@0.3.0:
 mem@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76"
+  integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=
   dependencies:
     mimic-fn "^1.0.0"
 
@@ -4067,6 +4696,25 @@ methods@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
 
+micromatch@^2.1.5:
+  version "2.3.11"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
+  integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=
+  dependencies:
+    arr-diff "^2.0.0"
+    array-unique "^0.2.1"
+    braces "^1.8.2"
+    expand-brackets "^0.1.4"
+    extglob "^0.3.1"
+    filename-regex "^2.0.0"
+    is-extglob "^1.0.0"
+    is-glob "^2.0.1"
+    kind-of "^3.0.2"
+    normalize-path "^2.0.1"
+    object.omit "^2.0.0"
+    parse-glob "^3.0.4"
+    regex-cache "^0.4.2"
+
 micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9:
   version "3.1.10"
   resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
@@ -4096,7 +4744,19 @@ miller-rabin@^4.0.0:
   version "1.36.0"
   resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397"
 
-mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.19:
+mime-db@~1.37.0:
+  version "1.37.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8"
+  integrity sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==
+
+mime-types@^2.1.12, mime-types@~2.1.19:
+  version "2.1.21"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96"
+  integrity sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==
+  dependencies:
+    mime-db "~1.37.0"
+
+mime-types@~2.1.17, mime-types@~2.1.18:
   version "2.1.20"
   resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19"
   dependencies:
@@ -4113,10 +4773,19 @@ mime@^2.0.3, mime@^2.3.1:
 mimic-fn@^1.0.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
+  integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
 
-mini-css-extract-plugin@^0.4.1:
-  version "0.4.3"
-  resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.3.tgz#98d60fcc5d228c3e36a9bd15a1d6816d6580beb8"
+min-document@^2.19.0:
+  version "2.19.0"
+  resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
+  integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=
+  dependencies:
+    dom-walk "^0.1.0"
+
+mini-css-extract-plugin@^0.4.5:
+  version "0.4.5"
+  resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.5.tgz#c99e9e78d54f3fa775633aee5933aeaa4e80719a"
+  integrity sha512-dqBanNfktnp2hwL2YguV9Jh91PFX7gu7nRLs4TGsbAfAG6WOtlynFRYzwDwmmeSb5uIwHo9nx1ta0f7vAZVp2w==
   dependencies:
     loader-utils "^1.1.0"
     schema-utils "^1.0.0"
@@ -4139,16 +4808,19 @@ minimatch@3.0.3:
 minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+  integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
   dependencies:
     brace-expansion "^1.1.7"
 
 minimist@0.0.8:
   version "0.0.8"
-  resolved "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+  integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
 
 minimist@^1.2.0:
   version "1.2.0"
-  resolved "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+  integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
 
 minimist@~0.0.1:
   version "0.0.10"
@@ -4182,6 +4854,22 @@ mississippi@^2.0.0:
     stream-each "^1.1.0"
     through2 "^2.0.0"
 
+mississippi@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022"
+  integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==
+  dependencies:
+    concat-stream "^1.5.0"
+    duplexify "^3.4.2"
+    end-of-stream "^1.1.0"
+    flush-write-stream "^1.0.0"
+    from2 "^2.1.0"
+    parallel-transform "^1.1.0"
+    pump "^3.0.0"
+    pumpify "^1.3.3"
+    stream-each "^1.1.0"
+    through2 "^2.0.0"
+
 mixin-deep@^1.2.0:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"
@@ -4191,7 +4879,8 @@ mixin-deep@^1.2.0:
 
 mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
   version "0.5.1"
-  resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+  integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
   dependencies:
     minimist "0.0.8"
 
@@ -4215,9 +4904,33 @@ mocha-nightwatch@3.2.2:
     mkdirp "0.5.1"
     supports-color "3.1.2"
 
+mocha-webpack@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/mocha-webpack/-/mocha-webpack-1.1.0.tgz#292158fc191641c943c1ee615329504f47c4b0ba"
+  integrity sha512-brmE0tR6G5JbEzZXspJmF/uZm2J/YM/69M3VS6ND76i6wXbebFpE+bQDaehilK8CZanNSsTCcqTTLh1PZuRKJw==
+  dependencies:
+    babel-runtime "^6.18.0"
+    chalk "^2.3.0"
+    chokidar "^1.6.1"
+    glob-parent "^3.1.0"
+    globby "^6.1.0"
+    interpret "^1.0.1"
+    is-glob "^4.0.0"
+    loader-utils "^1.1.0"
+    lodash "^4.3.0"
+    memory-fs "^0.4.1"
+    nodent-runtime "^3.0.3"
+    normalize-path "^2.0.1"
+    progress "^2.0.0"
+    source-map-support "^0.5.0"
+    strip-ansi "^4.0.0"
+    toposort "^1.0.0"
+    yargs "^4.8.0"
+
 mocha-webpack@^2.0.0-beta.0:
   version "2.0.0-beta.0"
   resolved "https://registry.yarnpkg.com/mocha-webpack/-/mocha-webpack-2.0.0-beta.0.tgz#d85fc9a70f82a4ad595b7702a1181605dfa59549"
+  integrity sha512-2ezbW0h5cYWr874F/hzytQCqINxk+GVelMY4xWTSHwwH1LrPAOzjlUljZ+/PhpaP6QeqYbL5x5vK/bnaXqkfEw==
   dependencies:
     babel-runtime "^6.18.0"
     chalk "^2.3.0"
@@ -4240,6 +4953,7 @@ mocha-webpack@^2.0.0-beta.0:
 mocha@^5.2.0:
   version "5.2.0"
   resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6"
+  integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==
   dependencies:
     browser-stdout "1.3.1"
     commander "2.15.1"
@@ -4253,6 +4967,14 @@ mocha@^5.2.0:
     mkdirp "0.5.1"
     supports-color "5.4.0"
 
+mock-local-storage@^1.1.8:
+  version "1.1.8"
+  resolved "https://registry.yarnpkg.com/mock-local-storage/-/mock-local-storage-1.1.8.tgz#82a3f7c666bf955d64b286cb71207478f1a29bf7"
+  integrity sha512-x/LPtSBqSROQGjJn60Fp0+rgOHZuuQZysgeSYfhG2l/moD0LLW60bKi4KpmCnNs9S3l0cbS5cmzQFzeKDL7RaQ==
+  dependencies:
+    core-js "^0.8.3"
+    global "^4.3.2"
+
 move-concurrently@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@@ -4271,10 +4993,12 @@ ms@0.7.1:
 ms@2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+  integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
 
 ms@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
+  integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
 
 multicast-dns-service-types@^1.1.0:
   version "1.1.0"
@@ -4290,6 +5014,7 @@ multicast-dns@^6.0.1:
 mute-stream@0.0.7:
   version "0.0.7"
   resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
+  integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
 
 nan@^2.9.2:
   version "2.11.1"
@@ -4314,6 +5039,7 @@ nanomatch@^1.2.9:
 natural-compare@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+  integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
 
 needle@^2.2.1:
   version "2.2.4"
@@ -4338,6 +5064,7 @@ netmask@~1.0.4:
 nice-try@^1.0.4:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
+  integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
 
 nightwatch@^0.9.21:
   version "0.9.21"
@@ -4354,6 +5081,17 @@ nightwatch@^0.9.21:
     proxy-agent "2.0.0"
     q "1.4.1"
 
+nise@^1.4.7:
+  version "1.4.8"
+  resolved "https://registry.yarnpkg.com/nise/-/nise-1.4.8.tgz#ce91c31e86cf9b2c4cac49d7fcd7f56779bfd6b0"
+  integrity sha512-kGASVhuL4tlAV0tvA34yJYZIVihrUt/5bDwpp4tTluigxUr2bBlJeDXmivb6NuEdFkqvdv/Ybb9dm16PSKUhtw==
+  dependencies:
+    "@sinonjs/formatio" "^3.1.0"
+    just-extend "^4.0.2"
+    lolex "^2.3.2"
+    path-to-regexp "^1.7.0"
+    text-encoding "^0.6.4"
+
 no-case@^2.2.0:
   version "2.3.2"
   resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
@@ -4367,6 +5105,7 @@ node-forge@0.7.5:
 node-ipc@^9.1.1:
   version "9.1.1"
   resolved "https://registry.yarnpkg.com/node-ipc/-/node-ipc-9.1.1.tgz#4e245ed6938e65100e595ebc5dc34b16e8dd5d69"
+  integrity sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==
   dependencies:
     event-pubsub "4.3.0"
     js-message "1.0.5"
@@ -4424,10 +5163,12 @@ node-releases@^1.0.0-alpha.11:
 nodent-runtime@^3.0.3:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/nodent-runtime/-/nodent-runtime-3.2.1.tgz#9e2755d85e39f764288f0d4752ebcfe3e541e00e"
+  integrity sha512-7Ws63oC+215smeKJQCxzrK21VFVlCFBkwl0MOObt0HOpVQXs3u483sAmtkF33nNqZ5rSOQjB76fgyPBmAUrtCA==
 
 nopt@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
+  integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=
   dependencies:
     abbrev "1"
     osenv "^0.1.4"
@@ -4435,6 +5176,7 @@ nopt@^4.0.1:
 normalize-package-data@^2.3.2:
   version "2.4.0"
   resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f"
+  integrity sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==
   dependencies:
     hosted-git-info "^2.1.4"
     is-builtin-module "^1.0.0"
@@ -4445,7 +5187,7 @@ normalize-path@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379"
 
-normalize-path@^2.0.1, normalize-path@^2.1.1:
+normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
   dependencies:
@@ -4473,6 +5215,7 @@ npm-packlist@^1.1.6:
 npm-run-path@^2.0.0:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
+  integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
   dependencies:
     path-key "^2.0.0"
 
@@ -4499,17 +5242,20 @@ number-is-nan@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
 
-nwsapi@^2.0.7:
+nwsapi@^2.0.9:
   version "2.0.9"
   resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.0.9.tgz#77ac0cdfdcad52b6a1151a84e73254edc33ed016"
+  integrity sha512-nlWFSCTYQcHk/6A9FFnfhKc14c3aFhfdNBXgo8Qgi9QTBu/qg3Ww+Uiz9wMzXd1T8GFxPc2QIHB6Qtf2XFryFQ==
 
 oauth-sign@~0.9.0:
   version "0.9.0"
   resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
+  integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
 
 object-assign@^4.0.1, object-assign@^4.1.0:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+  integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
 
 object-copy@^0.1.0:
   version "0.1.0"
@@ -4520,12 +5266,14 @@ object-copy@^0.1.0:
     kind-of "^3.0.3"
 
 object-hash@^1.1.4:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.0.tgz#76d9ba6ff113cf8efc0d996102851fe6723963e2"
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df"
+  integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==
 
 object-keys@^1.0.12:
   version "1.0.12"
   resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2"
+  integrity sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==
 
 object-visit@^1.0.0:
   version "1.0.1"
@@ -4540,6 +5288,14 @@ object.getownpropertydescriptors@^2.0.3:
     define-properties "^1.1.2"
     es-abstract "^1.5.1"
 
+object.omit@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
+  integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=
+  dependencies:
+    for-own "^0.1.4"
+    is-extendable "^0.1.1"
+
 object.pick@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
@@ -4572,28 +5328,33 @@ on-headers@~1.0.1:
 once@^1.3.0, once@^1.3.1, once@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
   dependencies:
     wrappy "1"
 
 onetime@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
+  integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=
   dependencies:
     mimic-fn "^1.0.0"
 
-opener@^1.4.3:
+opener@^1.5.1:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed"
+  integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==
 
 opn@^5.1.0, opn@^5.3.0:
   version "5.4.0"
   resolved "https://registry.yarnpkg.com/opn/-/opn-5.4.0.tgz#cb545e7aab78562beb11aa3bfabc7042e1761035"
+  integrity sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==
   dependencies:
     is-wsl "^1.1.0"
 
 optimist@0.6.1:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
+  integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY=
   dependencies:
     minimist "~0.0.1"
     wordwrap "~0.0.2"
@@ -4612,6 +5373,19 @@ optionator@^0.8.1, optionator@^0.8.2:
 ora@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/ora/-/ora-2.1.0.tgz#6caf2830eb924941861ec53a173799e008b51e5b"
+  integrity sha512-hNNlAd3gfv/iPmsNxYoAPLvxg7HuPozww7fFonMZvL84tP6Ox5igfk5j/+a9rtJJwqMgKK+JgWsAQik5o0HTLA==
+  dependencies:
+    chalk "^2.3.1"
+    cli-cursor "^2.1.0"
+    cli-spinners "^1.1.0"
+    log-symbols "^2.2.0"
+    strip-ansi "^4.0.0"
+    wcwidth "^1.0.1"
+
+ora@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/ora/-/ora-3.0.0.tgz#8179e3525b9aafd99242d63cc206fd64732741d0"
+  integrity sha512-LBS97LFe2RV6GJmXBi6OKcETKyklHNMV0xw7BtsVn2MlsgsydyZetSCbCANr+PFLmDyv4KV88nn0eCKza665Mg==
   dependencies:
     chalk "^2.3.1"
     cli-cursor "^2.1.0"
@@ -4620,9 +5394,10 @@ ora@^2.1.0:
     strip-ansi "^4.0.0"
     wcwidth "^1.0.1"
 
-original@>=0.0.5:
+original@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f"
+  integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==
   dependencies:
     url-parse "^1.4.3"
 
@@ -4634,9 +5409,17 @@ os-homedir@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
 
+os-locale@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9"
+  integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=
+  dependencies:
+    lcid "^1.0.0"
+
 os-locale@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
+  integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==
   dependencies:
     execa "^0.7.0"
     lcid "^1.0.0"
@@ -4668,6 +5451,7 @@ p-defer@^1.0.0:
 p-finally@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
+  integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
 
 p-is-promise@^1.1.0:
   version "1.1.0"
@@ -4676,6 +5460,7 @@ p-is-promise@^1.1.0:
 p-limit@^1.0.0, p-limit@^1.1.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
+  integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==
   dependencies:
     p-try "^1.0.0"
 
@@ -4688,6 +5473,7 @@ p-limit@^2.0.0:
 p-locate@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
+  integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=
   dependencies:
     p-limit "^1.1.0"
 
@@ -4704,6 +5490,7 @@ p-map@^1.1.1:
 p-try@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
+  integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
 
 p-try@^2.0.0:
   version "2.0.0"
@@ -4761,9 +5548,20 @@ parse-asn1@^5.0.0:
     evp_bytestokey "^1.0.0"
     pbkdf2 "^3.0.3"
 
+parse-glob@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
+  integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw=
+  dependencies:
+    glob-base "^0.3.0"
+    is-dotfile "^1.0.0"
+    is-extglob "^1.0.0"
+    is-glob "^2.0.0"
+
 parse-json@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
+  integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=
   dependencies:
     error-ex "^1.2.0"
 
@@ -4774,9 +5572,10 @@ parse-json@^4.0.0:
     error-ex "^1.3.1"
     json-parse-better-errors "^1.0.1"
 
-parse5@4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608"
+parse5@5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2"
+  integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==
 
 parseurl@~1.3.2:
   version "1.3.2"
@@ -4797,36 +5596,59 @@ path-dirname@^1.0.0:
 path-exists@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
+  integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=
   dependencies:
     pinkie-promise "^2.0.0"
 
 path-exists@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+  integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
 
 path-is-absolute@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+  integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
 
 path-is-inside@^1.0.1, path-is-inside@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
+  integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
 
 path-key@^2.0.0, path-key@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
+  integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
 
-path-parse@^1.0.5:
+path-parse@^1.0.5, path-parse@^1.0.6:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+  integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
 
 path-to-regexp@0.1.7:
   version "0.1.7"
   resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
 
+path-to-regexp@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
+  integrity sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=
+  dependencies:
+    isarray "0.0.1"
+
+path-type@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
+  integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=
+  dependencies:
+    graceful-fs "^4.1.2"
+    pify "^2.0.0"
+    pinkie-promise "^2.0.0"
+
 path-type@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
+  integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=
   dependencies:
     pify "^2.0.0"
 
@@ -4857,10 +5679,12 @@ pend@~1.2.0:
 performance-now@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+  integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
 
 pify@^2.0.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+  integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
 
 pify@^3.0.0:
   version "3.0.0"
@@ -4869,16 +5693,19 @@ pify@^3.0.0:
 pinkie-promise@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+  integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o=
   dependencies:
     pinkie "^2.0.0"
 
 pinkie@^2.0.0:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+  integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
 
 pkg-dir@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
+  integrity sha1-ektQio1bstYp1EcFb/TpyTFM89Q=
   dependencies:
     find-up "^1.0.0"
 
@@ -4897,12 +5724,23 @@ pkg-dir@^3.0.0:
 pluralize@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
+  integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==
 
 pn@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
+  integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==
+
+portfinder@^1.0.19:
+  version "1.0.20"
+  resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.20.tgz#bea68632e54b2e13ab7b0c4775e9b41bf270e44a"
+  integrity sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==
+  dependencies:
+    async "^1.5.2"
+    debug "^2.2.0"
+    mkdirp "0.5.x"
 
-portfinder@^1.0.13, portfinder@^1.0.9:
+portfinder@^1.0.9:
   version "1.0.17"
   resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.17.tgz#a8a1691143e46c4735edefcf4fbcccedad26456a"
   dependencies:
@@ -4923,6 +5761,16 @@ postcss-calc@^6.0.2:
     postcss-selector-parser "^2.2.2"
     reduce-css-calc "^2.0.0"
 
+postcss-calc@^7.0.0:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.1.tgz#36d77bab023b0ecbb9789d84dcb23c4941145436"
+  integrity sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ==
+  dependencies:
+    css-unit-converter "^1.1.1"
+    postcss "^7.0.5"
+    postcss-selector-parser "^5.0.0-rc.4"
+    postcss-value-parser "^3.3.1"
+
 postcss-colormin@^4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.2.tgz#93cd1fa11280008696887db1a528048b18e7ed99"
@@ -4971,14 +5819,15 @@ postcss-load-config@^2.0.0:
     cosmiconfig "^4.0.0"
     import-cwd "^2.0.0"
 
-postcss-loader@^2.1.6:
-  version "2.1.6"
-  resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.1.6.tgz#1d7dd7b17c6ba234b9bed5af13e0bea40a42d740"
+postcss-loader@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d"
+  integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==
   dependencies:
     loader-utils "^1.1.0"
-    postcss "^6.0.0"
+    postcss "^7.0.0"
     postcss-load-config "^2.0.0"
-    schema-utils "^0.4.0"
+    schema-utils "^1.0.0"
 
 postcss-merge-longhand@^4.0.6:
   version "4.0.6"
@@ -4989,6 +5838,16 @@ postcss-merge-longhand@^4.0.6:
     postcss-value-parser "^3.0.0"
     stylehacks "^4.0.0"
 
+postcss-merge-longhand@^4.0.9:
+  version "4.0.9"
+  resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.9.tgz#c2428b994833ffb2a072f290ca642e75ceabcd6f"
+  integrity sha512-UVMXrXF5K/kIwUbK/crPFCytpWbNX2Q3dZSc8+nQUgfOHrCT4+MHncpdxVphUlQeZxlLXUJbDyXc5NBhTnS2tA==
+  dependencies:
+    css-color-names "0.0.4"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+    stylehacks "^4.0.0"
+
 postcss-merge-rules@^4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.2.tgz#2be44401bf19856f27f32b8b12c0df5af1b88e74"
@@ -5177,6 +6036,15 @@ postcss-selector-parser@^3.0.0, postcss-selector-parser@^3.1.1:
     indexes-of "^1.0.1"
     uniq "^1.0.1"
 
+postcss-selector-parser@^5.0.0-rc.4:
+  version "5.0.0-rc.4"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0-rc.4.tgz#ca5e77238bf152966378c13e91ad6d611568ea87"
+  integrity sha512-0XvfYuShrKlTk1ooUrVzMCFQRcypsdEIsGqh5IxC5rdtBi4/M/tDAJeSONwC2MTqEFsmPZYAV7Dd4X8rgAfV0A==
+  dependencies:
+    cssesc "^2.0.0"
+    indexes-of "^1.0.1"
+    uniq "^1.0.1"
+
 postcss-svgo@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.1.tgz#5628cdb38f015de6b588ce6d0bf0724b492b581d"
@@ -5198,7 +6066,12 @@ postcss-value-parser@^3.0.0, postcss-value-parser@^3.2.3, postcss-value-parser@^
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15"
 
-postcss@^6.0.0, postcss@^6.0.1, postcss@^6.0.20, postcss@^6.0.23:
+postcss-value-parser@^3.3.1:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
+  integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
+
+postcss@^6.0.1, postcss@^6.0.20, postcss@^6.0.23:
   version "6.0.23"
   resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
   dependencies:
@@ -5214,9 +6087,24 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.2:
     source-map "^0.6.1"
     supports-color "^5.5.0"
 
+postcss@^7.0.5:
+  version "7.0.7"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.7.tgz#2754d073f77acb4ef08f1235c36c5721a7201614"
+  integrity sha512-HThWSJEPkupqew2fnuQMEI2YcTj/8gMV3n80cMdJsKxfIh5tHf7nM5JigNX6LxVMqo6zkgQNAI88hyFvBk41Pg==
+  dependencies:
+    chalk "^2.4.1"
+    source-map "^0.6.1"
+    supports-color "^5.5.0"
+
 prelude-ls@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+  integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
+
+preserve@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
+  integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=
 
 prettier@1.13.7:
   version "1.13.7"
@@ -5232,20 +6120,27 @@ pretty-error@^2.0.2:
 process-nextick-args@~2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
+  integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==
 
 process@^0.11.10:
   version "0.11.10"
   resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
 
+process@~0.5.1:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf"
+  integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=
+
 progress@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
+  integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
 
 promise-inflight@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
 
-proxy-addr@~2.0.3:
+proxy-addr@~2.0.3, proxy-addr@~2.0.4:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93"
   dependencies:
@@ -5272,10 +6167,12 @@ prr@~1.0.1:
 pseudomap@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
+  integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
 
-psl@^1.1.24:
-  version "1.1.29"
-  resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.29.tgz#60f580d360170bb722a797cc704411e6da850c67"
+psl@^1.1.24, psl@^1.1.28:
+  version "1.1.31"
+  resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184"
+  integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==
 
 public-encrypt@^4.0.0:
   version "4.0.2"
@@ -5294,6 +6191,14 @@ pump@^2.0.0, pump@^2.0.1:
     end-of-stream "^1.1.0"
     once "^1.3.1"
 
+pump@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
+  integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
+  dependencies:
+    end-of-stream "^1.1.0"
+    once "^1.3.1"
+
 pumpify@^1.3.3:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce"
@@ -5306,13 +6211,14 @@ punycode@1.3.2:
   version "1.3.2"
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
 
-punycode@2.x.x, punycode@^2.1.0:
+punycode@2.x.x, punycode@^2.1.0, punycode@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
 
 punycode@^1.2.4, punycode@^1.4.1:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+  integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
 
 q@1.4.1:
   version "1.4.1"
@@ -5326,9 +6232,10 @@ qs@6.5.1:
   version "6.5.1"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
 
-qs@~6.5.2:
+qs@6.5.2, qs@~6.5.2:
   version "6.5.2"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
+  integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
 
 querystring-es3@^0.2.0:
   version "0.2.1"
@@ -5342,6 +6249,15 @@ querystringify@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.0.0.tgz#fa3ed6e68eb15159457c89b37bc6472833195755"
 
+randomatic@^3.0.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed"
+  integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==
+  dependencies:
+    is-number "^4.0.0"
+    kind-of "^6.0.0"
+    math-random "^1.0.1"
+
 randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80"
@@ -5359,7 +6275,7 @@ range-parser@^1.0.3, range-parser@~1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
 
-raw-body@2:
+raw-body@2, raw-body@2.3.3:
   version "2.3.3"
   resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3"
   dependencies:
@@ -5386,16 +6302,35 @@ rc@^1.2.7:
     minimist "^1.2.0"
     strip-json-comments "~2.0.1"
 
+read-pkg-up@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
+  integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=
+  dependencies:
+    find-up "^1.0.0"
+    read-pkg "^1.0.0"
+
 read-pkg-up@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
+  integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=
   dependencies:
     find-up "^2.0.0"
     read-pkg "^2.0.0"
 
+read-pkg@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
+  integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=
+  dependencies:
+    load-json-file "^1.0.0"
+    normalize-package-data "^2.3.2"
+    path-type "^1.0.0"
+
 read-pkg@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
+  integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=
   dependencies:
     load-json-file "^2.0.0"
     normalize-package-data "^2.3.2"
@@ -5462,6 +6397,13 @@ regenerator-runtime@^0.11.0:
   version "0.11.1"
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
 
+regex-cache@^0.4.2:
+  version "0.4.4"
+  resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd"
+  integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==
+  dependencies:
+    is-equal-shallow "^0.1.3"
+
 regex-not@^1.0.0, regex-not@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
@@ -5472,6 +6414,7 @@ regex-not@^1.0.0, regex-not@^1.0.2:
 regexpp@^1.0.1:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab"
+  integrity sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==
 
 regexpu-core@^1.0.0:
   version "1.0.0"
@@ -5513,27 +6456,30 @@ repeat-element@^1.1.2:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
 
-repeat-string@^1.6.1:
+repeat-string@^1.5.2, repeat-string@^1.6.1:
   version "1.6.1"
   resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
 
 request-promise-core@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6"
+  integrity sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=
   dependencies:
     lodash "^4.13.1"
 
 request-promise-native@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.5.tgz#5281770f68e0c9719e5163fd3fab482215f4fda5"
+  integrity sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=
   dependencies:
     request-promise-core "1.1.1"
     stealthy-require "^1.1.0"
     tough-cookie ">=2.3.3"
 
-request@^2.87.0:
+request@^2.87.0, request@^2.88.0:
   version "2.88.0"
   resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
+  integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
   dependencies:
     aws-sign2 "~0.7.0"
     aws4 "^1.8.0"
@@ -5571,6 +6517,7 @@ require-main-filename@^1.0.1:
 require-uncached@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
+  integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=
   dependencies:
     caller-path "^0.1.0"
     resolve-from "^1.0.0"
@@ -5592,6 +6539,7 @@ resolve-cwd@^2.0.0:
 resolve-from@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
+  integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=
 
 resolve-from@^3.0.0:
   version "3.0.0"
@@ -5601,15 +6549,23 @@ resolve-url@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
 
-resolve@^1.3.2, resolve@^1.3.3, resolve@^1.5.0, resolve@^1.6.0:
+resolve@^1.3.2:
   version "1.8.1"
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26"
   dependencies:
     path-parse "^1.0.5"
 
+resolve@^1.3.3, resolve@^1.5.0, resolve@^1.6.0:
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.9.0.tgz#a14c6fdfa8f92a7df1d996cb7105fa744658ea06"
+  integrity sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ==
+  dependencies:
+    path-parse "^1.0.6"
+
 restore-cursor@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
+  integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368=
   dependencies:
     onetime "^2.0.0"
     signal-exit "^3.0.2"
@@ -5626,9 +6582,10 @@ rgba-regex@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
 
-rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2:
+rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@~2.6.2:
   version "2.6.2"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
+  integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==
   dependencies:
     glob "^7.0.5"
 
@@ -5642,6 +6599,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
 run-async@^2.2.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
+  integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA=
   dependencies:
     is-promise "^2.1.0"
 
@@ -5654,12 +6612,14 @@ run-queue@^1.0.0, run-queue@^1.0.3:
 rx-lite-aggregates@^4.0.8:
   version "4.0.8"
   resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be"
+  integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=
   dependencies:
     rx-lite "*"
 
 rx-lite@*, rx-lite@^4.0.8:
   version "4.0.8"
   resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
+  integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=
 
 safe-buffer@5.1.1:
   version "5.1.1"
@@ -5668,6 +6628,7 @@ safe-buffer@5.1.1:
 safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
   version "5.1.2"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
 
 safe-regex@^1.1.0:
   version "1.1.0"
@@ -5678,12 +6639,20 @@ safe-regex@^1.1.0:
 "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
 
 sax@^1.2.4, sax@~1.2.4:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
 
-schema-utils@^0.4.0, schema-utils@^0.4.2, schema-utils@^0.4.4, schema-utils@^0.4.5:
+saxes@^3.1.4:
+  version "3.1.4"
+  resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.4.tgz#4ad5c53eb085ac0570ea1071a07aaf22ad29cebd"
+  integrity sha512-GVZmLJnkS4Vl8Pe9o4nc5ALZ615VOVxCmea8Cs0l+8GZw3RQ5XGOSUomIUfuZuk4Todo44v4y+HY1EATkDDiZg==
+  dependencies:
+    xmlchars "^1.3.1"
+
+schema-utils@^0.4.2, schema-utils@^0.4.4:
   version "0.4.7"
   resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"
   dependencies:
@@ -5712,14 +6681,19 @@ selfsigned@^1.9.1:
   dependencies:
     node-forge "0.7.5"
 
-"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0:
-  version "5.5.1"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477"
+"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
+  version "5.6.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
+  integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
 
 semver@5.5.0:
   version "5.5.0"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
 
+semver@^5.0.1:
+  version "5.5.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477"
+
 semver@~5.0.1:
   version "5.0.3"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a"
@@ -5811,16 +6785,19 @@ sha.js@^2.4.0, sha.js@^2.4.8:
 shebang-command@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
+  integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
   dependencies:
     shebang-regex "^1.0.0"
 
 shebang-regex@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+  integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
 
 shell-quote@^1.6.1:
   version "1.6.1"
   resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767"
+  integrity sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=
   dependencies:
     array-filter "~0.0.0"
     array-map "~0.0.0"
@@ -5834,6 +6811,7 @@ shvl@^1.3.0:
 signal-exit@^3.0.0, signal-exit@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+  integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
 
 simple-swizzle@^0.2.2:
   version "0.2.2"
@@ -5841,6 +6819,19 @@ simple-swizzle@^0.2.2:
   dependencies:
     is-arrayish "^0.3.1"
 
+sinon@^7.2.2:
+  version "7.2.2"
+  resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.2.2.tgz#388ecabd42fa93c592bfc71d35a70894d5a0ca07"
+  integrity sha512-WLagdMHiEsrRmee3jr6IIDntOF4kbI6N2pfbi8wkv50qaUQcBglkzkjtoOEbeJ2vf1EsrHhLI+5Ny8//WHdMoA==
+  dependencies:
+    "@sinonjs/commons" "^1.2.0"
+    "@sinonjs/formatio" "^3.1.0"
+    "@sinonjs/samsam" "^3.0.2"
+    diff "^3.5.0"
+    lolex "^3.0.0"
+    nise "^1.4.7"
+    supports-color "^5.5.0"
+
 slash@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
@@ -5852,6 +6843,7 @@ slash@^2.0.0:
 slice-ansi@1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d"
+  integrity sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==
   dependencies:
     is-fullwidth-code-point "^2.0.0"
 
@@ -5886,16 +6878,17 @@ snapdragon@^0.8.1:
     source-map-resolve "^0.5.0"
     use "^3.1.0"
 
-sockjs-client@1.1.5:
-  version "1.1.5"
-  resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.5.tgz#1bb7c0f7222c40f42adf14f4442cbd1269771a83"
+sockjs-client@1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.3.0.tgz#12fc9d6cb663da5739d3dc5fb6e8687da95cb177"
+  integrity sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==
   dependencies:
-    debug "^2.6.6"
-    eventsource "0.1.6"
-    faye-websocket "~0.11.0"
-    inherits "^2.0.1"
+    debug "^3.2.5"
+    eventsource "^1.0.7"
+    faye-websocket "~0.11.1"
+    inherits "^2.0.3"
     json3 "^3.3.2"
-    url-parse "^1.1.8"
+    url-parse "^1.4.3"
 
 sockjs@0.3.19:
   version "0.3.19"
@@ -5926,6 +6919,7 @@ source-list-map@^2.0.0:
 source-map-resolve@^0.5.0:
   version "0.5.2"
   resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
+  integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==
   dependencies:
     atob "^2.1.1"
     decode-uri-component "^0.2.0"
@@ -5933,9 +6927,10 @@ source-map-resolve@^0.5.0:
     source-map-url "^0.4.0"
     urix "^0.1.0"
 
-source-map-support@^0.5.0:
+source-map-support@^0.5.0, source-map-support@~0.5.6:
   version "0.5.9"
   resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f"
+  integrity sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==
   dependencies:
     buffer-from "^1.0.0"
     source-map "^0.6.0"
@@ -5953,26 +6948,30 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
 
 spdx-correct@^3.0.0:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.1.tgz#434434ff9d1726b4d9f4219d1004813d80639e30"
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
+  integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==
   dependencies:
     spdx-expression-parse "^3.0.0"
     spdx-license-ids "^3.0.0"
 
 spdx-exceptions@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9"
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977"
+  integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==
 
 spdx-expression-parse@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0"
+  integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==
   dependencies:
     spdx-exceptions "^2.1.0"
     spdx-license-ids "^3.0.0"
 
 spdx-license-ids@^3.0.0:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz#e2a303236cac54b04031fa7a5a79c7e701df852f"
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz#81c0ce8f21474756148bbb5f3bfc0f36bf15d76e"
+  integrity sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==
 
 spdy-transport@^2.0.18:
   version "2.1.0"
@@ -6006,20 +7005,21 @@ split-string@^3.0.1, split-string@^3.0.2:
 sprintf-js@~1.0.2:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+  integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
 
 sshpk@^1.7.0:
-  version "1.14.2"
-  resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98"
+  version "1.16.0"
+  resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.0.tgz#1d4963a2fbffe58050aa9084ca20be81741c07de"
+  integrity sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ==
   dependencies:
     asn1 "~0.2.3"
     assert-plus "^1.0.0"
-    dashdash "^1.12.0"
-    getpass "^0.1.1"
-    safer-buffer "^2.0.2"
-  optionalDependencies:
     bcrypt-pbkdf "^1.0.0"
+    dashdash "^1.12.0"
     ecc-jsbn "~0.1.1"
+    getpass "^0.1.1"
     jsbn "~0.1.0"
+    safer-buffer "^2.0.2"
     tweetnacl "~0.14.0"
 
 ssri@^5.2.4:
@@ -6028,9 +7028,10 @@ ssri@^5.2.4:
   dependencies:
     safe-buffer "^5.1.1"
 
-ssri@^6.0.0:
+ssri@^6.0.0, ssri@^6.0.1:
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8"
+  integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==
   dependencies:
     figgy-pudding "^3.5.1"
 
@@ -6060,6 +7061,7 @@ statuses@~1.4.0:
 stealthy-require@^1.1.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
+  integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
 
 stream-browserify@^2.0.1:
   version "2.0.1"
@@ -6115,6 +7117,7 @@ string.prototype.padend@^3.0.0:
 string.prototype.padstart@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/string.prototype.padstart/-/string.prototype.padstart-3.0.0.tgz#5bcfad39f4649bb2d031292e19bcf0b510d4b242"
+  integrity sha1-W8+tOfRkm7LQMSkuGbzwtRDUskI=
   dependencies:
     define-properties "^1.1.2"
     es-abstract "^1.4.3"
@@ -6123,6 +7126,7 @@ string.prototype.padstart@^3.0.0:
 string_decoder@^1.0.0, string_decoder@~1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+  integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
   dependencies:
     safe-buffer "~5.1.0"
 
@@ -6139,16 +7143,26 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1:
 strip-ansi@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+  integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
   dependencies:
     ansi-regex "^3.0.0"
 
+strip-bom@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
+  integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=
+  dependencies:
+    is-utf8 "^0.2.0"
+
 strip-bom@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+  integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
 
 strip-eof@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
+  integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
 
 strip-indent@^2.0.0:
   version "2.0.0"
@@ -6157,6 +7171,7 @@ strip-indent@^2.0.0:
 strip-json-comments@~2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+  integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
 
 stylehacks@^4.0.0:
   version "4.0.1"
@@ -6175,16 +7190,19 @@ supports-color@3.1.2:
 supports-color@5.4.0:
   version "5.4.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
+  integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==
   dependencies:
     has-flag "^3.0.0"
 
 supports-color@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+  integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
 
 supports-color@^5.1.0, supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0:
   version "5.5.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
   dependencies:
     has-flag "^3.0.0"
 
@@ -6210,10 +7228,12 @@ svgo@^1.0.0:
 symbol-tree@^3.2.2:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
+  integrity sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=
 
 table@4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36"
+  integrity sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==
   dependencies:
     ajv "^5.2.3"
     ajv-keywords "^2.1.0"
@@ -6238,13 +7258,43 @@ tar@^4:
     safe-buffer "^5.1.2"
     yallist "^3.0.2"
 
+terser-webpack-plugin@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.1.0.tgz#cf7c25a1eee25bf121f4a587bb9e004e3f80e528"
+  integrity sha512-61lV0DSxMAZ8AyZG7/A4a3UPlrbOBo8NIQ4tJzLPAdGOQ+yoNC7l5ijEow27lBAL2humer01KLS6bGIMYQxKoA==
+  dependencies:
+    cacache "^11.0.2"
+    find-cache-dir "^2.0.0"
+    schema-utils "^1.0.0"
+    serialize-javascript "^1.4.0"
+    source-map "^0.6.1"
+    terser "^3.8.1"
+    webpack-sources "^1.1.0"
+    worker-farm "^1.5.2"
+
+terser@^3.8.1:
+  version "3.13.1"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-3.13.1.tgz#a02e8827fb9705fe7b609c31093d010b28cea8eb"
+  integrity sha512-ogyZye4DFqOtMzT92Y3Nxxw8OvXmL39HOALro4fc+EUYFFF9G/kk0znkvwMz6PPYgBtdKAodh3FPR70eugdaQA==
+  dependencies:
+    commander "~2.17.1"
+    source-map "~0.6.1"
+    source-map-support "~0.5.6"
+
+text-encoding@^0.6.4:
+  version "0.6.4"
+  resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19"
+  integrity sha1-45mpgiV6J22uQou5KEXLcb3CbRk=
+
 text-table@~0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+  integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
 
-thread-loader@^1.1.5:
+thread-loader@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/thread-loader/-/thread-loader-1.2.0.tgz#35dedb23cf294afbbce6c45c1339b950ed17e7a4"
+  integrity sha512-acJ0rvUk53+ly9cqYWNOpPqOgCkNpmHLPDGduNm4hDQWF7EDKEJXAopG9iEWsPPcml09wePkq3NF+ZUqnO6tbg==
   dependencies:
     async "^2.3.0"
     loader-runner "^2.3.0"
@@ -6259,7 +7309,8 @@ through2@^2.0.0:
 
 through@^2.3.6:
   version "2.3.8"
-  resolved "http://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+  resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+  integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
 
 thunkify@~2.1.1:
   version "2.1.2"
@@ -6282,6 +7333,7 @@ timsort@^0.3.0:
 tmp@^0.0.33:
   version "0.0.33"
   resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+  integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
   dependencies:
     os-tmpdir "~1.0.2"
 
@@ -6292,6 +7344,7 @@ to-arraybuffer@^1.0.0:
 to-fast-properties@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+  integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
 
 to-object-path@^0.3.0:
   version "0.3.0"
@@ -6316,18 +7369,28 @@ to-regex@^3.0.1, to-regex@^3.0.2:
     safe-regex "^1.1.0"
 
 topo@3.x.x:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/topo/-/topo-3.0.0.tgz#37e48c330efeac784538e0acd3e62ca5e231fe7a"
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/topo/-/topo-3.0.3.tgz#d5a67fb2e69307ebeeb08402ec2a2a6f5f7ad95c"
+  integrity sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==
   dependencies:
-    hoek "5.x.x"
+    hoek "6.x.x"
 
 toposort@^1.0.0:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029"
 
-tough-cookie@>=2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.4.3:
+tough-cookie@>=2.3.3, tough-cookie@^2.5.0:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
+  integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
+  dependencies:
+    psl "^1.1.28"
+    punycode "^2.1.1"
+
+tough-cookie@~2.4.3:
   version "2.4.3"
   resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
+  integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==
   dependencies:
     psl "^1.1.24"
     punycode "^1.4.1"
@@ -6335,20 +7398,23 @@ tough-cookie@>=2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.4.3:
 tr46@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
+  integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=
   dependencies:
     punycode "^2.1.0"
 
 trim-right@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
+  integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=
 
 tryer@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
 
-ts-loader@^4.4.2:
-  version "4.5.0"
-  resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-4.5.0.tgz#a1ce70b2dc799941fb2197605f0d67874097859b"
+ts-loader@^5.3.1:
+  version "5.3.2"
+  resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-5.3.2.tgz#31d10be522bedfac8ee4c20c735e05a9bd772faf"
+  integrity sha512-TPeXFkdPjOrVEawY4xUgRnzlHEmKQF1DclJghPGq67jKnroVvs6mEGHWYtbUczgeWTvTaqUjSSaMmp1k5do4vw==
   dependencies:
     chalk "^2.3.0"
     enhanced-resolve "^4.0.0"
@@ -6360,9 +7426,10 @@ tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0:
   version "1.9.3"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
 
-tslint@^5.10.0:
-  version "5.11.0"
-  resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.11.0.tgz#98f30c02eae3cde7006201e4c33cb08b48581eed"
+tslint@^5.11.0:
+  version "5.12.0"
+  resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.12.0.tgz#47f2dba291ed3d580752d109866fb640768fca36"
+  integrity sha512-CKEcH1MHUBhoV43SA/Jmy1l24HJJgI0eyLbBNSRyFlsQvb9v6Zdq+Nz2vEOH00nC5SUx4SneJ59PZUS/ARcokQ==
   dependencies:
     babel-code-frame "^6.22.0"
     builtin-modules "^1.1.1"
@@ -6390,16 +7457,19 @@ tty-browserify@0.0.0:
 tunnel-agent@^0.6.0:
   version "0.6.0"
   resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+  integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
   dependencies:
     safe-buffer "^5.0.1"
 
 tweetnacl@^0.14.3, tweetnacl@~0.14.0:
   version "0.14.5"
   resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+  integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
 
 type-check@~0.3.2:
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
+  integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
   dependencies:
     prelude-ls "~1.1.2"
 
@@ -6407,7 +7477,7 @@ type-detect@0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822"
 
-type-detect@^4.0.0, type-detect@^4.0.5:
+type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5:
   version "4.0.8"
   resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
 
@@ -6421,45 +7491,38 @@ type-is@~1.6.15, type-is@~1.6.16:
 typedarray@^0.0.6:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+  integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
+
+typescript-eslint-parser@^21.0.1:
+  version "21.0.2"
+  resolved "https://registry.yarnpkg.com/typescript-eslint-parser/-/typescript-eslint-parser-21.0.2.tgz#270af10e4724528677fbcf34ea495284bec3a894"
+  integrity sha512-u+pj4RVJBr4eTzj0n5npoXD/oRthvfUCjSKndhNI714MG0mQq2DJw5WP7qmonRNIFgmZuvdDOH3BHm9iOjIAfg==
+  dependencies:
+    eslint-scope "^4.0.0"
+    eslint-visitor-keys "^1.0.0"
+    typescript-estree "5.3.0"
 
-typescript-eslint-parser@^18.0.0:
-  version "18.0.0"
-  resolved "https://registry.yarnpkg.com/typescript-eslint-parser/-/typescript-eslint-parser-18.0.0.tgz#3e5055a44980d69e4154350fc5d8b1ab4e2332a8"
+typescript-estree@5.3.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/typescript-estree/-/typescript-estree-5.3.0.tgz#fb6c977b5e21073eb16cbdc0338a7f752da99ff5"
+  integrity sha512-Vu0KmYdSCkpae+J48wsFC1ti19Hq3Wi/lODUaE+uesc3gzqhWbZ5itWbsjylLVbjNW4K41RqDzSfnaYNbmEiMQ==
   dependencies:
     lodash.unescape "4.0.1"
     semver "5.5.0"
 
-typescript@^3.0.0:
-  version "3.1.1"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.1.tgz#3362ba9dd1e482ebb2355b02dfe8bcd19a2c7c96"
-
-uglify-es@^3.3.4:
-  version "3.3.9"
-  resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
-  dependencies:
-    commander "~2.13.0"
-    source-map "~0.6.1"
+typescript@^3.2.2:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.2.tgz#fe8101c46aa123f8353523ebdcf5730c2ae493e5"
+  integrity sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==
 
 uglify-js@3.4.x:
   version "3.4.9"
   resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"
+  integrity sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==
   dependencies:
     commander "~2.17.1"
     source-map "~0.6.1"
 
-uglifyjs-webpack-plugin@^1.2.4, uglifyjs-webpack-plugin@^1.2.7:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz#75f548160858163a08643e086d5fefe18a5d67de"
-  dependencies:
-    cacache "^10.0.4"
-    find-cache-dir "^1.0.0"
-    schema-utils "^0.4.5"
-    serialize-javascript "^1.4.0"
-    source-map "^0.6.1"
-    uglify-es "^3.3.4"
-    webpack-sources "^1.1.0"
-    worker-farm "^1.5.2"
-
 union-value@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4"
@@ -6526,15 +7589,16 @@ urix@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
 
-url-loader@^1.1.0:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.1.1.tgz#4d1f3b4f90dde89f02c008e662d604d7511167c1"
+url-loader@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.1.2.tgz#b971d191b83af693c5e3fea4064be9e1f2d7f8d8"
+  integrity sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==
   dependencies:
     loader-utils "^1.1.0"
     mime "^2.0.3"
     schema-utils "^1.0.0"
 
-url-parse@^1.1.8, url-parse@^1.4.3:
+url-parse@^1.4.3:
   version "1.4.3"
   resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.3.tgz#bfaee455c889023219d757e045fa6a684ec36c15"
   dependencies:
@@ -6555,10 +7619,12 @@ use@^3.1.0:
 util-deprecate@~1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+  integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
 
 util.promisify@1.0.0, util.promisify@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030"
+  integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==
   dependencies:
     define-properties "^1.1.2"
     object.getownpropertydescriptors "^2.0.3"
@@ -6590,14 +7656,17 @@ utils-merge@1.0.1:
 uuid@^3.0.1, uuid@^3.3.2:
   version "3.3.2"
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
+  integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
 
 v-clipboard@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/v-clipboard/-/v-clipboard-2.0.1.tgz#db5ce77338a9d7215daa342d4815cdb480f31658"
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/v-clipboard/-/v-clipboard-2.2.1.tgz#00519943312fe692acfda7cb4da11928db489c10"
+  integrity sha512-at0rawb+VbNrTIblpzKK6iDtHad0c8VvjG8aFBnEL5BSB7e/pdtWJZhpgXYaxcHW9soHlH/i7pI/sjackZHA0g==
 
 validate-npm-package-license@^3.0.1:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
+  integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
   dependencies:
     spdx-correct "^3.0.0"
     spdx-expression-parse "^3.0.0"
@@ -6613,6 +7682,7 @@ vendors@^1.0.0:
 verror@1.10.0:
   version "1.10.0"
   resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
+  integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
   dependencies:
     assert-plus "^1.0.0"
     core-util-is "1.0.2"
@@ -6625,12 +7695,14 @@ vm-browserify@0.0.4:
     indexof "0.0.1"
 
 vue-class-component@^6.0.0, vue-class-component@^6.2.0:
-  version "6.2.0"
-  resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-6.2.0.tgz#7adb1daa9a868c75f30f97f33f4f1b94aee62089"
+  version "6.3.2"
+  resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-6.3.2.tgz#e6037e84d1df2af3bde4f455e50ca1b9eec02be6"
+  integrity sha512-cH208IoM+jgZyEf/g7mnFyofwPDJTM/QvBNhYMjqGB8fCsRyTf68rH2ISw/G20tJv+5mIThQ3upKwoL4jLTr1A==
 
 vue-eslint-parser@^2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz#c268c96c6d94cfe3d938a5f7593959b0ca3360d1"
+  integrity sha512-ZezcU71Owm84xVF6gfurBQUGg8WQ+WZGxgDEQu1IHFBZNx7BFZg3L1yHxrCBNNwbwFtE1GuvfJKMtb6Xuwc/Bw==
   dependencies:
     debug "^3.1.0"
     eslint-scope "^3.7.1"
@@ -6654,18 +7726,21 @@ vue-loader@^15.4.2:
     vue-style-loader "^4.1.0"
 
 vue-notification@^1.3.12:
-  version "1.3.13"
-  resolved "https://registry.yarnpkg.com/vue-notification/-/vue-notification-1.3.13.tgz#e9a6d61888c8a4c91a4d3595f8e272462484016f"
+  version "1.3.14"
+  resolved "https://registry.yarnpkg.com/vue-notification/-/vue-notification-1.3.14.tgz#6717f9127bb34ed1e95d93644359421659680515"
+  integrity sha512-rtqCspGydHbT+HhaTJcREcSKTrWJM8tFPbo5W3UdpSEEKz0O9pLtxeh5OMTCVb5JrEvhOL3AuUroMdVtFb3Onw==
 
 vue-property-decorator@^7.0.0:
-  version "7.1.1"
-  resolved "https://registry.yarnpkg.com/vue-property-decorator/-/vue-property-decorator-7.1.1.tgz#0b1eefde50a7451e911ad44b91258010ffc08d01"
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/vue-property-decorator/-/vue-property-decorator-7.2.0.tgz#8e6b0f4dcc630c357135f76366ba7bd91f1db015"
+  integrity sha512-sCI6NVM3tEDg+mpZrQlgkddtxd9LbFWetue8D+nqO3agfSLz0KoC/UIi2P1l5E0TDhcUeIXS9rasuP2HWg+L4w==
   dependencies:
     vue-class-component "^6.2.0"
 
 vue-router@^3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9"
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.2.tgz#dedc67afe6c4e2bc25682c8b1c2a8c0d7c7e56be"
+  integrity sha512-opKtsxjp9eOcFWdp6xLQPLmRGgfM932Tl56U9chYTnoWqKxQ8M20N7AkdEbM5beUh6wICoFGYugAX9vQjyJLFg==
 
 vue-style-loader@^4.1.0:
   version "4.1.2"
@@ -6675,8 +7750,9 @@ vue-style-loader@^4.1.0:
     loader-utils "^1.0.2"
 
 vue-template-compiler@^2.5.16:
-  version "2.5.17"
-  resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.17.tgz#52a4a078c327deb937482a509ae85c06f346c3cb"
+  version "2.5.21"
+  resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.21.tgz#a57ceb903177e8f643560a8d639a0f8db647054a"
+  integrity sha512-Vmk5Cv7UcmI99B9nXJEkaK262IQNnHp5rJYo+EwYpe2epTAXqcVyExhV6pk8jTkxQK2vRc8v8KmZBAwdmUZvvw==
   dependencies:
     de-indent "^1.0.2"
     he "^1.1.0"
@@ -6686,12 +7762,14 @@ vue-template-es2015-compiler@^1.6.0:
   resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
 
 vue@^2.5.16:
-  version "2.5.17"
-  resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.17.tgz#0f8789ad718be68ca1872629832ed533589c6ada"
+  version "2.5.21"
+  resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.21.tgz#3d33dcd03bb813912ce894a8303ab553699c4a85"
+  integrity sha512-Aejvyyfhn0zjVeLvXd70h4hrE4zZDx1wfZqia6ekkobLmUZ+vNFQer53B4fu0EjWBSiqApxPejzkO1Znt3joxQ==
 
 vuetify@^1.1.9:
-  version "1.2.5"
-  resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-1.2.5.tgz#91578367709b20e406f2cdc7a164dfd70df954d9"
+  version "1.3.15"
+  resolved "https://registry.yarnpkg.com/vuetify/-/vuetify-1.3.15.tgz#8ef70a931d38093072775fd5a323b7ec882a45c6"
+  integrity sha512-4xpSvvz2OPwf8L9grz4clsgRswvw8xRJKe1xGmUi26gvbXllqPAMtiMt84vwXoGWYCb7dhnj/bWZ6h3Wj8lNLg==
 
 vuex-persistedstate@^2.5.4:
   version "2.5.4"
@@ -6714,9 +7792,19 @@ vuex@^3.0.0, vuex@^3.0.1:
 w3c-hr-time@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045"
+  integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=
   dependencies:
     browser-process-hrtime "^0.1.2"
 
+w3c-xmlserializer@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.0.1.tgz#054cdcd359dc5d1f3ec9be4e272c756af4b21d39"
+  integrity sha512-XZGI1OH/OLQr/NaJhhPmzhngwcAnZDLytsvXnRmlYeRkmbb0I7sqFFA22erq4WQR0sUu17ZSQOAV9mFwCqKRNg==
+  dependencies:
+    domexception "^1.0.1"
+    webidl-conversions "^4.0.2"
+    xml-name-validator "^3.0.0"
+
 watchpack@^1.5.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"
@@ -6734,33 +7822,37 @@ wbuf@^1.1.0, wbuf@^1.7.2:
 wcwidth@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
+  integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=
   dependencies:
     defaults "^1.0.3"
 
 webidl-conversions@^4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
+  integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
 
-webpack-bundle-analyzer@^2.13.1:
-  version "2.13.1"
-  resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.13.1.tgz#07d2176c6e86c3cdce4c23e56fae2a7b6b4ad526"
+webpack-bundle-analyzer@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.0.3.tgz#dbc7fff8f52058b6714a20fddf309d0790e3e0a0"
+  integrity sha512-naLWiRfmtH4UJgtUktRTLw6FdoZJ2RvCR9ePbwM9aRMsS/KjFerkPZG9epEvXRAw5d5oPdrs9+3p+afNjxW8Xw==
   dependencies:
-    acorn "^5.3.0"
-    bfj-node4 "^5.2.0"
-    chalk "^2.3.0"
-    commander "^2.13.0"
-    ejs "^2.5.7"
-    express "^4.16.2"
-    filesize "^3.5.11"
-    gzip-size "^4.1.0"
-    lodash "^4.17.4"
+    acorn "^5.7.3"
+    bfj "^6.1.1"
+    chalk "^2.4.1"
+    commander "^2.18.0"
+    ejs "^2.6.1"
+    express "^4.16.3"
+    filesize "^3.6.1"
+    gzip-size "^5.0.0"
+    lodash "^4.17.10"
     mkdirp "^0.5.1"
-    opener "^1.4.3"
-    ws "^4.0.0"
+    opener "^1.5.1"
+    ws "^6.0.0"
 
-webpack-chain@^4.8.0:
-  version "4.12.0"
-  resolved "https://registry.yarnpkg.com/webpack-chain/-/webpack-chain-4.12.0.tgz#2b73189c9155d11afad6ed1b990ac1942a571deb"
+webpack-chain@^4.11.0:
+  version "4.12.1"
+  resolved "https://registry.yarnpkg.com/webpack-chain/-/webpack-chain-4.12.1.tgz#6c8439bbb2ab550952d60e1ea9319141906c02a6"
+  integrity sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ==
   dependencies:
     deepmerge "^1.5.2"
     javascript-stringify "^1.6.0"
@@ -6774,9 +7866,10 @@ webpack-dev-middleware@3.4.0:
     range-parser "^1.0.3"
     webpack-log "^2.0.0"
 
-webpack-dev-server@^3.1.4:
-  version "3.1.9"
-  resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.1.9.tgz#8b32167624d2faff40dcedc2cbce17ed1f34d3e0"
+webpack-dev-server@^3.1.10:
+  version "3.1.10"
+  resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.1.10.tgz#507411bee727ee8d2fdffdc621b66a64ab3dea2b"
+  integrity sha512-RqOAVjfqZJtQcB0LmrzJ5y4Jp78lv9CK0MZ1YJDTaTmedMZ9PU9FLMQNrMCfVu8hHzaVLVOJKBlGEHMN10z+ww==
   dependencies:
     ansi-html "0.0.7"
     bonjour "^3.5.0"
@@ -6799,7 +7892,7 @@ webpack-dev-server@^3.1.4:
     selfsigned "^1.9.1"
     serve-index "^1.7.2"
     sockjs "0.3.19"
-    sockjs-client "1.1.5"
+    sockjs-client "1.3.0"
     spdy "^3.4.1"
     strip-ansi "^3.0.0"
     supports-color "^5.1.0"
@@ -6814,9 +7907,10 @@ webpack-log@^2.0.0:
     ansi-colors "^3.0.0"
     uuid "^3.3.2"
 
-webpack-merge@^4.1.3:
-  version "4.1.4"
-  resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.4.tgz#0fde38eabf2d5fd85251c24a5a8c48f8a3f4eb7b"
+webpack-merge@^4.1.4:
+  version "4.1.5"
+  resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.5.tgz#2be31e846c20767d1bef56bdca64c328a681190a"
+  integrity sha512-sVcM+MMJv6DO0C0GLLltx8mUlGMKXE0zBsuMqZ9jz2X9gsekALw6Rs0cAfTWc97VuWS6NpVUa78959zANnMMLQ==
   dependencies:
     lodash "^4.17.5"
 
@@ -6827,14 +7921,15 @@ webpack-sources@^1.1.0, webpack-sources@^1.3.0:
     source-list-map "^2.0.0"
     source-map "~0.6.1"
 
-webpack@^4.15.1:
-  version "4.20.2"
-  resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.20.2.tgz#89f6486b6bb276a91b0823453d377501fc625b5a"
+webpack@^4.26.1:
+  version "4.28.1"
+  resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.28.1.tgz#d0e2856e75d1224b170bf16c30b6ca9b75f0d958"
+  integrity sha512-qAS7BFyS5iuOZzGJxyDXmEI289h7tVNtJ5XMxf6Tz55J2riOyH42uaEsWF0F32TRaI+54SmI6qRgHM3GzsZ+sQ==
   dependencies:
-    "@webassemblyjs/ast" "1.7.8"
-    "@webassemblyjs/helper-module-context" "1.7.8"
-    "@webassemblyjs/wasm-edit" "1.7.8"
-    "@webassemblyjs/wasm-parser" "1.7.8"
+    "@webassemblyjs/ast" "1.7.11"
+    "@webassemblyjs/helper-module-context" "1.7.11"
+    "@webassemblyjs/wasm-edit" "1.7.11"
+    "@webassemblyjs/wasm-parser" "1.7.11"
     acorn "^5.6.2"
     acorn-dynamic-import "^3.0.0"
     ajv "^6.1.0"
@@ -6852,7 +7947,7 @@ webpack@^4.15.1:
     node-libs-browser "^2.0.0"
     schema-utils "^0.4.4"
     tapable "^1.1.0"
-    uglifyjs-webpack-plugin "^1.2.4"
+    terser-webpack-plugin "^1.1.0"
     watchpack "^1.5.0"
     webpack-sources "^1.3.0"
 
@@ -6867,32 +7962,32 @@ websocket-extensions@>=0.1.1:
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
 
-whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
+whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
+  integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==
   dependencies:
     iconv-lite "0.4.24"
 
-whatwg-mimetype@^2.1.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz#a3d58ef10b76009b042d03e25591ece89b88d171"
-
-whatwg-url@^6.4.1:
-  version "6.5.0"
-  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8"
-  dependencies:
-    lodash.sortby "^4.7.0"
-    tr46 "^1.0.1"
-    webidl-conversions "^4.0.2"
+whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
+  integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
 
 whatwg-url@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd"
+  integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==
   dependencies:
     lodash.sortby "^4.7.0"
     tr46 "^1.0.1"
     webidl-conversions "^4.0.2"
 
+which-module@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
+  integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=
+
 which-module@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
@@ -6900,6 +7995,7 @@ which-module@^2.0.0:
 which@^1.2.9:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+  integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
   dependencies:
     isexe "^2.0.0"
 
@@ -6909,6 +8005,11 @@ wide-align@^1.1.0:
   dependencies:
     string-width "^1.0.2 || 2"
 
+window-size@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075"
+  integrity sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=
+
 wordwrap@~0.0.2:
   version "0.0.3"
   resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
@@ -6916,6 +8017,7 @@ wordwrap@~0.0.2:
 wordwrap@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
+  integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
 
 worker-farm@^1.5.2:
   version "1.6.0"
@@ -6933,29 +8035,31 @@ wrap-ansi@^2.0.0:
 wrappy@1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
 
 write@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"
+  integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=
   dependencies:
     mkdirp "^0.5.1"
 
-ws@^4.0.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-4.1.0.tgz#a979b5d7d4da68bf54efe0408967c324869a7289"
-  dependencies:
-    async-limiter "~1.0.0"
-    safe-buffer "~5.1.0"
-
-ws@^5.2.0:
-  version "5.2.2"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f"
+ws@^6.0.0, ws@^6.1.2:
+  version "6.1.2"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.2.tgz#3cc7462e98792f0ac679424148903ded3b9c3ad8"
+  integrity sha512-rfUqzvz0WxmSXtJpPMX2EeASXabOrSMk1ruMOV3JBTBjo4ac2lDjGGsbQSyxj8Odhw5fBib8ZKEjDNvgouNKYw==
   dependencies:
     async-limiter "~1.0.0"
 
 xml-name-validator@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
+  integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
+
+xmlchars@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-1.3.1.tgz#1dda035f833dbb4f86a0c28eaa6ca769214793cf"
+  integrity sha512-tGkGJkN8XqCod7OT+EvGYK5Z4SfDQGD30zAa58OcnAa0RRWgzUEK72tkXhsX1FZd+rgnhRxFtmO+ihkp8LHSkw==
 
 xregexp@2.0.0:
   version "2.0.0"
@@ -6980,6 +8084,7 @@ y18n@^3.2.1:
 yallist@^2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
+  integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
 
 yallist@^3.0.0, yallist@^3.0.2:
   version "3.0.2"
@@ -6991,9 +8096,18 @@ yargs-parser@^10.1.0:
   dependencies:
     camelcase "^4.1.0"
 
+yargs-parser@^2.4.1:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4"
+  integrity sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=
+  dependencies:
+    camelcase "^3.0.0"
+    lodash.assign "^4.0.6"
+
 yargs-parser@^9.0.2:
   version "9.0.2"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077"
+  integrity sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=
   dependencies:
     camelcase "^4.1.0"
 
@@ -7016,7 +8130,8 @@ yargs@12.0.2:
 
 yargs@^11.0.0:
   version "11.1.0"
-  resolved "http://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77"
+  integrity sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==
   dependencies:
     cliui "^4.0.0"
     decamelize "^1.1.1"
@@ -7031,6 +8146,26 @@ yargs@^11.0.0:
     y18n "^3.2.1"
     yargs-parser "^9.0.2"
 
+yargs@^4.8.0:
+  version "4.8.1"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0"
+  integrity sha1-wMQpJMpKqmsObaFznfshZDn53cA=
+  dependencies:
+    cliui "^3.2.0"
+    decamelize "^1.1.1"
+    get-caller-file "^1.0.1"
+    lodash.assign "^4.0.3"
+    os-locale "^1.4.0"
+    read-pkg-up "^1.0.1"
+    require-directory "^2.1.1"
+    require-main-filename "^1.0.1"
+    set-blocking "^2.0.0"
+    string-width "^1.0.1"
+    which-module "^1.0.0"
+    window-size "^0.2.0"
+    y18n "^3.2.1"
+    yargs-parser "^2.4.1"
+
 yauzl@2.4.1:
   version "2.4.1"
   resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"
diff --git a/functional_tests/test_export_modal.py b/functional_tests/test_export_modal.py
new file mode 100644
index 00000000..346842dc
--- /dev/null
+++ b/functional_tests/test_export_modal.py
@@ -0,0 +1,117 @@
+from django.test import LiveServerTestCase
+from selenium import webdriver
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as ec
+
+from core.models import UserAccount
+from functional_tests.util import login, create_browser, reset_browser_after_test
+from util import factory_boys as fact
+
+
+class ExportTestModal(LiveServerTestCase):
+    browser: webdriver.Firefox = None
+    username = None
+    password = None
+    role = None
+
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        cls.browser = create_browser()
+
+    @classmethod
+    def tearDownClass(cls):
+        super().tearDownClass()
+        cls.browser.quit()
+
+    def setUp(self):
+        self.username = 'reviewer'
+        self.password = 'p'
+        self.role = UserAccount.REVIEWER
+        fact.UserAccountFactory(
+            username=self.username,
+            password=self.password,
+            role=self.role
+        )
+
+    def tearDown(self):
+        reset_browser_after_test(self.browser, self.live_server_url)
+
+    def _login(self):
+        login(self.browser, self.live_server_url, self.username, self.password)
+
+    def test_export_red_uncorrected_submissions(self):
+        def export_btn_is_red(*args):
+            exports_btn = self.browser.find_element_by_id('export-btn')
+            return 'red' in exports_btn.get_attribute('class')
+
+        fact.SubmissionFactory()
+        self._login()
+        WebDriverWait(self.browser, 3).until(export_btn_is_red)
+
+    def test_export_warning_tooltip_uncorrected_submissions(self):
+        fact.SubmissionFactory()
+        self._login()
+        tooltip_uncorrected = self.browser.find_element_by_id('uncorrected-tooltip')
+        self.assertNotEqual(None, tooltip_uncorrected)
+        self.assertRaises(Exception, self.browser.find_element_by_id, 'corrected-tooltip')
+
+    def test_export_green_all_corrected(self):
+        self._login()
+        exports_btn = self.browser.find_element_by_id('export-btn')
+        self.assertIn('green', exports_btn.get_attribute('class'))
+
+    def test_export_allgood_tooltip_all_corrected(self):
+        self._login()
+        tooltip_corrected = self.browser.find_element_by_id('corrected-tooltip')
+        self.assertNotEqual(None, tooltip_corrected)
+        self.assertRaises(Exception, self.browser.find_element_by_id, 'uncorrected-tooltip')
+
+    def test_export_list_popup_contains_correct_items(self):
+        self._login()
+        export_btn = self.browser.find_element_by_id('export-btn')
+        export_btn.click()
+        export_menu = self.browser.find_element_by_class_name('menuable__content__active')
+        export_list = export_menu.find_element_by_class_name('v-list')
+        list_elements = export_list.find_elements_by_tag_name('div')
+        self.assertEqual(2, len(list_elements))
+        self.assertEqual('Export student scores',
+                         list_elements[0].find_element_by_tag_name('a').text)
+        self.assertEqual('Export whole instance data',
+                         list_elements[1].find_element_by_tag_name('a').text)
+
+    def test_export_student_scores_as_json(self):
+        self._login()
+        fact.StudentInfoFactory()
+        export_btn = self.browser.find_element_by_id('export-btn')
+        export_btn.click()
+        export_scores = self.browser.find_element_by_id('export-list0')
+        export_scores.click()
+        data_export_modal = self.browser.find_element_by_id('data-export-modal')
+        export_type_select = data_export_modal.find_element_by_id('type-select')
+        export_type_select.click()
+        export_type_json = data_export_modal.find_element_by_xpath("//*[contains(text(), 'JSON')]")
+        export_type_json.click()
+        data_export_btn = data_export_modal.find_element_by_id('export-data-download-btn')
+        data_export_btn.click()
+        WebDriverWait(self.browser, 10).until(ec.new_window_is_opened)
+        tabs = self.browser.window_handles
+        self.assertEqual(2, len(tabs))
+        self.browser.switch_to.window(tabs[1])
+        self.assertIn('B.Inf.4242 Test Module', self.browser.find_element_by_tag_name('body').text)
+
+    def test_export_instance(self):
+        self._login()
+        fact.SubmissionFactory()
+        export_btn = self.browser.find_element_by_id('export-btn')
+        export_btn.click()
+        export_instance = self.browser.find_element_by_id('export-list1')
+        export_instance.click()
+        instance_export_modal = self.browser.find_element_by_id('instance-export-modal')
+        instance_export_btn = instance_export_modal.find_element_by_id('instance-export-dl')
+        instance_export_btn.click()
+        WebDriverWait(self.browser, 10).until(ec.new_window_is_opened)
+        tabs = self.browser.window_handles
+        self.assertEqual(2, len(tabs))
+        self.browser.switch_to.window(tabs[1])
+        self.assertIn('B.Inf.4242 Test Module', self.browser.find_element_by_tag_name('body').text)
diff --git a/functional_tests/test_front_pages.py b/functional_tests/test_front_pages.py
index 70e8dc08..ec38b58f 100644
--- a/functional_tests/test_front_pages.py
+++ b/functional_tests/test_front_pages.py
@@ -5,10 +5,15 @@ from selenium import webdriver
 
 from core import models
 from core.models import UserAccount
-from functional_tests.util import login, create_browser, extract_hrefs_hashes
+from functional_tests.util import (login, create_browser,
+                                   extract_hrefs_hashes, reset_browser_after_test)
 from util import factory_boys as fact
 
 
+# This is a little hack to have Super test class which implements common behaviour
+# and tests but is not executed. In order to have the testrunner ignore the
+# FrontPageTestsTutorReviewer class we need to define it within a class which does not inherit from
+# unittest
 class UntestedParent:
     class FrontPageTestsTutorReviewer(LiveServerTestCase):
         browser: webdriver.Firefox = None
@@ -16,9 +21,22 @@ class UntestedParent:
         password = None
         role = None
 
+        @classmethod
+        def setUpClass(cls):
+            super().setUpClass()
+            cls.browser = create_browser()
+
+        @classmethod
+        def tearDownClass(cls):
+            super().tearDownClass()
+            cls.browser.quit()
+
         def setUp(self):
             fact.SubmissionFactory.create_batch(4)
 
+        def tearDown(self):
+            reset_browser_after_test(self.browser, self.live_server_url)
+
         def _login(self):
             login(self.browser, self.live_server_url, self.username, self.password)
 
@@ -49,7 +67,6 @@ class UntestedParent:
 class FrontPageTestsTutor(UntestedParent.FrontPageTestsTutorReviewer):
     def setUp(self):
         super().setUp()
-        self.browser = create_browser()
         self.username = 'tutor'
         self.password = 'p'
         self.role = UserAccount.TUTOR
@@ -58,9 +75,6 @@ class FrontPageTestsTutor(UntestedParent.FrontPageTestsTutorReviewer):
             password=self.password
         )
 
-    def tearDown(self):
-        self.browser.quit()
-
     def test_side_bar_contains_correct_items(self):
         self._login()
         drawer = self.browser.find_element_by_class_name('v-navigation-drawer')
@@ -79,8 +93,6 @@ class FrontPageTestsTutor(UntestedParent.FrontPageTestsTutorReviewer):
 class FrontPageTestsReviewer(UntestedParent.FrontPageTestsTutorReviewer):
     def setUp(self):
         super().setUp()
-        self.browser = create_browser()
-        self.browser = create_browser()
         self.username = 'reviewer'
         self.password = 'p'
         self.role = UserAccount.REVIEWER
@@ -90,9 +102,6 @@ class FrontPageTestsReviewer(UntestedParent.FrontPageTestsTutorReviewer):
             role=self.role
         )
 
-    def tearDown(self):
-        self.browser.quit()
-
     def test_side_bar_contains_correct_items(self):
         self._login()
         drawer = self.browser.find_element_by_class_name('v-navigation-drawer')
diff --git a/functional_tests/test_login_page.py b/functional_tests/test_login_page.py
index 21907957..0849f0c6 100644
--- a/functional_tests/test_login_page.py
+++ b/functional_tests/test_login_page.py
@@ -1,25 +1,28 @@
-import os
 import time
 
 from django.test import LiveServerTestCase
 from selenium import webdriver
 from selenium.webdriver.common.keys import Keys
-from selenium.webdriver.firefox.options import Options
 
 from core.models import UserAccount
 from util.factories import make_test_data
+from functional_tests.util import create_browser, reset_browser_after_test
 
 
-LiveServerTestCase.port = int(os.environ.get('LIVE_SERVER_PORT', 0))
+class LoginPageTest(LiveServerTestCase):
+    browser: webdriver.Firefox = None
 
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        cls.browser = create_browser()
+
+    @classmethod
+    def tearDownClass(cls):
+        super().tearDownClass()
+        cls.browser.quit()
 
-class LoginPageTest(LiveServerTestCase):
     def setUp(self):
-        options = Options()
-        # funnily the method is marked deprecated but the alternative setter is not working...
-        options.headless = bool(os.environ.get('HEADLESS_TESTS', False))
-        self.browser = webdriver.Firefox(options=options)
-        self.browser.implicitly_wait(5)
         self.test_data = make_test_data(data_dict={
             'submission_types': [
                 {
@@ -90,7 +93,7 @@ class LoginPageTest(LiveServerTestCase):
         )
 
     def tearDown(self):
-        self.browser.quit()
+        reset_browser_after_test(self.browser, self.live_server_url)
 
     def _login(self, account):
         self.browser.get(self.live_server_url)
diff --git a/functional_tests/util.py b/functional_tests/util.py
index c1ffba6f..1333e36a 100644
--- a/functional_tests/util.py
+++ b/functional_tests/util.py
@@ -1,5 +1,4 @@
 import os
-import time
 from itertools import islice
 from typing import Sequence
 
@@ -9,11 +8,11 @@ from selenium.webdriver.firefox.options import Options
 from selenium.webdriver.remote.webelement import WebElement
 
 
-def create_browser():
+def create_browser() -> webdriver.Firefox:
     options = Options()
     options.headless = bool(os.environ.get('HEADLESS_TESTS', False))
     browser = webdriver.Firefox(options=options)
-    browser.implicitly_wait(5)
+    browser.implicitly_wait(10)
     return browser
 
 
@@ -24,7 +23,14 @@ def login(browser, live_server_url, username, password='p'):
     password_input = browser.find_element_by_xpath('//input[@aria-label="Password"]')
     password_input.send_keys(password)
     browser.find_element_by_xpath('//button[@type="submit"]').send_keys(Keys.ENTER)
-    time.sleep(1)
+
+
+def reset_browser_after_test(browser: webdriver.Firefox, live_server_url):
+    while len(browser.window_handles) > 1:
+        browser.close()
+    browser.switch_to.window(browser.window_handles[0])
+    browser.execute_script("window.sessionStorage.clear()")
+    browser.get(live_server_url)
 
 
 def nth(iterable, n, default=None):
diff --git a/util/factory_boys.py b/util/factory_boys.py
index 4de7d9ed..e89dd70a 100644
--- a/util/factory_boys.py
+++ b/util/factory_boys.py
@@ -21,7 +21,7 @@ class ExamTypeFactory(DjangoModelFactory):
 class SubmissionTypeFactory(DjangoModelFactory):
     class Meta:
         model = models.SubmissionType
-    name = factory.Sequence(lambda n: f"[${n}] Example submission type ")
+    name = factory.Sequence(lambda n: f"[{n}] Example submission type ")
     full_score = 15
     description = '<h1>This</h1> is a <b>description</b> containing html'
     solution = 'This usually contains commented code'
-- 
GitLab


From d7a76ebf03c43d2004f90103e064802b3e3c742b Mon Sep 17 00:00:00 2001
From: Dominik Seeger <dominik.seeger@gmx.net>
Date: Sat, 29 Dec 2018 18:09:40 +0100
Subject: [PATCH 07/21] Added first tests for AutoLogout component

---
 core/views/subscription.py                    |  2 +-
 frontend/src/components/AutoLogout.vue        |  2 +
 .../src/store/modules/submission-notes.ts     | 13 +++---
 .../AutoLogout.spec.ts}                       | 43 +++++++++++++++----
 functional_tests/test_export_modal.py         | 12 +++---
 functional_tests/test_front_pages.py          | 14 ++++--
 functional_tests/test_login_page.py           | 11 ++---
 functional_tests/util.py                      |  3 ++
 grady/settings/test.py                        |  1 +
 9 files changed, 71 insertions(+), 30 deletions(-)
 rename frontend/tests/unit/{autologout.spec.ts => components/AutoLogout.spec.ts} (51%)

diff --git a/core/views/subscription.py b/core/views/subscription.py
index 88dc1d3e..266bd647 100644
--- a/core/views/subscription.py
+++ b/core/views/subscription.py
@@ -97,7 +97,7 @@ class AssignmentApiViewSet(
 
     @permission_classes((IsReviewer,))
     def list(self, *args, **kwargs):
-        super().list(*args, **kwargs)
+        return super().list(*args, **kwargs)
 
     @action(detail=False, permission_classes=(IsReviewer,), methods=['get', 'delete'])
     def active(self, request):
diff --git a/frontend/src/components/AutoLogout.vue b/frontend/src/components/AutoLogout.vue
index fd45c04d..44af1434 100644
--- a/frontend/src/components/AutoLogout.vue
+++ b/frontend/src/components/AutoLogout.vue
@@ -15,10 +15,12 @@
       </v-card-text>
       <v-card-actions>
         <v-btn flat color="grey lighten-0"
+               id="logout-btn"
                @click="logout"
         >Logout now</v-btn>
         <v-spacer/>
         <v-btn flat color="blue darken-2"
+               id="continue-btn"
                @click="continueWork"
         >Continue</v-btn>
       </v-card-actions>
diff --git a/frontend/src/store/modules/submission-notes.ts b/frontend/src/store/modules/submission-notes.ts
index 3c1f1376..b34a6d71 100644
--- a/frontend/src/store/modules/submission-notes.ts
+++ b/frontend/src/store/modules/submission-notes.ts
@@ -1,14 +1,10 @@
 import Vue from 'vue'
 import * as hljs from 'highlight.js'
 import * as api from '@/api'
-import { nameSpacer } from '@/util/helpers'
-import { Feedback, FeedbackComment, Submission, SubmissionNoType } from '@/models'
+import { Feedback, FeedbackComment, SubmissionNoType } from '@/models'
 import { RootState } from '@/store/store'
-import { Module } from 'vuex'
 import { getStoreBuilder, BareActionContext } from 'vuex-typex'
 
-export const subNotesNamespace = nameSpacer('submissionNotes/')
-
 export interface SubmissionNotesState {
   submission: SubmissionNoType
   ui: {
@@ -143,13 +139,16 @@ async function submitFeedback ({ state }: BareActionContext<SubmissionNotesState
   }
   if (state.origFeedback.score === undefined && state.updatedFeedback.score === undefined) {
     throw new Error('You need to give a score.')
+  } else if (state.updatedFeedback.score !== undefined) {
+    feedback.score = state.updatedFeedback.score
   } else {
-    feedback.score = state.updatedFeedback.score || state.origFeedback.score || 0
+    feedback.score = state.origFeedback.score
   }
+
   if (Object.keys(state.updatedFeedback.feedbackLines || {}).length > 0) {
     feedback.feedbackLines = state.updatedFeedback.feedbackLines
   } else if (feedback.score! < SubmissionNotes.submissionType.fullScore!) {
-    throw new Error('You need to provide a reason for a reduced score.')
+    throw new Error('You need to add or change a comment when setting a non full score.')
   }
   // delete those comments that have been marked for deletion
   await SubmissionNotes.deleteComments()
diff --git a/frontend/tests/unit/autologout.spec.ts b/frontend/tests/unit/components/AutoLogout.spec.ts
similarity index 51%
rename from frontend/tests/unit/autologout.spec.ts
rename to frontend/tests/unit/components/AutoLogout.spec.ts
index 823f4815..e33bdabf 100644
--- a/frontend/tests/unit/autologout.spec.ts
+++ b/frontend/tests/unit/components/AutoLogout.spec.ts
@@ -1,33 +1,58 @@
 import Vuex from 'vuex'
 import { mount, createLocalVue } from '@vue/test-utils'
-import AutoLogout from '../../src/components/AutoLogout.vue'
+import AutoLogout from '@/components/AutoLogout.vue'
 import sinon from 'sinon'
 import { Authentication } from '@/store/modules/authentication'
-
 import chai from 'chai'
 chai.should()
 
-const localVue = createLocalVue();
-localVue.use(Vuex);
+let localVue = createLocalVue()
+localVue.use(Vuex)
 
 describe('Auto Logout Unit Tests', () => {
+  let store: any = null
+  let consoleTemp = {
+    warn: console.warn,
+    error: console.error,
+  }
+
+  before(function() {
+    console.warn = function() {}
+    console.error = function() {}
+  })
+
+  after(function() {
+    console.warn = consoleTemp.warn
+    console.error = consoleTemp.error
+  })
+
+  beforeEach(() => {
+    store = new Vuex.Store({
+      state: {}
+    })
+  })
+
   afterEach(() => {
     sinon.restore()
   })
-
+  
   it('should be hidden by default', () => {
-    let store = new Vuex.Store({})
-    let wrapper = mount(AutoLogout, { localVue, store })
+    let wrapper = mount(AutoLogout, { localVue: localVue, store })
     wrapper.vm.$data.logoutDialog.should.equal(false)
     wrapper.html().should.contain('<div class="v-dialog v-dialog--persistent" style="max-width: 30%; display: none;">')
   })
   it('should be visible when logoutDialog is set to true', () => {
+    let store = new Vuex.Store({})
+    let wrapper = mount(AutoLogout, { localVue, store })
+    wrapper.vm.$data.logoutDialog = true
+    wrapper.html().should.contain('<div class="v-dialog v-dialog--active v-dialog--persistent" style="max-width: 30%;">')
+  })
+  it('should get a refresh token from the server when user clicks button', () => {
     let spy = sinon.spy()
     sinon.replace(Authentication, 'refreshJWT', spy)
     let store = new Vuex.Store({})
     let wrapper = mount(AutoLogout, { localVue, store })
-    // @ts-ignore
-    wrapper.vm.continueWork()
+    wrapper.find('#continue-btn').trigger('click')
     spy.called.should.equal(true)
   })
 })
diff --git a/functional_tests/test_export_modal.py b/functional_tests/test_export_modal.py
index 346842dc..fbc3b130 100644
--- a/functional_tests/test_export_modal.py
+++ b/functional_tests/test_export_modal.py
@@ -93,8 +93,9 @@ class ExportTestModal(LiveServerTestCase):
         export_type_json = data_export_modal.find_element_by_xpath("//*[contains(text(), 'JSON')]")
         export_type_json.click()
         data_export_btn = data_export_modal.find_element_by_id('export-data-download-btn')
+        before_click_handles = self.browser.window_handles
         data_export_btn.click()
-        WebDriverWait(self.browser, 10).until(ec.new_window_is_opened)
+        WebDriverWait(self.browser, 10).until(ec.new_window_is_opened(before_click_handles))
         tabs = self.browser.window_handles
         self.assertEqual(2, len(tabs))
         self.browser.switch_to.window(tabs[1])
@@ -109,9 +110,10 @@ class ExportTestModal(LiveServerTestCase):
         export_instance.click()
         instance_export_modal = self.browser.find_element_by_id('instance-export-modal')
         instance_export_btn = instance_export_modal.find_element_by_id('instance-export-dl')
+        before_click_handles = self.browser.window_handles
         instance_export_btn.click()
-        WebDriverWait(self.browser, 10).until(ec.new_window_is_opened)
-        tabs = self.browser.window_handles
-        self.assertEqual(2, len(tabs))
-        self.browser.switch_to.window(tabs[1])
+        WebDriverWait(self.browser, 10).until(ec.new_window_is_opened(before_click_handles))
+        after_click_handles = self.browser.window_handles
+        self.assertEqual(2, len(after_click_handles))
+        self.browser.switch_to.window(after_click_handles[1])
         self.assertIn('B.Inf.4242 Test Module', self.browser.find_element_by_tag_name('body').text)
diff --git a/functional_tests/test_front_pages.py b/functional_tests/test_front_pages.py
index ec38b58f..adcc0975 100644
--- a/functional_tests/test_front_pages.py
+++ b/functional_tests/test_front_pages.py
@@ -1,7 +1,6 @@
-import time
-
 from django.test import LiveServerTestCase
 from selenium import webdriver
+from selenium.webdriver.support.ui import WebDriverWait
 
 from core import models
 from core.models import UserAccount
@@ -47,11 +46,20 @@ class UntestedParent:
             self.assertEqual('Statistics', title.text)
 
         def test_available_tasks_are_shown(self):
+            # A function that takes the element corresponding to the tasks
+            # component and return a function that can be used as a condition for
+            # WebDriverWait
+            def subscriptions_loaded_cond(tasks_el):
+                def loaded(*args):
+                    sub_links = tasks_el.find_elements_by_tag_name('a')
+                    return any((link != '/home' for link in extract_hrefs_hashes(sub_links)))
+                return loaded
+
             self._login()
             tasks = self.browser.find_element_by_name('subscription-list')
             title = tasks.find_element_by_class_name('v-toolbar__title')
             self.assertEqual('Tasks', title.text)
-            time.sleep(8)
+            WebDriverWait(self.browser, 6).until(subscriptions_loaded_cond(tasks))
             subscription_links = extract_hrefs_hashes(
                 tasks.find_elements_by_tag_name('a')
             )
diff --git a/functional_tests/test_login_page.py b/functional_tests/test_login_page.py
index 0849f0c6..23d5fb5d 100644
--- a/functional_tests/test_login_page.py
+++ b/functional_tests/test_login_page.py
@@ -1,8 +1,8 @@
-import time
-
 from django.test import LiveServerTestCase
 from selenium import webdriver
 from selenium.webdriver.common.keys import Keys
+from selenium.webdriver.support import expected_conditions as ec
+from selenium.webdriver.support.ui import WebDriverWait
 
 from core.models import UserAccount
 from util.factories import make_test_data
@@ -102,7 +102,7 @@ class LoginPageTest(LiveServerTestCase):
         password_input = self.browser.find_element_by_xpath('//input[@aria-label="Password"]')
         password_input.send_keys('p')
         self.browser.find_element_by_xpath('//button[@type="submit"]').send_keys(Keys.ENTER)
-        time.sleep(1)
+        WebDriverWait(self.browser, 3).until(ec.url_contains('/home'))
 
     def test_tutor_can_login(self):
         tutor = self.test_data['tutors'][0]
@@ -130,8 +130,9 @@ class LoginPageTest(LiveServerTestCase):
         username_input.send_keys(username)
         password_input = self.browser.find_element_by_id('input-register-password')
         password_input.send_keys(password)
-        self.browser.find_element_by_id('register-submit').click()
-        time.sleep(1)
+        register_submit_el = self.browser.find_element_by_id('register-submit')
+        register_submit_el.click()
+        WebDriverWait(self.browser, 3).until_not(ec.visibility_of(register_submit_el))
         tutor = UserAccount.objects.get(username=username)
         self.assertEqual(UserAccount.TUTOR, tutor.role)
         self.assertFalse(tutor.is_active, "Tutors should be inactive after registered")
diff --git a/functional_tests/util.py b/functional_tests/util.py
index 1333e36a..912d68a3 100644
--- a/functional_tests/util.py
+++ b/functional_tests/util.py
@@ -6,6 +6,8 @@ from selenium import webdriver
 from selenium.webdriver.common.keys import Keys
 from selenium.webdriver.firefox.options import Options
 from selenium.webdriver.remote.webelement import WebElement
+from selenium.webdriver.support import expected_conditions as ec
+from selenium.webdriver.support.ui import WebDriverWait
 
 
 def create_browser() -> webdriver.Firefox:
@@ -23,6 +25,7 @@ def login(browser, live_server_url, username, password='p'):
     password_input = browser.find_element_by_xpath('//input[@aria-label="Password"]')
     password_input.send_keys(password)
     browser.find_element_by_xpath('//button[@type="submit"]').send_keys(Keys.ENTER)
+    WebDriverWait(browser, 3).until(ec.url_contains('/home'))
 
 
 def reset_browser_after_test(browser: webdriver.Firefox, live_server_url):
diff --git a/grady/settings/test.py b/grady/settings/test.py
index d8eca64b..20e08001 100644
--- a/grady/settings/test.py
+++ b/grady/settings/test.py
@@ -1,4 +1,5 @@
 from .default import *
+from .instance import *
 from .live import *
 
 REST_FRAMEWORK['DEFAULT_THROTTLE_RATES']['anon'] = '1000/minute'
-- 
GitLab


From 69bdd24fcd778df0865cda287471baaad6a8bcaa Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Thu, 3 Jan 2019 17:26:06 +0100
Subject: [PATCH 08/21] fixed #130

---
 core/models.py            | 5 +++--
 grady/settings/default.py | 1 +
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/core/models.py b/core/models.py
index 499faf4c..d9514d48 100644
--- a/core/models.py
+++ b/core/models.py
@@ -47,9 +47,10 @@ def get_annotated_tutor_list() -> QuerySet:
 
 
 def get_random_element_from_queryset(queryset):
-    length = queryset.count()
+    qs_elements = queryset.all()
+    length = len(qs_elements)
     index = secrets.choice(range(length))
-    return queryset.all()[index]
+    return qs_elements[index]
 
 
 class ExamType(models.Model):
diff --git a/grady/settings/default.py b/grady/settings/default.py
index 2dd99ba6..5d168606 100644
--- a/grady/settings/default.py
+++ b/grady/settings/default.py
@@ -92,6 +92,7 @@ DATABASES = {
         'PASSWORD': os.environ.get('DB_PASSWORD', 'postgres'),
         'HOST': os.environ.get('DB_HOST', 'localhost'),
         'PORT': os.environ.get('DB_PORT', '5432'),
+        'ATOMIC_REQUESTS': True
     },
 }
 
-- 
GitLab


From c535b6b033bcbf753f5f95956a1412786dfbc3d7 Mon Sep 17 00:00:00 2001
From: Dominik Seeger <dominik.seeger@gmx.net>
Date: Fri, 4 Jan 2019 12:19:40 +0100
Subject: [PATCH 09/21] refactored export components for better testing

---
 frontend/src/components/export/DataExport.vue | 136 +++---------------
 .../src/components/export/InstanceExport.vue  |  69 +--------
 frontend/src/components/mixins/mixins.ts      | 126 +++++++++++++++-
 3 files changed, 149 insertions(+), 182 deletions(-)

diff --git a/frontend/src/components/export/DataExport.vue b/frontend/src/components/export/DataExport.vue
index 4bed957d..8fd23f31 100644
--- a/frontend/src/components/export/DataExport.vue
+++ b/frontend/src/components/export/DataExport.vue
@@ -42,7 +42,7 @@
             @click="exportDialog = false"
           >close</v-btn>
           <v-spacer/>
-          <v-btn id="export-data-download-btn" flat outline @click="getExportFile"
+          <v-btn id="export-data-download-btn" flat outline @click="getExportFile('data')"
           >{{mapFile || mapFileLoaded ? 'Download and apply mapping' : 'Download without mapping'}}</v-btn>
         </v-card-actions>
       </v-card-text>
@@ -56,105 +56,40 @@ import { getters } from '@/store/getters'
 import ax, { StudentExportItem, fetchStudentExportData } from '@/api'
 import FileSelect from '@/components/util/FileSelect.vue'
 import { mutations as mut } from '@/store/mutations'
-import { parseCSVMapMixin } from '@/components/mixins/mixins'
-
-enum ExportType {
-  JSON = 'JSON',
-  CSV = 'CSV'
-}
+import { exportMixin, ExportType } from '@/components/mixins/mixins'
 
 @Component({
   components: { FileSelect }
 })
-export default class DataExport extends Mixins(parseCSVMapMixin) {
+export default class DataExport extends Mixins(exportMixin) {
   exportDialog = true
   mapFile: File | null = null
   setPasswords = false
   exportType = ExportType.CSV
 
-  get corrected () { return getters.corrected }
   get studentMap () { return getters.state.studentMap }
-  get mapFileLoaded () {
-    return Object.keys(getters.state.studentMap).length > 0
-  }
-  get exportColor () {
-    return this.corrected ? 'green darken-1' : 'red lighten-1'
-  }
-  get availableExportTypes (): ExportType[] {
-    return Object.values(ExportType)
-  }
-
-  showDialog () {
-    this.exportDialog = true
-  }
-
-  readMapFileAndCommit () {
-    const fileReader = new FileReader()
-    return new Promise((resolve, reject) => {
-      // @ts-ignore
-      fileReader.onload = event => {
-        // @ts-ignore typings of EventTarget seem to be wrong
-        const studentMap = this.parseCSVMap(event.target.result)
-        mut.SET_STUDENT_MAP(studentMap)
-        resolve()
-      }
-      fileReader.onerror = () => {
-        fileReader.abort()
-        reject(new Error('Problem parsing input file.'))
-      }
-
-      if (!this.mapFile) {
-        reject(new Error('Can only call' +
-          ' readMapFileAndCommit when mapFile is not undefined'))
-      } else {
-        fileReader.readAsText(this.mapFile)
-      }
-    })
-  }
 
   applyMapping (studentExport: StudentExportItem[]) {
-    return studentExport.map(student => {
-      return {
-        ...student,
-        Matrikel: this.studentMap[student.Matrikel].matrikelNo,
-        Name: this.studentMap[student.Matrikel].name
-      }
-    })
-  }
-
-  jsonToCSV (studentExport: StudentExportItem[], delimeter = ';') {
-    let headerLine = Object.keys(studentExport[0]).reduce((acc: string, curr) => {
-      if (curr === 'Scores') {
-        return acc
-      }
-      return acc ? `${acc};${curr}` : `${curr}`
-    }, '')
-    headerLine += Object.values(studentExport[0].Scores)
-      .reduce((acc: string, curr) => {
-        return `${acc};${curr.type}`
-      }, '')
-
-    const lines = studentExport.map(student => {
-      const normalFields = Object.values(student).reduce((acc: string, curr): string => {
-        // skip  the Scores field
-        if (typeof curr === 'object') {
-          return acc
+    console.log(studentExport)
+    studentExport.forEach(student => {
+      if (this.studentMap[student.Matrikel]) {
+        student = {
+          ...student,
+          Matrikel: this.studentMap[student.Matrikel].matrikelNo,
+          Name: this.studentMap[student.Matrikel].name
         }
-        return acc ? `${acc};${curr}` : `${curr}`
-      }, '')
-
-      const scoreFields = Object.values(student.Scores).reduce((acc: string, curr) => {
-        return `${acc};${curr.score}`
-      }, '')
-      return normalFields + scoreFields
+      } else {
+        this.$notify({
+          title: `Unknown student: ${student.Name}`,
+          text: `Student ${student.Name} is missing in mapping file`,
+          type: 'error',
+          duration: -1
+        })
+      }
     })
-
-    return headerLine + lines.reduce((acc, curr) => {
-      return `${acc}\n${curr}`
-    }, '') + '\n' // add trailing newline
   }
 
-  createDownloadPopup (content: string | StudentExportItem[], fileType: ExportType) {
+  createDownloadPopup (content: string | StudentExportItem[], fileType: ExportType): void {
     const blobProperties: BlobPropertyBag = {}
     if (fileType === ExportType.JSON) {
       blobProperties.type = 'application/json'
@@ -165,39 +100,6 @@ export default class DataExport extends Mixins(parseCSVMapMixin) {
     const blobData = new Blob([<string> content], blobProperties)
     window.open(window.URL.createObjectURL(blobData))
   }
-
-  optionalConvertAndCreatePopup (studentData: StudentExportItem[]) {
-    const convertedData = this.exportType === ExportType.CSV
-      ? this.jsonToCSV(studentData) : studentData
-
-    this.createDownloadPopup(convertedData, this.exportType)
-  }
-
-  async getMappedExportFile (studentData: StudentExportItem[]) {
-    if (!this.mapFile && !this.mapFileLoaded) {
-      throw new Error('Either mapFile must be selected or already loaded ' +
-                      'to call getMappedExportFile')
-    }
-    if (this.mapFile) {
-      await this.readMapFileAndCommit()
-    }
-    const mappedData = this.applyMapping(studentData)
-    this.optionalConvertAndCreatePopup(mappedData)
-  }
-
-  async getExportFile () {
-    const studentData = await fetchStudentExportData({ setPasswords: this.setPasswords })
-
-    if (this.mapFile || this.mapFileLoaded) {
-      this.getMappedExportFile(studentData)
-    } else {
-      this.optionalConvertAndCreatePopup(studentData)
-    }
-  }
-
-  hide () {
-    this.$emit('hide')
-  }
 }
 </script>
 
diff --git a/frontend/src/components/export/InstanceExport.vue b/frontend/src/components/export/InstanceExport.vue
index 91c63ba6..681e0e6b 100644
--- a/frontend/src/components/export/InstanceExport.vue
+++ b/frontend/src/components/export/InstanceExport.vue
@@ -20,7 +20,7 @@
             @click="exportDialog = false"
           >close</v-btn>
           <v-spacer/>
-          <v-btn id="instance-export-dl" flat outline @click="getExportFile"
+          <v-btn id="instance-export-dl" flat outline @click="getExportFile('instance')"
           >{{mapFile || mapFileLoaded ? 'Download and apply mapping' : 'Download without mapping'}}</v-btn>
         </v-card-actions>
       </v-card-text>
@@ -34,50 +34,17 @@ import { getters } from '@/store/getters'
 import ax, { StudentExportItem, fetchStudentExportData, fetchInstanceExportData, InstanceExportData } from '@/api'
 import FileSelect from '@/components/util/FileSelect.vue'
 import { mutations as mut } from '@/store/mutations'
-import { parseCSVMapMixin } from '@/components/mixins/mixins'
+import { exportMixin, ExportType } from '@/components/mixins/mixins'
 
 @Component({
   components: { FileSelect }
 })
-export default class DataExport extends Mixins(parseCSVMapMixin) {
+export default class DataExport extends exportMixin {
   exportDialog = true
   mapFile: File | null = null
+  exportType = ExportType.JSON     // instance export is only available as JSON
 
-  get corrected () { return getters.corrected }
   get studentMap () { return getters.state.studentMap }
-  get mapFileLoaded () {
-    return Object.keys(getters.state.studentMap).length > 0
-  }
-  get exportColor () {
-    return this.corrected ? 'green darken-1' : 'red lighten-1'
-  }
-
-  showDialog () {
-    this.exportDialog = true
-  }
-
-  readMapFileAndCommit () {
-    const fileReader = new FileReader()
-    return new Promise((resolve, reject) => {
-      fileReader.onload = event => {
-        // @ts-ignore typings of EventTarget seem to be wrong
-        const studentMap = this.parseCSVMap(event.target.result)
-        mut.SET_STUDENT_MAP(studentMap)
-        resolve()
-      }
-      fileReader.onerror = () => {
-        fileReader.abort()
-        reject(new Error('Problem parsing input file.'))
-      }
-
-      if (!this.mapFile) {
-        reject(new Error('Can only call' +
-          ' readMapFileAndCommit when mapFile is not undefined'))
-      } else {
-        fileReader.readAsText(this.mapFile)
-      }
-    })
-  }
 
   applyMapping (instanceExport: InstanceExportData) {
     instanceExport.students.forEach(student => {
@@ -96,39 +63,13 @@ export default class DataExport extends Mixins(parseCSVMapMixin) {
     })
   }
 
-  createDownloadPopup (content: string | InstanceExportData) {
+  createDownloadPopup (content: string | InstanceExportData): void {
     const blobProperties: BlobPropertyBag = {}
     blobProperties.type = 'application/json'
     content = JSON.stringify(content)
     const blobData = new Blob([<string> content], blobProperties)
     window.open(window.URL.createObjectURL(blobData))
   }
-
-  async getMappedExportFile (studentData: InstanceExportData) {
-    if (!this.mapFile && !this.mapFileLoaded) {
-      throw new Error('Either mapFile must be selected or already loaded ' +
-                      'to call getMappedExportFile')
-    }
-    if (this.mapFile) {
-      await this.readMapFileAndCommit()
-    }
-    this.applyMapping(studentData)
-    this.createDownloadPopup(studentData)
-  }
-
-  async getExportFile () {
-    const instanceData = await fetchInstanceExportData()
-
-    if (this.mapFile || this.mapFileLoaded) {
-      this.getMappedExportFile(instanceData)
-    } else {
-      this.createDownloadPopup(instanceData)
-    }
-  }
-
-  hide () {
-    this.$emit('hide')
-  }
 }
 </script>
 
diff --git a/frontend/src/components/mixins/mixins.ts b/frontend/src/components/mixins/mixins.ts
index caae87ff..972371d0 100644
--- a/frontend/src/components/mixins/mixins.ts
+++ b/frontend/src/components/mixins/mixins.ts
@@ -1,7 +1,28 @@
 import { Vue, Component } from 'vue-property-decorator'
+import { fetchStudentExportData, StudentExportItem, InstanceExportData, fetchInstanceExportData } from '@/api'
+import { getters } from '@/store/getters'
+import { mutations as mut } from '@/store/mutations'
+
+export enum ExportType {
+  JSON = 'JSON',
+  CSV = 'CSV'
+}
 
 @Component
-export class parseCSVMapMixin extends Vue {
+export class exportMixin extends Vue {
+  exportDialog = true
+  mapFile: File | null = null
+  setPasswords = false
+  exportType = ExportType.CSV
+
+  get mapFileLoaded () {
+    return Object.keys(getters.state.studentMap).length > 0
+  }
+
+  get availableExportTypes (): ExportType[] {
+    return Object.values(ExportType)
+  }
+
   parseCSVMap (csvMap: string) {
     let lines = csvMap.split('\n')
     lines.shift() // drop the first line since it contains only headings
@@ -14,4 +35,107 @@ export class parseCSVMapMixin extends Vue {
       return acc
     }, {})
   }
+
+  async getExportFile (type: string) {
+    let studentData
+    if (type === 'data') {
+      studentData = await fetchStudentExportData({ setPasswords: this.setPasswords })
+    } else if (type === 'instance') {
+      studentData = await fetchInstanceExportData()
+    } else {
+      throw new Error('Unsupported export type')
+    }
+    
+    if (this.mapFile || this.mapFileLoaded) {
+      this.getMappedExportFile(studentData)
+    } else {
+      this.optionalConvertAndCreatePopup(studentData)
+    }
+  }
+
+  jsonToCSV (studentExport: StudentExportItem[], delimeter = ';') {
+    let headerLine = Object.keys(studentExport[0]).reduce((acc: string, curr) => {
+      if (curr === 'Scores') {
+        return acc
+      }
+      return acc ? `${acc};${curr}` : `${curr}`
+    }, '')
+    headerLine += Object.values(studentExport[0].Scores)
+      .reduce((acc: string, curr) => {
+        return `${acc};${curr.type}`
+      }, '')
+
+    const lines = studentExport.map(student => {
+      const normalFields = Object.values(student).reduce((acc: string, curr): string => {
+        // skip  the Scores field
+        if (typeof curr === 'object') {
+          return acc
+        }
+        return acc ? `${acc};${curr}` : `${curr}`
+      }, '')
+
+      const scoreFields = Object.values(student.Scores).reduce((acc: string, curr) => {
+        return `${acc};${curr.score}`
+      }, '')
+      return normalFields + scoreFields
+    })
+
+    return headerLine + lines.reduce((acc, curr) => {
+      return `${acc}\n${curr}`
+    }, '') + '\n' // add trailing newline
+  }
+
+  optionalConvertAndCreatePopup(studentData: StudentExportItem[] | InstanceExportData) {
+    const convertedData = this.exportType === ExportType.CSV
+      ? this.jsonToCSV(studentData as StudentExportItem[]) : studentData
+      // we have a cast here because only student export may be converted to csv
+
+    this.createDownloadPopup(convertedData, this.exportType)
+  }
+
+  async getMappedExportFile (studentData: StudentExportItem[] | InstanceExportData) {
+    if (!this.mapFile && !this.mapFileLoaded) {
+      throw new Error('Either mapFile must be selected or already loaded ' +
+                      'to call getMappedExportFile')
+    }
+    if (this.mapFile) {
+      await this.readMapFileAndCommit()
+    }
+    this.applyMapping(studentData)
+    this.optionalConvertAndCreatePopup(studentData)
+  }
+
+  readMapFileAndCommit () {
+    const fileReader = new FileReader()
+    return new Promise((resolve, reject) => {
+      fileReader.onload = event => {
+        // @ts-ignore typings of EventTarget seem to be wrong
+        const studentMap = this.parseCSVMap(event.target.result)
+        mut.SET_STUDENT_MAP(studentMap)
+        resolve()
+      }
+      fileReader.onerror = () => {
+        fileReader.abort()
+        reject(new Error('Problem parsing input file.'))
+      }
+
+      if (!this.mapFile) {
+        reject(new Error('Can only call' +
+          ' readMapFileAndCommit when mapFile is not undefined'))
+      } else {
+        fileReader.readAsText(this.mapFile)
+      }
+    })
+  }
+
+  hide () {
+    this.$emit('hide')
+  }
+
+  showDialog () {
+    this.exportDialog = true
+  }
+
+  applyMapping (exportData: StudentExportItem[] | InstanceExportData): void { throw new Error("Not implemented.") }
+  createDownloadPopup (content: string | StudentExportItem[] | InstanceExportData, fileType: ExportType): void { throw new Error("Not implemented.") }
 }
-- 
GitLab


From f15d6b21b8f0e5a42313f3568fd25913af88a6c9 Mon Sep 17 00:00:00 2001
From: Dominik Seeger <dominik.seeger@gmx.net>
Date: Fri, 4 Jan 2019 12:20:58 +0100
Subject: [PATCH 10/21] added tests for DataExport component

---
 .../tests/unit/components/DataExport.spec.ts  | 97 +++++++++++++++++++
 1 file changed, 97 insertions(+)
 create mode 100644 frontend/tests/unit/components/DataExport.spec.ts

diff --git a/frontend/tests/unit/components/DataExport.spec.ts b/frontend/tests/unit/components/DataExport.spec.ts
new file mode 100644
index 00000000..41f965a2
--- /dev/null
+++ b/frontend/tests/unit/components/DataExport.spec.ts
@@ -0,0 +1,97 @@
+import Vuex from 'vuex'
+import { mount, createLocalVue } from '@vue/test-utils'
+import DataExport from '@/components/export/DataExport.vue'
+import sinon from 'sinon'
+import chai from 'chai'
+import VueRouter from 'vue-router'
+
+import * as api from '@/api'
+chai.should()
+
+let localVue = createLocalVue()
+localVue.use(Vuex)
+localVue.use(VueRouter)
+let router = new VueRouter()
+
+// @ts-ignore
+global.requestAnimationFrame = cb => cb()
+
+describe('DataExport Component Unit Tests', () => {
+  let store: any = null
+  let consoleTemp = {
+    warn: console.warn,
+    error: console.error
+  }
+
+  // for creating test data
+  let students = [{
+    Matrikel: 1000000,
+    Name: 'test',
+    Username: 'test',
+    Sum: 100,
+    Exam: 'test',
+    Password: 'test',
+    Scores: { type: 'test', score: 100 }
+  }]
+
+  before(function () {
+    console.warn = function () {}
+    console.error = function () {}
+  })
+
+  after(function () {
+    console.warn = consoleTemp.warn
+    console.error = consoleTemp.error
+  })
+
+  beforeEach(() => {
+    store = new Vuex.Store({
+      state: {}
+    })
+  })
+
+  afterEach(() => {
+    sinon.restore()
+  })
+
+  it('should download CSV when selected', () => {
+    let wrapper = mount(DataExport, { localVue: localVue, store })
+    wrapper.vm.$data.exportType = 'CSV'
+    let spy = sinon.spy()
+    // @ts-ignore
+    sinon.replace(wrapper.vm, 'jsonToCSV', spy)
+    // @ts-ignore
+    sinon.replace(wrapper.vm, 'createDownloadPopup', sinon.spy())
+    // @ts-ignore
+    wrapper.vm.optionalConvertAndCreatePopup(students)
+    spy.called.should.equal(true)
+  })
+  it('should download JSON when selected', () => {
+    let wrapper = mount(DataExport, { localVue: localVue, store })
+    wrapper.vm.$data.exportType = 'JSON'
+    let spy = sinon.spy()
+    // @ts-ignore
+    sinon.replace(wrapper.vm, 'jsonToCSV', spy)
+    // @ts-ignore
+    sinon.replace(wrapper.vm, 'createDownloadPopup', sinon.spy())
+    // @ts-ignore
+    wrapper.vm.optionalConvertAndCreatePopup(students)
+    spy.called.should.equal(false)
+  })
+  it('should download obfuscated data when no mapping was selected', async () => {
+    let wrapper = mount(DataExport, { localVue: localVue, store })
+    let stub = sinon.stub().returns(Promise.resolve({ data: students }))
+    let spy = sinon.spy()
+    // @ts-ignore replace ax.post because of fetch in getExportFile
+    sinon.replace(api.default, 'post', stub)
+    // @ts-ignore
+    sinon.replace(wrapper.vm, 'optionalConvertAndCreatePopup', spy)
+    // @ts-ignore
+    await wrapper.vm.getExportFile('data')
+    spy.called.should.equal(true)
+    spy.calledWithExactly(students).should.equal(true)
+  })
+  it('should download deobfuscated data when no mapping was selected', async () => {
+
+  })
+})
-- 
GitLab


From 688a77d1e1c45be15cf2a693b4c2cf49c89a28cb Mon Sep 17 00:00:00 2001
From: Dominik Seeger <dominik.seeger@gmx.net>
Date: Fri, 4 Jan 2019 12:21:11 +0100
Subject: [PATCH 11/21] added tests for AutoLogout component

---
 .../tests/unit/components/AutoLogout.spec.ts     | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/frontend/tests/unit/components/AutoLogout.spec.ts b/frontend/tests/unit/components/AutoLogout.spec.ts
index e33bdabf..5a5730f6 100644
--- a/frontend/tests/unit/components/AutoLogout.spec.ts
+++ b/frontend/tests/unit/components/AutoLogout.spec.ts
@@ -3,6 +3,8 @@ import { mount, createLocalVue } from '@vue/test-utils'
 import AutoLogout from '@/components/AutoLogout.vue'
 import sinon from 'sinon'
 import { Authentication } from '@/store/modules/authentication'
+import { SET_LAST_INTERACTION } from '@/store/mutations'
+import { lastInteraction } from '@/store/plugins/lastInteractionPlugin'
 import chai from 'chai'
 chai.should()
 
@@ -28,7 +30,7 @@ describe('Auto Logout Unit Tests', () => {
 
   beforeEach(() => {
     store = new Vuex.Store({
-      state: {}
+      plugins: [lastInteraction]
     })
   })
 
@@ -43,7 +45,7 @@ describe('Auto Logout Unit Tests', () => {
   })
   it('should be visible when logoutDialog is set to true', () => {
     let store = new Vuex.Store({})
-    let wrapper = mount(AutoLogout, { localVue, store })
+    let wrapper = mount(AutoLogout, { localVue: localVue, store })
     wrapper.vm.$data.logoutDialog = true
     wrapper.html().should.contain('<div class="v-dialog v-dialog--active v-dialog--persistent" style="max-width: 30%;">')
   })
@@ -51,8 +53,16 @@ describe('Auto Logout Unit Tests', () => {
     let spy = sinon.spy()
     sinon.replace(Authentication, 'refreshJWT', spy)
     let store = new Vuex.Store({})
-    let wrapper = mount(AutoLogout, { localVue, store })
+    let wrapper = mount(AutoLogout, { localVue: localVue, store })
     wrapper.find('#continue-btn').trigger('click')
     spy.called.should.equal(true)
   })
+  it('should automatically update jwt when it is older than 20% of its lifetime', () => {
+    // replace lastTokenRefreshTry and jwtTimeDelta from the store to emulate wanted behaviour
+    let wrapper = mount(AutoLogout, { localVue: localVue, store })
+    let spy = sinon.spy()
+    sinon.replaceGetter(Authentication.state, 'jwtTimeDelta', () => 10 * 1e3)
+    sinon.replaceGetter(Authentication.state, 'lastTokenRefreshTry', () => Date.now() - 2.1 * 1e3)
+    sinon.replace(Authentication, 'refreshJWT', spy)
+  })
 })
-- 
GitLab


From a875b9ceb23d3911a4965cd91d38eea37e7f72e6 Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Thu, 3 Jan 2019 19:36:12 +0100
Subject: [PATCH 12/21] Upgraded backend dependencies

Removed unused frontend dependency
---
 core/models.py                                |   2 +-
 core/serializers/feedback.py                  |   2 +-
 .../test_subscription_assignment_service.py   |   4 +-
 core/urls.py                                  |  12 +-
 core/views/common_views.py                    |   8 +-
 frontend/package.json                         |   1 -
 frontend/yarn.lock                            | 502 +-----------------
 requirements.dev.txt                          |  14 +-
 requirements.txt                              |  20 +-
 util/format_index.py                          |   2 +-
 util/importer.py                              |  10 +-
 11 files changed, 56 insertions(+), 521 deletions(-)

diff --git a/core/models.py b/core/models.py
index d9514d48..b2fba4b1 100644
--- a/core/models.py
+++ b/core/models.py
@@ -663,7 +663,7 @@ class SubmissionSubscription(models.Model):
     def get_available_in_stage(self) -> int:
         try:
             return self._get_available_submissions_in_subscription_stage().count()  # noqa
-        except (SubscriptionTemporarilyEnded, SubscriptionEnded) as err:
+        except (SubscriptionTemporarilyEnded, SubscriptionEnded):
             return 0
 
     @transaction.atomic
diff --git a/core/serializers/feedback.py b/core/serializers/feedback.py
index 43db2622..94949fbb 100644
--- a/core/serializers/feedback.py
+++ b/core/serializers/feedback.py
@@ -155,7 +155,7 @@ class FeedbackSerializer(DynamicFieldsModelSerializer):
             try:
                 score = data.get('score')
                 submission = data.get('of_submission')
-            except KeyError as err:
+            except KeyError:
                 raise serializers.ValidationError(
                     'You need a score and a submission.')
 
diff --git a/core/tests/test_subscription_assignment_service.py b/core/tests/test_subscription_assignment_service.py
index 708b088c..ca576ae9 100644
--- a/core/tests/test_subscription_assignment_service.py
+++ b/core/tests/test_subscription_assignment_service.py
@@ -43,7 +43,7 @@ class SubmissionSubscriptionRandomTest(APITestCase):
         self.submission_02.delete()
         try:
             self.subscription.get_or_create_work_assignment()
-        except SubscriptionEnded as err:
+        except SubscriptionEnded:
             self.assertFalse(False)
         else:
             self.assertTrue(False)
@@ -59,7 +59,7 @@ class SubmissionSubscriptionRandomTest(APITestCase):
             feedback_stage=SubmissionSubscription.FEEDBACK_VALIDATION)
         try:
             validation.get_or_create_work_assignment()
-        except SubscriptionTemporarilyEnded as err:
+        except SubscriptionTemporarilyEnded:
             self.assertTrue(True)
         else:
             self.assertTrue(False)
diff --git a/core/urls.py b/core/urls.py
index 3357065b..18efbbd1 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -9,19 +9,19 @@ from core import views
 # Create a router and register our viewsets with it.
 router = DefaultRouter()
 router.register('student', views.StudentReviewerApiViewSet,
-                base_name='student')
+                basename='student')
 router.register('examtype', views.ExamApiViewSet)
 router.register('feedback', views.FeedbackApiView)
 router.register('feedback-comment', views.FeedbackCommentApiView)
 router.register('submission', views.SubmissionViewSet,
-                base_name='submission')
+                basename='submission')
 router.register('submissiontype', views.SubmissionTypeApiView)
-router.register('tutor', views.TutorApiViewSet, base_name='tutor')
+router.register('tutor', views.TutorApiViewSet, basename='tutor')
 router.register('subscription', views.SubscriptionApiViewSet,
-                base_name='subscription')
+                basename='subscription')
 router.register('assignment', views.AssignmentApiViewSet)
-router.register('statistics', views.StatisticsEndpoint, base_name='statistics')
-router.register('user', views.UserAccountViewSet, base_name='user')
+router.register('statistics', views.StatisticsEndpoint, basename='statistics')
+router.register('user', views.UserAccountViewSet, basename='user')
 
 schema_view = get_schema_view(
     openapi.Info(
diff --git a/core/views/common_views.py b/core/views/common_views.py
index a7fd17e7..66bb478e 100644
--- a/core/views/common_views.py
+++ b/core/views/common_views.py
@@ -10,7 +10,7 @@ import django.contrib.auth.password_validation as validators
 from django.core import exceptions
 
 from rest_framework import generics, mixins, status, viewsets
-from rest_framework.decorators import (api_view, list_route, throttle_classes,
+from rest_framework.decorators import (api_view, throttle_classes,
                                        action)
 from rest_framework.exceptions import PermissionDenied
 from rest_framework.permissions import AllowAny
@@ -78,12 +78,12 @@ class StudentReviewerApiViewSet(viewsets.ReadOnlyModelViewSet):
             user.is_active = active
             user.save()
 
-    @list_route(methods=['post'])
+    @action(detail=False, methods=['post'])
     def deactivate(self, request):
         self._set_students_active(False)
         return Response(status=status.HTTP_200_OK)
 
-    @list_route(methods=['post'])
+    @action(detail=False, methods=['post'])
     def activate(self, request):
         self._set_students_active(True)
         return Response(status=status.HTTP_200_OK)
@@ -111,7 +111,7 @@ class TutorApiViewSet(
         .prefetch_related('subscriptions__assignments')
     serializer_class = TutorSerializer
 
-    @list_route(methods=['post'], permission_classes=[AllowAny])
+    @action(detail=False, methods=['post'], permission_classes=[AllowAny])
     @throttle_classes([AnonRateThrottle])
     def register(self, request):
         serializer = self.get_serializer(data=request.data)
diff --git a/frontend/package.json b/frontend/package.json
index 78a12a64..0741ed55 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -31,7 +31,6 @@
     "@types/mocha": "^5.2.5",
     "@types/nightwatch": "^0.9.8",
     "@types/sinon": "^7.0.2",
-    "@vue/cli-plugin-e2e-nightwatch": "^3.0.0-rc.10",
     "@vue/cli-plugin-eslint": "^3.0.0-rc.10",
     "@vue/cli-plugin-typescript": "^3.2.0",
     "@vue/cli-plugin-unit-mocha": "^3.2.0",
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index 27374204..0e9a83e7 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -164,17 +164,6 @@
   resolved "https://registry.yarnpkg.com/@vue/cli-overlay/-/cli-overlay-3.2.0.tgz#bb5d781914bb5af97d92410babbaa3720707b728"
   integrity sha512-RKMSfgTtRs4VOXQhmbrNZJaCCheshebji9NcPNGyXzukCMBtoAbu3cG9HxizCSUA//oFFAdPP5BGeHvv0cpu/A==
 
-"@vue/cli-plugin-e2e-nightwatch@^3.0.0-rc.10":
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-e2e-nightwatch/-/cli-plugin-e2e-nightwatch-3.0.4.tgz#4a1de0c4d98d70cb3e9a2008e00b4402b51b41d0"
-  dependencies:
-    "@vue/cli-shared-utils" "^3.0.4"
-    chromedriver "^2.40.0"
-    deepmerge "^2.1.1"
-    execa "^0.10.0"
-    nightwatch "^0.9.21"
-    selenium-server "^3.13.0"
-
 "@vue/cli-plugin-eslint@^3.0.0-rc.10":
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/@vue/cli-plugin-eslint/-/cli-plugin-eslint-3.2.1.tgz#4dc353add93023363bf4c6b347e64472d1f2f432"
@@ -266,7 +255,7 @@
     webpack-merge "^4.1.4"
     yorkie "^2.0.0"
 
-"@vue/cli-shared-utils@^3.0.4", "@vue/cli-shared-utils@^3.2.0":
+"@vue/cli-shared-utils@^3.2.0":
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/@vue/cli-shared-utils/-/cli-shared-utils-3.2.0.tgz#504037063c2a4a346dc35c9652bd3863da4816e7"
   integrity sha512-FCX5ABFg5pWhomyXLpCaogJktMvjsS5d4Mn5BfvqcJxCvzOX6ze8ihFK3u//XMeM78dOFpHSjxnRSvHtkEwgsg==
@@ -544,13 +533,6 @@ address@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9"
 
-agent-base@2:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-2.1.1.tgz#d6de10d5af6132d5bd692427d46fc538539094c7"
-  dependencies:
-    extend "~3.0.0"
-    semver "~5.0.1"
-
 ajv-errors@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.0.tgz#ecf021fa108fd17dfb5e6b383f2dd233e31ffc59"
@@ -772,10 +754,6 @@ assert@^1.1.1:
   dependencies:
     util "0.10.3"
 
-assertion-error@1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.0.tgz#c7f85438fdd466bc7ca16ab90c81513797a5d23b"
-
 assertion-error@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
@@ -784,10 +762,6 @@ assign-symbols@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
 
-ast-types@0.x.x:
-  version "0.11.5"
-  resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.5.tgz#9890825d660c03c28339f315e9fa0a360e31ec28"
-
 async-each@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
@@ -978,7 +952,7 @@ boolbase@^1.0.0, boolbase@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
 
-brace-expansion@^1.0.0, brace-expansion@^1.1.7:
+brace-expansion@^1.1.7:
   version "1.1.11"
   resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
   dependencies:
@@ -1018,10 +992,6 @@ browser-process-hrtime@^0.1.2:
   resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4"
   integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==
 
-browser-stdout@1.3.0:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f"
-
 browser-stdout@1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
@@ -1244,13 +1214,6 @@ caseless@~0.12.0:
   resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
   integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
 
-chai-nightwatch@~0.1.x:
-  version "0.1.1"
-  resolved "http://registry.npmjs.org/chai-nightwatch/-/chai-nightwatch-0.1.1.tgz#1ca56de768d3c0868fe7fc2f4d32c2fe894e6be9"
-  dependencies:
-    assertion-error "1.0.0"
-    deep-eql "0.1.3"
-
 chai@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5"
@@ -1341,16 +1304,6 @@ chrome-trace-event@^1.0.0:
   dependencies:
     tslib "^1.9.0"
 
-chromedriver@^2.40.0:
-  version "2.42.0"
-  resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-2.42.0.tgz#49d8d314389e76e143a54451b85a8ddfcb1c6128"
-  dependencies:
-    del "^3.0.0"
-    extract-zip "^1.6.7"
-    kew "^0.7.0"
-    mkdirp "^0.5.1"
-    request "^2.87.0"
-
 ci-info@^1.5.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497"
@@ -1433,10 +1386,6 @@ co@^4.6.0:
   resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
   integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
 
-co@~3.0.6:
-  version "3.0.6"
-  resolved "https://registry.yarnpkg.com/co/-/co-3.0.6.tgz#1445f226c5eb956138e68c9ac30167ea7d2e6bda"
-
 coa@~2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.1.tgz#f3f8b0b15073e35d70263fb1042cb2c023db38af"
@@ -1504,12 +1453,6 @@ commander@2.17.x, commander@~2.17.1:
   version "2.17.1"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
 
-commander@2.9.0:
-  version "2.9.0"
-  resolved "http://registry.npmjs.org/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
-  dependencies:
-    graceful-readlink ">= 1.0.0"
-
 commander@^2.12.1:
   version "2.18.0"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970"
@@ -1551,7 +1494,7 @@ concat-map@0.0.1:
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
   integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
 
-concat-stream@1.6.2, concat-stream@^1.5.0, concat-stream@^1.6.0:
+concat-stream@^1.5.0, concat-stream@^1.6.0:
   version "1.6.2"
   resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
   dependencies:
@@ -1959,10 +1902,6 @@ dashdash@^1.12.0:
   dependencies:
     assert-plus "^1.0.0"
 
-data-uri-to-buffer@1:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835"
-
 data-urls@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe"
@@ -1981,18 +1920,12 @@ de-indent@^1.0.2:
   resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
   integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=
 
-debug@2, debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
+debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
   version "2.6.9"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
   dependencies:
     ms "2.0.0"
 
-debug@2.2.0:
-  version "2.2.0"
-  resolved "http://registry.npmjs.org/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
-  dependencies:
-    ms "0.7.1"
-
 debug@3.1.0, debug@=3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
@@ -2027,12 +1960,6 @@ decode-uri-component@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
 
-deep-eql@0.1.3:
-  version "0.1.3"
-  resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2"
-  dependencies:
-    type-detect "0.1.1"
-
 deep-eql@^3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df"
@@ -2100,14 +2027,6 @@ define-property@^2.0.2:
     is-descriptor "^1.0.2"
     isobject "^3.0.1"
 
-degenerator@~1.0.2:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095"
-  dependencies:
-    ast-types "0.x.x"
-    escodegen "1.x.x"
-    esprima "3.x.x"
-
 del@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
@@ -2155,10 +2074,6 @@ detect-node@^2.0.3:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
 
-diff@1.4.0:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf"
-
 diff@3.5.0, diff@^3.2.0, diff@^3.5.0:
   version "3.5.0"
   resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
@@ -2314,10 +2229,6 @@ ee-first@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
 
-ejs@2.5.7:
-  version "2.5.7"
-  resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a"
-
 ejs@^2.6.1:
   version "2.6.1"
   resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0"
@@ -2412,7 +2323,7 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
 
-escodegen@1.x.x, escodegen@^1.11.0:
+escodegen@^1.11.0:
   version "1.11.0"
   resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589"
   dependencies:
@@ -2585,7 +2496,7 @@ espree@^3.5.2, espree@^3.5.4:
     acorn "^5.5.0"
     acorn-jsx "^3.0.0"
 
-esprima@3.x.x, esprima@^3.1.3:
+esprima@^3.1.3:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
 
@@ -2809,7 +2720,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
     assign-symbols "^1.0.0"
     is-extendable "^1.0.1"
 
-extend@3, extend@~3.0.0, extend@~3.0.2:
+extend@~3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
 
@@ -2842,15 +2753,6 @@ extglob@^2.0.4:
     snapdragon "^0.8.1"
     to-regex "^3.0.1"
 
-extract-zip@^1.6.7:
-  version "1.6.7"
-  resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9"
-  dependencies:
-    concat-stream "1.6.2"
-    debug "2.6.9"
-    mkdirp "0.5.1"
-    yauzl "2.4.1"
-
 extsprintf@1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
@@ -2908,12 +2810,6 @@ faye-websocket@~0.11.1:
   dependencies:
     websocket-driver ">=0.5.1"
 
-fd-slicer@~1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
-  dependencies:
-    pend "~1.2.0"
-
 figgy-pudding@^3.1.0, figgy-pudding@^3.5.1:
   version "3.5.1"
   resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
@@ -2941,10 +2837,6 @@ file-loader@^2.0.0:
     loader-utils "^1.0.2"
     schema-utils "^1.0.0"
 
-file-uri-to-path@1:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
-
 filename-regex@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
@@ -3169,13 +3061,6 @@ fsevents@^1.0.0, fsevents@^1.2.2:
     nan "^2.9.2"
     node-pre-gyp "^0.10.0"
 
-ftp@~0.3.10:
-  version "0.3.10"
-  resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d"
-  dependencies:
-    readable-stream "1.1.x"
-    xregexp "2.0.0"
-
 function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
@@ -3219,17 +3104,6 @@ get-stream@^4.0.0:
   dependencies:
     pump "^3.0.0"
 
-get-uri@2:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.2.tgz#5c795e71326f6ca1286f2fc82575cd2bab2af578"
-  dependencies:
-    data-uri-to-buffer "1"
-    debug "2"
-    extend "3"
-    file-uri-to-path "1"
-    ftp "~0.3.10"
-    readable-stream "2"
-
 get-value@^2.0.3, get-value@^2.0.6:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
@@ -3267,17 +3141,6 @@ glob-to-regexp@^0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
 
-glob@7.0.5:
-  version "7.0.5"
-  resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.5.tgz#b4202a69099bbb4d292a7c1b95b6682b67ebdc95"
-  dependencies:
-    fs.realpath "^1.0.0"
-    inflight "^1.0.4"
-    inherits "2"
-    minimatch "^3.0.2"
-    once "^1.3.0"
-    path-is-absolute "^1.0.0"
-
 glob@7.1.2:
   version "7.1.2"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
@@ -3357,19 +3220,11 @@ graceful-fs@^4.1.2:
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
   integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
 
-"graceful-readlink@>= 1.0.0":
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
-
 growl@1.10.5:
   version "1.10.5"
   resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
   integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
 
-growl@1.9.2:
-  version "1.9.2"
-  resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f"
-
 gzip-size@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.0.0.tgz#a55ecd99222f4c48fd8c01c625ce3b349d0a0e80"
@@ -3402,10 +3257,6 @@ has-ansi@^2.0.0:
   dependencies:
     ansi-regex "^2.0.0"
 
-has-flag@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
-
 has-flag@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
@@ -3609,14 +3460,6 @@ http-parser-js@>=0.4.0:
   version "0.4.13"
   resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.13.tgz#3bd6d6fde6e3172c9334c3b33b6c193d80fe1137"
 
-http-proxy-agent@1:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a"
-  dependencies:
-    agent-base "2"
-    debug "2"
-    extend "3"
-
 http-proxy-middleware@~0.18.0:
   version "0.18.0"
   resolved "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz#0987e6bb5a5606e5a69168d8f967a87f15dd8aab"
@@ -3647,14 +3490,6 @@ https-browserify@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
 
-https-proxy-agent@1:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6"
-  dependencies:
-    agent-base "2"
-    debug "2"
-    extend "3"
-
 iconv-lite@0.4.19:
   version "0.4.19"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
@@ -3796,11 +3631,7 @@ ip-regex@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
 
-ip@1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/ip/-/ip-1.0.1.tgz#c7e356cdea225ae71b36d70f2e71a92ba4e42590"
-
-ip@^1.1.0, ip@^1.1.4, ip@^1.1.5:
+ip@^1.1.0, ip@^1.1.5:
   version "1.1.5"
   resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
 
@@ -4242,7 +4073,7 @@ json-stringify-safe@~5.0.1:
   resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
   integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
 
-json3@3.3.2, json3@^3.3.2:
+json3@^3.3.2:
   version "3.3.2"
   resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
 
@@ -4277,10 +4108,6 @@ just-extend@^4.0.2:
   resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.0.2.tgz#f3f47f7dfca0f989c55410a7ebc8854b07108afc"
   integrity sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==
 
-kew@^0.7.0:
-  version "0.7.0"
-  resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b"
-
 killable@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
@@ -4405,100 +4232,15 @@ locate-path@^3.0.0:
     p-locate "^3.0.0"
     path-exists "^3.0.0"
 
-lodash._arraycopy@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz#76e7b7c1f1fb92547374878a562ed06a3e50f6e1"
-
-lodash._arrayeach@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz#bab156b2a90d3f1bbd5c653403349e5e5933ef9e"
-
-lodash._baseassign@^3.0.0:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e"
-  dependencies:
-    lodash._basecopy "^3.0.0"
-    lodash.keys "^3.0.0"
-
-lodash._baseclone@^3.0.0:
-  version "3.3.0"
-  resolved "https://registry.yarnpkg.com/lodash._baseclone/-/lodash._baseclone-3.3.0.tgz#303519bf6393fe7e42f34d8b630ef7794e3542b7"
-  dependencies:
-    lodash._arraycopy "^3.0.0"
-    lodash._arrayeach "^3.0.0"
-    lodash._baseassign "^3.0.0"
-    lodash._basefor "^3.0.0"
-    lodash.isarray "^3.0.0"
-    lodash.keys "^3.0.0"
-
-lodash._baseclone@^4.0.0:
-  version "4.5.7"
-  resolved "https://registry.yarnpkg.com/lodash._baseclone/-/lodash._baseclone-4.5.7.tgz#ce42ade08384ef5d62fa77c30f61a46e686f8434"
-
-lodash._basecopy@^3.0.0:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36"
-
-lodash._basecreate@^3.0.0:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821"
-
-lodash._basefor@^3.0.0:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/lodash._basefor/-/lodash._basefor-3.0.3.tgz#7550b4e9218ef09fad24343b612021c79b4c20c2"
-
-lodash._bindcallback@^3.0.0:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
-
-lodash._getnative@^3.0.0:
-  version "3.9.1"
-  resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
-
-lodash._isiterateecall@^3.0.0:
-  version "3.0.9"
-  resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c"
-
-lodash._stack@^4.0.0:
-  version "4.1.3"
-  resolved "https://registry.yarnpkg.com/lodash._stack/-/lodash._stack-4.1.3.tgz#751aa76c1b964b047e76d14fc72a093fcb5e2dd0"
-
 lodash.assign@^4.0.3, lodash.assign@^4.0.6:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
   integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=
 
-lodash.clone@3.0.3:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-3.0.3.tgz#84688c73d32b5a90ca25616963f189252a997043"
-  dependencies:
-    lodash._baseclone "^3.0.0"
-    lodash._bindcallback "^3.0.0"
-    lodash._isiterateecall "^3.0.0"
-
-lodash.create@3.1.1:
-  version "3.1.1"
-  resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7"
-  dependencies:
-    lodash._baseassign "^3.0.0"
-    lodash._basecreate "^3.0.0"
-    lodash._isiterateecall "^3.0.0"
-
 lodash.debounce@^4.0.8:
   version "4.0.8"
   resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
 
-lodash.defaultsdeep@4.3.2:
-  version "4.3.2"
-  resolved "https://registry.yarnpkg.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.3.2.tgz#6c1a586e6c5647b0e64e2d798141b8836158be8a"
-  dependencies:
-    lodash._baseclone "^4.0.0"
-    lodash._stack "^4.0.0"
-    lodash.isplainobject "^4.0.0"
-    lodash.keysin "^4.0.0"
-    lodash.mergewith "^4.0.0"
-    lodash.rest "^4.0.0"
-
 lodash.defaultsdeep@^4.6.0:
   version "4.6.0"
   resolved "https://registry.yarnpkg.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.0.tgz#bec1024f85b1bd96cbea405b23c14ad6443a6f81"
@@ -4508,30 +4250,6 @@ lodash.get@^4.4.2:
   resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
   integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
 
-lodash.isarguments@^3.0.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
-
-lodash.isarray@^3.0.0:
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
-
-lodash.isplainobject@^4.0.0:
-  version "4.0.6"
-  resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
-
-lodash.keys@^3.0.0:
-  version "3.1.2"
-  resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
-  dependencies:
-    lodash._getnative "^3.0.0"
-    lodash.isarguments "^3.0.0"
-    lodash.isarray "^3.0.0"
-
-lodash.keysin@^4.0.0:
-  version "4.2.0"
-  resolved "https://registry.yarnpkg.com/lodash.keysin/-/lodash.keysin-4.2.0.tgz#8cc3fb35c2d94acc443a1863e02fa40799ea6f28"
-
 lodash.mapvalues@^4.6.0:
   version "4.6.0"
   resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c"
@@ -4540,14 +4258,6 @@ lodash.memoize@^4.1.2:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
 
-lodash.mergewith@^4.0.0:
-  version "4.6.1"
-  resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927"
-
-lodash.rest@^4.0.0:
-  version "4.0.5"
-  resolved "https://registry.yarnpkg.com/lodash.rest/-/lodash.rest-4.0.5.tgz#954ef75049262038c96d1fc98b28fdaf9f0772aa"
-
 lodash.sortby@^4.7.0:
   version "4.7.0"
   resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
@@ -4610,10 +4320,6 @@ lru-cache@^4.1.1, lru-cache@^4.1.2:
     pseudomap "^1.0.2"
     yallist "^2.1.2"
 
-lru-cache@~2.6.5:
-  version "2.6.5"
-  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5"
-
 make-dir@^1.0.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
@@ -4799,12 +4505,6 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
 
-minimatch@3.0.3:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774"
-  dependencies:
-    brace-expansion "^1.0.0"
-
 minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
@@ -4822,10 +4522,6 @@ minimist@^1.2.0:
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
   integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
 
-minimist@~0.0.1:
-  version "0.0.10"
-  resolved "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
-
 minipass@^2.2.1, minipass@^2.3.3:
   version "2.3.4"
   resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.4.tgz#4768d7605ed6194d6d576169b9e12ef71e9d9957"
@@ -4884,26 +4580,6 @@ mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@
   dependencies:
     minimist "0.0.8"
 
-mkpath@1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/mkpath/-/mkpath-1.0.0.tgz#ebb3a977e7af1c683ae6fda12b545a6ba6c5853d"
-
-mocha-nightwatch@3.2.2:
-  version "3.2.2"
-  resolved "https://registry.yarnpkg.com/mocha-nightwatch/-/mocha-nightwatch-3.2.2.tgz#91bcb9b3bde057dd7677c78125e491e58d66647c"
-  dependencies:
-    browser-stdout "1.3.0"
-    commander "2.9.0"
-    debug "2.2.0"
-    diff "1.4.0"
-    escape-string-regexp "1.0.5"
-    glob "7.0.5"
-    growl "1.9.2"
-    json3 "3.3.2"
-    lodash.create "3.1.1"
-    mkdirp "0.5.1"
-    supports-color "3.1.2"
-
 mocha-webpack@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/mocha-webpack/-/mocha-webpack-1.1.0.tgz#292158fc191641c943c1ee615329504f47c4b0ba"
@@ -4986,10 +4662,6 @@ move-concurrently@^1.0.1:
     rimraf "^2.5.4"
     run-queue "^1.0.3"
 
-ms@0.7.1:
-  version "0.7.1"
-  resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
-
 ms@2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -5057,30 +4729,11 @@ neo-async@^2.5.0:
   version "2.5.2"
   resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.2.tgz#489105ce7bc54e709d736b195f82135048c50fcc"
 
-netmask@~1.0.4:
-  version "1.0.6"
-  resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35"
-
 nice-try@^1.0.4:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
   integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
 
-nightwatch@^0.9.21:
-  version "0.9.21"
-  resolved "https://registry.yarnpkg.com/nightwatch/-/nightwatch-0.9.21.tgz#9e794a7514b4fd5f46602d368e50515232ab9e90"
-  dependencies:
-    chai-nightwatch "~0.1.x"
-    ejs "2.5.7"
-    lodash.clone "3.0.3"
-    lodash.defaultsdeep "4.3.2"
-    minimatch "3.0.3"
-    mkpath "1.0.0"
-    mocha-nightwatch "3.2.2"
-    optimist "0.6.1"
-    proxy-agent "2.0.0"
-    q "1.4.1"
-
 nise@^1.4.7:
   version "1.4.8"
   resolved "https://registry.yarnpkg.com/nise/-/nise-1.4.8.tgz#ce91c31e86cf9b2c4cac49d7fcd7f56779bfd6b0"
@@ -5351,14 +5004,6 @@ opn@^5.1.0, opn@^5.3.0:
   dependencies:
     is-wsl "^1.1.0"
 
-optimist@0.6.1:
-  version "0.6.1"
-  resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
-  integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY=
-  dependencies:
-    minimist "~0.0.1"
-    wordwrap "~0.0.2"
-
 optionator@^0.8.1, optionator@^0.8.2:
   version "0.8.2"
   resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
@@ -5496,30 +5141,6 @@ p-try@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1"
 
-pac-proxy-agent@1:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz#34a385dfdf61d2f0ecace08858c745d3e791fd4d"
-  dependencies:
-    agent-base "2"
-    debug "2"
-    extend "3"
-    get-uri "2"
-    http-proxy-agent "1"
-    https-proxy-agent "1"
-    pac-resolver "~2.0.0"
-    raw-body "2"
-    socks-proxy-agent "2"
-
-pac-resolver@~2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-2.0.0.tgz#99b88d2f193fbdeefc1c9a529c1f3260ab5277cd"
-  dependencies:
-    co "~3.0.6"
-    degenerator "~1.0.2"
-    ip "1.0.1"
-    netmask "~1.0.4"
-    thunkify "~2.1.1"
-
 pako@~1.0.5:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
@@ -5672,10 +5293,6 @@ pbkdf2@^3.0.3:
     safe-buffer "^5.0.1"
     sha.js "^2.4.8"
 
-pend@~1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
-
 performance-now@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
@@ -6147,19 +5764,6 @@ proxy-addr@~2.0.3, proxy-addr@~2.0.4:
     forwarded "~0.1.2"
     ipaddr.js "1.8.0"
 
-proxy-agent@2.0.0:
-  version "2.0.0"
-  resolved "http://registry.npmjs.org/proxy-agent/-/proxy-agent-2.0.0.tgz#57eb5347aa805d74ec681cb25649dba39c933499"
-  dependencies:
-    agent-base "2"
-    debug "2"
-    extend "3"
-    http-proxy-agent "1"
-    https-proxy-agent "1"
-    lru-cache "~2.6.5"
-    pac-proxy-agent "1"
-    socks-proxy-agent "2"
-
 prr@~1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
@@ -6220,10 +5824,6 @@ punycode@^1.2.4, punycode@^1.4.1:
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
   integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
 
-q@1.4.1:
-  version "1.4.1"
-  resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
-
 q@^1.1.2:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
@@ -6275,15 +5875,6 @@ range-parser@^1.0.3, range-parser@~1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
 
-raw-body@2, raw-body@2.3.3:
-  version "2.3.3"
-  resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3"
-  dependencies:
-    bytes "3.0.0"
-    http-errors "1.6.3"
-    iconv-lite "0.4.23"
-    unpipe "1.0.0"
-
 raw-body@2.3.2:
   version "2.3.2"
   resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89"
@@ -6293,6 +5884,15 @@ raw-body@2.3.2:
     iconv-lite "0.4.19"
     unpipe "1.0.0"
 
+raw-body@2.3.3:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3"
+  dependencies:
+    bytes "3.0.0"
+    http-errors "1.6.3"
+    iconv-lite "0.4.23"
+    unpipe "1.0.0"
+
 rc@^1.2.7:
   version "1.2.8"
   resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
@@ -6344,7 +5944,7 @@ read-pkg@^4.0.1:
     parse-json "^4.0.0"
     pify "^3.0.0"
 
-"readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.3, readable-stream@^2.3.6:
+"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.3, readable-stream@^2.3.6:
   version "2.3.6"
   resolved "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
   dependencies:
@@ -6365,15 +5965,6 @@ readable-stream@1.0:
     isarray "0.0.1"
     string_decoder "~0.10.x"
 
-readable-stream@1.1.x:
-  version "1.1.14"
-  resolved "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
-  dependencies:
-    core-util-is "~1.0.0"
-    inherits "~2.0.1"
-    isarray "0.0.1"
-    string_decoder "~0.10.x"
-
 readdirp@^2.0.0:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525"
@@ -6671,10 +6262,6 @@ select-hose@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
 
-selenium-server@^3.13.0:
-  version "3.14.0"
-  resolved "https://registry.yarnpkg.com/selenium-server/-/selenium-server-3.14.0.tgz#09b79bfebe38d1d3611b7848e0edcf9ac6a54955"
-
 selfsigned@^1.9.1:
   version "1.10.3"
   resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.3.tgz#d628ecf9e3735f84e8bafba936b3cf85bea43823"
@@ -6694,10 +6281,6 @@ semver@^5.0.1:
   version "5.5.1"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477"
 
-semver@~5.0.1:
-  version "5.0.3"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a"
-
 send@0.16.2:
   version "0.16.2"
   resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1"
@@ -6847,10 +6430,6 @@ slice-ansi@1.0.0:
   dependencies:
     is-fullwidth-code-point "^2.0.0"
 
-smart-buffer@^1.0.13:
-  version "1.1.15"
-  resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16"
-
 snapdragon-node@^2.0.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
@@ -6897,21 +6476,6 @@ sockjs@0.3.19:
     faye-websocket "^0.10.0"
     uuid "^3.0.1"
 
-socks-proxy-agent@2:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz#86ebb07193258637870e13b7bd99f26c663df3d3"
-  dependencies:
-    agent-base "2"
-    extend "3"
-    socks "~1.1.5"
-
-socks@~1.1.5:
-  version "1.1.10"
-  resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a"
-  dependencies:
-    ip "^1.1.4"
-    smart-buffer "^1.0.13"
-
 source-list-map@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085"
@@ -7181,12 +6745,6 @@ stylehacks@^4.0.0:
     postcss "^7.0.0"
     postcss-selector-parser "^3.0.0"
 
-supports-color@3.1.2:
-  version "3.1.2"
-  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"
-  dependencies:
-    has-flag "^1.0.0"
-
 supports-color@5.4.0:
   version "5.4.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
@@ -7312,10 +6870,6 @@ through@^2.3.6:
   resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
   integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
 
-thunkify@~2.1.1:
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d"
-
 thunky@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.0.2.tgz#a862e018e3fb1ea2ec3fce5d55605cf57f247371"
@@ -7473,10 +7027,6 @@ type-check@~0.3.2:
   dependencies:
     prelude-ls "~1.1.2"
 
-type-detect@0.1.1:
-  version "0.1.1"
-  resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822"
-
 type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5:
   version "4.0.8"
   resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
@@ -8010,10 +7560,6 @@ window-size@^0.2.0:
   resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075"
   integrity sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=
 
-wordwrap@~0.0.2:
-  version "0.0.3"
-  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
-
 wordwrap@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
@@ -8061,10 +7607,6 @@ xmlchars@^1.3.1:
   resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-1.3.1.tgz#1dda035f833dbb4f86a0c28eaa6ca769214793cf"
   integrity sha512-tGkGJkN8XqCod7OT+EvGYK5Z4SfDQGD30zAa58OcnAa0RRWgzUEK72tkXhsX1FZd+rgnhRxFtmO+ihkp8LHSkw==
 
-xregexp@2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943"
-
 xregexp@4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020"
@@ -8166,12 +7708,6 @@ yargs@^4.8.0:
     y18n "^3.2.1"
     yargs-parser "^2.4.1"
 
-yauzl@2.4.1:
-  version "2.4.1"
-  resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"
-  dependencies:
-    fd-slicer "~1.0.1"
-
 yorkie@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/yorkie/-/yorkie-2.0.0.tgz#92411912d435214e12c51c2ae1093e54b6bb83d9"
diff --git a/requirements.dev.txt b/requirements.dev.txt
index 27871cf0..43886874 100644
--- a/requirements.dev.txt
+++ b/requirements.dev.txt
@@ -1,7 +1,7 @@
-flake8~=3.5.0
-pre-commit~=1.4.1
-pytest-cov~=2.5.1
-pytest-django~=3.1.2
-selenium~=3.14.1
-factory-boy~=2.11.1
-Faker~=0.9.2
+flake8~=3.6.0
+pre-commit~=1.13.0
+pytest-cov~=2.6.0
+pytest-django~=3.4.0
+selenium~=3.141.0
+factory-boy~=2.11.0
+Faker~=1.0.0
diff --git a/requirements.txt b/requirements.txt
index 446b8907..9278d9aa 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,15 +1,15 @@
-django-cors-headers~=2.1.0
+django-cors-headers~=2.4.0
 django-extensions~=2.1
 djangorestframework-jwt~=1.11.0
 djangorestframework~=3.8
 git+https://github.com/robinhundt/djangorestframework-camel-case
 Django~=2.1
-drf-yasg
-gevent~=1.3.2
-gunicorn~=19.7.0
-psycopg2-binary~=2.7.4
-python-json-logger~=0.1.9
-tqdm~=4.19.5
-whitenoise~=3.3.1
-xlrd~=1.0.0
-xkcdpass==1.16.5
+drf-yasg~=1.12.0
+gevent~=1.3.0
+gunicorn~=19.9.0
+psycopg2-binary~=2.7.0
+python-json-logger~=0.1.0
+tqdm~=4.28.0
+whitenoise~=4.1.0
+xlrd~=1.2.0
+xkcdpass==1.17.0
diff --git a/util/format_index.py b/util/format_index.py
index bf2ffa8f..0820d999 100644
--- a/util/format_index.py
+++ b/util/format_index.py
@@ -6,7 +6,7 @@ file = 'core/templates/index.html'
 with open(file, "r+") as f:
     s = f.read()
     f.seek(0)
-    f.write("{% load staticfiles %}\n" + s)
+    f.write("{% load static %}\n" + s)
 
 for i, line in enumerate(fileinput.input(file, inplace=1)):
     sys.stdout.write(line.replace('/static/', "{% static '"))
diff --git a/util/importer.py b/util/importer.py
index a49641ce..e7475e88 100644
--- a/util/importer.py
+++ b/util/importer.py
@@ -12,7 +12,7 @@ from core.models import UserAccount as User
 from util.factories import GradyUserFactory
 from util.messages import info, warn
 
-WELCOME = '''
+WELCOME = r'''
    ______               __         ____                           __
   / ____/________ _____/ /_  __   /  _/___ ___  ____  ____  _____/ /____  _____
  / / __/ ___/ __ `/ __  / / / /   / // __ `__ \/ __ \/ __ \/ ___/ __/ _ \/ ___/
@@ -67,7 +67,7 @@ class chdir_context(object):
         info(f'Returned to {self.old_dir}')
 
 
-def i(prompt: str, default: str='', is_path: bool=False, is_file: bool=False):
+def i(prompt: str, default: str = '', is_path: bool = False, is_file: bool = False):
     if default is YES or default is NO:
         answer = valid[input(f'[Q] {prompt} ({default}): ').lower() or (
             'y' if YES == default else 'n')]
@@ -460,12 +460,12 @@ def start():
         else:
             call_loader(call_order[int(fid)])
 
-    except (EOFError, KeyboardInterrupt) as err:
+    except (EOFError, KeyboardInterrupt):
         print()
         return
-    except FileNotFoundError as err:
+    except FileNotFoundError:
         raise
-    except Exception as err:
+    except Exception:
         import traceback
         traceback.print_exc()
     finally:
-- 
GitLab


From 06ccdaccde4b16978d640f879e6787f20944af16 Mon Sep 17 00:00:00 2001
From: Dominik Seeger <dominik.seeger@gmx.net>
Date: Fri, 4 Jan 2019 17:07:20 +0100
Subject: [PATCH 13/21] added tests for export mixin

added more tests for exportMixin
---
 .../src/components/export/InstanceExport.vue  |   2 +-
 frontend/src/components/mixins/mixins.ts      |   8 +-
 .../tests/unit/components/AutoLogout.spec.ts  |  12 +-
 .../tests/unit/mixins/exportMixin.spec.ts     | 153 ++++++++++++++++++
 frontend/tests/utils/testUtils.ts             |  40 +++++
 5 files changed, 204 insertions(+), 11 deletions(-)
 create mode 100644 frontend/tests/unit/mixins/exportMixin.spec.ts
 create mode 100644 frontend/tests/utils/testUtils.ts

diff --git a/frontend/src/components/export/InstanceExport.vue b/frontend/src/components/export/InstanceExport.vue
index 681e0e6b..844a0ab4 100644
--- a/frontend/src/components/export/InstanceExport.vue
+++ b/frontend/src/components/export/InstanceExport.vue
@@ -42,7 +42,7 @@ import { exportMixin, ExportType } from '@/components/mixins/mixins'
 export default class DataExport extends exportMixin {
   exportDialog = true
   mapFile: File | null = null
-  exportType = ExportType.JSON     // instance export is only available as JSON
+  exportType = ExportType.JSON // instance export is only available as JSON
 
   get studentMap () { return getters.state.studentMap }
 
diff --git a/frontend/src/components/mixins/mixins.ts b/frontend/src/components/mixins/mixins.ts
index 972371d0..bee6fc96 100644
--- a/frontend/src/components/mixins/mixins.ts
+++ b/frontend/src/components/mixins/mixins.ts
@@ -45,7 +45,7 @@ export class exportMixin extends Vue {
     } else {
       throw new Error('Unsupported export type')
     }
-    
+
     if (this.mapFile || this.mapFileLoaded) {
       this.getMappedExportFile(studentData)
     } else {
@@ -85,7 +85,7 @@ export class exportMixin extends Vue {
     }, '') + '\n' // add trailing newline
   }
 
-  optionalConvertAndCreatePopup(studentData: StudentExportItem[] | InstanceExportData) {
+  optionalConvertAndCreatePopup (studentData: StudentExportItem[] | InstanceExportData) {
     const convertedData = this.exportType === ExportType.CSV
       ? this.jsonToCSV(studentData as StudentExportItem[]) : studentData
       // we have a cast here because only student export may be converted to csv
@@ -136,6 +136,6 @@ export class exportMixin extends Vue {
     this.exportDialog = true
   }
 
-  applyMapping (exportData: StudentExportItem[] | InstanceExportData): void { throw new Error("Not implemented.") }
-  createDownloadPopup (content: string | StudentExportItem[] | InstanceExportData, fileType: ExportType): void { throw new Error("Not implemented.") }
+  applyMapping (exportData: StudentExportItem[] | InstanceExportData): void { throw new Error('Not implemented.') }
+  createDownloadPopup (content: string | StudentExportItem[] | InstanceExportData, fileType: ExportType): void { throw new Error('Not implemented.') }
 }
diff --git a/frontend/tests/unit/components/AutoLogout.spec.ts b/frontend/tests/unit/components/AutoLogout.spec.ts
index 5a5730f6..57501f45 100644
--- a/frontend/tests/unit/components/AutoLogout.spec.ts
+++ b/frontend/tests/unit/components/AutoLogout.spec.ts
@@ -15,15 +15,15 @@ describe('Auto Logout Unit Tests', () => {
   let store: any = null
   let consoleTemp = {
     warn: console.warn,
-    error: console.error,
+    error: console.error
   }
 
-  before(function() {
-    console.warn = function() {}
-    console.error = function() {}
+  before(function () {
+    console.warn = function () {}
+    console.error = function () {}
   })
 
-  after(function() {
+  after(function () {
     console.warn = consoleTemp.warn
     console.error = consoleTemp.error
   })
@@ -37,7 +37,7 @@ describe('Auto Logout Unit Tests', () => {
   afterEach(() => {
     sinon.restore()
   })
-  
+
   it('should be hidden by default', () => {
     let wrapper = mount(AutoLogout, { localVue: localVue, store })
     wrapper.vm.$data.logoutDialog.should.equal(false)
diff --git a/frontend/tests/unit/mixins/exportMixin.spec.ts b/frontend/tests/unit/mixins/exportMixin.spec.ts
new file mode 100644
index 00000000..a6c6cdd0
--- /dev/null
+++ b/frontend/tests/unit/mixins/exportMixin.spec.ts
@@ -0,0 +1,153 @@
+import { exportMixin, ExportType } from '@/components/mixins/mixins'
+import sinon from 'sinon'
+import ax from '@/api'
+import testUtils, { FakeFileReader } from '@/../tests/utils/testUtils'
+import chai from 'chai'
+import { mutations as mut } from '@/store/mutations'
+chai.should()
+
+describe('Export Mixin Unit Tests', () => {
+  describe('getExportFile()', () => {
+    let e = new exportMixin()
+
+    afterEach(() => {
+      sinon.restore()
+    })
+
+    it('should fetch from the dataExport endpoint when called with "data"', async () => {
+      let stub = sinon.stub().resolves({ data: testUtils.studentExports })
+      sinon.replace(ax, 'post', stub)
+      sinon.replace(e, 'optionalConvertAndCreatePopup', () => {})
+      await e.getExportFile('data')
+      stub.calledWith('/api/export/json/').should.equal(true)
+    })
+    it('should fetch from instanceExport endpoint when called with "instance"', async () => {
+      let stub = sinon.stub().resolves({ data: testUtils.instanceExports })
+      sinon.replace(ax, 'get', stub)
+      sinon.replace(e, 'optionalConvertAndCreatePopup', () => {})
+      await e.getExportFile('instance')
+      stub.calledWith('/api/instance/export').should.equal(true)
+    })
+    it('should try to apply mapping when map file is given', async () => {
+      let spy = sinon.spy()
+      e.mapFile = testUtils.fakeFile
+      sinon.replace(ax, 'post', sinon.stub().resolves({ data: testUtils.studentExports }))
+      sinon.replace(e, 'getMappedExportFile', spy)
+      await e.getExportFile('data')
+      e.mapFile = null
+      spy.calledOnce.should.equal(true)
+      spy.calledWith(testUtils.studentExports).should.equal(true)
+    })
+    it('should call popup creation method with proper arguments when no mapping file is given', async () => {
+      let spy = sinon.spy()
+      sinon.replace(ax, 'post', sinon.stub().resolves({ data: testUtils.studentExports }))
+      sinon.replace(e, 'optionalConvertAndCreatePopup', spy)
+      await e.getExportFile('data')
+      spy.calledOnce.should.equal(true)
+      spy.calledWith(testUtils.studentExports).should.equal(true)
+    })
+  })
+  describe('optionalConvertAndCreatePopup()', () => {
+    let e = new exportMixin()
+
+    afterEach(() => {
+      sinon.restore()
+    })
+
+    it('should convert data to csv when csv is selected in dropdown', () => {
+      let stub = sinon.stub().returns('students as csv')
+      let spy = sinon.spy()
+      e.exportType = ExportType.CSV
+      sinon.replace(e, 'jsonToCSV', stub)
+      sinon.replace(e, 'createDownloadPopup', spy)
+      e.optionalConvertAndCreatePopup(testUtils.studentExports)
+      stub.calledWith(testUtils.studentExports).should.equal(true)
+      spy.calledWith('students as csv', ExportType.CSV).should.equal(true)
+    })
+  })
+  describe('readMapFileAndCommit', () => {
+    let e = new exportMixin()
+    // @ts-ignore
+    global.FileReader = FakeFileReader
+
+    afterEach(() => {
+      sinon.restore()
+    })
+
+    it('should throw an error when no mapfile was given', () => {
+      e.readMapFileAndCommit().catch((res) => {
+        res.should.not.equal(undefined)
+      })
+    })
+    it('should read selected mapFile', async () => {
+      e.mapFile = testUtils.fakeFile
+      let stub = sinon.stub()
+      // @ts-ignore
+      sinon.replace(global.FileReader.prototype, 'readAsText', stub)
+      e.readMapFileAndCommit()
+      e.mapFile = null
+      stub.args[0][0].name.should.equal(testUtils.fakeFile.name)
+    })
+    it('should parse file and trigger mutation when file was read', async () => {
+      e.mapFile = testUtils.fakeFile
+      let stub = sinon.stub().returns(testUtils.studentMap)
+      let mutSpy = sinon.spy()
+      sinon.replace(e, 'parseCSVMap', stub)
+      sinon.replace(mut, 'SET_STUDENT_MAP', mutSpy)
+      await e.readMapFileAndCommit()
+      e.mapFile = null
+      stub.calledOnce.should.equal(true)
+      stub.calledWithExactly('Grady testUtils fake result').should.equal(true)
+      mutSpy.calledWithExactly(testUtils.studentMap).should.equal(true)
+    })
+  })
+  describe('getMappedExportFile', () => {
+    let e = new exportMixin
+
+    afterEach(() => {
+      sinon.restore()
+    })
+
+    it('should read mapFile if it is not already loaded when used with instanceExports then apply mapping and create popup', async () => {
+      e.mapFile = testUtils.fakeFile
+      let spy = sinon.spy()
+      sinon.replace(e, 'readMapFileAndCommit', spy)
+      sinon.replace(e, 'applyMapping', () => {})
+      sinon.replace(e, 'optionalConvertAndCreatePopup', () => {})
+      await e.getMappedExportFile(testUtils.instanceExports)
+      e.mapFile = null
+      spy.called.should.equal(true)
+    })
+    it('should read mapFile if it is not already loaded when used with dataExport then apply mapping and create popup', async () => {
+      e.mapFile = testUtils.fakeFile
+      let spy = sinon.spy()
+      sinon.replace(e, 'readMapFileAndCommit', spy)
+      sinon.replace(e, 'applyMapping', () => {})
+      sinon.replace(e, 'optionalConvertAndCreatePopup', () => {})
+      await e.getMappedExportFile(testUtils.studentExports)
+      e.mapFile = null
+      spy.called.should.equal(true)
+    })
+    it('should apply the mapping and then open popup when mapFile is loaded', async () => {
+      sinon.replaceGetter(e, 'mapFileLoaded', () => { return true })
+      let mappingSpy = sinon.spy()
+      let popupSpy = sinon.spy()
+      sinon.replace(e, 'applyMapping', mappingSpy)
+      sinon.replace(e, 'optionalConvertAndCreatePopup', popupSpy)
+      await e.getMappedExportFile(testUtils.instanceExports)
+      e.mapFile = null
+      mappingSpy.calledWithExactly(testUtils.instanceExports).should.equal(true)
+      popupSpy.calledWithExactly(testUtils.instanceExports).should.equal(true)
+    })
+  })
+  describe('jsonToCSV()', () => {
+    let e = new exportMixin
+
+    it('should correctly parse JSON input to CSV with ; delimiter', () => {
+      let csv = e.jsonToCSV(testUtils.studentExports)
+      let expected =  'Matrikel;Name;Username;Sum;Exam;Password;test01\n' + 
+      '1000000;name;username;100;exam;pwd;100\n'
+      csv.should.equal(expected)
+    })
+  })
+})
diff --git a/frontend/tests/utils/testUtils.ts b/frontend/tests/utils/testUtils.ts
new file mode 100644
index 00000000..599cb750
--- /dev/null
+++ b/frontend/tests/utils/testUtils.ts
@@ -0,0 +1,40 @@
+import { StudentExportItem, InstanceExportData } from '@/api'
+
+export class FakeFileReader {
+  result: String = 'Grady testUtils fake result'
+
+  constructor () {}
+  // readAsText will execute the callback that was provided to onload
+  readAsText (file: File) { this.onload({ target: this }) }
+  onload: Function = () => { }
+}
+
+export default {
+  studentExports: <StudentExportItem[]> [{
+    Matrikel: '1000000',
+    Name: 'name',
+    Username: 'username',
+    Sum: 100,
+    Exam: 'exam',
+    Password: 'pwd',
+    Scores: [{ type: 'test01', score: 100 }]
+  }],
+  instanceExports: <InstanceExportData> {
+    students: [{
+      name: 'test',
+      matrikelNo: '1000000'
+    }]
+  },
+  fakeFile: <File> {
+    lastModified: 1,
+    name: 'Grady testUtils fake file',
+    size: 1,
+    slice: (start?: number | undefined, end?: number | undefined, contentType?: string | undefined): Blob => { return new Blob() },
+    type: 'fake file'
+  },
+  studentMap: {
+    '1000000': {
+      matrikelNo: '1000000', name: 'test'
+    }
+  }
+}
-- 
GitLab


From 6f6883d0573268f419d6931dc6a393dd501de94d Mon Sep 17 00:00:00 2001
From: Dominik Seeger <dominik.seeger@gmx.net>
Date: Fri, 4 Jan 2019 18:32:40 +0100
Subject: [PATCH 14/21] changed to use testUtils / added a test

---
 .../tests/unit/components/DataExport.spec.ts  | 36 +++++++++----------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/frontend/tests/unit/components/DataExport.spec.ts b/frontend/tests/unit/components/DataExport.spec.ts
index 41f965a2..f41c8bf8 100644
--- a/frontend/tests/unit/components/DataExport.spec.ts
+++ b/frontend/tests/unit/components/DataExport.spec.ts
@@ -4,6 +4,7 @@ import DataExport from '@/components/export/DataExport.vue'
 import sinon from 'sinon'
 import chai from 'chai'
 import VueRouter from 'vue-router'
+import testUtils from '@/../tests/utils/testUtils'
 
 import * as api from '@/api'
 chai.should()
@@ -23,17 +24,6 @@ describe('DataExport Component Unit Tests', () => {
     error: console.error
   }
 
-  // for creating test data
-  let students = [{
-    Matrikel: 1000000,
-    Name: 'test',
-    Username: 'test',
-    Sum: 100,
-    Exam: 'test',
-    Password: 'test',
-    Scores: { type: 'test', score: 100 }
-  }]
-
   before(function () {
     console.warn = function () {}
     console.error = function () {}
@@ -63,7 +53,7 @@ describe('DataExport Component Unit Tests', () => {
     // @ts-ignore
     sinon.replace(wrapper.vm, 'createDownloadPopup', sinon.spy())
     // @ts-ignore
-    wrapper.vm.optionalConvertAndCreatePopup(students)
+    wrapper.vm.optionalConvertAndCreatePopup(testUtils.studentExports)
     spy.called.should.equal(true)
   })
   it('should download JSON when selected', () => {
@@ -75,12 +65,12 @@ describe('DataExport Component Unit Tests', () => {
     // @ts-ignore
     sinon.replace(wrapper.vm, 'createDownloadPopup', sinon.spy())
     // @ts-ignore
-    wrapper.vm.optionalConvertAndCreatePopup(students)
+    wrapper.vm.optionalConvertAndCreatePopup(testUtils.studentExports)
     spy.called.should.equal(false)
   })
   it('should download obfuscated data when no mapping was selected', async () => {
     let wrapper = mount(DataExport, { localVue: localVue, store })
-    let stub = sinon.stub().returns(Promise.resolve({ data: students }))
+    let stub = sinon.stub().returns(Promise.resolve({ data: testUtils.studentExports }))
     let spy = sinon.spy()
     // @ts-ignore replace ax.post because of fetch in getExportFile
     sinon.replace(api.default, 'post', stub)
@@ -88,10 +78,20 @@ describe('DataExport Component Unit Tests', () => {
     sinon.replace(wrapper.vm, 'optionalConvertAndCreatePopup', spy)
     // @ts-ignore
     await wrapper.vm.getExportFile('data')
-    spy.called.should.equal(true)
-    spy.calledWithExactly(students).should.equal(true)
+    spy.calledWithExactly(testUtils.studentExports).should.equal(true)
   })
-  it('should download deobfuscated data when no mapping was selected', async () => {
-
+  it('should download deobfuscated data when mapping was selected', async () => {
+    let wrapper = mount(DataExport, { localVue: localVue, store })
+    let stub = sinon.stub().returns(Promise.resolve({ data: testUtils.studentExports }))
+    let spy = sinon.spy()
+    // @ts-ignore
+    wrapper.vm.mapFile = testUtils.fakeFile
+    // @ts-ignore replace ax.post because of fetch in getExportFile
+    sinon.replace(api.default, 'post', stub)
+    // @ts-ignore
+    sinon.replace(wrapper.vm, 'getMappedExportFile', spy)
+    // @ts-ignore
+    await wrapper.vm.getExportFile('data')
+    spy.calledWithExactly(testUtils.studentExports).should.equal(true)
   })
 })
-- 
GitLab


From 556bf7b3e9f35fa37171336013a549bb04ba6088 Mon Sep 17 00:00:00 2001
From: Dominik Seeger <dominik.seeger@gmx.net>
Date: Fri, 4 Jan 2019 18:59:03 +0100
Subject: [PATCH 15/21] added stage for frontend unit tests

---
 .gitlab-ci.yml | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 84953ada..d5d3a240 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -39,6 +39,7 @@ build_frontend:
   artifacts:
     paths:
       - frontend/dist
+      - frontend/node_modules/
     expire_in: 20 minutes
   cache:
     key: "$CI_JOB_NAME"
@@ -102,6 +103,17 @@ test_frontend:
     - python manage.py collectstatic --no-input
     - HEADLESS_TESTS=True pytest --ds=grady.settings.test functional_tests
 
+test_frontend_unit:
+  image: node:carbon
+  stage: test
+  dependencies:
+    - build_frontend
+  tags: 
+    - docker
+  script:
+    - cd frontend/
+    - yarn test:unit
+
 
 # =========================== Build Image section ============================ #
 build_backend:
-- 
GitLab


From b695ab50326ea68b71f3c218e6024c8653398be9 Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Fri, 4 Jan 2019 21:50:35 +0100
Subject: [PATCH 16/21] Added create feedback e2e tests

---
 Makefile                                      |   4 +
 frontend/src/components/SubmissionTests.vue   |  10 +-
 frontend/src/components/SubmissionType.vue    |   2 +-
 .../submission_notes/SubmissionCorrection.vue |   7 +-
 .../submission_notes/base/CommentForm.vue     |   4 +-
 .../AnnotatedSubmissionBottomToolbar.vue      |   5 +
 .../subscriptions/SubscriptionEnded.vue       |   2 +-
 .../subscriptions/SubscriptionList.vue        |   2 +-
 functional_tests/test_feedback_creation.py    | 245 ++++++++++++++++++
 functional_tests/test_front_pages.py          |  11 +-
 functional_tests/util.py                      |  11 +
 util/factory_boys.py                          |  17 +-
 12 files changed, 286 insertions(+), 34 deletions(-)
 create mode 100644 functional_tests/test_feedback_creation.py

diff --git a/Makefile b/Makefile
index 3b60ef1f..b20a9c5d 100644
--- a/Makefile
+++ b/Makefile
@@ -33,6 +33,10 @@ test:
 teste2e:
 	cd frontend && yarn build && cp dist/index.html ../core/templates && cd .. && python util/format_index.py && python manage.py collectstatic --no-input && HEADLESS_TESTS=$(headless) pytest --ds=grady.settings $(path); git checkout core/templates/index.html
 
+teste2e-nc:
+	cp frontend/dist/index.html ./core/templates && python util/format_index.py && python manage.py collectstatic --no-input && HEADLESS_TESTS=$(headless) pytest --ds=grady.settings $(path); git checkout core/templates/index.html
+
+
 coverage:
 	DJANGO_SETTINGS_MODULE=grady.settings pytest --cov
 	coverage html
diff --git a/frontend/src/components/SubmissionTests.vue b/frontend/src/components/SubmissionTests.vue
index d782693f..0012808e 100644
--- a/frontend/src/components/SubmissionTests.vue
+++ b/frontend/src/components/SubmissionTests.vue
@@ -1,5 +1,5 @@
 <template>
-    <v-card>
+    <v-card id="submission-tests">
       <v-card-title class="title py-0" v-if="tests.length > 0">
           Tests
         <v-spacer/>
@@ -12,15 +12,15 @@
         No Tests available
       </v-card-title>
       <v-expansion-panel v-if="expanded">
-        <v-expansion-panel-content v-for="item in tests" :key="item.pk">
-          <div slot="header">
-            <v-layout row class="pr-4">
+        <v-expansion-panel-content v-for="item in tests" :key="item.pk" >
+          <div slot="header" name="test-name-label">
+            <v-layout row class="pr-4" >
               {{item.name}}
               <v-spacer/>
               {{item.label}}
             </v-layout>
           </div>
-          <v-card>
+          <v-card name="test-output">
             <v-card-text class="test-output">{{item.annotation}}</v-card-text>
           </v-card>
         </v-expansion-panel-content>
diff --git a/frontend/src/components/SubmissionType.vue b/frontend/src/components/SubmissionType.vue
index 279ba355..3554e20d 100644
--- a/frontend/src/components/SubmissionType.vue
+++ b/frontend/src/components/SubmissionType.vue
@@ -1,6 +1,6 @@
 <template>
   <v-layout column>
-    <v-card>
+    <v-card id="submission-type">
       <v-card-title class="title mb-2">{{ name }} - Full score: {{ fullScore }}</v-card-title>
       <v-expansion-panel expand v-model="expanded">
         <v-expansion-panel-content
diff --git a/frontend/src/components/submission_notes/SubmissionCorrection.vue b/frontend/src/components/submission_notes/SubmissionCorrection.vue
index 88811af8..29d973b9 100644
--- a/frontend/src/components/submission_notes/SubmissionCorrection.vue
+++ b/frontend/src/components/submission_notes/SubmissionCorrection.vue
@@ -5,8 +5,8 @@
         class="mb-1 elevation-1"
         slot="header"
       />
-      <template slot="table-content">
-        <tr v-for="(code, lineNo) in submission" :key="`${submissionObj.pk}${lineNo}`">
+      <template slot="table-content" id='sub-lines'>
+        <tr v-for="(code, lineNo) in submission" :key="`${submissionObj.pk}${lineNo}`" :id="`sub-line-${lineNo}`">
           <submission-line
             :code="code"
             :line-no="lineNo"
@@ -134,7 +134,8 @@ export default {
         this.$notify({
           title: 'Feedback creation Error!',
           text: err.message,
-          type: 'error'
+          type: 'error',
+          duration: -1
         })
       }).finally(() => {
         this.loading = false
diff --git a/frontend/src/components/submission_notes/base/CommentForm.vue b/frontend/src/components/submission_notes/base/CommentForm.vue
index 32989753..1e25cbd8 100644
--- a/frontend/src/components/submission_notes/base/CommentForm.vue
+++ b/frontend/src/components/submission_notes/base/CommentForm.vue
@@ -13,8 +13,8 @@
       auto-grow
       hide-details
     />
-    <v-btn color="success" @click="submitFeedback"><v-icon>check</v-icon>Submit</v-btn>
-    <v-btn @click="collapseTextField"><v-icon>cancel</v-icon>cancel</v-btn>
+    <v-btn id="submit-comment" color="success" @click="submitFeedback"><v-icon>check</v-icon>Submit</v-btn>
+    <v-btn id="cancel-comment" @click="collapseTextField"><v-icon>cancel</v-icon>cancel</v-btn>
   </div>
 </template>
 
diff --git a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue
index 7e25ecf1..0005c11b 100644
--- a/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue
+++ b/frontend/src/components/submission_notes/toolbars/AnnotatedSubmissionBottomToolbar.vue
@@ -3,6 +3,7 @@
     <v-tooltip top v-if="skippable">
       <v-btn
         slot="activator"
+        id="skip-submission"
         outline round color="grey darken-2"
         @click="skipSubmission"
       >Skip</v-btn>
@@ -19,6 +20,7 @@
     <input
       class="score-text-field"
       type="number"
+      id="score-input"
       v-model="score"
       @input="validateScore"
       @change="validateScore"
@@ -26,11 +28,13 @@
     <span>&nbsp;/ {{fullScore}}</span>
     <v-btn
       outline round flat
+      id="score-zero"
       @click="score = 0"
       color="red lighten-1"
       class="score-button">0</v-btn>
     <v-btn
       outline round flat
+      id="score-full"
       @click="score = fullScore"
       color="blue darken-3"
       class="score-button">{{fullScore}}</v-btn>
@@ -45,6 +49,7 @@
       <v-btn
         color="success"
         slot="activator"
+        id="submit-feedback"
         :loading="loading"
         @click="submit"
       >Submit<v-icon>chevron_right</v-icon></v-btn>
diff --git a/frontend/src/components/subscriptions/SubscriptionEnded.vue b/frontend/src/components/subscriptions/SubscriptionEnded.vue
index 7db964e7..598df9d8 100644
--- a/frontend/src/components/subscriptions/SubscriptionEnded.vue
+++ b/frontend/src/components/subscriptions/SubscriptionEnded.vue
@@ -1,5 +1,5 @@
 <template>
-  <v-card class="mx-auto center-page">
+  <v-card class="mx-auto center-page" id="subscription-ended">
     <v-card-title class="title">
       It seems like your subscription has (temporarily) ended.
     </v-card-title>
diff --git a/frontend/src/components/subscriptions/SubscriptionList.vue b/frontend/src/components/subscriptions/SubscriptionList.vue
index 316b1b0e..fcdf863c 100644
--- a/frontend/src/components/subscriptions/SubscriptionList.vue
+++ b/frontend/src/components/subscriptions/SubscriptionList.vue
@@ -21,7 +21,7 @@
         {{item}}
       </v-tab>
       <v-tab-item v-for="(stage, i) in stages" :key="i">
-        <subscriptions-for-stage :stage="stage"/>
+        <subscriptions-for-stage :stage="stage" :id="`stage-${i}`"/>
       </v-tab-item>
     </v-tabs>
   </v-card>
diff --git a/functional_tests/test_feedback_creation.py b/functional_tests/test_feedback_creation.py
new file mode 100644
index 00000000..74486392
--- /dev/null
+++ b/functional_tests/test_feedback_creation.py
@@ -0,0 +1,245 @@
+from django.test import LiveServerTestCase
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.common.exceptions import StaleElementReferenceException
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as ec
+
+from core.models import UserAccount, Submission, FeedbackComment
+from functional_tests.util import (login, create_browser, reset_browser_after_test,
+                                   subscriptions_loaded_cond)
+from util import factory_boys as fact
+
+
+class UntestedParent:
+    class TestFeedbackCreationGeneric(LiveServerTestCase):
+        browser: webdriver.Firefox = None
+        username = None
+        password = None
+        role = None
+
+        @classmethod
+        def setUpClass(cls):
+            super().setUpClass()
+            cls.browser = create_browser()
+
+        @classmethod
+        def tearDownClass(cls):
+            super().tearDownClass()
+            cls.browser.quit()
+
+        def setUp(self):
+            self.sub_type = fact.SubmissionTypeFactory.create()
+            fact.SubmissionFactory.create_batch(2, type=self.sub_type)
+
+        def tearDown(self):
+            reset_browser_after_test(self.browser, self.live_server_url)
+
+        def _login(self):
+            login(self.browser, self.live_server_url, self.username, self.password)
+
+        def _reconstruct_submission_code(self):
+            sub_table = self.browser.find_element_by_class_name('submission-table')
+            lines = sub_table.find_elements_by_tag_name('tr')
+            line_no_code_pairs = [
+                (line.get_attribute('id'),
+                 # call get_attribute here to get non normalized text
+                 # https://github.com/SeleniumHQ/selenium/issues/2608
+                 line.find_element_by_class_name('code-cell-content').get_attribute('textContent'))
+                for line in lines
+            ]
+            line_no_code_pairs.sort(key=lambda x: x[0])  # sort by ids
+            code_lines = list(zip(*line_no_code_pairs))[1]
+            return '\n'.join(code_lines)
+
+        # stage can be 'initial', 'validate', or 'conflict'
+        def _go_to_subscription(self, stage='initial', sub_type=None):
+            tasks = self.browser.find_element_by_name('subscription-list')
+            WebDriverWait(self.browser, 3).until(subscriptions_loaded_cond(tasks))
+            tab = tasks.find_element_by_xpath(f'//*[contains(text(), "{stage}")]')
+            tab.click()
+            sub_type = sub_type if sub_type is not None else self.sub_type
+
+            sub_type_xpath = f'//*[contains(text(), "{sub_type.name}") ' \
+                f'and not(contains(@class, "inactive"))]'
+            WebDriverWait(self.browser, 3).until(
+                ec.element_to_be_clickable((By.XPATH, sub_type_xpath)),
+                message="SubmissionType not clickable"
+            )
+            sub_type_el = tasks.find_element_by_xpath(sub_type_xpath)
+            sub_type_el.click()
+            WebDriverWait(self.browser, 3).until(ec.url_contains('subscription'))
+
+        def write_comments_on_lines(self, line_comment_tuples):
+            """ line_comment_tuples is an iterable containing tuples of
+                (line_no, comment) where the line number starts at 1
+            """
+
+            sub_table = self.browser.find_element_by_class_name('submission-table')
+            lines = sub_table.find_elements_by_tag_name('tr')
+
+            for (line_no, comment) in line_comment_tuples:
+                line = lines[line_no-1]
+                line.find_element_by_tag_name('button').click()
+                textarea = line.find_element_by_tag_name('textarea')
+                textarea.send_keys(comment)
+                line.find_element_by_id('submit-comment').click()
+
+        def test_student_text_is_correctly_displayed(self):
+            self._login()
+            self._go_to_subscription()
+            code = self._reconstruct_submission_code()
+            # query db for Submission with seen code, throws if not present and test fails
+            Submission.objects.get(text=code)
+
+        def test_submission_type_is_correctly_displayed(self):
+            self._login()
+            self._go_to_subscription()
+            sub_type_el = self.browser.find_element_by_id('submission-type')
+            title = sub_type_el.find_element_by_class_name('title')
+            self.assertEqual(
+                f'{self.sub_type.name} - Full score: {self.sub_type.full_score}',
+                title.text
+            )
+            solution = sub_type_el.find_element_by_class_name('solution-code')
+            self.assertEqual(self.sub_type.solution, solution.get_attribute('textContent'))
+            description = sub_type_el.find_element_by_class_name('type-description')
+            html_el_in_desc = description.find_element_by_tag_name('h1')
+            self.assertEqual('This', html_el_in_desc.text)
+
+        def test_test_output_is_displayed(self):
+            # create a test for every submission
+            test = None
+            for submission in Submission.objects.all():
+                test = fact.TestFactory.create(submission=submission, annotation='This is a test')
+            self._login()
+            self._go_to_subscription()
+            tests = self.browser.find_element_by_id('submission-tests')
+            name_label = tests.find_element_by_name('test-name-label')
+            name_label.click()
+            self.assertIn(test.name, name_label.text)
+            self.assertIn(test.label, name_label.text)
+            test_output = tests.find_element_by_name('test-output')
+            WebDriverWait(self.browser, 3).until(ec.visibility_of(test_output))
+            self.assertEqual(test.annotation, test_output.text)
+
+        def wait_until_code_changes(self, code):
+            def condition(*args):
+                try:
+                    # code might change during the call resulting in the exception
+                    new_code = self._reconstruct_submission_code()
+                except StaleElementReferenceException:
+                    return False
+                return code != new_code
+            return condition
+
+        def test_can_give_max_score(self):
+            self._login()
+            self._go_to_subscription()
+            code = self._reconstruct_submission_code()
+            self.browser.find_element_by_id('score-full').click()
+            submit_btn = self.browser.find_element_by_id('submit-feedback')
+            submit_btn.click()
+            WebDriverWait(self.browser, 3).until(
+                self.wait_until_code_changes(code)
+            )
+            submission_for_code = Submission.objects.get(text=code)
+            self.assertEqual(self.sub_type.full_score, submission_for_code.feedback.score)
+
+        def test_zero_score_without_warning_gives_error(self):
+            self._login()
+            self._go_to_subscription()
+            self.browser.find_element_by_id('score-zero').click()
+            submit_btn = self.browser.find_element_by_id('submit-feedback')
+            submit_btn.click()
+            notification = self.browser.find_element_by_class_name('notification-content')
+            self.assertIn('comment', notification.text)
+
+        def test_can_give_zero_score(self):
+            self._login()
+            self._go_to_subscription()
+            code = self._reconstruct_submission_code()
+            self.browser.find_element_by_id('score-zero').click()
+            self.write_comments_on_lines([(0, 'A comment')])
+            self.browser.find_element_by_id('submit-feedback').click()
+            WebDriverWait(self.browser, 3).until(self.wait_until_code_changes(code))
+            submission_for_code = Submission.objects.get(text=code)
+            self.assertEqual(0, submission_for_code.feedback.score)
+
+        def test_can_give_comments_and_decreased_score(self):
+            self._login()
+            self._go_to_subscription()
+            code = self._reconstruct_submission_code()
+
+            # give half full score
+            score_input = self.browser.find_element_by_id('score-input')
+            score_input.send_keys(self.sub_type.full_score // 2)
+
+            # give feedback on first and last line of submission
+            comment_text = 'This is feedback'
+            self.write_comments_on_lines([
+                (1, comment_text), (0, comment_text)  # 0 corresponds to the last line
+            ])
+
+            submit_btn = self.browser.find_element_by_id('submit-feedback')
+            submit_btn.click()
+            WebDriverWait(self.browser, 3).until(
+                self.wait_until_code_changes(code)
+            )
+            submission_for_code = Submission.objects.get(text=code)
+            self.assertEqual(self.sub_type.full_score // 2, submission_for_code.feedback.score)
+            self.assertEqual(2, submission_for_code.feedback.feedback_lines.count())
+            fst_comment = FeedbackComment.objects.get(
+                of_feedback=submission_for_code.feedback,
+                of_line=1
+            )
+            self.assertEqual(comment_text, fst_comment.text)
+            last_line_of_sub = len(submission_for_code.text.split('\n'))
+            snd_comment = FeedbackComment.objects.get(
+                of_feedback=submission_for_code.feedback,
+                of_line=last_line_of_sub
+            )
+            self.assertEqual(comment_text, snd_comment.text)
+
+        def test_can_skip_submission(self):
+            self._login()
+            self._go_to_subscription()
+            code = self._reconstruct_submission_code()
+            self.browser.find_element_by_id('skip-submission').click()
+            WebDriverWait(self.browser, 3).until(self.wait_until_code_changes(code))
+
+        def test_can_validate_submission(self):
+            self._login()
+            self._go_to_subscription()
+            code = self._reconstruct_submission_code()
+            self.write_comments_on_lines([(0, 'A comment by me')])
+            self.browser.find_element_by_id('score-zero').click()
+            self.browser.find_element_by_id('submit-feedback').click()
+            WebDriverWait(self.browser, 3).until(self.wait_until_code_changes(code))
+            reset_browser_after_test(self.browser, self.live_server_url)  # logs out user
+
+            user_snd = 'tutor_snd'
+            password = 'p'
+            fact.UserAccountFactory(username=user_snd, password=password)
+
+            login(self.browser, self.live_server_url, user_snd, password)
+            self._go_to_subscription(stage='validate')
+            self.write_comments_on_lines([(0, 'I disagree'), (1, 'Full points!')])
+            self.browser.find_element_by_id('score-full').click()
+            self.browser.find_element_by_id('submit-feedback').click()
+            WebDriverWait(self.browser, 5).until(ec.url_contains('subscription/ended'))
+            submission_for_code = Submission.objects.get(text=code)
+            self.assertEqual(self.sub_type.full_score, submission_for_code.feedback.score)
+            self.assertEqual(3, submission_for_code.feedback.feedback_lines.count())
+
+
+class TestFeedbackCreationTutor(UntestedParent.TestFeedbackCreationGeneric):
+    def setUp(self):
+        super().setUp()
+        self.username = 'tutor'
+        self.password = 'p'
+        self.role = UserAccount.TUTOR
+        fact.UserAccountFactory(
+            username=self.username,
+            password=self.password
+        )
diff --git a/functional_tests/test_front_pages.py b/functional_tests/test_front_pages.py
index adcc0975..d7a05296 100644
--- a/functional_tests/test_front_pages.py
+++ b/functional_tests/test_front_pages.py
@@ -4,7 +4,7 @@ from selenium.webdriver.support.ui import WebDriverWait
 
 from core import models
 from core.models import UserAccount
-from functional_tests.util import (login, create_browser,
+from functional_tests.util import (login, create_browser, subscriptions_loaded_cond,
                                    extract_hrefs_hashes, reset_browser_after_test)
 from util import factory_boys as fact
 
@@ -46,15 +46,6 @@ class UntestedParent:
             self.assertEqual('Statistics', title.text)
 
         def test_available_tasks_are_shown(self):
-            # A function that takes the element corresponding to the tasks
-            # component and return a function that can be used as a condition for
-            # WebDriverWait
-            def subscriptions_loaded_cond(tasks_el):
-                def loaded(*args):
-                    sub_links = tasks_el.find_elements_by_tag_name('a')
-                    return any((link != '/home' for link in extract_hrefs_hashes(sub_links)))
-                return loaded
-
             self._login()
             tasks = self.browser.find_element_by_name('subscription-list')
             title = tasks.find_element_by_class_name('v-toolbar__title')
diff --git a/functional_tests/util.py b/functional_tests/util.py
index 912d68a3..d6ca5434 100644
--- a/functional_tests/util.py
+++ b/functional_tests/util.py
@@ -15,6 +15,7 @@ def create_browser() -> webdriver.Firefox:
     options.headless = bool(os.environ.get('HEADLESS_TESTS', False))
     browser = webdriver.Firefox(options=options)
     browser.implicitly_wait(10)
+    browser.set_window_size(1920, 1080)
     return browser
 
 
@@ -44,3 +45,13 @@ def nth(iterable, n, default=None):
 def extract_hrefs_hashes(web_elements: Sequence[WebElement]):
     return [nth(el.get_attribute('href').split('#'), 1, '')
             for el in web_elements if el.get_attribute('href')]
+
+
+# A function that takes the element corresponding to the tasks
+# component and return a function that can be used as a condition for
+# WebDriverWait
+def subscriptions_loaded_cond(tasks_el):
+    def loaded(*args):
+        sub_links = tasks_el.find_elements_by_tag_name('a')
+        return any((link != '/home' for link in extract_hrefs_hashes(sub_links)))
+    return loaded
diff --git a/util/factory_boys.py b/util/factory_boys.py
index e89dd70a..12db198a 100644
--- a/util/factory_boys.py
+++ b/util/factory_boys.py
@@ -21,10 +21,11 @@ class ExamTypeFactory(DjangoModelFactory):
 class SubmissionTypeFactory(DjangoModelFactory):
     class Meta:
         model = models.SubmissionType
-    name = factory.Sequence(lambda n: f"[{n}] Example submission type ")
+    name = factory.Sequence(lambda n: f"[{n}] Example submission type")
     full_score = 15
-    description = '<h1>This</h1> is a <b>description</b> containing html'
-    solution = 'This usually contains commented code'
+    description = factory.Sequence(
+        lambda n: f'Type {n} \n<h1>This</h1> is a description containing html')
+    solution = factory.Sequence(lambda n: f'//This is a solution\n#include<stdio.h>\n\nint main() {{\n\tprintf("Hello World\\n");\n\treturn {n};\n}}')  # noqa
     programming_language = models.SubmissionType.C
 
 
@@ -53,20 +54,14 @@ class TestFactory(DjangoModelFactory):
 
     name = 'EmptyTest'
     label = 'Empty'
-    annotation = 'This is an annotation'
+    annotation = factory.Sequence(lambda n: f'Test: {n} This is an annotation')
 
 
 class SubmissionFactory(DjangoModelFactory):
     class Meta:
         model = models.Submission
 
-    text = """#include<stdio.h>
-
-int main() {
-    printf("Hello World\n");
-    return 0;
-}
-"""
+    text = factory.Sequence(lambda n: f'#include<stdio.h>\n\nint main() {{\n\tprintf("Hello World\\n");\n\treturn {n};\n}}')  # noqa
     type = factory.SubFactory(SubmissionTypeFactory)
     student = factory.SubFactory(StudentInfoFactory)
 
-- 
GitLab


From cb2b812b07db7269c160e52a4a57d404a7c421dc Mon Sep 17 00:00:00 2001
From: Dominik Seeger <dominik.seeger@gmx.net>
Date: Sun, 6 Jan 2019 20:38:17 +0100
Subject: [PATCH 17/21] re-added parseCSVMapMixin

---
 frontend/src/components/mixins/mixins.ts | 34 +++++++++++++-----------
 1 file changed, 19 insertions(+), 15 deletions(-)

diff --git a/frontend/src/components/mixins/mixins.ts b/frontend/src/components/mixins/mixins.ts
index bee6fc96..4b93f298 100644
--- a/frontend/src/components/mixins/mixins.ts
+++ b/frontend/src/components/mixins/mixins.ts
@@ -1,4 +1,4 @@
-import { Vue, Component } from 'vue-property-decorator'
+import { Vue, Component, Mixins } from 'vue-property-decorator'
 import { fetchStudentExportData, StudentExportItem, InstanceExportData, fetchInstanceExportData } from '@/api'
 import { getters } from '@/store/getters'
 import { mutations as mut } from '@/store/mutations'
@@ -9,21 +9,9 @@ export enum ExportType {
 }
 
 @Component
-export class exportMixin extends Vue {
-  exportDialog = true
-  mapFile: File | null = null
-  setPasswords = false
-  exportType = ExportType.CSV
-
-  get mapFileLoaded () {
-    return Object.keys(getters.state.studentMap).length > 0
-  }
-
-  get availableExportTypes (): ExportType[] {
-    return Object.values(ExportType)
-  }
-
+export class parseCSVMapMixin extends Vue {
   parseCSVMap (csvMap: string) {
+    console.log('parsing')
     let lines = csvMap.split('\n')
     lines.shift() // drop the first line since it contains only headings
     // TODO remove any type
@@ -35,6 +23,22 @@ export class exportMixin extends Vue {
       return acc
     }, {})
   }
+}
+
+@Component
+export class exportMixin extends Mixins(Vue, parseCSVMapMixin) {
+  exportDialog = true
+  mapFile: File | null = null
+  setPasswords = false
+  exportType = ExportType.CSV
+
+  get mapFileLoaded () {
+    return Object.keys(getters.state.studentMap).length > 0
+  }
+
+  get availableExportTypes (): ExportType[] {
+    return Object.values(ExportType)
+  }
 
   async getExportFile (type: string) {
     let studentData
-- 
GitLab


From 2c91e2937e8f39e67b2062adbcf0fbac341f4698 Mon Sep 17 00:00:00 2001
From: Dominik Seeger <dominik.seeger@gmx.net>
Date: Sun, 6 Jan 2019 20:38:50 +0100
Subject: [PATCH 18/21] fixed matrikelNr not getting applied correctly

---
 frontend/src/components/export/DataExport.vue | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/frontend/src/components/export/DataExport.vue b/frontend/src/components/export/DataExport.vue
index 8fd23f31..6b4d0dc5 100644
--- a/frontend/src/components/export/DataExport.vue
+++ b/frontend/src/components/export/DataExport.vue
@@ -70,14 +70,10 @@ export default class DataExport extends Mixins(exportMixin) {
   get studentMap () { return getters.state.studentMap }
 
   applyMapping (studentExport: StudentExportItem[]) {
-    console.log(studentExport)
     studentExport.forEach(student => {
       if (this.studentMap[student.Matrikel]) {
-        student = {
-          ...student,
-          Matrikel: this.studentMap[student.Matrikel].matrikelNo,
-          Name: this.studentMap[student.Matrikel].name
-        }
+        student.Name = this.studentMap[student.Matrikel].name
+        student.Matrikel = this.studentMap[student.Matrikel].matrikelNo
       } else {
         this.$notify({
           title: `Unknown student: ${student.Name}`,
-- 
GitLab


From 6539f0802e0e43cb1d05c9e1f4643312d3695398 Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Sun, 6 Jan 2019 20:04:56 +0100
Subject: [PATCH 19/21] fixes #129

---
 frontend/src/components/AutoLogout.vue       | 6 +++---
 frontend/src/store/modules/authentication.ts | 1 +
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/frontend/src/components/AutoLogout.vue b/frontend/src/components/AutoLogout.vue
index 44af1434..7572dd86 100644
--- a/frontend/src/components/AutoLogout.vue
+++ b/frontend/src/components/AutoLogout.vue
@@ -72,13 +72,13 @@ export default {
   },
   mounted () {
     this.timer = setInterval(() => {
-      const timeToLogOutDialog = Math.min(600 * 1e3,
+      const timeDialogAppearsBeforeLogout = Math.min(600 * 1e3,
         this.jwtTimeDelta ? this.jwtTimeDelta * 0.3 : Infinity)
-      if (this.$route.name !== 'login' && this.$store.getters.isLoggedIn) {
+      if (this.$route.name !== 'login' && Authentication.isLoggedIn) {
         if (Date.now() > this.lastTokenRefreshTry + this.jwtTimeDelta) {
           this.logoutDialog = false
           actions.logout("You've been logged out due to inactivity.")
-        } else if (Date.now() + timeToLogOutDialog > this.lastTokenRefreshTry + this.jwtTimeDelta) {
+        } else if (Date.now() + timeDialogAppearsBeforeLogout > this.lastTokenRefreshTry + this.jwtTimeDelta) {
           this.logoutDialog = true
         }
       }
diff --git a/frontend/src/store/modules/authentication.ts b/frontend/src/store/modules/authentication.ts
index f5aa875d..ac97806f 100644
--- a/frontend/src/store/modules/authentication.ts
+++ b/frontend/src/store/modules/authentication.ts
@@ -60,6 +60,7 @@ function SET_MESSAGE (state: AuthState, message: string) {
 }
 function SET_JWT_TOKEN (state: AuthState, token: string) {
   window.sessionStorage.setItem('token', token)
+  api.default.defaults.headers['Authorization'] = `JWT ${token}`
   state.token = token
 }
 function SET_JWT_TIME_DELTA (state: AuthState, timeDelta: number) {
-- 
GitLab


From 3bc6af9840648a6b531a548aee281ec4f46bffe0 Mon Sep 17 00:00:00 2001
From: Dominik Seeger <dominik.seeger@gmx.net>
Date: Sun, 6 Jan 2019 21:28:26 +0100
Subject: [PATCH 20/21] added tests for studentList deobfuscation logic

---
 .../tests/unit/components/DataExport.spec.ts  | 14 +++++
 .../unit/components/InstanceExport.spec.ts    | 61 +++++++++++++++++++
 frontend/tests/utils/testUtils.ts             | 23 +++++--
 3 files changed, 94 insertions(+), 4 deletions(-)
 create mode 100644 frontend/tests/unit/components/InstanceExport.spec.ts

diff --git a/frontend/tests/unit/components/DataExport.spec.ts b/frontend/tests/unit/components/DataExport.spec.ts
index f41c8bf8..617670fb 100644
--- a/frontend/tests/unit/components/DataExport.spec.ts
+++ b/frontend/tests/unit/components/DataExport.spec.ts
@@ -94,4 +94,18 @@ describe('DataExport Component Unit Tests', () => {
     await wrapper.vm.getExportFile('data')
     spy.calledWithExactly(testUtils.studentExports).should.equal(true)
   })
+  it('should correctly apply the student-map file', () => {
+    let wrapper = mount(DataExport, { localVue: localVue, store })
+    let mapStub = sinon.stub().returns(testUtils.studentMap)
+    let spy = sinon.spy()
+    // @ts-ignore
+    sinon.replaceGetter(wrapper.vm, 'studentMap', mapStub)
+    // @ts-ignore
+    sinon.replaceGetter(wrapper.vm, 'mapFileLoaded', sinon.stub().returns(true))
+    // @ts-ignore
+    sinon.replace(wrapper.vm, 'optionalConvertAndCreatePopup', spy)
+    // @ts-ignore
+    wrapper.vm.getMappedExportFile(testUtils.studentExportsObfuscated)
+    spy.firstCall.args[0][0].Matrikel.should.equal(testUtils.studentMap['username'].matrikelNo)
+  })
 })
diff --git a/frontend/tests/unit/components/InstanceExport.spec.ts b/frontend/tests/unit/components/InstanceExport.spec.ts
new file mode 100644
index 00000000..9f9a6de9
--- /dev/null
+++ b/frontend/tests/unit/components/InstanceExport.spec.ts
@@ -0,0 +1,61 @@
+import Vuex from 'vuex'
+import { mount, createLocalVue, createWrapper } from '@vue/test-utils'
+import InstanceExport from '@/components/export/InstanceExport.vue'
+import sinon from 'sinon'
+import chai from 'chai'
+import VueRouter from 'vue-router'
+import testUtils from '@/../tests/utils/testUtils'
+
+import * as api from '@/api'
+chai.should()
+
+let localVue = createLocalVue()
+localVue.use(Vuex)
+localVue.use(VueRouter)
+let router = new VueRouter()
+
+// @ts-ignore
+global.requestAnimationFrame = cb => cb()
+
+describe('InstanceExport Component Unit Tests', () => {
+  let store: any = null
+  let consoleTemp = {
+    warn: console.warn,
+    error: console.error
+  }
+
+  before(function () {
+    console.warn = function () {}
+    console.error = function () {}
+  })
+
+  after(function () {
+    console.warn = consoleTemp.warn
+    console.error = consoleTemp.error
+  })
+
+  beforeEach(() => {
+    store = new Vuex.Store({
+      state: {}
+    })
+  })
+
+  afterEach(() => {
+    sinon.restore()
+  })
+
+  it('should correctly apply the student-map file', () => {
+    let wrp = mount(InstanceExport, { localVue: localVue, store })
+    let mapStub = sinon.stub().returns(testUtils.studentMap)
+    let spy = sinon.spy()
+    // @ts-ignore
+    sinon.replaceGetter(wrp.vm, 'studentMap', mapStub)
+    // @ts-ignore
+    sinon.replaceGetter(wrp.vm, 'mapFileLoaded', sinon.stub().returns(true))
+    // @ts-ignore
+    sinon.replace(wrp.vm, 'optionalConvertAndCreatePopup', spy)
+    // @ts-ignore
+    wrp.vm.getMappedExportFile(testUtils.instanceExportsObfuscated)
+    spy.firstCall.args[0].students[0].matrikelNo.should.equal(testUtils.studentMap['username'].matrikelNo)
+  })
+})
\ No newline at end of file
diff --git a/frontend/tests/utils/testUtils.ts b/frontend/tests/utils/testUtils.ts
index 599cb750..7009a255 100644
--- a/frontend/tests/utils/testUtils.ts
+++ b/frontend/tests/utils/testUtils.ts
@@ -19,12 +19,27 @@ export default {
     Password: 'pwd',
     Scores: [{ type: 'test01', score: 100 }]
   }],
+  studentExportsObfuscated: <StudentExportItem[]> [{
+    Matrikel: 'username',
+    Name: 'name',
+    Username: 'username',
+    Sum: 100,
+    Exam: 'exam',
+    Password: 'pwd',
+    Scores: [{ type: 'test01', score: 100 }]
+  }],
   instanceExports: <InstanceExportData> {
     students: [{
-      name: 'test',
+      name: 'name',
       matrikelNo: '1000000'
     }]
   },
+  instanceExportsObfuscated: <InstanceExportData> {
+    students: [{
+      name: 'name',
+      matrikelNo: 'username'
+    }]
+  },
   fakeFile: <File> {
     lastModified: 1,
     name: 'Grady testUtils fake file',
@@ -33,8 +48,8 @@ export default {
     type: 'fake file'
   },
   studentMap: {
-    '1000000': {
-      matrikelNo: '1000000', name: 'test'
+    'username': {
+      matrikelNo: '1000000', name: 'name'
     }
-  }
+  },
 }
-- 
GitLab


From 7c7283f49a5246289295f40fe177b81d86650500 Mon Sep 17 00:00:00 2001
From: "robinwilliam.hundt" <robinwilliam.hundt@stud.uni-goettingen.de>
Date: Sun, 6 Jan 2019 21:45:57 +0100
Subject: [PATCH 21/21] Added AutoLogout test

---
 frontend/src/components/AutoLogout.vue   |  4 +-
 frontend/src/components/mixins/mixins.ts |  1 -
 functional_tests/test_auto_logout.py     | 67 ++++++++++++++++++++++++
 3 files changed, 69 insertions(+), 3 deletions(-)
 create mode 100644 functional_tests/test_auto_logout.py

diff --git a/frontend/src/components/AutoLogout.vue b/frontend/src/components/AutoLogout.vue
index 7572dd86..ae037ddf 100644
--- a/frontend/src/components/AutoLogout.vue
+++ b/frontend/src/components/AutoLogout.vue
@@ -4,7 +4,7 @@
     max-width="30%"
     v-model="logoutDialog"
   >
-    <v-card>
+    <v-card id="logout-dialog">
       <v-card-title class="headline">
         You'll be logged out!
       </v-card-title>
@@ -82,7 +82,7 @@ export default {
           this.logoutDialog = true
         }
       }
-    }, 5 * 1e3)
+    }, 1 * 1e3)
   },
   beforeDestroy () {
     clearInterval(this.timer)
diff --git a/frontend/src/components/mixins/mixins.ts b/frontend/src/components/mixins/mixins.ts
index 4b93f298..974e18df 100644
--- a/frontend/src/components/mixins/mixins.ts
+++ b/frontend/src/components/mixins/mixins.ts
@@ -11,7 +11,6 @@ export enum ExportType {
 @Component
 export class parseCSVMapMixin extends Vue {
   parseCSVMap (csvMap: string) {
-    console.log('parsing')
     let lines = csvMap.split('\n')
     lines.shift() // drop the first line since it contains only headings
     // TODO remove any type
diff --git a/functional_tests/test_auto_logout.py b/functional_tests/test_auto_logout.py
new file mode 100644
index 00000000..ecdfa41f
--- /dev/null
+++ b/functional_tests/test_auto_logout.py
@@ -0,0 +1,67 @@
+import datetime
+import time
+import logging
+from django.test import LiveServerTestCase
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as ec
+
+from core.models import UserAccount
+from functional_tests.util import (login, create_browser)
+from util import factory_boys as fact
+
+log = logging.getLogger(__name__)
+
+
+class TestAutoLogout(LiveServerTestCase):
+    browser: webdriver.Firefox = None
+    username = None
+    password = None
+    role = None
+
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        cls.browser = create_browser()
+
+    @classmethod
+    def tearDownClass(cls):
+        super().tearDownClass()
+        cls.browser.quit()
+
+    def setUp(self):
+        self.username = 'reviewer'
+        self.password = 'p'
+        self.role = UserAccount.TUTOR
+        fact.UserAccountFactory(
+            username=self.username,
+            password=self.password,
+            role=self.role
+        )
+
+    def _login(self):
+        login(self.browser, self.live_server_url, self.username, self.password)
+
+    def test_auto_logout_can_continue(self):
+        with self.settings(JWT_AUTH={
+            'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=5),
+            'JWT_ALLOW_REFRESH': True,
+        }
+        ):
+            self._login()
+            initial_token = self.browser.execute_script(
+                'return sessionStorage["token"]'
+            )
+            logout_dialog = self.browser.find_element_by_id('logout-dialog')
+            WebDriverWait(self.browser, 5).until(
+                ec.visibility_of_element_located((By.ID, 'logout-dialog')))
+            logout_dialog.find_element_by_id('continue-btn').click()
+            # explicit sleep is unfortunately necessary here, since we need to wait
+            # for the initial token to expire and the site to be the same
+            time.sleep(2)
+            self.assertNotIn('login', self.browser.current_url)
+            new_token = self.browser.execute_script(
+                'return sessionStorage["token"]'
+            )
+            self.assertNotEqual(initial_token, new_token)
-- 
GitLab