"""Tests for items views.""" import json import datetime from pytz import UTC from django.core.urlresolvers import reverse from contentstore.tests.utils import CourseTestCase from xmodule.modulestore.tests.factories import CourseFactory from xmodule.capa_module import CapaDescriptor from xmodule.modulestore.django import modulestore class DeleteItem(CourseTestCase): """Tests for '/xblock' DELETE url.""" def setUp(self): """ Creates the test course with a static page in it. """ super(DeleteItem, self).setUp() self.course = CourseFactory.create(org='mitX', number='333', display_name='Dummy Course') def test_delete_static_page(self): # Add static tab data = json.dumps({ 'parent_location': 'i4x://mitX/333/course/Dummy_Course', 'category': 'static_tab' }) resp = self.client.post( reverse('create_item'), data, content_type="application/json" ) self.assertEqual(resp.status_code, 200) # Now delete it. There was a bug that the delete was failing (static tabs do not exist in draft modulestore). resp_content = json.loads(resp.content) resp = self.client.delete(resp_content['update_url']) self.assertEqual(resp.status_code, 204) class TestCreateItem(CourseTestCase): """ Test the create_item handler thoroughly """ def response_id(self, response): """ Get the id from the response payload :param response: """ parsed = json.loads(response.content) return parsed['id'] def test_create_nicely(self): """ Try the straightforward use cases """ # create a chapter display_name = 'Nicely created' resp = self.client.post( reverse('create_item'), json.dumps({ 'parent_location': self.course.location.url(), 'display_name': display_name, 'category': 'chapter' }), content_type="application/json" ) self.assertEqual(resp.status_code, 200) # get the new item and check its category and display_name chap_location = self.response_id(resp) new_obj = modulestore().get_item(chap_location) self.assertEqual(new_obj.scope_ids.block_type, 'chapter') self.assertEqual(new_obj.display_name, display_name) self.assertEqual(new_obj.location.org, self.course.location.org) self.assertEqual(new_obj.location.course, self.course.location.course) # get the course and ensure it now points to this one course = modulestore().get_item(self.course.location) self.assertIn(chap_location, course.children) # use default display name resp = self.client.post( reverse('create_item'), json.dumps({ 'parent_location': chap_location, 'category': 'vertical' }), content_type="application/json" ) self.assertEqual(resp.status_code, 200) vert_location = self.response_id(resp) # create problem w/ boilerplate template_id = 'multiplechoice.yaml' resp = self.client.post( reverse('create_item'), json.dumps({ 'parent_location': vert_location, 'category': 'problem', 'boilerplate': template_id }), content_type="application/json" ) self.assertEqual(resp.status_code, 200) prob_location = self.response_id(resp) problem = modulestore('draft').get_item(prob_location) # ensure it's draft self.assertTrue(problem.is_draft) # check against the template template = CapaDescriptor.get_template(template_id) self.assertEqual(problem.data, template['data']) self.assertEqual(problem.display_name, template['metadata']['display_name']) self.assertEqual(problem.markdown, template['metadata']['markdown']) def test_create_item_negative(self): """ Negative tests for create_item """ # non-existent boilerplate: creates a default resp = self.client.post( reverse('create_item'), json.dumps( {'parent_location': self.course.location.url(), 'category': 'problem', 'boilerplate': 'nosuchboilerplate.yaml' }), content_type="application/json" ) self.assertEqual(resp.status_code, 200) class TestEditItem(CourseTestCase): """ Test contentstore.views.item.save_item """ def response_id(self, response): """ Get the id from the response payload :param response: """ parsed = json.loads(response.content) return parsed['id'] def setUp(self): """ Creates the test course structure and a couple problems to 'edit'. """ super(TestEditItem, self).setUp() # create a chapter display_name = 'chapter created' resp = self.client.post( reverse('create_item'), json.dumps( {'parent_location': self.course.location.url(), 'display_name': display_name, 'category': 'chapter' }), content_type="application/json" ) chap_location = self.response_id(resp) resp = self.client.post( reverse('create_item'), json.dumps({ 'parent_location': chap_location, 'category': 'sequential', }), content_type="application/json" ) self.seq_location = self.response_id(resp) # create problem w/ boilerplate template_id = 'multiplechoice.yaml' resp = self.client.post( reverse('create_item'), json.dumps({ 'parent_location': self.seq_location, 'category': 'problem', 'boilerplate': template_id, }), content_type="application/json" ) self.problems = [self.response_id(resp)] def test_delete_field(self): """ Sending null in for a field 'deletes' it """ self.client.post( reverse('save_item'), json.dumps({ 'id': self.problems[0], 'metadata': {'rerandomize': 'onreset'} }), content_type="application/json" ) problem = modulestore('draft').get_item(self.problems[0]) self.assertEqual(problem.rerandomize, 'onreset') self.client.post( reverse('save_item'), json.dumps({ 'id': self.problems[0], 'metadata': {'rerandomize': None} }), content_type="application/json" ) problem = modulestore('draft').get_item(self.problems[0]) self.assertEqual(problem.rerandomize, 'never') def test_null_field(self): """ Sending null in for a field 'deletes' it """ problem = modulestore('draft').get_item(self.problems[0]) self.assertIsNotNone(problem.markdown) self.client.post( reverse('save_item'), json.dumps({ 'id': self.problems[0], 'nullout': ['markdown'] }), content_type="application/json" ) problem = modulestore('draft').get_item(self.problems[0]) self.assertIsNone(problem.markdown) def test_date_fields(self): """ Test setting due & start dates on sequential """ sequential = modulestore().get_item(self.seq_location) self.assertIsNone(sequential.due) self.client.post( reverse('save_item'), json.dumps({ 'id': self.seq_location, 'metadata': {'due': '2010-11-22T04:00Z'} }), content_type="application/json" ) sequential = modulestore().get_item(self.seq_location) self.assertEqual(sequential.due, datetime.datetime(2010, 11, 22, 4, 0, tzinfo=UTC)) self.client.post( reverse('save_item'), json.dumps({ 'id': self.seq_location, 'metadata': {'start': '2010-09-12T14:00Z'} }), content_type="application/json" ) sequential = modulestore().get_item(self.seq_location) self.assertEqual(sequential.due, datetime.datetime(2010, 11, 22, 4, 0, tzinfo=UTC)) self.assertEqual(sequential.start, datetime.datetime(2010, 9, 12, 14, 0, tzinfo=UTC))