Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Playwright E2E tests #63

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/lint.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
FROM python:3.10-alpine
FROM python:3.10-bookworm

WORKDIR /app

RUN apk update && apk add git freetype-dev gcc musl-dev libffi-dev g++ curl tar python3
RUN apt update && apt install -y git libfreetype6-dev gcc musl-dev libffi-dev g++ curl tar python3

RUN adduser --disabled-password prospector prospector \
RUN adduser --disabled-password prospector \
&& chown -R prospector:prospector /app \
&& rm -rf ${HOME}/.cache/ ${HOME}/.local/bin/__pycache__/
USER prospector
Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,10 @@ tokenizer/scalariform/project/project/target/
__pycache__
*.pyc
*.pdf

dolos-proxy
package-lock.json
package.json
*.Identifier
settings.json
temp_submission_files/
57 changes: 57 additions & 0 deletions e2e_tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Radar Testing Guide

This document provides a guide to create and run end-to-end tests for Radar using [Playwright](https://playwright.dev/python/).

## Set up environment

Make sure everything is set up following this [Radar development guide](https://github.com/apluslms/radar/blob/master/doc/DEVELOPMENT.md) before continuing.

1. Run Python environment: `source py_venv/bin/activate`

2. Before testing create super user using: `python manage.py createsuperuser`<br>
Username: `Username`<br>
Email: `Username@email.com`<br>
Password: `Password`<br>

3. Run server: `python manage.py runserver`

4. Load submissions: `./run_loadsubmissions.sh ${directory_with_submissions} {course} {exercise} 1`

5. In a browser navigate to: `http://localhost:8000/{course}/{exercise}/settings/`
* Press the "Recompare all" button

6. Match submissions: `python manage.py matchsubmissions {course}/{exercise}`

## Set up Dolos

Dolos is required for `test_dolos` in `e2e_tests/test_dolos.py`.

1. Clone the [Dolos](https://github.com/dodona-edu/dolos) repository

2. Launch Docker daemon: `dockerd`

3. In a new terminal at the root of the Dolos repository run: `docker compose up`

Now Dolos should be running in a separate window and work with Radar.

## Create tests

1. Open new terminal.

2. Run Python environment: `source py_venv/bin/activate`

3. Record tests: `playwright codegen --target python-pytest "localhost:8000"`

4. Copy generated code into a Python file.

[Generating tests](https://playwright.dev/python/docs/codegen-intro)

[Writing tests](https://playwright.dev/python/docs/writing-tests)

## Run and debug tests

Run all tests: `pytest`

Run and debug a specific test: `PWDEBUG=1 pytest -s -k <test_method_name>`

[Running and debugging tests](https://playwright.dev/python/docs/running-tests)
Empty file added e2e_tests/__init__.py
Empty file.
26 changes: 26 additions & 0 deletions e2e_tests/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Imports
from playwright.sync_api import Page, expect

# Function to login
def login(page: Page) -> None:
page.goto('http://localhost:8000/')
page.get_by_label('Username').click()
page.get_by_label('Username').fill('Username')
page.get_by_label('Password').click()
page.get_by_label('Password').fill('Password')
page.get_by_role('link', name='Hide »').click()
page.get_by_role('button', name='Login').click()
page.locator('td > a').first.click()

# Function to save settings and navigate back
def save_and_navigate_back(page: Page) -> None:
page.locator('button[name=\'save\']').click()
page.locator('.breadcrumb > li:nth-child(2)').click()

#Function to change exercise value and check if it was changed
def change_value(page: Page, value_to_change: str, exercise_value: str, contain_text: str = '') -> None:
page.get_by_role('link', name=' Settings').first.click()
page.get_by_label(value_to_change).click()
page.get_by_label(value_to_change).fill(exercise_value)
save_and_navigate_back(page)
expect(page.locator('tbody')).to_contain_text(f'{exercise_value}{contain_text}')
11 changes: 11 additions & 0 deletions e2e_tests/test_dolos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Imports
from playwright.sync_api import Page, expect
from e2e_tests.helpers import login

# Test to check if Dolos is generating the plagiarism report
def test_dolos(page: Page) -> None:
login(page)
page.locator('td > a').first.click()
page.locator('#dolos-filter').select_option('all')
page.get_by_role('link', name='Run analysis').click()
expect(page.get_by_text('Source code plagiarism')).to_be_visible(timeout=25000)
101 changes: 101 additions & 0 deletions e2e_tests/test_radar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Imports
import re
from playwright.sync_api import Page, expect
from random import randrange
from e2e_tests.helpers import login, save_and_navigate_back, change_value

# Test login
def test_login(page: Page) -> None:
login(page)
expect(page.get_by_role('button', name='Logout')).to_be_visible()

# Test logout
def test_logout(page: Page) -> None:
login(page)
page.get_by_role('button', name='Logout').click()
expect(page.get_by_text('Username')).to_be_visible()
expect(page.get_by_text('Password')).to_be_visible()

# Test change exercise name
def test_change_exercise_name(page: Page) -> None:
exercise_num = randrange(9) + 1
login(page)
current_exercise_name = page.locator('td > a').first.inner_text()
change_value(page, 'Name', f'exercise{exercise_num}')
change_value(page, 'Name', f'{current_exercise_name}')

# Test change exercise tokenizer
def test_change_tokenizer(page: Page) -> None:
tokenizers = [
("skip", "Skip"),
("scala", "Scala"),
("python", "Python"),
("js", "JavaScript (ECMA 2016)"),
("html", "HTML5"),
("css", "CSS"),
]

login(page)
page.get_by_role('link', name=' Settings').first.click()

current_tokenizer = page.locator('#id_tokenizer').input_value()
current_tokenizer_index = [x for x, y in enumerate(tokenizers) if y[0] == current_tokenizer][0]
new_tokenizer_index = randrange(len(tokenizers))
while current_tokenizer_index == new_tokenizer_index:
new_tokenizer_index = randrange(len(tokenizers))

page.get_by_label('Tokenizer type').select_option(index=new_tokenizer_index)
save_and_navigate_back(page)
expect(page.locator('tbody')).to_contain_text(tokenizers[new_tokenizer_index][1])
page.get_by_role('link', name=' Settings').first.click()
page.get_by_label('Tokenizer type').select_option(index=current_tokenizer_index)
save_and_navigate_back(page)
expect(page.locator('tbody')).to_contain_text(tokenizers[current_tokenizer_index][1])

# Test change exercise minimum match tokens
def test_change_minimum_match_tokens(page: Page) -> None:
min_tokens = randrange(8) + 3
login(page)
current_min_tokens = int(page.locator('tr > td:nth-child(9)').first.inner_text().split(' ')[0])
change_value(page, 'Minimum match tokens', f'{min_tokens}')
change_value(page, 'Minimum match tokens', f'{current_min_tokens}', ' tokens')

# Test visibility of histogram and grid
def test_similarity_visibility(page: Page) -> None:
login(page)
page.locator('td > a').first.click()
expect(page.get_by_role('img')).to_contain_text(re.compile(r'.+0.00.10.20.30.40.50.60.70.80.91.0'))
expect(page.locator('.comparison-grid')).to_be_visible()

# Test visibility of exercise histogram
def test_histogram_visibility(page: Page) -> None:
login(page)
page.get_by_role('link', name=' Exercise histograms').click()
expect(page.get_by_role('img').first).to_contain_text(re.compile(r'.+0.00.10.20.30.40.50.60.70.80.91.0'))

# Test visibility of student view
def test_student_view(page: Page) -> None:
login(page)
page.get_by_role('link', name=' Students view').click()
page.locator('.sorting_1 > a').first.click()
expect(page.locator('.content.container-fluid > p').first).to_contain_text(
re.compile(r'All comparisons for .+ with similarity greater than .+%')
)

# Test visibility of flagged pairs
def test_flagged_pairs(page: Page) -> None:
login(page)
page.locator('td > a').first.click()
page.get_by_role('link', name=re.compile(r'.+% .+ vs .+')).first.click()
page.locator('[name="review"]').click()
page.get_by_role('link', name='Plagiate', exact=True).click()
page.locator('.breadcrumb > li:nth-child(2)').click()
page.get_by_role('link', name=' Flagged pairs').click()
page.get_by_role('link', name=re.compile(r'.+ vs .+')).click()
page.get_by_role('link', name='Get summary of marked').click()
expect(page.get_by_role('heading')).to_contain_text('Similarity Summary')
page.locator('.breadcrumb > li:nth-child(2)').click()
page.locator('td > a').first.click()
page.get_by_role('link', name=re.compile(r'.+% .+ vs .+')).first.click()
page.locator('[name="review"]').click()
page.get_by_role('link', name='Unspecified match', exact=True).click()
8 changes: 8 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[pytest]
# Run chromium without UI
addopts = --output test_results --browser chromium --browser firefox
python_files = test*.py
testpaths =
./e2e_tests

DJANGO_SETTINGS_MODULE = radar.settings
8 changes: 7 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@ git+https://github.com/apluslms/django-essentials.git@1.6.0#egg=django-essential
git+https://github.com/apluslms/a-plus-client.git@v1.3.0#egg=a_plus_client
git+https://github.com/apluslms/greedy-string-tiling.git@v0.13.0
aplus-auth ~= 0.2.3
django-debug-toolbar
setuptools ~= 68.1.2
django-debug-toolbar ~= 4.4.6
pytz ~= 2024.2
pylibmc ~= 1.6.3
pytest-playwright ~= 0.6.2
pytest-django ~= 4.9.0
pytest-env ~= 1.1.5
Loading