Skip to content

Commit d1c0cb5

Browse files
hanseguckerKakadusniklasmohrin
authored
Use Django's LiveServerTestCase for frontend tests (#2077)
* add selenium live server test base * migrate student-vote.ts to python * migrate staff-evaluation-edit.ts to python * remove staff-user-import.ts * migrate results-index.ts to python * remove render_pages typescript interaction * use async clipboard api * disable serialized rollbacks --------- Co-authored-by: Jonas Dittrich <[email protected]> Co-authored-by: Niklas Mohrin <[email protected]>
1 parent 177546f commit d1c0cb5

30 files changed

+673
-707
lines changed

.github/workflows/tests.yml

+43-77
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,34 @@ jobs:
8787
- name: Check that "evaluation" section appears in help string
8888
run: python -m evap --help | grep --fixed-strings "[evaluation]"
8989

90+
test_frontend:
91+
name: Test Frontend
92+
93+
runs-on: ubuntu-22.04
94+
95+
steps:
96+
- uses: actions/checkout@v4
97+
with:
98+
submodules: true
99+
100+
- uses: ./.github/setup_evap
101+
with:
102+
shell: .#evap-frontend-dev
103+
start-db: true
104+
105+
- name: Compile assets
106+
run: |
107+
python manage.py ts compile
108+
python manage.py scss
109+
110+
- name: Run tests (shuffled)
111+
run: coverage run manage.py test --shuffle --tag selenium
112+
- name: Upload coverage
113+
uses: codecov/codecov-action@v5
114+
with:
115+
flags: frontend-tests
116+
token: ${{ secrets.CODECOV_TOKEN }}
117+
90118
mypy:
91119
runs-on: ubuntu-22.04
92120

@@ -98,6 +126,21 @@ jobs:
98126
- name: Run MyPy
99127
run: mypy
100128

129+
typescript:
130+
runs-on: ubuntu-22.04
131+
name: Test Typescript
132+
133+
steps:
134+
- uses: actions/checkout@v4
135+
with:
136+
submodules: true
137+
- uses: ./.github/setup_evap
138+
with:
139+
npm-ci: true
140+
141+
- name: Run tests
142+
run: python manage.py ts test
143+
101144
linter:
102145
runs-on: ubuntu-22.04
103146

@@ -171,83 +214,6 @@ jobs:
171214
- name: Reload backup
172215
run: echo "yy" | deployment/load_production_backup.sh backup.json
173216

174-
compile_scss:
175-
runs-on: ubuntu-22.04
176-
177-
name: Compile Scss
178-
179-
steps:
180-
- uses: actions/checkout@v4
181-
with:
182-
submodules: true
183-
- uses: ./.github/setup_evap
184-
with:
185-
npm-ci: true
186-
187-
- name: Compile Scss
188-
run: npx sass evap/static/scss/evap.scss evap/static/css/evap.css
189-
- name: Store Css
190-
uses: actions/upload-artifact@v4
191-
with:
192-
name: css
193-
path: evap/static/css/evap.css
194-
195-
render_pages:
196-
runs-on: ubuntu-22.04
197-
198-
name: Render Html pages
199-
200-
steps:
201-
- uses: actions/checkout@v4
202-
- uses: ./.github/setup_evap
203-
with:
204-
start-db: true
205-
206-
- name: Render pages
207-
run: coverage run manage.py ts render_pages
208-
- name: Upload coverage
209-
uses: codecov/codecov-action@v5
210-
with:
211-
flags: render-pages
212-
token: ${{ secrets.CODECOV_TOKEN }}
213-
- name: Store rendered pages
214-
uses: actions/upload-artifact@v4
215-
with:
216-
name: rendered-pages
217-
path: evap/static/ts/rendered
218-
219-
typescript:
220-
runs-on: ubuntu-22.04
221-
222-
needs: [ compile_scss, render_pages ]
223-
224-
name: Test Typescript
225-
226-
steps:
227-
- uses: actions/checkout@v4
228-
with:
229-
submodules: true
230-
- uses: ./.github/setup_evap
231-
with:
232-
npm-ci: true
233-
234-
- run: npx puppeteer browsers install chrome
235-
236-
- name: Compile Typescript
237-
run: npx tsc --project evap/static/ts/tsconfig.compile.json
238-
- name: Load rendered pages
239-
uses: actions/download-artifact@v4
240-
with:
241-
name: rendered-pages
242-
path: evap/static/ts/rendered
243-
- name: Load Css
244-
uses: actions/download-artifact@v4
245-
with:
246-
name: css
247-
path: evap/static/css
248-
- name: Run tests
249-
run: xvfb-run --auto-servernum npx jest
250-
251217
macos-nix-build:
252218
runs-on: macos-14
253219
name: Build nix environment on MacOS

deployment/manage_autocompletion.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# generated using
44
# ./manage.py | grep -v -E "^\[|^$" | tail -n +3 | sort | xargs
55
COMMANDS="admin_generator anonymize changepassword check clean_pyc clear_cache clearsessions collectstatic compile_pyc compilemessages create_command create_jobs create_template_tags createcachetable createsuperuser dbshell delete_squashed_migrations describe_form diffsettings drop_test_database dump_testdata dumpdata dumpscript export_emails find_template findstatic flush format generate_password generate_secret_key graph_models inspectdb lint list_model_info list_signals loaddata loaddata_unlogged mail_debug makemessages makemigrations managestate merge_model_instances migrate notes optimizemigration pipchecker precommit print_settings print_user_for_session raise_test_exception refresh_results_cache reload_testdata remove_stale_contenttypes reset_db reset_schema run runjob runjobs runprofileserver runscript runserver runserver_plus scss send_reminders sendtestemail set_default_site set_fake_emails set_fake_passwords shell shell_plus show_template_tags show_urls showmigrations sqlcreate sqldiff sqldsn sqlflush sqlmigrate sqlsequencereset squashmigrations startapp startproject sync_s3 syncdata test testserver tools translate ts typecheck unreferenced_files update_evaluation_states update_permissions validate_templates"
6-
TS_COMMANDS="compile test render_pages"
6+
TS_COMMANDS="compile test"
77

88
_managepy_complete()
99
{

evap/contributor/tests/test_views.py

-20
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
WebTest,
99
WebTestWith200Check,
1010
create_evaluation_with_responsible_and_editor,
11-
render_pages,
1211
submit_with_modal,
1312
)
1413

@@ -136,8 +135,6 @@ def test_without_questionnaires_assigned(self):
136135

137136

138137
class TestContributorEvaluationEditView(WebTest):
139-
render_pages_url = "/contributor/evaluation/PK/edit"
140-
141138
@classmethod
142139
def setUpTestData(cls):
143140
result = create_evaluation_with_responsible_and_editor()
@@ -146,23 +143,6 @@ def setUpTestData(cls):
146143
cls.evaluation = result["evaluation"]
147144
cls.url = f"/contributor/evaluation/{cls.evaluation.pk}/edit"
148145

149-
@render_pages
150-
def render_pages(self):
151-
self.evaluation.allow_editors_to_edit = False
152-
self.evaluation.save()
153-
154-
content_without_allow_editors_to_edit = self.app.get(self.url, user=self.editor).content
155-
156-
self.evaluation.allow_editors_to_edit = True
157-
self.evaluation.save()
158-
159-
content_with_allow_editors_to_edit = self.app.get(self.url, user=self.editor).content
160-
161-
return {
162-
"normal": content_without_allow_editors_to_edit,
163-
"allow_editors_to_edit": content_with_allow_editors_to_edit,
164-
}
165-
166146
def test_not_authenticated(self):
167147
"""
168148
Asserts that an unauthorized user gets redirected to the login page.

evap/evaluation/management/commands/ts.py

-26
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,9 @@
11
import argparse
22
import subprocess # nosec
3-
import unittest
43

54
from django.conf import settings
65
from django.core.management import call_command
76
from django.core.management.base import BaseCommand, CommandError
8-
from django.test.runner import DiscoverRunner
9-
10-
11-
class RenderPagesRunner(DiscoverRunner):
12-
"""Test runner which only includes `render_pages.*` methods.
13-
The actual logic of the page rendering is implemented in the `@render_pages` decorator."""
14-
15-
test_loader = unittest.TestLoader()
16-
17-
def __init__(self, **kwargs):
18-
super().__init__(**kwargs)
19-
self.test_loader.testMethodPrefix = "render_pages"
207

218

229
class Command(BaseCommand):
@@ -31,7 +18,6 @@ def add_arguments(self, parser: argparse.ArgumentParser):
3118
self.add_fresh_argument(compile_parser)
3219
test_parser = subparsers.add_parser("test")
3320
self.add_fresh_argument(test_parser)
34-
subparsers.add_parser("render_pages")
3521

3622
@staticmethod
3723
def add_fresh_argument(parser: argparse.ArgumentParser):
@@ -47,8 +33,6 @@ def handle(self, *args, **options):
4733
self.compile(**options)
4834
elif options["command"] == "test":
4935
self.test(**options)
50-
elif options["command"] == "render_pages":
51-
self.render_pages(**options)
5236

5337
def run_command(self, command):
5438
try:
@@ -80,14 +64,4 @@ def compile(self, watch=False, fresh=False, **_options):
8064
def test(self, **options):
8165
call_command("scss")
8266
self.compile(**options)
83-
self.render_pages()
8467
self.run_command(["npx", "jest"])
85-
86-
@staticmethod
87-
def render_pages(**_options):
88-
# Enable debug mode as otherwise a collectstatic beforehand would be necessary,
89-
# as missing static files would result into an error.
90-
test_runner = RenderPagesRunner(debug_mode=True)
91-
failed_tests = test_runner.run_tests([])
92-
if failed_tests > 0:
93-
raise CommandError("Failures during render_pages")

evap/evaluation/tests/test_commands.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -269,12 +269,10 @@ def test_ts_compile_with_watch(self, mock_subprocess_run):
269269

270270
@patch("subprocess.run")
271271
@patch("evap.evaluation.management.commands.ts.call_command")
272-
@patch("evap.evaluation.management.commands.ts.Command.render_pages")
273-
def test_ts_test(self, mock_render_pages, mock_call_command, mock_subprocess_run):
272+
def test_ts_test(self, mock_call_command, mock_subprocess_run):
274273
management.call_command("ts", "test")
275274

276275
# Mock render pages to prevent a second call into the test framework
277-
mock_render_pages.assert_called_once()
278276
mock_call_command.assert_called_once_with("scss")
279277
mock_subprocess_run.assert_has_calls(
280278
[

evap/evaluation/tests/test_live.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from django.core import mail
2+
from django.urls import reverse
3+
from selenium.webdriver.common.by import By
4+
from selenium.webdriver.support.expected_conditions import text_to_be_present_in_element, visibility_of_element_located
5+
6+
from evap.evaluation.tests.tools import LiveServerTest
7+
8+
9+
class ContactModalTests(LiveServerTest):
10+
def test_contact_modal(self) -> None:
11+
self.selenium.get(self.live_server_url + reverse("evaluation:index"))
12+
self.selenium.find_element(By.ID, "feedbackModalShowButton").click()
13+
self.wait.until(visibility_of_element_located((By.ID, "feedbackModalMessageText")))
14+
self.selenium.find_element(By.ID, "feedbackModalMessageText").send_keys("Test message")
15+
self.selenium.find_element(By.ID, "feedbackModalActionButton").click()
16+
17+
self.wait.until(
18+
text_to_be_present_in_element(
19+
(By.CSS_SELECTOR, "#successMessageModal_feedbackModal .modal-body"),
20+
"Your message was successfully sent.",
21+
)
22+
)
23+
self.assertEqual(len(mail.outbox), 1)
24+
25+
self.assertEqual(mail.outbox[0].subject, f"[EvaP] Message from {self.manager.email}")

evap/evaluation/tests/test_views.py

-10
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,10 @@
1111
WebTestWith200Check,
1212
create_evaluation_with_responsible_and_editor,
1313
make_manager,
14-
store_ts_test_asset,
1514
)
1615
from evap.staff.tests.utils import WebTestStaffMode
1716

1817

19-
class RenderJsTranslationCatalog(WebTest):
20-
url = reverse("javascript-catalog")
21-
22-
def render_pages(self):
23-
# Not using render_pages decorator to manually create a single (special) javascript file
24-
content = self.app.get(self.url).content
25-
store_ts_test_asset("catalog.js", content)
26-
27-
2818
@override_settings(PASSWORD_HASHERS=["django.contrib.auth.hashers.MD5PasswordHasher"])
2919
class TestIndexView(WebTest):
3020
url = "/"

0 commit comments

Comments
 (0)