Skip to content

Commit

Permalink
test: add frontend-tests with pytest-playwright
Browse files Browse the repository at this point in the history
Co-authored-by: David Wallace

Fixes: #715
  • Loading branch information
afuetterer committed Nov 2, 2023
1 parent cdc70f8 commit 82923e6
Showing 1 changed file with 200 additions and 0 deletions.
200 changes: 200 additions & 0 deletions rdmo/management/tests/test_frontend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import os
import re
from dataclasses import dataclass
from urllib.parse import urlparse

import pytest

from django.core.management import call_command

from playwright.sync_api import Page, expect
from pytest_django.live_server_helper import LiveServer

from rdmo.accounts.utils import set_group_permissions
from rdmo.conditions.models import Condition
from rdmo.core.models import Model
from rdmo.domain.models import Attribute
from rdmo.options.models import Option, OptionSet
from rdmo.questions.models import Catalog, Question, Section
from rdmo.questions.models import Page as PageModel
from rdmo.questions.models.questionset import QuestionSet
from rdmo.tasks.models import Task
from rdmo.views.models import View

pytestmark = pytest.mark.e2e

# needed for playwright to run
os.environ.setdefault("DJANGO_ALLOW_ASYNC_UNSAFE", "true")


@dataclass
class ModelHelper:
"""Helper class to bundle information about models for test cases."""

url: str
model: Model
form_field: str = "URI Path"
db_field: str = "uri_path"
has_nested: bool = False

@property
def url_name(self) -> str:
"""Return the url name for the model."""
url = self.url.rstrip("s")
url = url.replace("set", " set")
return url


@pytest.fixture(scope="function")
def e2e_tests_django_db_setup(django_db_setup, django_db_blocker, fixtures):
"""Set up database and populate with fixtures, that get restored for every test case."""
with django_db_blocker.unblock():
call_command("loaddata", *fixtures)
set_group_permissions()


@pytest.fixture(scope="session")
def base_url(live_server: LiveServer) -> str:
"""Enable playwright to address URLs with base URL automatically prefixed."""
return live_server.url


@pytest.fixture
def logged_in_admin_user(e2e_tests_django_db_setup, page: Page) -> Page:
"""Log in as admin user through django login UI, returns logged in page for e2e tests."""
page.goto("/account/login")
page.get_by_label("Username").fill("admin", timeout=5000)
page.get_by_label("Password").fill("admin")
page.get_by_role("button", name="Login").click()
page.goto("/management")
yield page


def test_management_navigation(logged_in_admin_user: Page) -> None:
"""Test that each content type is available through the navigation."""
page = logged_in_admin_user
expect(page.get_by_role("heading", name="Management")).to_be_visible()
page.get_by_role("link", name="Catalogs").click()
expect(page).to_have_url(re.compile(r".*/catalogs/"))
expect(page.locator("strong").filter(has_text="Catalogs")).to_be_visible()
page.get_by_role("link", name="Sections").click()
expect(page).to_have_url(re.compile(r".*/sections/"))
page.get_by_role("link", name="Pages").click()
expect(page).to_have_url(re.compile(r".*/pages/"))
page.get_by_role("link", name="Question sets").click()
expect(page).to_have_url(re.compile(r".*/questionsets/"))
page.get_by_role("link", name="Questions", exact=True).click()
expect(page).to_have_url(re.compile(r".*/questions/"))
page.get_by_role("link", name="Attributes").click()
expect(page).to_have_url(re.compile(r".*/attributes/"))
page.get_by_role("link", name="Option sets").click()
expect(page).to_have_url(re.compile(r".*/optionsets/"))
page.get_by_role("link", name="Options", exact=True).click()
expect(page).to_have_url(re.compile(r".*/options/"))
page.get_by_role("link", name="Conditions").click()
expect(page).to_have_url(re.compile(r".*/conditions/"))
page.get_by_role("link", name="Tasks").click()
expect(page).to_have_url(re.compile(r".*/tasks/"))
page.get_by_role("link", name="Views").click()
expect(page).to_have_url(re.compile(r".*/views/"))


model_helpers = (
ModelHelper("catalogs", Catalog, has_nested=True),
ModelHelper("sections", Section, has_nested=True),
ModelHelper("pages", PageModel, has_nested=True),
ModelHelper("questionsets", QuestionSet, has_nested=True),
ModelHelper("questions", Question),
ModelHelper(
"attributes", Attribute, has_nested=True, form_field="Key", db_field="key"
),
ModelHelper("optionsets", OptionSet, has_nested=True),
ModelHelper("options", Option),
ModelHelper("conditions", Condition),
ModelHelper("tasks", Task),
ModelHelper("views", View),
)


@pytest.mark.parametrize("helper", model_helpers)
def test_management_has_items(logged_in_admin_user: Page, helper: ModelHelper) -> None:
"""Test all items in database are visible in management UI."""
page = logged_in_admin_user
num_items_in_database = helper.model.objects.count()
page.goto(f"/management/{helper.url}")
items_in_ui = page.locator(".list-group > .list-group-item")
expect(items_in_ui).to_have_count(num_items_in_database)


@pytest.mark.parametrize("helper", model_helpers)
def test_management_nested_view(
logged_in_admin_user: Page, helper: ModelHelper
) -> None:
"""For each element type, that has a nested view, click the first example."""
page = logged_in_admin_user
page.goto(f"/management/{helper.url}")
# Open nested view for element type
if helper.has_nested:
page.get_by_title(f"View {helper.url_name} nested").first.click()
expect(page.locator(".panel-default").first).to_be_visible()
expect(page.locator(".panel-default > .panel-body").first).to_be_visible()


@pytest.mark.parametrize("helper", model_helpers)
def test_management_create_model(
logged_in_admin_user: Page, helper: ModelHelper
) -> None:
"""Test management UI can create objects in the database."""
page = logged_in_admin_user
num_objects_at_start = helper.model.objects.count()
page.goto(f"/management/{helper.url}")
# click "New" button
page.get_by_role("button", name="New").click()
# fill mandatory fields
value = "some-value"
page.get_by_label(helper.form_field).fill(value)
if helper.model == Condition:
# conditions need to have a source attribute
source_form = (
page.locator(".form-group")
.filter(has_text="Source")
.locator(".select-item > .react-select")
)
source_form.click()
page.keyboard.type(Attribute.objects.first().uri)
page.keyboard.press("Enter")

# save
page.get_by_role("button", name="Create").nth(1).click()
# check if new item is in list
items_in_ui = page.locator(".list-group > .list-group-item")
expect(items_in_ui).to_have_count(num_objects_at_start + 1)

num_objects_after_save = helper.model.objects.count()
assert num_objects_after_save - num_objects_at_start == 1
query = {helper.db_field: value}
assert helper.model.objects.get(**query)


@pytest.mark.parametrize("helper", model_helpers)
def test_management_edit_model(logged_in_admin_user: Page, helper: ModelHelper) -> None:
page = logged_in_admin_user
page.goto(f"/management/{helper.url}")
# click edit
edit_button_title = f"Edit {helper.url_name}"
page.get_by_title(f"{edit_button_title}").first.click()
# edit
page.get_by_label("Comment").click()
comment = "this is a comment."
page.get_by_label("Comment").fill(comment)
# save
page.get_by_role("button", name="Save").nth(1).click()
# click on edit element again
page.get_by_title(f"{edit_button_title}").first.click()
# check the updated comment
comment_locator = page.get_by_label("Comment")
expect(comment_locator).to_have_text(comment)
# compare the updated comment with element object from db
url_id = int(urlparse(page.url).path.rstrip("/").split("/")[-1])
model_obj = helper.model.objects.get(id=url_id)
assert model_obj.comment == comment

0 comments on commit 82923e6

Please sign in to comment.