fix unicode strings in common/

This commit is contained in:
Matthew Piatetsky
2019-02-01 13:34:57 -05:00
parent 33d16d8e24
commit 9681ca36b8
88 changed files with 457 additions and 456 deletions

View File

@@ -386,7 +386,7 @@ class CourseEntitlement(TimeStampedModel):
mode=entitlement.mode
)
except CourseEnrollmentException:
log.exception('Login for Course Entitlement {uuid} failed'.format(uuid=entitlement.uuid))
log.exception(u'Login for Course Entitlement {uuid} failed'.format(uuid=entitlement.uuid))
return False
entitlement.set_enrollment(enrollment)
@@ -432,7 +432,7 @@ class CourseEntitlement(TimeStampedModel):
if not refund_successful:
# This state is achieved in most cases by a failure in the ecommerce service to process the refund.
log.warn(
'Entitlement Refund failed for Course Entitlement [%s], alert User',
u'Entitlement Refund failed for Course Entitlement [%s], alert User',
self.uuid
)
# Force Transaction reset with an Integrity error exception, this will revert all previous transactions

View File

@@ -36,8 +36,8 @@ def is_course_run_entitlement_fulfillable(
try:
course_overview = CourseOverview.get_from_id(course_run_key)
except CourseOverview.DoesNotExist:
log.error(('There is no CourseOverview entry available for {course_run_id}, '
'course run cannot be applied to entitlement').format(
log.error((u'There is no CourseOverview entry available for {course_run_id}, '
u'course run cannot be applied to entitlement').format(
course_run_id=str(course_run_key)
))
return False

View File

@@ -104,7 +104,7 @@ class ActivationEmailTests(CacheIsolationTestCase):
u"high-quality {platform} courses".format(platform=settings.PLATFORM_NAME),
"http://edx.org/activate/",
(
"please use our web form at "
u"please use our web form at "
u"{support_url} ".format(support_url=settings.SUPPORT_SITE_LINK)
)
]

View File

@@ -151,10 +151,10 @@ class SAMLConfigurationAdmin(KeyedConfigurationModelAdmin):
public_key = inst.get_setting('SP_PUBLIC_CERT')
private_key = inst.get_setting('SP_PRIVATE_KEY')
if not public_key or not private_key:
return u'<em>Key pair incomplete/missing</em>'
return HTML(u'<em>Key pair incomplete/missing</em>')
pub1, pub2 = public_key[0:10], public_key[-10:]
priv1, priv2 = private_key[0:10], private_key[-10:]
return u'Public: {}{}<br>Private: {}{}'.format(pub1, pub2, priv1, priv2)
return HTML(u'Public: {}{}<br>Private: {}{}').format(pub1, pub2, priv1, priv2)
key_summary.allow_tags = True
admin.site.register(SAMLConfiguration, SAMLConfigurationAdmin)
@@ -210,7 +210,7 @@ class ApiPermissionsAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ApiPermissionsAdminForm, self).__init__(*args, **kwargs)
self.fields['provider_id'].choices = (
(provider.provider_id, "{} ({})".format(provider.name, provider.provider_id))
(provider.provider_id, u"{} ({})".format(provider.name, provider.provider_id))
for provider in Registry.enabled()
)

View File

@@ -259,7 +259,7 @@ class UserMappingViewAPITests(TpaAPITestCase):
if access_token == 'valid-token':
access_token = token.token
response = self.client.get(url, HTTP_AUTHORIZATION='Bearer {}'.format(access_token))
response = self.client.get(url, HTTP_AUTHORIZATION=u'Bearer {}'.format(access_token))
self._verify_response(response, expect_code, expect_data)
@ddt.data(

View File

@@ -117,7 +117,7 @@ class BaseUserView(APIView):
if identifier.kind not in self.identifier_kinds:
# This is already checked before we get here, so raise a 500 error
# if the check fails.
raise ValueError("Identifier kind {} not in {}".format(identifier.kind, self.identifier_kinds))
raise ValueError(u"Identifier kind {} not in {}".format(identifier.kind, self.identifier_kinds))
self_request = False
if identifier == self.identifier('username', request.user.username):

View File

@@ -186,7 +186,7 @@ class LTIAuthBackend(BaseAuth):
if valid:
return data
except AttributeError as error:
log.error("'{}' not found.".format(text_type(error)))
log.error(u"'{}' not found.".format(text_type(error)))
return None
@classmethod

View File

@@ -27,10 +27,10 @@ class Command(BaseCommand):
log.addHandler(log_handler)
total, skipped, attempted, updated, failed, failure_messages = fetch_saml_metadata()
self.stdout.write(
"\nDone."
"\n{total} provider(s) found in database."
"\n{skipped} skipped and {attempted} attempted."
"\n{updated} updated and {failed} failed.\n".format(
u"\nDone."
u"\n{total} provider(s) found in database."
u"\n{skipped} skipped and {attempted} attempted."
u"\n{updated} updated and {failed} failed.\n".format(
total=total,
skipped=skipped, attempted=attempted,
updated=updated, failed=failed,
@@ -39,7 +39,7 @@ class Command(BaseCommand):
if failed > 0:
raise CommandError(
"Command finished with the following exceptions:\n\n{failures}".format(
u"Command finished with the following exceptions:\n\n{failures}".format(
failures="\n\n".join(failure_messages)
)
)

View File

@@ -18,7 +18,6 @@ from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from provider.oauth2.models import Client
from provider.utils import long_token
from six import text_type
from social_core.backends.base import BaseAuth
from social_core.backends.oauth import OAuthAuth
from social_core.backends.saml import SAMLAuth
@@ -61,9 +60,9 @@ def clean_json(value, of_type):
try:
value_python = json.loads(value)
except ValueError as err:
raise ValidationError("Invalid JSON: {}".format(text_type(err)))
raise ValidationError(u"Invalid JSON: {}".format(err))
if not isinstance(value_python, of_type):
raise ValidationError("Expected a JSON {}".format(of_type))
raise ValidationError(u"Expected a JSON {}".format(of_type))
return json.dumps(value_python, indent=4)
@@ -345,7 +344,7 @@ class OAuth2ProviderConfig(ProviderConfig):
help_text=(
'For increased security, you can avoid storing this in your database by leaving '
' this field blank and setting '
'SOCIAL_AUTH_OAUTH_SECRETS = {"(backend name)": "secret", ...} '
'SOCIAL_AUTH_OAUTH_SECRETS = {"(backend name)": "secret", ...} ' # pylint: disable=unicode-format-string
'in your instance\'s Django settings (or lms.auth.json)'
)
)
@@ -444,7 +443,7 @@ class SAMLConfiguration(ConfigurationModel):
"""
Return human-readable string representation.
"""
return "SAMLConfiguration {site}: {slug} on {date:%Y-%m-%d %H:%M:%S}".format(
return u"SAMLConfiguration {site}: {slug} on {date:%Y-%m-%d %H:%M:%S}".format(
site=self.site.name,
slug=self.slug,
date=self.change_date,
@@ -475,7 +474,7 @@ class SAMLConfiguration(ConfigurationModel):
""" Get the value of a setting, or raise KeyError """
default_saml_contact = {
# Default contact information to put into the SAML metadata that gets generated by python-saml.
"givenName": _("{platform_name} Support").format(
"givenName": _(u"{platform_name} Support").format(
platform_name=configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME)
),
"emailAddress": configuration_helpers.get_value('TECH_SUPPORT_EMAIL', settings.TECH_SUPPORT_EMAIL),
@@ -594,9 +593,9 @@ class SAMLProviderConfig(ProviderConfig):
verbose_name="Advanced settings", blank=True,
help_text=(
'For advanced use cases, enter a JSON object with addtional configuration. '
'The tpa-saml backend supports {"requiredEntitlements": ["urn:..."]}, '
'The tpa-saml backend supports {"requiredEntitlements": ["urn:..."]}, ' # pylint: disable=unicode-format-string
'which can be used to require the presence of a specific eduPersonEntitlement, '
'and {"extra_field_definitions": [{"name": "...", "urn": "..."},...]}, which can be '
'and {"extra_field_definitions": [{"name": "...", "urn": "..."},...]}, which can be ' # pylint: disable=unicode-format-string,line-too-long
'used to define registration form fields and the URNs that can be used to retrieve '
'the relevant values from the SAML response. Custom provider types, as selected '
'in the "Identity Provider Type" field, may make use of the information stored '
@@ -683,7 +682,7 @@ class SAMLProviderConfig(ProviderConfig):
data = SAMLProviderData.current(self.entity_id)
if not data or not data.is_valid():
log.error(
'No SAMLProviderData found for provider "%s" with entity id "%s" and IdP slug "%s". '
'No SAMLProviderData found for provider "%s" with entity id "%s" and IdP slug "%s". ' # pylint: disable=unicode-format-string,line-too-long
'Run "manage.py saml pull" to fix or debug.',
self.name, self.entity_id, self.slug
)
@@ -794,7 +793,7 @@ class LTIProviderConfig(ProviderConfig):
'tool consumer instance should know this value. '
'For increased security, you can avoid storing this in '
'your database by leaving this field blank and setting '
'SOCIAL_AUTH_LTI_CONSUMER_SECRETS = {"consumer key": "secret", ...} '
'SOCIAL_AUTH_LTI_CONSUMER_SECRETS = {"consumer key": "secret", ...} ' # pylint: disable=unicode-format-string,line-too-long
'in your instance\'s Django setttigs (or lms.auth.json)'
),
blank=True,

View File

@@ -275,7 +275,7 @@ def _get_enabled_provider(provider_id):
enabled_provider = provider.Registry.get(provider_id)
if not enabled_provider:
raise ValueError('Provider %s not enabled' % provider_id)
raise ValueError(u'Provider %s not enabled' % provider_id)
return enabled_provider
@@ -317,7 +317,7 @@ def get_complete_url(backend_name):
ValueError: if no provider is enabled with the given backend_name.
"""
if not any(provider.Registry.get_enabled_by_backend_name(backend_name)):
raise ValueError('Provider with backend %s not enabled' % backend_name)
raise ValueError(u'Provider with backend %s not enabled' % backend_name)
return _get_url('social:complete', backend_name)
@@ -593,7 +593,7 @@ def ensure_user_information(strategy, auth_entry, backend=None, user=None, socia
# register anew via SSO. See SOL-1324 in JIRA.
# However, we will log a warning for this case:
logger.warning(
'User "%s" is using third_party_auth to login but has not yet activated their account. ',
u'User "%s" is using third_party_auth to login but has not yet activated their account. ',
user.username
)
@@ -727,8 +727,8 @@ def user_details_force_sync(auth_entry, strategy, details, user=None, *args, **k
current_value = getattr(model, field)
if provider_value is not None and current_value != provider_value:
if field in integrity_conflict_fields and User.objects.filter(**{field: provider_value}).exists():
logger.warning('User with ID [%s] tried to synchronize profile data through [%s] '
'but there was a conflict with an existing [%s]: [%s].',
logger.warning(u'User with ID [%s] tried to synchronize profile data through [%s] '
u'but there was a conflict with an existing [%s]: [%s].',
user.id, current_provider.name, field, provider_value)
continue
changed[provider_field] = current_value
@@ -736,8 +736,8 @@ def user_details_force_sync(auth_entry, strategy, details, user=None, *args, **k
if changed:
logger.info(
"User [%s] performed SSO through [%s] who synchronizes profile data, and the "
"following fields were changed: %s", user.username, current_provider.name, changed.keys(),
u"User [%s] performed SSO through [%s] who synchronizes profile data, and the "
u"following fields were changed: %s", user.username, current_provider.name, changed.keys(),
)
# Save changes to user and user.profile models.
@@ -763,7 +763,7 @@ def user_details_force_sync(auth_entry, strategy, details, user=None, *args, **k
email.send()
except SMTPException:
logger.exception('Error sending IdP learner data sync-initiated email change '
'notification email for user [%s].', user.username)
u'notification email for user [%s].', user.username)
def set_id_verification_status(auth_entry, strategy, details, user=None, *args, **kwargs):

View File

@@ -155,7 +155,7 @@ class SAMLAuthBackend(SAMLAuth): # pylint: disable=abstract-method
for expected in idp.conf['requiredEntitlements']:
if expected not in entitlements:
log.warning(
"SAML user from IdP %s rejected due to missing eduPersonEntitlement %s", idp.name, expected)
u"SAML user from IdP %s rejected due to missing eduPersonEntitlement %s", idp.name, expected)
raise AuthForbidden(self)
def _create_saml_auth(self, idp):
@@ -177,7 +177,7 @@ class SAMLAuthBackend(SAMLAuth): # pylint: disable=abstract-method
def wrapped_method(*args, **kwargs):
""" Wrapped login or process_response method """
result = method(*args, **kwargs)
log.info("SAML login %s for IdP %s. XML is:\n%s", action_description, idp.name, xml_getter())
log.info(u"SAML login %s for IdP %s. XML is:\n%s", action_description, idp.name, xml_getter())
return result
setattr(auth_inst, method_name, wrapped_method)
@@ -363,8 +363,8 @@ class SapSuccessFactorsIdentityProvider(EdXSAMLIdentityProvider):
if not all(var in self.conf for var in self.required_variables):
missing = [var for var in self.required_variables if var not in self.conf]
log.warning(
"To retrieve rich user data for an SAP SuccessFactors identity provider, the following keys in "
"'other_settings' are required, but were missing: %s",
u"To retrieve rich user data for an SAP SuccessFactors identity provider, the following keys in "
u"'other_settings' are required, but were missing: %s",
missing
)
return missing
@@ -381,14 +381,14 @@ class SapSuccessFactorsIdentityProvider(EdXSAMLIdentityProvider):
token_data = transaction_data.get('token_data')
token_data = token_data if token_data else 'Not available'
log_msg_template = (
'SAPSuccessFactors exception received for {operation_name} request. ' +
'URL: {url} ' +
'Company ID: {company_id}. ' +
'User ID: {user_id}. ' +
'Error message: {err_msg}. ' +
'System message: {sys_msg}. ' +
'Headers: {headers}. ' +
'Token Data: {token_data}.'
u'SAPSuccessFactors exception received for {operation_name} request. ' +
u'URL: {url} ' +
u'Company ID: {company_id}. ' +
u'User ID: {user_id}. ' +
u'Error message: {err_msg}. ' +
u'System message: {sys_msg}. ' +
u'Headers: {headers}. ' +
u'Token Data: {token_data}.'
)
log_msg = log_msg_template.format(
operation_name=transaction_data['operation_name'],
@@ -469,7 +469,7 @@ class SapSuccessFactorsIdentityProvider(EdXSAMLIdentityProvider):
if not access_token_data:
return None
token_string = access_token_data['access_token']
session.headers.update({'Authorization': 'Bearer {}'.format(token_string), 'Accept': 'application/json'})
session.headers.update({'Authorization': u'Bearer {}'.format(token_string), 'Accept': 'application/json'})
session.token_data = access_token_data
return session
@@ -537,7 +537,7 @@ def get_saml_idp_class(idp_identifier_string):
}
if idp_identifier_string not in choices:
log.error(
'%s is not a valid EdXSAMLIdentityProvider subclass; using EdXSAMLIdentityProvider base class.',
u'%s is not a valid EdXSAMLIdentityProvider subclass; using EdXSAMLIdentityProvider base class.',
idp_identifier_string
)
return choices.get(idp_identifier_string, EdXSAMLIdentityProvider)

View File

@@ -17,6 +17,7 @@ from requests import exceptions
from six import text_type
from third_party_auth.models import SAMLConfiguration, SAMLProviderConfig, SAMLProviderData
from openedx.core.djangolib.markup import Text
log = logging.getLogger(__name__)
@@ -76,9 +77,9 @@ def fetch_saml_metadata():
failure_messages = [] # We return the length of this array for num_failed
for url, entity_ids in url_map.items():
try:
log.info("Fetching %s", url)
log.info(u"Fetching %s", url)
if not url.lower().startswith('https'):
log.warning("This SAML metadata URL is not secure! It should use HTTPS. (%s)", url)
log.warning(u"This SAML metadata URL is not secure! It should use HTTPS. (%s)", url)
response = requests.get(url, verify=True) # May raise HTTPError or SSLError or ConnectionError
response.raise_for_status() # May raise an HTTPError
@@ -108,23 +109,23 @@ def fetch_saml_metadata():
log.exception(text_type(error))
failure_messages.append(
"{error_type}: {error_message}\nMetadata Source: {url}\nEntity IDs: \n{entity_ids}.".format(
u"{error_type}: {error_message}\nMetadata Source: {url}\nEntity IDs: \n{entity_ids}.".format(
error_type=type(error).__name__,
error_message=text_type(error),
url=url,
entity_ids="\n".join(
["\t{}: {}".format(count, item) for count, item in enumerate(entity_ids, start=1)],
[u"\t{}: {}".format(count, item) for count, item in enumerate(entity_ids, start=1)],
)
)
)
except etree.XMLSyntaxError as error:
log.exception(text_type(error))
failure_messages.append(
"XMLSyntaxError: {error_message}\nMetadata Source: {url}\nEntity IDs: \n{entity_ids}.".format(
u"XMLSyntaxError: {error_message}\nMetadata Source: {url}\nEntity IDs: \n{entity_ids}.".format(
error_message=str(error.error_log),
url=url,
entity_ids="\n".join(
["\t{}: {}".format(count, item) for count, item in enumerate(entity_ids, start=1)],
[u"\t{}: {}".format(count, item) for count, item in enumerate(entity_ids, start=1)],
)
)
)
@@ -144,12 +145,12 @@ def _parse_metadata_xml(xml, entity_id):
entity_desc = xml
else:
if xml.tag != etree.QName(SAML_XML_NS, 'EntitiesDescriptor'):
raise MetadataParseError("Expected root element to be <EntitiesDescriptor>, not {}".format(xml.tag))
raise MetadataParseError(Text(u"Expected root element to be <EntitiesDescriptor>, not {}").format(xml.tag))
entity_desc = xml.find(
".//{}[@entityID='{}']".format(etree.QName(SAML_XML_NS, 'EntityDescriptor'), entity_id)
)
if not entity_desc:
raise MetadataParseError("Can't find EntityDescriptor for entityID {}".format(entity_id))
raise MetadataParseError(u"Can't find EntityDescriptor for entityID {}".format(entity_id))
expires_at = None
if "validUntil" in xml.attrib:

View File

@@ -63,7 +63,10 @@ class HelperMixin(object):
"""
self.assertEqual(200, response.status_code)
# Check that the correct provider was selected.
self.assertIn('successfully signed in with <strong>%s</strong>' % self.provider.name, response.content)
self.assertIn(
u'successfully signed in with <strong>%s</strong>' % self.provider.name,
response.content.decode(response.charset)
)
# Expect that each truthy value we've prepopulated the register form
# with is actually present.
form_field_data = self.provider.get_register_form_data(pipeline_kwargs)
@@ -120,8 +123,8 @@ class HelperMixin(object):
"""Asserts failure on /login for missing social auth looks right."""
self.assertEqual(403, response.status_code)
self.assertIn(
"successfully logged into your %s account, but this account isn&#39;t linked" % self.provider.name,
response.content
u"successfully logged into your %s account, but this account isn&#39;t linked" % self.provider.name,
response.content.decode(response.charset)
)
def assert_json_failure_response_is_username_collision(self, response):

View File

@@ -290,13 +290,13 @@ class TestShibIntegrationTest(SamlIntegrationTestUtilities, IntegrationTestMixin
self.assertEqual(mock_log.call_count, 4)
(msg, action_type, idp_name, xml), _kwargs = mock_log.call_args_list[0]
self.assertTrue(msg.startswith("SAML login %s"))
self.assertTrue(msg.startswith(u"SAML login %s"))
self.assertEqual(action_type, "request")
self.assertEqual(idp_name, self.PROVIDER_IDP_SLUG)
self.assertIn('<samlp:AuthnRequest', xml)
(msg, action_type, idp_name, xml), _kwargs = mock_log.call_args_list[1]
self.assertTrue(msg.startswith("SAML login %s"))
self.assertTrue(msg.startswith(u"SAML login %s"))
self.assertEqual(action_type, "response")
self.assertEqual(idp_name, self.PROVIDER_IDP_SLUG)
self.assertIn('<saml2p:Response', xml)

View File

@@ -350,9 +350,9 @@ class UserDetailsForceSyncTestCase(testutil.TestCase, test.TestCase):
self.old_username = self.user.username
self.old_fullname = self.user.profile.name
self.details = {
'email': 'new+{}'.format(self.user.email),
'username': 'new_{}'.format(self.user.username),
'fullname': 'Grown Up {}'.format(self.user.profile.name),
'email': u'new+{}'.format(self.user.email),
'username': u'new_{}'.format(self.user.username),
'fullname': u'Grown Up {}'.format(self.user.profile.name),
'country': 'PK',
'non_existing_field': 'value',
}
@@ -381,7 +381,7 @@ class UserDetailsForceSyncTestCase(testutil.TestCase, test.TestCase):
# User now has updated information in the DB.
user = User.objects.get()
assert user.email == 'new+{}'.format(self.old_email)
assert user.profile.name == 'Grown Up {}'.format(self.old_fullname)
assert user.profile.name == u'Grown Up {}'.format(self.old_fullname)
assert user.profile.country == 'PK'
# Now verify that username field is not updated
@@ -407,7 +407,7 @@ class UserDetailsForceSyncTestCase(testutil.TestCase, test.TestCase):
# The email is not changed, but everything else is.
user = User.objects.get(pk=self.user.pk)
assert user.email == self.old_email
assert user.profile.name == 'Grown Up {}'.format(self.old_fullname)
assert user.profile.name == u'Grown Up {}'.format(self.old_fullname)
assert user.profile.country == 'PK'
# Now verify that username field is not updated
@@ -437,7 +437,7 @@ class UserDetailsForceSyncTestCase(testutil.TestCase, test.TestCase):
user = User.objects.get(pk=self.user.pk)
assert user.email == 'new+{}'.format(self.old_email)
assert user.username == self.old_username
assert user.profile.name == 'Grown Up {}'.format(self.old_fullname)
assert user.profile.name == u'Grown Up {}'.format(self.old_fullname)
assert user.profile.country == 'PK'
# An email should still be sent because the email changed.

View File

@@ -53,8 +53,8 @@ class SAMLMetadataTest(SAMLTestCase):
self.enable_saml(
other_config_str=(
'{'
'"TECHNICAL_CONTACT": {"givenName": "Jane Tech", "emailAddress": "jane@example.com"},'
'"SUPPORT_CONTACT": {"givenName": "Joe Support", "emailAddress": "joe@example.com"}'
'"TECHNICAL_CONTACT": {"givenName": "Jane Tech", "emailAddress": "jane@example.com"},' # pylint: disable=unicode-format-string,line-too-long
'"SUPPORT_CONTACT": {"givenName": "Joe Support", "emailAddress": "joe@example.com"}' # pylint: disable=unicode-format-string,line-too-long
'}'
)
)

View File

@@ -220,7 +220,7 @@ class SAMLTestCase(TestCase):
error_mock = log_mock.error
idp_class = get_saml_idp_class('fake_idp_class_option')
error_mock.assert_called_once_with(
'%s is not a valid EdXSAMLIdentityProvider subclass; using EdXSAMLIdentityProvider base class.',
u'%s is not a valid EdXSAMLIdentityProvider subclass; using EdXSAMLIdentityProvider base class.',
'fake_idp_class_option'
)
self.assertIs(idp_class, EdXSAMLIdentityProvider)

View File

@@ -861,7 +861,6 @@ class MatlabTest(unittest.TestCase):
'response_data': {},
'describedby_html': 'aria-describedby="status_{id}"'.format(id=prob_id)
}
self.assertEqual(context, expected)
self.the_input.capa_system.render_template = DemoSystem().render_template
self.the_input.get_html() # Should not raise an exception

View File

@@ -40,12 +40,12 @@ class StudioApiFixture(object):
self.user = response.json()
if not self.user:
raise StudioApiLoginError('Auto-auth failed. Response was: {}'.format(self.user))
raise StudioApiLoginError(u'Auto-auth failed. Response was: {}'.format(self.user))
return session
else:
msg = 'Could not log in to use Studio restful API. Status code: {0}'.format(response.status_code)
msg = u'Could not log in to use Studio restful API. Status code: {0}'.format(response.status_code)
raise StudioApiLoginError(msg)
@lazy
@@ -123,14 +123,14 @@ class XBlockContainerFixture(StudioApiFixture):
)
if not response.ok:
msg = "Could not create {0}. Status was {1}".format(xblock_desc, response.status_code)
msg = u"Could not create {0}. Status was {1}".format(xblock_desc, response.status_code)
raise FixtureError(msg)
try:
loc = response.json().get('locator')
xblock_desc.locator = loc
except ValueError:
raise FixtureError("Could not decode JSON from '{0}'".format(response.content))
raise FixtureError(u"Could not decode JSON from '{0}'".format(response.content))
# Configure the XBlock
response = self.session.post(
@@ -142,7 +142,7 @@ class XBlockContainerFixture(StudioApiFixture):
if response.ok:
return loc
else:
raise FixtureError("Could not update {0}. Status code: {1}".format(xblock_desc, response.status_code))
raise FixtureError(u"Could not update {0}. Status code: {1}".format(xblock_desc, response.status_code))
def _update_xblock(self, locator, data):
"""
@@ -156,7 +156,7 @@ class XBlockContainerFixture(StudioApiFixture):
)
if not response.ok:
msg = "Could not update {} with data {}. Status was {}".format(locator, data, response.status_code)
msg = u"Could not update {} with data {}. Status was {}".format(locator, data, response.status_code)
raise FixtureError(msg)
def _encode_post_dict(self, post_dict):

View File

@@ -45,7 +45,7 @@ class CertificateConfigFixture(StudioApiFixture):
if not response.ok:
raise CertificateConfigFixtureError(
"Could not create certificate {0}. Status was {1}".format(
u"Could not create certificate {0}. Status was {1}".format(
json.dumps(self.certificates), response.status_code
)
)
@@ -64,7 +64,7 @@ class CertificateConfigFixture(StudioApiFixture):
if not response.ok:
raise CertificateConfigUpdateFixtureError(
"Could not update certificate {0}. Status was {1}".format(
u"Could not update certificate {0}. Status was {1}".format(
json.dumps(self.certificates), response.status_code
)
)

View File

@@ -46,7 +46,7 @@ class ConfigModelFixture(object):
if not response.ok:
raise ConfigModelFixtureError(
"Could not configure url '{}'. response: {} - {}".format(
u"Could not configure url '{}'. response: {} - {}".format(
self._api_base,
response,
response.content,
@@ -86,7 +86,7 @@ class ConfigModelFixture(object):
if response.ok:
# auto_auth returns information about the newly created user
# capture this so it can be used by by the testcases.
user_pattern = re.compile(r'Logged in user {0} \({1}\) with password {2} and user_id {3}'.format(
user_pattern = re.compile(ur'Logged in user {0} \({1}\) with password {2} and user_id {3}'.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:
@@ -95,5 +95,5 @@ class ConfigModelFixture(object):
return session
else:
msg = "Could not log in to use ConfigModel restful API. Status code: {0}".format(response.status_code)
msg = u"Could not log in to use ConfigModel restful API. Status code: {0}".format(response.status_code)
raise ConfigModelFixtureError(msg)

View File

@@ -74,7 +74,7 @@ class XBlockFixtureDesc(object):
Return a string representation of the description.
Useful for error messages.
"""
return dedent("""
return dedent(u"""
<XBlockFixtureDescriptor:
category={0},
data={1},
@@ -152,7 +152,7 @@ class CourseFixture(XBlockContainerFixture):
"""
String representation of the course fixture, useful for debugging.
"""
return "<CourseFixture: org='{org}', number='{number}', run='{run}'>".format(**self._course_dict)
return u"<CourseFixture: org='{org}', number='{number}', run='{run}'>".format(**self._course_dict)
def add_course_details(self, course_details):
"""
@@ -234,14 +234,14 @@ class CourseFixture(XBlockContainerFixture):
if not response.ok:
raise FixtureError(
"Could not retrieve course outline json. Status was {0}".format(
u"Could not retrieve course outline json. Status was {0}".format(
response.status_code))
try:
course_outline_json = response.json()
except ValueError:
raise FixtureError(
"Could not decode course outline as JSON: '{0}'".format(response)
u"Could not decode course outline as JSON: '{0}'".format(response)
)
return course_outline_json
@@ -289,18 +289,18 @@ class CourseFixture(XBlockContainerFixture):
except ValueError:
raise FixtureError(
"Could not parse response from course request as JSON: '{0}'".format(
u"Could not parse response from course request as JSON: '{0}'".format(
response.content))
# This will occur if the course identifier is not unique
if err is not None:
raise FixtureError("Could not create course {0}. Error message: '{1}'".format(self, err))
raise FixtureError(u"Could not create course {0}. Error message: '{1}'".format(self, err))
if response.ok:
self._course_key = response.json()['course_key']
else:
raise FixtureError(
"Could not create course {0}. Status was {1}\nResponse content was: {2}".format(
u"Could not create course {0}. Status was {1}\nResponse content was: {2}".format(
self._course_dict, response.status_code, response.content))
def _configure_course(self):
@@ -314,14 +314,14 @@ class CourseFixture(XBlockContainerFixture):
if not response.ok:
raise FixtureError(
"Could not retrieve course details. Status was {0}".format(
u"Could not retrieve course details. Status was {0}".format(
response.status_code))
try:
details = response.json()
except ValueError:
raise FixtureError(
"Could not decode course details as JSON: '{0}'".format(details)
u"Could not decode course details as JSON: '{0}'".format(details)
)
# Update the old details with our overrides
@@ -335,7 +335,7 @@ class CourseFixture(XBlockContainerFixture):
if not response.ok:
raise FixtureError(
"Could not update course details to '{0}' with {1}: Status was {2}.".format(
u"Could not update course details to '{0}' with {1}: Status was {2}.".format(
self._course_details, url, response.status_code))
def _install_course_handouts(self):
@@ -346,10 +346,10 @@ class CourseFixture(XBlockContainerFixture):
# Construct HTML with each of the handout links
handouts_li = [
'<li><a href="/static/{handout}">Example Handout</a></li>'.format(handout=handout)
u'<li><a href="/static/{handout}">Example Handout</a></li>'.format(handout=handout)
for handout in self._handouts
]
handouts_html = '<ol class="treeview-handoutsnav">{}</ol>'.format("".join(handouts_li))
handouts_html = u'<ol class="treeview-handoutsnav">{}</ol>'.format("".join(handouts_li))
# Update the course's handouts HTML
payload = json.dumps({
@@ -363,7 +363,7 @@ class CourseFixture(XBlockContainerFixture):
if not response.ok:
raise FixtureError(
"Could not update course handouts with {0}. Status was {1}".format(url, response.status_code))
u"Could not update course handouts with {0}. Status was {1}".format(url, response.status_code))
def _install_course_updates(self):
"""
@@ -380,7 +380,7 @@ class CourseFixture(XBlockContainerFixture):
if not response.ok:
raise FixtureError(
"Could not add update to course: {0} with {1}. Status was {2}".format(
u"Could not add update to course: {0} with {1}. Status was {2}".format(
update, url, response.status_code))
def _upload_assets(self):
@@ -406,7 +406,7 @@ class CourseFixture(XBlockContainerFixture):
upload_response = self.session.post(url, files=files, headers=headers)
if not upload_response.ok:
raise FixtureError('Could not upload {asset_name} with {url}. Status code: {code}'.format(
raise FixtureError(u'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):
@@ -421,7 +421,7 @@ class CourseFixture(XBlockContainerFixture):
if not response.ok:
raise FixtureError(
"Could not add book to course: {0} with {1}. Status was {2}".format(
u"Could not add book to course: {0} with {1}. Status was {2}".format(
book, url, response.status_code))
def _add_advanced_settings(self):
@@ -438,7 +438,7 @@ class CourseFixture(XBlockContainerFixture):
if not response.ok:
raise FixtureError(
"Could not update advanced details to '{0}' with {1}: Status was {2}.".format(
u"Could not update advanced details to '{0}' with {1}: Status was {2}.".format(
self._advanced_settings, url, response.status_code))
def _create_xblock_children(self, parent_loc, xblock_descriptions):

View File

@@ -57,7 +57,7 @@ class EdxNotesFixture(object):
if not response.ok:
raise EdxNotesFixtureError(
"Could not create notes {0}. Status was {1}".format(
u"Could not create notes {0}. Status was {1}".format(
json.dumps(self.notes), response.status_code
)
)
@@ -73,7 +73,7 @@ class EdxNotesFixture(object):
if not response.ok:
raise EdxNotesFixtureError(
"Could not cleanup EdxNotes service {0}. Status was {1}".format(
u"Could not cleanup EdxNotes service {0}. Status was {1}".format(
json.dumps(self.notes), response.status_code
)
)

View File

@@ -35,7 +35,7 @@ class LibraryFixture(XBlockContainerFixture):
"""
String representation of the library fixture, useful for debugging.
"""
return "<LibraryFixture: org='{org}', number='{number}'>".format(**self.library_info)
return u"<LibraryFixture: org='{org}', number='{number}'>".format(**self.library_info)
def install(self):
"""
@@ -82,7 +82,7 @@ class LibraryFixture(XBlockContainerFixture):
err_msg = response.json().get('ErrMsg')
except ValueError:
err_msg = "Unknown Error"
raise FixtureError("Could not create library {}. Status was {}, error was: {}".format(
raise FixtureError(u"Could not create library {}. Status was {}, error was: {}".format(
self.library_info, response.status_code, err_msg
))

View File

@@ -46,5 +46,5 @@ class XQueueResponseFixture(object):
if not response.ok:
raise XQueueResponseFixtureError(
"Could not configure XQueue stub for queue '{1}'. Status code: {2}".format(
u"Could not configure XQueue stub for queue '{1}'. Status code: {2}".format(
self._pattern, self._response_dict))

View File

@@ -70,14 +70,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-field-{} .u-field-order-{}'.format(field_id, field_name))
query = self.q(css=u'.u-field-{} .u-field-order-{}'.format(field_id, 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-field-{} .u-field-{}'.format(field_id, 'link')).visible
return self.q(css=u'.u-field-{} .u-field-{}'.format(field_id, 'link')).visible
@property
def is_delete_button_visible(self):

View File

@@ -40,7 +40,7 @@ class AnnotationComponentPage(PageObject):
"""
Return css selector for current active problem with sub_selector.
"""
return 'div[data-problem-id="{}"] {}'.format(
return u'div[data-problem-id="{}"] {}'.format(
self.q(css='.vert-{}'.format(self.active_problem + 1)).map(
lambda el: el.get_attribute('data-id')).results[0],
sub_selector,

View File

@@ -161,7 +161,7 @@ class CourseOutlinePage(PageObject):
try:
subsection_index = subsection_titles.index(text_type(subsection_title))
except ValueError:
raise ValueError("Could not find subsection '{0}' in section '{1}'".format(
raise ValueError(u"Could not find subsection '{0}' in section '{1}'".format(
subsection_title, section_title
))
@@ -189,11 +189,11 @@ class CourseOutlinePage(PageObject):
try:
section_title = self._section_titles()[section_index]
except IndexError:
raise ValueError("Section index '{0}' is out of range.".format(section_index))
raise ValueError(u"Section index '{0}' is out of range.".format(section_index))
try:
subsection_title = self._subsection_titles(section_index)[subsection_index]
except IndexError:
raise ValueError("Subsection index '{0}' in section index '{1}' is out of range.".format(
raise ValueError(u"Subsection index '{0}' in section index '{1}' is out of range.".format(
subsection_index, section_index
))
@@ -206,7 +206,7 @@ class CourseOutlinePage(PageObject):
try:
section_index = self._section_titles().index(section_title)
except ValueError:
raise ValueError("Could not find section '{0}'".format(section_title))
raise ValueError(u"Could not find section '{0}'".format(section_title))
return section_index
@@ -253,7 +253,7 @@ class CourseOutlinePage(PageObject):
self.wait_for(
promise_check_func=lambda: courseware_page.nav.is_on_section(
section_title, subsection_title),
description="Waiting for course page with section '{0}' and subsection '{1}'".format(
description=u"Waiting for course page with section '{0}' and subsection '{1}'".format(
section_title, subsection_title)
)

View File

@@ -95,7 +95,7 @@ class CoursewarePage(CoursePage, CompletionOnViewMixin):
# When Student Notes feature is enabled, it looks for the content inside
# `.edx-notes-wrapper-content` element (Otherwise, you will get an
# additional html related to Student Notes).
element = self.q(css='{} .edx-notes-wrapper-content'.format(self.xblock_component_selector))
element = self.q(css=u'{} .edx-notes-wrapper-content'.format(self.xblock_component_selector))
if element.first:
return element.attrs('innerHTML')[index].strip()
else:
@@ -110,8 +110,8 @@ class CoursewarePage(CoursePage, CompletionOnViewMixin):
for index, tab in enumerate(self.q(css='#sequence-list > li')):
ActionChains(self.browser).move_to_element(tab).perform()
self.wait_for_element_visibility(
'#tab_{index} > .sequence-tooltip'.format(index=index),
'Tab {index} should appear'.format(index=index)
u'#tab_{index} > .sequence-tooltip'.format(index=index),
u'Tab {index} should appear'.format(index=index)
)
@property
@@ -142,7 +142,7 @@ class CoursewarePage(CoursePage, CompletionOnViewMixin):
except IndexError:
return False
sequential_position_css = '#sequence-list #tab_{0}'.format(sequential_position - 1)
sequential_position_css = u'#sequence-list #tab_{0}'.format(sequential_position - 1)
self.q(css=sequential_position_css).first.click()
EmptyPromise(is_at_new_position, "Position navigation fulfilled").fulfill()
@@ -196,7 +196,7 @@ class CoursewarePage(CoursePage, CompletionOnViewMixin):
return False
self.q(
css='.{} > .sequence-nav-button.{}'.format(top_or_bottom_class, next_or_previous_class)
css=u'.{} > .sequence-nav-button.{}'.format(top_or_bottom_class, next_or_previous_class)
).first.click()
EmptyPromise(is_at_new_tab_id, "Button navigation fulfilled").fulfill()
@@ -348,7 +348,7 @@ class CoursewarePage(CoursePage, CompletionOnViewMixin):
answer_word(str): An answer words to be filled in the field
"""
self.wait_for_element_visibility('.input-cloud', "Word cloud fields are visible")
css = '.input_cloud_section label:nth-child({}) .input-cloud'
css = u'.input_cloud_section label:nth-child({}) .input-cloud'
for index in range(1, len(self.q(css='.input-cloud')) + 1):
self.q(css=css.format(index)).fill(answer_word + str(index))
@@ -461,7 +461,7 @@ class CourseNavPage(PageObject):
for sec_index, sec_title in enumerate(section_titles):
if len(section_titles) < 1:
self.warning("Could not find subsections for '{0}'".format(sec_title))
self.warning(u"Could not find subsections for '{0}'".format(sec_title))
else:
# Add one to convert list index (starts at 0) to CSS index (starts at 1)
nav_dict[sec_title] = self._subsection_titles(sec_index + 1)
@@ -498,25 +498,25 @@ class CourseNavPage(PageObject):
try:
sec_index = self._section_titles().index(section_title)
except ValueError:
self.warning("Could not find section '{0}'".format(section_title))
self.warning(u"Could not find section '{0}'".format(section_title))
return
# Click the section to ensure it's open (no harm in clicking twice if it's already open)
# Add one to convert from list index to CSS index
section_css = '.course-navigation .chapter:nth-of-type({0})'.format(sec_index + 1)
section_css = u'.course-navigation .chapter:nth-of-type({0})'.format(sec_index + 1)
self.q(css=section_css).first.click()
# Get the subsection by index
try:
subsec_index = self._subsection_titles(sec_index + 1).index(subsection_title)
except ValueError:
msg = "Could not find subsection '{0}' in section '{1}'".format(subsection_title, section_title)
msg = u"Could not find subsection '{0}' in section '{1}'".format(subsection_title, section_title)
self.warning(msg)
return
# Convert list indices (start at zero) to CSS indices (start at 1)
subsection_css = (
".course-navigation .chapter-content-container:nth-of-type({0}) "
u".course-navigation .chapter-content-container:nth-of-type({0}) "
".menu-item:nth-of-type({1})"
).format(sec_index + 1, subsec_index + 1)
@@ -536,7 +536,7 @@ class CourseNavPage(PageObject):
seq_index = all_items.index(vertical_title)
except ValueError:
msg = "Could not find sequential '{0}'. Available sequentials: [{1}]".format(
msg = u"Could not find sequential '{0}'. Available sequentials: [{1}]".format(
vertical_title, ", ".join(all_items)
)
self.warning(msg)
@@ -567,7 +567,7 @@ class CourseNavPage(PageObject):
# Retrieve the subsection title for the section
# Add one to the list index to get the CSS index, which starts at one
subsection_css = (
".course-navigation .chapter-content-container:nth-of-type({0}) "
u".course-navigation .chapter-content-container:nth-of-type({0}) "
".menu-item a p:nth-of-type(1)"
).format(section_index)
@@ -587,7 +587,7 @@ class CourseNavPage(PageObject):
Return a `Promise` that is fulfilled when the user is on
the correct section and subsection.
"""
desc = "currently at section '{0}' and subsection '{1}'".format(section_title, subsection_title)
desc = u"currently at section '{0}' and subsection '{1}'".format(section_title, subsection_title)
return EmptyPromise(
lambda: self.is_on_section(section_title, subsection_title), desc
)

View File

@@ -75,7 +75,7 @@ class DashboardPage(PageObject):
# and the other being the enrollment mode.
enrollment_mode = course_listing[0].get_attribute('class').split('course ')[1]
else:
raise Exception("No course named {} was found on the dashboard".format(course_name))
raise Exception(u"No course named {} was found on the dashboard".format(course_name))
return enrollment_mode
@@ -103,7 +103,7 @@ class DashboardPage(PageObject):
upgrade_page.wait_for_page()
else:
raise Exception("No enrollment for {} is visible on the dashboard.".format(course_name))
raise Exception(u"No enrollment for {} is visible on the dashboard.".format(course_name))
def view_course(self, course_id):
"""
@@ -114,7 +114,7 @@ class DashboardPage(PageObject):
if link_css is not None:
self.q(css=link_css).first.click()
else:
msg = "No links found for course {0}".format(course_id)
msg = u"No links found for course {0}".format(course_id)
self.warning(msg)
def _link_css(self, course_id):
@@ -156,7 +156,7 @@ class DashboardPage(PageObject):
}
else:
msg = "No links found for course {0}".format(course_id)
msg = u"No links found for course {0}".format(course_id)
self.warning(msg)
def get_course_actions_link_css(self, course_id):

View File

@@ -178,7 +178,7 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
@wait_for_js
def is_response_editor_visible(self, response_id):
"""Returns true if the response editor is present, false otherwise"""
return self.is_element_visible(".response_{} .edit-post-body".format(response_id))
return self.is_element_visible(u".response_{} .edit-post-body".format(response_id))
@wait_for_js
def is_discussion_body_visible(self):
@@ -201,27 +201,27 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
def is_response_visible(self, comment_id):
"""Returns true if the response is viewable onscreen"""
self.wait_for_ajax()
return self.is_element_visible(".response_{} .response-body".format(comment_id))
return self.is_element_visible(u".response_{} .response-body".format(comment_id))
def is_response_editable(self, response_id):
"""Returns true if the edit response button is present, false otherwise"""
with self.secondary_action_menu_open(".response_{} .discussion-response".format(response_id)):
return self.is_element_visible(".response_{} .discussion-response .action-edit".format(response_id))
with self.secondary_action_menu_open(u".response_{} .discussion-response".format(response_id)):
return self.is_element_visible(u".response_{} .discussion-response .action-edit".format(response_id))
def is_response_deletable(self, response_id):
"""
Returns true if the delete response button is present, false otherwise
"""
with self.secondary_action_menu_open(".response_{} .discussion-response".format(response_id)):
return self.is_element_visible(".response_{} .discussion-response .action-delete".format(response_id))
with self.secondary_action_menu_open(u".response_{} .discussion-response".format(response_id)):
return self.is_element_visible(u".response_{} .discussion-response .action-delete".format(response_id))
def get_response_body(self, response_id):
return self._get_element_text(".response_{} .response-body".format(response_id))
def start_response_edit(self, response_id):
"""Click the edit button for the response, loading the editing view"""
with self.secondary_action_menu_open(".response_{} .discussion-response".format(response_id)):
self._find_within(".response_{} .discussion-response .action-edit".format(response_id)).first.click()
with self.secondary_action_menu_open(u".response_{} .discussion-response".format(response_id)):
self._find_within(u".response_{} .discussion-response .action-edit".format(response_id)).first.click()
EmptyPromise(
lambda: self.is_response_editor_visible(response_id),
"Response edit started"
@@ -237,26 +237,26 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
vote_count_element = self.browser.find_element_by_css_selector(vote_count_css)
# To get the vote count, one must hover over the element first.
hover(self.browser, vote_count_element)
return self._get_element_text(".response_{} .discussion-response .action-vote .vote-count".format(response_id))
return self._get_element_text(u".response_{} .discussion-response .action-vote .vote-count".format(response_id))
def vote_response(self, response_id):
current_count = self.get_response_vote_count(response_id)
self._find_within(".response_{} .discussion-response .action-vote".format(response_id)).first.click()
self._find_within(u".response_{} .discussion-response .action-vote".format(response_id)).first.click()
self.wait_for(
lambda: current_count != self.get_response_vote_count(response_id),
description="Vote updated for {response_id}".format(response_id=response_id)
description=u"Vote updated for {response_id}".format(response_id=response_id)
)
def cannot_vote_response(self, response_id):
"""Assert that the voting button is not visible on this response"""
return not self.is_element_visible(".response_{} .discussion-response .action-vote".format(response_id))
return not self.is_element_visible(u".response_{} .discussion-response .action-vote".format(response_id))
def is_response_reported(self, response_id):
return self.is_element_visible(".response_{} .discussion-response .post-label-reported".format(response_id))
def report_response(self, response_id):
with self.secondary_action_menu_open(".response_{} .discussion-response".format(response_id)):
self._find_within(".response_{} .discussion-response .action-report".format(response_id)).first.click()
self._find_within(u".response_{} .discussion-response .action-report".format(response_id)).first.click()
self.wait_for_ajax()
EmptyPromise(
lambda: self.is_response_reported(response_id),
@@ -265,7 +265,7 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
def cannot_report_response(self, response_id):
"""Assert that the reporting button is not visible on this response"""
return not self.is_element_visible(".response_{} .discussion-response .action-report".format(response_id))
return not self.is_element_visible(u".response_{} .discussion-response .action-report".format(response_id))
def is_response_endorsed(self, response_id):
return "endorsed" in self._get_element_text(".response_{} .discussion-response .posted-details".format(response_id))
@@ -280,7 +280,7 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
def set_response_editor_value(self, response_id, new_body):
"""Replace the contents of the response editor"""
self._find_within(".response_{} .discussion-response .wmd-input".format(response_id)).fill(new_body)
self._find_within(u".response_{} .discussion-response .wmd-input".format(response_id)).fill(new_body)
def verify_link_editor_error_messages_shown(self):
"""
@@ -322,7 +322,7 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
was successfully updated, False otherwise.
"""
self._find_within(
".response_{} .discussion-response .post-update".format(
u".response_{} .discussion-response .post-update".format(
response_id
)
).first.click()
@@ -337,13 +337,13 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
def is_show_comments_visible(self, response_id):
"""Returns true if the "show comments" link is visible for a response"""
return self.is_element_visible(".response_{} .action-show-comments".format(response_id))
return self.is_element_visible(u".response_{} .action-show-comments".format(response_id))
def show_comments(self, response_id):
"""Click the "show comments" link for a response"""
self._find_within(".response_{} .action-show-comments".format(response_id)).first.click()
self._find_within(u".response_{} .action-show-comments".format(response_id)).first.click()
EmptyPromise(
lambda: self.is_element_visible(".response_{} .comments".format(response_id)),
lambda: self.is_element_visible(u".response_{} .comments".format(response_id)),
"Comments shown"
).fulfill()
@@ -353,7 +353,7 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
def is_comment_visible(self, comment_id):
"""Returns true if the comment is viewable onscreen"""
return self.is_element_visible("#comment_{} .response-body".format(comment_id))
return self.is_element_visible(u"#comment_{} .response-body".format(comment_id))
def get_comment_body(self, comment_id):
return self._get_element_text("#comment_{} .response-body".format(comment_id))
@@ -361,12 +361,12 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
def is_comment_deletable(self, comment_id):
"""Returns true if the delete comment button is present, false otherwise"""
with self.secondary_action_menu_open("#comment_{}".format(comment_id)):
return self.is_element_visible("#comment_{} .action-delete".format(comment_id))
return self.is_element_visible(u"#comment_{} .action-delete".format(comment_id))
def delete_comment(self, comment_id):
with self.handle_alert():
with self.secondary_action_menu_open("#comment_{}".format(comment_id)):
self._find_within("#comment_{} .action-delete".format(comment_id)).first.click()
self._find_within(u"#comment_{} .action-delete".format(comment_id)).first.click()
EmptyPromise(
lambda: not self.is_comment_visible(comment_id),
"Deleted comment was removed"
@@ -375,7 +375,7 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
def is_comment_editable(self, comment_id):
"""Returns true if the edit comment button is present, false otherwise"""
with self.secondary_action_menu_open("#comment_{}".format(comment_id)):
return self.is_element_visible("#comment_{} .action-edit".format(comment_id))
return self.is_element_visible(u"#comment_{} .action-edit".format(comment_id))
def is_comment_editor_visible(self, comment_id):
"""Returns true if the comment editor is present, false otherwise"""
@@ -388,7 +388,7 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
"""Click the edit button for the comment, loading the editing view"""
old_body = self.get_comment_body(comment_id)
with self.secondary_action_menu_open("#comment_{}".format(comment_id)):
self._find_within("#comment_{} .action-edit".format(comment_id)).first.click()
self._find_within(u"#comment_{} .action-edit".format(comment_id)).first.click()
EmptyPromise(
lambda: (
self.is_comment_editor_visible(comment_id) and
@@ -400,11 +400,11 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
def set_comment_editor_value(self, comment_id, new_body):
"""Replace the contents of the comment editor"""
self._find_within("#comment_{} .wmd-input".format(comment_id)).fill(new_body)
self._find_within(u"#comment_{} .wmd-input".format(comment_id)).fill(new_body)
def submit_comment_edit(self, comment_id, new_comment_body):
"""Click the submit button on the comment editor"""
self._find_within("#comment_{} .post-update".format(comment_id)).first.click()
self._find_within(u"#comment_{} .post-update".format(comment_id)).first.click()
self.wait_for_ajax()
EmptyPromise(
lambda: (
@@ -417,7 +417,7 @@ class DiscussionThreadPage(PageObject, DiscussionPageMixin):
def cancel_comment_edit(self, comment_id, original_body):
"""Click the cancel button on the comment editor"""
self._find_within("#comment_{} .post-cancel".format(comment_id)).first.click()
self._find_within(u"#comment_{} .post-cancel".format(comment_id)).first.click()
EmptyPromise(
lambda: (
not self.is_comment_editor_visible(comment_id) and
@@ -459,7 +459,7 @@ class DiscussionSortPreferencePage(CoursePage):
"""
Change the option of sorting by clicking on new option.
"""
self.q(css=".forum-nav-sort-control option[value='{0}']".format(sort_by)).click()
self.q(css=u".forum-nav-sort-control option[value='{0}']".format(sort_by)).click()
# Click initiates an ajax call, waiting for it to complete
self.wait_for_ajax()
@@ -475,7 +475,7 @@ class DiscussionTabSingleThreadPage(CoursePage):
super(DiscussionTabSingleThreadPage, self).__init__(browser, course_id)
self.thread_page = DiscussionThreadPage(
browser,
"body.discussion .discussion-article[data-id='{thread_id}']".format(thread_id=thread_id)
u"body.discussion .discussion-article[data-id='{thread_id}']".format(thread_id=thread_id)
)
self.url_path = "discussion/forum/{discussion_id}/threads/{thread_id}".format(
discussion_id=discussion_id, thread_id=thread_id
@@ -526,7 +526,7 @@ class InlineDiscussionPage(PageObject, DiscussionPageMixin):
def __init__(self, browser, discussion_id):
super(InlineDiscussionPage, self).__init__(browser)
self.root_selector = (
".discussion-module[data-discussion-id='{discussion_id}'] ".format(
u".discussion-module[data-discussion-id='{discussion_id}'] ".format(
discussion_id=discussion_id
)
)
@@ -561,8 +561,8 @@ class InlineDiscussionPage(PageObject, DiscussionPageMixin):
def click_element(self, selector):
self.wait_for_element_presence(
"{discussion} {selector}".format(discussion=self.root_selector, selector=selector),
"{selector} is visible".format(selector=selector)
u"{discussion} {selector}".format(discussion=self.root_selector, selector=selector),
u"{selector} is visible".format(selector=selector)
)
self._find_within(selector).click()
@@ -582,7 +582,7 @@ class InlineDiscussionPage(PageObject, DiscussionPageMixin):
Clicks the link for the specified thread to show the detailed view.
"""
self.wait_for_element_presence('.forum-nav-thread-link', 'Thread list has loaded')
thread_selector = ".forum-nav-thread[data-id='{thread_id}'] .forum-nav-thread-link".format(thread_id=thread_id)
thread_selector = u".forum-nav-thread[data-id='{thread_id}'] .forum-nav-thread-link".format(thread_id=thread_id)
self._find_within(thread_selector).first.click()
self.thread_page = InlineDiscussionThreadPage(self.browser, thread_id) # pylint: disable=attribute-defined-outside-init
self.thread_page.wait_for_page()
@@ -595,7 +595,7 @@ class InlineDiscussionThreadPage(DiscussionThreadPage):
def __init__(self, browser, thread_id):
super(InlineDiscussionThreadPage, self).__init__(
browser,
".discussion-module .discussion-article[data-id='{thread_id}']".format(thread_id=thread_id)
u".discussion-module .discussion-article[data-id='{thread_id}']".format(thread_id=thread_id)
)
def is_thread_anonymous(self):
@@ -699,7 +699,7 @@ class DiscussionTabHomePage(CoursePage, DiscussionPageMixin):
return self.q(css=".search-alert").filter(lambda elem: text in elem.text)
for alert_id in _match_messages(text).attrs("id"):
self.q(css="{}#{} .dismiss".format(self.ALERT_SELECTOR, alert_id)).click()
self.q(css=u"{}#{} .dismiss".format(self.ALERT_SELECTOR, alert_id)).click()
EmptyPromise(
lambda: _match_messages(text).results == [],
"waiting for dismissed alerts to disappear"

View File

@@ -22,7 +22,7 @@ class NoteChild(PageObject):
"""
Return `selector`, but limited to this particular `NoteChild` context
"""
return "{}#{} {}".format(
return u"{}#{} {}".format(
self.BODY_SELECTOR,
self.item_id,
selector,
@@ -101,7 +101,7 @@ class EdxNotesTagsGroup(NoteChild, EdxNotesGroupMixin):
top_script = "return " + title_selector + ".getBoundingClientRect().top;"
EmptyPromise(
lambda: 8 < self.browser.execute_script(top_script) < 12,
"Expected tag title '{}' to scroll to top, but was at location {}".format(
u"Expected tag title '{}' to scroll to top, but was at location {}".format(
self.title, self.browser.execute_script(top_script)
)
).fulfill()
@@ -177,7 +177,7 @@ class EdxNotesPageView(PageObject):
try:
return self.wait_for_page()
except BrokenPromise:
raise PageLoadError("Timed out waiting to load page '{!r}'".format(self))
raise PageLoadError(u"Timed out waiting to load page '{!r}'".format(self))
def is_browser_on_page(self):
return all([
@@ -191,13 +191,13 @@ class EdxNotesPageView(PageObject):
"""
Indicates if tab is closable or not.
"""
return self.q(css="{} .action-close".format(self.TAB_SELECTOR)).present
return self.q(css=u"{} .action-close".format(self.TAB_SELECTOR)).present
def close(self):
"""
Closes the tab.
"""
self.q(css="{} .action-close".format(self.TAB_SELECTOR)).first.click()
self.q(css=u"{} .action-close".format(self.TAB_SELECTOR)).first.click()
@property
def children(self):
@@ -652,7 +652,7 @@ class EdxNoteHighlight(NoteChild):
label_exists = False
EmptyPromise(
lambda: len(self.q(css=self._bounded_selector("li.annotator-item > label.sr"))) > sr_index,
"Expected more than '{}' sr labels".format(sr_index)
u"Expected more than '{}' sr labels".format(sr_index)
).fulfill()
annotator_field_label = self.q(css=self._bounded_selector("li.annotator-item > label.sr"))[sr_index]
for_attrib_correct = annotator_field_label.get_attribute("for") == "annotator-field-" + str(field_index)

View File

@@ -16,7 +16,7 @@ class FieldsMixin(object):
"""
Return field with field_id.
"""
query = self.q(css='.u-field-{}'.format(field_id))
query = self.q(css=u'.u-field-{}'.format(field_id))
return query.text[0] if query.present else None
def wait_for_field(self, field_id):
@@ -25,7 +25,7 @@ class FieldsMixin(object):
"""
EmptyPromise(
lambda: self.field(field_id) is not None,
"Field with id \"{0}\" is in DOM.".format(field_id)
u"Field with id \"{0}\" is in DOM.".format(field_id)
).fulfill()
def mode_for_field(self, field_id):
@@ -37,7 +37,7 @@ class FieldsMixin(object):
"""
self.wait_for_field(field_id)
query = self.q(css='.u-field-{}'.format(field_id))
query = self.q(css=u'.u-field-{}'.format(field_id))
if not query.present:
return None
@@ -59,7 +59,7 @@ class FieldsMixin(object):
"""
self.wait_for_field(field_id)
query = self.q(css='.u-field-{} .u-field-icon'.format(field_id))
query = self.q(css=u'.u-field-{} .u-field-icon'.format(field_id))
return query.present and icon_id in query.attrs('class')[0].split()
def title_for_field(self, field_id):
@@ -68,7 +68,7 @@ class FieldsMixin(object):
"""
self.wait_for_field(field_id)
query = self.q(css='.u-field-{} .u-field-title'.format(field_id))
query = self.q(css=u'.u-field-{} .u-field-title'.format(field_id))
return query.text[0] if query.present else None
def message_for_field(self, field_id):
@@ -77,7 +77,7 @@ class FieldsMixin(object):
"""
self.wait_for_field(field_id)
query = self.q(css='.u-field-{} .u-field-message'.format(field_id))
query = self.q(css=u'.u-field-{} .u-field-message'.format(field_id))
return query.text[0] if query.present else None
def message_for_textarea_field(self, field_id):
@@ -86,7 +86,7 @@ class FieldsMixin(object):
"""
self.wait_for_field(field_id)
query = self.q(css='.u-field-{} .u-field-message-help'.format(field_id))
query = self.q(css=u'.u-field-{} .u-field-message-help'.format(field_id))
return query.text[0] if query.present else None
def wait_for_message(self, field_id, message):
@@ -95,7 +95,7 @@ class FieldsMixin(object):
"""
EmptyPromise(
lambda: message in (self.message_for_field(field_id) or ''),
"Messsage \"{0}\" is visible.".format(message)
u"Messsage \"{0}\" is visible.".format(message)
).fulfill()
def indicator_for_field(self, field_id):
@@ -104,7 +104,7 @@ class FieldsMixin(object):
"""
self.wait_for_field(field_id)
query = self.q(css='.u-field-{} .u-field-message .fa'.format(field_id))
query = self.q(css=u'.u-field-{} .u-field-message .fa'.format(field_id))
return [
class_name for class_name
in query.attrs('class')[0].split(' ')
@@ -117,14 +117,14 @@ class FieldsMixin(object):
"""
EmptyPromise(
lambda: indicator == self.indicator_for_field(field_id),
"Indicator \"{0}\" is visible.".format(self.indicator_for_field(field_id))
u"Indicator \"{0}\" 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-field-{}'.format(field_id))
query = self.q(css=u'.u-field-{}'.format(field_id))
if not query.present:
return None
@@ -137,7 +137,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-field-{}'.format(field_id)).first.click()
self.q(css=u'.u-field-{}'.format(field_id)).first.click()
def value_for_readonly_field(self, field_id):
"""
@@ -145,7 +145,7 @@ class FieldsMixin(object):
"""
self.wait_for_field(field_id)
query = self.q(css='.u-field-{} .u-field-value'.format(field_id))
query = self.q(css=u'.u-field-{} .u-field-value'.format(field_id))
if not query.present:
return None
@@ -157,7 +157,7 @@ class FieldsMixin(object):
"""
self.wait_for_field(field_id)
query = self.q(css='.u-field-{} input'.format(field_id))
query = self.q(css=u'.u-field-{} input'.format(field_id))
if not query.present:
return None
@@ -176,7 +176,7 @@ class FieldsMixin(object):
self.wait_for_field(field_id)
self.make_field_editable(field_id)
field_selector = '.u-field-{} textarea'.format(field_id)
field_selector = u'.u-field-{} textarea'.format(field_id)
self.wait_for_element_presence(field_selector, 'Editable textarea is present.')
query = self.q(css=field_selector)
@@ -190,7 +190,7 @@ class FieldsMixin(object):
self.wait_for_field(field_id)
self.wait_for_ajax()
return self.q(css='.u-field-{} .u-field-value .u-field-value-readonly'.format(field_id)).text[0]
return self.q(css=u'.u-field-{} .u-field-value .u-field-value-readonly'.format(field_id)).text[0]
def value_for_dropdown_field(self, field_id, value=None, focus_out=False):
"""
@@ -200,7 +200,7 @@ class FieldsMixin(object):
self.make_field_editable(field_id)
query = self.q(css='.u-field-{} select'.format(field_id))
query = self.q(css=u'.u-field-{} select'.format(field_id))
if not query.present:
return None
@@ -218,7 +218,7 @@ class FieldsMixin(object):
"""
self.wait_for_field(field_id)
query = self.q(css='.u-field-link-title-{}'.format(field_id))
query = self.q(css=u'.u-field-link-title-{}'.format(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 +227,7 @@ class FieldsMixin(object):
"""
return EmptyPromise(
lambda: self.link_title_for_link_field(field_id) == expected_title,
"Link field with link title \"{0}\" is visible.".format(expected_title)
u"Link field with link title \"{0}\" is visible.".format(expected_title)
).fulfill()
def click_on_link_in_link_field(self, field_id, field_type='a'):
@@ -236,7 +236,7 @@ class FieldsMixin(object):
"""
self.wait_for_field(field_id)
query = self.q(css='.u-field-{} {}'.format(field_id, field_type))
query = self.q(css=u'.u-field-{} {}'.format(field_id, field_type))
if query.present:
query.first.click()
@@ -244,5 +244,5 @@ class FieldsMixin(object):
"""
Returns bool based on the highlighted border for field.
"""
query = self.q(css='.u-field-{}.error'.format(field_id))
query = self.q(css=u'.u-field-{}.error'.format(field_id))
return True if query.present else False

View File

@@ -162,7 +162,7 @@ class BulkEmailPage(PageObject):
"""
Return `selector`, but limited to the bulk-email context.
"""
return '.send-email {}'.format(selector)
return u'.send-email {}'.format(selector)
def _select_recipient(self, recipient):
"""
@@ -302,7 +302,7 @@ class CohortManagementSection(PageObject):
"""
Return `selector`, but limited to the cohort management context.
"""
return '.cohort-management {}'.format(selector)
return u'.cohort-management {}'.format(selector)
def _get_cohort_options(self):
"""
@@ -708,13 +708,13 @@ class DiscussionManagementSection(PageObject):
"""
Return `selector`, but limited to the divided discussion management context.
"""
return '.discussions-management {}'.format(selector)
return u'.discussions-management {}'.format(selector)
def is_save_button_disabled(self, key):
"""
Returns the status for form's save button, enabled or disabled.
"""
save_button_css = '%s %s' % (self.discussion_form_selectors[key], '.action-save')
save_button_css = u'%s %s' % (self.discussion_form_selectors[key], '.action-save')
disabled = self.q(css=self._bounded_selector(save_button_css)).attrs('disabled')
return disabled[0] == 'true'
@@ -729,7 +729,7 @@ class DiscussionManagementSection(PageObject):
"""
Returns the text of discussion topic headings if it exists, otherwise return False.
"""
form_heading_css = '%s %s' % (self.discussion_form_selectors[key], '.subsection-title')
form_heading_css = u'%s %s' % (self.discussion_form_selectors[key], '.subsection-title')
discussion_heading = self.q(css=self._bounded_selector(form_heading_css))
if len(discussion_heading) == 0:
@@ -753,7 +753,7 @@ class DiscussionManagementSection(PageObject):
"""
Saves the discussion topics.
"""
save_button_css = '%s %s' % (self.discussion_form_selectors[key], '.action-save')
save_button_css = u'%s %s' % (self.discussion_form_selectors[key], '.action-save')
self.q(css=self._bounded_selector(save_button_css)).first.click()
def always_inline_discussion_selected(self):
@@ -791,7 +791,7 @@ class DiscussionManagementSection(PageObject):
"""
Returns the message related to modifying discussion topics.
"""
title_css = "%s .message-%s .message-title" % (self.discussion_form_selectors[key], msg_type)
title_css = u"%s .message-%s .message-title" % (self.discussion_form_selectors[key], msg_type)
EmptyPromise(
lambda: self.q(css=self._bounded_selector(title_css)),
@@ -871,8 +871,8 @@ class MembershipPageAutoEnrollSection(PageObject):
MembershipPageAutoEnrollSection.NOTIFICATION_ERROR
Returns True if a {section_type} notification is displayed.
"""
notification_selector = '.auto_enroll_csv .results .message-%s' % section_type
self.wait_for_element_presence(notification_selector, "%s Notification" % section_type.title())
notification_selector = u'.auto_enroll_csv .results .message-%s' % section_type
self.wait_for_element_presence(notification_selector, u"%s Notification" % section_type.title())
return self.q(css=notification_selector).is_present()
def first_notification_message(self, section_type):
@@ -881,8 +881,8 @@ class MembershipPageAutoEnrollSection(PageObject):
MembershipPageAutoEnrollSection.NOTIFICATION_ERROR
Returns the first message from the list of messages in the {section_type} section.
"""
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())
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())
return self.q(css=error_message_selector).text[0]
def upload_correct_csv_file(self):
@@ -915,8 +915,8 @@ class MembershipPageAutoEnrollSection(PageObject):
"""
Fill in the form with the provided email and submit it.
"""
email_selector = "{} textarea".format(self.batch_enrollment_selector)
enrollment_button = "{} .enrollment-button[data-action='enroll']".format(self.batch_enrollment_selector)
email_selector = u"{} textarea".format(self.batch_enrollment_selector)
enrollment_button = u"{} .enrollment-button[data-action='enroll']".format(self.batch_enrollment_selector)
# Fill the email addresses after the email selector is visible.
self.wait_for_element_visibility(email_selector, 'Email field is visible')
@@ -932,9 +932,9 @@ class MembershipPageAutoEnrollSection(PageObject):
"""
Check notification div is visible and have message.
"""
notification_selector = '{} .request-response'.format(self.batch_enrollment_selector)
notification_selector = u'{} .request-response'.format(self.batch_enrollment_selector)
self.wait_for_element_visibility(notification_selector, 'Notification div is visible')
return self.q(css="{} h3".format(notification_selector)).text
return self.q(css=u"{} h3".format(notification_selector)).text
class MembershipPageBetaTesterSection(PageObject):
@@ -953,8 +953,8 @@ class MembershipPageBetaTesterSection(PageObject):
"""
Fill in the form with the provided username and submit it.
"""
username_selector = "{} textarea".format(self.batch_beta_tester_selector)
enrollment_button = "{} .enrollment-button[data-action='add']".format(self.batch_beta_tester_selector)
username_selector = u"{} textarea".format(self.batch_beta_tester_selector)
enrollment_button = u"{} .enrollment-button[data-action='add']".format(self.batch_beta_tester_selector)
# Fill the username after the username selector is visible.
self.wait_for_element_visibility(username_selector, 'username field is visible')
@@ -968,10 +968,10 @@ class MembershipPageBetaTesterSection(PageObject):
"""
Check notification div is visible and have message.
"""
notification_selector = '{} .request-response'.format(self.batch_beta_tester_selector)
notification_selector = u'{} .request-response'.format(self.batch_beta_tester_selector)
self.wait_for_element_visibility(notification_selector, 'Notification div is visible')
notification_header_text = self.q(css="{} h3".format(notification_selector)).text
notification_username = self.q(css="{} li".format(notification_selector)).text
notification_header_text = self.q(css=u"{} h3".format(notification_selector)).text
notification_username = self.q(css=u"{} li".format(notification_selector)).text
return notification_header_text, notification_username
@@ -1198,7 +1198,7 @@ class StudentAdminPage(PageObject):
Returns the input box with the given name
for this object's container.
"""
return self.q(css='{} input[name={}]'.format(self.CONTAINER, input_name))
return self.q(css=u'{} input[name={}]'.format(self.CONTAINER, input_name))
@property
def problem_location_input(self):
@@ -1285,7 +1285,7 @@ class StudentAdminPage(PageObject):
"""
Promise Check Function
"""
query = self.q(css="{} .{}".format(self.CONTAINER, self.TASK_HISTORY_TABLE_NAME))
query = self.q(css=u"{} .{}".format(self.CONTAINER, self.TASK_HISTORY_TABLE_NAME))
return query.visible, query
return Promise(check_func, "Waiting for student admin task history table to be visible.").fulfill()
@@ -1363,14 +1363,14 @@ class EntranceExamAdmin(StudentAdminPage):
"""
Return Let Student Skip Entrance Exam button.
"""
return self.q(css='{} input[name=skip-entrance-exam]'.format(self.CONTAINER))
return self.q(css=u'{} input[name=skip-entrance-exam]'.format(self.CONTAINER))
@property
def top_notification(self):
"""
Returns show background task history for student button.
"""
return self.q(css='{} .request-response-error'.format(self.CONTAINER)).first
return self.q(css=u'{} .request-response-error'.format(self.CONTAINER)).first
def are_all_buttons_visible(self):
"""

View File

@@ -11,7 +11,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-field-select-account_privacy option[value="{}"]'
PROFILE_VISIBILITY_SELECTOR = u'#u-field-select-account_privacy option[value="{}"]'
PROFILE_VISIBILITY_INPUT = '#u-field-select-account_privacy'
@@ -53,8 +53,8 @@ class Badge(PageObject):
"""
Execute javascript to bring the popup(.badges-model) inside the window.
"""
script_to_execute = ("var popup = document.querySelectorAll('.badges-modal')[0];;"
"popup.style.left = '20%';")
script_to_execute = (u"var popup = document.querySelectorAll('.badges-modal')[0];;"
u"popup.style.left = '20%';")
self.browser.execute_script(script_to_execute)
def close_modal(self):
@@ -145,7 +145,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, 'Privacy is set to {}'.format(privacy)).fulfill()
EmptyPromise(lambda: privacy == self.privacy, u'Privacy is set to {}'.format(privacy)).fulfill()
self.q(css='.btn-change-privacy').first.click()
self.wait_for_ajax()

View File

@@ -25,7 +25,7 @@ class LibraryContentXBlockWrapper(PageObject):
"""
Return `selector`, but limited to this particular block's context
"""
return '{}[data-id="{}"] {}'.format(
return u'{}[data-id="{}"] {}'.format(
self.BODY_SELECTOR,
self.locator,
selector

View File

@@ -311,7 +311,7 @@ class ProblemPage(PageObject):
status_selector(str): status selector string.
message(str): description of promise, to be logged.
"""
msg = "Wait for status to be {}".format(message)
msg = u"Wait for status to be {}".format(message)
self.wait_for_element_visibility(status_selector, msg)
def is_expected_status_visible(self, status_selector):
@@ -375,7 +375,7 @@ class ProblemPage(PageObject):
Arguments:
hint_index (int): Index of a displayed hint
"""
css = '.notification-hint .notification-message > ol > li.hint-index-{hint_index}'.format(
css = u'.notification-hint .notification-message > ol > li.hint-index-{hint_index}'.format(
hint_index=hint_index
)
self.wait_for(
@@ -387,7 +387,7 @@ class ProblemPage(PageObject):
"""
Click on the "Review" button within the visible notification.
"""
css_string = '.notification.notification-{notification_type} .review-btn'.format(
css_string = u'.notification.notification-{notification_type} .review-btn'.format(
notification_type=notification_type
)
@@ -468,7 +468,7 @@ class ProblemPage(PageObject):
Problem <clarification>clarification text hidden by an icon in rendering</clarification> Text
"""
self.q(css='div.problem .clarification:nth-child({index}) span[data-tooltip]'.format(index=index + 1)).click()
self.q(css=u'div.problem .clarification:nth-child({index}) span[data-tooltip]'.format(index=index + 1)).click()
@property
def visible_tooltip_text(self):
@@ -489,10 +489,10 @@ class ProblemPage(PageObject):
"""
Check if the given answer/choice is highlighted for choice group.
"""
choice_status_xpath = ('//fieldset/div[contains(@class, "field")][{{0}}]'
'/label[contains(@class, "choicegroup_{choice}")]'
'/span[contains(@class, "status {choice}")]'.format(choice=choice))
any_status_xpath = '//fieldset/div[contains(@class, "field")][{0}]/label/span'
choice_status_xpath = (u'//fieldset/div[contains(@class, "field")][{{0}}]'
u'/label[contains(@class, "choicegroup_{choice}")]'
u'/span[contains(@class, "status {choice}")]'.format(choice=choice))
any_status_xpath = u'//fieldset/div[contains(@class, "field")][{0}]/label/span'
for choice in choices_list:
if not self.q(xpath=choice_status_xpath.format(choice)).is_present():
return False

View File

@@ -122,7 +122,7 @@ class ProgressPage(CoursePage):
# CSS indices are 1-indexed, so add one to the list index
return chapter_titles.index(title.lower()) + 1
except ValueError:
self.warning("Could not find chapter '{0}'".format(title))
self.warning(u"Could not find chapter '{0}'".format(title))
return None
def _section_index(self, chapter_index, title):
@@ -134,7 +134,7 @@ class ProgressPage(CoursePage):
# This is a hideous CSS selector that means:
# Get the links containing the section titles in `chapter_index`.
# The link text is the section title.
section_css = '.chapters>section:nth-of-type({0}) .sections div .hd a'.format(chapter_index)
section_css = u'.chapters>section:nth-of-type({0}) .sections div .hd a'.format(chapter_index)
section_titles = self.q(css=section_css).map(lambda el: el.text.lower().strip()).results
# The section titles also contain "n of m possible points" on the second line
@@ -148,7 +148,7 @@ class ProgressPage(CoursePage):
# CSS indices are 1-indexed, so add one to the list index
return section_titles.index(title.lower()) + 1
except ValueError:
self.warning("Could not find section '{0}'".format(title))
self.warning(u"Could not find section '{0}'".format(title))
return None
def _aggregate_section_score(self, chapter_index, section_index):
@@ -156,7 +156,7 @@ class ProgressPage(CoursePage):
Return a tuple of the form `(points, max_points)` representing
the aggregate score for the specified chapter and section.
"""
score_css = ".chapters>section:nth-of-type({0}) .sections>div:nth-of-type({1}) .hd>span".format(
score_css = u".chapters>section:nth-of-type({0}) .sections>div:nth-of-type({1}) .hd>span".format(
chapter_index, section_index
)
@@ -183,7 +183,7 @@ class ProgressPage(CoursePage):
# This is CSS selector means:
# Get the scores for the chapter at `chapter_index` and the section at `section_index`
# Example text of the retrieved elements: "0/1"
score_css = ".chapters>section:nth-of-type({0}) .sections>div:nth-of-type({1}) .scores>dd".format(
score_css = u".chapters>section:nth-of-type({0}) .sections>div:nth-of-type({1}) .scores>dd".format(
chapter_index, section_index
)

View File

@@ -29,7 +29,7 @@ class TabNavPage(PageObject):
"""
if tab_name not in ['Course', 'Home', 'Discussion', 'Wiki', 'Progress']:
self.warning("'{0}' is not a valid tab name".format(tab_name))
self.warning(u"'{0}' is not a valid tab name".format(tab_name))
# The only identifier for individual tabs is the link href
# so we find the tab with `tab_name` in its text.
@@ -38,7 +38,7 @@ class TabNavPage(PageObject):
if tab_css is not None:
self.q(css=tab_css).first.click()
else:
self.warning("No tabs found for '{0}'".format(tab_name))
self.warning(u"No tabs found for '{0}'".format(tab_name))
self.wait_for_page()
self._is_on_tab_promise(tab_name).fulfill()
@@ -74,9 +74,9 @@ class TabNavPage(PageObject):
return None
else:
if self.is_using_boostrap_style_tabs():
return 'ul.navbar-nav li:nth-of-type({0}) a'.format(tab_index + 1)
return u'ul.navbar-nav li:nth-of-type({0}) a'.format(tab_index + 1)
else:
return 'ol.course-tabs li:nth-of-type({0}) a'.format(tab_index + 1)
return u'ol.course-tabs li:nth-of-type({0}) a'.format(tab_index + 1)
@property
def tab_names(self):
@@ -122,7 +122,7 @@ 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),
"{0} is the current tab".format(tab_name)
u"{0} is the current tab".format(tab_name)
)
def has_new_post_button_visible_on_tab(self):

View File

@@ -23,7 +23,7 @@ class TeamCardsMixin(object):
def _bounded_selector(self, css):
"""Bind the CSS to a particular tabpanel (e.g. My Teams or Browse)."""
return '{tabpanel_id} {css}'.format(tabpanel_id=getattr(self, 'tabpanel_id', ''), css=css)
return u'{tabpanel_id} {css}'.format(tabpanel_id=getattr(self, 'tabpanel_id', ''), css=css)
def view_first_team(self):
"""Click the 'view' button of the first team card on the page."""
@@ -189,14 +189,14 @@ class BrowseTopicsPage(CoursePage, PaginatedUIMixin):
Show the teams list for `topic_name`.
"""
self.q(css=TEAMS_LINK_CSS).filter(
text='View Teams in the {topic_name} Topic'.format(topic_name=topic_name)
text=u'View Teams in the {topic_name} Topic'.format(topic_name=topic_name)
)[0].click()
self.wait_for_ajax()
def sort_topics_by(self, sort_order):
"""Sort the list of topics by the given `sort_order`."""
self.q(
css='#paging-header-select option[value={sort_order}]'.format(sort_order=sort_order)
css=u'#paging-header-select option[value={sort_order}]'.format(sort_order=sort_order)
).click()
self.wait_for_ajax()
@@ -273,7 +273,7 @@ class BaseTeamsPage(CoursePage, PaginatedUIMixin, TeamCardsMixin, BreadcrumbsMix
def sort_teams_by(self, sort_order):
"""Sort the list of teams by the given `sort_order`."""
self.q(
css='#paging-header-select option[value={sort_order}]'.format(sort_order=sort_order)
css=u'#paging-header-select option[value={sort_order}]'.format(sort_order=sort_order)
).click()
self.wait_for_ajax()
@@ -564,7 +564,7 @@ class TeamPage(CoursePage, PaginatedUIMixin, BreadcrumbsMixin):
def format_capacity_text(self, num_members, max_size):
""" Helper method to format the expected team capacity text. """
return '{num_members} / {max_size} {members_text}'.format(
return u'{num_members} / {max_size} {members_text}'.format(
num_members=num_members,
max_size=max_size,
members_text='Member' if num_members == max_size else 'Members'

View File

@@ -34,7 +34,7 @@ CSS_CLASS_NAMES = {
'captions_rendered': '.video.is-captions-rendered',
'captions': '.subtitles',
'captions_text': '.subtitles li span',
'captions_text_getter': '.subtitles li span[role="link"][data-index="{}"]',
'captions_text_getter': u'.subtitles li span[role="link"][data-index="{}"]',
'closed_captions': '.closed-captions',
'error_message': '.video .video-player .video-error',
'video_container': '.video',
@@ -121,7 +121,7 @@ class VideoPage(PageObject):
video_player_buttons.append('play')
for button in video_player_buttons:
self.wait_for_element_visibility(VIDEO_BUTTONS[button], '{} button is visible'.format(button))
self.wait_for_element_visibility(VIDEO_BUTTONS[button], u'{} button is visible'.format(button))
def _is_finished_loading():
"""
@@ -148,7 +148,7 @@ class VideoPage(PageObject):
video_player_buttons = ['do_not_show_again', 'skip_bumper', 'volume']
for button in video_player_buttons:
self.wait_for_element_visibility(VIDEO_BUTTONS[button], '{} button is visible'.format(button))
self.wait_for_element_visibility(VIDEO_BUTTONS[button], u'{} button is visible'.format(button))
@property
def is_poster_shown(self):
@@ -179,7 +179,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("Incorrect Video Display Name: '{0}'".format(video_display_name))
raise ValueError(u"Incorrect Video Display Name: '{0}'".format(video_display_name))
return '.vert.vert-{}'.format(video_display_names.index(video_display_name))
else:
return '.vert.vert-0'
@@ -197,7 +197,7 @@ class VideoPage(PageObject):
"""
if vertical:
return '{vertical} {video_element}'.format(
return u'{vertical} {video_element}'.format(
vertical=self.get_video_vertical_selector(self.current_video_display_name),
video_element=class_name)
else:
@@ -245,7 +245,7 @@ class VideoPage(PageObject):
is_present = href_src.startswith('blob:') or href_src.startswith('mediasource:')
return is_present, is_present
return Promise(_is_element_present, 'Video Rendering Failed in {0} mode.'.format(mode)).fulfill()
return Promise(_is_element_present, u'Video Rendering Failed in {0} mode.'.format(mode)).fulfill()
@property
def video_download_url(self):
@@ -387,7 +387,7 @@ class VideoPage(PageObject):
# Verify that captions state is toggled/changed
EmptyPromise(lambda: self.is_captions_visible() == captions_new_state,
"Transcripts are {state}".format(state=state)).fulfill()
u"Transcripts are {state}".format(state=state)).fulfill()
@wait_for_js
def _closed_captions_visibility(self, closed_captions_new_state):
@@ -404,7 +404,7 @@ class VideoPage(PageObject):
# Make sure that the captions are visible
EmptyPromise(lambda: self.is_closed_captions_visible() == closed_captions_new_state,
"Closed captions are {state}".format(state=state)).fulfill()
u"Closed captions are {state}".format(state=state)).fulfill()
@property
def captions_text(self):
@@ -485,7 +485,7 @@ class VideoPage(PageObject):
hover = ActionChains(self.browser).move_to_element(element_to_hover_over)
hover.perform()
speed_selector = self.get_element_selector('li[data-speed="{speed}"] .control'.format(speed=speed))
speed_selector = self.get_element_selector(u'li[data-speed="{speed}"] .control'.format(speed=speed))
self.q(css=speed_selector).first.click()
# Click triggers an ajax event
self.wait_for_ajax()
@@ -677,7 +677,7 @@ class VideoPage(PageObject):
element_to_hover_over = self.q(css=cc_button_selector).results[0]
ActionChains(self.browser).move_to_element(element_to_hover_over).perform()
language_selector = VIDEO_MENUS["language"] + ' li[data-lang-code="{code}"]'.format(code=code)
language_selector = VIDEO_MENUS["language"] + u' li[data-lang-code="{code}"]'.format(code=code)
language_selector = self.get_element_selector(language_selector)
self.wait_for_element_visibility(language_selector, 'language menu is visible')
hover_target = self.q(css=language_selector).results[0]
@@ -786,7 +786,7 @@ class VideoPage(PageObject):
# For troubleshooting purposes show what the current state is.
# The debug statements will only be displayed in the event of a failure.
logging.debug("Current state of '{}' element is '{}'".format(state_selector, current_state))
logging.debug(u"Current state of '{}' element is '{}'".format(state_selector, current_state))
# See the JS video player's onStateChange function
if 'is-playing' in current_state:
@@ -824,7 +824,7 @@ class VideoPage(PageObject):
"""
self._wait_for(
lambda: self.state == state,
'State is {state}'.format(state=state)
u'State is {state}'.format(state=state)
)
def seek(self, seek_value):
@@ -837,7 +837,7 @@ class VideoPage(PageObject):
"""
seek_time = _parse_time_str(seek_value)
seek_selector = self.get_element_selector(' .video')
js_code = "$('{seek_selector}').data('video-player-state').videoPlayer.onSlideSeek({{time: {seek_time}}})".format(
js_code = u"$('{seek_selector}').data('video-player-state').videoPlayer.onSlideSeek({{time: {seek_time}}})".format(
seek_selector=seek_selector, seek_time=seek_time)
self.browser.execute_script(js_code)
@@ -887,7 +887,7 @@ class VideoPage(PageObject):
"""
self._wait_for(
lambda: self.position == position,
'Position is {position}'.format(position=position)
u'Position is {position}'.format(position=position)
)
@property

View File

@@ -219,7 +219,7 @@ class AssetIndexPageStudioFrontend(CoursePage):
"""Delete the asset with the specified name."""
names = self.asset_files_names
if name not in names:
raise LookupError('Asset with filename {} not found.'.format(name))
raise LookupError(u'Asset with filename {} not found.'.format(name))
delete_buttons = self.asset_delete_buttons
assets = dict(zip(names, delete_buttons))
# Now click the link in that row

View File

@@ -27,7 +27,7 @@ class ContainerPage(PageObject, HelpMixin):
@property
def url(self):
"""URL to the container page for an xblock."""
return "{}/container/{}".format(BASE_URL, self.locator)
return u"{}/container/{}".format(BASE_URL, self.locator)
@property
def name(self):
@@ -39,7 +39,7 @@ class ContainerPage(PageObject, HelpMixin):
def is_browser_on_page(self):
def _xblock_count(class_name, request_token):
return len(self.q(css='{body_selector} .xblock.{class_name}[data-request-token="{request_token}"]'.format(
return len(self.q(css=u'{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)
@@ -50,7 +50,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='{} [data-request-token="{}"]'.format(XBlockWrapper.BODY_SELECTOR, request_token)).results)
num_wrappers = len(self.q(css=u'{} [data-request-token="{}"]'.format(XBlockWrapper.BODY_SELECTOR, request_token)).results)
# 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"
@@ -323,7 +323,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='confirmation message {status}'.format(
self.wait_for(_verify_message, description=u'confirmation message {status}'.format(
status='hidden' if verify_hidden else 'present'
))
@@ -402,7 +402,7 @@ class ContainerPage(PageObject, HelpMixin):
Returns:
list
"""
css = '#tab{tab_index} button[data-category={category_type}] span'.format(
css = u'#tab{tab_index} button[data-category={category_type}] span'.format(
tab_index=tab_index,
category_type=category_type
)
@@ -435,7 +435,7 @@ class XBlockWrapper(PageObject):
"""
Return `selector`, but limited to this particular `CourseOutlineChild` context
"""
return '{}[data-locator="{}"] {}'.format(
return u'{}[data-locator="{}"] {}'.format(
self.BODY_SELECTOR,
self.locator,
selector
@@ -487,7 +487,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('{} p.{}'.format(self.VALIDATION_SELECTOR, css_class)))
return self.q(css=self._bounded_selector(u'{} p.{}'.format(self.VALIDATION_SELECTOR, css_class)))
@property
def has_validation_warning(self):
@@ -619,7 +619,7 @@ class XBlockWrapper(PageObject):
"""
If editing, set the value of a field.
"""
selector = '{} li.field label:contains("{}") + input'.format(self.editor_selector, field_display_name)
selector = u'{} li.field label:contains("{}") + input'.format(self.editor_selector, field_display_name)
script = "$(arguments[0]).val(arguments[1]).change();"
self.browser.execute_script(script, selector, field_value)
@@ -627,7 +627,7 @@ class XBlockWrapper(PageObject):
"""
If editing, reset the value of a field to its default.
"""
scope = '{} li.field label:contains("{}")'.format(self.editor_selector, field_display_name)
scope = u'{} li.field label:contains("{}")'.format(self.editor_selector, field_display_name)
script = "$(arguments[0]).siblings('.setting-clear').click();"
self.browser.execute_script(script, scope)
@@ -635,18 +635,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='$("{}").find'.format(self.editor_selector))
type_in_codemirror(self, index, text, find_prefix=u'$("{}").find'.format(self.editor_selector))
def set_license(self, license_type):
"""
Uses the UI to set the course's license to the given license_type (str)
"""
css_selector = (
"ul.license-types li[data-license={license_type}] button"
u"ul.license-types li[data-license={license_type}] button"
).format(license_type=license_type)
self.wait_for_element_presence(
css_selector,
"{license_type} button is present".format(license_type=license_type)
u"{license_type} button is present".format(license_type=license_type)
)
self.q(css=css_selector).click()

View File

@@ -22,7 +22,7 @@ class DiscussionComponentEditor(XBlockEditorView):
"""
If editing, set the value of a field.
"""
selector = '.xblock-studio_view li.field label:contains("{}") + input'.format(field_display_name)
selector = u'.xblock-studio_view li.field label:contains("{}") + input'.format(field_display_name)
script = "$(arguments[0]).val(arguments[1]).change();"
self.browser.execute_script(script, selector, field_value)

View File

@@ -37,7 +37,7 @@ class PagesPage(CoursePage):
description="Static tab is added"
)
self.wait_for_element_visibility(
'.tab-list :nth-child({}) .xblock-student_view'.format(total_tabs),
u'.tab-list :nth-child({}) .xblock-student_view'.format(total_tabs),
'Static tab is visible'
)
# self.wait_for_ajax()
@@ -121,7 +121,7 @@ class PagesPage(CoursePage):
true(bool): if tab is visible
false(bool): if tab is not visible
"""
css_selector = '[data-tab-id="{}"] .toggle-checkbox'.format(tab_name)
css_selector = u'[data-tab-id="{}"] .toggle-checkbox'.format(tab_name)
return True if not self.q(css=css_selector).selected else False
def toggle_tab(self, tab_name):
@@ -130,7 +130,7 @@ class PagesPage(CoursePage):
Args:
tab_name(string): Name of the tab to be toggled
"""
css_selector = '[data-tab-id="{}"] .action-visible'.format(tab_name)
css_selector = u'[data-tab-id="{}"] .action-visible'.format(tab_name)
return self.q(css=css_selector).first.click()
def set_field_val(self, field_display_name, field_value):
@@ -141,7 +141,7 @@ class PagesPage(CoursePage):
field_display_name(str): Display name of the field for which the value is to be changed
field_value(str): New value for the field
"""
selector = '.xblock-studio_view li.field label:contains("{}") + input'.format(field_display_name)
selector = u'.xblock-studio_view li.field label:contains("{}") + input'.format(field_display_name)
script = '$(arguments[0]).val(arguments[1]).change();'
self.browser.execute_script(script, selector, field_value)

View File

@@ -218,7 +218,7 @@ class HtmlXBlockEditorView(XBlockEditorView):
"""
If editing, set the value of a field.
"""
selector = '.xblock-studio_view li.field label:contains("{}") + input'.format(field_display_name)
selector = u'.xblock-studio_view li.field label:contains("{}") + input'.format(field_display_name)
script = "$(arguments[0]).val(arguments[1]).change();"
self.browser.execute_script(script, selector, field_value)

View File

@@ -53,8 +53,8 @@ class ImportExportMixin(object):
"""
Return python datetime object from the parsed timestamp tuple (date, time)
"""
timestamp = "{0} {1}".format(*self.timestamp)
formatted_timestamp = time.strptime(timestamp, "%m/%d/%Y %H:%M")
timestamp = u"{0} {1}".format(*self.timestamp)
formatted_timestamp = time.strptime(timestamp, u"%m/%d/%Y %H:%M")
return datetime.fromtimestamp(time.mktime(formatted_timestamp))
@property
@@ -65,7 +65,7 @@ class ImportExportMixin(object):
"""
string = self.q(css='.item-progresspoint-success-date').text[0]
return re.match(r'\(([^ ]+).+?(\d{2}:\d{2})', string).groups()
return re.match(ur'\(([^ ]+).+?(\d{2}:\d{2})', string).groups()
def wait_for_tasks(self, completed=False, fail_on=None):
"""
@@ -80,11 +80,11 @@ class ImportExportMixin(object):
for desc, css_class in self.task_classes.items():
desc_text = desc_template.format(desc)
# pylint: disable=cell-var-from-loop
EmptyPromise(lambda: self.q(css='.{}.{}'.format(css_class, state)).present, desc_text, timeout=30)
EmptyPromise(lambda: self.q(css=u'.{}.{}'.format(css_class, state)).present, desc_text, timeout=30)
if fail_on == desc:
EmptyPromise(
lambda: self.q(css='.{}.is-complete.has-error'.format(css_class)).present,
"{} checkpoint marked as failed".format(desc),
lambda: self.q(css=u'.{}.is-complete.has-error'.format(css_class)).present,
u"{} checkpoint marked as failed".format(desc),
timeout=30
)
# The rest should never run.
@@ -102,9 +102,9 @@ class ImportExportMixin(object):
Outputs the CSS class and promise description for task states based on completion.
"""
if completed:
return 'is-complete', "'{}' is marked complete"
return 'is-complete', u"'{}' is marked complete"
else:
return 'is-not-started', "'{}' is in not-yet-started status"
return 'is-not-started', u"'{}' is in not-yet-started status"
class ExportMixin(ImportExportMixin):

View File

@@ -114,7 +114,7 @@ class DashboardPage(PageObject, HelpMixin):
Fill out the form to create a new library.
Must have called click_new_library() first.
"""
field = lambda fn: self.q(css='.wrapper-create-library #new-library-{}'.format(fn))
field = lambda fn: self.q(css=u'.wrapper-create-library #new-library-{}'.format(fn))
field('name').fill(display_name)
field('org').fill(org)
field('number').fill(number)
@@ -158,7 +158,7 @@ class DashboardPage(PageObject, HelpMixin):
"""
Fill out the form to create a new course.
"""
field = lambda fn: self.q(css='.wrapper-create-course #new-course-{}'.format(fn))
field = lambda fn: self.q(css=u'.wrapper-create-course #new-course-{}'.format(fn))
field('name').fill(display_name)
field('org').fill(org)
field('number').fill(number)
@@ -218,7 +218,7 @@ class DashboardPage(PageObject, HelpMixin):
List all the courses found on the page's list of courses.
"""
# Workaround Selenium/Firefox bug: `.text` property is broken on invisible elements
tab_selector = '#course-index-tabs .{} a'.format('archived-courses-tab' if archived else 'courses-tab')
tab_selector = u'#course-index-tabs .{} a'.format('archived-courses-tab' if archived else 'courses-tab')
self.wait_for_element_presence(tab_selector, "Courses Tab")
self.q(css=tab_selector).click()
div2info = lambda element: {
@@ -228,7 +228,7 @@ class DashboardPage(PageObject, HelpMixin):
'run': element.find_element_by_css_selector('.course-run .value').text,
'url': element.find_element_by_css_selector('a.course-link').get_attribute('href'),
}
course_list_selector = '.{} li.course-item'.format('archived-courses' if archived else 'courses')
course_list_selector = u'.{} li.course-item'.format('archived-courses' if archived else 'courses')
return self.q(css=course_list_selector).map(div2info).results
def has_course(self, org, number, run, archived=False):
@@ -364,7 +364,7 @@ class AccessibilityPage(IndexPage):
"""
To simulate leaving a field blank, click on the field, then press TAB to move off focus off the field.
"""
field = self.q(css='#root {}#{}'.format(field_type, field_id))[0]
field = self.q(css=u'#root {}#{}'.format(field_type, field_id))[0]
field.click()
field.send_keys(Keys.TAB)
@@ -378,7 +378,7 @@ class AccessibilityPage(IndexPage):
"""
Check that at least one error message is shown and at least one contains the specified text.
"""
selector = '#root div#error-{}'.format(field_id)
selector = u'#root div#error-{}'.format(field_id)
self.wait_for_element_visibility(selector, 'An error message is visible')
error_messages = self.q(css=selector)
for message in error_messages:

View File

@@ -82,7 +82,7 @@ class LibraryEditPage(LibraryPage, PaginatedMixin, UsersPageMixin):
self.q(css='.toggle-preview-button').click()
EmptyPromise(
lambda: self.are_previews_showing() == toggle,
'Preview is visible: %s' % toggle,
u'Preview is visible: %s' % toggle,
timeout=30
).fulfill()
self.wait_until_ready()
@@ -129,7 +129,7 @@ class LibraryEditPage(LibraryPage, PaginatedMixin, UsersPageMixin):
action is 'edit', 'duplicate', or 'delete'
"""
return self._div_for_xblock_id(xblock_id)[0].find_element_by_css_selector(
'.header-actions .{action}-button.action-button'.format(action=action)
u'.header-actions .{action}-button.action-button'.format(action=action)
)

View File

@@ -38,9 +38,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 "{}(<browser>, {!r})".format(self.__class__.__name__, self.locator)
return u"{}(<browser>, {!r})".format(self.__class__.__name__, self.locator)
except AttributeError:
return "{}(<browser>)".format(self.__class__.__name__)
return u"{}(<browser>)".format(self.__class__.__name__)
def _bounded_selector(self, selector):
"""
@@ -50,7 +50,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 '{}[data-locator="{}"] {}'.format(
return u'{}[data-locator="{}"] {}'.format(
self.BODY_SELECTOR,
self.locator,
selector
@@ -153,7 +153,7 @@ class CourseOutlineItem(object):
select_option_by_text(groups_select, partition_name)
for group_id in group_ids:
checkbox = self.q(css='#content-group-{group_id}'.format(group_id=group_id))
checkbox = self.q(css=u'#content-group-{group_id}'.format(group_id=group_id))
checkbox.click()
modal.save()
@@ -302,11 +302,11 @@ class CourseOutlineContainer(CourseOutlineItem):
self.scroll_to_element(css_element) # pylint: disable=no-member
ele = self.browser.find_element_by_css_selector(css_element) # pylint: disable=no-member
ActionChains(self.browser).move_to_element_with_offset(ele, 8, 8).click().perform() # pylint: disable=no-member
self.wait_for_element_presence(self._bounded_selector(self.ADD_BUTTON_SELECTOR), 'Subsection is expanded')
self.wait_for_element_presence(self._bounded_selector(self.ADD_BUTTON_SELECTOR), u'Subsection is expanded')
EmptyPromise(
lambda: subsection_expanded() != currently_expanded,
"Check that the container {} has been toggled".format(self.locator)
u"Check that the container {} has been toggled".format(self.locator)
).fulfill()
enable_animations(self)
@@ -348,7 +348,7 @@ class CourseOutlineChild(PageObject, CourseOutlineItem):
"""
Return `selector`, but limited to this particular `CourseOutlineChild` context
"""
return '{}[data-locator="{}"] {}'.format(
return u'{}[data-locator="{}"] {}'.format(
self.BODY_SELECTOR,
self.locator,
selector
@@ -528,7 +528,7 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
"""
Find and click on first section name in course outline
"""
self.q(css='{} .section-name'.format(parent_css)).first.click()
self.q(css=u'{} .section-name'.format(parent_css)).first.click()
def get_section_name(self, parent_css='', page_refresh=False):
"""
@@ -536,21 +536,21 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
"""
if page_refresh:
self.browser.refresh()
return self.q(css='{} .section-name'.format(parent_css)).text
return self.q(css=u'{} .section-name'.format(parent_css)).text
def section_name_edit_form_present(self, parent_css=''):
"""
Check that section name edit form present
"""
return self.q(css='{} .section-name input'.format(parent_css)).present
return self.q(css=u'{} .section-name input'.format(parent_css)).present
def change_section_name(self, new_name, parent_css=''):
"""
Change section name of first section present in course outline
"""
self.click_section_name(parent_css)
self.q(css='{} .section-name input'.format(parent_css)).first.fill(new_name)
self.q(css='{} .section-name .save-button'.format(parent_css)).first.click()
self.q(css=u'{} .section-name input'.format(parent_css)).first.fill(new_name)
self.q(css=u'{} .section-name .save-button'.format(parent_css)).first.click()
self.wait_for_ajax()
def sections(self):
@@ -1007,14 +1007,14 @@ class CourseOutlineModal(object):
else: # Use default timepicker values, which are current month and year.
current_month, current_year = datetime.datetime.today().month, datetime.datetime.today().year
date_diff = 12 * (year - current_year) + month - current_month
selector = "a.ui-datepicker-{}".format('next' if date_diff > 0 else 'prev')
selector = u"a.ui-datepicker-{}".format('next' if date_diff > 0 else 'prev')
for __ in xrange(abs(date_diff)):
self.page.q(css=selector).click()
self.page.q(css="a.ui-state-default").nth(day - 1).click() # set day
self.page.wait_for_element_invisibility("#ui-datepicker-div", "datepicker should be closed")
EmptyPromise(
lambda: getattr(self, property_name) == u'{m}/{d}/{y}'.format(m=month, d=day, y=year),
"{} is updated in modal.".format(property_name)
u"{} is updated in modal.".format(property_name)
).fulfill()
def set_time(self, input_selector, time):

View File

@@ -17,7 +17,7 @@ class PaginatedMixin(object):
To specify a specific arrow, pass an iterable with a single element, 'next' or 'previous'.
"""
return all([
self.q(css='nav.%s * .%s-page-link.is-disabled' % (position, arrow))
self.q(css=u'nav.%s * .%s-page-link.is-disabled' % (position, arrow))
for arrow in arrows
])
@@ -25,14 +25,14 @@ class PaginatedMixin(object):
"""
Clicks one of the forward nav buttons. Position can be 'top' or 'bottom'.
"""
self.q(css='nav.%s * .previous-page-link' % position)[0].click()
self.q(css=u'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='nav.%s * .next-page-link' % position)[0].click()
self.q(css=u'nav.%s * .next-page-link' % position)[0].click()
self.wait_until_ready()
def go_to_page(self, number):

View File

@@ -24,7 +24,7 @@ class ProblemXBlockEditorView(XBlockEditorView):
"""
If editing, set the value of a field.
"""
selector = '.xblock-studio_view li.field label:contains("{}") + input'.format(field_display_name)
selector = u'.xblock-studio_view li.field label:contains("{}") + input'.format(field_display_name)
script = "$(arguments[0]).val(arguments[1]).change();"
self.browser.execute_script(script, selector, field_value)
@@ -37,7 +37,7 @@ class ProblemXBlockEditorView(XBlockEditorView):
Returns:
(string): Value of the field
"""
script = "return $('.wrapper-comp-setting label:contains({}) + input').val();".format(field_display_name)
script = u"return $('.wrapper-comp-setting label:contains({}) + input').val();".format(field_display_name)
return self.browser.execute_script(script)
def get_default_dropdown_value(self, css):
@@ -60,9 +60,9 @@ class ProblemXBlockEditorView(XBlockEditorView):
dropdown_name(string): Name of the dropdown to be opened
value(string): Value to be selected
"""
self.q(css='select[class="input setting-input"][name="{}"]'.format(dropdown_name)).first.click()
self.wait_for_element_visibility('option[value="{}"]'.format(value), 'Dropdown is visible')
self.q(css='option[value="{}"]'.format(value)).click()
self.q(css=u'select[class="input setting-input"][name="{}"]'.format(dropdown_name)).first.click()
self.wait_for_element_visibility(u'option[value="{}"]'.format(value), 'Dropdown is visible')
self.q(css=u'option[value="{}"]'.format(value)).click()
def get_value_from_the_dropdown(self, dropdown_name):
"""
@@ -74,7 +74,7 @@ class ProblemXBlockEditorView(XBlockEditorView):
"""
dropdown = self.browser.find_element_by_css_selector(
'select[class="input setting-input"][name="{}"]'.format(dropdown_name)
u'select[class="input setting-input"][name="{}"]'.format(dropdown_name)
)
return Select(dropdown).first_selected_option.text
@@ -86,7 +86,7 @@ class ProblemXBlockEditorView(XBlockEditorView):
"""
settings_dict = {}
number_of_settings = len(self.q(css='.wrapper-comp-setting'))
css = '.list-input.settings-list .field.comp-setting-entry:nth-of-type({}) {}'
css = u'.list-input.settings-list .field.comp-setting-entry:nth-of-type({}) {}'
for index in range(1, number_of_settings + 1):
key = self.q(css=css.format(index, "label")).text[0]

View File

@@ -196,7 +196,7 @@ class CertificateSectionPage(CertificatesPage):
:return:
"""
self.selector = prefix + ' .certificates-list-item-{}'.format(index)
self.selector = prefix + u' .certificates-list-item-{}'.format(index)
self.index = index
super(CertificateSectionPage, self).__init__(container.browser, **container.course_info)
@@ -446,7 +446,7 @@ class SignatorySectionPage(CertificatesPage):
"""
Return selector fo signatory container
"""
selector = self.prefix + ' .signatory-{}-view-{}'.format(self.mode, self.index)
selector = self.prefix + u' .signatory-{}-view-{}'.format(self.mode, self.index)
return ' '.join([selector, css])
def find_css(self, css_selector):

View File

@@ -110,7 +110,7 @@ class GroupConfiguration(object):
def __init__(self, page, prefix, index):
self.page = page
self.SELECTOR = prefix + ' .wrapper-collection-{}'.format(index)
self.SELECTOR = prefix + u' .wrapper-collection-{}'.format(index)
self.index = index
def get_selector(self, css=''):

View File

@@ -94,14 +94,14 @@ class TextbookUploadPage(CoursePage):
Adds chapter name by taking the ordinal of the chapter.
"""
index = ["first", "second", "third"].index(ordinal)
self.set_input_field_value('.textbook .chapter{i} input.chapter-name'.format(i=index + 1), chapter_name)
self.set_input_field_value(u'.textbook .chapter{i} input.chapter-name'.format(i=index + 1), chapter_name)
def fill_chapter_asset(self, ordinal, chapter_asset):
"""
Adds chapter asset by taking the ordinal of the chapter.
"""
index = ["first", "second", "third"].index(ordinal)
self.set_input_field_value('.textbook .chapter{i} input.chapter-asset-path'.format(i=index + 1), chapter_asset)
self.set_input_field_value(u'.textbook .chapter{i} input.chapter-asset-path'.format(i=index + 1), chapter_asset)
def submit_chapter(self):
"""

View File

@@ -113,7 +113,7 @@ class UsersPageMixin(PageObject):
def modal_dialog_text(self, dialog_type):
""" Gets modal dialog text """
return self.q(css='.prompt.{dialog_type} .message'.format(dialog_type=dialog_type)).text[0]
return self.q(css=u'.prompt.{dialog_type} .message'.format(dialog_type=dialog_type)).text[0]
def wait_until_no_loading_indicator(self):
"""
@@ -204,7 +204,7 @@ class UserWrapper(PageObject):
def __init__(self, browser, email):
super(UserWrapper, self).__init__(browser)
self.email = email
self.selector = '.user-list .user-item[data-email="{}"]'.format(self.email)
self.selector = u'.user-list .user-item[data-email="{}"]'.format(self.email)
def is_browser_on_page(self):
"""
@@ -216,7 +216,7 @@ class UserWrapper(PageObject):
"""
Return `selector`, but limited to this particular user entry's context
"""
return '{} {}'.format(self.selector, selector)
return u'{} {}'.format(self.selector, selector)
@property
def name(self):

View File

@@ -23,8 +23,8 @@ def press_the_notification_button(page, name):
# the "Save" button at the UI level.
# Instead, we use JavaScript to reliably click
# the button.
btn_css = 'div#page-notification button.action-%s' % name.lower()
page.browser.execute_script("$('{}').focus().click()".format(btn_css))
btn_css = u'div#page-notification button.action-%s' % name.lower()
page.browser.execute_script(u"$('{}').focus().click()".format(btn_css))
page.wait_for_ajax()
@@ -59,8 +59,8 @@ def add_advanced_component(page, menu_index, name):
page.wait_for_element_visibility('.new-component-advanced', 'Advanced component menu is visible')
# Now click on the component to add it.
component_css = 'button[data-category={}]'.format(name)
page.wait_for_element_visibility(component_css, 'Advanced component {} is visible'.format(name))
component_css = u'button[data-category={}]'.format(name)
page.wait_for_element_visibility(component_css, u'Advanced component {} is visible'.format(name))
# Adding some components, e.g. the Discussion component, will make an ajax call
# but we should be OK because the click_css method is written to handle that.
@@ -76,11 +76,11 @@ def add_component(page, item_type, specific_type, is_advanced_problem=False):
specific_type is required for some types and should be something like
"Blank Common Problem".
"""
btn = page.q(css='.add-xblock-component .add-xblock-component-button[data-type={}]'.format(item_type))
btn = page.q(css=u'.add-xblock-component .add-xblock-component-button[data-type={}]'.format(item_type))
multiple_templates = btn.filter(lambda el: 'multiple-templates' in el.get_attribute('class')).present
btn.click()
if multiple_templates:
sub_template_menu_div_selector = '.new-component-{}'.format(item_type)
sub_template_menu_div_selector = u'.new-component-{}'.format(item_type)
page.wait_for_element_visibility(sub_template_menu_div_selector, 'Wait for the templates sub-menu to appear')
page.wait_for_element_invisibility(
'.add-xblock-component .new-component',
@@ -96,11 +96,11 @@ def add_component(page, item_type, specific_type, is_advanced_problem=False):
# Wait for the advanced tab to be active
css = '.problem-type-tabs li.ui-tabs-active a'
page.wait_for(
lambda: len(page.q(css=css).filter(text='Advanced').execute()) > 0,
lambda: len(page.q(css=css).filter(text=u'Advanced').execute()) > 0,
'Waiting for the Advanced problem tab to be active'
)
all_options = page.q(css='.new-component-{} ul.new-component-template li button span'.format(item_type))
all_options = page.q(css=u'.new-component-{} ul.new-component-template li button span'.format(item_type))
chosen_option = all_options.filter(text=specific_type).first
chosen_option.click()
sync_on_notification(page)
@@ -134,13 +134,13 @@ def add_html_component(page, menu_index, boilerplate=None):
page.wait_for_element_visibility('.new-component-html', 'HTML component menu is visible')
# Now click on the component to add it.
component_css = 'button[data-category=html]'
component_css = u'button[data-category=html]'
if boilerplate:
component_css += '[data-boilerplate={}]'.format(boilerplate)
component_css += u'[data-boilerplate={}]'.format(boilerplate)
else:
component_css += ':not([data-boilerplate])'
component_css += u':not([data-boilerplate])'
page.wait_for_element_visibility(component_css, 'HTML component {} is visible'.format(boilerplate))
page.wait_for_element_visibility(component_css, u'HTML component {} is visible'.format(boilerplate))
# Adding some components will make an ajax call but we should be OK because
# the click_css method is written to handle that.
@@ -149,7 +149,7 @@ def add_html_component(page, menu_index, boilerplate=None):
@js_defined('window.jQuery')
def type_in_codemirror(page, index, text, find_prefix="$"):
script = """
script = u"""
var cm = {find_prefix}('div.CodeMirror:eq({index})').get(0).CodeMirror;
CodeMirror.signal(cm, "focus", cm);
cm.setValue(arguments[0]);
@@ -161,9 +161,9 @@ def type_in_codemirror(page, index, text, find_prefix="$"):
@js_defined('window.jQuery')
def get_codemirror_value(page, index=0, find_prefix="$"):
return page.browser.execute_script(
"""
return {find_prefix}('div.CodeMirror:eq({index})').get(0).CodeMirror.getValue();
""".format(index=index, find_prefix=find_prefix)
u"return {find_prefix}('div.CodeMirror:eq({index})').get(0).CodeMirror.getValue();".format(
index=index, find_prefix=find_prefix
)
)
@@ -173,7 +173,7 @@ def get_input_value(page, css_selector):
"""
page.wait_for_element_presence(
css_selector,
'Elements matching "{}" selector are present'.format(css_selector)
u'Elements matching "{}" selector are present'.format(css_selector)
)
return page.q(css=css_selector).attrs('value')[0]
@@ -237,7 +237,7 @@ def verify_ordering(test_class, page, expected_orderings):
expected_length = len(expected_ordering.get(parent))
test_class.assertEqual(
expected_length, len(children),
"Number of children incorrect for group {0}. Expected {1} but got {2}.".format(parent, expected_length, len(children)))
u"Number of children incorrect for group {0}. Expected {1} but got {2}.".format(parent, expected_length, len(children)))
for idx, expected in enumerate(expected_ordering.get(parent)):
test_class.assertEqual(expected, children[idx].name)
blocks_checked.add(expected)

View File

@@ -328,7 +328,7 @@ class VideoComponentPage(VideoPage):
line_number (int): caption line number
"""
caption_line_selector = ".subtitles li span[data-index='{index}']".format(index=line_number - 1)
caption_line_selector = u".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):
@@ -339,7 +339,7 @@ class VideoComponentPage(VideoPage):
line_number (int): caption line number
"""
caption_line_selector = ".subtitles li span[data-index='{index}']".format(index=line_number - 1)
caption_line_selector = u".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()
@@ -487,7 +487,7 @@ class VideoComponentPage(VideoPage):
"""
translations_items = '.wrapper-translations-settings .list-settings-item'
language_selector = translations_items + ' select option[value="{}"]'.format(language_code)
language_selector = translations_items + u' select option[value="{}"]'.format(language_code)
self.q(css=language_selector).nth(index).click()
def upload_translation(self, transcript_name, language_code):
@@ -575,7 +575,7 @@ class VideoComponentPage(VideoPage):
As all the captions lines are exactly same so only getting partial lines will work.
"""
self.wait_for_captions()
selector = '.subtitles li:nth-child({})'
selector = u'.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):
@@ -607,7 +607,7 @@ class VideoComponentPage(VideoPage):
"""
if message_type == 'status':
self.wait_for_element_visibility(CLASS_SELECTORS[message_type],
'{} message is Visible'.format(message_type.title()))
u'{} message is Visible'.format(message_type.title()))
return self.q(css=CLASS_SELECTORS[message_type]).text[0]
@@ -653,7 +653,7 @@ class VideoComponentPage(VideoPage):
"""
Clear video url fields.
"""
script = """
script = u"""
$('{selector}')
.prop('disabled', false)
.removeClass('is-disabled')

View File

@@ -30,7 +30,7 @@ class BaseXBlockEditorView(PageObject):
"""
Return `selector`, but limited to this particular `XBlockEditorView` context
"""
return '{}[data-locator="{}"] {}'.format(
return u'{}[data-locator="{}"] {}'.format(
self.BODY_SELECTOR,
self.locator,
selector

View File

@@ -31,7 +31,7 @@ class AcidView(PageObject):
# First make sure that an element with the view-container class is present on the page,
# and then wait to make sure that the xblock has finished initializing.
return (
self.q(css='{} .acid-block'.format(self.context_selector)).present and
self.q(css=u'{} .acid-block'.format(self.context_selector)).present and
wait_for_xblock_initialization(self, self.context_selector) and
self._ajax_finished()
)
@@ -52,14 +52,14 @@ class AcidView(PageObject):
"""
Return whether a particular :class:`.AcidBlock` test passed.
"""
selector = '{} .acid-block {} .pass'.format(self.context_selector, test_selector)
selector = u'{} .acid-block {} .pass'.format(self.context_selector, test_selector)
return bool(self.q(css=selector).results)
def child_test_passed(self, test_selector):
"""
Return whether a particular :class:`.AcidParentBlock` test passed.
"""
selector = '{} .acid-parent-block {} .pass'.format(self.context_selector, test_selector)
selector = u'{} .acid-parent-block {} .pass'.format(self.context_selector, test_selector)
return bool(self.q(css=selector).execute(try_interval=0.1, timeout=3))
@property
@@ -88,7 +88,7 @@ class AcidView(PageObject):
def scope_passed(self, scope):
return all(
self.test_passed('.scope-storage-test.scope-{} {}'.format(scope, test))
self.test_passed(u'.scope-storage-test.scope-{} {}'.format(scope, test))
for test in (
".server-storage-test-returned",
".server-storage-test-succeeded",

View File

@@ -10,7 +10,7 @@ def wait_for_xblock_initialization(page, xblock_css):
"""
def _is_finished_loading():
# Wait for the xblock javascript to finish initializing
is_done = page.browser.execute_script("return $({!r}).data('initialized')".format(xblock_css))
is_done = page.browser.execute_script(u"return $({!r}).data('initialized')".format(xblock_css))
return (is_done, is_done)
return Promise(_is_finished_loading, 'Finished initializing the xblock.').fulfill()

View File

@@ -578,7 +578,7 @@ class CohortConfigurationTest(EventsTestMixin, UniqueCourseTest, CohortTestMixin
start_time = datetime.now(UTC)
self.cohort_management_page.upload_cohort_file(filename)
self._verify_cohort_by_csv_notification(
"Your file '{}' has been uploaded. Allow a few minutes for processing.".format(filename)
u"Your file '{}' has been uploaded. Allow a few minutes for processing.".format(filename)
)
if not skip_events:

View File

@@ -50,7 +50,7 @@ class CohortedDiscussionTestMixin(BaseDiscussionMixin, CohortTestMixin):
self.refresh_thread_page(self.thread_id)
self.assertEquals(
self.thread_page.get_group_visibility_label(),
"This post is visible only to {}.".format(self.cohort_1_name)
u"This post is visible only to {}.".format(self.cohort_1_name)
)
# Disable cohorts and verify that the post now shows as visible to everyone.

View File

@@ -36,7 +36,7 @@ from common.test.acceptance.tests.helpers import UniqueCourseTest, get_modal_ale
from openedx.core.lib.tests import attr
THREAD_CONTENT_WITH_LATEX = """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
THREAD_CONTENT_WITH_LATEX = u"""Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
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.
@@ -123,7 +123,7 @@ class DiscussionResponsePaginationTestMixin(BaseDiscussionMixin):
(
None if response_total == 0 else
"Showing all responses" if response_total == displayed_responses else
"Showing first {} responses".format(displayed_responses)
u"Showing first {} responses".format(displayed_responses)
)
)
self.assertEqual(
@@ -648,10 +648,10 @@ class DiscussionResponseEditTest(BaseDiscussionTestCase):
page.submit_response_edit(response_id, description)
expected_response_html = (
'<p><a href="{}">{}</a></p>'.format(url, description)
u'<p><a href="{}">{}</a></p>'.format(url, description)
)
actual_response_html = page.q(
css=".response_{} .response-body".format(response_id)
css=u".response_{} .response-body".format(response_id)
).html[0]
self.assertEqual(expected_response_html, actual_response_html)
@@ -682,10 +682,10 @@ class DiscussionResponseEditTest(BaseDiscussionTestCase):
page.submit_response_edit(response_id, '')
expected_response_html = (
'<p><img src="{}" alt="{}" title=""></p>'.format(url, description)
u'<p><img src="{}" alt="{}" title=""></p>'.format(url, description)
)
actual_response_html = page.q(
css=".response_{} .response-body".format(response_id)
css=u".response_{} .response-body".format(response_id)
).html[0]
self.assertEqual(expected_response_html, actual_response_html)
@@ -737,11 +737,11 @@ class DiscussionResponseEditTest(BaseDiscussionTestCase):
page.submit_response_edit(response_id, "Some content")
expected_response_html = (
'<p>Some content<img src="{}" alt="{}" title=""></p>'.format(
u'<p>Some content<img src="{}" alt="{}" title=""></p>'.format(
url, description)
)
actual_response_html = page.q(
css=".response_{} .response-body".format(response_id)
css=u".response_{} .response-body".format(response_id)
).html[0]
self.assertEqual(expected_response_html, actual_response_html)
@@ -1027,9 +1027,9 @@ class DiscussionEditorPreviewTest(UniqueCourseTest):
appear in the preview box
"""
self.page.set_new_post_editor_value(
r'\begin{equation}'
r'\tau_g(\omega) = - \frac{d}{d\omega}\phi(\omega) \hspace{2em} (1) '
r'\end{equation}'
ur'\begin{equation}'
ur'\tau_g(\omega) = - \frac{d}{d\omega}\phi(\omega) \hspace{2em} (1) '
ur'\end{equation}'
)
self.assertIsNotNone(self.page.get_new_post_preview_text())
self.page.click_element(".cancel")

View File

@@ -482,7 +482,7 @@ class DivisionSchemeTest(BaseDividedDiscussionTest, BaseDiscussionMixin):
refresh_thread_page()
self.assertEquals(
self.thread_page.get_group_visibility_label(),
"This post is visible only to {}.".format("Audit")
u"This post is visible only to {}.".format("Audit")
)
# Disable dividing discussions and verify that the post now shows as visible to everyone.

View File

@@ -58,7 +58,7 @@ def skip_if_browser(browser):
@functools.wraps(test_function)
def wrapper(self, *args, **kwargs):
if self.browser.name == browser:
raise SkipTest('Skipping as this test will not work with {}'.format(browser))
raise SkipTest(u'Skipping as this test will not work with {}'.format(browser))
test_function(self, *args, **kwargs)
return wrapper
return decorator
@@ -102,7 +102,7 @@ def is_focused_on_element(browser, selector):
"""
Check if the focus is on the element that matches the selector.
"""
return browser.execute_script("return $('{}').is(':focus')".format(selector))
return browser.execute_script(u"return $('{}').is(':focus')".format(selector))
def load_data_str(rel_path):
@@ -159,7 +159,7 @@ def disable_css_animations(page):
"""
Disable CSS3 animations, transitions, transforms.
"""
page.browser.execute_script("""
page.browser.execute_script(u"""
var id = 'no-transitions';
// if styles were already added, just do nothing.
@@ -236,7 +236,7 @@ def select_option_by_text(select_browser_query, option_text, focus_out=False):
except StaleElementReferenceException:
return False
msg = 'Selected option {}'.format(option_text)
msg = u'Selected option {}'.format(option_text)
EmptyPromise(lambda: select_option(select_browser_query, option_text), msg).fulfill()
@@ -350,7 +350,7 @@ def get_element_padding(page, selector):
progress_page.get_element_padding('.wrapper-msg.wrapper-auto-cert')
"""
js_script = """
js_script = u"""
var $element = $('%(selector)s');
element_padding = {
@@ -380,7 +380,7 @@ def create_multiple_choice_xml(correct_choice=2, num_choices=4):
choices[correct_choice] = True
choice_names = ['choice_{}'.format(index) for index in range(num_choices)]
question_text = 'The correct answer is Choice {}'.format(correct_choice)
question_text = u'The correct answer is Choice {}'.format(correct_choice)
return MultipleChoiceResponseXMLFactory().build_xml(
question_text=question_text,
@@ -434,7 +434,7 @@ def assert_opened_help_link_is_correct(test, url):
# Check that the URL loads. Can't do this in the browser because it might
# be loading a "Maze Found" missing content page.
response = requests.get(url)
test.assertEqual(response.status_code, 200, "URL {!r} returned {}".format(url, response.status_code))
test.assertEqual(response.status_code, 200, u"URL {!r} returned {}".format(url, response.status_code))
EDX_BOOKS = {
@@ -560,7 +560,7 @@ class EventsTestMixin(TestCase):
# This is a bit of a hack, Promise calls str(description), so I set the description to an object with a
# custom __str__ and have it do some intelligent stuff to generate a helpful error message.
CollectedEventsDescription(
'Waiting for {number_of_matches} events to match the filter:\n{event_filter}'.format(
u'Waiting for {number_of_matches} events to match the filter:\n{event_filter}'.format(
number_of_matches=number_of_matches,
event_filter=self.event_filter_to_descriptive_string(event_filter),
),
@@ -855,7 +855,7 @@ class YouTubeStubConfig(object):
if not response.ok:
raise YouTubeConfigError(
'YouTube Server Configuration Failed. URL {0}, Configuration Data: {1}, Status was {2}'.format(
u'YouTube Server Configuration Failed. URL {0}, Configuration Data: {1}, Status was {2}'.format(
youtube_stub_config_url, config, response.status_code))
@classmethod
@@ -873,7 +873,7 @@ class YouTubeStubConfig(object):
if not response.ok:
raise YouTubeConfigError(
'YouTube Server Configuration Failed. URL: {0} Status was {1}'.format(
u'YouTube Server Configuration Failed. URL: {0} Status was {1}'.format(
youtube_stub_config_url, response.status_code))
@classmethod

View File

@@ -52,7 +52,7 @@ class LibraryContentTestBase(UniqueCourseTest):
self.course_info['run']
)
self.library_fixture = LibraryFixture('test_org', self.unique_id, 'Test Library {}'.format(self.unique_id))
self.library_fixture = LibraryFixture('test_org', self.unique_id, u'Test Library {}'.format(self.unique_id))
self.populate_library_fixture(self.library_fixture)
self.library_fixture.install()
@@ -218,11 +218,11 @@ class StudioLibraryContainerCapaFilterTest(LibraryContentTestBase, TestWithSearc
def _get_problem_choice_group_text(self, name, items):
""" Generates Choice Group CAPA problem XML """
items_text = "\n".join([
"<choice correct='{correct}'>{item}</choice>".format(correct=correct, item=item)
u"<choice correct='{correct}'>{item}</choice>".format(correct=correct, item=item)
for item, correct in items
])
return textwrap.dedent("""
return textwrap.dedent(u"""
<problem>
<p>{name}</p>
<multiplechoiceresponse>
@@ -234,7 +234,7 @@ class StudioLibraryContainerCapaFilterTest(LibraryContentTestBase, TestWithSearc
""" Generates Select Option CAPA problem XML """
items_text = ",".join(["'{0}'".format(item) for item in items])
return textwrap.dedent("""
return textwrap.dedent(u"""
<problem>
<p>{name}</p>
<optionresponse>

View File

@@ -263,8 +263,8 @@ class LoginFromCombinedPageTest(UniqueCourseTest):
"""
Create a new user with a unique name and email.
"""
username = "test_{uuid}".format(uuid=self.unique_id[0:6])
email = "{user}@example.com".format(user=username)
username = u"test_{uuid}".format(uuid=self.unique_id[0:6])
email = u"{user}@example.com".format(user=username)
password = "password"
# Create the user (automatically logs us in)
@@ -306,8 +306,8 @@ class RegisterFromCombinedPageTest(UniqueCourseTest):
self.register_page.visit()
# Fill in the form and submit it
username = "test_{uuid}".format(uuid=self.unique_id[0:6])
email = "{user}@example.com".format(user=username)
username = u"test_{uuid}".format(uuid=self.unique_id[0:6])
email = u"{user}@example.com".format(user=username)
self.register_page.register(
email=email,
password="password",
@@ -329,8 +329,8 @@ class RegisterFromCombinedPageTest(UniqueCourseTest):
# Don't agree to the terms of service / honor code.
# Don't specify a country code, which is required.
# Don't specify a favorite movie.
username = "test_{uuid}".format(uuid=self.unique_id[0:6])
email = "{user}@example.com".format(user=username)
username = u"test_{uuid}".format(uuid=self.unique_id[0:6])
email = u"{user}@example.com".format(user=username)
self.register_page.register(
email=email,
password="password",
@@ -721,7 +721,7 @@ class PDFTextBooksTabTest(UniqueCourseTest):
# Add PDF textbooks to course fixture.
for i in range(1, 3):
course_fix.add_textbook("PDF Book {}".format(i), [{"title": "Chapter Of Book {}".format(i), "url": ""}])
course_fix.add_textbook(u"PDF Book {}".format(i), [{"title": u"Chapter Of Book {}".format(i), "url": ""}])
course_fix.install()
@@ -736,7 +736,7 @@ class PDFTextBooksTabTest(UniqueCourseTest):
# Verify each PDF textbook tab by visiting, it will fail if correct tab is not loaded.
for i in range(1, 3):
self.tab_nav.go_to_tab("PDF Book {}".format(i))
self.tab_nav.go_to_tab(u"PDF Book {}".format(i))
@attr(shard=1)

View File

@@ -113,9 +113,9 @@ class CoursewareTest(UniqueCourseTest):
"""
xblocks = self.course_fix.get_nested_xblocks(category="problem")
for index in range(1, len(xblocks) + 1):
test_section_title = 'Test Section {}'.format(index)
test_subsection_title = 'Test Subsection {}'.format(index)
test_unit_title = 'Test Problem {}'.format(index)
test_section_title = u'Test Section {}'.format(index)
test_subsection_title = u'Test Subsection {}'.format(index)
test_unit_title = u'Test Problem {}'.format(index)
self.course_home_page.visit()
self.course_home_page.outline.go_to_section(test_section_title, test_subsection_title)
course_nav = self.courseware_page.nav

View File

@@ -9,8 +9,8 @@ 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 = '{dt:%b} {dt.day}, {dt.year}'
TEST_DATE_FORMAT = '{dt:%b} {dt.day}, {dt.year} {dt.hour:02}:{dt.minute:02}'
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}'
class BaseLmsDashboardTest(UniqueCourseTest):
@@ -201,7 +201,7 @@ class LmsDashboardPageTest(BaseLmsDashboardTest):
self.course_fixture.configure_course()
end_date = DEFAULT_SHORT_DATE_FORMAT.format(dt=course_end_date)
expected_course_date = "Ended - {end_date}".format(end_date=end_date)
expected_course_date = u"Ended - {end_date}".format(end_date=end_date)
# reload the page for changes to course date changes to appear in dashboard
self.dashboard_page.visit()
@@ -234,7 +234,7 @@ class LmsDashboardPageTest(BaseLmsDashboardTest):
self.course_fixture.configure_course()
start_date = DEFAULT_SHORT_DATE_FORMAT.format(dt=course_start_date)
expected_course_date = "Started - {start_date}".format(start_date=start_date)
expected_course_date = u"Started - {start_date}".format(start_date=start_date)
# reload the page for changes to course date changes to appear in dashboard
self.dashboard_page.visit()
@@ -267,7 +267,7 @@ class LmsDashboardPageTest(BaseLmsDashboardTest):
self.course_fixture.configure_course()
start_date = DEFAULT_SHORT_DATE_FORMAT.format(dt=course_start_date)
expected_course_date = "Starts - {start_date}".format(start_date=start_date)
expected_course_date = u"Starts - {start_date}".format(start_date=start_date)
# reload the page for changes to course date changes to appear in dashboard
self.dashboard_page.visit()
@@ -301,7 +301,7 @@ class LmsDashboardPageTest(BaseLmsDashboardTest):
self.course_fixture.configure_course()
start_date = TEST_DATE_FORMAT.format(dt=course_start_date)
expected_course_date = "Starts - {start_date} UTC".format(start_date=start_date)
expected_course_date = u"Starts - {start_date} UTC".format(start_date=start_date)
# reload the page for changes to course date changes to appear in dashboard
self.dashboard_page.visit()
@@ -338,7 +338,7 @@ class LmsDashboardPageTest(BaseLmsDashboardTest):
})
self.course_fixture._add_advanced_settings()
expected_course_date = "Starts - {start_date}".format(start_date=course_advertised_start)
expected_course_date = u"Starts - {start_date}".format(start_date=course_advertised_start)
self.dashboard_page.visit()
course_date = self.dashboard_page.get_course_date()

View File

@@ -59,14 +59,14 @@ class EdxNotesTestMixin(UniqueCourseTest):
XBlockFixtureDesc(
"html",
"Test HTML 2",
data="""<p><span class="{}">Annotate this!</span></p>""".format(self.selector)
data=u"""<p><span class="{}">Annotate this!</span></p>""".format(self.selector)
),
),
XBlockFixtureDesc("vertical", "Test Unit 2").add_children(
XBlockFixtureDesc(
"html",
"Test HTML 3",
data="""<p><span class="{}">Annotate this!</span></p>""".format(self.selector)
data=u"""<p><span class="{}">Annotate this!</span></p>""".format(self.selector)
),
),
),
@@ -75,7 +75,7 @@ class EdxNotesTestMixin(UniqueCourseTest):
XBlockFixtureDesc(
"html",
"Test HTML 4",
data="""
data=u"""
<p><span class="{}">Annotate this!</span></p>
""".format(self.selector)
),
@@ -88,14 +88,14 @@ class EdxNotesTestMixin(UniqueCourseTest):
XBlockFixtureDesc(
"html",
"Test HTML 5",
data="""
data=u"""
<p><span class="{}">Annotate this!</span></p>
""".format(self.selector)
),
XBlockFixtureDesc(
"html",
"Test HTML 6",
data="""<p><span class="{}">Annotate this!</span></p>""".format(self.selector)
data=u"""<p><span class="{}">Annotate this!</span></p>""".format(self.selector)
),
),
),
@@ -132,7 +132,7 @@ class EdxNotesDefaultInteractionsTest(EdxNotesTestMixin):
index = offset
for component in components:
for note in component.create_note(".{}".format(self.selector)):
note.text = "TEST TEXT {}".format(index)
note.text = u"TEST TEXT {}".format(index)
index += 1
def edit_notes(self, components, offset=0):
@@ -141,7 +141,7 @@ class EdxNotesDefaultInteractionsTest(EdxNotesTestMixin):
for component in components:
self.assertGreater(len(component.notes), 0)
for note in component.edit_note():
note.text = "TEST TEXT {}".format(index)
note.text = u"TEST TEXT {}".format(index)
index += 1
def edit_tags_in_notes(self, components, tags):
@@ -166,7 +166,7 @@ class EdxNotesDefaultInteractionsTest(EdxNotesTestMixin):
def assert_text_in_notes(self, notes):
actual = [note.text for note in notes]
expected = ["TEST TEXT {}".format(i) for i in xrange(len(notes))]
expected = [u"TEST TEXT {}".format(i) for i in xrange(len(notes))]
self.assertEqual(expected, actual)
def assert_tags_in_notes(self, notes, expected_tags):

View File

@@ -999,7 +999,7 @@ class CertificatesTest(BaseInstructorDashboardTest):
self.certificates_section.add_certificate_exception(self.user_name, '')
self.assertIn(
'{user} already in exception list.'.format(user=self.user_name),
u'{user} already in exception list.'.format(user=self.user_name),
self.certificates_section.message.text
)
@@ -1046,7 +1046,7 @@ class CertificatesTest(BaseInstructorDashboardTest):
self.certificates_section.wait_for_ajax()
self.assertIn(
"{user} does not exist in the LMS. Please check your spelling and retry.".format(user=invalid_user),
u"{user} does not exist in the LMS. Please check your spelling and retry.".format(user=invalid_user),
self.certificates_section.message.text
)
@@ -1079,7 +1079,7 @@ class CertificatesTest(BaseInstructorDashboardTest):
self.certificates_section.wait_for_ajax()
self.assertIn(
"{user} is not enrolled in this course. Please check your spelling and retry.".format(user=new_user),
u"{user} is not enrolled in this course. Please check your spelling and retry.".format(user=new_user),
self.certificates_section.message.text
)
@@ -1217,7 +1217,7 @@ class CertificateInvalidationTest(BaseInstructorDashboardTest):
# Validate success message
self.assertIn(
"Certificate has been successfully invalidated for {user}.".format(user=self.student_name),
u"Certificate has been successfully invalidated for {user}.".format(user=self.student_name),
self.certificates_section.certificate_invalidation_message.text
)

View File

@@ -21,7 +21,7 @@ class MatlabProblemTest(ProblemsTest):
"""
Create a matlab problem for the test.
"""
problem_data = dedent("""
problem_data = dedent(u"""
<problem markdown="null">
<text>
<p>

View File

@@ -76,7 +76,7 @@ class ProblemClarificationTest(ProblemsTest):
"""
Create a problem with a <clarification>
"""
xml = dedent("""
xml = dedent(u"""
<problem markdown="null">
<text>
<p>
@@ -705,7 +705,7 @@ class ProblemQuestionDescriptionTest(ProblemsTest):
"""
Create a problem with question and description.
"""
xml = dedent("""
xml = dedent(u"""
<problem>
<choiceresponse>
<label>Eggplant is a _____?</label>

View File

@@ -51,7 +51,7 @@ class ProblemTypeTestBaseMeta(ABCMeta):
]
for required_attr in required_attrs:
msg = ('{} is a required attribute for {}').format(
msg = (u'{} is a required attribute for {}').format(
required_attr, str(cls)
)
@@ -126,7 +126,7 @@ class ProblemTypeTestBase(ProblemsTest, EventsTestMixin):
Args:
status: one of ("correct", "incorrect", "unanswered", "submitted")
"""
msg = "Wait for status to be {}".format(status)
msg = u"Wait for status to be {}".format(status)
selector = ', '.join(self.status_indicators[status])
self.problem_page.wait_for_element_visibility(selector, msg)
@@ -536,9 +536,9 @@ class ProblemNeverShowCorrectnessMixin(object):
# Problem progress text depends on points possible
possible = 'possible (ungraded, results hidden)'
if self.problem_points == 1:
problem_progress = '1 point {}'.format(possible)
problem_progress = u'1 point {}'.format(possible)
else:
problem_progress = '{} points {}'.format(self.problem_points, possible)
problem_progress = u'{} points {}'.format(self.problem_points, possible)
# Make sure we're looking at the right problem
self.problem_page.wait_for(
@@ -994,7 +994,7 @@ class MultipleChoiceProblemTypeTestMultipleAttempt(MultipleChoiceProblemTypeBase
for attempts_used in range(3):
self.assertEqual(
self.problem_page.submission_feedback,
"You have used {} of 3 attempts".format(str(attempts_used)),
u"You have used {} of 3 attempts".format(str(attempts_used)),
"All 3 attempts are not available"
)
if attempts_used == 2:
@@ -1777,7 +1777,7 @@ class ChoiceTextProblemTypeTestBase(ProblemTypeTestBase):
Selects the nth (where n == input_num) choice of the problem.
"""
self.problem_page.q(
css='div.problem input.ctinput[type="{}"]'.format(self.choice_type)
css=u'div.problem input.ctinput[type="{}"]'.format(self.choice_type)
).nth(input_num).click()
def _fill_input_text(self, value, input_num):

View File

@@ -372,8 +372,7 @@ class SubsectionGradingPolicyA11yTest(SubsectionGradingPolicyBase):
# Verify that y-Axis labels are aria-hidden
self.assertEqual(['100%', 'true'], self.progress_page.y_tick_label(0))
self.assertEqual(['0%', 'true'], self.progress_page.y_tick_label(1))
self.assertEqual(['Pass 50%', 'true'], self.progress_page.y_tick_label(2))
self.assertEqual(['Pass 50%', 'true'], self.progress_page.y_tick_label(2)) # pylint: disable=unicode-format-string,line-too-long
# Verify x-Axis labels and sr-text
self._check_tick_text(0, [u'Homework 1 - Test Subsection 1 - 50% (1/2)'], u'HW 01')
@@ -434,7 +433,7 @@ class SubsectionGradingPolicyA11yTest(SubsectionGradingPolicyBase):
# Verify the overall score. The first element in the array is the sr-only text, and the
# second is the total text (including the sr-only text).
self.assertEqual(['Overall Score', 'Overall Score\n2%'], self.progress_page.graph_overall_score())
self.assertEqual(['Overall Score', 'Overall Score\n2%'], self.progress_page.graph_overall_score()) # pylint: disable=unicode-format-string,line-too-long
class ProgressPageA11yTest(ProgressPageBaseTest):

View File

@@ -56,8 +56,8 @@ class TeamsTabBase(EventsTestMixin, ForumsConfigMixin, UniqueCourseTest):
team = {
'course_id': self.course_id,
'topic_id': topic['id'],
'name': 'Team {}'.format(i),
'description': 'Description {}'.format(i),
'name': u'Team {}'.format(i),
'description': u'Description {}'.format(i),
'language': 'aa',
'country': 'AF'
}
@@ -623,7 +623,7 @@ class BrowseTeamsWithinTopicTest(TeamsTabBase):
self.assertEqual(search_results_page.header_name, 'Team Search')
self.assertEqual(
search_results_page.header_description,
'Showing results for "{search_query}"'.format(search_query=search_query)
u'Showing results for "{search_query}"'.format(search_query=search_query)
)
def verify_on_page(self, teams_page, page_num, total_teams, pagination_header_text, footer_visible):
@@ -916,7 +916,7 @@ class TeamFormActions(TeamsTabBase):
title='Create a New Team',
description='Create a new team if you can\'t find an existing team to join, '
'or if you would like to learn with friends you know.',
breadcrumbs='All Topics {topic_name}'.format(topic_name=self.topic['name'])
breadcrumbs=u'All Topics {topic_name}'.format(topic_name=self.topic['name'])
)
def verify_and_navigate_to_edit_team_page(self):
@@ -933,7 +933,7 @@ class TeamFormActions(TeamsTabBase):
title='Edit Team',
description='If you make significant changes, make sure you notify '
'members of the team before making these changes.',
breadcrumbs='All Topics {topic_name} {team_name}'.format(
breadcrumbs=u'All Topics {topic_name} {team_name}'.format(
topic_name=self.topic['name'],
team_name=self.team['name']
)

View File

@@ -144,7 +144,7 @@ class StudioLibraryTest(AcceptanceTest):
fixture = LibraryFixture(
'test_org',
self.unique_id,
'Test Library {}'.format(self.unique_id),
u'Test Library {}'.format(self.unique_id),
)
self.populate_library_fixture(fixture)
fixture.install()

View File

@@ -48,7 +48,7 @@ class NestedVerticalTest(ContainerBase):
self.group_a_item_1_action_index = 0
self.group_a_item_2_action_index = 1
self.duplicate_label = "Duplicate of '{0}'"
self.duplicate_label = u"Duplicate of '{0}'"
self.discussion_label = "Discussion"
course_fixture.add_children(
@@ -286,7 +286,7 @@ class BaseGroupConfigurationsTest(ContainerBase):
self.assertEqual("Access is not restricted", visibility_editor.current_groups_message)
else:
self.assertEqual(
"Access is restricted to: {groups}".format(groups=expected_current_groups),
u"Access is restricted to: {groups}".format(groups=expected_current_groups),
visibility_editor.current_groups_message
)
@@ -1236,8 +1236,8 @@ class MoveComponentTest(ContainerBase):
}
self.source_component_display_name = 'HTML 11'
self.source_xblock_category = 'component'
self.message_move = 'Success! "{display_name}" has been moved.'
self.message_undo = 'Move cancelled. "{display_name}" has been moved back to its original location.'
self.message_move = u'Success! "{display_name}" has been moved.'
self.message_undo = u'Move cancelled. "{display_name}" has been moved back to its original location.'
def populate_course_fixture(self, course_fixture):
"""

View File

@@ -141,7 +141,7 @@ class GradingPageTest(StudioCourseTest):
self.assertIn(
'0-3',
grade_ranges,
'expected range: 0-3, not found in grade ranges:{}'.format(grade_ranges)
u'expected range: 0-3, not found in grade ranges:{}'.format(grade_ranges)
)
def test_settings_are_persisted_on_save_only(self):

View File

@@ -150,7 +150,7 @@ class HTMLComponentEditorTests(ContainerBase):
--></style>
""
"""
content = '<p class="title">pages</p><style><!-- .title { color: red; } --></style>'
content = u'<p class="title">pages</p><style><!-- .title { color: red; } --></style>'
# Add HTML Text type component
self._add_component('Text')

View File

@@ -218,8 +218,8 @@ class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest, TestWithSe
And I set Problem Type selector so "Any"
Then I can see that "No matching content" warning is shown
"""
expected_tpl = "The specified library is configured to fetch {count} problems, " \
"but there are only {actual} matching problems."
expected_tpl = u"The specified library is configured to fetch {count} problems, " \
u"but there are only {actual} matching problems."
library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[1])

View File

@@ -43,9 +43,9 @@ class CertificatesTest(StudioCourseTest):
Makes signatory dict which can be used in the tests to create certificates
"""
return {
'name': '{prefix} Signatory Name'.format(prefix=prefix),
'title': '{prefix} Signatory Title'.format(prefix=prefix),
'organization': '{prefix} Signatory Organization'.format(prefix=prefix),
'name': u'{prefix} Signatory Name'.format(prefix=prefix),
'title': u'{prefix} Signatory Title'.format(prefix=prefix),
'organization': u'{prefix} Signatory Organization'.format(prefix=prefix),
}
def create_and_verify_certificate(self, course_title_override, existing_certs, signatories):

View File

@@ -106,7 +106,7 @@ class PagesTest(StudioCourseTest):
self.assertEqual(
static_tab_titles,
['Empty', 'First'],
'Order should be:["Empty", "First] but getting {} from the page'.format(static_tab_titles)
u'Order should be:["Empty", "First] but getting {} from the page'.format(static_tab_titles)
)
def test_user_can_reorder_builtin_tabs(self):

View File

@@ -28,14 +28,14 @@ class AnnotatableProblemTest(UniqueCourseTest):
USERNAME = "STAFF_TESTER"
EMAIL = "johndoe@example.com"
DATA_TEMPLATE = dedent("""\
DATA_TEMPLATE = dedent(u"""\
<annotatable>
<instructions>Instruction text</instructions>
<p>{}</p>
</annotatable>
""")
ANNOTATION_TEMPLATE = dedent("""\
ANNOTATION_TEMPLATE = dedent(u"""\
Before {0}.
<annotation title="region {0}" body="Comment {0}" highlight="yellow" problem="{0}">
Region Contents {0}
@@ -43,7 +43,7 @@ class AnnotatableProblemTest(UniqueCourseTest):
After {0}.
""")
PROBLEM_TEMPLATE = dedent("""\
PROBLEM_TEMPLATE = dedent(u"""\
<problem max_attempts="1" weight="">
<annotationresponse>
<annotationinput>
@@ -63,7 +63,7 @@ class AnnotatableProblemTest(UniqueCourseTest):
</problem>
""")
OPTION_TEMPLATE = """<option choice="{correctness}">{number}</option>"""
OPTION_TEMPLATE = u"""<option choice="{correctness}">{number}</option>"""
def setUp(self):
super(AnnotatableProblemTest, self).setUp()

View File

@@ -46,10 +46,10 @@ class VideoEventsTestMixin(EventsTestMixin, VideoBaseTest):
def assert_field_type(self, event_dict, field, field_type):
"""Assert that a particular `field` in the `event_dict` has a particular type"""
self.assertIn(field, event_dict, '{0} not found in the root of the event'.format(field))
self.assertIn(field, event_dict, u'{0} not found in the root of the event'.format(field))
self.assertTrue(
isinstance(event_dict[field], field_type),
'Expected "{key}" to be a "{field_type}", but it has the value "{value}" of type "{t}"'.format(
u'Expected "{key}" to be a "{field_type}", but it has the value "{value}" of type "{t}"'.format(
key=field,
value=event_dict[field],
t=type(event_dict[field]),
@@ -123,7 +123,7 @@ class VideoEventsTest(VideoEventsTestMixin):
)
for field in dynamic_string_fields:
self.assert_field_type(load_video_event, field, basestring)
self.assertIn(field, load_video_event, '{0} not found in the root of the event'.format(field))
self.assertIn(field, load_video_event, u'{0} not found in the root of the event'.format(field))
del load_video_event[field]
# A weak assertion for the timestamp as well
@@ -360,7 +360,7 @@ class VideoBumperEventsTest(VideoEventsTestMixin):
)
for field in dynamic_string_fields:
self.assert_field_type(load_video_event, field, basestring)
self.assertIn(field, load_video_event, '{0} not found in the root of the event'.format(field))
self.assertIn(field, load_video_event, u'{0} not found in the root of the event'.format(field))
del load_video_event[field]
# A weak assertion for the timestamp as well

View File

@@ -128,7 +128,7 @@ class VideoBaseTest(UniqueCourseTest):
:param vertical_index: index for the vertical display name
:return: XBlockFixtureDesc
"""
xblock_course_vertical = XBlockFixtureDesc('vertical', 'Test Vertical-{0}'.format(vertical_index))
xblock_course_vertical = XBlockFixtureDesc('vertical', u'Test Vertical-{0}'.format(vertical_index))
for video in vertical_contents:
xblock_course_vertical.add_children(
@@ -1202,8 +1202,8 @@ class HLSVideoTest(VideoBaseTest):
for line_no in range(5):
self.video.click_transcript_line(line_no=line_no)
self.video.wait_for_position('0:0{}'.format(line_no))
self.video.wait_for_position(u'0:0{}'.format(line_no))
for line_no in range(5):
self.video.seek('0:0{}'.format(line_no))
self.assertEqual(self.video.active_caption_text, 'Hi, edX welcomes you{}.'.format(line_no))
self.video.seek(u'0:0{}'.format(line_no))
self.assertEqual(self.video.active_caption_text, u'Hi, edX welcomes you{}.'.format(line_no))

View File

@@ -133,7 +133,7 @@ class reprwrapper(object):
"""
def __init__(self, func):
self._func = func
self.repr = 'Func: {}'.format(func.__name__)
self.repr = u'Func: {}'.format(func.__name__)
functools.update_wrapper(self, func)
def __call__(self, *args, **kw):