def load_root_xblock(self, student_id="student_1"): """ Load (in Python) the XBlock at the root of the current scenario. """ dom_node = self.browser.find_element_by_css_selector('.workbench .preview > div.xblock-v1:first-child') usage_id = dom_node.get_attribute('data-usage') runtime = WorkbenchRuntime(student_id) return runtime.get_block(usage_id)
def test_correct_answer_submission(): runtime = WorkbenchRuntime() # WorkbenchRuntime has an id_generator, but most runtimes won't # (because the generator will be contextual), so we # pass it explicitly to parse_xml_string. brewing101_usage_id = runtime.parse_xml_string(""" <brewing101/> """, runtime.id_generator) brewing101 = runtime.get_block(brewing101_usage_id) json_data = json.dumps(172) resp = runtime.handle(brewing101, 'submit_answer', make_request(json_data)) resp_data = json.loads(text_of_response(resp)) print(resp_data) assert_equals(resp_data['answerCorrect'], True)
def setUp(self): """ Create the runtime. """ super(XBlockHandlerTestCaseMixin, self).setUp() self.runtime = WorkbenchRuntime() mock_publish = mock.MagicMock(side_effect=self.runtime.publish) self.runtime.publish = mock_publish
class TestChildIsInstance(unittest.TestCase): """ Test child_isinstance helper method, in the workbench runtime. """ @XBlock.register_temp_plugin(GoldenRetrieverXBlock, "gr") @XBlock.register_temp_plugin(CatXBlock, "cat") @XBlock.register_temp_plugin(BasicXBlock, "block") def test_child_isinstance(self): """ Check that child_isinstance() works on direct children """ self.runtime = WorkbenchRuntime() self.root_id = self.runtime.parse_xml_string('<block> <block><cat/><gr/></block> <cat/> <gr/> </block>') root = self.runtime.get_block(self.root_id) self.assertFalse(child_isinstance(root, root.children[0], DogXBlock)) self.assertFalse(child_isinstance(root, root.children[0], GoldenRetrieverXBlock)) self.assertTrue(child_isinstance(root, root.children[0], BasicXBlock)) self.assertFalse(child_isinstance(root, root.children[1], DogXBlock)) self.assertFalse(child_isinstance(root, root.children[1], GoldenRetrieverXBlock)) self.assertTrue(child_isinstance(root, root.children[1], CatXBlock)) self.assertFalse(child_isinstance(root, root.children[2], CatXBlock)) self.assertTrue(child_isinstance(root, root.children[2], DogXBlock)) self.assertTrue(child_isinstance(root, root.children[2], GoldenRetrieverXBlock)) @XBlock.register_temp_plugin(GoldenRetrieverXBlock, "gr") @XBlock.register_temp_plugin(CatXBlock, "cat") @XBlock.register_temp_plugin(BasicXBlock, "block") def test_child_isinstance_descendants(self): """ Check that child_isinstance() works on deeper descendants """ self.runtime = WorkbenchRuntime() self.root_id = self.runtime.parse_xml_string('<block> <block><cat/><gr/></block> <cat/> <gr/> </block>') root = self.runtime.get_block(self.root_id) block = root.runtime.get_block(root.children[0]) self.assertIsInstance(block, BasicXBlock) self.assertFalse(child_isinstance(root, block.children[0], DogXBlock)) self.assertTrue(child_isinstance(root, block.children[0], CatXBlock)) self.assertTrue(child_isinstance(root, block.children[1], DogXBlock)) self.assertTrue(child_isinstance(root, block.children[1], GoldenRetrieverXBlock)) self.assertFalse(child_isinstance(root, block.children[1], CatXBlock))
class XmlTest(object): """Helpful things for XML tests.""" def setUp(self): super(XmlTest, self).setUp() self.runtime = WorkbenchRuntime() def parse_xml_to_block(self, xml): """A helper to get a block from some XML.""" usage_id = self.runtime.parse_xml_string(xml) block = self.runtime.get_block(usage_id) return block def export_xml_for_block(self, block): """A helper to return the XML string for a block.""" output = StringIO.StringIO() self.runtime.export_to_xml(block, output) return output.getvalue()
def test_problem_submission(): runtime = WorkbenchRuntime() problem_usage_id = runtime.parse_xml_string(""" <problem> <textinput name='vote_count' input_type='int'/> <script> numvotes = 4 </script> <equality name='votes_named' left='./vote_count/@student_input' right='$numvotes'> Number of upvotes matches entered string </equality> </problem> """) problem = runtime.get_block(problem_usage_id) json_data = json.dumps({"vote_count": [{"name": "input", "value": "4"}]}) resp = runtime.handle(problem, 'check', make_request(json_data)) resp_data = json.loads(text_of_response(resp)) assert_equals(resp_data['checkResults']['votes_named'], True)
class XmlTest(object): """Helpful things for XML tests.""" def setUp(self): super(XmlTest, self).setUp() self.runtime = WorkbenchRuntime() def parse_xml_to_block(self, xml): """A helper to get a block from some XML.""" # WorkbenchRuntime has an id_generator, but most runtimes won't # (because the generator will be contextual), so we # pass it explicitly to parse_xml_string. usage_id = self.runtime.parse_xml_string(xml, self.runtime.id_generator) block = self.runtime.get_block(usage_id) return block def export_xml_for_block(self, block): """A helper to return the XML string for a block.""" output = StringIO.StringIO() self.runtime.export_to_xml(block, output) return output.getvalue()
def test_problem_submission(): runtime = WorkbenchRuntime() # WorkbenchRuntime has an id_generator, but most runtimes won't # (because the generator will be contextual), so we # pass it explicitly to parse_xml_string. problem_usage_id = runtime.parse_xml_string(""" <problem_demo> <textinput_demo name='vote_count' input_type='int'/> <script> numvotes = 4 </script> <equality_demo name='votes_named' left='./vote_count/@student_input' right='$numvotes'> Number of upvotes matches entered string </equality_demo> </problem_demo> """, runtime.id_generator) problem = runtime.get_block(problem_usage_id) json_data = json.dumps({"vote_count": [{"name": "input", "value": "4"}]}) resp = runtime.handle(problem, 'check', make_request(json_data)) resp_data = json.loads(text_of_response(resp)) assert_equals(resp_data['checkResults']['votes_named'], True)
def test_child_isinstance_descendants(self): """ Check that child_isinstance() works on deeper descendants """ self.runtime = WorkbenchRuntime() self.root_id = self.runtime.parse_xml_string('<block> <block><cat/><gr/></block> <cat/> <gr/> </block>') root = self.runtime.get_block(self.root_id) block = root.runtime.get_block(root.children[0]) self.assertIsInstance(block, BasicXBlock) self.assertFalse(child_isinstance(root, block.children[0], DogXBlock)) self.assertTrue(child_isinstance(root, block.children[0], CatXBlock)) self.assertTrue(child_isinstance(root, block.children[1], DogXBlock)) self.assertTrue(child_isinstance(root, block.children[1], GoldenRetrieverXBlock)) self.assertFalse(child_isinstance(root, block.children[1], CatXBlock))
def test_child_isinstance(self): """ Check that child_isinstance() works on direct children """ self.runtime = WorkbenchRuntime() self.root_id = self.runtime.parse_xml_string('<block> <block><cat/><gr/></block> <cat/> <gr/> </block>') root = self.runtime.get_block(self.root_id) self.assertFalse(child_isinstance(root, root.children[0], DogXBlock)) self.assertFalse(child_isinstance(root, root.children[0], GoldenRetrieverXBlock)) self.assertTrue(child_isinstance(root, root.children[0], BasicXBlock)) self.assertFalse(child_isinstance(root, root.children[1], DogXBlock)) self.assertFalse(child_isinstance(root, root.children[1], GoldenRetrieverXBlock)) self.assertTrue(child_isinstance(root, root.children[1], CatXBlock)) self.assertFalse(child_isinstance(root, root.children[2], CatXBlock)) self.assertTrue(child_isinstance(root, root.children[2], DogXBlock)) self.assertTrue(child_isinstance(root, root.children[2], GoldenRetrieverXBlock))
def setUp(self): super(TestChildIsInstance, self).setUp() self.runtime = WorkbenchRuntime() self.root_id = self.runtime.parse_xml_string('<block> <block><cat/><gr/></block> <cat/> <gr/> </block>')
def get_root(self): self.runtime = WorkbenchRuntime() self.root_id = self.runtime.parse_xml_string('<showanswer />') return self.runtime.get_block(self.root_id)
class TestShowAnswerXBlock(TestCase): """ Unit tests for ShowAnswerXBlockMixin """ def get_root(self): self.runtime = WorkbenchRuntime() self.root_id = self.runtime.parse_xml_string('<showanswer />') return self.runtime.get_block(self.root_id) @ddt.data(*[ [True, True, True], [True, False, False], [False, True, True], [False, False, True], ]) @ddt.unpack @XBlock.register_temp_plugin(ShowAnswerXBlock, "showanswer") def test_closed(self, can_attempt, past_due, expected): """ Assert possible values for closed() """ block = self.get_root() with mock.patch.object( ShowAnswerXBlock, 'can_attempt', return_value=can_attempt, ), mock.patch.object( ShowAnswerXBlock, 'is_past_due', return_value=past_due, ): self.assertEqual(block.closed(), expected) @XBlock.register_temp_plugin(ShowAnswerXBlock, "showanswer") def test_answer_available_no_correctness(self): """ If no correctness is available, the answer is not available """ block = self.get_root() with mock.patch.object(ShowAnswerXBlock, 'correctness_available', return_value=False): self.assertFalse(block.answer_available()) @XBlock.register_temp_plugin(ShowAnswerXBlock, "showanswer") def test_answer_available_user_is_staff(self): """ If user is staff and correctness is available, the answer is available """ block = self.get_root() self.runtime.user_is_staff = True with mock.patch.object(ShowAnswerXBlock, 'correctness_available', return_value=True), mock.patch.object( ShowAnswerXBlock, 'runtime_user_is_staff', return_value=True, ): self.assertTrue(block.answer_available()) @ddt.data(*[ ['', {}, False], [ShowAnswer.NEVER, {}, False], [ShowAnswer.ATTEMPTED, {}, False], [ShowAnswer.ATTEMPTED, { 'has_attempted': True }, True], [ShowAnswer.ANSWERED, {}, False], [ShowAnswer.ANSWERED, { 'is_correct': True }, True], [ShowAnswer.CLOSED, {}, False], [ShowAnswer.CLOSED, { 'closed': True }, True], [ShowAnswer.FINISHED, {}, False], [ShowAnswer.FINISHED, { 'closed': True }, True], [ShowAnswer.FINISHED, { 'is_correct': True }, True], [ShowAnswer.FINISHED, { 'closed': True, 'is_correct': True }, True], [ShowAnswer.CORRECT_OR_PAST_DUE, {}, False], [ShowAnswer.CORRECT_OR_PAST_DUE, { 'is_correct': True }, True], [ShowAnswer.CORRECT_OR_PAST_DUE, { 'is_past_due': True }, True], [ ShowAnswer.CORRECT_OR_PAST_DUE, { 'is_correct': True, 'is_past_due': True }, True ], [ShowAnswer.PAST_DUE, {}, False], [ShowAnswer.PAST_DUE, { 'is_past_due': True }, True], [ShowAnswer.ALWAYS, {}, True], ['unexpected', {}, False], ]) @ddt.unpack @XBlock.register_temp_plugin(ShowAnswerXBlock, "showanswer") def test_answer_available_showanswer(self, showanswer, properties, expected): """ Try different values of showanswer and assert answer_available() """ block = self.get_root() block.showanswer = showanswer with mock.patch.object( ShowAnswerXBlock, 'correctness_available', return_value=True, ), mock.patch.object( ShowAnswerXBlock, 'runtime_user_is_staff', return_value=False, ), mock.patch.object( ShowAnswerXBlock, 'has_attempted', return_value=properties.get('has_attempted', False), ), mock.patch.object( ShowAnswerXBlock, 'is_correct', return_value=properties.get('is_correct', False), ), mock.patch.object( ShowAnswerXBlock, 'closed', return_value=properties.get('closed', False), ), mock.patch.object( ShowAnswerXBlock, 'is_past_due', return_value=properties.get('is_past_due', False), ): self.assertEqual(block.answer_available(), expected)
class XBlockHandlerTestCaseMixin: """ Load the XBlock in the workbench runtime to test its handler. """ def setUp(self): """ Create the runtime. """ super().setUp() self.runtime = WorkbenchRuntime() mock_publish = mock.MagicMock(side_effect=self.runtime.publish) self.runtime.publish = mock_publish def set_user(self, user_id): """ Provide a user ID to the runtime. Args: user_id (str): a user ID. Returns: None """ self.runtime.user_id = user_id def load_scenario(self, xml_path): """ Load an XML definition of an XBlock and return the XBlock instance. Args: xml (string): Path to an XML definition of the XBlock, relative to the test module. Returns: XBlock """ block_id = self.runtime.parse_xml_string( self.load_fixture_str(xml_path), self.runtime.id_generator) return self.runtime.get_block(block_id) def request(self, xblock, handler_name, content, request_method="POST", response_format=None, use_runtime=True): """ Make a request to an XBlock handler. Args: xblock (XBlock): The XBlock instance that should handle the request. handler_name (str): The name of the handler. content (unicode): Content of the request. Keyword Arguments: request_method (str): The HTTP method of the request (defaults to POST) response_format (None or str): Expected format of the response string. If `None`, return the raw response content; if 'json', parse the response as JSON and return the result. Raises: NotImplementedError: Response format not supported. Returns: Content of the response (mixed). """ # Create a fake request request = webob.Request(dict()) request.method = request_method request.body = content.encode('utf-8') # Send the request to the XBlock handler if use_runtime: response = self.runtime.handle(xblock, handler_name, request) else: response = getattr(xblock, handler_name)(request) # Parse the response (if a format is specified) if response_format is None: return response.body elif response_format == 'json': return json.loads(response.body.decode('utf-8')) else: raise NotImplementedError( f"Response format '{response_format}' not supported") def assert_assessment_event_published(self, xblock, event_name, assessment, **kwargs): """ Checks assessment event published successfuly. """ parts_list = [] for part in assessment["parts"]: # Some assessment parts do not include point values, # only written feedback. In this case, the assessment # part won't have an associated option. option_dict = None if part["option"] is not None: option_dict = { "name": part["option"]["name"], "points": part["option"]["points"], } # All assessment parts are associated with criteria criterion_dict = { "name": part["criterion"]["name"], "points_possible": part["criterion"]["points_possible"] } parts_list.append({ "option": option_dict, "criterion": criterion_dict, "feedback": part["feedback"] }) event_data = { "feedback": assessment["feedback"], "rubric": { "content_hash": assessment["rubric"]["content_hash"], }, "scorer_id": assessment["scorer_id"], "score_type": assessment["score_type"], "scored_at": assessment["scored_at"], "submission_uuid": assessment["submission_uuid"], "parts": parts_list } for key in kwargs: event_data[key] = kwargs[key] self.assert_event_published(xblock, event_name, event_data) def assert_event_published(self, xblock, event_name, event_data): """ Assert that an event was emitted with the given parameters. Args: event_name(str): The name of the event emitted event_data(dict): A dictionary containing the data we expect to have publish """ self.runtime.publish.assert_any_call(xblock, event_name, event_data) @staticmethod def load_fixture_str(path): """ Load data from a fixture file. Args: path (str): Path to the file. Returns: unicode: contents of the file. """ base_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(base_dir, path)) as file_handle: return file_handle.read()
def setUp(self): super(TestChildIsInstance, self).setUp() self.runtime = WorkbenchRuntime() self.root_id = self.runtime.parse_xml_string( '<block> <block><cat/><gr/></block> <cat/> <gr/> </block>')
def setUp(self): """Create the runtime. """ super(XBlockHandlerTestCaseMixin, self).setUp() self.runtime = WorkbenchRuntime()
class XBlockHandlerTestCaseMixin(object): """ Load the XBlock in the workbench runtime to test its handler. """ def setUp(self): """ Create the runtime. """ super(XBlockHandlerTestCaseMixin, self).setUp() self.runtime = WorkbenchRuntime() mock_publish = mock.MagicMock(side_effect=self.runtime.publish) self.runtime.publish = mock_publish def set_user(self, user_id): """ Provide a user ID to the runtime. Args: user_id (str): a user ID. Returns: None """ self.runtime.user_id = user_id def load_scenario(self, xml_path): """ Load an XML definition of an XBlock and return the XBlock instance. Args: xml (string): Path to an XML definition of the XBlock, relative to the test module. Returns: XBlock """ block_id = self.runtime.parse_xml_string( self.load_fixture_str(xml_path), self.runtime.id_generator ) return self.runtime.get_block(block_id) def request(self, xblock, handler_name, content, request_method="POST", response_format=None, use_runtime=True): """ Make a request to an XBlock handler. Args: xblock (XBlock): The XBlock instance that should handle the request. handler_name (str): The name of the handler. content (unicode): Content of the request. Keyword Arguments: request_method (str): The HTTP method of the request (defaults to POST) response_format (None or str): Expected format of the response string. If `None`, return the raw response content; if 'json', parse the response as JSON and return the result. Raises: NotImplementedError: Response format not supported. Returns: Content of the response (mixed). """ # Create a fake request request = webob.Request(dict()) request.method = request_method request.body = content # Send the request to the XBlock handler if use_runtime: response = self.runtime.handle(xblock, handler_name, request) else: response = getattr(xblock, handler_name)(request) # Parse the response (if a format is specified) if response_format is None: return response.body elif response_format == 'json': return json.loads(response.body) else: raise NotImplementedError("Response format '{format}' not supported".format(response_format)) def assert_assessment_event_published(self, xblock, event_name, assessment, **kwargs): parts_list = [] for part in assessment["parts"]: # Some assessment parts do not include point values, # only written feedback. In this case, the assessment # part won't have an associated option. option_dict = None if part["option"] is not None: option_dict = { "name": part["option"]["name"], "points": part["option"]["points"], } # All assessment parts are associated with criteria criterion_dict = { "name": part["criterion"]["name"], "points_possible": part["criterion"]["points_possible"] } parts_list.append({ "option": option_dict, "criterion": criterion_dict, "feedback": part["feedback"] }) event_data = { "feedback": assessment["feedback"], "rubric": { "content_hash": assessment["rubric"]["content_hash"], }, "scorer_id": assessment["scorer_id"], "score_type": assessment["score_type"], "scored_at": assessment["scored_at"], "submission_uuid": assessment["submission_uuid"], "parts": parts_list } for key in kwargs: event_data[key] = kwargs[key] self.assert_event_published( xblock, event_name, event_data ) def assert_event_published(self, xblock, event_name, event_data): """ Assert that an event was emitted with the given parameters. Args: event_name(str): The name of the event emitted event_data(dict): A dictionary containing the data we expect to have publish """ self.runtime.publish.assert_any_call( xblock, event_name, event_data ) @staticmethod def load_fixture_str(path): """ Load data from a fixture file. Args: path (str): Path to the file. Returns: unicode: contents of the file. """ base_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(base_dir, path)) as file_handle: return file_handle.read()
class XBlockHandlerTestCaseMixin(object): """Load the XBlock in the workbench runtime to test its handler. """ def setUp(self): """Create the runtime. """ super(XBlockHandlerTestCaseMixin, self).setUp() self.runtime = WorkbenchRuntime() def set_user(self, user_id): """Provide a user ID to the runtime. Args: user_id (str): a user ID. Returns: None """ self.runtime.user_id = user_id def load_scenario(self, xml_path): """Load an XML definition of an XBlock and return the XBlock instance. Args: xml (string): Path to an XML definition of the XBlock, relative to the test module. Returns: XBlock """ block_id = self.runtime.parse_xml_string( self.load_fixture_str(xml_path), self.runtime.id_generator ) return self.runtime.get_block(block_id) def request(self, xblock, handler_name, content, request_method="POST", response_format=None): """Make a request to an XBlock handler. Args: xblock (XBlock): The XBlock instance that should handle the request. handler_name (str): The name of the handler. content (unicode): Content of the request. Keyword Arguments: request_method (str): The HTTP method of the request (defaults to POST) response_format (None or str): Expected format of the response string. If `None`, return the raw response content; if 'json', parse the response as JSON and return the result. Raises: NotImplementedError: Response format not supported. Returns: Content of the response (mixed). """ # Create a fake request request = webob.Request(dict()) request.method = request_method request.body = content # Send the request to the XBlock handler response = self.runtime.handle(xblock, handler_name, request) # Parse the response (if a format is specified) if response_format is None: return response.body elif response_format == 'json': return json.loads(response.body) else: raise NotImplementedError("Response format '{format}' not supported".format(response_format)) @staticmethod def load_fixture_str(path): """Load data from a fixture file. Args: path (str): Path to the file. Returns: unicode: contents of the file. """ with open(path, 'rb') as file_handle: return file_handle.read()
def setUp(self): super(XmlTest, self).setUp() self.runtime = WorkbenchRuntime()
def setUp(self): """ Create the runtime. """ self.runtime = WorkbenchRuntime()
class XBlockHandlerTestCaseMixin(object): """Load the XBlock in the workbench runtime to test its handler. """ def setUp(self): """Create the runtime. """ super(XBlockHandlerTestCaseMixin, self).setUp() self.runtime = WorkbenchRuntime() def set_user(self, user_id): """Provide a user ID to the runtime. Args: user_id (str): a user ID. Returns: None """ self.runtime.user_id = user_id def load_scenario(self, xml_path): """Load an XML definition of an XBlock and return the XBlock instance. Args: xml (string): Path to an XML definition of the XBlock, relative to the test module. Returns: XBlock """ block_id = self.runtime.parse_xml_string( self.load_fixture_str(xml_path), self.runtime.id_generator) return self.runtime.get_block(block_id) def request(self, xblock, handler_name, content, request_method="POST", response_format=None): """Make a request to an XBlock handler. Args: xblock (XBlock): The XBlock instance that should handle the request. handler_name (str): The name of the handler. content (unicode): Content of the request. Keyword Arguments: request_method (str): The HTTP method of the request (defaults to POST) response_format (None or str): Expected format of the response string. If `None`, return the raw response content; if 'json', parse the response as JSON and return the result. Raises: NotImplementedError: Response format not supported. Returns: Content of the response (mixed). """ # Create a fake request request = webob.Request(dict()) request.method = request_method request.body = content # Send the request to the XBlock handler response = self.runtime.handle(xblock, handler_name, request) # Parse the response (if a format is specified) if response_format is None: return response.body elif response_format == 'json': return json.loads(response.body) else: raise NotImplementedError( u"Response format '{}' not supported".format(response_format)) @staticmethod def load_fixture_str(path): """Load data from a fixture file. Args: path (str): Path to the file. Returns: unicode: contents of the file. """ with open(path, 'rb') as file_handle: return file_handle.read()