refactor: pyupgrade in common/tests (#26725)
This commit is contained in:
@@ -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')
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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<username>\S+)', r'(?P<email>[^\)]+)', r'(?P<password>\S+)', r'(?P<user_id>\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)
|
||||
|
||||
@@ -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("""
|
||||
<XBlockFixtureDescriptor:
|
||||
category={0},
|
||||
data={1},
|
||||
@@ -120,7 +119,7 @@ class CourseFixture(XBlockContainerFixture):
|
||||
to enable entrance exam settings would be a dict like this {"entrance_exam_enabled": "true"}
|
||||
These have the same meaning as in the Studio restful API /course end-point.
|
||||
"""
|
||||
super(CourseFixture, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__()
|
||||
self._course_dict = {
|
||||
'org': org,
|
||||
'number': number,
|
||||
@@ -154,7 +153,7 @@ class CourseFixture(XBlockContainerFixture):
|
||||
"""
|
||||
String representation of the course fixture, useful for debugging.
|
||||
"""
|
||||
return u"<CourseFixture: org='{org}', number='{number}', run='{run}'>".format(**self._course_dict)
|
||||
return "<CourseFixture: org='{org}', number='{number}', run='{run}'>".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'<li><a href="/static/{handout}">Example Handout</a></li>'.format(handout=handout)
|
||||
f'<li><a href="/static/{handout}">Example Handout</a></li>'
|
||||
for handout in self._handouts
|
||||
]
|
||||
handouts_html = u'<ol class="treeview-handoutsnav">{}</ol>'.format("".join(handouts_li))
|
||||
handouts_html = '<ol class="treeview-handoutsnav">{}</ol>'.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)
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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"<LibraryFixture: org='{org}', number='{number}'>".format(**self.library_info)
|
||||
return "<LibraryFixture: org='{org}', number='{number}'>".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)
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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}')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}')
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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}')
|
||||
|
||||
@@ -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 <p> 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):
|
||||
"""
|
||||
|
||||
@@ -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)])
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"{}(<browser>, {!r})".format(self.__class__.__name__, self.locator)
|
||||
return f"{self.__class__.__name__}(<browser>, {self.locator!r})"
|
||||
except AttributeError:
|
||||
return u"{}(<browser>)".format(self.__class__.__name__)
|
||||
return f"{self.__class__.__name__}(<browser>)"
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
Course Schedule and Details Settings page.
|
||||
"""
|
||||
|
||||
@@ -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.
|
||||
"""
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user