diff --git a/common/djangoapps/entitlements/models.py b/common/djangoapps/entitlements/models.py index 71ebf29af6..ba39546b09 100644 --- a/common/djangoapps/entitlements/models.py +++ b/common/djangoapps/entitlements/models.py @@ -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 diff --git a/common/djangoapps/entitlements/utils.py b/common/djangoapps/entitlements/utils.py index 84b5c5b13f..ce86389c18 100644 --- a/common/djangoapps/entitlements/utils.py +++ b/common/djangoapps/entitlements/utils.py @@ -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 diff --git a/common/djangoapps/student/tests/test_email.py b/common/djangoapps/student/tests/test_email.py index 7dd29304f9..002293c912 100644 --- a/common/djangoapps/student/tests/test_email.py +++ b/common/djangoapps/student/tests/test_email.py @@ -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) ) ] diff --git a/common/djangoapps/third_party_auth/admin.py b/common/djangoapps/third_party_auth/admin.py index 4387a26c3c..a3b527b149 100644 --- a/common/djangoapps/third_party_auth/admin.py +++ b/common/djangoapps/third_party_auth/admin.py @@ -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'Key pair incomplete/missing' + return HTML(u'Key pair incomplete/missing') pub1, pub2 = public_key[0:10], public_key[-10:] priv1, priv2 = private_key[0:10], private_key[-10:] - return u'Public: {}…{}
Private: {}…{}'.format(pub1, pub2, priv1, priv2) + return HTML(u'Public: {}…{}
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() ) diff --git a/common/djangoapps/third_party_auth/api/tests/test_views.py b/common/djangoapps/third_party_auth/api/tests/test_views.py index cb5e15aef9..36eb21c515 100644 --- a/common/djangoapps/third_party_auth/api/tests/test_views.py +++ b/common/djangoapps/third_party_auth/api/tests/test_views.py @@ -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( diff --git a/common/djangoapps/third_party_auth/api/views.py b/common/djangoapps/third_party_auth/api/views.py index 5004a65369..7481057228 100644 --- a/common/djangoapps/third_party_auth/api/views.py +++ b/common/djangoapps/third_party_auth/api/views.py @@ -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): diff --git a/common/djangoapps/third_party_auth/lti.py b/common/djangoapps/third_party_auth/lti.py index c383506712..f6bd4a87a0 100644 --- a/common/djangoapps/third_party_auth/lti.py +++ b/common/djangoapps/third_party_auth/lti.py @@ -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 diff --git a/common/djangoapps/third_party_auth/management/commands/saml.py b/common/djangoapps/third_party_auth/management/commands/saml.py index b126a2b649..8717162a01 100644 --- a/common/djangoapps/third_party_auth/management/commands/saml.py +++ b/common/djangoapps/third_party_auth/management/commands/saml.py @@ -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) ) ) diff --git a/common/djangoapps/third_party_auth/models.py b/common/djangoapps/third_party_auth/models.py index 5eb1512fa5..9adbac3c34 100644 --- a/common/djangoapps/third_party_auth/models.py +++ b/common/djangoapps/third_party_auth/models.py @@ -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, diff --git a/common/djangoapps/third_party_auth/pipeline.py b/common/djangoapps/third_party_auth/pipeline.py index 4a40148634..583791b764 100644 --- a/common/djangoapps/third_party_auth/pipeline.py +++ b/common/djangoapps/third_party_auth/pipeline.py @@ -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): diff --git a/common/djangoapps/third_party_auth/saml.py b/common/djangoapps/third_party_auth/saml.py index d2ea612459..8e5918575a 100644 --- a/common/djangoapps/third_party_auth/saml.py +++ b/common/djangoapps/third_party_auth/saml.py @@ -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) diff --git a/common/djangoapps/third_party_auth/tasks.py b/common/djangoapps/third_party_auth/tasks.py index ad19eda2be..02fb0b53d7 100644 --- a/common/djangoapps/third_party_auth/tasks.py +++ b/common/djangoapps/third_party_auth/tasks.py @@ -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 , not {}".format(xml.tag)) + raise MetadataParseError(Text(u"Expected root element to be , 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: diff --git a/common/djangoapps/third_party_auth/tests/specs/base.py b/common/djangoapps/third_party_auth/tests/specs/base.py index 190e8edfe5..05aa6e1894 100644 --- a/common/djangoapps/third_party_auth/tests/specs/base.py +++ b/common/djangoapps/third_party_auth/tests/specs/base.py @@ -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 %s' % self.provider.name, response.content) + self.assertIn( + u'successfully signed in with %s' % 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't linked" % self.provider.name, - response.content + u"successfully logged into your %s account, but this account isn't linked" % self.provider.name, + response.content.decode(response.charset) ) def assert_json_failure_response_is_username_collision(self, response): diff --git a/common/djangoapps/third_party_auth/tests/specs/test_testshib.py b/common/djangoapps/third_party_auth/tests/specs/test_testshib.py index e195e3efd2..ec6ba70da7 100644 --- a/common/djangoapps/third_party_auth/tests/specs/test_testshib.py +++ b/common/djangoapps/third_party_auth/tests/specs/test_testshib.py @@ -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('\S+)', r'(?P[^\)]+)', r'(?P\S+)', r'(?P\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) diff --git a/common/test/acceptance/fixtures/course.py b/common/test/acceptance/fixtures/course.py index 4d16d5bbf6..f9ac1c9c1c 100644 --- a/common/test/acceptance/fixtures/course.py +++ b/common/test/acceptance/fixtures/course.py @@ -74,7 +74,7 @@ class XBlockFixtureDesc(object): Return a string representation of the description. Useful for error messages. """ - return dedent(""" + return dedent(u""" ".format(**self._course_dict) + return u"".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 = [ - '
  • Example Handout
  • '.format(handout=handout) + u'
  • Example Handout
  • '.format(handout=handout) for handout in self._handouts ] - handouts_html = '
      {}
    '.format("".join(handouts_li)) + handouts_html = u'
      {}
    '.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): diff --git a/common/test/acceptance/fixtures/edxnotes.py b/common/test/acceptance/fixtures/edxnotes.py index 1fa20fd9f9..f8bfc1bd18 100644 --- a/common/test/acceptance/fixtures/edxnotes.py +++ b/common/test/acceptance/fixtures/edxnotes.py @@ -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 ) ) diff --git a/common/test/acceptance/fixtures/library.py b/common/test/acceptance/fixtures/library.py index b22b09bb8e..5e6b1af68e 100644 --- a/common/test/acceptance/fixtures/library.py +++ b/common/test/acceptance/fixtures/library.py @@ -35,7 +35,7 @@ class LibraryFixture(XBlockContainerFixture): """ String representation of the library fixture, useful for debugging. """ - return "".format(**self.library_info) + return u"".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 )) diff --git a/common/test/acceptance/fixtures/xqueue.py b/common/test/acceptance/fixtures/xqueue.py index c99a1afff5..0a019368d6 100644 --- a/common/test/acceptance/fixtures/xqueue.py +++ b/common/test/acceptance/fixtures/xqueue.py @@ -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)) diff --git a/common/test/acceptance/pages/lms/account_settings.py b/common/test/acceptance/pages/lms/account_settings.py index 1c1423c86d..30e21e6c6e 100644 --- a/common/test/acceptance/pages/lms/account_settings.py +++ b/common/test/acceptance/pages/lms/account_settings.py @@ -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): diff --git a/common/test/acceptance/pages/lms/annotation_component.py b/common/test/acceptance/pages/lms/annotation_component.py index ca6223cd9e..2a389d7a4f 100644 --- a/common/test/acceptance/pages/lms/annotation_component.py +++ b/common/test/acceptance/pages/lms/annotation_component.py @@ -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, diff --git a/common/test/acceptance/pages/lms/course_home.py b/common/test/acceptance/pages/lms/course_home.py index b1563ec040..e8ebccd4af 100644 --- a/common/test/acceptance/pages/lms/course_home.py +++ b/common/test/acceptance/pages/lms/course_home.py @@ -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) ) diff --git a/common/test/acceptance/pages/lms/courseware.py b/common/test/acceptance/pages/lms/courseware.py index 2eae42747a..4e3c3503d8 100644 --- a/common/test/acceptance/pages/lms/courseware.py +++ b/common/test/acceptance/pages/lms/courseware.py @@ -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 ) diff --git a/common/test/acceptance/pages/lms/dashboard.py b/common/test/acceptance/pages/lms/dashboard.py index ac5edb6072..78457521bb 100644 --- a/common/test/acceptance/pages/lms/dashboard.py +++ b/common/test/acceptance/pages/lms/dashboard.py @@ -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): diff --git a/common/test/acceptance/pages/lms/discussion.py b/common/test/acceptance/pages/lms/discussion.py index 7790f9df25..2f7700cc62 100644 --- a/common/test/acceptance/pages/lms/discussion.py +++ b/common/test/acceptance/pages/lms/discussion.py @@ -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" diff --git a/common/test/acceptance/pages/lms/edxnotes.py b/common/test/acceptance/pages/lms/edxnotes.py index dc31ebe681..4aea252095 100644 --- a/common/test/acceptance/pages/lms/edxnotes.py +++ b/common/test/acceptance/pages/lms/edxnotes.py @@ -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) diff --git a/common/test/acceptance/pages/lms/fields.py b/common/test/acceptance/pages/lms/fields.py index 24e028dfa6..f5322f515d 100644 --- a/common/test/acceptance/pages/lms/fields.py +++ b/common/test/acceptance/pages/lms/fields.py @@ -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 diff --git a/common/test/acceptance/pages/lms/instructor_dashboard.py b/common/test/acceptance/pages/lms/instructor_dashboard.py index 3ad1cebfdd..2c56e5874a 100644 --- a/common/test/acceptance/pages/lms/instructor_dashboard.py +++ b/common/test/acceptance/pages/lms/instructor_dashboard.py @@ -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): """ diff --git a/common/test/acceptance/pages/lms/learner_profile.py b/common/test/acceptance/pages/lms/learner_profile.py index 361661b673..26ad216b00 100644 --- a/common/test/acceptance/pages/lms/learner_profile.py +++ b/common/test/acceptance/pages/lms/learner_profile.py @@ -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() diff --git a/common/test/acceptance/pages/lms/library.py b/common/test/acceptance/pages/lms/library.py index bc5087b46d..fe53112448 100644 --- a/common/test/acceptance/pages/lms/library.py +++ b/common/test/acceptance/pages/lms/library.py @@ -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 diff --git a/common/test/acceptance/pages/lms/problem.py b/common/test/acceptance/pages/lms/problem.py index a333e25ab0..f99ef895d3 100644 --- a/common/test/acceptance/pages/lms/problem.py +++ b/common/test/acceptance/pages/lms/problem.py @@ -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 text hidden by an icon in rendering 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 diff --git a/common/test/acceptance/pages/lms/progress.py b/common/test/acceptance/pages/lms/progress.py index e09807dee4..5f689b3732 100644 --- a/common/test/acceptance/pages/lms/progress.py +++ b/common/test/acceptance/pages/lms/progress.py @@ -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 ) diff --git a/common/test/acceptance/pages/lms/tab_nav.py b/common/test/acceptance/pages/lms/tab_nav.py index 82545fad79..b001e7aa19 100644 --- a/common/test/acceptance/pages/lms/tab_nav.py +++ b/common/test/acceptance/pages/lms/tab_nav.py @@ -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): diff --git a/common/test/acceptance/pages/lms/teams.py b/common/test/acceptance/pages/lms/teams.py index a4084992c4..849547f68b 100644 --- a/common/test/acceptance/pages/lms/teams.py +++ b/common/test/acceptance/pages/lms/teams.py @@ -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' diff --git a/common/test/acceptance/pages/lms/video/video.py b/common/test/acceptance/pages/lms/video/video.py index 033d3cc0eb..078c1330dc 100644 --- a/common/test/acceptance/pages/lms/video/video.py +++ b/common/test/acceptance/pages/lms/video/video.py @@ -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 diff --git a/common/test/acceptance/pages/studio/asset_index.py b/common/test/acceptance/pages/studio/asset_index.py index cacf67fed5..65d4168bdf 100644 --- a/common/test/acceptance/pages/studio/asset_index.py +++ b/common/test/acceptance/pages/studio/asset_index.py @@ -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 diff --git a/common/test/acceptance/pages/studio/container.py b/common/test/acceptance/pages/studio/container.py index 850071e533..d664ec6be4 100644 --- a/common/test/acceptance/pages/studio/container.py +++ b/common/test/acceptance/pages/studio/container.py @@ -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

    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() diff --git a/common/test/acceptance/pages/studio/discussion_component_editor.py b/common/test/acceptance/pages/studio/discussion_component_editor.py index cc9328b3bc..e5aa11e826 100644 --- a/common/test/acceptance/pages/studio/discussion_component_editor.py +++ b/common/test/acceptance/pages/studio/discussion_component_editor.py @@ -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) diff --git a/common/test/acceptance/pages/studio/edit_tabs.py b/common/test/acceptance/pages/studio/edit_tabs.py index ce21a8d522..736f79cf95 100644 --- a/common/test/acceptance/pages/studio/edit_tabs.py +++ b/common/test/acceptance/pages/studio/edit_tabs.py @@ -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) diff --git a/common/test/acceptance/pages/studio/html_component_editor.py b/common/test/acceptance/pages/studio/html_component_editor.py index 04cab54306..477c3ce8d6 100644 --- a/common/test/acceptance/pages/studio/html_component_editor.py +++ b/common/test/acceptance/pages/studio/html_component_editor.py @@ -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) diff --git a/common/test/acceptance/pages/studio/import_export.py b/common/test/acceptance/pages/studio/import_export.py index bc249347dc..130d12a9a2 100644 --- a/common/test/acceptance/pages/studio/import_export.py +++ b/common/test/acceptance/pages/studio/import_export.py @@ -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): diff --git a/common/test/acceptance/pages/studio/index.py b/common/test/acceptance/pages/studio/index.py index a0afcb9ecb..20bd1a1714 100644 --- a/common/test/acceptance/pages/studio/index.py +++ b/common/test/acceptance/pages/studio/index.py @@ -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: diff --git a/common/test/acceptance/pages/studio/library.py b/common/test/acceptance/pages/studio/library.py index 141a8b333e..0416d1bb90 100644 --- a/common/test/acceptance/pages/studio/library.py +++ b/common/test/acceptance/pages/studio/library.py @@ -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) ) diff --git a/common/test/acceptance/pages/studio/overview.py b/common/test/acceptance/pages/studio/overview.py index dbad6c97d1..a056463fe3 100644 --- a/common/test/acceptance/pages/studio/overview.py +++ b/common/test/acceptance/pages/studio/overview.py @@ -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 "{}(, {!r})".format(self.__class__.__name__, self.locator) + return u"{}(, {!r})".format(self.__class__.__name__, self.locator) except AttributeError: - return "{}()".format(self.__class__.__name__) + return u"{}()".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): diff --git a/common/test/acceptance/pages/studio/pagination.py b/common/test/acceptance/pages/studio/pagination.py index cde6d2896e..975bc8fee5 100644 --- a/common/test/acceptance/pages/studio/pagination.py +++ b/common/test/acceptance/pages/studio/pagination.py @@ -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): diff --git a/common/test/acceptance/pages/studio/problem_editor.py b/common/test/acceptance/pages/studio/problem_editor.py index 10d40ddc30..d317902a29 100644 --- a/common/test/acceptance/pages/studio/problem_editor.py +++ b/common/test/acceptance/pages/studio/problem_editor.py @@ -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] diff --git a/common/test/acceptance/pages/studio/settings_certificates.py b/common/test/acceptance/pages/studio/settings_certificates.py index f785ecfcde..57de50b592 100644 --- a/common/test/acceptance/pages/studio/settings_certificates.py +++ b/common/test/acceptance/pages/studio/settings_certificates.py @@ -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): diff --git a/common/test/acceptance/pages/studio/settings_group_configurations.py b/common/test/acceptance/pages/studio/settings_group_configurations.py index fbd7def09f..073783ba73 100644 --- a/common/test/acceptance/pages/studio/settings_group_configurations.py +++ b/common/test/acceptance/pages/studio/settings_group_configurations.py @@ -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=''): diff --git a/common/test/acceptance/pages/studio/textbook_upload.py b/common/test/acceptance/pages/studio/textbook_upload.py index 3e8bc38d5e..2dcd88d707 100644 --- a/common/test/acceptance/pages/studio/textbook_upload.py +++ b/common/test/acceptance/pages/studio/textbook_upload.py @@ -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): """ diff --git a/common/test/acceptance/pages/studio/users.py b/common/test/acceptance/pages/studio/users.py index 751a4b9ba3..65df7f57e1 100644 --- a/common/test/acceptance/pages/studio/users.py +++ b/common/test/acceptance/pages/studio/users.py @@ -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): diff --git a/common/test/acceptance/pages/studio/utils.py b/common/test/acceptance/pages/studio/utils.py index 0d8f9bfe29..9ad379a232 100644 --- a/common/test/acceptance/pages/studio/utils.py +++ b/common/test/acceptance/pages/studio/utils.py @@ -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) diff --git a/common/test/acceptance/pages/studio/video/video.py b/common/test/acceptance/pages/studio/video/video.py index 94e1a7e0c2..a61deb7a1d 100644 --- a/common/test/acceptance/pages/studio/video/video.py +++ b/common/test/acceptance/pages/studio/video/video.py @@ -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') diff --git a/common/test/acceptance/pages/studio/xblock_editor.py b/common/test/acceptance/pages/studio/xblock_editor.py index 6206d5ffd1..21f0e6406c 100644 --- a/common/test/acceptance/pages/studio/xblock_editor.py +++ b/common/test/acceptance/pages/studio/xblock_editor.py @@ -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 diff --git a/common/test/acceptance/pages/xblock/acid.py b/common/test/acceptance/pages/xblock/acid.py index 2153b9c798..235815cb1d 100644 --- a/common/test/acceptance/pages/xblock/acid.py +++ b/common/test/acceptance/pages/xblock/acid.py @@ -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", diff --git a/common/test/acceptance/pages/xblock/utils.py b/common/test/acceptance/pages/xblock/utils.py index 0195c69449..c0deb4b74c 100644 --- a/common/test/acceptance/pages/xblock/utils.py +++ b/common/test/acceptance/pages/xblock/utils.py @@ -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() diff --git a/common/test/acceptance/tests/discussion/test_cohort_management.py b/common/test/acceptance/tests/discussion/test_cohort_management.py index 22553280bb..8ae1d597de 100644 --- a/common/test/acceptance/tests/discussion/test_cohort_management.py +++ b/common/test/acceptance/tests/discussion/test_cohort_management.py @@ -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: diff --git a/common/test/acceptance/tests/discussion/test_cohorts.py b/common/test/acceptance/tests/discussion/test_cohorts.py index 3d98f56fdb..f4cf7235f9 100644 --- a/common/test/acceptance/tests/discussion/test_cohorts.py +++ b/common/test/acceptance/tests/discussion/test_cohorts.py @@ -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. diff --git a/common/test/acceptance/tests/discussion/test_discussion.py b/common/test/acceptance/tests/discussion/test_discussion.py index c3c1f56216..93e2eab4ad 100644 --- a/common/test/acceptance/tests/discussion/test_discussion.py +++ b/common/test/acceptance/tests/discussion/test_discussion.py @@ -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 = ( - '

    {}

    '.format(url, description) + u'

    {}

    '.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 = ( - '

    {}

    '.format(url, description) + u'

    {}

    '.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 = ( - '

    Some content{}

    '.format( + u'

    Some content{}

    '.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") diff --git a/common/test/acceptance/tests/discussion/test_discussion_management.py b/common/test/acceptance/tests/discussion/test_discussion_management.py index c9abd58049..a1af93790f 100644 --- a/common/test/acceptance/tests/discussion/test_discussion_management.py +++ b/common/test/acceptance/tests/discussion/test_discussion_management.py @@ -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. diff --git a/common/test/acceptance/tests/helpers.py b/common/test/acceptance/tests/helpers.py index 4d323d5824..e95b4521b5 100644 --- a/common/test/acceptance/tests/helpers.py +++ b/common/test/acceptance/tests/helpers.py @@ -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 diff --git a/common/test/acceptance/tests/lms/test_library.py b/common/test/acceptance/tests/lms/test_library.py index 972dbe427c..8ceb16b117 100644 --- a/common/test/acceptance/tests/lms/test_library.py +++ b/common/test/acceptance/tests/lms/test_library.py @@ -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([ - "{item}".format(correct=correct, item=item) + u"{item}".format(correct=correct, item=item) for item, correct in items ]) - return textwrap.dedent(""" + return textwrap.dedent(u"""

    {name}

    @@ -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"""

    {name}

    diff --git a/common/test/acceptance/tests/lms/test_lms.py b/common/test/acceptance/tests/lms/test_lms.py index 34d089b72f..84656efecb 100644 --- a/common/test/acceptance/tests/lms/test_lms.py +++ b/common/test/acceptance/tests/lms/test_lms.py @@ -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) diff --git a/common/test/acceptance/tests/lms/test_lms_courseware.py b/common/test/acceptance/tests/lms/test_lms_courseware.py index 9cac09a192..2fc8aa0e8b 100644 --- a/common/test/acceptance/tests/lms/test_lms_courseware.py +++ b/common/test/acceptance/tests/lms/test_lms_courseware.py @@ -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 diff --git a/common/test/acceptance/tests/lms/test_lms_dashboard.py b/common/test/acceptance/tests/lms/test_lms_dashboard.py index a4c5c970ab..58cecf49a9 100644 --- a/common/test/acceptance/tests/lms/test_lms_dashboard.py +++ b/common/test/acceptance/tests/lms/test_lms_dashboard.py @@ -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() diff --git a/common/test/acceptance/tests/lms/test_lms_edxnotes.py b/common/test/acceptance/tests/lms/test_lms_edxnotes.py index a296ad8145..8a92a91145 100644 --- a/common/test/acceptance/tests/lms/test_lms_edxnotes.py +++ b/common/test/acceptance/tests/lms/test_lms_edxnotes.py @@ -59,14 +59,14 @@ class EdxNotesTestMixin(UniqueCourseTest): XBlockFixtureDesc( "html", "Test HTML 2", - data="""

    Annotate this!

    """.format(self.selector) + data=u"""

    Annotate this!

    """.format(self.selector) ), ), XBlockFixtureDesc("vertical", "Test Unit 2").add_children( XBlockFixtureDesc( "html", "Test HTML 3", - data="""

    Annotate this!

    """.format(self.selector) + data=u"""

    Annotate this!

    """.format(self.selector) ), ), ), @@ -75,7 +75,7 @@ class EdxNotesTestMixin(UniqueCourseTest): XBlockFixtureDesc( "html", "Test HTML 4", - data=""" + data=u"""

    Annotate this!

    """.format(self.selector) ), @@ -88,14 +88,14 @@ class EdxNotesTestMixin(UniqueCourseTest): XBlockFixtureDesc( "html", "Test HTML 5", - data=""" + data=u"""

    Annotate this!

    """.format(self.selector) ), XBlockFixtureDesc( "html", "Test HTML 6", - data="""

    Annotate this!

    """.format(self.selector) + data=u"""

    Annotate this!

    """.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): diff --git a/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py b/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py index 1e8495a45f..6a22d918e9 100644 --- a/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py +++ b/common/test/acceptance/tests/lms/test_lms_instructor_dashboard.py @@ -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 ) diff --git a/common/test/acceptance/tests/lms/test_lms_matlab_problem.py b/common/test/acceptance/tests/lms/test_lms_matlab_problem.py index 9206a0f108..dcb3ccdb4e 100644 --- a/common/test/acceptance/tests/lms/test_lms_matlab_problem.py +++ b/common/test/acceptance/tests/lms/test_lms_matlab_problem.py @@ -21,7 +21,7 @@ class MatlabProblemTest(ProblemsTest): """ Create a matlab problem for the test. """ - problem_data = dedent(""" + problem_data = dedent(u"""

    diff --git a/common/test/acceptance/tests/lms/test_lms_problems.py b/common/test/acceptance/tests/lms/test_lms_problems.py index 659df27859..907b69ecdf 100644 --- a/common/test/acceptance/tests/lms/test_lms_problems.py +++ b/common/test/acceptance/tests/lms/test_lms_problems.py @@ -76,7 +76,7 @@ class ProblemClarificationTest(ProblemsTest): """ Create a problem with a """ - xml = dedent(""" + xml = dedent(u"""

    @@ -705,7 +705,7 @@ class ProblemQuestionDescriptionTest(ProblemsTest): """ Create a problem with question and description. """ - xml = dedent(""" + xml = dedent(u""" diff --git a/common/test/acceptance/tests/lms/test_problem_types.py b/common/test/acceptance/tests/lms/test_problem_types.py index 9484195e55..c7187b4380 100644 --- a/common/test/acceptance/tests/lms/test_problem_types.py +++ b/common/test/acceptance/tests/lms/test_problem_types.py @@ -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): diff --git a/common/test/acceptance/tests/lms/test_progress_page.py b/common/test/acceptance/tests/lms/test_progress_page.py index c5494e2b7b..b8133e9bbf 100644 --- a/common/test/acceptance/tests/lms/test_progress_page.py +++ b/common/test/acceptance/tests/lms/test_progress_page.py @@ -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): diff --git a/common/test/acceptance/tests/lms/test_teams.py b/common/test/acceptance/tests/lms/test_teams.py index 233777b820..43c422610f 100644 --- a/common/test/acceptance/tests/lms/test_teams.py +++ b/common/test/acceptance/tests/lms/test_teams.py @@ -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'] ) diff --git a/common/test/acceptance/tests/studio/base_studio_test.py b/common/test/acceptance/tests/studio/base_studio_test.py index d882233253..70a0d45f1a 100644 --- a/common/test/acceptance/tests/studio/base_studio_test.py +++ b/common/test/acceptance/tests/studio/base_studio_test.py @@ -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() diff --git a/common/test/acceptance/tests/studio/test_studio_container.py b/common/test/acceptance/tests/studio/test_studio_container.py index e8ba36a73c..988cc6ae51 100644 --- a/common/test/acceptance/tests/studio/test_studio_container.py +++ b/common/test/acceptance/tests/studio/test_studio_container.py @@ -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): """ diff --git a/common/test/acceptance/tests/studio/test_studio_grading.py b/common/test/acceptance/tests/studio/test_studio_grading.py index e84c787374..798b9667e4 100644 --- a/common/test/acceptance/tests/studio/test_studio_grading.py +++ b/common/test/acceptance/tests/studio/test_studio_grading.py @@ -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): diff --git a/common/test/acceptance/tests/studio/test_studio_html_editor.py b/common/test/acceptance/tests/studio/test_studio_html_editor.py index 7756324d36..54288cc1e6 100644 --- a/common/test/acceptance/tests/studio/test_studio_html_editor.py +++ b/common/test/acceptance/tests/studio/test_studio_html_editor.py @@ -150,7 +150,7 @@ class HTMLComponentEditorTests(ContainerBase): --> "" """ - content = '

    pages

    ' + content = u'

    pages

    ' # Add HTML Text type component self._add_component('Text') diff --git a/common/test/acceptance/tests/studio/test_studio_library_container.py b/common/test/acceptance/tests/studio/test_studio_library_container.py index 3ad265efd7..cdf678669c 100644 --- a/common/test/acceptance/tests/studio/test_studio_library_container.py +++ b/common/test/acceptance/tests/studio/test_studio_library_container.py @@ -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]) diff --git a/common/test/acceptance/tests/studio/test_studio_settings_certificates.py b/common/test/acceptance/tests/studio/test_studio_settings_certificates.py index 92366e0b73..76a2eab7ce 100644 --- a/common/test/acceptance/tests/studio/test_studio_settings_certificates.py +++ b/common/test/acceptance/tests/studio/test_studio_settings_certificates.py @@ -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): diff --git a/common/test/acceptance/tests/studio/test_studio_tabs.py b/common/test/acceptance/tests/studio/test_studio_tabs.py index 6dc537ce94..79b261810c 100644 --- a/common/test/acceptance/tests/studio/test_studio_tabs.py +++ b/common/test/acceptance/tests/studio/test_studio_tabs.py @@ -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): diff --git a/common/test/acceptance/tests/test_annotatable.py b/common/test/acceptance/tests/test_annotatable.py index 0dbe587145..ce8d54d153 100644 --- a/common/test/acceptance/tests/test_annotatable.py +++ b/common/test/acceptance/tests/test_annotatable.py @@ -28,14 +28,14 @@ class AnnotatableProblemTest(UniqueCourseTest): USERNAME = "STAFF_TESTER" EMAIL = "johndoe@example.com" - DATA_TEMPLATE = dedent("""\ + DATA_TEMPLATE = dedent(u"""\ Instruction text

    {}

    """) - ANNOTATION_TEMPLATE = dedent("""\ + ANNOTATION_TEMPLATE = dedent(u"""\ Before {0}. Region Contents {0} @@ -43,7 +43,7 @@ class AnnotatableProblemTest(UniqueCourseTest): After {0}. """) - PROBLEM_TEMPLATE = dedent("""\ + PROBLEM_TEMPLATE = dedent(u"""\ @@ -63,7 +63,7 @@ class AnnotatableProblemTest(UniqueCourseTest): """) - OPTION_TEMPLATE = """""" + OPTION_TEMPLATE = u"""""" def setUp(self): super(AnnotatableProblemTest, self).setUp() diff --git a/common/test/acceptance/tests/video/test_video_events.py b/common/test/acceptance/tests/video/test_video_events.py index 88199b8d97..b1a8d5bf97 100644 --- a/common/test/acceptance/tests/video/test_video_events.py +++ b/common/test/acceptance/tests/video/test_video_events.py @@ -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 diff --git a/common/test/acceptance/tests/video/test_video_module.py b/common/test/acceptance/tests/video/test_video_module.py index 277f2acaa2..33c96e69bc 100644 --- a/common/test/acceptance/tests/video/test_video_module.py +++ b/common/test/acceptance/tests/video/test_video_module.py @@ -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)) diff --git a/common/test/utils.py b/common/test/utils.py index d4012d8006..6262f4180a 100644 --- a/common/test/utils.py +++ b/common/test/utils.py @@ -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):