290 lines
12 KiB
Python
290 lines
12 KiB
Python
"""
|
|
Test scenarios for the crowdsource hinter xblock.
|
|
"""
|
|
import json
|
|
import unittest
|
|
|
|
from django.conf import settings
|
|
from django.urls import reverse
|
|
from nose.plugins.attrib import attr
|
|
from six import text_type
|
|
|
|
from lms.djangoapps.courseware.tests.factories import GlobalStaffFactory
|
|
from lms.djangoapps.courseware.tests.helpers import LoginEnrollmentTestCase
|
|
from openedx.core.lib.url_utils import quote_slashes
|
|
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
|
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
|
|
|
|
|
class TestCrowdsourceHinter(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
|
"""
|
|
Create the test environment with the crowdsourcehinter xblock.
|
|
"""
|
|
STUDENTS = [
|
|
{'email': 'view@test.com', 'password': 'foo'},
|
|
{'email': 'view2@test.com', 'password': 'foo'}
|
|
]
|
|
XBLOCK_NAMES = ['crowdsourcehinter']
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
# Nose runs setUpClass methods even if a class decorator says to skip
|
|
# the class: https://github.com/nose-devs/nose/issues/946
|
|
# So, skip the test class here if we are not in the LMS.
|
|
if settings.ROOT_URLCONF != 'lms.urls':
|
|
raise unittest.SkipTest('Test only valid in lms')
|
|
|
|
super(TestCrowdsourceHinter, cls).setUpClass()
|
|
cls.course = CourseFactory.create(
|
|
display_name='CrowdsourceHinter_Test_Course'
|
|
)
|
|
with cls.store.bulk_operations(cls.course.id, emit_signals=False):
|
|
cls.chapter = ItemFactory.create(
|
|
parent=cls.course, display_name='Overview'
|
|
)
|
|
cls.section = ItemFactory.create(
|
|
parent=cls.chapter, display_name='Welcome'
|
|
)
|
|
cls.unit = ItemFactory.create(
|
|
parent=cls.section, display_name='New Unit'
|
|
)
|
|
cls.xblock = ItemFactory.create(
|
|
parent=cls.unit,
|
|
category='crowdsourcehinter',
|
|
display_name='crowdsourcehinter'
|
|
)
|
|
|
|
cls.course_url = reverse(
|
|
'courseware_section',
|
|
kwargs={
|
|
'course_id': text_type(cls.course.id),
|
|
'chapter': 'Overview',
|
|
'section': 'Welcome',
|
|
}
|
|
)
|
|
|
|
def setUp(self):
|
|
super(TestCrowdsourceHinter, self).setUp()
|
|
for idx, student in enumerate(self.STUDENTS):
|
|
username = "u{}".format(idx)
|
|
self.create_account(username, student['email'], student['password'])
|
|
self.activate_user(student['email'])
|
|
|
|
self.staff_user = GlobalStaffFactory()
|
|
|
|
def get_handler_url(self, handler, xblock_name=None):
|
|
"""
|
|
Get url for the specified xblock handler
|
|
"""
|
|
if xblock_name is None:
|
|
xblock_name = TestCrowdsourceHinter.XBLOCK_NAMES[0]
|
|
return reverse('xblock_handler', kwargs={
|
|
'course_id': text_type(self.course.id),
|
|
'usage_id': quote_slashes(text_type(self.course.id.make_usage_key('crowdsourcehinter', xblock_name))),
|
|
'handler': handler,
|
|
'suffix': ''
|
|
})
|
|
|
|
def enroll_student(self, email, password):
|
|
"""
|
|
Student login and enroll for the course
|
|
"""
|
|
self.login(email, password)
|
|
self.enroll(self.course, verify=True)
|
|
|
|
def enroll_staff(self, staff):
|
|
"""
|
|
Staff login and enroll for the course
|
|
"""
|
|
email = staff.email
|
|
password = 'test'
|
|
self.login(email, password)
|
|
self.enroll(self.course, verify=True)
|
|
|
|
def initialize_database_by_id(self, handler, resource_id, times, xblock_name=None):
|
|
"""
|
|
Call a ajax event (vote, delete, endorse) on a resource by its id
|
|
several times
|
|
"""
|
|
if xblock_name is None:
|
|
xblock_name = TestCrowdsourceHinter.XBLOCK_NAMES[0]
|
|
url = self.get_handler_url(handler, xblock_name)
|
|
for _ in range(times):
|
|
self.client.post(url, json.dumps({'id': resource_id}), '')
|
|
|
|
def call_event(self, handler, resource, xblock_name=None):
|
|
"""
|
|
Call a ajax event (add, edit, flag, etc.) by specifying the resource
|
|
it takes
|
|
"""
|
|
if xblock_name is None:
|
|
xblock_name = TestCrowdsourceHinter.XBLOCK_NAMES[0]
|
|
url = self.get_handler_url(handler, xblock_name)
|
|
return self.client.post(url, json.dumps(resource), '')
|
|
|
|
def check_event_response_by_element(self, handler, resource, resp_key, resp_val, xblock_name=None):
|
|
"""
|
|
Call the event specified by the handler with the resource, and check
|
|
whether the element (resp_key) in response is as expected (resp_val)
|
|
"""
|
|
if xblock_name is None:
|
|
xblock_name = TestCrowdsourceHinter.XBLOCK_NAMES[0]
|
|
resp = self.call_event(handler, resource, xblock_name)
|
|
self.assertEqual(resp[resp_key], resp_val)
|
|
self.assert_request_status_code(200, self.course_url)
|
|
|
|
|
|
@attr(shard=6)
|
|
class TestHinterFunctions(TestCrowdsourceHinter):
|
|
"""
|
|
Check that the essential functions of the hinter work as expected.
|
|
Tests cover the basic process of receiving a hint, adding a new hint,
|
|
and rating/reporting hints.
|
|
"""
|
|
def test_get_hint_with_no_hints(self):
|
|
"""
|
|
Check that a generic statement is returned when no default/specific hints exist
|
|
"""
|
|
result = self.call_event('get_hint', {'submittedanswer': 'ans=incorrect+answer+1'}, 'crowdsourcehinter')
|
|
expected = {'BestHint': 'Sorry, there are no hints for this answer.', 'StudentAnswer': 'incorrect answer 1',
|
|
'HintCategory': False}
|
|
self.assertEqual(json.loads(result.content), expected)
|
|
|
|
def test_add_new_hint(self):
|
|
"""
|
|
Test the ability to add a new specific hint
|
|
"""
|
|
self.enroll_student(self.STUDENTS[0]['email'], self.STUDENTS[0]['password'])
|
|
data = {'new_hint_submission': 'new hint for answer 1', 'answer': 'incorrect answer 1'}
|
|
self.call_event('get_hint', {'submittedanswer': 'ans=incorrect+answer+1'})
|
|
result = self.call_event('add_new_hint', data)
|
|
expected = {'success': True,
|
|
'result': 'Hint added'}
|
|
self.assertEqual(json.loads(result.content), expected)
|
|
|
|
def test_get_hint(self):
|
|
"""
|
|
Check that specific hints are returned
|
|
"""
|
|
self.call_event('get_hint', {'submittedanswer': 'ans=incorrect+answer+1'})
|
|
submission = {'new_hint_submission': 'new hint for answer 1',
|
|
'answer': 'incorrect answer 1'}
|
|
self.call_event('add_new_hint', submission)
|
|
result = self.call_event('get_hint', {'submittedanswer': 'ans=incorrect+answer+1'})
|
|
expected = {'BestHint': 'new hint for answer 1', 'StudentAnswer': 'incorrect answer 1',
|
|
'HintCategory': 'ErrorResponse'}
|
|
self.assertEqual(json.loads(result.content), expected)
|
|
|
|
def test_rate_hint_upvote(self):
|
|
"""
|
|
Test hint upvoting
|
|
"""
|
|
self.call_event('get_hint', {'submittedanswer': 'ans=incorrect+answer+1'})
|
|
submission = {'new_hint_submission': 'new hint for answer 1',
|
|
'answer': 'incorrect answer 1'}
|
|
self.call_event('add_new_hint', submission)
|
|
data = {
|
|
'student_answer': 'incorrect answer 1',
|
|
'hint': 'new hint for answer 1',
|
|
'student_rating': 'upvote'
|
|
}
|
|
expected = {'success': True}
|
|
result = self.call_event('rate_hint', data)
|
|
self.assertEqual(json.loads(result.content), expected)
|
|
|
|
def test_rate_hint_downvote(self):
|
|
"""
|
|
Test hint downvoting
|
|
"""
|
|
self.call_event('get_hint', {'submittedanswer': 'ans=incorrect+answer+1'})
|
|
submission = {'new_hint_submission': 'new hint for answer 1',
|
|
'answer': 'incorrect answer 1'}
|
|
self.call_event('add_new_hint', submission)
|
|
data = {
|
|
'student_answer': 'incorrect answer 1',
|
|
'hint': 'new hint for answer 1',
|
|
'student_rating': 'downvote'
|
|
}
|
|
expected = {'success': True}
|
|
result = self.call_event('rate_hint', data)
|
|
self.assertEqual(json.loads(result.content), expected)
|
|
|
|
def test_report_hint(self):
|
|
"""
|
|
Test hint reporting
|
|
"""
|
|
self.call_event('get_hint', {'submittedanswer': 'ans=incorrect+answer+1'})
|
|
submission = {'new_hint_submission': 'new hint for answer 1',
|
|
'answer': 'incorrect answer 1'}
|
|
self.call_event('add_new_hint', submission)
|
|
data = {
|
|
'student_answer': 'incorrect answer 1',
|
|
'hint': 'new hint for answer 1',
|
|
'student_rating': 'report'
|
|
}
|
|
expected = {'rating': 'reported', 'hint': 'new hint for answer 1'}
|
|
result = self.call_event('rate_hint', data)
|
|
self.assertEqual(json.loads(result.content), expected)
|
|
|
|
def test_dont_show_reported_hint(self):
|
|
"""
|
|
Check that reported hints are returned
|
|
"""
|
|
self.call_event('get_hint', {'submittedanswer': 'ans=incorrect+answer+1'})
|
|
submission = {'new_hint_submission': 'new hint for answer 1',
|
|
'answer': 'incorrect answer 1'}
|
|
self.call_event('add_new_hint', submission)
|
|
data = {
|
|
'student_answer': 'incorrect answer 1',
|
|
'hint': 'new hint for answer 1',
|
|
'student_rating': 'report'
|
|
}
|
|
self.call_event('rate_hint', data)
|
|
result = self.call_event('get_hint', {'submittedanswer': 'ans=incorrect+answer+1'})
|
|
expected = {'BestHint': 'Sorry, there are no hints for this answer.', 'StudentAnswer': 'incorrect answer 1',
|
|
'HintCategory': False}
|
|
self.assertEqual(json.loads(result.content), expected)
|
|
|
|
def test_get_used_hint_answer_data(self):
|
|
"""
|
|
Check that hint/answer information from previous submissions are returned upon correctly
|
|
answering the problem
|
|
"""
|
|
self.call_event('get_hint', {'submittedanswer': 'ans=incorrect+answer+1'})
|
|
self.call_event('get_used_hint_answer_data', "")
|
|
submission = {'new_hint_submission': 'new hint for answer 1',
|
|
'answer': 'incorrect answer 1'}
|
|
self.call_event('add_new_hint', submission)
|
|
self.call_event('get_hint', {'submittedanswer': 'ans=incorrect+answer+1'})
|
|
result = self.call_event('get_used_hint_answer_data', "")
|
|
expected = {'new hint for answer 1': 'incorrect answer 1'}
|
|
self.assertEqual(json.loads(result.content), expected)
|
|
|
|
def test_show_best_hint(self):
|
|
"""
|
|
Check that the most upvoted hint is shown
|
|
"""
|
|
self.call_event('get_hint', {'submittedanswer': 'ans=incorrect+answer+1'})
|
|
submission1 = {'new_hint_submission': 'new hint for answer 1',
|
|
'answer': 'incorrect answer 1'}
|
|
submission2 = {'new_hint_submission': 'new hint for answer 1 to report',
|
|
'answer': 'incorrect answer 1'}
|
|
self.call_event('add_new_hint', submission1)
|
|
self.call_event('add_new_hint', submission2)
|
|
data_upvote = {
|
|
'student_answer': 'incorrect answer 1',
|
|
'hint': 'new hint for answer 1 to report',
|
|
'student_rating': 'upvote'
|
|
}
|
|
self.call_event('rate_hint', data_upvote)
|
|
data_downvote = {
|
|
'student_answer': 'incorrect answer 1',
|
|
'hint': 'new hint for answer 1 to report',
|
|
'student_rating': 'report'
|
|
}
|
|
self.call_event('rate_hint', data_downvote)
|
|
result = self.call_event('get_hint', {'submittedanswer': 'ans=incorrect+answer+1'})
|
|
expected = {'BestHint': 'new hint for answer 1', 'StudentAnswer': 'incorrect answer 1',
|
|
'HintCategory': 'ErrorResponse'}
|
|
self.assertEqual(json.loads(result.content), expected)
|