diff --git a/lms/static/js/commerce/views/receipt_view.js b/lms/static/js/commerce/views/receipt_view.js index a265632..c3c1fe7 100644 --- a/lms/static/js/commerce/views/receipt_view.js +++ b/lms/static/js/commerce/views/receipt_view.js @@ -3,7 +3,7 @@ */ var edx = edx || {}; -(function ($, _, _s, Backbone) { +(function ($, _, Backbone) { 'use strict'; edx.commerce = edx.commerce || {}; @@ -19,11 +19,6 @@ var edx = edx || {}; this.useEcommerceApi = this.ecommerceBasketId || this.ecommerceOrderNumber; _.bindAll(this, 'renderReceipt', 'renderError', 'getProviderData', 'renderProvider', 'getCourseData'); - /* Mix non-conflicting functions from underscore.string (all but include, contains, and reverse) into - * the Underscore namespace. - */ - _.mixin(_s.exports()); - this.render(); }, @@ -125,17 +120,16 @@ var edx = edx || {}; * @return {object} JQuery Promise. */ getReceiptData: function (orderId) { - var urlFormat = '/shoppingcart/receipt/%s/'; + var urlFormat = '/shoppingcart/receipt/{orderId}/'; if (this.ecommerceOrderNumber) { - urlFormat = '/api/commerce/v1/orders/%s/'; + urlFormat = '/api/commerce/v1/orders/{orderId}/'; } else if (this.ecommerceBasketId){ - urlFormat = '/api/commerce/v0/baskets/%s/order/'; + urlFormat = '/api/commerce/v0/baskets/{orderId}/order/'; } - return $.ajax({ - url: _.sprintf(urlFormat, orderId), + url: edx.StringUtils.interpolate(urlFormat, {orderId: orderId}), type: 'GET', dataType: 'json' }).retry({times: 5, timeout: 2000, statusCodes: [404]}); @@ -146,10 +140,10 @@ var edx = edx || {}; * @return {object} JQuery Promise. */ getProviderData: function (providerId) { - var providerUrl = '/api/credit/v1/providers/%s/'; + var providerUrl = '/api/credit/v1/providers/{providerId}/'; return $.ajax({ - url: _.sprintf(providerUrl, providerId), + url: edx.StringUtils.interpolate(providerUrl, {providerId: providerId}), type: 'GET', dataType: 'json', contentType: 'application/json', @@ -164,9 +158,9 @@ var edx = edx || {}; * @return {object} JQuery Promise. */ getCourseData: function (courseId) { - var courseDetailUrl = '/api/course_structure/v0/courses/%s/'; + var courseDetailUrl = '/api/course_structure/v0/courses/{courseId}/'; return $.ajax({ - url: _.sprintf(courseDetailUrl, courseId), + url: edx.StringUtils.interpolate(courseDetailUrl, {courseId: courseId}), type: 'GET', dataType: 'json' }); @@ -306,7 +300,7 @@ var edx = edx || {}; el: $('#receipt-container') }); -})(jQuery, _, _.str, Backbone); // jshint ignore:line +})(jQuery, _, Backbone); // jshint ignore:line function completeOrder(event) { // jshint ignore:line var courseKey = $(event).data("course-key"), diff --git a/lms/static/js/fixtures/commerce/checkout_receipt.html b/lms/static/js/fixtures/commerce/checkout_receipt.html new file mode 100644 index 0000000..9c0809a --- /dev/null +++ b/lms/static/js/fixtures/commerce/checkout_receipt.html @@ -0,0 +1,28 @@ +<div id="error-container" class="hidden"> + <div id="error" class="wrapper-msg wrapper-msg-activate"> + <div class=" msg msg-activate"> + <span class="msg-icon icon fa fa-exclamation-triangle" aria-hidden="true"></span> + + <div class="msg-content"> + <h3 class="title"> + Error + </h3> + <div class="copy"> + <p>dummy error text</p> + </div> + </div> + </div> + </div> +</div> + +<div class="container"> + <section class="wrapper carousel"> + <div id="receipt-container" class="pay-and-verify hidden" data-is-payment-complete='True' + data-platform-name='edx-platform' data-verified='True' data-username='user-1' + data-is-request-in-themed-site='True'> + <h2>Loading Order Data...</h2> + <span>Please wait while we retrieve your order details.</span> + </div> + </section> +</div> + diff --git a/lms/static/js/spec/commerce/receipt_view_spec.js b/lms/static/js/spec/commerce/receipt_view_spec.js new file mode 100644 index 0000000..f4abef3 --- /dev/null +++ b/lms/static/js/spec/commerce/receipt_view_spec.js @@ -0,0 +1,188 @@ +define([ + 'jquery', + 'jquery.ajax-retry', + 'js/commerce/views/receipt_view', + 'common/js/spec_helpers/ajax_helpers' + ], + function ($, AjaxRetry, ReceiptView, AjaxHelpers){ + 'use strict'; + describe('edx.commerce.ReceiptView', function() { + var data, courseResponseData, providerResponseData, mockRequests, mockRender, createReceiptView; + + createReceiptView = function() { + return new ReceiptView({el: $('#receipt-container')}); + }; + + mockRequests = function(requests, method, apiUrl, responseData) { + AjaxHelpers.expectRequest(requests, method, apiUrl); + AjaxHelpers.respondWithJson(requests, responseData); + }; + + mockRender = function(useEcommerceOrderNumber, isVerified) { + var requests, view, orderUrlFormat; + requests = AjaxHelpers.requests(this); + $("#receipt-container").data("verified", isVerified); + view = createReceiptView(); + view.useEcommerceApi = true; + if (useEcommerceOrderNumber){ + view.ecommerceOrderNumber = 'EDX-123456'; + orderUrlFormat = '/api/commerce/v1/orders/EDX-123456/'; + } + else { + view.ecommerceBasketId = 'EDX-123456'; + orderUrlFormat = '/api/commerce/v0/baskets/EDX-123456/order/'; + } + view.render(); + mockRequests(requests, 'GET', orderUrlFormat, data); + + mockRequests( + requests, 'GET', '/api/course_structure/v0/courses/course-v1:edx+dummy+2015_T3/', courseResponseData + ); + + mockRequests(requests, 'GET', '/api/credit/v1/providers/edx/', providerResponseData); + return view; + }; + + beforeEach(function() { + var receiptFixture, providerFixture; + // Stub analytics tracking + window.analytics = jasmine.createSpyObj('analytics', ['page', 'track', 'trackLink']); + + loadFixtures('js/fixtures/commerce/checkout_receipt.html'); + + receiptFixture = readFixtures('templates/commerce/receipt.underscore'); + providerFixture = readFixtures('templates/commerce/provider.underscore'); + appendSetFixtures( + '<script id=\"receipt-tpl\" type=\"text/template\" >' + receiptFixture + '</script>' + + '<script id=\"provider-tpl\" type=\"text/template\" >' + providerFixture + '</script>' + ); + + data = { + "status": "Open", + "billed_to": { + "city": "dummy city", + "first_name": "john", + "last_name": "doe", + "country": "AL", + "line2": "line2", + "line1": "line1", + "state": "", + "postcode": "12345" + }, + "lines": [ + { + "status": "Open", + "unit_price_excl_tax": "10.00", + "product": { + "attribute_values": [ + { + "name": "certificate_type", + "value": "verified" + }, + { + "name": "course_key", + "value": "course-v1:edx+dummy+2015_T3" + }, + { + "name": "credit_provider", + "value": "edx" + } + ], + "stockrecords": [ + { + "price_currency": "USD", + "product": 123, + "partner_sku": "1234ABC", + "partner": 1, + "price_excl_tax": "10.00", + "id": 123 + } + ], + "product_class": "Seat", + "title": "Dummy title", + "url": "https://ecom.edx.org/api/v2/products/123/", + "price": "10.00", + "expires": null, + "is_available_to_buy": true, + "id": 123, + "structure": "child" + }, + "line_price_excl_tax": "10.00", + "description": "dummy description", + "title": "dummy title", + "quantity": 1 + } + ], + "number": "EDX-123456", + "date_placed": "2016-01-01T01:01:01Z", + "currency": "USD", + "total_excl_tax": "10.00" + }; + providerResponseData = { + "id": "edx", + "display_name": "edX", + "url": "http://www.edx.org", + "status_url": "http://www.edx.org/status", + "description": "Nothing", + "enable_integration": false, + "fulfillment_instructions": "", + "thumbnail_url": "http://edx.org/thumbnail.png" + }; + courseResponseData = { + "id": "course-v1:edx+dummy+2015_T3", + "name": "receipt test", + "category": "course", + "org": "edx", + "run": "2015_T2", + "course": "CS420", + "uri": "http://test.com/api/course_structure/v0/courses/course-v1:edx+dummy+2015_T3/", + "image_url": "/test.jpg", + "start": "2030-01-01T00:00:00Z", + "end": null + }; + + }); + + it('sends analytic event when verified receipt is rendered', function() { + mockRender(true, 'True'); + expect(window.analytics.track).toHaveBeenCalledWith( + 'Completed Order', + { + orderId: 'EDX-123456', + total: '10.00', + currency: 'USD' + } + ); + + }); + + it('sends analytic event when non verified receipt is rendered', function() { + mockRender(true, 'False'); + expect(window.analytics.track).toHaveBeenCalledWith( + 'Completed Order', + { + orderId: 'EDX-123456', + total: '10.00', + currency: 'USD' + } + ); + + }); + + it('renders a receipt correctly with Ecommerce Order Number', function() { + var view; + + view = mockRender(true, 'True'); + expect(view.$('.course_name_placeholder').text()).toContain('receipt test'); + }); + + it('renders a receipt correctly with Ecommerce Basket Id', function() { + var view; + + view = mockRender(false, 'True'); + expect(view.$('.course_name_placeholder').text()).toContain('receipt test'); + }); + + }); + } +); diff --git a/lms/static/js/spec/main.js b/lms/static/js/spec/main.js index 34781f7..ee45508 100644 --- a/lms/static/js/spec/main.js +++ b/lms/static/js/spec/main.js @@ -1,4 +1,6 @@ (function(requirejs, define) { + 'use strict'; + // TODO: how can we share the vast majority of this config that is in common with CMS? requirejs.config({ baseUrl: '/base/', @@ -14,6 +16,7 @@ 'jquery.markitup': 'xmodule_js/common_static/js/vendor/markitup/jquery.markitup', 'jquery.leanModal': 'xmodule_js/common_static/js/vendor/jquery.leanModal', 'jquery.ajaxQueue': 'xmodule_js/common_static/js/vendor/jquery.ajaxQueue', + 'jquery.ajax-retry': 'js/vendor/jquery.ajax-retry', 'jquery.smoothScroll': 'xmodule_js/common_static/js/vendor/jquery.smooth-scroll.min', 'jquery.scrollTo': 'xmodule_js/common_static/js/vendor/jquery.scrollTo-1.4.2-min', 'jquery.timepicker': 'xmodule_js/common_static/js/vendor/timepicker/jquery.timepicker', @@ -72,6 +75,7 @@ // Manually specify LMS files that are not converted to RequireJS 'history': 'js/vendor/history', + 'js/commerce/views/receipt_view': 'js/commerce/views/receipt_view', 'js/staff_debug_actions': 'js/staff_debug_actions', 'js/vendor/jquery.qubit': 'js/vendor/jquery.qubit', 'js/utils/navigation': 'js/utils/navigation', @@ -314,6 +318,10 @@ exports: 'js/ccx/schedule', deps: ['jquery', 'underscore', 'backbone', 'gettext', 'moment'] }, + 'js/commerce/views/receipt_view': { + exports: 'edx.commerce.ReceiptView', + deps: ['jquery', 'jquery.url', 'backbone', 'underscore', 'string_utils'] + }, // Backbone classes loaded explicitly until they are converted to use RequireJS 'js/instructor_dashboard/ecommerce': { @@ -646,122 +654,123 @@ }); var testFiles = [ - 'js/spec/components/header/header_spec.js', + 'js/spec/api_admin/catalog_preview_spec.js', + 'js/spec/bookmarks/bookmark_button_view_spec.js', + 'js/spec/bookmarks/bookmarks_list_view_spec.js', + 'js/spec/ccx/schedule_spec.js', + 'js/spec/commerce/receipt_view_spec.js', 'js/spec/components/card/card_spec.js', - 'js/spec/staff_debug_actions_spec.js', - 'js/spec/views/notification_spec.js', - 'js/spec/views/file_uploader_spec.js', + 'js/spec/components/header/header_spec.js', + 'js/spec/courseware/updates_visibility.js', 'js/spec/dashboard/donation.js', 'js/spec/dashboard/dropdown_spec.js', 'js/spec/dashboard/track_events_spec.js', + 'js/spec/discovery/collections/filters_spec.js', + 'js/spec/discovery/discovery_factory_spec.js', + 'js/spec/discovery/models/course_card_spec.js', + 'js/spec/discovery/models/course_directory_spec.js', + 'js/spec/discovery/models/facet_option_spec.js', + 'js/spec/discovery/models/filter_spec.js', + 'js/spec/discovery/models/search_state_spec.js', + 'js/spec/discovery/views/course_card_spec.js', + 'js/spec/discovery/views/courses_listing_spec.js', + 'js/spec/discovery/views/filter_bar_spec.js', + 'js/spec/discovery/views/refine_sidebar_spec.js', + 'js/spec/discovery/views/search_form_spec.js', + 'js/spec/edxnotes/collections/notes_spec.js', + 'js/spec/edxnotes/models/note_spec.js', + 'js/spec/edxnotes/models/tab_spec.js', + 'js/spec/edxnotes/plugins/accessibility_spec.js', + 'js/spec/edxnotes/plugins/caret_navigation_spec.js', + 'js/spec/edxnotes/plugins/events_spec.js', + 'js/spec/edxnotes/plugins/scroller_spec.js', + 'js/spec/edxnotes/plugins/store_error_handler_spec.js', + 'js/spec/edxnotes/utils/logger_spec.js', + 'js/spec/edxnotes/views/note_item_spec.js', + 'js/spec/edxnotes/views/notes_factory_spec.js', + 'js/spec/edxnotes/views/notes_page_spec.js', + 'js/spec/edxnotes/views/notes_visibility_factory_spec.js', + 'js/spec/edxnotes/views/search_box_spec.js', + 'js/spec/edxnotes/views/shim_spec.js', + 'js/spec/edxnotes/views/tab_item_spec.js', + 'js/spec/edxnotes/views/tab_view_spec.js', + 'js/spec/edxnotes/views/tabs/course_structure_spec.js', + 'js/spec/edxnotes/views/tabs/recent_activity_spec.js', + 'js/spec/edxnotes/views/tabs/search_results_spec.js', + 'js/spec/edxnotes/views/tabs/tags_spec.js', + 'js/spec/edxnotes/views/tabs_list_spec.js', + 'js/spec/edxnotes/views/visibility_decorator_spec.js', + 'js/spec/financial-assistance/financial_assistance_form_view_spec.js', 'js/spec/groups/views/cohorts_spec.js', - 'js/spec/shoppingcart/shoppingcart_spec.js', - 'js/spec/instructor_dashboard/ecommerce_spec.js', - 'js/spec/instructor_dashboard/student_admin_spec.js', + 'js/spec/instructor_dashboard/certificates_bulk_exception_spec.js', 'js/spec/instructor_dashboard/certificates_exception_spec.js', 'js/spec/instructor_dashboard/certificates_invalidation_spec.js', - 'js/spec/instructor_dashboard/certificates_bulk_exception_spec.js', 'js/spec/instructor_dashboard/certificates_spec.js', - 'js/spec/student_account/account_spec.js', + 'js/spec/instructor_dashboard/ecommerce_spec.js', + 'js/spec/instructor_dashboard/student_admin_spec.js', + 'js/spec/learner_dashboard/certificate_view_spec.js', + 'js/spec/learner_dashboard/collection_list_view_spec.js', + 'js/spec/learner_dashboard/program_card_view_spec.js', + 'js/spec/learner_dashboard/sidebar_view_spec.js', + 'js/spec/markdown_editor_spec.js', + 'js/spec/navigation_spec.js', + 'js/spec/search/search_spec.js', + 'js/spec/shoppingcart/shoppingcart_spec.js', + 'js/spec/staff_debug_actions_spec.js', 'js/spec/student_account/access_spec.js', - 'js/spec/student_account/logistration_factory_spec.js', + 'js/spec/student_account/account_settings_factory_spec.js', + 'js/spec/student_account/account_settings_fields_spec.js', + 'js/spec/student_account/account_settings_view_spec.js', + 'js/spec/student_account/account_spec.js', + 'js/spec/student_account/emailoptin_spec.js', + 'js/spec/student_account/enrollment_spec.js', 'js/spec/student_account/finish_auth_spec.js', 'js/spec/student_account/hinted_login_spec.js', - 'js/spec/student_account/login_spec.js', 'js/spec/student_account/institution_login_spec.js', - 'js/spec/student_account/register_spec.js', + 'js/spec/student_account/login_spec.js', + 'js/spec/student_account/logistration_factory_spec.js', 'js/spec/student_account/password_reset_spec.js', - 'js/spec/student_account/enrollment_spec.js', - 'js/spec/student_account/emailoptin_spec.js', + 'js/spec/student_account/register_spec.js', 'js/spec/student_account/shoppingcart_spec.js', - 'js/spec/student_account/account_settings_factory_spec.js', - 'js/spec/student_account/account_settings_fields_spec.js', - 'js/spec/student_account/account_settings_view_spec.js', - 'js/spec/views/fields_spec.js', + 'js/spec/student_profile/badge_list_container_spec.js', + 'js/spec/student_profile/badge_list_view_spec.js', + 'js/spec/student_profile/badge_view_spec.js', 'js/spec/student_profile/learner_profile_factory_spec.js', - 'js/spec/student_profile/learner_profile_view_spec.js', 'js/spec/student_profile/learner_profile_fields_spec.js', - 'js/spec/student_profile/share_modal_view_spec.js', - 'js/spec/student_profile/badge_view_spec.js', + 'js/spec/student_profile/learner_profile_view_spec.js', 'js/spec/student_profile/section_two_tab_spec.js', - 'js/spec/student_profile/badge_list_view_spec.js', - 'js/spec/student_profile/badge_list_container_spec.js', + 'js/spec/student_profile/share_modal_view_spec.js', + 'js/spec/verify_student/image_input_spec.js', + 'js/spec/verify_student/make_payment_step_view_ab_testing_spec.js', + 'js/spec/verify_student/make_payment_step_view_spec.js', 'js/spec/verify_student/pay_and_verify_view_spec.js', 'js/spec/verify_student/reverify_view_spec.js', - 'js/spec/verify_student/webcam_photo_view_spec.js', - 'js/spec/verify_student/image_input_spec.js', 'js/spec/verify_student/review_photos_step_view_spec.js', - 'js/spec/verify_student/make_payment_step_view_spec.js', - 'js/spec/verify_student/make_payment_step_view_ab_testing_spec.js', - 'js/spec/edxnotes/utils/logger_spec.js', - 'js/spec/edxnotes/views/notes_factory_spec.js', - 'js/spec/edxnotes/views/shim_spec.js', - 'js/spec/edxnotes/views/note_item_spec.js', - 'js/spec/edxnotes/views/notes_page_spec.js', - 'js/spec/edxnotes/views/search_box_spec.js', - 'js/spec/edxnotes/views/tabs_list_spec.js', - 'js/spec/edxnotes/views/tab_item_spec.js', - 'js/spec/edxnotes/views/tab_view_spec.js', - 'js/spec/edxnotes/views/tabs/search_results_spec.js', - 'js/spec/edxnotes/views/tabs/recent_activity_spec.js', - 'js/spec/edxnotes/views/tabs/course_structure_spec.js', - 'js/spec/edxnotes/views/tabs/tags_spec.js', - 'js/spec/edxnotes/views/visibility_decorator_spec.js', - 'js/spec/edxnotes/views/notes_visibility_factory_spec.js', - 'js/spec/edxnotes/models/tab_spec.js', - 'js/spec/edxnotes/models/note_spec.js', - 'js/spec/edxnotes/plugins/accessibility_spec.js', - 'js/spec/edxnotes/plugins/events_spec.js', - 'js/spec/edxnotes/plugins/scroller_spec.js', - 'js/spec/edxnotes/plugins/caret_navigation_spec.js', - 'js/spec/edxnotes/plugins/store_error_handler_spec.js', - 'js/spec/edxnotes/collections/notes_spec.js', - 'js/spec/search/search_spec.js', - 'js/spec/navigation_spec.js', - 'js/spec/courseware/updates_visibility.js', - 'js/spec/discovery/collections/filters_spec.js', - 'js/spec/discovery/models/course_card_spec.js', - 'js/spec/discovery/models/course_directory_spec.js', - 'js/spec/discovery/models/facet_option_spec.js', - 'js/spec/discovery/models/filter_spec.js', - 'js/spec/discovery/models/search_state_spec.js', - 'js/spec/discovery/views/course_card_spec.js', - 'js/spec/discovery/views/courses_listing_spec.js', - 'js/spec/discovery/views/filter_bar_spec.js', - 'js/spec/discovery/views/refine_sidebar_spec.js', - 'js/spec/discovery/views/search_form_spec.js', - 'js/spec/discovery/discovery_factory_spec.js', - 'js/spec/ccx/schedule_spec.js', + 'js/spec/verify_student/webcam_photo_view_spec.js', + 'js/spec/views/fields_spec.js', + 'js/spec/views/file_uploader_spec.js', + 'js/spec/views/message_banner_spec.js', + 'js/spec/views/notification_spec.js', 'support/js/spec/collections/enrollment_spec.js', 'support/js/spec/models/enrollment_spec.js', + 'support/js/spec/views/certificates_spec.js', 'support/js/spec/views/enrollment_modal_spec.js', 'support/js/spec/views/enrollment_spec.js', - 'support/js/spec/views/certificates_spec.js', 'teams/js/spec/collections/topic_collection_spec.js', 'teams/js/spec/teams_tab_factory_spec.js', - 'teams/js/spec/views/edit_team_spec.js', 'teams/js/spec/views/edit_team_members_spec.js', + 'teams/js/spec/views/edit_team_spec.js', 'teams/js/spec/views/instructor_tools_spec.js', 'teams/js/spec/views/my_teams_spec.js', 'teams/js/spec/views/team_card_spec.js', 'teams/js/spec/views/team_discussion_spec.js', + 'teams/js/spec/views/team_profile_header_actions_spec.js', 'teams/js/spec/views/team_profile_spec.js', 'teams/js/spec/views/teams_spec.js', 'teams/js/spec/views/teams_tab_spec.js', 'teams/js/spec/views/topic_card_spec.js', 'teams/js/spec/views/topic_teams_spec.js', - 'teams/js/spec/views/topics_spec.js', - 'teams/js/spec/views/team_profile_header_actions_spec.js', - 'js/spec/financial-assistance/financial_assistance_form_view_spec.js', - 'js/spec/bookmarks/bookmarks_list_view_spec.js', - 'js/spec/bookmarks/bookmark_button_view_spec.js', - 'js/spec/views/message_banner_spec.js', - 'js/spec/markdown_editor_spec.js', - 'js/spec/learner_dashboard/collection_list_view_spec.js', - 'js/spec/learner_dashboard/sidebar_view_spec.js', - 'js/spec/learner_dashboard/program_card_view_spec.js', - 'js/spec/learner_dashboard/certificate_view_spec.js', - 'js/spec/api_admin/catalog_preview_spec.js', + 'teams/js/spec/views/topics_spec.js' ]; for (var i = 0; i < testFiles.length; i++) { diff --git a/lms/templates/commerce/receipt.underscore b/lms/templates/commerce/receipt.underscore index 6b9f06d..3e26954 100644 --- a/lms/templates/commerce/receipt.underscore +++ b/lms/templates/commerce/receipt.underscore @@ -89,13 +89,13 @@ <% if ( verified || is_request_in_themed_site) { %> <a class="next action-primary right" href="/dashboard"><%- gettext( "Go to Dashboard" ) %></a> <% } else { %> - <a id="verify_later_button" class="next action-secondary verify-later nav-link" href="/dashboard" data-tooltip="<%- _.sprintf( gettext( "If you don't verify your identity now, you can still explore your course from your dashboard. You will receive periodic reminders from %(platformName)s to verify your identity." ), { platformName: platformName } ) %>"> + <a id="verify_later_button" class="next action-secondary verify-later nav-link" href="/dashboard" data-tooltip="<%- edx.StringUtils.interpolate( gettext( "If you don't verify your identity now, you can still explore your course from your dashboard. You will receive periodic reminders from {platformName} to verify your identity." ), { platformName: platformName } ) %>"> <%- gettext( "Want to confirm your identity later?" ) %> </a> <a id="verify_now_button" class="next action-primary right" - href="<%- _.sprintf( '/verify_student/verify-now/%(courseKey)s/', { courseKey: courseKey } ) %>" + href="<%- edx.StringUtils.interpolate( '/verify_student/verify-now/{courseKey}/', { courseKey: courseKey } ) %>" > <%- gettext( "Verify Now" ) %> </a>