Skip to content

Commit

Permalink
Merge pull request #63 from Harman-Aalto/Create-Test
Browse files Browse the repository at this point in the history
Create Playwright E2E tests
  • Loading branch information
mikaelGusse authored Jan 13, 2025
2 parents b1c8040 + 81cf464 commit 9e56f8c
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 4 deletions.
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
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: `[email protected]`<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/[email protected]#egg=django-essential
git+https://github.com/apluslms/[email protected]#egg=a_plus_client
git+https://github.com/apluslms/[email protected]
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

0 comments on commit 9e56f8c

Please sign in to comment.