From 41a49f5f074955bfec453b316cd6a2a049839d58 Mon Sep 17 00:00:00 2001
From: Jeremy Bowman <jbowman@edx.org>
Date: Sat, 21 Jul 2018 11:40:11 -0400
Subject: [PATCH] TE-2635 Upgrade bok-choy

---
 common/test/acceptance/tests/helpers.py | 89 +++++++++++++++----------
 requirements/edx-sandbox/base.txt       |  2 +-
 requirements/edx-sandbox/shared.txt     |  2 +-
 requirements/edx/base.txt               | 12 ++--
 requirements/edx/development.txt        | 16 +++--
 requirements/edx/paver.txt              |  2 +-
 requirements/edx/testing.txt            | 14 ++--
 7 files changed, 79 insertions(+), 58 deletions(-)

diff --git a/common/test/acceptance/tests/helpers.py b/common/test/acceptance/tests/helpers.py
index f36f3fa1509..4e2513d776f 100644
--- a/common/test/acceptance/tests/helpers.py
+++ b/common/test/acceptance/tests/helpers.py
@@ -4,15 +4,16 @@ Test helper functions and base classes.
 
 import functools
 import inspect
+import io
 import json
 import operator
 import os
 import pprint
-import unittest
+import sys
 import urlparse
 from contextlib import contextmanager
 from datetime import datetime
-from unittest import TestCase
+from unittest import SkipTest, TestCase
 
 import requests
 from bok_choy.javascript import js_defined
@@ -23,7 +24,6 @@ from opaque_keys.edx.locator import CourseLocator
 from path import Path as path
 from pymongo import ASCENDING, MongoClient
 from selenium.common.exceptions import StaleElementReferenceException
-from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
 from selenium.webdriver.common.keys import Keys
 from selenium.webdriver.support import expected_conditions as EC
 from selenium.webdriver.support.select import Select
@@ -52,10 +52,13 @@ def skip_if_browser(browser):
 
     """
     def decorator(test_function):
+        """
+        The decorator to be applied to the test function.
+        """
         @functools.wraps(test_function)
         def wrapper(self, *args, **kwargs):
             if self.browser.name == browser:
-                raise unittest.SkipTest('Skipping as this test will not work with {}'.format(browser))
+                raise SkipTest('Skipping as this test will not work with {}'.format(browser))
             test_function(self, *args, **kwargs)
         return wrapper
     return decorator
@@ -107,7 +110,7 @@ def load_data_str(rel_path):
     Load a file from the "data" directory as a string.
     `rel_path` is the path relative to the data directory.
     """
-    full_path = path(__file__).abspath().dirname() / "data" / rel_path
+    full_path = path(__file__).abspath().dirname() / "data" / rel_path  # pylint: disable=no-value-for-parameter
     with open(full_path) as data_file:
         return data_file.read()
 
@@ -318,7 +321,7 @@ def element_has_text(page, css_selector, text):
     text_present = False
     text_list = page.q(css=css_selector).text
 
-    if len(text_list) > 0 and (text in text_list):
+    if text_list and (text in text_list):
         text_present = True
 
     return text_present
@@ -445,13 +448,13 @@ OPEN_BOOKS = {
 }
 
 
-def url_for_help(book_slug, path):
+def url_for_help(book_slug, path_component):
     """
     Create a full help URL given a book slug and a path component.
     """
     # Emulate the switch between books that happens in envs/bokchoy.py
     books = EDX_BOOKS if RELEASE_LINE == "master" else OPEN_BOOKS
-    url = 'http://edx.readthedocs.io/projects/{}/en/{}{}'.format(books[book_slug], doc_version(), path)
+    url = 'http://edx.readthedocs.io/projects/{}/en/{}{}'.format(books[book_slug], doc_version(), path_component)
     return url
 
 
@@ -741,37 +744,49 @@ class AcceptanceTest(WebAppTest):
         self.longMessage = True  # pylint: disable=invalid-name
 
     def tearDown(self):
-        try:
-            self.browser.get('http://{}:{}'.format(
-                os.environ.get('BOK_CHOY_HOSTNAME', '127.0.0.1'),
-                os.environ.get('BOK_CHOY_LMS_PORT', 8003),
-            ))
-        except:  # pylint: disable=bare-except
-            self.browser.get('http://{}:{}'.format(
-                os.environ.get('BOK_CHOY_HOSTNAME', '127.0.0.1'),
-                os.environ.get('BOK_CHOY_CMS_PORT', 8031),
-            ))
-        logs = self.browser.execute_script("return window.localStorage.getItem('console_log_capture');")
-        if not logs:
+        self._save_console_log()
+        super(AcceptanceTest, self).tearDown()
+
+    def _save_console_log(self):
+        """
+        Retrieve any JS errors caught by our error handler in the browser
+        and save them to a log file.  This is a workaround for Firefox not
+        supporting the Selenium log capture API yet; for details, see
+        https://github.com/mozilla/geckodriver/issues/284
+        """
+        browser_name = os.environ.get('SELENIUM_BROWSER', 'firefox')
+        if browser_name != 'firefox':
             return
-        logs = json.loads(logs)
-
-        log_dir = path('test_root') / 'log'
-        if 'shard' in os.environ:
-            log_dir /= "shard_{}".format(os.environ["SHARD"])
-        log_dir.mkdir_p()
-
-        with (log_dir / '{}.browser.log'.format(self.id()[:60])).open('w') as browser_log:
-            for (message, url, line_no, col_no, stack) in logs:
-                browser_log.write(u"{}:{}:{}: {}\n    {}\n".format(
-                    url,
-                    line_no,
-                    col_no,
-                    message,
-                    (stack or "").replace('\n', '\n    ')
-                ))
+        result = sys.exc_info()
+        exception_type = result[0]
 
-        super(AcceptanceTest, self).tearDown()
+        # Do not save for skipped tests.
+        if exception_type is SkipTest:
+            return
+
+        # If the test failed, save the browser console log.
+        # The exception info will either be an assertion error (on failure)
+        # or an actual exception (on error)
+        if result != (None, None, None):
+            logs = self.browser.execute_script("return window.localStorage.getItem('console_log_capture');")
+            if not logs:
+                return
+            logs = json.loads(logs)
+
+            log_dir = os.environ.get('SELENIUM_DRIVER_LOG_DIR')
+            if log_dir and not os.path.exists(log_dir):
+                os.makedirs(log_dir)
+
+            log_path = os.path.join(log_dir, '{}_browser.log'.format(self.id()))
+            with io.open(log_path, 'w') as browser_log:
+                for (message, url, line_no, col_no, stack) in logs:
+                    browser_log.write(u"{}:{}:{}: {}\n    {}\n".format(
+                        url,
+                        line_no,
+                        col_no,
+                        message,
+                        (stack or "").replace('\n', '\n    ')
+                    ))
 
 
 class UniqueCourseTest(AcceptanceTest):
diff --git a/requirements/edx-sandbox/base.txt b/requirements/edx-sandbox/base.txt
index bc878d0d089..3da81fa90de 100644
--- a/requirements/edx-sandbox/base.txt
+++ b/requirements/edx-sandbox/base.txt
@@ -11,7 +11,7 @@ common/lib/symmath
 asn1crypto==0.24.0
 backports-abc==0.5        # via tornado
 cffi==1.11.5
-cryptography==2.2.2
+cryptography==2.3
 enum34==1.1.6
 futures==3.2.0            # via tornado
 idna==2.7
diff --git a/requirements/edx-sandbox/shared.txt b/requirements/edx-sandbox/shared.txt
index 9c112a860a4..7640564156e 100644
--- a/requirements/edx-sandbox/shared.txt
+++ b/requirements/edx-sandbox/shared.txt
@@ -10,7 +10,7 @@
 -e common/lib/symmath
 asn1crypto==0.24.0        # via cryptography
 cffi==1.11.5              # via cryptography
-cryptography==2.2.2
+cryptography==2.3
 enum34==1.1.6             # via cryptography
 idna==2.7                 # via cryptography
 ipaddress==1.0.22         # via cryptography
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 0b4656aaaf7..278c4205a95 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -42,6 +42,7 @@ git+https://github.com/edx/xblock-utils.git@v1.1.1#egg=xblock-utils==1.1.1
 -e common/lib/xmodule
 amqp==1.4.9               # via kombu
 analytics-python==1.1.0
+aniso8601==3.0.2          # via tincan
 anyjson==0.3.3            # via kombu
 appdirs==1.4.3            # via fs
 argh==0.26.2
@@ -61,7 +62,7 @@ charade==1.0.3            # via pysrt
 click==6.7                # via user-util
 coreapi==2.3.3            # via django-rest-swagger, openapi-codec
 coreschema==0.0.4         # via coreapi
-cryptography==2.2.2
+cryptography==2.3
 cssutils==1.0.2           # via pynliner
 ddt==0.8.0
 decorator==4.3.0          # via dogapi, pycontracts
@@ -83,7 +84,7 @@ django-method-override==0.1.0
 django-model-utils==3.0.0
 django-mptt==0.8.7
 django-multi-email-field==0.5.1  # via edx-enterprise
-django-mysql==2.3.0
+django-mysql==2.3.1
 django-oauth-toolkit==0.12.0
 django-object-actions==0.10.0  # via edx-enterprise
 django-pyfs==2.0
@@ -93,7 +94,7 @@ django-require==1.0.11
 django-rest-swagger==2.2.0
 django-sekizai==0.10.0
 django-ses==0.8.4
-django-simple-history==2.2.0
+django-simple-history==2.3.0
 django-splash==0.2.2
 django-statici18n==1.4.0
 django-storages==1.4.1
@@ -177,7 +178,7 @@ openapi-codec==1.3.2      # via django-rest-swagger
 path.py==8.2.1
 pathtools==0.1.2
 paver==1.3.4
-pbr==4.1.0
+pbr==4.1.1
 pdfminer==20140328
 piexif==1.0.2
 pillow==3.4.0
@@ -211,7 +212,7 @@ requests-oauthlib==0.6.1
 requests==2.9.1
 rest-condition==1.0.3
 rfc6266-parser==0.0.5.post2
-rules==1.3
+rules==2.0
 s3transfer==0.1.13        # via boto3
 sailthru-client==2.2.3
 scipy==0.14.0
@@ -227,6 +228,7 @@ sorl-thumbnail==12.3
 sortedcontainers==0.9.2
 stevedore==1.10.0
 sympy==0.7.1
+tincan==0.0.5             # via edx-enterprise
 unicodecsv==0.14.1
 uritemplate==3.0.0        # via coreapi
 urllib3==1.23             # via elasticsearch
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index dddeb3090ed..6a9ab722c9b 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -46,6 +46,7 @@ git+https://github.com/edx/xblock-utils.git@v1.1.1#egg=xblock-utils==1.1.1
 alabaster==0.7.11         # via sphinx
 amqp==1.4.9
 analytics-python==1.1.0
+aniso8601==3.0.2
 anyjson==0.3.3
 apipkg==1.5
 appdirs==1.4.3
@@ -62,7 +63,7 @@ beautifulsoup==3.2.1
 before-after==1.0.1
 billiard==3.3.0.23
 bleach==1.4
-bok-choy==0.7.3
+bok-choy==0.8.0
 boto3==1.4.8
 boto==2.39.0
 botocore==1.8.17
@@ -77,7 +78,7 @@ constantly==15.1.0
 coreapi==2.3.3
 coreschema==0.0.4
 coverage==4.2
-cryptography==2.2.2
+cryptography==2.3
 cssselect==1.0.3
 cssutils==1.0.2
 ddt==0.8.0
@@ -103,7 +104,7 @@ django-method-override==0.1.0
 django-model-utils==3.0.0
 django-mptt==0.8.7
 django-multi-email-field==0.5.1
-django-mysql==2.3.0
+django-mysql==2.3.1
 django-oauth-toolkit==0.12.0
 django-object-actions==0.10.0
 django-pyfs==2.0
@@ -113,7 +114,7 @@ django-require==1.0.11
 django-rest-swagger==2.2.0
 django-sekizai==0.10.0
 django-ses==0.8.4
-django-simple-history==2.2.0
+django-simple-history==2.3.0
 django-splash==0.2.2
 django-statici18n==1.4.0
 django-storages==1.4.1
@@ -231,7 +232,7 @@ parsel==1.5.0
 path.py==8.2.1
 pathtools==0.1.2
 paver==1.3.4
-pbr==4.1.0
+pbr==4.1.1
 pdfminer==20140328
 piexif==1.0.2
 pillow==3.4.0
@@ -293,7 +294,7 @@ requests-oauthlib==0.6.1
 requests==2.9.1
 rest-condition==1.0.3
 rfc6266-parser==0.0.5.post2
-rules==1.3
+rules==2.0
 s3transfer==0.1.13
 sailthru-client==2.2.3
 scipy==0.14.0
@@ -313,7 +314,7 @@ social-auth-app-django==2.1.0
 social-auth-core==1.7.0
 sorl-thumbnail==12.3
 sortedcontainers==0.9.2
-sphinx==1.7.5
+sphinx==1.7.6
 sphinxcontrib-websupport==1.1.0  # via sphinx
 splinter==0.8.0
 sqlparse==0.2.4           # via django-debug-toolbar
@@ -323,6 +324,7 @@ sympy==0.7.1
 testfixtures==6.2.0
 testtools==2.3.0
 text-unidecode==1.2
+tincan==0.0.5
 tox-battery==0.5.1
 tox==3.1.2
 traceback2==1.4.0
diff --git a/requirements/edx/paver.txt b/requirements/edx/paver.txt
index ebaa7f91c5e..0aed1139d1c 100644
--- a/requirements/edx/paver.txt
+++ b/requirements/edx/paver.txt
@@ -14,7 +14,7 @@ mock==1.0.1
 path.py==8.2.1
 pathtools==0.1.2          # via watchdog
 paver==1.3.4
-pbr==4.1.0                # via stevedore
+pbr==4.1.1                # via stevedore
 psutil==1.2.1
 pymongo==2.9.1
 python-memcached==1.48
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index 8f6f5b82a2f..bca1f12750d 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -43,6 +43,7 @@ git+https://github.com/edx/xblock-utils.git@v1.1.1#egg=xblock-utils==1.1.1
 -e common/lib/xmodule
 amqp==1.4.9
 analytics-python==1.1.0
+aniso8601==3.0.2
 anyjson==0.3.3
 apipkg==1.5               # via execnet
 appdirs==1.4.3
@@ -59,7 +60,7 @@ beautifulsoup==3.2.1
 before-after==1.0.1
 billiard==3.3.0.23
 bleach==1.4
-bok-choy==0.7.3
+bok-choy==0.8.0
 boto3==1.4.8
 boto==2.39.0
 botocore==1.8.17
@@ -74,7 +75,7 @@ constantly==15.1.0        # via twisted
 coreapi==2.3.3
 coreschema==0.0.4
 coverage==4.2
-cryptography==2.2.2
+cryptography==2.3
 cssselect==1.0.3
 cssutils==1.0.2
 ddt==0.8.0
@@ -99,7 +100,7 @@ django-method-override==0.1.0
 django-model-utils==3.0.0
 django-mptt==0.8.7
 django-multi-email-field==0.5.1
-django-mysql==2.3.0
+django-mysql==2.3.1
 django-oauth-toolkit==0.12.0
 django-object-actions==0.10.0
 django-pyfs==2.0
@@ -109,7 +110,7 @@ django-require==1.0.11
 django-rest-swagger==2.2.0
 django-sekizai==0.10.0
 django-ses==0.8.4
-django-simple-history==2.2.0
+django-simple-history==2.3.0
 django-splash==0.2.2
 django-statici18n==1.4.0
 django-storages==1.4.1
@@ -222,7 +223,7 @@ parsel==1.5.0             # via scrapy
 path.py==8.2.1
 pathtools==0.1.2
 paver==1.3.4
-pbr==4.1.0
+pbr==4.1.1
 pdfminer==20140328
 piexif==1.0.2
 pillow==3.4.0
@@ -282,7 +283,7 @@ requests-oauthlib==0.6.1
 requests==2.9.1
 rest-condition==1.0.3
 rfc6266-parser==0.0.5.post2
-rules==1.3
+rules==2.0
 s3transfer==0.1.13
 sailthru-client==2.2.3
 scipy==0.14.0
@@ -307,6 +308,7 @@ sympy==0.7.1
 testfixtures==6.2.0
 testtools==2.3.0          # via fixtures, python-subunit
 text-unidecode==1.2       # via faker
+tincan==0.0.5
 tox-battery==0.5.1
 tox==3.1.2
 traceback2==1.4.0         # via testtools, unittest2
-- 
GitLab