Skip to content
Snippets Groups Projects
test_feedback_creation.py 12.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • from django.test import LiveServerTestCase
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.common.exceptions import StaleElementReferenceException
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as ec
    
    from core.models import UserAccount, Submission, FeedbackComment
    from functional_tests.util import (login, create_browser, reset_browser_after_test,
                                       subscriptions_loaded_cond)
    from util import factory_boys as fact
    
    
    class UntestedParent:
        class TestFeedbackCreationGeneric(LiveServerTestCase):
            browser: webdriver.Firefox = None
            username = None
            password = None
            role = None
    
            @classmethod
            def setUpClass(cls):
                super().setUpClass()
                cls.browser = create_browser()
    
            @classmethod
            def tearDownClass(cls):
                super().tearDownClass()
                cls.browser.quit()
    
            def setUp(self):
                self.sub_type = fact.SubmissionTypeFactory.create()
                fact.SubmissionFactory.create_batch(2, type=self.sub_type)
    
            def tearDown(self):
                reset_browser_after_test(self.browser, self.live_server_url)
    
            def _login(self):
                login(self.browser, self.live_server_url, self.username, self.password)
    
            def _reconstruct_submission_code(self):
                sub_table = self.browser.find_element_by_class_name('submission-table')
                lines = sub_table.find_elements_by_tag_name('tr')
                line_no_code_pairs = [
                    (line.get_attribute('id'),
                     # call get_attribute here to get non normalized text
                     # https://github.com/SeleniumHQ/selenium/issues/2608
                     line.find_element_by_class_name('code-cell-content').get_attribute('textContent'))
                    for line in lines
                ]
                line_no_code_pairs.sort(key=lambda x: x[0])  # sort by ids
                code_lines = list(zip(*line_no_code_pairs))[1]
                return '\n'.join(code_lines)
    
            # stage can be 'initial', 'validate', or 'conflict'
            def _go_to_subscription(self, stage='initial', sub_type=None):
    
                WebDriverWait(self.browser, 10).until(subscriptions_loaded_cond(self.browser))
    
                tasks = self.browser.find_element_by_name('subscription-list')
                tab = tasks.find_element_by_xpath(f'//*[contains(text(), "{stage}")]')
                tab.click()
                sub_type = sub_type if sub_type is not None else self.sub_type
    
                sub_type_xpath = f'//*[contains(text(), "{sub_type.name}") ' \
                    f'and not(contains(@class, "inactive"))]'
    
                WebDriverWait(self.browser, 10).until(
    
                    ec.element_to_be_clickable((By.XPATH, sub_type_xpath)),
                    message="SubmissionType not clickable"
                )
                sub_type_el = tasks.find_element_by_xpath(sub_type_xpath)
                sub_type_el.click()
    
                WebDriverWait(self.browser, 10).until(ec.url_contains('subscription'))
    
    
            def write_comments_on_lines(self, line_comment_tuples):
                """ line_comment_tuples is an iterable containing tuples of
                    (line_no, comment) where the line number starts at 1
                """
    
                sub_table = self.browser.find_element_by_class_name('submission-table')
                lines = sub_table.find_elements_by_tag_name('tr')
    
                for (line_no, comment) in line_comment_tuples:
                    line = lines[line_no-1]
                    line.find_element_by_tag_name('button').click()
                    textarea = line.find_element_by_tag_name('textarea')
                    textarea.send_keys(comment)
                    line.find_element_by_id('submit-comment').click()
    
            def test_student_text_is_correctly_displayed(self):
                self._login()
                self._go_to_subscription()
                code = self._reconstruct_submission_code()
                # query db for Submission with seen code, throws if not present and test fails
                Submission.objects.get(text=code)
    
            def test_submission_type_is_correctly_displayed(self):
                self._login()
                self._go_to_subscription()
                sub_type_el = self.browser.find_element_by_id('submission-type')
                title = sub_type_el.find_element_by_class_name('title')
                self.assertEqual(
                    f'{self.sub_type.name} - Full score: {self.sub_type.full_score}',
                    title.text
                )
                solution = sub_type_el.find_element_by_class_name('solution-code')
                self.assertEqual(self.sub_type.solution, solution.get_attribute('textContent'))
                description = sub_type_el.find_element_by_class_name('type-description')
                html_el_in_desc = description.find_element_by_tag_name('h1')
                self.assertEqual('This', html_el_in_desc.text)
    
            def test_test_output_is_displayed(self):
                # create a test for every submission
                test = None
                for submission in Submission.objects.all():
                    test = fact.TestFactory.create(submission=submission, annotation='This is a test')
                self._login()
                self._go_to_subscription()
                tests = self.browser.find_element_by_id('submission-tests')
                name_label = tests.find_element_by_name('test-name-label')
                name_label.click()
                self.assertIn(test.name, name_label.text)
                self.assertIn(test.label, name_label.text)
                test_output = tests.find_element_by_name('test-output')
    
                WebDriverWait(self.browser, 10).until(ec.visibility_of(test_output))
    
                self.assertEqual(test.annotation, test_output.text)
    
            def wait_until_code_changes(self, code):
                def condition(*args):
                    try:
                        # code might change during the call resulting in the exception
                        new_code = self._reconstruct_submission_code()
                    except StaleElementReferenceException:
                        return False
                    return code != new_code
                return condition
    
            def test_can_give_max_score(self):
                self._login()
                self._go_to_subscription()
                code = self._reconstruct_submission_code()
                self.browser.find_element_by_id('score-full').click()
                submit_btn = self.browser.find_element_by_id('submit-feedback')
                submit_btn.click()
    
                WebDriverWait(self.browser, 10).until(
    
                    self.wait_until_code_changes(code)
                )
                submission_for_code = Submission.objects.get(text=code)
                self.assertEqual(self.sub_type.full_score, submission_for_code.feedback.score)
    
            def test_zero_score_without_warning_gives_error(self):
                self._login()
                self._go_to_subscription()
                self.browser.find_element_by_id('score-zero').click()
                submit_btn = self.browser.find_element_by_id('submit-feedback')
                submit_btn.click()
                notification = self.browser.find_element_by_class_name('notification-content')
                self.assertIn('comment', notification.text)
    
            def test_can_give_zero_score(self):
                self._login()
                self._go_to_subscription()
                code = self._reconstruct_submission_code()
                self.browser.find_element_by_id('score-zero').click()
                self.write_comments_on_lines([(0, 'A comment')])
                self.browser.find_element_by_id('submit-feedback').click()
    
                WebDriverWait(self.browser, 10).until(self.wait_until_code_changes(code))
    
                submission_for_code = Submission.objects.get(text=code)
                self.assertEqual(0, submission_for_code.feedback.score)
    
            def test_can_give_comments_and_decreased_score(self):
                self._login()
                self._go_to_subscription()
                code = self._reconstruct_submission_code()
    
                # give half full score
                score_input = self.browser.find_element_by_id('score-input')
                score_input.send_keys(self.sub_type.full_score // 2)
    
                # give feedback on first and last line of submission
                comment_text = 'This is feedback'
                self.write_comments_on_lines([
                    (1, comment_text), (0, comment_text)  # 0 corresponds to the last line
                ])
    
                submit_btn = self.browser.find_element_by_id('submit-feedback')
                submit_btn.click()
    
                WebDriverWait(self.browser, 10).until(
    
                    self.wait_until_code_changes(code)
                )
                submission_for_code = Submission.objects.get(text=code)
                self.assertEqual(self.sub_type.full_score // 2, submission_for_code.feedback.score)
                self.assertEqual(2, submission_for_code.feedback.feedback_lines.count())
                fst_comment = FeedbackComment.objects.get(
                    of_feedback=submission_for_code.feedback,
                    of_line=1
                )
                self.assertEqual(comment_text, fst_comment.text)
                last_line_of_sub = len(submission_for_code.text.split('\n'))
                snd_comment = FeedbackComment.objects.get(
                    of_feedback=submission_for_code.feedback,
                    of_line=last_line_of_sub
                )
                self.assertEqual(comment_text, snd_comment.text)
    
            def test_can_skip_submission(self):
                self._login()
                self._go_to_subscription()
                code = self._reconstruct_submission_code()
                self.browser.find_element_by_id('skip-submission').click()
    
                WebDriverWait(self.browser, 10).until(self.wait_until_code_changes(code))
    
    
            def test_can_validate_submission(self):
                self._login()
                self._go_to_subscription()
    
    
                def correct():
                    code = self._reconstruct_submission_code()
                    self.write_comments_on_lines([(0, 'A comment by me')])
                    self.browser.find_element_by_id('score-zero').click()
                    self.browser.find_element_by_id('submit-feedback').click()
                    return code
                code = correct()
    
                WebDriverWait(self.browser, 10).until(self.wait_until_code_changes(code))
    
                correct()
    
                sub_url = 'subscription/' + str(self.sub_type.pk) + '/ended'
                WebDriverWait(self.browser, 10).until(ec.url_contains(sub_url))
    
    
                reset_browser_after_test(self.browser, self.live_server_url)  # logs out user
    
                user_snd = 'tutor_snd'
                password = 'p'
                fact.UserAccountFactory(username=user_snd, password=password)
    
                login(self.browser, self.live_server_url, user_snd, password)
                self._go_to_subscription(stage='validate')
                self.write_comments_on_lines([(0, 'I disagree'), (1, 'Full points!')])
    
                code_final = self._reconstruct_submission_code()
    
                self.browser.find_element_by_id('score-full').click()
                self.browser.find_element_by_id('submit-feedback').click()
    
    
                WebDriverWait(self.browser, 10).until(self.wait_until_code_changes(code_final))
                code_non_final = self._reconstruct_submission_code()
                self.browser.find_element_by_class_name('final-checkbox').click()
                self.browser.find_element_by_id('submit-feedback').click()
    
    
                sub_url = 'subscription/' + str(self.sub_type.pk) + '/ended'
                WebDriverWait(self.browser, 10).until(ec.url_contains(sub_url))
    
    
                reset_browser_after_test(self.browser, self.live_server_url)
    
                user_rev = 'rev'
                password = 'p'
                role = UserAccount.REVIEWER
                fact.UserAccountFactory(username=user_rev, password=password, role=role)
                login(self.browser, self.live_server_url, user_rev, password)
    
                self._go_to_subscription('conflict')
                code = self._reconstruct_submission_code()
                self.assertEqual(code, code_non_final)
    
                submission_for_code = Submission.objects.get(text=code_final)
    
                self.assertEqual(self.sub_type.full_score, submission_for_code.feedback.score)
                self.assertEqual(3, submission_for_code.feedback.feedback_lines.count())
    
    
                submission_for_code = Submission.objects.get(text=code_non_final)
                self.assertEqual(0, submission_for_code.feedback.score)
                self.assertEqual(1, submission_for_code.feedback.feedback_lines.count())
    
    
    
    class TestFeedbackCreationTutor(UntestedParent.TestFeedbackCreationGeneric):
        def setUp(self):
            super().setUp()
            self.username = 'tutor'
            self.password = 'p'
            self.role = UserAccount.TUTOR
            fact.UserAccountFactory(
                username=self.username,
    
                password=self.password,
                role=self.role