diff --git a/common/test/acceptance/fixtures/__init__.py b/common/test/acceptance/fixtures/__init__.py index 12551034ab..c4b65ec27c 100644 --- a/common/test/acceptance/fixtures/__init__.py +++ b/common/test/acceptance/fixtures/__init__.py @@ -10,10 +10,10 @@ CMS_PORT = os.environ.get('BOK_CHOY_CMS_PORT', '8031') LMS_PORT = os.environ.get('BOK_CHOY_LMS_PORT', '8003') # Get the URL of the Studio instance under test -STUDIO_BASE_URL = os.environ.get('studio_url', 'http://{}:{}'.format(HOSTNAME, CMS_PORT)) +STUDIO_BASE_URL = os.environ.get('studio_url', f'http://{HOSTNAME}:{CMS_PORT}') # Get the URL of the LMS instance under test -LMS_BASE_URL = os.environ.get('lms_url', 'http://{}:{}'.format(HOSTNAME, LMS_PORT)) +LMS_BASE_URL = os.environ.get('lms_url', f'http://{HOSTNAME}:{LMS_PORT}') # Get the URL of the XQueue stub used in the test XQUEUE_STUB_URL = os.environ.get('xqueue_url', 'http://localhost:8040') @@ -22,10 +22,10 @@ XQUEUE_STUB_URL = os.environ.get('xqueue_url', 'http://localhost:8040') ORA_STUB_URL = os.environ.get('ora_url', 'http://localhost:8041') # Get the URL of the comments service stub used in the test -COMMENTS_STUB_URL = os.environ.get('comments_url', 'http://{}:4567'.format(HOSTNAME)) +COMMENTS_STUB_URL = os.environ.get('comments_url', f'http://{HOSTNAME}:4567') # Get the URL of the EdxNotes service stub used in the test -EDXNOTES_STUB_URL = os.environ.get('edxnotes_url', 'http://{}:8042'.format(HOSTNAME)) +EDXNOTES_STUB_URL = os.environ.get('edxnotes_url', f'http://{HOSTNAME}:8042') # Get the URL of the Catalog service stub used in the test CATALOG_STUB_URL = os.environ.get('catalog_url', 'http://localhost:8091') diff --git a/common/test/acceptance/fixtures/base.py b/common/test/acceptance/fixtures/base.py index 6b4a3b15f2..fefade7365 100644 --- a/common/test/acceptance/fixtures/base.py +++ b/common/test/acceptance/fixtures/base.py @@ -18,7 +18,7 @@ class StudioApiLoginError(Exception): pass # lint-amnesty, pylint: disable=unnecessary-pass -class StudioApiFixture(object): +class StudioApiFixture: """ Base class for fixtures that use the Studio restful API. """ @@ -42,12 +42,12 @@ class StudioApiFixture(object): self.user = response.json() if not self.user: - raise StudioApiLoginError(u'Auto-auth failed. Response was: {}'.format(self.user)) + raise StudioApiLoginError(f'Auto-auth failed. Response was: {self.user}') return session else: - msg = u'Could not log in to use Studio restful API. Status code: {0}'.format(response.status_code) + msg = f'Could not log in to use Studio restful API. Status code: {response.status_code}' raise StudioApiLoginError(msg) @lazy @@ -84,7 +84,7 @@ class XBlockContainerFixture(StudioApiFixture): def __init__(self): self.children = [] - super(XBlockContainerFixture, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + super().__init__() def add_children(self, *args): """ @@ -125,14 +125,14 @@ class XBlockContainerFixture(StudioApiFixture): ) if not response.ok: - msg = u"Could not create {0}. Status was {1}".format(xblock_desc, response.status_code) + msg = f"Could not create {xblock_desc}. Status was {response.status_code}" raise FixtureError(msg) try: loc = response.json().get('locator') xblock_desc.locator = loc except ValueError: - raise FixtureError(u"Could not decode JSON from '{0}'".format(response.content)) # lint-amnesty, pylint: disable=raise-missing-from + raise FixtureError(f"Could not decode JSON from '{response.content}'") # lint-amnesty, pylint: disable=raise-missing-from # Configure the XBlock response = self.session.post( @@ -144,7 +144,7 @@ class XBlockContainerFixture(StudioApiFixture): if response.ok: return loc else: - raise FixtureError(u"Could not update {0}. Status code: {1}".format(xblock_desc, response.status_code)) + raise FixtureError(f"Could not update {xblock_desc}. Status code: {response.status_code}") def _update_xblock(self, locator, data): """ @@ -152,13 +152,13 @@ class XBlockContainerFixture(StudioApiFixture): """ # Create the new XBlock response = self.session.put( - "{}/xblock/{}".format(STUDIO_BASE_URL, locator), + f"{STUDIO_BASE_URL}/xblock/{locator}", data=json.dumps(data), headers=self.headers, ) if not response.ok: - msg = u"Could not update {} with data {}. Status was {}".format(locator, data, response.status_code) + msg = f"Could not update {locator} with data {data}. Status was {response.status_code}" raise FixtureError(msg) def _encode_post_dict(self, post_dict): diff --git a/common/test/acceptance/fixtures/catalog.py b/common/test/acceptance/fixtures/catalog.py index 1b6ea42634..5cc3e2e649 100644 --- a/common/test/acceptance/fixtures/catalog.py +++ b/common/test/acceptance/fixtures/catalog.py @@ -11,7 +11,7 @@ from common.test.acceptance.fixtures import CATALOG_STUB_URL from common.test.acceptance.fixtures.config import ConfigModelFixture -class CatalogFixture(object): +class CatalogFixture: """ Interface to set up mock responses from the Catalog stub server. """ @@ -30,15 +30,15 @@ class CatalogFixture(object): uuid = program['uuid'] uuids.append(uuid) - program_key = '{base}.{uuid}'.format(base=key, uuid=uuid) + program_key = f'{key}.{uuid}' requests.put( - '{}/set_config'.format(CATALOG_STUB_URL), + f'{CATALOG_STUB_URL}/set_config', data={program_key: json.dumps(program)}, ) # Stub list endpoint as if the uuids_only query param had been passed. requests.put( - '{}/set_config'.format(CATALOG_STUB_URL), + f'{CATALOG_STUB_URL}/set_config', data={key: json.dumps(uuids)}, ) @@ -50,7 +50,7 @@ class CatalogFixture(object): pathways (list): A list of credit pathways. List endpoint will be stubbed using data from this list. """ requests.put( - '{}/set_config'.format(CATALOG_STUB_URL), + f'{CATALOG_STUB_URL}/set_config', data={'catalog.pathways': json.dumps({'results': pathways, 'next': None})} ) @@ -62,18 +62,18 @@ class CatalogFixture(object): program_types (list): A list of program types. List endpoint will be stubbed using data from this list. """ requests.put( - '{}/set_config'.format(CATALOG_STUB_URL), + f'{CATALOG_STUB_URL}/set_config', data={'catalog.programs_types': json.dumps(program_types)}, ) -class CatalogIntegrationMixin(object): +class CatalogIntegrationMixin: """Mixin providing a method used to configure the catalog integration.""" def set_catalog_integration(self, is_enabled=False, service_username=None): """Use this to change the catalog integration config model during tests.""" ConfigModelFixture('/config/catalog', { 'enabled': is_enabled, - 'internal_api_url': '{}/api/v1/'.format(CATALOG_STUB_URL), + 'internal_api_url': f'{CATALOG_STUB_URL}/api/v1/', 'cache_ttl': 0, 'service_username': service_username, }).install() diff --git a/common/test/acceptance/fixtures/certificates.py b/common/test/acceptance/fixtures/certificates.py index ff4ed291e5..df75f38722 100644 --- a/common/test/acceptance/fixtures/certificates.py +++ b/common/test/acceptance/fixtures/certificates.py @@ -32,21 +32,21 @@ class CertificateConfigFixture(StudioApiFixture): def __init__(self, course_id, certificates_data): self.course_id = course_id self.certificates = certificates_data - super(CertificateConfigFixture, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + super().__init__() def install(self): """ Push the certificates config data to certificate endpoint. """ response = self.session.post( - '{}/certificates/{}'.format(STUDIO_BASE_URL, self.course_id), + f'{STUDIO_BASE_URL}/certificates/{self.course_id}', data=json.dumps(self.certificates), headers=self.headers ) if not response.ok: raise CertificateConfigFixtureError( - u"Could not create certificate {0}. Status was {1}".format( + "Could not create certificate {}. Status was {}".format( json.dumps(self.certificates), response.status_code ) ) @@ -58,14 +58,14 @@ class CertificateConfigFixture(StudioApiFixture): Update the certificates config data to certificate endpoint. """ response = self.session.put( - '{}/certificates/{}/{}'.format(STUDIO_BASE_URL, self.course_id, certificate_id), + f'{STUDIO_BASE_URL}/certificates/{self.course_id}/{certificate_id}', data=json.dumps(self.certificates), headers=self.headers ) if not response.ok: raise CertificateConfigUpdateFixtureError( - u"Could not update certificate {0}. Status was {1}".format( + "Could not update certificate {}. Status was {}".format( json.dumps(self.certificates), response.status_code ) ) diff --git a/common/test/acceptance/fixtures/config.py b/common/test/acceptance/fixtures/config.py index 9ee545a98f..29596d6dd4 100644 --- a/common/test/acceptance/fixtures/config.py +++ b/common/test/acceptance/fixtures/config.py @@ -7,7 +7,6 @@ import json import re import requests -import six from lazy import lazy from common.test.acceptance.fixtures import LMS_BASE_URL, STUDIO_BASE_URL @@ -20,7 +19,7 @@ class ConfigModelFixtureError(Exception): pass # lint-amnesty, pylint: disable=unnecessary-pass -class ConfigModelFixture(object): +class ConfigModelFixture: """ Configure a ConfigurationModel by using it's JSON api. """ @@ -49,7 +48,7 @@ class ConfigModelFixture(object): if not response.ok: raise ConfigModelFixtureError( - u"Could not configure url '{}'. response: {} - {}".format( + "Could not configure url '{}'. response: {} - {}".format( self._api_base, response, response.content, @@ -90,7 +89,7 @@ class ConfigModelFixture(object): # auto_auth returns information about the newly created user # capture this so it can be used by by the testcases. user_pattern = re.compile( - six.text_type(r'Logged in user {0} \({1}\) with password {2} and user_id {3}').format( + r'Logged in user {} \({}\) with password {} and user_id {}'.format( r'(?P\S+)', r'(?P[^\)]+)', r'(?P\S+)', r'(?P\d+)')) user_matches = re.match(user_pattern, response.text) if user_matches: @@ -99,5 +98,5 @@ class ConfigModelFixture(object): return session else: - msg = u"Could not log in to use ConfigModel restful API. Status code: {0}".format(response.status_code) + msg = f"Could not log in to use ConfigModel restful API. Status code: {response.status_code}" raise ConfigModelFixtureError(msg) diff --git a/common/test/acceptance/fixtures/course.py b/common/test/acceptance/fixtures/course.py index edc44e4e45..23296c46f3 100644 --- a/common/test/acceptance/fixtures/course.py +++ b/common/test/acceptance/fixtures/course.py @@ -9,7 +9,6 @@ import mimetypes from collections import namedtuple from textwrap import dedent -import six from opaque_keys.edx.keys import CourseKey from path import Path @@ -17,7 +16,7 @@ from common.test.acceptance.fixtures import STUDIO_BASE_URL from common.test.acceptance.fixtures.base import FixtureError, XBlockContainerFixture -class XBlockFixtureDesc(object): +class XBlockFixtureDesc: """ Description of an XBlock, used to configure a course fixture. """ @@ -76,7 +75,7 @@ class XBlockFixtureDesc(object): Return a string representation of the description. Useful for error messages. """ - return dedent(u""" + return dedent(""" ".format(**self._course_dict) + return "".format(**self._course_dict) def add_course_details(self, course_details): """ @@ -236,14 +235,14 @@ class CourseFixture(XBlockContainerFixture): if not response.ok: raise FixtureError( - u"Could not retrieve course outline json. Status was {0}".format( + "Could not retrieve course outline json. Status was {}".format( response.status_code)) try: course_outline_json = response.json() except ValueError: raise FixtureError( # lint-amnesty, pylint: disable=raise-missing-from - u"Could not decode course outline as JSON: '{0}'".format(response) + f"Could not decode course outline as JSON: '{response}'" ) return course_outline_json @@ -257,7 +256,7 @@ class CourseFixture(XBlockContainerFixture): block_id = self._course_dict['run'] else: block_id = 'course' - return six.text_type(course_key.make_usage_key('course', block_id)) + return str(course_key.make_usage_key('course', block_id)) @property def _assets_url(self): @@ -272,7 +271,7 @@ class CourseFixture(XBlockContainerFixture): Return the locator string for the course handouts """ course_key = CourseKey.from_string(self._course_key) - return six.text_type(course_key.make_usage_key('course_info', 'handouts')) + return str(course_key.make_usage_key('course_info', 'handouts')) def _create_course(self): """ @@ -291,18 +290,18 @@ class CourseFixture(XBlockContainerFixture): except ValueError: raise FixtureError( # lint-amnesty, pylint: disable=raise-missing-from - u"Could not parse response from course request as JSON: '{0}'".format( + "Could not parse response from course request as JSON: '{}'".format( response.content)) # This will occur if the course identifier is not unique if err is not None: - raise FixtureError(u"Could not create course {0}. Error message: '{1}'".format(self, err)) + raise FixtureError(f"Could not create course {self}. Error message: '{err}'") if response.ok: self._course_key = response.json()['course_key'] else: raise FixtureError( - u"Could not create course {0}. Status was {1}\nResponse content was: {2}".format( + "Could not create course {}. Status was {}\nResponse content was: {}".format( self._course_dict, response.status_code, response.content)) def _configure_course(self): @@ -316,14 +315,14 @@ class CourseFixture(XBlockContainerFixture): if not response.ok: raise FixtureError( - u"Could not retrieve course details. Status was {0}".format( + "Could not retrieve course details. Status was {}".format( response.status_code)) try: details = response.json() except ValueError: raise FixtureError( # lint-amnesty, pylint: disable=raise-missing-from - u"Could not decode course details as JSON: '{0}'".format(details) + f"Could not decode course details as JSON: '{details}'" ) # Update the old details with our overrides @@ -337,7 +336,7 @@ class CourseFixture(XBlockContainerFixture): if not response.ok: raise FixtureError( - u"Could not update course details to '{0}' with {1}: Status was {2}.".format( + "Could not update course details to '{}' with {}: Status was {}.".format( self._course_details, url, response.status_code)) def _install_course_handouts(self): @@ -348,10 +347,10 @@ class CourseFixture(XBlockContainerFixture): # Construct HTML with each of the handout links handouts_li = [ - u'
  • Example Handout
  • '.format(handout=handout) + f'
  • Example Handout
  • ' for handout in self._handouts ] - handouts_html = u'
      {}
    '.format("".join(handouts_li)) + handouts_html = '
      {}
    '.format("".join(handouts_li)) # Update the course's handouts HTML payload = json.dumps({ @@ -365,7 +364,7 @@ class CourseFixture(XBlockContainerFixture): if not response.ok: raise FixtureError( - u"Could not update course handouts with {0}. Status was {1}".format(url, response.status_code)) + f"Could not update course handouts with {url}. Status was {response.status_code}") def _install_course_updates(self): """ @@ -382,7 +381,7 @@ class CourseFixture(XBlockContainerFixture): if not response.ok: raise FixtureError( - u"Could not add update to course: {0} with {1}. Status was {2}".format( + "Could not add update to course: {} with {}. Status was {}".format( update, url, response.status_code)) def _upload_assets(self): @@ -408,7 +407,7 @@ class CourseFixture(XBlockContainerFixture): upload_response = self.session.post(url, files=files, headers=headers) if not upload_response.ok: - raise FixtureError(u'Could not upload {asset_name} with {url}. Status code: {code}'.format( + raise FixtureError('Could not upload {asset_name} with {url}. Status code: {code}'.format( asset_name=asset_name, url=url, code=upload_response.status_code)) def _install_course_textbooks(self): @@ -423,7 +422,7 @@ class CourseFixture(XBlockContainerFixture): if not response.ok: raise FixtureError( - u"Could not add book to course: {0} with {1}. Status was {2}".format( + "Could not add book to course: {} with {}. Status was {}".format( book, url, response.status_code)) def _add_advanced_settings(self): @@ -440,12 +439,12 @@ class CourseFixture(XBlockContainerFixture): if not response.ok: raise FixtureError( - u"Could not update advanced details to '{0}' with {1}: Status was {2}.".format( + "Could not update advanced details to '{}' with {}: Status was {}.".format( self._advanced_settings, url, response.status_code)) def _create_xblock_children(self, parent_loc, xblock_descriptions): """ Recursively create XBlock children. """ - super(CourseFixture, self)._create_xblock_children(parent_loc, xblock_descriptions) # lint-amnesty, pylint: disable=super-with-arguments + super()._create_xblock_children(parent_loc, xblock_descriptions) self._publish_xblock(parent_loc) diff --git a/common/test/acceptance/fixtures/discussion.py b/common/test/acceptance/fixtures/discussion.py index 1e1bf99ce8..418626b407 100644 --- a/common/test/acceptance/fixtures/discussion.py +++ b/common/test/acceptance/fixtures/discussion.py @@ -14,7 +14,7 @@ from common.test.acceptance.fixtures.config import ConfigModelFixture class ContentFactory(factory.Factory): # lint-amnesty, pylint: disable=missing-class-docstring - class Meta(object): + class Meta: model = dict id = None @@ -68,7 +68,7 @@ class Response(Comment): class SearchResult(factory.Factory): # lint-amnesty, pylint: disable=missing-class-docstring - class Meta(object): + class Meta: model = dict discussion_data = [] @@ -78,14 +78,14 @@ class SearchResult(factory.Factory): # lint-amnesty, pylint: disable=missing-cl corrected_text = None -class DiscussionContentFixture(object): # lint-amnesty, pylint: disable=missing-class-docstring +class DiscussionContentFixture: # lint-amnesty, pylint: disable=missing-class-docstring def push(self): """ Push the data to the stub comments service. """ return requests.put( - '{}/set_config'.format(COMMENTS_STUB_URL), + f'{COMMENTS_STUB_URL}/set_config', data=self.get_config_data() ) @@ -175,7 +175,7 @@ class SearchResultFixture(DiscussionContentFixture): # lint-amnesty, pylint: di return {"search_result": json.dumps(self.result)} -class ForumsConfigMixin(object): +class ForumsConfigMixin: """Mixin providing a method used to configure the forums integration.""" def enable_forums(self, is_enabled=True): """Configures whether or not forums are enabled.""" diff --git a/common/test/acceptance/fixtures/library.py b/common/test/acceptance/fixtures/library.py index 0fd4a4ea64..c4e98684f5 100644 --- a/common/test/acceptance/fixtures/library.py +++ b/common/test/acceptance/fixtures/library.py @@ -1,9 +1,6 @@ """ Fixture to create a Content Library """ - - -import six from opaque_keys.edx.keys import CourseKey from common.test.acceptance.fixtures import STUDIO_BASE_URL @@ -22,7 +19,7 @@ class LibraryFixture(XBlockContainerFixture): """ Configure the library fixture to create a library with """ - super(LibraryFixture, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + super().__init__() self.library_info = { 'org': org, 'number': number, @@ -31,13 +28,13 @@ class LibraryFixture(XBlockContainerFixture): self.display_name = display_name self._library_key = None - super(LibraryFixture, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + super().__init__() def __str__(self): """ String representation of the library fixture, useful for debugging. """ - return u"".format(**self.library_info) + return "".format(**self.library_info) def install(self): """ @@ -64,7 +61,7 @@ class LibraryFixture(XBlockContainerFixture): Return the locator string for the LibraryRoot XBlock that is the root of the library hierarchy. """ lib_key = CourseKey.from_string(self._library_key) - return six.text_type(lib_key.make_usage_key('library', 'library')) + return str(lib_key.make_usage_key('library', 'library')) def _create_library(self): """ @@ -84,7 +81,7 @@ class LibraryFixture(XBlockContainerFixture): err_msg = response.json().get('ErrMsg') except ValueError: err_msg = "Unknown Error" - raise FixtureError(u"Could not create library {}. Status was {}, error was: {}".format( + raise FixtureError("Could not create library {}. Status was {}, error was: {}".format( self.library_info, response.status_code, err_msg )) @@ -92,4 +89,4 @@ class LibraryFixture(XBlockContainerFixture): # Disable publishing for library XBlocks: xblock_desc.publish = "not-applicable" - return super(LibraryFixture, self).create_xblock(parent_loc, xblock_desc) # lint-amnesty, pylint: disable=super-with-arguments + return super().create_xblock(parent_loc, xblock_desc) diff --git a/common/test/acceptance/fixtures/programs.py b/common/test/acceptance/fixtures/programs.py index 609991a3de..935cb49981 100644 --- a/common/test/acceptance/fixtures/programs.py +++ b/common/test/acceptance/fixtures/programs.py @@ -6,7 +6,7 @@ Tools to create programs-related data for use in bok choy tests. from common.test.acceptance.fixtures.config import ConfigModelFixture -class ProgramsConfigMixin(object): +class ProgramsConfigMixin: """Mixin providing a method used to configure the programs feature.""" def set_programs_api_configuration(self, is_enabled=False): """Dynamically adjusts the Programs config model during tests.""" diff --git a/common/test/acceptance/pages/common/__init__.py b/common/test/acceptance/pages/common/__init__.py index f6c2a2364b..a949ee2d31 100644 --- a/common/test/acceptance/pages/common/__init__.py +++ b/common/test/acceptance/pages/common/__init__.py @@ -10,7 +10,7 @@ CMS_PORT = os.environ.get('BOK_CHOY_CMS_PORT', 8031) LMS_PORT = os.environ.get('BOK_CHOY_LMS_PORT', 8003) # Get the URL of the instance under test -BASE_URL = os.environ.get('test_url', 'http://{}:{}'.format(HOSTNAME, LMS_PORT)) +BASE_URL = os.environ.get('test_url', f'http://{HOSTNAME}:{LMS_PORT}') # The URL used for user auth in testing -AUTH_BASE_URL = os.environ.get('test_url', 'http://{}:{}'.format(HOSTNAME, CMS_PORT)) +AUTH_BASE_URL = os.environ.get('test_url', f'http://{HOSTNAME}:{CMS_PORT}') diff --git a/common/test/acceptance/pages/common/auto_auth.py b/common/test/acceptance/pages/common/auto_auth.py index 8a7b11d9e8..60d06bfc19 100644 --- a/common/test/acceptance/pages/common/auto_auth.py +++ b/common/test/acceptance/pages/common/auto_auth.py @@ -5,14 +5,14 @@ Auto-auth page (used to automatically log in during testing). import json import os +from urllib import parse -from six.moves import urllib from bok_choy.page_object import PageObject, unguarded # The URL used for user auth in testing HOSTNAME = os.environ.get('BOK_CHOY_HOSTNAME', 'localhost') CMS_PORT = os.environ.get('BOK_CHOY_CMS_PORT', 8031) -AUTH_BASE_URL = os.environ.get('test_url', 'http://{}:{}'.format(HOSTNAME, CMS_PORT)) +AUTH_BASE_URL = os.environ.get('test_url', f'http://{HOSTNAME}:{CMS_PORT}') FULL_NAME = 'Test' @@ -45,7 +45,7 @@ class AutoAuthPage(PageObject): Note that "global staff" is NOT the same as course staff. """ - super(AutoAuthPage, self).__init__(browser) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser) # This will eventually hold the details about the user account self._user_info = None @@ -94,7 +94,7 @@ class AutoAuthPage(PageObject): Construct the URL. """ url = AUTH_BASE_URL + "/auto_auth" - query_str = urllib.parse.urlencode(self._params) + query_str = parse.urlencode(self._params) if query_str: url += "?" + query_str diff --git a/common/test/acceptance/pages/common/utils.py b/common/test/acceptance/pages/common/utils.py index 4bdea6fb98..4dc47f5fa5 100644 --- a/common/test/acceptance/pages/common/utils.py +++ b/common/test/acceptance/pages/common/utils.py @@ -1,10 +1,6 @@ """ Utility methods common to Studio and the LMS. """ - - -import six - from common.test.acceptance.tests.helpers import disable_animations @@ -21,7 +17,7 @@ def click_css(page, css, source_index=0): """Is the given element visible?""" # Only make the call to size once (instead of once for the height and once for the width) # because otherwise you will trigger a extra query on a remote element. - return element.is_displayed() and all(size > 0 for size in six.itervalues(element.size)) + return element.is_displayed() and all(size > 0 for size in element.size.values()) # Disable all animations for faster testing with more reliable synchronization disable_animations(page) diff --git a/common/test/acceptance/pages/lms/__init__.py b/common/test/acceptance/pages/lms/__init__.py index fbd187cea8..4b834fdd7c 100644 --- a/common/test/acceptance/pages/lms/__init__.py +++ b/common/test/acceptance/pages/lms/__init__.py @@ -8,4 +8,4 @@ import os # Get the URL of the instance under test HOSTNAME = os.environ.get('BOK_CHOY_HOSTNAME', 'localhost') LMS_PORT = os.environ.get('BOK_CHOY_LMS_PORT', 8003) -BASE_URL = os.environ.get('test_url', 'http://{}:{}'.format(HOSTNAME, LMS_PORT)) +BASE_URL = os.environ.get('test_url', f'http://{HOSTNAME}:{LMS_PORT}') diff --git a/common/test/acceptance/pages/lms/account_settings.py b/common/test/acceptance/pages/lms/account_settings.py index d3db20e1f9..3c1cb13130 100644 --- a/common/test/acceptance/pages/lms/account_settings.py +++ b/common/test/acceptance/pages/lms/account_settings.py @@ -63,7 +63,7 @@ class AccountSettingsPage(FieldsMixin, PageObject): """ Switch between the different account settings tabs. """ - self.q(css='#{}'.format(tab_id)).click() + self.q(css=f'#{tab_id}').click() @property def is_order_history_tab_visible(self): @@ -72,14 +72,14 @@ class AccountSettingsPage(FieldsMixin, PageObject): def get_value_of_order_history_row_item(self, field_id, field_name): """ Return the text value of the provided order field name.""" - query = self.q(css=u'.u-field-{} .u-field-order-{}'.format(field_id, field_name)) + query = self.q(css=f'.u-field-{field_id} .u-field-order-{field_name}') return query.text if query.present else None def order_button_is_visible(self, field_id): """ Check that if hovering over the order history row shows the order detail link or not. """ - return self.q(css=u'.u-field-{} .u-field-{}'.format(field_id, 'link')).visible + return self.q(css='.u-field-{} .u-field-{}'.format(field_id, 'link')).visible @property def is_delete_button_visible(self): diff --git a/common/test/acceptance/pages/lms/course_home.py b/common/test/acceptance/pages/lms/course_home.py index 20e024a877..eb2ec99720 100644 --- a/common/test/acceptance/pages/lms/course_home.py +++ b/common/test/acceptance/pages/lms/course_home.py @@ -20,7 +20,7 @@ class CourseHomePage(CoursePage): return self.q(css='.course-outline').present def __init__(self, browser, course_id): - super(CourseHomePage, self).__init__(browser, course_id) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser, course_id) self.course_id = course_id self.preview = StaffPreviewPage(browser, self) # TODO: TNL-6546: Remove the following diff --git a/common/test/acceptance/pages/lms/course_page.py b/common/test/acceptance/pages/lms/course_page.py index 1b0b9a8b0e..c2cbdde832 100644 --- a/common/test/acceptance/pages/lms/course_page.py +++ b/common/test/acceptance/pages/lms/course_page.py @@ -23,7 +23,7 @@ class CoursePage(PageObject): # lint-amnesty, pylint: disable=abstract-method Course ID is currently of the form "edx/999/2013_Spring" but this format could change. """ - super(CoursePage, self).__init__(browser) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser) self.course_id = course_id @property diff --git a/common/test/acceptance/pages/lms/course_wiki.py b/common/test/acceptance/pages/lms/course_wiki.py index af675ba224..b3ed78d3fe 100644 --- a/common/test/acceptance/pages/lms/course_wiki.py +++ b/common/test/acceptance/pages/lms/course_wiki.py @@ -57,7 +57,7 @@ class CourseWikiSubviewPage(CoursePage): # pylint: disable=abstract-method Course ID is currently of the form "edx/999/2013_Spring" but this format could change. """ - super(CourseWikiSubviewPage, self).__init__(browser, course_id) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser, course_id) self.course_id = course_id self.course_info = course_info self.article_name = "{org}.{course_number}.{course_run}".format( diff --git a/common/test/acceptance/pages/lms/courseware.py b/common/test/acceptance/pages/lms/courseware.py index 222d946b53..e9a2b82730 100644 --- a/common/test/acceptance/pages/lms/courseware.py +++ b/common/test/acceptance/pages/lms/courseware.py @@ -21,7 +21,7 @@ class CoursewarePage(CoursePage): subsection_selector = '.chapter-content-container a' def __init__(self, browser, course_id): # lint-amnesty, pylint: disable=useless-super-delegation - super(CoursewarePage, self).__init__(browser, course_id) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser, course_id) # self.nav = CourseNavPage(browser, self) def is_browser_on_page(self): @@ -45,7 +45,7 @@ class CoursewarePage(CoursePage): except IndexError: return False - sequential_position_css = u'#sequence-list #tab_{0}'.format(sequential_position - 1) + sequential_position_css = '#sequence-list #tab_{}'.format(sequential_position - 1) self.q(css=sequential_position_css).first.click() EmptyPromise(is_at_new_position, "Position navigation fulfilled").fulfill() @@ -74,6 +74,6 @@ class CoursewarePage(CoursePage): return False self.q( - css=u'.{} > .sequence-nav-button.{}'.format(top_or_bottom_class, next_or_previous_class) + css=f'.{top_or_bottom_class} > .sequence-nav-button.{next_or_previous_class}' ).first.click() EmptyPromise(is_at_new_tab_id, "Button navigation fulfilled").fulfill() diff --git a/common/test/acceptance/pages/lms/dashboard.py b/common/test/acceptance/pages/lms/dashboard.py index 1e298fdaac..69d907bc0c 100644 --- a/common/test/acceptance/pages/lms/dashboard.py +++ b/common/test/acceptance/pages/lms/dashboard.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Student dashboard page. """ @@ -14,7 +13,7 @@ class DashboardPage(PageObject): Student dashboard, where the student can view courses she/he has registered for. """ - url = "{base}/dashboard".format(base=BASE_URL) + url = f"{BASE_URL}/dashboard" def is_browser_on_page(self): return self.q(css='.my-courses').present diff --git a/common/test/acceptance/pages/lms/discussion.py b/common/test/acceptance/pages/lms/discussion.py index 9ba7b6a12b..b24c8ef8fe 100644 --- a/common/test/acceptance/pages/lms/discussion.py +++ b/common/test/acceptance/pages/lms/discussion.py @@ -18,7 +18,7 @@ class DiscussionThreadPage(PageObject): url = None def __init__(self, browser, thread_selector): - super(DiscussionThreadPage, self).__init__(browser) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser) self.thread_selector = thread_selector def _find_within(self, selector): @@ -76,10 +76,10 @@ class DiscussionThreadPage(PageObject): class DiscussionTabSingleThreadPage(CoursePage): # lint-amnesty, pylint: disable=missing-class-docstring def __init__(self, browser, course_id, discussion_id, thread_id): - super(DiscussionTabSingleThreadPage, self).__init__(browser, course_id) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser, course_id) self.thread_page = DiscussionThreadPage( browser, - u"body.discussion .discussion-article[data-id='{thread_id}']".format(thread_id=thread_id) + f"body.discussion .discussion-article[data-id='{thread_id}']" ) self.url_path = "discussion/forum/{discussion_id}/threads/{thread_id}".format( discussion_id=discussion_id, thread_id=thread_id @@ -103,7 +103,7 @@ class DiscussionTabHomePage(CoursePage): ALERT_SELECTOR = ".discussion-body .forum-nav .search-alert" def __init__(self, browser, course_id): - super(DiscussionTabHomePage, self).__init__(browser, course_id) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser, course_id) self.url_path = "discussion/forum/" self.root_selector = None diff --git a/common/test/acceptance/pages/lms/fields.py b/common/test/acceptance/pages/lms/fields.py index 9369f4b375..ea562c8303 100644 --- a/common/test/acceptance/pages/lms/fields.py +++ b/common/test/acceptance/pages/lms/fields.py @@ -1,16 +1,12 @@ -# -*- coding: utf-8 -*- """ Mixins for fields. """ - - -import six from bok_choy.promise import EmptyPromise from common.test.acceptance.tests.helpers import get_selected_option_text, select_option_by_text -class FieldsMixin(object): +class FieldsMixin: """ Methods for testing fields in pages. """ @@ -19,7 +15,7 @@ class FieldsMixin(object): """ Return field with field_id. """ - query = self.q(css=u'.u-field-{}'.format(field_id)) + query = self.q(css=f'.u-field-{field_id}') return query.text[0] if query.present else None def wait_for_field(self, field_id): @@ -28,7 +24,7 @@ class FieldsMixin(object): """ EmptyPromise( lambda: self.field(field_id) is not None, - u"Field with id \"{0}\" is in DOM.".format(field_id) + f"Field with id \"{field_id}\" is in DOM." ).fulfill() def mode_for_field(self, field_id): @@ -40,7 +36,7 @@ class FieldsMixin(object): """ self.wait_for_field(field_id) - query = self.q(css=u'.u-field-{}'.format(field_id)) + query = self.q(css=f'.u-field-{field_id}') if not query.present: return None @@ -62,7 +58,7 @@ class FieldsMixin(object): """ self.wait_for_field(field_id) - query = self.q(css=u'.u-field-{} .u-field-icon'.format(field_id)) + query = self.q(css=f'.u-field-{field_id} .u-field-icon') return query.present and icon_id in query.attrs('class')[0].split() def title_for_field(self, field_id): @@ -70,7 +66,7 @@ class FieldsMixin(object): Return the title of a field. """ self.wait_for_field(field_id) - query = self.q(css=six.u('.u-field-{} .u-field-title').format(field_id)) + query = self.q(css=f'.u-field-{field_id} .u-field-title') return query.text[0] if query.present else None def message_for_field(self, field_id): @@ -78,7 +74,7 @@ class FieldsMixin(object): Return the current message in a field. """ self.wait_for_field(field_id) - query = self.q(css=six.u('.u-field-{} .u-field-message'.format(field_id))) + query = self.q(css=f'.u-field-{field_id} .u-field-message') return query.text[0] if query.present else None def message_for_textarea_field(self, field_id): @@ -87,7 +83,7 @@ class FieldsMixin(object): """ self.wait_for_field(field_id) - query = self.q(css=u'.u-field-{} .u-field-message-help'.format(field_id)) + query = self.q(css=f'.u-field-{field_id} .u-field-message-help') return query.text[0] if query.present else None def wait_for_message(self, field_id, message): @@ -96,7 +92,7 @@ class FieldsMixin(object): """ EmptyPromise( lambda: message in (self.message_for_field(field_id) or ''), - u"Messsage \"{0}\" is visible.".format(message) + f"Messsage \"{message}\" is visible." ).fulfill() def indicator_for_field(self, field_id): @@ -105,7 +101,7 @@ class FieldsMixin(object): """ self.wait_for_field(field_id) - query = self.q(css=u'.u-field-{} .u-field-message .fa'.format(field_id)) + query = self.q(css=f'.u-field-{field_id} .u-field-message .fa') return [ class_name for class_name in query.attrs('class')[0].split(' ') @@ -118,14 +114,14 @@ class FieldsMixin(object): """ EmptyPromise( lambda: indicator == self.indicator_for_field(field_id), - u"Indicator \"{0}\" is visible.".format(self.indicator_for_field(field_id)) + "Indicator \"{}\" is visible.".format(self.indicator_for_field(field_id)) ).fulfill() def make_field_editable(self, field_id): """ Make a field editable. """ - query = self.q(css=u'.u-field-{}'.format(field_id)) + query = self.q(css=f'.u-field-{field_id}') if not query.present: return None @@ -138,7 +134,7 @@ class FieldsMixin(object): self.wait_for_element_visibility(bio_field_selector, 'Bio field is visible') self.browser.execute_script("$('" + bio_field_selector + "').click();") else: - self.q(css=u'.u-field-{}'.format(field_id)).first.click() + self.q(css=f'.u-field-{field_id}').first.click() def value_for_readonly_field(self, field_id): """ @@ -146,7 +142,7 @@ class FieldsMixin(object): """ self.wait_for_field(field_id) - query = self.q(css=u'.u-field-{} .u-field-value'.format(field_id)) + query = self.q(css=f'.u-field-{field_id} .u-field-value') if not query.present: return None @@ -157,16 +153,16 @@ class FieldsMixin(object): Get or set the value of a text field. """ self.wait_for_field(field_id) - query = self.q(css=six.u('.u-field-{} input'.format(field_id))) + query = self.q(css=f'.u-field-{field_id} input') if not query.present: return None if value is not None: current_value = query.attrs('value')[0] - query.results[0].send_keys(u'\ue003' * len(current_value)) # Delete existing value. + query.results[0].send_keys('\ue003' * len(current_value)) # Delete existing value. query.results[0].send_keys(value) # Input new value if press_enter: - query.results[0].send_keys(u'\ue007') # Press Enter + query.results[0].send_keys('\ue007') # Press Enter return query.attrs('value')[0] def set_value_for_textarea_field(self, field_id, value): @@ -176,12 +172,12 @@ class FieldsMixin(object): self.wait_for_field(field_id) self.make_field_editable(field_id) - field_selector = u'.u-field-{} textarea'.format(field_id) + field_selector = f'.u-field-{field_id} textarea' self.wait_for_element_presence(field_selector, 'Editable textarea is present.') query = self.q(css=field_selector) query.fill(value) - query.results[0].send_keys(u'\ue007') # Press Enter + query.results[0].send_keys('\ue007') # Press Enter def get_non_editable_mode_value(self, field_id): """ @@ -190,7 +186,7 @@ class FieldsMixin(object): self.wait_for_field(field_id) self.wait_for_ajax() - return self.q(css=u'.u-field-{} .u-field-value .u-field-value-readonly'.format(field_id)).text[0] + return self.q(css=f'.u-field-{field_id} .u-field-value .u-field-value-readonly').text[0] def value_for_dropdown_field(self, field_id, value=None, focus_out=False): """ @@ -200,7 +196,7 @@ class FieldsMixin(object): self.make_field_editable(field_id) - query = self.q(css=u'.u-field-{} select'.format(field_id)) + query = self.q(css=f'.u-field-{field_id} select') if not query.present: return None @@ -218,7 +214,7 @@ class FieldsMixin(object): """ self.wait_for_field(field_id) - query = self.q(css=u'.u-field-link-title-{}'.format(field_id)) + query = self.q(css=f'.u-field-link-title-{field_id}') return query.text[0] if query.present else None def wait_for_link_title_for_link_field(self, field_id, expected_title): @@ -227,7 +223,7 @@ class FieldsMixin(object): """ return EmptyPromise( lambda: self.link_title_for_link_field(field_id) == expected_title, - u"Link field with link title \"{0}\" is visible.".format(expected_title) + f"Link field with link title \"{expected_title}\" is visible." ).fulfill() def click_on_link_in_link_field(self, field_id, field_type='a'): @@ -236,7 +232,7 @@ class FieldsMixin(object): """ self.wait_for_field(field_id) - query = self.q(css=u'.u-field-{} {}'.format(field_id, field_type)) + query = self.q(css=f'.u-field-{field_id} {field_type}') if query.present: query.first.click() @@ -244,12 +240,12 @@ class FieldsMixin(object): """ Returns bool based on the highlighted border for field. """ - query = self.q(css=u'.u-field-{}.error'.format(field_id)) + query = self.q(css=f'.u-field-{field_id}.error') return True if query.present else False # lint-amnesty, pylint: disable=simplifiable-if-expression def get_social_first_element(self): """ Returns the title of first social media link. """ - query = self.q(css=six.u('.u-field-social_links > .field > .field-label')) + query = self.q(css='.u-field-social_links > .field > .field-label') return query[0].text diff --git a/common/test/acceptance/pages/lms/instructor_dashboard.py b/common/test/acceptance/pages/lms/instructor_dashboard.py index 8661d0c795..9eefc29ccd 100644 --- a/common/test/acceptance/pages/lms/instructor_dashboard.py +++ b/common/test/acceptance/pages/lms/instructor_dashboard.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Instructor (2) dashboard page. """ @@ -123,7 +122,7 @@ class CohortManagementSection(PageObject): cohorts_warning_title = '.message-warning .message-title' if self.q(css=cohorts_warning_title).visible: - return self.q(css='.message-title').text[0] == u'You currently have no cohorts configured' + return self.q(css='.message-title').text[0] == 'You currently have no cohorts configured' # The page may be in either the traditional management state, or an 'add new cohort' state. # Confirm the CSS class is visible because the CSS class can exist on the page even in different states. return self.q(css='.cohorts-state-section').visible or self.q(css='.new-cohort-form').visible @@ -132,7 +131,7 @@ class CohortManagementSection(PageObject): """ Return `selector`, but limited to the cohort management context. """ - return u'.cohort-management {}'.format(selector) + return f'.cohort-management {selector}' def _get_cohort_options(self): """ @@ -585,8 +584,8 @@ class MembershipPageAutoEnrollSection(PageObject): MembershipPageAutoEnrollSection.NOTIFICATION_ERROR Returns True if a {section_type} notification is displayed. """ - notification_selector = u'.auto_enroll_csv .results .message-%s' % section_type - self.wait_for_element_presence(notification_selector, u"%s Notification" % section_type.title()) + notification_selector = '.auto_enroll_csv .results .message-%s' % section_type + self.wait_for_element_presence(notification_selector, "%s Notification" % section_type.title()) return self.q(css=notification_selector).is_present() def first_notification_message(self, section_type): @@ -595,8 +594,8 @@ class MembershipPageAutoEnrollSection(PageObject): MembershipPageAutoEnrollSection.NOTIFICATION_ERROR Returns the first message from the list of messages in the {section_type} section. """ - error_message_selector = u'.auto_enroll_csv .results .message-%s li.summary-item' % section_type - self.wait_for_element_presence(error_message_selector, u"%s message" % section_type.title()) + error_message_selector = '.auto_enroll_csv .results .message-%s li.summary-item' % section_type + self.wait_for_element_presence(error_message_selector, "%s message" % section_type.title()) return self.q(css=error_message_selector).text[0] def upload_correct_csv_file(self): @@ -629,8 +628,8 @@ class MembershipPageAutoEnrollSection(PageObject): """ Fill in the form with the provided email and submit it. """ - email_selector = u"{} textarea".format(self.batch_enrollment_selector) - enrollment_button = u"{} .enrollment-button[data-action='enroll']".format(self.batch_enrollment_selector) + email_selector = f"{self.batch_enrollment_selector} textarea" + enrollment_button = f"{self.batch_enrollment_selector} .enrollment-button[data-action='enroll']" # Fill the email addresses after the email selector is visible. self.wait_for_element_visibility(email_selector, 'Email field is visible') @@ -646,9 +645,9 @@ class MembershipPageAutoEnrollSection(PageObject): """ Check notification div is visible and have message. """ - notification_selector = u'{} .request-response'.format(self.batch_enrollment_selector) + notification_selector = f'{self.batch_enrollment_selector} .request-response' self.wait_for_element_visibility(notification_selector, 'Notification div is visible') - return self.q(css=u"{} h3".format(notification_selector)).text + return self.q(css=f"{notification_selector} h3").text class CertificatesPage(PageObject): diff --git a/common/test/acceptance/pages/lms/learner_profile.py b/common/test/acceptance/pages/lms/learner_profile.py index cc0de5759f..acd194eebf 100644 --- a/common/test/acceptance/pages/lms/learner_profile.py +++ b/common/test/acceptance/pages/lms/learner_profile.py @@ -13,7 +13,7 @@ from common.test.acceptance.pages.lms.fields import FieldsMixin from common.test.acceptance.pages.lms.instructor_dashboard import InstructorDashboardPage from common.test.acceptance.tests.helpers import select_option_by_value -PROFILE_VISIBILITY_SELECTOR = u'#u-field-select-account_privacy option[value="{}"]' +PROFILE_VISIBILITY_SELECTOR = '#u-field-select-account_privacy option[value="{}"]' PROFILE_VISIBILITY_INPUT = '#u-field-select-account_privacy' @@ -25,7 +25,7 @@ class Badge(PageObject): def __init__(self, element, browser): self.element = element - super(Badge, self).__init__(browser) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser) def is_browser_on_page(self): return BrowserQuery(self.element, css=".badge-details").visible @@ -55,8 +55,8 @@ class Badge(PageObject): """ Execute javascript to bring the popup(.badges-model) inside the window. """ - script_to_execute = (u"var popup = document.querySelectorAll('.badges-modal')[0];;" - u"popup.style.left = '20%';") + script_to_execute = ("var popup = document.querySelectorAll('.badges-modal')[0];;" + "popup.style.left = '20%';") self.browser.execute_script(script_to_execute) def close_modal(self): @@ -84,7 +84,7 @@ class LearnerProfilePage(FieldsMixin, PageObject): browser (Browser): The browser instance. username (str): Profile username. """ - super(LearnerProfilePage, self).__init__(browser) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser) self.username = username @property @@ -147,7 +147,7 @@ class LearnerProfilePage(FieldsMixin, PageObject): if privacy != self.privacy: query = self.q(css=PROFILE_VISIBILITY_INPUT) select_option_by_value(query, privacy) - EmptyPromise(lambda: privacy == self.privacy, u'Privacy is set to {}'.format(privacy)).fulfill() + EmptyPromise(lambda: privacy == self.privacy, f'Privacy is set to {privacy}').fulfill() self.q(css='.btn-change-privacy').first.click() self.wait_for_ajax() @@ -165,7 +165,7 @@ class LearnerProfilePage(FieldsMixin, PageObject): True/False """ self.wait_for_ajax() - return self.q(css='.u-field-{}'.format(field_id)).visible + return self.q(css=f'.u-field-{field_id}').visible def field_is_editable(self, field_id): """ diff --git a/common/test/acceptance/pages/lms/programs.py b/common/test/acceptance/pages/lms/programs.py index 4e91e19f80..2832c45ab1 100644 --- a/common/test/acceptance/pages/lms/programs.py +++ b/common/test/acceptance/pages/lms/programs.py @@ -29,7 +29,7 @@ class ProgramListingPage(PageObject): class ProgramDetailsPage(PageObject): """Program details page.""" program_uuid = str(uuid4()) - url = '{base}/dashboard/programs/{program_uuid}/'.format(base=BASE_URL, program_uuid=program_uuid) + url = f'{BASE_URL}/dashboard/programs/{program_uuid}/' def is_browser_on_page(self): return self.q(css='.js-program-details-wrapper').present diff --git a/common/test/acceptance/pages/lms/staff_view.py b/common/test/acceptance/pages/lms/staff_view.py index c9b89b3a54..7b4b23ee16 100644 --- a/common/test/acceptance/pages/lms/staff_view.py +++ b/common/test/acceptance/pages/lms/staff_view.py @@ -28,7 +28,7 @@ class StaffPreviewPage(PageObject): parent_page: None if this is being used as a subclass. Otherwise, the parent_page the contains this staff preview page fragment. """ - super(StaffPreviewPage, self).__init__(browser) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser) self.parent_page = parent_page def is_browser_on_page(self): diff --git a/common/test/acceptance/pages/lms/tab_nav.py b/common/test/acceptance/pages/lms/tab_nav.py index 7fcf7ecd91..54c4e92c1e 100644 --- a/common/test/acceptance/pages/lms/tab_nav.py +++ b/common/test/acceptance/pages/lms/tab_nav.py @@ -30,7 +30,7 @@ class TabNavPage(PageObject): """ if tab_name not in ['Course', 'Home', 'Discussion', 'Wiki', 'Progress']: - self.warning(u"'{0}' is not a valid tab name".format(tab_name)) + self.warning(f"'{tab_name}' is not a valid tab name") # The only identifier for individual tabs is the link href # so we find the tab with `tab_name` in its text. @@ -39,7 +39,7 @@ class TabNavPage(PageObject): if tab_css is not None: self.q(css=tab_css).first.click() else: - self.warning(u"No tabs found for '{0}'".format(tab_name)) + self.warning(f"No tabs found for '{tab_name}'") self.wait_for_page() self._is_on_tab_promise(tab_name).fulfill() @@ -57,9 +57,9 @@ class TabNavPage(PageObject): return None else: if self.is_using_boostrap_style_tabs(): - return u'ul.navbar-nav li:nth-of-type({0}) a'.format(tab_index + 1) + return 'ul.navbar-nav li:nth-of-type({}) a'.format(tab_index + 1) else: - return u'ol.course-tabs li:nth-of-type({0}) a'.format(tab_index + 1) + return 'ol.course-tabs li:nth-of-type({}) a'.format(tab_index + 1) @property def tab_names(self): @@ -105,5 +105,5 @@ class TabNavPage(PageObject): # Use the private version of _is_on_tab to skip the page check return EmptyPromise( lambda: self._is_on_tab(tab_name), - u"{0} is the current tab".format(tab_name) + f"{tab_name} is the current tab" ) diff --git a/common/test/acceptance/pages/lms/video/video.py b/common/test/acceptance/pages/lms/video/video.py index 91a234d0c2..5813107db2 100644 --- a/common/test/acceptance/pages/lms/video/video.py +++ b/common/test/acceptance/pages/lms/video/video.py @@ -31,7 +31,7 @@ CSS_CLASS_NAMES = { 'captions_rendered': '.video.is-captions-rendered', 'captions': '.subtitles', 'captions_text': '.subtitles li span', - 'captions_text_getter': u'.subtitles li span[role="link"][data-index="{}"]', + 'captions_text_getter': '.subtitles li span[role="link"][data-index="{}"]', 'closed_captions': '.closed-captions', 'error_message': '.video .video-player .video-error', 'video_container': '.video', @@ -76,7 +76,7 @@ class VideoPage(PageObject): @wait_for_js def is_browser_on_page(self): - return self.q(css='div{0}'.format(CSS_CLASS_NAMES['video_xmodule'])).present + return self.q(css='div{}'.format(CSS_CLASS_NAMES['video_xmodule'])).present @wait_for_js def wait_for_video_class(self): @@ -86,7 +86,7 @@ class VideoPage(PageObject): """ self.wait_for_ajax() - video_selector = '{0}'.format(CSS_CLASS_NAMES['video_container']) + video_selector = '{}'.format(CSS_CLASS_NAMES['video_container']) self.wait_for_element_presence(video_selector, 'Video is initialized') @wait_for_js @@ -106,7 +106,7 @@ class VideoPage(PageObject): video_player_buttons.append('play') for button in video_player_buttons: - self.wait_for_element_visibility(VIDEO_BUTTONS[button], u'{} button is visible'.format(button)) + self.wait_for_element_visibility(VIDEO_BUTTONS[button], f'{button} button is visible') def _is_finished_loading(): """ @@ -136,7 +136,7 @@ class VideoPage(PageObject): if video_display_name: video_display_names = self.q(css=CSS_CLASS_NAMES['video_display_name']).text if video_display_name not in video_display_names: - raise ValueError(u"Incorrect Video Display Name: '{0}'".format(video_display_name)) + raise ValueError(f"Incorrect Video Display Name: '{video_display_name}'") return '.vert.vert-{}'.format(video_display_names.index(video_display_name)) else: return '.vert.vert-0' @@ -154,7 +154,7 @@ class VideoPage(PageObject): """ if vertical: - return u'{vertical} {video_element}'.format( + return '{vertical} {video_element}'.format( vertical=self.get_video_vertical_selector(self.current_video_display_name), video_element=class_name) else: @@ -224,4 +224,4 @@ class VideoPage(PageObject): # Verify that captions state is toggled/changed EmptyPromise(lambda: self.is_captions_visible() == captions_new_state, - u"Transcripts are {state}".format(state=state)).fulfill() + f"Transcripts are {state}").fulfill() diff --git a/common/test/acceptance/pages/studio/__init__.py b/common/test/acceptance/pages/studio/__init__.py index 211196bfe2..13026028d1 100644 --- a/common/test/acceptance/pages/studio/__init__.py +++ b/common/test/acceptance/pages/studio/__init__.py @@ -9,5 +9,5 @@ import os HOSTNAME = os.environ.get('BOK_CHOY_HOSTNAME', 'localhost') CMS_PORT = os.environ.get('BOK_CHOY_CMS_PORT', 8031) LMS_PORT = os.environ.get('BOK_CHOY_LMS_PORT', 8003) -BASE_URL = os.environ.get('test_url', 'http://{}:{}'.format(HOSTNAME, CMS_PORT)) -LMS_URL = os.environ.get('test_url', 'http://{}:{}'.format(HOSTNAME, LMS_PORT)) +BASE_URL = os.environ.get('test_url', f'http://{HOSTNAME}:{CMS_PORT}') +LMS_URL = os.environ.get('test_url', f'http://{HOSTNAME}:{LMS_PORT}') diff --git a/common/test/acceptance/pages/studio/container.py b/common/test/acceptance/pages/studio/container.py index 82c3510da3..2fd548eb17 100644 --- a/common/test/acceptance/pages/studio/container.py +++ b/common/test/acceptance/pages/studio/container.py @@ -22,13 +22,13 @@ class ContainerPage(PageObject, HelpMixin): ADD_MISSING_GROUPS_SELECTOR = '.notification-action-button[data-notification-action="add-missing-groups"]' def __init__(self, browser, locator): - super(ContainerPage, self).__init__(browser) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser) self.locator = locator @property def url(self): """URL to the container page for an xblock.""" - return u"{}/container/{}".format(BASE_URL, self.locator) + return f"{BASE_URL}/container/{self.locator}" @property def name(self): # lint-amnesty, pylint: disable=missing-function-docstring @@ -40,7 +40,7 @@ class ContainerPage(PageObject, HelpMixin): def is_browser_on_page(self): def _xblock_count(class_name, request_token): - return len(self.q(css=u'{body_selector} .xblock.{class_name}[data-request-token="{request_token}"]'.format( + return len(self.q(css='{body_selector} .xblock.{class_name}[data-request-token="{request_token}"]'.format( body_selector=XBlockWrapper.BODY_SELECTOR, class_name=class_name, request_token=request_token )).results) @@ -51,7 +51,7 @@ class ContainerPage(PageObject, HelpMixin): if len(data_request_elements) > 0: request_token = data_request_elements.first.attrs('data-request-token')[0] # Then find the number of Studio xblock wrappers on the page with that request token. - num_wrappers = len(self.q(css=u'{} [data-request-token="{}"]'.format(XBlockWrapper.BODY_SELECTOR, request_token)).results) # lint-amnesty, pylint: disable=line-too-long + num_wrappers = len(self.q(css=f'{XBlockWrapper.BODY_SELECTOR} [data-request-token="{request_token}"]').results) # lint-amnesty, pylint: disable=line-too-long # Wait until all components have been loaded and marked as either initialized or failed. # See: # - common/static/js/xblock/core.js which adds the class "xblock-initialized" @@ -324,7 +324,7 @@ class ContainerPage(PageObject, HelpMixin): text = self.q(css='#page-alert .alert.confirmation #alert-confirmation-title').text return text and message not in text[0] if verify_hidden else text and message in text[0] - self.wait_for(_verify_message, description=u'confirmation message {status}'.format( + self.wait_for(_verify_message, description='confirmation message {status}'.format( status='hidden' if verify_hidden else 'present' )) @@ -389,8 +389,8 @@ class ContainerPage(PageObject, HelpMixin): Returns: list """ - self.q(css='.add-xblock-component-button[data-type={}]'.format(category_type)).first.click() - return self.q(css='.{}-type-tabs>li>a'.format(category_type)).text + self.q(css=f'.add-xblock-component-button[data-type={category_type}]').first.click() + return self.q(css=f'.{category_type}-type-tabs>li>a').text def get_category_tab_components(self, category_type, tab_index): """ @@ -403,7 +403,7 @@ class ContainerPage(PageObject, HelpMixin): Returns: list """ - css = u'#tab{tab_index} button[data-category={category_type}] span'.format( + css = '#tab{tab_index} button[data-category={category_type}] span'.format( tab_index=tab_index, category_type=category_type ) @@ -433,17 +433,17 @@ class XBlockWrapper(PageObject): } def __init__(self, browser, locator): - super(XBlockWrapper, self).__init__(browser) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser) self.locator = locator def is_browser_on_page(self): - return self.q(css='{}[data-locator="{}"]'.format(self.BODY_SELECTOR, self.locator)).present + return self.q(css=f'{self.BODY_SELECTOR}[data-locator="{self.locator}"]').present def _bounded_selector(self, selector): """ Return `selector`, but limited to this particular `CourseOutlineChild` context """ - return u'{}[data-locator="{}"] {}'.format( + return '{}[data-locator="{}"] {}'.format( self.BODY_SELECTOR, self.locator, selector @@ -495,7 +495,7 @@ class XBlockWrapper(PageObject): def _validation_paragraph(self, css_class): """ Helper method to return the

    element of a validation warning """ - return self.q(css=self._bounded_selector(u'{} p.{}'.format(self.VALIDATION_SELECTOR, css_class))) + return self.q(css=self._bounded_selector(f'{self.VALIDATION_SELECTOR} p.{css_class}')) @property def has_validation_warning(self): @@ -524,7 +524,7 @@ class XBlockWrapper(PageObject): @property def validation_error_messages(self): - return self.q(css=self._bounded_selector('{} .xblock-message-item.error'.format(self.VALIDATION_SELECTOR))).text + return self.q(css=self._bounded_selector(f'{self.VALIDATION_SELECTOR} .xblock-message-item.error')).text @property def validation_not_configured_warning_text(self): @@ -627,7 +627,7 @@ class XBlockWrapper(PageObject): """ If editing, set the value of a field. """ - selector = u'{} li.field label:contains("{}") + input'.format(self.editor_selector, field_display_name) + selector = f'{self.editor_selector} li.field label:contains("{field_display_name}") + input' script = "$(arguments[0]).val(arguments[1]).change();" self.browser.execute_script(script, selector, field_value) @@ -635,7 +635,7 @@ class XBlockWrapper(PageObject): """ If editing, reset the value of a field to its default. """ - scope = u'{} li.field label:contains("{}")'.format(self.editor_selector, field_display_name) + scope = f'{self.editor_selector} li.field label:contains("{field_display_name}")' script = "$(arguments[0]).siblings('.setting-clear').click();" self.browser.execute_script(script, scope) @@ -643,18 +643,18 @@ class XBlockWrapper(PageObject): """ Set the text of a CodeMirror editor that is part of this xblock's settings. """ - type_in_codemirror(self, index, text, find_prefix=u'$("{}").find'.format(self.editor_selector)) + type_in_codemirror(self, index, text, find_prefix=f'$("{self.editor_selector}").find') def set_license(self, license_type): """ Uses the UI to set the course's license to the given license_type (str) """ css_selector = ( - u"ul.license-types li[data-license={license_type}] button" + "ul.license-types li[data-license={license_type}] button" ).format(license_type=license_type) self.wait_for_element_presence( css_selector, - u"{license_type} button is present".format(license_type=license_type) + f"{license_type} button is present" ) self.q(css=css_selector).click() @@ -666,7 +666,7 @@ class XBlockWrapper(PageObject): @property def editor_selector(self): - return u'.xblock-studio_view' + return '.xblock-studio_view' def _click_button(self, button_name): """ diff --git a/common/test/acceptance/pages/studio/course_page.py b/common/test/acceptance/pages/studio/course_page.py index 0a23be3dc0..d504f26bbe 100644 --- a/common/test/acceptance/pages/studio/course_page.py +++ b/common/test/acceptance/pages/studio/course_page.py @@ -6,7 +6,6 @@ Base class for pages specific to a course in Studio. import os from abc import abstractmethod -import six from bok_choy.page_object import PageObject from opaque_keys.edx.locator import CourseLocator @@ -39,7 +38,7 @@ class CoursePage(PageObject, HelpMixin): These identifiers will likely change in the future. """ - super(CoursePage, self).__init__(browser) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser) self.course_info = { 'course_org': course_org, 'course_num': course_num, @@ -59,4 +58,4 @@ class CoursePage(PageObject, HelpMixin): self.course_info['course_run'], deprecated=(default_store == 'draft') ) - return "/".join([BASE_URL, self.url_path, six.text_type(course_key)]) + return "/".join([BASE_URL, self.url_path, str(course_key)]) diff --git a/common/test/acceptance/pages/studio/library.py b/common/test/acceptance/pages/studio/library.py index c78b2da8bf..fd168fb104 100644 --- a/common/test/acceptance/pages/studio/library.py +++ b/common/test/acceptance/pages/studio/library.py @@ -2,9 +2,6 @@ Library edit page in Studio """ - -import six - from bok_choy.page_object import PageObject from common.test.acceptance.pages.studio import BASE_URL @@ -18,7 +15,7 @@ class LibraryPage(PageObject, HelpMixin): Base page for Library pages. Defaults URL to the edit page. """ def __init__(self, browser, locator): - super(LibraryPage, self).__init__(browser) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser) self.locator = locator @property @@ -26,7 +23,7 @@ class LibraryPage(PageObject, HelpMixin): """ URL to the library edit page for the given library. """ - return "{}/library/{}".format(BASE_URL, six.text_type(self.locator)) + return "{}/library/{}".format(BASE_URL, str(self.locator)) def is_browser_on_page(self): """ @@ -50,4 +47,4 @@ class LibraryEditPage(LibraryPage, PaginatedMixin, UsersPageMixin): for improved test reliability. """ self.wait_for_ajax() - super(LibraryEditPage, self).wait_until_ready() # lint-amnesty, pylint: disable=super-with-arguments + super().wait_until_ready() diff --git a/common/test/acceptance/pages/studio/overview.py b/common/test/acceptance/pages/studio/overview.py index bc6d7b5ff0..2c80e2f687 100644 --- a/common/test/acceptance/pages/studio/overview.py +++ b/common/test/acceptance/pages/studio/overview.py @@ -13,7 +13,7 @@ from common.test.acceptance.pages.studio.course_page import CoursePage @js_defined('jQuery') -class CourseOutlineItem(object): +class CourseOutlineItem: """ A mixin class for any :class:`PageObject` shown in a course outline. """ @@ -33,9 +33,9 @@ class CourseOutlineItem(object): # Check for the existence of a locator so that errors when navigating to the course outline page don't show up # as errors in the repr method instead. try: - return u"{}(, {!r})".format(self.__class__.__name__, self.locator) + return f"{self.__class__.__name__}(, {self.locator!r})" except AttributeError: - return u"{}()".format(self.__class__.__name__) + return f"{self.__class__.__name__}()" def _bounded_selector(self, selector): """ @@ -45,7 +45,7 @@ class CourseOutlineItem(object): # This happens in the context of the CourseOutlinePage # pylint: disable=no-member if self.BODY_SELECTOR and hasattr(self, 'locator'): - return u'{}[data-locator="{}"] {}'.format( + return '{}[data-locator="{}"] {}'.format( self.BODY_SELECTOR, self.locator, selector @@ -105,17 +105,17 @@ class CourseOutlineChild(PageObject, CourseOutlineItem): BODY_SELECTOR = '.outline-item' def __init__(self, browser, locator): - super(CourseOutlineChild, self).__init__(browser) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(browser) self.locator = locator def is_browser_on_page(self): - return self.q(css='{}[data-locator="{}"]'.format(self.BODY_SELECTOR, self.locator)).present + return self.q(css=f'{self.BODY_SELECTOR}[data-locator="{self.locator}"]').present def _bounded_selector(self, selector): """ Return `selector`, but limited to this particular `CourseOutlineChild` context """ - return u'{}[data-locator="{}"] {}'.format( + return '{}[data-locator="{}"] {}'.format( self.BODY_SELECTOR, self.locator, selector @@ -216,7 +216,7 @@ class CourseOutlineSection(CourseOutlineContainer, CourseOutlineChild): self.add_child() # lint-amnesty, pylint: disable=no-member -class ExpandCollapseLinkState(object): +class ExpandCollapseLinkState: """ Represents the three states that the expand/collapse link can be in """ @@ -271,7 +271,7 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer): self.wait_for_element_visibility('#is_prereq', 'Gating settings fields are present.') -class CourseOutlineModal(object): +class CourseOutlineModal: """ Page object specifically for a modal window on the course outline page. diff --git a/common/test/acceptance/pages/studio/pagination.py b/common/test/acceptance/pages/studio/pagination.py index a7bdc7ddf0..e4a66a6832 100644 --- a/common/test/acceptance/pages/studio/pagination.py +++ b/common/test/acceptance/pages/studio/pagination.py @@ -6,7 +6,7 @@ Mixin to include for Paginated container pages from selenium.webdriver.common.keys import Keys -class PaginatedMixin(object): +class PaginatedMixin: """ Mixin class used for paginated page tests. """ @@ -18,20 +18,20 @@ class PaginatedMixin(object): To specify a specific arrow, pass an iterable with a single element, 'next' or 'previous'. """ - return all(self.q(css=u'nav.%s * .%s-page-link.is-disabled' % (position, arrow)) for arrow in arrows) + return all(self.q(css=f'nav.{position} * .{arrow}-page-link.is-disabled') for arrow in arrows) def move_back(self, position): """ Clicks one of the forward nav buttons. Position can be 'top' or 'bottom'. """ - self.q(css=u'nav.%s * .previous-page-link' % position)[0].click() + self.q(css='nav.%s * .previous-page-link' % position)[0].click() self.wait_until_ready() def move_forward(self, position): """ Clicks one of the forward nav buttons. Position can be 'top' or 'bottom'. """ - self.q(css=u'nav.%s * .next-page-link' % position)[0].click() + self.q(css='nav.%s * .next-page-link' % position)[0].click() self.wait_until_ready() def go_to_page(self, number): diff --git a/common/test/acceptance/pages/studio/settings.py b/common/test/acceptance/pages/studio/settings.py index c5594a6fcf..4b29de9ebb 100644 --- a/common/test/acceptance/pages/studio/settings.py +++ b/common/test/acceptance/pages/studio/settings.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Course Schedule and Details Settings page. """ diff --git a/common/test/acceptance/pages/studio/utils.py b/common/test/acceptance/pages/studio/utils.py index 714a6d7e28..5ed94f6d2a 100644 --- a/common/test/acceptance/pages/studio/utils.py +++ b/common/test/acceptance/pages/studio/utils.py @@ -16,7 +16,7 @@ SIDE_BAR_HELP_CSS = '.external-help a, .external-help-button' @js_defined('window.jQuery') def type_in_codemirror(page, index, text, find_prefix="$"): # lint-amnesty, pylint: disable=missing-function-docstring - script = u""" + script = """ var cm = {find_prefix}('div.CodeMirror:eq({index})').get(0).CodeMirror; CodeMirror.signal(cm, "focus", cm); cm.setValue(arguments[0]); @@ -28,7 +28,7 @@ def type_in_codemirror(page, index, text, find_prefix="$"): # lint-amnesty, pyl @js_defined('window.jQuery') def get_codemirror_value(page, index=0, find_prefix="$"): return page.browser.execute_script( - u"return {find_prefix}('div.CodeMirror:eq({index})').get(0).CodeMirror.getValue();".format( + "return {find_prefix}('div.CodeMirror:eq({index})').get(0).CodeMirror.getValue();".format( index=index, find_prefix=find_prefix ) ) @@ -80,7 +80,7 @@ def verify_ordering(test_class, page, expected_orderings): # pylint: disable=un assert len(blocks_checked) == len(xblocks) -class HelpMixin(object): +class HelpMixin: """ Mixin for testing Help links. """ diff --git a/common/test/acceptance/pages/studio/video/video.py b/common/test/acceptance/pages/studio/video/video.py index 6b81382db3..2cfd69ffac 100644 --- a/common/test/acceptance/pages/studio/video/video.py +++ b/common/test/acceptance/pages/studio/video/video.py @@ -12,7 +12,6 @@ from bok_choy.javascript import js_defined, wait_for_js from bok_choy.promise import EmptyPromise, Promise from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys -from six.moves import range from common.test.acceptance.pages.common.utils import sync_on_notification # lint-amnesty, pylint: disable=no-name-in-module from common.test.acceptance.pages.lms.video.video import VideoPage @@ -108,12 +107,12 @@ class VideoComponentPage(VideoPage): @wait_for_js def is_browser_on_page(self): return ( - self.q(css='div{0}'.format(CLASS_SELECTORS['video_xmodule'])).present or - self.q(css='div{0}'.format(CLASS_SELECTORS['xblock'])).present + self.q(css='div{}'.format(CLASS_SELECTORS['video_xmodule'])).present or + self.q(css='div{}'.format(CLASS_SELECTORS['xblock'])).present ) def get_element_selector(self, class_name, vertical=False): - return super(VideoComponentPage, self).get_element_selector(class_name, vertical=vertical) # lint-amnesty, pylint: disable=super-with-arguments + return super().get_element_selector(class_name, vertical=vertical) def _wait_for(self, check_func, desc, result=False, timeout=30): """ @@ -271,7 +270,7 @@ class VideoComponentPage(VideoPage): Download handout at `url` """ kwargs = dict() - session_id = [{i['name']: i['value']} for i in self.browser.get_cookies() if i['name'] == u'sessionid'] + session_id = [{i['name']: i['value']} for i in self.browser.get_cookies() if i['name'] == 'sessionid'] if session_id: kwargs.update({ 'cookies': session_id[0] @@ -332,7 +331,7 @@ class VideoComponentPage(VideoPage): line_number (int): caption line number """ - caption_line_selector = u".subtitles li span[data-index='{index}']".format(index=line_number - 1) + caption_line_selector = ".subtitles li span[data-index='{index}']".format(index=line_number - 1) self.q(css=caption_line_selector).results[0].send_keys(Keys.ENTER) def is_caption_line_focused(self, line_number): @@ -343,7 +342,7 @@ class VideoComponentPage(VideoPage): line_number (int): caption line number """ - caption_line_selector = u".subtitles li span[data-index='{index}']".format(index=line_number - 1) + caption_line_selector = ".subtitles li span[data-index='{index}']".format(index=line_number - 1) caption_container = self.q(css=caption_line_selector).results[0].find_element_by_xpath('..') return 'focused' in caption_container.get_attribute('class').split() @@ -441,9 +440,9 @@ class VideoComponentPage(VideoPage): field_id = self.q(css=query).nth(index).attrs('for')[0] break - self.q(css='#{}'.format(field_id)).fill(field_value) + self.q(css=f'#{field_id}').fill(field_value) elif field_type == 'select': - self.q(css=u'select[name="{0}"] option[value="{1}"]'.format(field_name, field_value)).first.click() + self.q(css=f'select[name="{field_name}"] option[value="{field_value}"]').first.click() def verify_field_value(self, field_name, field_value): """ @@ -491,7 +490,7 @@ class VideoComponentPage(VideoPage): """ translations_items = '.wrapper-translations-settings .list-settings-item' - language_selector = translations_items + u' select option[value="{}"]'.format(language_code) + language_selector = translations_items + f' select option[value="{language_code}"]' self.q(css=language_selector).nth(index).click() def upload_translation(self, transcript_name, language_code): @@ -547,7 +546,7 @@ class VideoComponentPage(VideoPage): """ mime_type = 'application/x-subrip' - lang_code = '?language_code={}'.format(language_code) + lang_code = f'?language_code={language_code}' link = [link for link in self.q(css='.download-action').attrs('href') if lang_code in link] result, headers, content = self._get_transcript(link[0]) # lint-amnesty, pylint: disable=no-member @@ -579,7 +578,7 @@ class VideoComponentPage(VideoPage): As all the captions lines are exactly same so only getting partial lines will work. """ self.wait_for_captions() # lint-amnesty, pylint: disable=no-member - selector = u'.subtitles li:nth-child({})' + selector = '.subtitles li:nth-child({})' return ' '.join([self.q(css=selector.format(i)).text[0] for i in range(1, 6)]) def set_url_field(self, url, field_number): @@ -611,7 +610,7 @@ class VideoComponentPage(VideoPage): """ if message_type == 'status': self.wait_for_element_visibility(CLASS_SELECTORS[message_type], - u'{} message is Visible'.format(message_type.title())) + f'{message_type.title()} message is Visible') return self.q(css=CLASS_SELECTORS[message_type]).text[0] @@ -657,7 +656,7 @@ class VideoComponentPage(VideoPage): """ Clear video url fields. """ - script = u""" + script = """ $('{selector}') .prop('disabled', false) .removeClass('is-disabled') diff --git a/common/test/acceptance/tests/discussion/helpers.py b/common/test/acceptance/tests/discussion/helpers.py index fd50fde288..3c166b529c 100644 --- a/common/test/acceptance/tests/discussion/helpers.py +++ b/common/test/acceptance/tests/discussion/helpers.py @@ -6,7 +6,6 @@ Helper functions and classes for discussion tests. import json from uuid import uuid4 -from six.moves import range from common.test.acceptance.fixtures import LMS_BASE_URL from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc @@ -19,7 +18,7 @@ from common.test.acceptance.pages.lms.discussion import DiscussionTabSingleThrea from common.test.acceptance.tests.helpers import UniqueCourseTest -class BaseDiscussionMixin(object): +class BaseDiscussionMixin: """ A mixin containing methods common to discussion tests. """ @@ -31,7 +30,7 @@ class BaseDiscussionMixin(object): self.thread_ids = [] threads = [] for i in range(thread_count): - thread_id = "test_thread_{}_{}".format(i, uuid4().hex) + thread_id = f"test_thread_{i}_{uuid4().hex}" thread_body = "Dummy long text body." * 50 threads.append( Thread(id=thread_id, commentable_id=self.discussion_id, body=thread_body, **thread_kwargs), @@ -42,7 +41,7 @@ class BaseDiscussionMixin(object): assert response.ok, 'Failed to push discussion content' -class CohortTestMixin(object): +class CohortTestMixin: """ Mixin for tests of cohorted courses """ @@ -53,7 +52,7 @@ class CohortTestMixin(object): """ course_fixture._update_xblock(course_fixture._course_location, { # lint-amnesty, pylint: disable=protected-access "metadata": { - u"cohort_config": { + "cohort_config": { "auto_cohort_groups": auto_cohort_groups or [], "cohorted_discussions": [], "cohorted": True, @@ -75,7 +74,7 @@ class CohortTestMixin(object): """ Adds a user to the specified cohort. """ - url = LMS_BASE_URL + "/courses/" + course_fixture._course_key + "/cohorts/{}/add".format(cohort_id) # lint-amnesty, pylint: disable=protected-access + url = LMS_BASE_URL + "/courses/" + course_fixture._course_key + f"/cohorts/{cohort_id}/add" # lint-amnesty, pylint: disable=protected-access data = {"users": username} course_fixture.headers['Content-type'] = 'application/x-www-form-urlencoded' response = course_fixture.session.post(url, data=data, headers=course_fixture.headers) @@ -85,9 +84,9 @@ class CohortTestMixin(object): class BaseDiscussionTestCase(UniqueCourseTest, ForumsConfigMixin): """Base test case class for all discussions-related tests.""" def setUp(self): - super(BaseDiscussionTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() - self.discussion_id = "test_discussion_{}".format(uuid4().hex) + self.discussion_id = f"test_discussion_{uuid4().hex}" self.course_fixture = CourseFixture(**self.course_info) self.course_fixture.add_children( XBlockFixtureDesc("chapter", "Test Section").add_children( diff --git a/common/test/acceptance/tests/discussion/test_cohort_management.py b/common/test/acceptance/tests/discussion/test_cohort_management.py index 8c408acaed..9efc20490e 100644 --- a/common/test/acceptance/tests/discussion/test_cohort_management.py +++ b/common/test/acceptance/tests/discussion/test_cohort_management.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ End-to-end tests related to the cohort management on the LMS Instructor Dashboard """ @@ -24,7 +23,7 @@ class CohortConfigurationTest(EventsTestMixin, UniqueCourseTest, CohortTestMixin """ Set up a cohorted course """ - super(CohortConfigurationTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() # create course with cohorts self.manual_cohort_name = "ManualCohort1" diff --git a/common/test/acceptance/tests/discussion/test_discussion.py b/common/test/acceptance/tests/discussion/test_discussion.py index a0cbd83e49..b5e4675e56 100644 --- a/common/test/acceptance/tests/discussion/test_discussion.py +++ b/common/test/acceptance/tests/discussion/test_discussion.py @@ -25,7 +25,7 @@ from common.test.acceptance.tests.discussion.helpers import BaseDiscussionMixin, from common.test.acceptance.tests.helpers import UniqueCourseTest from openedx.core.lib.tests import attr -THREAD_CONTENT_WITH_LATEX = u"""Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt # lint-amnesty, pylint: disable=line-too-long +THREAD_CONTENT_WITH_LATEX = """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt # lint-amnesty, pylint: disable=line-too-long ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit sse cillum dolore eu fugiat nulla pariatur. @@ -96,7 +96,7 @@ class DiscussionHomePageTest(BaseDiscussionTestCase): SEARCHED_USERNAME = "gizmo" def setUp(self): - super(DiscussionHomePageTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() AutoAuthPage(self.browser, course_id=self.course_id).visit() self.page = DiscussionTabHomePage(self.browser, self.course_id) self.page.visit() @@ -119,7 +119,7 @@ class DiscussionTabMultipleThreadTest(BaseDiscussionTestCase, BaseDiscussionMixi Tests for the discussion page with multiple threads """ def setUp(self): - super(DiscussionTabMultipleThreadTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() AutoAuthPage(self.browser, course_id=self.course_id).visit() self.thread_count = 2 self.thread_ids = [] @@ -169,9 +169,9 @@ class DiscussionOpenClosedThreadTest(BaseDiscussionTestCase): """ def setUp(self): - super(DiscussionOpenClosedThreadTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() - self.thread_id = "test_thread_{}".format(uuid4().hex) + self.thread_id = f"test_thread_{uuid4().hex}" def setup_user(self, roles=[]): # lint-amnesty, pylint: disable=dangerous-default-value roles_str = ','.join(roles) @@ -323,7 +323,7 @@ class DiscussionSearchAlertTest(UniqueCourseTest): SEARCHED_USERNAME = "gizmo" def setUp(self): - super(DiscussionSearchAlertTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() CourseFixture(**self.course_info).install() # first auto auth call sets up a user that we will search for in some tests self.searched_user_id = AutoAuthPage( diff --git a/common/test/acceptance/tests/helpers.py b/common/test/acceptance/tests/helpers.py index f3f4c8cff7..1e9af5375f 100644 --- a/common/test/acceptance/tests/helpers.py +++ b/common/test/acceptance/tests/helpers.py @@ -4,7 +4,6 @@ Test helper functions and base classes. import functools -import io import json import os import sys @@ -12,7 +11,6 @@ from datetime import datetime from unittest import SkipTest, TestCase import requests -import six from bok_choy.javascript import js_defined from bok_choy.page_object import XSS_INJECTION from bok_choy.promise import EmptyPromise, Promise @@ -24,7 +22,6 @@ from selenium.common.exceptions import StaleElementReferenceException from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.select import Select from selenium.webdriver.support.ui import WebDriverWait -from six.moves import range # lint-amnesty, pylint: disable=unused-import from capa.tests.response_xml_factory import MultipleChoiceResponseXMLFactory from common.test.acceptance.fixtures.course import XBlockFixtureDesc @@ -52,7 +49,7 @@ def skip_if_browser(browser): @functools.wraps(test_function) def wrapper(self, *args, **kwargs): if self.browser.name == browser: - raise SkipTest(u'Skipping as this test will not work with {}'.format(browser)) + raise SkipTest(f'Skipping as this test will not work with {browser}') test_function(self, *args, **kwargs) return wrapper return decorator @@ -80,7 +77,7 @@ def is_youtube_available(): 'transcript': 'http://video.google.com/timedtext?lang=en&v=3_yD_cEKoCk', } - for url in six.itervalues(youtube_api_urls): + for url in youtube_api_urls.values(): try: response = requests.get(url, allow_redirects=False) except requests.exceptions.ConnectionError: @@ -96,7 +93,7 @@ def is_focused_on_element(browser, selector): """ Check if the focus is on the element that matches the selector. """ - return browser.execute_script(u"return $('{}').is(':focus')".format(selector)) + return browser.execute_script(f"return $('{selector}').is(':focus')") def load_data_str(rel_path): @@ -153,7 +150,7 @@ def disable_css_animations(page): """ Disable CSS3 animations, transitions, transforms. """ - page.browser.execute_script(u""" + page.browser.execute_script(""" var id = 'no-transitions'; // if styles were already added, just do nothing. @@ -230,7 +227,7 @@ def select_option_by_text(select_browser_query, option_text, focus_out=False): except StaleElementReferenceException: return False - msg = u'Selected option {}'.format(option_text) + msg = f'Selected option {option_text}' EmptyPromise(lambda: select_option(select_browser_query, option_text), msg).fulfill() @@ -307,8 +304,8 @@ def create_multiple_choice_xml(correct_choice=2, num_choices=4): choices = [False for _ in range(num_choices)] choices[correct_choice] = True - choice_names = ['choice_{}'.format(index) for index in range(num_choices)] - question_text = u'The correct answer is Choice {}'.format(correct_choice) + choice_names = [f'choice_{index}' for index in range(num_choices)] + question_text = f'The correct answer is Choice {correct_choice}' return MultipleChoiceResponseXMLFactory().build_xml( question_text=question_text, @@ -342,7 +339,7 @@ class EventsTestMixin(TestCase): Helpers and setup for running tests that evaluate events emitted """ def setUp(self): - super(EventsTestMixin, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() mongo_host = 'edx.devstack.mongo' if 'BOK_CHOY_HOSTNAME' in os.environ else 'localhost' self.event_collection = MongoClient(mongo_host)["test"]["events"] self.start_time = datetime.now() @@ -354,14 +351,14 @@ class AcceptanceTest(WebAppTest): """ def __init__(self, *args, **kwargs): - super(AcceptanceTest, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) # Use long messages so that failures show actual and expected values self.longMessage = True # pylint: disable=invalid-name def tearDown(self): self._save_console_log() - super(AcceptanceTest, self).tearDown() # lint-amnesty, pylint: disable=super-with-arguments + super().tearDown() def _save_console_log(self): """ @@ -393,10 +390,10 @@ class AcceptanceTest(WebAppTest): 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: + log_path = os.path.join(log_dir, f'{self.id()}_browser.log') + with open(log_path, 'w') as browser_log: for (message, url, line_no, col_no, stack) in logs: - browser_log.write(u"{}:{}:{}: {}\n {}\n".format( + browser_log.write("{}:{}:{}: {}\n {}\n".format( url, line_no, col_no, @@ -411,7 +408,7 @@ class UniqueCourseTest(AcceptanceTest): """ def setUp(self): - super(UniqueCourseTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course_info = { 'org': 'test_org', @@ -433,7 +430,7 @@ class UniqueCourseTest(AcceptanceTest): self.course_info['run'], deprecated=(default_store == 'draft') ) - return six.text_type(course_key) + return str(course_key) class YouTubeConfigError(Exception): @@ -443,14 +440,14 @@ class YouTubeConfigError(Exception): pass # lint-amnesty, pylint: disable=unnecessary-pass -class YouTubeStubConfig(object): +class YouTubeStubConfig: """ Configure YouTube Stub Server. """ YOUTUBE_HOSTNAME = os.environ.get('BOK_CHOY_HOSTNAME', '127.0.0.1') PORT = 9080 - URL = 'http://{}:{}/'.format(YOUTUBE_HOSTNAME, PORT) + URL = f'http://{YOUTUBE_HOSTNAME}:{PORT}/' @classmethod def configure(cls, config): @@ -471,7 +468,7 @@ class YouTubeStubConfig(object): if not response.ok: raise YouTubeConfigError( - u'YouTube Server Configuration Failed. URL {0}, Configuration Data: {1}, Status was {2}'.format( + 'YouTube Server Configuration Failed. URL {}, Configuration Data: {}, Status was {}'.format( youtube_stub_config_url, config, response.status_code)) @classmethod @@ -489,7 +486,7 @@ class YouTubeStubConfig(object): if not response.ok: raise YouTubeConfigError( - u'YouTube Server Configuration Failed. URL: {0} Status was {1}'.format( + 'YouTube Server Configuration Failed. URL: {} Status was {}'.format( youtube_stub_config_url, response.status_code)) @classmethod @@ -533,7 +530,7 @@ def create_user_partition_json(partition_id, name, description, groups, scheme=" Helper method to create user partition JSON. If scheme is not supplied, "random" is used. """ # All that is persisted about a scheme is its name. - class MockScheme(object): + class MockScheme: name = scheme return UserPartition( diff --git a/common/test/acceptance/tests/lms/test_account_settings.py b/common/test/acceptance/tests/lms/test_account_settings.py index 550ceb47c3..6bc7ff7461 100644 --- a/common/test/acceptance/tests/lms/test_account_settings.py +++ b/common/test/acceptance/tests/lms/test_account_settings.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ End-to-end tests for the Account Settings page. """ @@ -14,9 +13,9 @@ class AccountSettingsTestMixin(EventsTestMixin, AcceptanceTest): Mixin with helper methods to test the account settings page. """ - CHANGE_INITIATED_EVENT_NAME = u"edx.user.settings.change_initiated" + CHANGE_INITIATED_EVENT_NAME = "edx.user.settings.change_initiated" USER_SETTINGS_CHANGED_EVENT_NAME = 'edx.user.settings.changed' - ACCOUNT_SETTINGS_REFERER = u"/account/settings" + ACCOUNT_SETTINGS_REFERER = "/account/settings" shard = 23 diff --git a/common/test/acceptance/tests/lms/test_learner_profile.py b/common/test/acceptance/tests/lms/test_learner_profile.py index 31a46c194b..7a5e47c19f 100644 --- a/common/test/acceptance/tests/lms/test_learner_profile.py +++ b/common/test/acceptance/tests/lms/test_learner_profile.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ End-to-end tests for Student's Profile Page. """ @@ -17,15 +16,15 @@ class LearnerProfileTestMixin(EventsTestMixin): Mixin with helper methods for testing learner profile pages. """ - PRIVACY_PUBLIC = u'all_users' - PRIVACY_PRIVATE = u'private' + PRIVACY_PUBLIC = 'all_users' + PRIVACY_PRIVATE = 'private' PUBLIC_PROFILE_FIELDS = ['username', 'country', 'language_proficiencies', 'bio'] PRIVATE_PROFILE_FIELDS = ['username'] PUBLIC_PROFILE_EDITABLE_FIELDS = ['country', 'language_proficiencies', 'bio'] - USER_SETTINGS_CHANGED_EVENT_NAME = u"edx.user.settings.changed" + USER_SETTINGS_CHANGED_EVENT_NAME = "edx.user.settings.changed" def log_in_as_unique_user(self): """ diff --git a/common/test/acceptance/tests/lms/test_lms.py b/common/test/acceptance/tests/lms/test_lms.py index e9e502de66..60ba21ac48 100644 --- a/common/test/acceptance/tests/lms/test_lms.py +++ b/common/test/acceptance/tests/lms/test_lms.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ End-to-end tests for the LMS. """ @@ -30,7 +29,7 @@ class CourseWikiA11yTest(UniqueCourseTest): """ Initialize pages and install a course fixture. """ - super(CourseWikiA11yTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() # self.course_info['number'] must be shorter since we are accessing the wiki. See TNL-1751 self.course_info['number'] = self.unique_id[0:6] diff --git a/common/test/acceptance/tests/lms/test_lms_course_home.py b/common/test/acceptance/tests/lms/test_lms_course_home.py index 313d69d961..b3aadfcfac 100644 --- a/common/test/acceptance/tests/lms/test_lms_course_home.py +++ b/common/test/acceptance/tests/lms/test_lms_course_home.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ End-to-end tests for the LMS that utilize the course home page and course outline. """ @@ -22,7 +21,7 @@ class CourseHomeBaseTest(UniqueCourseTest): """ Initialize pages and install a course fixture. """ - super(CourseHomeBaseTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course_home_page = CourseHomePage(self.browser, self.course_id) self.courseware_page = CoursewarePage(self.browser, self.course_id) diff --git a/common/test/acceptance/tests/lms/test_lms_dashboard.py b/common/test/acceptance/tests/lms/test_lms_dashboard.py index b7908faa6c..b5fe7cb7e7 100644 --- a/common/test/acceptance/tests/lms/test_lms_dashboard.py +++ b/common/test/acceptance/tests/lms/test_lms_dashboard.py @@ -1,17 +1,13 @@ -# -*- coding: utf-8 -*- """ End-to-end tests for the main LMS Dashboard (aka, Student Dashboard). """ - -import six - from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc from common.test.acceptance.pages.common.auto_auth import AutoAuthPage from common.test.acceptance.pages.lms.dashboard import DashboardPage from common.test.acceptance.tests.helpers import UniqueCourseTest, generate_course_key -DEFAULT_SHORT_DATE_FORMAT = u'{dt:%b} {dt.day}, {dt.year}' -TEST_DATE_FORMAT = u'{dt:%b} {dt.day}, {dt.year} {dt.hour:02}:{dt.minute:02}' +DEFAULT_SHORT_DATE_FORMAT = '{dt:%b} {dt.day}, {dt.year}' +TEST_DATE_FORMAT = '{dt:%b} {dt.day}, {dt.year} {dt.hour:02}:{dt.minute:02}' class BaseLmsDashboardTestMultiple(UniqueCourseTest): @@ -23,7 +19,7 @@ class BaseLmsDashboardTestMultiple(UniqueCourseTest): """ # Some parameters are provided by the parent setUp() routine, such as the following: # self.course_id, self.course_info, self.unique_id - super(BaseLmsDashboardTestMultiple, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() # Load page objects for use by the tests self.dashboard_page = DashboardPage(self.browser) @@ -57,12 +53,12 @@ class BaseLmsDashboardTestMultiple(UniqueCourseTest): } self.username = "test_{uuid}".format(uuid=self.unique_id[0:6]) - self.email = "{user}@example.com".format(user=self.username) + self.email = f"{self.username}@example.com" self.course_keys = {} self.course_fixtures = {} - for key, value in six.iteritems(self.courses): + for key, value in self.courses.items(): course_key = generate_course_key( value['org'], value['number'], @@ -77,8 +73,8 @@ class BaseLmsDashboardTestMultiple(UniqueCourseTest): ) course_fixture.add_advanced_settings({ - u"social_sharing_url": {u"value": "http://custom/course/url"}, - u"cert_name_long": {u"value": value['cert_name_long']} + "social_sharing_url": {"value": "http://custom/course/url"}, + "cert_name_long": {"value": value['cert_name_long']} }) course_fixture.add_children( XBlockFixtureDesc('chapter', 'Test Section 1').add_children( diff --git a/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py b/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py index 1553d96eb3..878fe0b456 100644 --- a/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py +++ b/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ End-to-end tests for the LMS Instructor Dashboard. """ @@ -59,7 +58,7 @@ class LMSInstructorDashboardA11yTest(BaseInstructorDashboardTest): Instructor dashboard base accessibility test. """ def setUp(self): - super(LMSInstructorDashboardA11yTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course_fixture = CourseFixture(**self.course_info).install() self.log_in_as_instructor() self.instructor_dashboard_page = self.visit_instructor_dashboard() @@ -82,7 +81,7 @@ class BulkEmailTest(BaseInstructorDashboardTest): shard = 23 def setUp(self): - super(BulkEmailTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course_fixture = CourseFixture(**self.course_info).install() self.log_in_as_instructor() instructor_dashboard_page = self.visit_instructor_dashboard() @@ -114,7 +113,7 @@ class AutoEnrollmentWithCSVTest(BaseInstructorDashboardTest): """ def setUp(self): - super(AutoEnrollmentWithCSVTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course_fixture = CourseFixture(**self.course_info).install() self.log_in_as_instructor() instructor_dashboard_page = self.visit_instructor_dashboard() @@ -141,7 +140,7 @@ class CertificatesTest(BaseInstructorDashboardTest): """ def setUp(self): - super(CertificatesTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.test_certificate_config = { 'id': 1, 'name': 'Certificate name', @@ -183,7 +182,7 @@ class CertificateInvalidationTest(BaseInstructorDashboardTest): @classmethod def setUpClass(cls): - super(CertificateInvalidationTest, cls).setUpClass() + super().setUpClass() # Create course fixture once each test run CourseFixture( @@ -194,7 +193,7 @@ class CertificateInvalidationTest(BaseInstructorDashboardTest): ).install() def setUp(self): - super(CertificateInvalidationTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() # set same course number as we have in fixture json self.course_info['number'] = "335535897951379478207964576572017930000" diff --git a/common/test/acceptance/tests/lms/test_lms_problems.py b/common/test/acceptance/tests/lms/test_lms_problems.py index 2220dbd4a5..c96c7262f5 100644 --- a/common/test/acceptance/tests/lms/test_lms_problems.py +++ b/common/test/acceptance/tests/lms/test_lms_problems.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Bok choy acceptance tests for problems in the LMS """ @@ -20,10 +19,10 @@ class ProblemsTest(UniqueCourseTest): """ def setUp(self): - super(ProblemsTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.username = "test_student_{uuid}".format(uuid=self.unique_id[0:8]) - self.email = "{username}@example.com".format(username=self.username) + self.email = f"{self.username}@example.com" self.password = "keep it secret; keep it safe." self.xqueue_grade_response = None @@ -63,7 +62,7 @@ class ProblemsTest(UniqueCourseTest): return XBlockFixtureDesc('sequential', 'Test Subsection') -class CAPAProblemA11yBaseTestMixin(object): +class CAPAProblemA11yBaseTestMixin: """Base TestCase Class to verify CAPA problem accessibility.""" def test_a11y(self): diff --git a/common/test/acceptance/tests/lms/test_lms_user_preview.py b/common/test/acceptance/tests/lms/test_lms_user_preview.py index b628bbf761..195350fb7a 100644 --- a/common/test/acceptance/tests/lms/test_lms_user_preview.py +++ b/common/test/acceptance/tests/lms/test_lms_user_preview.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Tests the "preview" selector in the LMS that allows changing between Staff, Learner, and Content Groups. """ @@ -24,7 +23,7 @@ class StaffViewTest(UniqueCourseTest): EMAIL = "johndoe@example.com" def setUp(self): - super(StaffViewTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.courseware_page = CoursewarePage(self.browser, self.course_id) @@ -60,11 +59,11 @@ class CourseWithContentGroupsTest(StaffViewTest): """ def setUp(self): - super(CourseWithContentGroupsTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() # pylint: disable=protected-access self.course_fixture._update_xblock(self.course_fixture._course_location, { "metadata": { - u"user_partitions": [ + "user_partitions": [ create_user_partition_json( MINIMUM_STATIC_PARTITION_ID, 'Configuration alpha,beta', diff --git a/common/test/acceptance/tests/lms/test_problem_types.py b/common/test/acceptance/tests/lms/test_problem_types.py index 2195955732..0171577b15 100644 --- a/common/test/acceptance/tests/lms/test_problem_types.py +++ b/common/test/acceptance/tests/lms/test_problem_types.py @@ -8,7 +8,6 @@ import textwrap from abc import ABCMeta, abstractmethod import ddt -import six from bok_choy.promise import BrokenPromise from capa.tests.response_xml_factory import ( @@ -49,7 +48,7 @@ class ProblemTypeTestBaseMeta(ABCMeta): ] for required_attr in required_attrs: - msg = (u'{} is a required attribute for {}').format( + msg = ('{} is a required attribute for {}').format( required_attr, str(cls) ) @@ -62,7 +61,7 @@ class ProblemTypeTestBaseMeta(ABCMeta): return obj -class ProblemTypeTestBase(six.with_metaclass(ProblemTypeTestBaseMeta, ProblemsTest, EventsTestMixin)): +class ProblemTypeTestBase(ProblemsTest, EventsTestMixin, metaclass=ProblemTypeTestBaseMeta): """ Base class for testing assesment problem types in bok choy. @@ -96,7 +95,7 @@ class ProblemTypeTestBase(six.with_metaclass(ProblemTypeTestBaseMeta, ProblemsTe """ Visits courseware_page and defines self.problem_page. """ - super(ProblemTypeTestBase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.courseware_page.visit() self.problem_page = ProblemPage(self.browser) @@ -123,7 +122,7 @@ class ProblemTypeTestBase(six.with_metaclass(ProblemTypeTestBaseMeta, ProblemsTe Args: status: one of ("correct", "incorrect", "unanswered", "submitted") """ - msg = u"Wait for status to be {}".format(status) + msg = f"Wait for status to be {status}" selector = ', '.join(self.status_indicators[status]) self.problem_page.wait_for_element_visibility(selector, msg) @@ -154,7 +153,7 @@ class ProblemTypeTestBase(six.with_metaclass(ProblemTypeTestBaseMeta, ProblemsTe raise NotImplementedError() -class ProblemTypeA11yTestMixin(object): +class ProblemTypeA11yTestMixin: """ Shared a11y tests for all problem types. """ @@ -215,7 +214,7 @@ class AnnotationProblemTypeBase(ProblemTypeTestBase): """ Additional setup for AnnotationProblemTypeBase """ - super(AnnotationProblemTypeBase, self).setUp(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().setUp(*args, **kwargs) self.problem_page.a11y_audit.config.set_rules({ "ignore": [ @@ -238,7 +237,7 @@ class AnnotationProblemTypeBase(ProblemTypeTestBase): self.problem_page.q(css='div.problem textarea.comment').fill(answer) self.problem_page.q( - css='div.problem span.tag'.format(choice=choice) + css='div.problem span.tag' ).nth(choice).click() @@ -902,7 +901,7 @@ class ChoiceTextProblemTypeTestBase(ProblemTypeTestBase): Selects the nth (where n == input_num) choice of the problem. """ self.problem_page.q( - css=u'div.problem input.ctinput[type="{}"]'.format(self.choice_type) + css=f'div.problem input.ctinput[type="{self.choice_type}"]' ).nth(input_num).click() def _fill_input_text(self, value, input_num): @@ -974,7 +973,7 @@ class RadioTextProblemTypeBase(ChoiceTextProblemTypeTestBase): """ Additional setup for RadioTextProblemTypeBase """ - super(RadioTextProblemTypeBase, self).setUp(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().setUp(*args, **kwargs) self.problem_page.a11y_audit.config.set_rules({ "ignore": [ @@ -1037,7 +1036,7 @@ class CheckboxTextProblemTypeBase(ChoiceTextProblemTypeTestBase): """ Additional setup for CheckboxTextProblemTypeBase """ - super(CheckboxTextProblemTypeBase, self).setUp(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().setUp(*args, **kwargs) self.problem_page.a11y_audit.config.set_rules({ "ignore": [ diff --git a/common/test/acceptance/tests/lms/test_programs.py b/common/test/acceptance/tests/lms/test_programs.py index c5bf244a07..3571394cab 100644 --- a/common/test/acceptance/tests/lms/test_programs.py +++ b/common/test/acceptance/tests/lms/test_programs.py @@ -20,7 +20,7 @@ from openedx.core.djangoapps.catalog.tests.factories import ( class ProgramPageBase(ProgramsConfigMixin, CatalogIntegrationMixin, UniqueCourseTest): """Base class used for program listing page tests.""" def setUp(self): - super(ProgramPageBase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.set_programs_api_configuration(is_enabled=True) @@ -79,7 +79,7 @@ class ProgramListingPageA11yTest(ProgramPageBase): a11y = True def setUp(self): - super(ProgramListingPageA11yTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.listing_page = ProgramListingPage(self.browser) @@ -128,7 +128,7 @@ class ProgramDetailsPageA11yTest(ProgramPageBase): a11y = True def setUp(self): - super(ProgramDetailsPageA11yTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.details_page = ProgramDetailsPage(self.browser) diff --git a/common/test/acceptance/tests/lms/test_progress_page.py b/common/test/acceptance/tests/lms/test_progress_page.py index 0a8ad69332..e84c06f87d 100644 --- a/common/test/acceptance/tests/lms/test_progress_page.py +++ b/common/test/acceptance/tests/lms/test_progress_page.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ End-to-end tests for the LMS that utilize the progress page. @@ -7,7 +6,6 @@ progress page. from contextlib import contextmanager -from six.moves import range from ...fixtures.course import CourseFixture, XBlockFixtureDesc from ...pages.common.logout import LogoutPage @@ -36,7 +34,7 @@ class ProgressPageBaseTest(UniqueCourseTest): PROBLEM_NAME_2 = 'Test Problem 2' def setUp(self): - super(ProgressPageBaseTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.courseware_page = CoursewarePage(self.browser, self.course_id) self.problem_page = ProblemPage(self.browser) self.progress_page = ProgressPage(self.browser, self.course_id) @@ -89,7 +87,7 @@ class ProgressPageBaseTest(UniqueCourseTest): Submit the given choice for the problem. """ self.courseware_page.go_to_sequential_position(1) - self.problem_page.click_choice('choice_choice_{}'.format(choice)) + self.problem_page.click_choice(f'choice_choice_{choice}') self.problem_page.click_submit() def _get_section_score(self): @@ -128,7 +126,7 @@ class SubsectionGradingPolicyBase(ProgressPageBaseTest): the progress page """ def setUp(self): - super(SubsectionGradingPolicyBase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self._set_policy_for_subsection("Homework", 0) self._set_policy_for_subsection("Lab", 1) @@ -194,46 +192,46 @@ class SubsectionGradingPolicyA11yTest(SubsectionGradingPolicyBase): assert ['0%', 'true'] == self.progress_page.y_tick_label(1) assert ['Pass 50%', 'true'] == self.progress_page.y_tick_label(2) # Verify x-Axis labels and sr-text - self._check_tick_text(0, [u'Homework 1 - Test Subsection 1 - 50% (1/2)'], u'HW 01') + self._check_tick_text(0, ['Homework 1 - Test Subsection 1 - 50% (1/2)'], 'HW 01') # Homeworks 2-10 are checked in the for loop below. self._check_tick_text( 10, - [u'Homework 11 Unreleased - 0% (?/?)', u'The lowest 2 Homework scores are dropped.'], - u'HW 11' + ['Homework 11 Unreleased - 0% (?/?)', 'The lowest 2 Homework scores are dropped.'], + 'HW 11' ) self._check_tick_text( 11, - [u'Homework 12 Unreleased - 0% (?/?)', u'The lowest 2 Homework scores are dropped.'], - u'HW 12' + ['Homework 12 Unreleased - 0% (?/?)', 'The lowest 2 Homework scores are dropped.'], + 'HW 12' ) - self._check_tick_text(12, [u'Homework Average = 5%'], u'HW Avg') - self._check_tick_text(13, [u'Lab 1 - Lab Subsection - 100% (1/1)'], u'Lab 01') + self._check_tick_text(12, ['Homework Average = 5%'], 'HW Avg') + self._check_tick_text(13, ['Lab 1 - Lab Subsection - 100% (1/1)'], 'Lab 01') # Labs 2-10 are checked in the for loop below. self._check_tick_text( 23, - [u'Lab 11 Unreleased - 0% (?/?)', u'The lowest 2 Lab scores are dropped.'], - u'Lab 11' + ['Lab 11 Unreleased - 0% (?/?)', 'The lowest 2 Lab scores are dropped.'], + 'Lab 11' ) self._check_tick_text( 24, - [u'Lab 12 Unreleased - 0% (?/?)', u'The lowest 2 Lab scores are dropped.'], - u'Lab 12' + ['Lab 12 Unreleased - 0% (?/?)', 'The lowest 2 Lab scores are dropped.'], + 'Lab 12' ) - self._check_tick_text(25, [u'Lab Average = 10%'], u'Lab Avg') - self._check_tick_text(26, [u'Midterm Exam = 0%'], u'Midterm') - self._check_tick_text(27, [u'Final Exam = 0%'], u'Final') + self._check_tick_text(25, ['Lab Average = 10%'], 'Lab Avg') + self._check_tick_text(26, ['Midterm Exam = 0%'], 'Midterm') + self._check_tick_text(27, ['Final Exam = 0%'], 'Final') self._check_tick_text( 28, - [u'Homework = 0.75% of a possible 15.00%', u'Lab = 1.50% of a possible 15.00%'], - u'Total', + ['Homework = 0.75% of a possible 15.00%', 'Lab = 1.50% of a possible 15.00%'], + 'Total', False # The label "Total" should NOT be aria-hidden ) @@ -242,13 +240,13 @@ class SubsectionGradingPolicyA11yTest(SubsectionGradingPolicyBase): for i in range(1, 10): self._check_tick_text( i, - [u'Homework {index} Unreleased - 0% (?/?)'.format(index=i + 1)], - u'HW 0{index}'.format(index=i + 1) if i < 9 else u'HW {index}'.format(index=i + 1) + ['Homework {index} Unreleased - 0% (?/?)'.format(index=i + 1)], + 'HW 0{index}'.format(index=i + 1) if i < 9 else 'HW {index}'.format(index=i + 1) ) self._check_tick_text( i + 13, - [u'Lab {index} Unreleased - 0% (?/?)'.format(index=i + 1)], - u'Lab 0{index}'.format(index=i + 1) if i < 9 else u'Lab {index}'.format(index=i + 1) + ['Lab {index} Unreleased - 0% (?/?)'.format(index=i + 1)], + 'Lab 0{index}'.format(index=i + 1) if i < 9 else 'Lab {index}'.format(index=i + 1) ) # Verify the overall score. The first element in the array is the sr-only text, and the diff --git a/common/test/acceptance/tests/studio/base_studio_test.py b/common/test/acceptance/tests/studio/base_studio_test.py index a1d8a48f41..9f36b53af0 100644 --- a/common/test/acceptance/tests/studio/base_studio_test.py +++ b/common/test/acceptance/tests/studio/base_studio_test.py @@ -22,7 +22,7 @@ class StudioCourseTest(UniqueCourseTest): """ Install a course with no content using a fixture. """ - super(StudioCourseTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.test_xss = test_xss self.install_course_fixture(is_staff) @@ -38,7 +38,7 @@ class StudioCourseTest(UniqueCourseTest): ) if self.test_xss: xss_injected_unique_id = XSS_INJECTION + self.unique_id - test_improper_escaping = {u"value": xss_injected_unique_id} + test_improper_escaping = {"value": xss_injected_unique_id} self.course_fixture.add_advanced_settings({ "advertised_start": test_improper_escaping, "info_sidebar_name": test_improper_escaping, @@ -90,7 +90,7 @@ class ContainerBase(StudioCourseTest): Create a unique identifier for the course used in this test. """ # Ensure that the superclass sets up - super(ContainerBase, self).setUp(is_staff=is_staff) # lint-amnesty, pylint: disable=super-with-arguments + super().setUp(is_staff=is_staff) self.outline = CourseOutlinePage( self.browser, @@ -142,11 +142,11 @@ class StudioLibraryTest(AcceptanceTest): """ Install a library with no content using a fixture. """ - super(StudioLibraryTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() fixture = LibraryFixture( 'test_org', self.unique_id, - u'Test Library {}'.format(self.unique_id), + f'Test Library {self.unique_id}', ) self.populate_library_fixture(fixture) fixture.install() diff --git a/common/test/acceptance/tests/studio/test_studio_settings.py b/common/test/acceptance/tests/studio/test_studio_settings.py index f4bf7d55d0..5a06d5b483 100644 --- a/common/test/acceptance/tests/studio/test_studio_settings.py +++ b/common/test/acceptance/tests/studio/test_studio_settings.py @@ -1,11 +1,10 @@ -# coding: utf-8 """ Acceptance tests for Studio's Setting pages """ import os -from mock import patch +from unittest.mock import patch from common.test.acceptance.fixtures.course import XBlockFixtureDesc from common.test.acceptance.pages.studio.overview import CourseOutlinePage @@ -22,7 +21,7 @@ class StudioSettingsA11yTest(StudioCourseTest): """ def setUp(self): # pylint: disable=arguments-differ - super(StudioSettingsA11yTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.settings_page = SettingsPage(self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run']) @@ -67,7 +66,7 @@ class StudioSubsectionSettingsA11yTest(StudioCourseTest): browser = 'firefox' with patch.dict(os.environ, {'SELENIUM_BROWSER': browser}): - super(StudioSubsectionSettingsA11yTest, self).setUp(is_staff=True) # lint-amnesty, pylint: disable=super-with-arguments + super().setUp(is_staff=True) self.course_outline = CourseOutlinePage( self.browser, diff --git a/common/test/acceptance/tests/video/test_video_module.py b/common/test/acceptance/tests/video/test_video_module.py index ec6581fbae..e829bb511f 100644 --- a/common/test/acceptance/tests/video/test_video_module.py +++ b/common/test/acceptance/tests/video/test_video_module.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ Acceptance tests for Video. """ @@ -7,7 +5,7 @@ Acceptance tests for Video. import os from unittest import skipIf -from mock import patch +from unittest.mock import patch from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc from common.test.acceptance.pages.common.auto_auth import AutoAuthPage @@ -25,17 +23,17 @@ VIDEO_SOURCE_PORT = 8777 VIDEO_HOSTNAME = os.environ.get('BOK_CHOY_HOSTNAME', 'localhost') HTML5_SOURCES = [ - 'http://{}:{}/gizmo.mp4'.format(VIDEO_HOSTNAME, VIDEO_SOURCE_PORT), - 'http://{}:{}/gizmo.webm'.format(VIDEO_HOSTNAME, VIDEO_SOURCE_PORT), - 'http://{}:{}/gizmo.ogv'.format(VIDEO_HOSTNAME, VIDEO_SOURCE_PORT), + f'http://{VIDEO_HOSTNAME}:{VIDEO_SOURCE_PORT}/gizmo.mp4', + f'http://{VIDEO_HOSTNAME}:{VIDEO_SOURCE_PORT}/gizmo.webm', + f'http://{VIDEO_HOSTNAME}:{VIDEO_SOURCE_PORT}/gizmo.ogv', ] HTML5_SOURCES_INCORRECT = [ - 'http://{}:{}/gizmo.mp99'.format(VIDEO_HOSTNAME, VIDEO_SOURCE_PORT), + f'http://{VIDEO_HOSTNAME}:{VIDEO_SOURCE_PORT}/gizmo.mp99', ] HLS_SOURCES = [ - 'http://{}:{}/hls/history.m3u8'.format(VIDEO_HOSTNAME, VIDEO_SOURCE_PORT), + f'http://{VIDEO_HOSTNAME}:{VIDEO_SOURCE_PORT}/hls/history.m3u8', ] @@ -50,7 +48,7 @@ class VideoBaseTest(UniqueCourseTest): """ Initialization of pages and course fixture for video tests """ - super(VideoBaseTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.longMessage = True self.video = VideoPage(self.browser) @@ -125,7 +123,7 @@ class VideoBaseTest(UniqueCourseTest): :param vertical_index: index for the vertical display name :return: XBlockFixtureDesc """ - xblock_course_vertical = XBlockFixtureDesc('vertical', u'Test Vertical-{0}'.format(vertical_index)) + xblock_course_vertical = XBlockFixtureDesc('vertical', f'Test Vertical-{vertical_index}') for video in vertical_contents: xblock_course_vertical.add_children( @@ -229,7 +227,7 @@ class LMSVideoBlockA11yTest(VideoBaseTest): browser = 'firefox' with patch.dict(os.environ, {'SELENIUM_BROWSER': browser}): - super(LMSVideoBlockA11yTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() def test_video_player_a11y(self): # load transcripts so we can test skipping to diff --git a/common/test/utils.py b/common/test/utils.py index 54a03b7d8f..2c55df7d1e 100644 --- a/common/test/utils.py +++ b/common/test/utils.py @@ -6,6 +6,7 @@ General testing utilities. import functools import sys from contextlib import contextmanager + from django.dispatch import Signal from markupsafe import escape from mock import Mock, patch @@ -19,7 +20,7 @@ def nostderr(): """ savestderr = sys.stderr - class Devnull(object): + class Devnull: """ /dev/null incarnation as output-stream-like object """ def write(self, _): """ Write method - just does nothing""" @@ -32,7 +33,7 @@ def nostderr(): sys.stderr = savestderr -class XssTestMixin(object): +class XssTestMixin: """ Mixin for testing XSS vulnerabilities. """ @@ -60,7 +61,7 @@ def disable_signal(module, signal): return patch.object(module, signal, new=Signal()) -class MockSignalHandlerMixin(object): +class MockSignalHandlerMixin: """Mixin for testing sending of signals.""" @contextmanager @@ -114,12 +115,12 @@ def skip_signal(signal, **kwargs): signal.connect(**kwargs) -class MockS3BotoMixin(object): +class MockS3BotoMixin: """ TestCase mixin that mocks the S3BotoStorage save method and s3 connection. """ def setUp(self): - super(MockS3BotoMixin, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self._mocked_connection = patch('boto.connect_s3', return_value=Mock()) self.mocked_connection = self._mocked_connection.start() @@ -129,16 +130,16 @@ class MockS3BotoMixin(object): def tearDown(self): self._mocked_connection.stop() self.patcher.stop() - super(MockS3BotoMixin, self).tearDown() # lint-amnesty, pylint: disable=super-with-arguments + super().tearDown() -class reprwrapper(object): +class reprwrapper: """ Wrapper class for functions that need a normalized string representation. """ def __init__(self, func): self._func = func - self.repr = u'Func: {}'.format(func.__name__) + self.repr = f'Func: {func.__name__}' functools.update_wrapper(self, func) def __call__(self, *args, **kw):