Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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'))
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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