Commit 198d33dc by Awais Committed by Awais Qureshi

New AB Testing URL for checkout page.

ECOM-2866
parent 068b439a
......@@ -102,11 +102,17 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
result = self.client.login(username=self.USERNAME, password=self.PASSWORD)
self.assertTrue(result, msg="Could not log in")
@ddt.data("verified", "professional")
def test_start_flow_not_verified(self, course_mode):
@ddt.data(
("verified", "verify_student_start_flow"),
("professional", "verify_student_start_flow"),
("verified", "verify_student_begin_flow"),
("professional", "verify_student_begin_flow")
)
@ddt.unpack
def test_start_flow_not_verified(self, course_mode, payment_flow):
course = self._create_course(course_mode)
self._enroll(course.id)
response = self._get_page('verify_student_start_flow', course.id)
response = self._get_page(payment_flow, course.id)
self._assert_displayed_mode(response, course_mode)
self._assert_steps_displayed(
response,
......@@ -120,11 +126,15 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
])
self._assert_upgrade_session_flag(False)
@ddt.data("no-id-professional")
def test_start_flow_with_no_id_professional(self, course_mode):
@ddt.data(
("no-id-professional", "verify_student_start_flow"),
("no-id-professional", "verify_student_begin_flow")
)
@ddt.unpack
def test_start_flow_with_no_id_professional(self, course_mode, payment_flow):
course = self._create_course(course_mode)
self._enroll(course.id)
response = self._get_page('verify_student_start_flow', course.id)
response = self._get_page(payment_flow, course.id)
self._assert_displayed_mode(response, course_mode)
self._assert_steps_displayed(
response,
......@@ -134,12 +144,26 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
self._assert_messaging(response, PayAndVerifyView.FIRST_TIME_VERIFY_MSG)
self._assert_requirements_displayed(response, [])
@ddt.data("expired", "denied")
def test_start_flow_expired_or_denied_verification(self, verification_status):
def test_ab_testing_page(self):
course = self._create_course("verified")
self._enroll(course.id, "verified")
response = self._get_page("verify_student_begin_flow", course.id)
self._assert_displayed_mode(response, "verified")
self.assertContains(response, "Upgrade to a Verified Certificate")
self.assertContains(response, "Before you upgrade to a certificate track,")
self.assertContains(response, "To receive a certificate, you must also verify your identity")
self.assertContains(response, "You will use your webcam to take a picture of")
@ddt.data(
("expired", "verify_student_start_flow"),
("denied", "verify_student_begin_flow")
)
@ddt.unpack
def test_start_flow_expired_or_denied_verification(self, verification_status, payment_flow):
course = self._create_course("verified")
self._enroll(course.id, "verified")
self._set_verification_status(verification_status)
response = self._get_page('verify_student_start_flow', course.id)
response = self._get_page(payment_flow, course.id)
# Expect the same content as when the user has not verified
self._assert_steps_displayed(
......@@ -154,18 +178,24 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
])
@ddt.data(
("verified", "submitted"),
("verified", "approved"),
("verified", "error"),
("professional", "submitted"),
("no-id-professional", None),
("verified", "submitted", "verify_student_start_flow"),
("verified", "approved", "verify_student_start_flow"),
("verified", "error", "verify_student_start_flow"),
("professional", "submitted", "verify_student_start_flow"),
("no-id-professional", None, "verify_student_start_flow"),
("verified", "submitted", "verify_student_begin_flow"),
("verified", "approved", "verify_student_begin_flow"),
("verified", "error", "verify_student_begin_flow"),
("professional", "submitted", "verify_student_begin_flow"),
("no-id-professional", None, "verify_student_begin_flow"),
)
@ddt.unpack
def test_start_flow_already_verified(self, course_mode, verification_status):
def test_start_flow_already_verified(self, course_mode, verification_status, payment_flow):
course = self._create_course(course_mode)
self._enroll(course.id)
self._set_verification_status(verification_status)
response = self._get_page('verify_student_start_flow', course.id)
response = self._get_page(payment_flow, course.id)
self._assert_displayed_mode(response, course_mode)
self._assert_steps_displayed(
response,
......@@ -175,11 +205,17 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
self._assert_messaging(response, PayAndVerifyView.FIRST_TIME_VERIFY_MSG)
self._assert_requirements_displayed(response, [])
@ddt.data("verified", "professional")
def test_start_flow_already_paid(self, course_mode):
@ddt.data(
("verified", "verify_student_start_flow"),
("professional", "verify_student_start_flow"),
("verified", "verify_student_begin_flow"),
("professional", "verify_student_begin_flow")
)
@ddt.unpack
def test_start_flow_already_paid(self, course_mode, payment_flow):
course = self._create_course(course_mode)
self._enroll(course.id, course_mode)
response = self._get_page('verify_student_start_flow', course.id)
response = self._get_page(payment_flow, course.id)
self._assert_displayed_mode(response, course_mode)
self._assert_steps_displayed(
response,
......@@ -192,15 +228,16 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
PayAndVerifyView.WEBCAM_REQ,
])
def test_start_flow_not_enrolled(self):
@ddt.data("verify_student_start_flow", "verify_student_begin_flow")
def test_start_flow_not_enrolled(self, payment_flow):
course = self._create_course("verified")
self._set_verification_status("submitted")
response = self._get_page('verify_student_start_flow', course.id)
response = self._get_page(payment_flow, course.id)
# This shouldn't happen if the student has been auto-enrolled,
# but if they somehow end up on this page without enrolling,
# treat them as if they need to pay
response = self._get_page('verify_student_start_flow', course.id)
response = self._get_page(payment_flow, course.id)
self._assert_steps_displayed(
response,
PayAndVerifyView.PAYMENT_STEPS,
......@@ -208,7 +245,8 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
)
self._assert_requirements_displayed(response, [])
def test_start_flow_unenrolled(self):
@ddt.data("verify_student_start_flow", "verify_student_begin_flow")
def test_start_flow_unenrolled(self, payment_flow):
course = self._create_course("verified")
self._set_verification_status("submitted")
self._enroll(course.id, "verified")
......@@ -216,7 +254,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
# If unenrolled, treat them like they haven't paid at all
# (we assume that they've gotten a refund or didn't pay initially)
response = self._get_page('verify_student_start_flow', course.id)
response = self._get_page(payment_flow, course.id)
self._assert_steps_displayed(
response,
PayAndVerifyView.PAYMENT_STEPS,
......@@ -225,27 +263,31 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
self._assert_requirements_displayed(response, [])
@ddt.data(
("verified", "submitted"),
("verified", "approved"),
("professional", "submitted")
("verified", "submitted", "verify_student_start_flow"),
("verified", "approved", "verify_student_start_flow"),
("professional", "submitted", "verify_student_start_flow"),
("verified", "submitted", "verify_student_begin_flow"),
("verified", "approved", "verify_student_begin_flow"),
("professional", "submitted", "verify_student_begin_flow")
)
@ddt.unpack
def test_start_flow_already_verified_and_paid(self, course_mode, verification_status):
def test_start_flow_already_verified_and_paid(self, course_mode, verification_status, payment_flow):
course = self._create_course(course_mode)
self._enroll(course.id, course_mode)
self._set_verification_status(verification_status)
response = self._get_page(
'verify_student_start_flow',
payment_flow,
course.id,
expected_status_code=302
)
self._assert_redirects_to_dashboard(response)
@patch.dict(settings.FEATURES, {"IS_EDX_DOMAIN": True})
def test_pay_and_verify_hides_header_nav(self):
@ddt.data("verify_student_start_flow", "verify_student_begin_flow")
def test_pay_and_verify_hides_header_nav(self, payment_flow):
course = self._create_course("verified")
self._enroll(course.id, "verified")
response = self._get_page('verify_student_start_flow', course.id)
response = self._get_page(payment_flow, course.id)
# Verify that the header navigation links are hidden for the edx.org version
self.assertNotContains(response, "How it Works")
......@@ -351,7 +393,8 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
PayAndVerifyView.WEBCAM_REQ,
])
def test_payment_cannot_skip(self):
@ddt.data("verify_student_start_flow", "verify_student_begin_flow")
def test_payment_cannot_skip(self, payment_flow):
"""
Simple test to verify that certain steps cannot be skipped. This test sets up
a scenario where the user should be on the MAKE_PAYMENT_STEP, but is trying to
......@@ -360,7 +403,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
"""
course = self._create_course("verified")
response = self._get_page(
'verify_student_start_flow',
payment_flow,
course.id,
skip_first_step=True
)
......@@ -523,6 +566,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
pages = [
'verify_student_start_flow',
'verify_student_begin_flow',
'verify_student_verify_now',
'verify_student_upgrade_and_verify',
]
......@@ -534,16 +578,25 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
expected_status_code=404
)
@ddt.data([], ["no-id-professional", "professional"], ["honor", "audit"])
def test_no_id_professional_entry_point(self, modes_available):
@ddt.data(
([], "verify_student_start_flow"),
(["no-id-professional", "professional"], "verify_student_start_flow"),
(["honor", "audit"], "verify_student_start_flow"),
([], "verify_student_begin_flow"),
(["no-id-professional", "professional"], "verify_student_begin_flow"),
(["honor", "audit"], "verify_student_begin_flow"),
)
@ddt.unpack
def test_no_id_professional_entry_point(self, modes_available, payment_flow):
course = self._create_course(*modes_available)
if "no-id-professional" in modes_available or "professional" in modes_available:
self._get_page("verify_student_start_flow", course.id, expected_status_code=200)
self._get_page(payment_flow, course.id, expected_status_code=200)
else:
self._get_page("verify_student_start_flow", course.id, expected_status_code=404)
self._get_page(payment_flow, course.id, expected_status_code=404)
@ddt.data(
"verify_student_start_flow",
"verify_student_begin_flow",
"verify_student_verify_now",
"verify_student_upgrade_and_verify",
)
......@@ -561,6 +614,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
@ddt.data(
"verify_student_start_flow",
"verify_student_begin_flow",
"verify_student_verify_now",
"verify_student_upgrade_and_verify",
)
......@@ -572,11 +626,12 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
expected_status_code=404
)
def test_account_not_active(self):
@ddt.data("verify_student_start_flow", "verify_student_begin_flow")
def test_account_not_active(self, payment_flow):
self.user.is_active = False
self.user.save()
course = self._create_course("verified")
response = self._get_page('verify_student_start_flow', course.id)
response = self._get_page(payment_flow, course.id)
self._assert_steps_displayed(
response,
PayAndVerifyView.PAYMENT_STEPS + PayAndVerifyView.VERIFICATION_STEPS,
......@@ -588,32 +643,36 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
PayAndVerifyView.WEBCAM_REQ,
])
def test_no_contribution(self):
@ddt.data("verify_student_start_flow", "verify_student_begin_flow")
def test_no_contribution(self, payment_flow):
# Do NOT specify a contribution for the course in a session var.
course = self._create_course("verified")
response = self._get_page("verify_student_start_flow", course.id)
response = self._get_page(payment_flow, course.id)
self._assert_contribution_amount(response, "")
def test_contribution_other_course(self):
@ddt.data("verify_student_start_flow", "verify_student_begin_flow")
def test_contribution_other_course(self, payment_flow):
# Specify a contribution amount for another course in the session
course = self._create_course("verified")
other_course_id = CourseLocator(org="other", run="test", course="test")
self._set_contribution("12.34", other_course_id)
# Expect that the contribution amount is NOT pre-filled,
response = self._get_page("verify_student_start_flow", course.id)
response = self._get_page(payment_flow, course.id)
self._assert_contribution_amount(response, "")
def test_contribution(self):
@ddt.data("verify_student_start_flow", "verify_student_begin_flow")
def test_contribution(self, payment_flow):
# Specify a contribution amount for this course in the session
course = self._create_course("verified")
self._set_contribution("12.34", course.id)
# Expect that the contribution amount is pre-filled,
response = self._get_page("verify_student_start_flow", course.id)
response = self._get_page(payment_flow, course.id)
self._assert_contribution_amount(response, "12.34")
def test_verification_deadline(self):
@ddt.data("verify_student_start_flow", "verify_student_begin_flow")
def test_verification_deadline(self, payment_flow):
deadline = datetime(2999, 1, 2, tzinfo=pytz.UTC)
course = self._create_course("verified")
......@@ -625,7 +684,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
self._set_deadlines(course.id, upgrade_deadline=deadline, verification_deadline=deadline)
# Expect that the expiration date is set
response = self._get_page("verify_student_start_flow", course.id)
response = self._get_page(payment_flow, course.id)
data = self._get_page_data(response)
self.assertEqual(data['verification_deadline'], "Jan 02, 2999 at 00:00 UTC")
......@@ -661,7 +720,9 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
# Try to pay or upgrade.
# We should get an error message since the deadline has passed.
for page_name in ["verify_student_start_flow", "verify_student_upgrade_and_verify"]:
for page_name in ["verify_student_start_flow",
"verify_student_begin_flow",
"verify_student_upgrade_and_verify"]:
response = self._get_page(page_name, course.id)
self.assertContains(response, "Upgrade Deadline Has Passed")
......@@ -708,18 +769,20 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
self.assertContains(response, "Jan 02, 1999 at 00:00 UTC")
@mock.patch.dict(settings.FEATURES, {'EMBARGO': True})
def test_embargo_restrict(self):
@ddt.data("verify_student_start_flow", "verify_student_begin_flow")
def test_embargo_restrict(self, payment_flow):
course = self._create_course("verified")
with restrict_course(course.id) as redirect_url:
# Simulate that we're embargoed from accessing this
# course based on our IP address.
response = self._get_page('verify_student_start_flow', course.id, expected_status_code=302)
response = self._get_page(payment_flow, course.id, expected_status_code=302)
self.assertRedirects(response, redirect_url)
@mock.patch.dict(settings.FEATURES, {'EMBARGO': True})
def test_embargo_allow(self):
@ddt.data("verify_student_start_flow", "verify_student_begin_flow")
def test_embargo_allow(self, payment_flow):
course = self._create_course("verified")
self._get_page('verify_student_start_flow', course.id)
self._get_page(payment_flow, course.id)
def _create_course(self, *course_modes, **kwargs):
"""Create a new course with the specified course modes. """
......@@ -918,7 +981,8 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
url = reverse('verify_student_upgrade_and_verify', kwargs={'course_id': unicode(course_id)})
self.assertRedirects(response, url)
def test_course_upgrade_page_with_unicode_and_special_values_in_display_name(self):
@ddt.data("verify_student_start_flow", "verify_student_begin_flow")
def test_course_upgrade_page_with_unicode_and_special_values_in_display_name(self, payment_flow):
"""Check the course information on the page. """
mode_display_name = u"Introduction à l'astrophysique"
course = CourseFactory.create(display_name=mode_display_name)
......@@ -932,13 +996,14 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
)
self._enroll(course.id)
response_dict = self._get_page_data(self._get_page('verify_student_start_flow', course.id))
response_dict = self._get_page_data(self._get_page(payment_flow, course.id))
self.assertEqual(response_dict['course_name'], mode_display_name)
@httpretty.activate
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY)
def test_processors_api(self):
@ddt.data("verify_student_start_flow", "verify_student_begin_flow")
def test_processors_api(self, payment_flow):
"""
Check that when working with a product being processed by the
ecommerce api, we correctly call to that api for the list of
......@@ -957,7 +1022,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
content_type="application/json",
)
# make the server request
response = self._get_page('verify_student_start_flow', course.id)
response = self._get_page(payment_flow, course.id)
self.assertEqual(response.status_code, 200)
# ensure the mock api call was made. NOTE: the following line
......
......@@ -23,6 +23,16 @@ urlpatterns = patterns(
}
),
# This is for A/B testing.
url(
r'^begin-flow/{course}/$'.format(course=settings.COURSE_ID_PATTERN),
views.PayAndVerifyView.as_view(),
name="verify_student_begin_flow",
kwargs={
'message': views.PayAndVerifyView.FIRST_TIME_VERIFY_MSG
}
),
# The user is enrolled in a non-paid mode and wants to upgrade.
# This is the same as the "start verification" flow,
# except with slight messaging changes.
......
......@@ -423,7 +423,9 @@ class PayAndVerifyView(View):
'verification_good_until': verification_good_until,
'capture_sound': staticfiles_storage.url("audio/camera_capture.wav"),
'nav_hidden': True,
'is_ab_testing': 'begin-flow' in request.path,
}
return render_to_response("verify_student/pay_and_verify.html", context)
def _redirect_if_necessary(
......
......@@ -674,6 +674,7 @@
'lms/include/js/spec/verify_student/image_input_spec.js',
'lms/include/js/spec/verify_student/review_photos_step_view_spec.js',
'lms/include/js/spec/verify_student/make_payment_step_view_spec.js',
'lms/include/js/spec/verify_student/make_payment_step_view_ab_testing_spec.js',
'lms/include/js/spec/edxnotes/utils/logger_spec.js',
'lms/include/js/spec/edxnotes/views/notes_factory_spec.js',
'lms/include/js/spec/edxnotes/views/shim_spec.js',
......
define([
'jquery',
'underscore',
'backbone',
'common/js/spec_helpers/ajax_helpers',
'common/js/spec_helpers/template_helpers',
'js/verify_student/views/make_payment_step_view'
],
function( $, _, Backbone, AjaxHelpers, TemplateHelpers, MakePaymentStepView ) {
'use strict';
var checkPaymentButtons,
expectPaymentSubmitted,
goToPayment,
expectPaymentDisabledBecauseInactive,
expectPaymentButtonEnabled,
expectPriceSelected,
createView,
SERVER_ERROR_MSG = 'An error occurred!';
describe( 'edx.verify_student.MakePaymentStepView', function() {
var STEP_DATA = {
minPrice: '12',
currency: 'usd',
processors: ['test-payment-processor'],
courseKey: 'edx/test/test',
courseModeSlug: 'verified',
isABTesting: true
};
createView = function( stepDataOverrides ) {
var view = new MakePaymentStepView({
el: $( '#current-step-container' ),
stepData: _.extend( _.clone( STEP_DATA ), stepDataOverrides ),
errorModel: new ( Backbone.Model.extend({}) )()
}).render();
// Stub the payment form submission
spyOn( view, 'submitForm' ).andCallFake( function() {} );
return view;
};
expectPriceSelected = function( price ) {
var sel = $( 'input[name="contribution"]' );
// check that contribution value is same as price given
expect( sel.length ).toEqual(1);
expect( sel.val() ).toEqual(price);
};
expectPaymentButtonEnabled = function( isEnabled ) {
var el = $( '.payment-button'),
appearsDisabled = el.hasClass( 'is-disabled' ),
isDisabled = el.prop( 'disabled' );
expect( appearsDisabled ).not.toEqual( isEnabled );
expect( isDisabled ).not.toEqual( isEnabled );
};
expectPaymentDisabledBecauseInactive = function() {
var payButton = $( '.payment-button' );
// Payment button should be hidden
expect( payButton.length ).toEqual(0);
};
goToPayment = function( requests, kwargs ) {
var params = {
contribution: kwargs.amount || '',
course_id: kwargs.courseId || '',
processor: kwargs.processor || '',
sku: kwargs.sku || ''
};
// Click the "go to payment" button
$( '.payment-button' ).click();
// Verify that the request was made to the server
AjaxHelpers.expectPostRequest(
requests, '/verify_student/create_order/', $.param( params )
);
// Simulate the server response
if ( kwargs.succeeds ) {
// TODO put fixture responses in the right place
AjaxHelpers.respondWithJson(
requests, {payment_page_url: 'http://payment-page-url/', payment_form_data: {foo: 'bar'}}
);
} else {
AjaxHelpers.respondWithTextError( requests, 400, SERVER_ERROR_MSG);
}
};
expectPaymentSubmitted = function( view, params ) {
var form;
expect(view.submitForm).toHaveBeenCalled();
form = view.submitForm.mostRecentCall.args[0];
expect(form.serialize()).toEqual($.param(params));
expect(form.attr('method')).toEqual('POST');
expect(form.attr('action')).toEqual('http://payment-page-url/');
};
checkPaymentButtons = function( requests, buttons ) {
var $el = $( '.payment-button' );
expect($el.length).toEqual(_.size(buttons));
_.each(buttons, function( expectedText, expectedId ) {
var buttonEl = $( '#' + expectedId),
request;
buttonEl.removeAttr('disabled');
expect( buttonEl.length ).toEqual( 1 );
expect( buttonEl[0] ).toHaveClass( 'payment-button' );
expect( buttonEl[0] ).toHaveText( expectedText );
expect( buttonEl[0] ).toHaveClass( 'action-primary-blue' );
buttonEl[0].click();
expect( buttonEl[0] ).toHaveClass( 'is-selected' );
expectPaymentButtonEnabled( false );
request = AjaxHelpers.currentRequest(requests);
expect(request.requestBody.split('&')).toContain('processor=' + expectedId);
AjaxHelpers.respondWithJson(requests, {});
});
};
beforeEach(function() {
window.analytics = jasmine.createSpyObj('analytics', ['track', 'page', 'trackLink']);
setFixtures( '<div id="current-step-container"></div>' );
TemplateHelpers.installTemplate( 'templates/verify_student/make_payment_step_ab_testing' );
});
it( 'A/B Testing: check Initialize method with AB testing enable ', function() {
var view = createView();
expect( view.templateName ).toEqual('make_payment_step_ab_testing');
expect( view.btnClass ).toEqual('action-primary-blue');
});
it( 'shows users only minimum price', function() {
var view = createView(),
requests = AjaxHelpers.requests(this);
expectPriceSelected( STEP_DATA.minPrice );
expectPaymentButtonEnabled( true );
goToPayment( requests, {
amount: STEP_DATA.minPrice,
courseId: STEP_DATA.courseKey,
processor: STEP_DATA.processors[0],
succeeds: true
});
expectPaymentSubmitted( view, {foo: 'bar'} );
});
it( 'A/B Testing: provides working payment buttons for a single processor', function() {
createView({processors: ['cybersource']});
checkPaymentButtons( AjaxHelpers.requests(this), {cybersource: 'Checkout'});
});
it( 'A/B Testing: provides working payment buttons for multiple processors', function() {
createView({processors: ['cybersource', 'paypal', 'other']});
checkPaymentButtons( AjaxHelpers.requests(this), {
cybersource: 'Checkout',
paypal: 'Checkout with PayPal',
other: 'Checkout with other'
});
});
it( 'A/B Testing: by default minimum price is selected if no suggested prices are given', function() {
var view = createView(),
requests = AjaxHelpers.requests( this );
expectPriceSelected( STEP_DATA.minPrice);
expectPaymentButtonEnabled( true );
goToPayment( requests, {
amount: STEP_DATA.minPrice,
courseId: STEP_DATA.courseKey,
processor: STEP_DATA.processors[0],
succeeds: true
});
expectPaymentSubmitted( view, {foo: 'bar'} );
});
it( 'A/B Testing: min price is always selected even if contribution amount is provided', function() {
// Pre-select a price NOT in the suggestions
createView({
contributionAmount: '99.99'
});
// Expect that the price is filled in
expectPriceSelected( STEP_DATA.minPrice );
});
it( 'A/B Testing: disables payment for inactive users', function() {
createView({ isActive: false });
expectPaymentDisabledBecauseInactive();
});
it( 'A/B Testing: displays an error if the order could not be created', function() {
var requests = AjaxHelpers.requests( this ),
view = createView();
goToPayment( requests, {
amount: STEP_DATA.minPrice,
courseId: STEP_DATA.courseKey,
processor: STEP_DATA.processors[0],
succeeds: false
});
// Expect that an error is displayed
expect( view.errorModel.get('shown') ).toBe( true );
expect( view.errorModel.get('errorTitle') ).toEqual( 'Could not submit order' );
expect( view.errorModel.get('errorMsg') ).toEqual( SERVER_ERROR_MSG );
// Expect that the payment button is re-enabled
expectPaymentButtonEnabled( true );
});
});
}
);
......@@ -108,6 +108,7 @@ define([
buttonEl.removeAttr('disabled');
expect( buttonEl.length ).toEqual( 1 );
expect( buttonEl[0] ).toHaveClass( 'payment-button' );
expect( buttonEl[0] ).toHaveClass( 'action-primary' );
expect( buttonEl[0] ).toHaveText( expectedText );
buttonEl[0].click();
......@@ -216,6 +217,12 @@ define([
'Try the transaction again in a few minutes.'
);
});
it( 'check Initialize method without AB testing ', function() {
var view = createView();
expect( view.templateName ).toEqual('make_payment_step');
expect( view.btnClass ).toEqual('action-primary');
});
});
}
);
......@@ -66,7 +66,8 @@ var edx = edx || {};
verificationDeadline: el.data('verification-deadline'),
courseModeSlug: el.data('course-mode-slug'),
alreadyVerified: el.data('already-verified'),
verificationGoodUntil: el.data('verification-good-until')
verificationGoodUntil: el.data('verification-good-until'),
isABTesting: el.data('is-ab-testing')
},
'payment-confirmation-step': {
courseKey: el.data('course-key'),
......
......@@ -11,6 +11,15 @@ var edx = edx || {};
edx.verify_student.MakePaymentStepView = edx.verify_student.StepView.extend({
templateName: "make_payment_step",
btnClass: 'action-primary',
initialize: function( obj ) {
_.extend( this, obj );
if (this.templateContext().isABTesting) {
this.templateName = 'make_payment_step_ab_testing';
this.btnClass = 'action-primary-blue';
}
},
defaultContext: function() {
return {
......@@ -27,7 +36,8 @@ var edx = edx || {};
platformName: '',
alreadyVerified: false,
courseModeSlug: 'audit',
verificationGoodUntil: ''
verificationGoodUntil: '',
isABTesting: false
};
},
......@@ -61,8 +71,8 @@ var edx = edx || {};
_getPaymentButtonHtml: function(processorName) {
var self = this;
return _.template(
'<button class="next action-primary payment-button" id="<%- name %>" ><%- text %></button> '
)({name: processorName, text: self._getPaymentButtonText(processorName)});
'<button class="next <%- btnClass %> payment-button" id="<%- name %>" ><%- text %></button> '
)({name: processorName, text: self._getPaymentButtonText(processorName), btnClass: this.btnClass});
},
postRender: function() {
......
......@@ -175,6 +175,14 @@
color: $white !important;
}
// elements - controls
.action-primary-blue {
@extend %btn-primary-blue;
// needed for override due to .register a:link styling
border: 0 !important;
color: $white !important;
}
.action-confirm {
@extend %btn-verify-primary;
// needed for override due to .register a:link styling
......@@ -821,6 +829,109 @@
// indiv slides - review
#wrapper-review {
color: $black;
.page-title {
@extend %t-strong;
border-bottom: 2px solid $m-gray-d3;
padding-bottom: ($baseline*0.75);
margin-bottom: $baseline;
text-transform: inherit;
}
.review {
.certificate {
@include font-size(18);
background-repeat: no-repeat;
padding-left: ($baseline*2.5);
overflow: hidden;
min-height: 32px;
p {
@include line-height(22);
@extend %t-strong;
margin-top: 0;
color: $black;
}
.purchase {
@include float(right);
@include margin-left($baseline*0.75);
text-align: right;
.product-info {
@include font-size(22);
@extend %t-strong;
color: $blue;
}
}
&.verified_icon {
background-image: url('#{$static-path}/images/icon-sm-verified.png');
}
&.no-id-professional_icon,
&.professional_icon {
background-image: url('#{$static-path}/images/icon-sm-professional.png');
}
}
.payment-buttons {
overflow: auto;
padding-bottom: ($baseline/4);
margin: {
top: ($baseline / 2);
bottom: ($baseline * 0.75);
};
.payment-button {
padding: ($baseline*0.4) $baseline;
min-width: 200px;
}
.action-primary-blue {
&.is-selected {
background: $blue !important;
}
}
}
.border-gray {
border-bottom: 2px solid $gray;
margin: ($baseline*1.12) 0;
}
}
.container {
padding: ($baseline*0.75) 0;
p {
@include line-height(22);
color: $black;
}
.photo-requirement {
@include font-size(12);
position: relative;
padding-left: ($baseline*2);
margin-top: ($baseline*0.75);
background-repeat: no-repeat;
background-position: left top;
.fa {
position: absolute;
left:0;
color: $mediumGrey;
}
h6 {
font-weight: bold;
color: $extraDarkGrey;
}
}
}
.review-task {
margin-bottom: ($baseline*1.5);
......
<div id="wrapper-review" tab-index="0" class="wrapper-view make-payment-step">
<div class="review view">
<% if (!isActive ) { %>
<h2 class="page-title">
<%- gettext("Account Not Activated")%>
</h2>
<% } else if ( !upgrade ) { %>
<h2 class="page-title">
<%= _.sprintf(
gettext( "You are enrolling in %(courseName)s"),
{ courseName: '<span class="course-title">' + courseName + '</span>' }
) %>
</h2>
<% } else { %>
<h2 class="page-title">
<%= _.sprintf(
gettext( "Upgrade to a Verified Certificate for %(courseName)s"),
{ courseName: '<span class="course-title">' + courseName + '</span>' }
) %>
</h2>
<% } %>
<% if ( !isActive ) { %>
<p>
<%- gettext("Before you upgrade to a certificate track, you must activate your account.") %>
<%- gettext("Check your email for an activation message.") %>
</p>
<% } else { %>
<div class="certificate <%- courseModeSlug %>_icon">
<div class="purchase">
<p class="product-info"><span class="product-name"></span> <%- gettext( "Total" ) %>: <span class="price">$<%- minPrice %> USD</span></p>
</div>
<p>
<% if ( courseModeSlug === 'no-id-professional' || courseModeSlug === 'professional') { %>
<%= _.sprintf(
gettext( "Professional Certificate for %(courseName)s"),{ courseName: courseName }
)%>
<% } else { %>
<%= _.sprintf(
gettext( "Verified Certificate for %(courseName)s"),{ courseName: courseName }
)%>
<% } %>
</p>
</div>
<% } %>
<% if ( isActive ) { %>
<div class="payment-buttons is-ready center">
<input type="hidden" name="contribution" value="<%- minPrice %>" />
<input type="hidden" name="sku" value="<%- sku %>" />
<div class="pay-options">
<%
// payment buttons will go here
%>
</div>
</div>
<div class="border-gray"></div>
<% } %>
</div>
<% if ( isActive ) { %>
<div class="container">
<% if ( _.some( requirements, function( isVisible ) { return isVisible; } ) ) { %>
<p>
<% if ( verificationDeadline ) { %>
<%- _.sprintf(
gettext( "To receive a certificate, you must also verify your identity before %(date)s." ),
{ date: verificationDeadline }
) %>
<% } else { %>
<%- gettext( "To receive a certificate, you must also verify your identity." ) %>
<% } %>
<%- gettext("To verify your identity, you need a webcam and a government-issued photo ID.") %>
</p>
<% if ( requirements['photo-id-required'] ) { %>
<div class="photo-requirement user_icon">
<i class="fa fa-user fa-2x" aria-hidden="true"></i>
<h6>
<%- gettext("Photo ID") %>
</h6>
<p>
<%- gettext("Your ID must be a government-issued photo ID that clearly shows your face.") %>
</p>
</div>
<% } %>
<% if ( requirements['webcam-required'] ) { %>
<div class="photo-requirement cam_icon">
<i class="fa fa-video-camera fa-2x" aria-hidden="true"></i>
<h6>
<%- gettext("Webcam") %>
</h6>
<p>
<%- gettext("You will use your webcam to take a picture of your face and of your government-issued photo ID.") %>
</p>
</div>
<% } %>
<% } %>
</div>
<% } %>
<form id="payment-processor-form"></form>
</div>
......@@ -24,9 +24,15 @@ from lms.djangoapps.verify_student.views import PayAndVerifyView
<%
template_names = (
["webcam_photo", "image_input", "error"] +
["intro_step", "make_payment_step", "payment_confirmation_step"] +
["intro_step", "payment_confirmation_step"] +
["face_photo_step", "id_photo_step", "review_photos_step", "enrollment_confirmation_step"]
)
if not is_ab_testing:
template_names.append("make_payment_step")
else:
template_names.append("make_payment_step_ab_testing")
%>
% for template_name in template_names:
<script type="text/template" id="${template_name}-tpl">
......@@ -76,6 +82,7 @@ from lms.djangoapps.verify_student.views import PayAndVerifyView
data-already-verified='${already_verified}'
data-verification-good-until='${verification_good_until}'
data-capture-sound='${capture_sound}'
data-is-ab-testing='${json.dumps(is_ab_testing)}'
## If we reached the verification flow from an in-course checkpoint,
## then pass the checkpoint location in so that we can associate
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment