Commit d11aaec3 by Waqas Khalid

Handle the indexerror exception on create_order

LMS-2646
parent c7d41833
......@@ -31,6 +31,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
from student.tests.factories import UserFactory
from student.models import CourseEnrollment
from course_modes.tests.factories import CourseModeFactory
from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE
from course_modes.models import CourseMode
from verify_student.views import render_to_response
from verify_student.models import SoftwareSecurePhotoVerification
......@@ -65,6 +66,97 @@ class StartView(TestCase):
self.assertHttpForbidden(self.client.get(self.start_url()))
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
class TestCreateOrderView(TestCase):
"""
Tests for the create_order view of verified course registration process
"""
def setUp(self):
self.user = UserFactory.create(username="rusty", password="test")
self.client.login(username="rusty", password="test")
self.course_id = 'Robot/999/Test_Course'
CourseFactory.create(org='Robot', number='999', display_name='Test Course')
verified_mode = CourseMode(
course_id=SlashSeparatedCourseKey("Robot", "999", 'Test_Course'),
mode_slug="verified",
mode_display_name="Verified Certificate",
min_price=50
)
verified_mode.save()
course_mode_post_data = {
'certificate_mode': 'Select Certificate',
'contribution': 50,
'contribution-other-amt': '',
'explain': ''
}
self.client.post(
reverse("course_modes_choose", kwargs={'course_id': self.course_id}),
course_mode_post_data
)
def test_invalid_photos_data(self):
"""
Test that the invalid photo data cannot be submitted
"""
create_order_post_data = {
'contribution': 50,
'course_id': self.course_id,
'face_image': '',
'photo_id_image': ''
}
response = self.client.post(reverse('verify_student_create_order'), create_order_post_data)
json_response = json.loads(response.content)
self.assertFalse(json_response.get('success'))
@patch.dict(settings.FEATURES, {'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING': True})
def test_invalid_amount(self):
"""
Test that the user cannot give invalid amount
"""
create_order_post_data = {
'contribution': '1.a',
'course_id': self.course_id,
'face_image': ',',
'photo_id_image': ','
}
response = self.client.post(reverse('verify_student_create_order'), create_order_post_data)
self.assertEquals(response.status_code, 400)
self.assertIn('Selected price is not valid number.', response.content)
@patch.dict(settings.FEATURES, {'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING': True})
def test_invalid_mode(self):
"""
Test that the course without verified mode cannot be processed
"""
course_id = 'Fake/999/Test_Course'
CourseFactory.create(org='Fake', number='999', display_name='Test Course')
create_order_post_data = {
'contribution': '50',
'course_id': course_id,
'face_image': ',',
'photo_id_image': ','
}
response = self.client.post(reverse('verify_student_create_order'), create_order_post_data)
self.assertEquals(response.status_code, 400)
self.assertIn('This course doesn\'t support verified certificates', response.content)
@patch.dict(settings.FEATURES, {'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING': True})
def test_create_order_success(self):
"""
Test that the order is created successfully when given valid data
"""
create_order_post_data = {
'contribution': 50,
'course_id': self.course_id,
'face_image': ',',
'photo_id_image': ','
}
response = self.client.post(reverse('verify_student_create_order'), create_order_post_data)
json_response = json.loads(response.content)
self.assertTrue(json_response.get('success'))
self.assertIsNotNone(json_response.get('orderNumber'))
@override_settings(MODULESTORE=MODULESTORE_CONFIG)
class TestVerifyView(ModuleStoreTestCase):
def setUp(self):
......
......@@ -38,6 +38,8 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
from .exceptions import WindowExpiredException
from xmodule.modulestore.django import modulestore
from util.json_request import JsonResponse
log = logging.getLogger(__name__)
EVENT_NAME_USER_ENTERED_MIDCOURSE_REVERIFY_VIEW = 'edx.course.enrollment.reverify.started'
......@@ -114,6 +116,7 @@ class VerifyView(View):
# TODO (ECOM-16): Remove once the AB test completes
"autoreg": request.session.get('auto_register', False),
"retake": request.GET.get('retake', False),
}
return render_to_response('verify_student/photo_verification.html', context)
......@@ -177,9 +180,14 @@ def create_order(request):
"""
if not SoftwareSecurePhotoVerification.user_has_valid_or_pending(request.user):
attempt = SoftwareSecurePhotoVerification(user=request.user)
b64_face_image = request.POST['face_image'].split(",")[1]
b64_photo_id_image = request.POST['photo_id_image'].split(",")[1]
try:
b64_face_image = request.POST['face_image'].split(",")[1]
b64_photo_id_image = request.POST['photo_id_image'].split(",")[1]
except IndexError:
context = {
'success': False,
}
return JsonResponse(context)
attempt.upload_face_image(b64_face_image.decode('base64'))
attempt.upload_photo_id_image(b64_photo_id_image.decode('base64'))
attempt.mark_ready()
......@@ -203,13 +211,13 @@ def create_order(request):
# prefer professional mode over verified_mode
current_mode = CourseMode.verified_mode_for_course(course_id)
if current_mode.slug == 'professional':
amount = current_mode.min_price
# make sure this course has a verified mode
if not current_mode:
return HttpResponseBadRequest(_("This course doesn't support verified certificates"))
if current_mode.slug == 'professional':
amount = current_mode.min_price
if amount < current_mode.min_price:
return HttpResponseBadRequest(_("No selected price or selected price is below minimum."))
......@@ -225,7 +233,7 @@ def create_order(request):
params = get_signed_purchase_params(
cart, callback_url=callback_url
)
params['success'] = True
return HttpResponse(json.dumps(params), content_type="text/json")
......@@ -484,8 +492,7 @@ def midcourse_reverify_dash(request):
try:
course_enrollment_pairs.append((modulestore().get_course(enrollment.course_id), enrollment))
except ItemNotFoundError:
log.error("User {0} enrolled in non-existent course {1}"
.format(user.username, enrollment.course_id))
log.error("User {0} enrolled in non-existent course {1}".format(user.username, enrollment.course_id))
statuses = ["approved", "pending", "must_reverify", "denied"]
......
describe("Photo Verification", function() {
beforeEach(function() {
setFixtures('<div id="order-error" style="display: none;"></div><input type="radio" name="contribution" value="35" id="contribution-35" checked="checked"><input type="radio" id="contribution-other" name="contribution" value=""><input type="text" size="9" name="contribution-other-amt" id="contribution-other-amt" value="30"><img id="face_image" src="src="data:image/png;base64,dummy"><img id="photo_id_image" src="src="data:image/png;base64,dummy">');
});
it('retake photo', function() {
spyOn(window,"refereshPageMessage").andCallFake(function(){
return
})
spyOn($, "ajax").andCallFake(function(e) {
e.success({"success":false});
});
submitToPaymentProcessing();
expect(window.refereshPageMessage).toHaveBeenCalled();
});
it('successful submission', function() {
spyOn(window,"submitForm").andCallFake(function(){
return
})
spyOn($, "ajax").andCallFake(function(e) {
e.success({"success":true});
});
submitToPaymentProcessing();
expect(window.submitForm).toHaveBeenCalled();
});
it('Error during process', function() {
spyOn(window,"showSubmissionError").andCallFake(function(){
return
})
spyOn($, "ajax").andCallFake(function(e) {
e.error({});
});
submitToPaymentProcessing();
expect(window.showSubmissionError).toHaveBeenCalled();
});
});
......@@ -44,6 +44,30 @@ var submitMidcourseReverificationPhotos = function() {
$("#reverify_form").submit();
}
function showSubmissionError() {
if (xhr.status == 400) {
$('#order-error .copy p').html(xhr.responseText);
}
$('#order-error').show();
$("html, body").animate({ scrollTop: 0 });
}
function submitForm(data) {
for (prop in data) {
$('<input>').attr({
type: 'hidden',
name: prop,
value: data[prop]
}).appendTo('#pay_form');
}
$("#pay_form").submit();
}
function refereshPageMessage() {
$('#photo-error').show();
$("html, body").animate({ scrollTop: 0 });
}
var submitToPaymentProcessing = function() {
var contribution_input = $("input[name='contribution']:checked")
var contribution = 0;
......@@ -54,33 +78,25 @@ var submitToPaymentProcessing = function() {
contribution = contribution_input.val();
}
var course_id = $("input[name='course_id']").val();
var xhr = $.post(
"/verify_student/create_order",
{
$.ajax({
url: "/verify_student/create_order",
type: 'POST',
data: {
"course_id" : course_id,
"contribution": contribution,
"face_image" : $("#face_image")[0].src,
"photo_id_image" : $("#photo_id_image")[0].src
},
function(data) {
for (prop in data) {
$('<input>').attr({
type: 'hidden',
name: prop,
value: data[prop]
}).appendTo('#pay_form');
success:function(data) {
if (data.success) {
submitForm(data);
} else {
refereshPageMessage();
}
},
error:function(xhr,status,error) {
showSubmissionError()
}
)
.done(function(data) {
$("#pay_form").submit();
})
.fail(function(jqXhr,text_status, error_thrown) {
if(jqXhr.status == 400) {
$('#order-error .copy p').html(jqXhr.responseText);
}
$('#order-error').show();
$("html, body").animate({ scrollTop: 0 });
});
}
......@@ -298,6 +314,7 @@ $(document).ready(function() {
// when moving between steps
$('#face_next_link').click(function(){
analytics.pageview("Capture ID Photo");
$('#photo-error').hide();
$('body').addClass('step-photos-id').removeClass('step-photos-cam')
})
......
<%! from django.utils.translation import ugettext as _ %>
<%! from django.core.urlresolvers import reverse %>
<%! from lms.envs.common import TECH_SUPPORT_EMAIL %>
<%inherit file="../main.html" />
<%namespace name='static' file='/static_content.html'/>
......@@ -57,6 +57,25 @@
</div>
</div>
<div id="photo-error" style="display: none;" class="wrapper-msg wrapper-msg-activate">
<div class=" msg msg-activate">
<i class="msg-icon icon-warning-sign"></i>
<div class="msg-content">
<h3 class="title">${_("Error processing your order")}</h3>
<div class="copy">
<p>${_("We're sorry, there has been an error on this page. Please refresh the page and retake your photos. "
"If you are still having trouble, please contact us at "
"{email_address}").format(
email_address='<a href="mailto:{technical_email}">{technical_email}</a>'.format(
technical_email=TECH_SUPPORT_EMAIL
)
)}
</p>
</div>
</div>
</div>
</div>
<div class="container">
<section class="wrapper">
......
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