diff --git a/Makefile b/Makefile index 9aa05e461655f1ead6a16d5d8875b289d25d5c98..3b60ef1fcc6ae7a03155ff09074a4957936e26ee 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,9 @@ install: test: DJANGO_SETTINGS_MODULE=grady.settings pytest +teste2e: + cd frontend && yarn build && cp dist/index.html ../core/templates && cd .. && python util/format_index.py && python manage.py collectstatic --no-input && HEADLESS_TESTS=$(headless) pytest --ds=grady.settings $(path); git checkout core/templates/index.html + coverage: DJANGO_SETTINGS_MODULE=grady.settings pytest --cov coverage html diff --git a/frontend/src/components/subscriptions/SubscriptionList.vue b/frontend/src/components/subscriptions/SubscriptionList.vue index 0edd3ceddf96b1ef97e2aa05b6737157fcac62b0..316b1b0e4c8cfe76618b35b84ea262d7a8576c98 100644 --- a/frontend/src/components/subscriptions/SubscriptionList.vue +++ b/frontend/src/components/subscriptions/SubscriptionList.vue @@ -1,5 +1,5 @@ <template> - <v-card> + <v-card name='subscription-list'> <v-toolbar color="teal" :dense="sidebar"> <v-toolbar-side-icon><v-icon>assignment</v-icon></v-toolbar-side-icon> <v-toolbar-title v-if="showDetail" style="min-width: fit-content;"> diff --git a/functional_tests/test_front_pages.py b/functional_tests/test_front_pages.py index 5aa9c73c3caefde5114339616122f8df8ef4c012..70e8dc0834a90b1b9744e7cb903f091700aa891f 100644 --- a/functional_tests/test_front_pages.py +++ b/functional_tests/test_front_pages.py @@ -1,38 +1,62 @@ -import os -from typing import Sequence +import time from django.test import LiveServerTestCase from selenium import webdriver -from selenium.webdriver.remote.webelement import WebElement -from functional_tests.util import get_frontend_url, login, create_browser +from core import models +from core.models import UserAccount +from functional_tests.util import login, create_browser, extract_hrefs_hashes from util import factory_boys as fact -LiveServerTestCase.port = int(os.environ.get('LIVE_SERVER_PORT', 0)) - -class FrontPageTestsTutorReviewer(): - def __init__(self): - self.browser: webdriver.Firefox = None - - def _login(self): - login(self.browser, self.live_server_url, self.username, self.password) - - def test_statistics_are_shown(self): - self._login() - statistcs = self.browser.find_element_by_id('correction-statistics') - title = statistcs.find_element_by_class_name('title') - self.assertEqual('Statistics', title.text) - - -class FrontPageTestsTutor(LiveServerTestCase, FrontPageTestsTutorReviewer): +class UntestedParent: + class FrontPageTestsTutorReviewer(LiveServerTestCase): + browser: webdriver.Firefox = None + username = None + password = None + role = None + + def setUp(self): + fact.SubmissionFactory.create_batch(4) + + def _login(self): + login(self.browser, self.live_server_url, self.username, self.password) + + def test_statistics_are_shown(self): + self._login() + statistics = self.browser.find_element_by_id('correction-statistics') + title = statistics.find_element_by_class_name('title') + self.assertEqual('Statistics', title.text) + + def test_available_tasks_are_shown(self): + self._login() + tasks = self.browser.find_element_by_name('subscription-list') + title = tasks.find_element_by_class_name('v-toolbar__title') + self.assertEqual('Tasks', title.text) + time.sleep(8) + subscription_links = extract_hrefs_hashes( + tasks.find_elements_by_tag_name('a') + ) + subscriptions = models.SubmissionSubscription.objects.filter( + owner__username=self.username, + deactivated=False, + feedback_stage=models.SubmissionSubscription.FEEDBACK_CREATION + ).exclude(query_type=models.SubmissionSubscription.RANDOM) + for sub in subscriptions: + self.assertIn(f'/subscription/{sub.pk}', subscription_links) + + +class FrontPageTestsTutor(UntestedParent.FrontPageTestsTutorReviewer): def setUp(self): - self.live_server_url = get_frontend_url(self.live_server_url) + super().setUp() self.browser = create_browser() self.username = 'tutor' self.password = 'p' - fact.UserAccountFactory(username=self.username, password=self.password) - fact.SubmissionFactory() + self.role = UserAccount.TUTOR + fact.UserAccountFactory( + username=self.username, + password=self.password + ) def tearDown(self): self.browser.quit() @@ -40,8 +64,9 @@ class FrontPageTestsTutor(LiveServerTestCase, FrontPageTestsTutorReviewer): def test_side_bar_contains_correct_items(self): self._login() drawer = self.browser.find_element_by_class_name('v-navigation-drawer') - links = extract_hrefs(drawer.find_elements_by_tag_name('a')) - self.assertTrue(all(link in links for link in ['#/home', '#/feedback'])) + links = extract_hrefs_hashes(drawer.find_elements_by_tag_name('a')) + print(links) + self.assertTrue(all(link in links for link in ['/home', '/feedback'])) task_title = drawer.find_element_by_class_name('v-toolbar__title') self.assertEqual('Tasks', task_title.text) footer = drawer.find_element_by_class_name('sidebar-footer') @@ -51,13 +76,19 @@ class FrontPageTestsTutor(LiveServerTestCase, FrontPageTestsTutorReviewer): feedback_link.get_attribute('href')) -class FrontPageTestsReviewer(LiveServerTestCase, FrontPageTestsTutorReviewer): +class FrontPageTestsReviewer(UntestedParent.FrontPageTestsTutorReviewer): def setUp(self): - self.live_server_url = get_frontend_url(self.live_server_url) + super().setUp() + self.browser = create_browser() self.browser = create_browser() self.username = 'reviewer' self.password = 'p' - fact.UserAccountFactory(username=self.username, password=self.password) + self.role = UserAccount.REVIEWER + fact.UserAccountFactory( + username=self.username, + password=self.password, + role=self.role + ) def tearDown(self): self.browser.quit() @@ -65,9 +96,9 @@ class FrontPageTestsReviewer(LiveServerTestCase, FrontPageTestsTutorReviewer): def test_side_bar_contains_correct_items(self): self._login() drawer = self.browser.find_element_by_class_name('v-navigation-drawer') - links = extract_hrefs(drawer.find_elements_by_tag_name('a')) + links = extract_hrefs_hashes(drawer.find_elements_by_tag_name('a')) self.assertTrue(all(link in links for link in - ['#/home', '#/feedback', '#/student-overview', '#/tutor-overview'])) + ['/home', '/feedback', '/student-overview', '/tutor-overview'])) task_title = drawer.find_element_by_class_name('v-toolbar__title') self.assertEqual('Tasks', task_title.text) footer = drawer.find_element_by_class_name('sidebar-footer') @@ -75,7 +106,3 @@ class FrontPageTestsReviewer(LiveServerTestCase, FrontPageTestsTutorReviewer): self.assertEqual('Give us Feedback!', feedback_link.text) self.assertEqual('https://gitlab.gwdg.de/j.michal/grady/issues', feedback_link.get_attribute('href')) - - -def extract_hrefs(web_elements: Sequence[WebElement]): - return [el.get_attribute('href') for el in web_elements] diff --git a/functional_tests/test_login_page.py b/functional_tests/test_login_page.py index b564cc7627070c6c47ffb36168e162632014756f..21907957aef136fc4d1e216e0f60645b5ad1a7be 100644 --- a/functional_tests/test_login_page.py +++ b/functional_tests/test_login_page.py @@ -7,7 +7,6 @@ from selenium.webdriver.common.keys import Keys from selenium.webdriver.firefox.options import Options from core.models import UserAccount -from functional_tests.util import get_frontend_url from util.factories import make_test_data @@ -16,7 +15,6 @@ LiveServerTestCase.port = int(os.environ.get('LIVE_SERVER_PORT', 0)) class LoginPageTest(LiveServerTestCase): def setUp(self): - self.live_server_url = get_frontend_url(self.live_server_url) options = Options() # funnily the method is marked deprecated but the alternative setter is not working... options.headless = bool(os.environ.get('HEADLESS_TESTS', False)) @@ -134,4 +132,3 @@ class LoginPageTest(LiveServerTestCase): tutor = UserAccount.objects.get(username=username) self.assertEqual(UserAccount.TUTOR, tutor.role) self.assertFalse(tutor.is_active, "Tutors should be inactive after registered") - diff --git a/functional_tests/util.py b/functional_tests/util.py index 178ddd2596e1e51a82ea771b88f059b37ba489ea..c1ffba6f90c4a69fa92446446a6fecfd6294fe34 100644 --- a/functional_tests/util.py +++ b/functional_tests/util.py @@ -1,13 +1,12 @@ import os import time +from itertools import islice +from typing import Sequence from selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdriver.firefox.options import Options - - -def get_frontend_url(live_server_url): - return os.environ.get('FRONTEND_URL', live_server_url) +from selenium.webdriver.remote.webelement import WebElement def create_browser(): @@ -26,3 +25,13 @@ def login(browser, live_server_url, username, password='p'): password_input.send_keys(password) browser.find_element_by_xpath('//button[@type="submit"]').send_keys(Keys.ENTER) time.sleep(1) + + +def nth(iterable, n, default=None): + "Returns the nth item or a default value" + return next(islice(iterable, n, None), default) + + +def extract_hrefs_hashes(web_elements: Sequence[WebElement]): + return [nth(el.get_attribute('href').split('#'), 1, '') + for el in web_elements if el.get_attribute('href')] diff --git a/util/factory_boys.py b/util/factory_boys.py index 38d21ed3924b62722ae4c3b4d504440f517cae23..4de7d9edabbbc291a926c2903a40b3fb41406c25 100644 --- a/util/factory_boys.py +++ b/util/factory_boys.py @@ -10,6 +10,8 @@ fake.seed(42) class ExamTypeFactory(DjangoModelFactory): class Meta: model = models.ExamType + django_get_or_create = ('module_reference',) + module_reference = 'B.Inf.4242 Test Module' total_score = 90 pass_score = 45 @@ -29,11 +31,12 @@ class SubmissionTypeFactory(DjangoModelFactory): class UserAccountFactory(DjangoModelFactory): class Meta: model = models.UserAccount + django_get_or_create = ('username',) - role = models.UserAccount.REVIEWER - fullname = fake.name() - username = fake.user_name() - password = factory.PostGenerationMethodCall('set_password', 'p') + role = models.UserAccount.TUTOR + fullname = fake.name + username = fake.user_name + password = factory.PostGenerationMethodCall('set_password', 'redrum-is-murder-reversed') class StudentInfoFactory(DjangoModelFactory): @@ -49,7 +52,7 @@ class TestFactory(DjangoModelFactory): model = models.Test name = 'EmptyTest' - label = 'Epmty' + label = 'Empty' annotation = 'This is an annotation'