Skip to content
Merged
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
8 changes: 8 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,11 @@ def force_login_user(username):
pass

return force_login_user


@pytest.fixture
def delete_all_objects():
def delete_all(*models):
for model in models:
model.objects.all().delete()
return delete_all
22 changes: 4 additions & 18 deletions rdmo/core/tests/e2e/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
import pytest

from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.management import call_command
from django.test import Client

from playwright.sync_api import Browser, BrowserContext, Page

Expand All @@ -32,20 +30,11 @@ def django_db_setup(django_db_setup, django_db_blocker, fixtures):


@pytest.fixture
def authenticated_client(db, e2e_username) -> Client:
"""An authenticated test client, used to bypass the login page."""
user = get_user_model().objects.get(username=e2e_username)
client = Client()
client.user = user # attach user to client to access in other fixtures
client.force_login(user)
return client


@pytest.fixture
def authenticated_context(live_server, browser: Browser, authenticated_client) -> BrowserContext:
def authenticated_context(client, login, live_server, browser: Browser, e2e_username) -> BrowserContext:
"""Creates an authenticated Playwright browser context with session cookies."""
# retrieve the session cookie from the authenticated client
session_cookie = authenticated_client.cookies[settings.SESSION_COOKIE_NAME]
login(e2e_username)
session_cookie = client.cookies[settings.SESSION_COOKIE_NAME]
cookie = {
"name": session_cookie.key,
"value": session_cookie.value,
Expand All @@ -60,16 +49,13 @@ def authenticated_context(live_server, browser: Browser, authenticated_client) -


@pytest.fixture
def authenticated_page(authenticated_context: BrowserContext, authenticated_client) -> Page:
def authenticated_page(authenticated_context: BrowserContext) -> Page:
"""Provides an authenticated Playwright page without forcing navigation.
The page is authenticated with session cookies from authenticated_context.
"""
page = authenticated_context.new_page()
page.set_default_timeout(PLAYWRIGHT_TIMEOUT)
page.set_default_navigation_timeout(PLAYWRIGHT_TIMEOUT)

# Attach the authenticated user to the page
page.user = authenticated_client.user
yield page
page.close()

Expand Down
19 changes: 13 additions & 6 deletions rdmo/management/tests/e2e/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,33 @@
from rdmo.core.tests.e2e.conftest import ( # noqa: F401
PLAYWRIGHT_TIMEOUT,
_set_django_allow_async_unsafe,
authenticated_client,
authenticated_context,
authenticated_page,
django_db_setup,
fail_on_js_error,
)

USERNAME = "editor" # the user needs exist in the database


@pytest.fixture
def e2e_username(scope="session") -> str:
"""Fixture to specify which user should be authenticated.
This can be overridden in individual test modules or fixtures.
"""
return USERNAME
return "editor"


@pytest.fixture
def page(live_server, browser, authenticated_page: Page) -> Page: # noqa: F811
def page(django_db_setup, live_server, browser, authenticated_page: Page) -> Page: # noqa: F811
"""Navigates the authenticated page to /management."""
authenticated_page.goto("/management") # Navigate to the projects section
authenticated_page.wait_for_load_state("networkidle")
authenticated_page.wait_for_load_state() # maybe not needed
return authenticated_page


@pytest.fixture
def page_multisite(django_db_setup, live_server, browser, authenticated_page: Page, settings) -> Page: # noqa: F811
"""Enables the multisite and navigates the authenticated page to /management."""
settings.MULTISITE = True
authenticated_page.goto("/management") # Navigate to the projects section
authenticated_page.wait_for_load_state() # maybe not needed
return authenticated_page
7 changes: 4 additions & 3 deletions rdmo/management/tests/e2e/test_frontend_import_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from playwright.sync_api import Page, expect

from rdmo.management.tests.helpers_import_elements import IMPORT_ELEMENT_PANELS_LOCATOR
from rdmo.management.tests.helpers_models import delete_all_objects
from rdmo.options.models import Option, OptionSet

pytestmark = pytest.mark.e2e
Expand All @@ -17,12 +16,14 @@
IMPORT_FILTER_LABEL_TEXT = 'Show only new and changed elements (%s)'


def test_import_and_update_optionsets_in_management(page: Page) -> None:
def test_import_and_update_optionsets_in_management(db, page: Page, delete_all_objects) -> None:
"""Test that each content type is available through the navigation."""
delete_all_objects([OptionSet, Option])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not just OptionSet.objects.delete() and Option.objects.delete()?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe a fixture, but still delete_all_objects([OptionSet, Option]) or better delete_all(OptionSet, Option) (using *args in the fixture).


expect(page.get_by_role("heading", name="Management")).to_be_visible()
expect(page.locator("strong").filter(has_text="Catalogs")).to_be_visible()
# delete the OptionSet, Option objects
delete_all_objects(OptionSet, Option)

## 1. Import fresh optionset.xml
# choose the file to be imported
page.locator('input[name="uploaded_file"]').set_input_files(import_xml)
Expand Down
7 changes: 3 additions & 4 deletions rdmo/management/tests/e2e/test_frontend_import_questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@
from playwright.sync_api import expect

from rdmo.management.tests.helpers_import_elements import IMPORT_ELEMENT_PANELS_LOCATOR_SHOWN
from rdmo.management.tests.helpers_models import delete_all_objects
from rdmo.questions.models import Catalog, Question, Section
from rdmo.questions.models import Page as PageModel
from rdmo.questions.models.questionset import QuestionSet

pytestmark = pytest.mark.e2e


def test_import_catalogs_in_management(page) -> None:
def test_import_catalogs_in_management(db, page, delete_all_objects) -> None:
"""Test that the catalogs.xml can be imported correctly."""

delete_all_objects([Catalog, Section, PageModel, QuestionSet, Question])

expect(page.get_by_role("heading", name="Management")).to_be_visible()
expect(page.locator("strong").filter(has_text="Catalogs")).to_be_visible()
# all Catalog related objects are deleted
delete_all_objects(Catalog, Section, PageModel, QuestionSet, Question)
# choose the file to be imported
page.locator('input[name="uploaded_file"]').set_input_files("./testing/xml/elements/catalogs.xml")
# click the import form submit button, this will take some time
Expand Down
34 changes: 34 additions & 0 deletions rdmo/management/tests/e2e/test_frontend_management_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,37 @@ def test_management_edit_model(page: Page, helper: ModelHelper) -> None:
url_id = int(urlparse(page.url).path.rstrip("/").split("/")[-1])
model_obj = helper.model.objects.get(id=url_id)
assert model_obj.comment == comment


def test_management_navigation_filters(page_multisite: Page) -> None:
"""Test that each content type is available through the navigation."""
page = page_multisite

expect(page.get_by_role("heading", name="Management")).to_be_visible()

# add search to filter
page.get_by_role("textbox", name="Filter catalogs").click()
page.get_by_role("textbox", name="Filter catalogs").fill("bar")

# filter URI prefix
page.get_by_label("Filter URI prefix").select_option("https://bar.com/terms")

# filter sites and editors, requires MULTISITE to be enabled
page.get_by_label("Filter sites").select_option("3")
page.get_by_label("Filter editors").select_option("3")

# assert bar-catalog
expect(page.get_by_text("bar-catalog", exact=True)).to_be_visible()

# reload
page.reload()

# reset all filters
page.get_by_label("Filter URI prefix").select_option("")
page.get_by_label("Filter sites").select_option("")
page.get_by_label("Filter editors").select_option("")
page.get_by_role("button", name="Reset").click()

# reload
page.reload()
expect(page.get_by_role("link", name="http://example.com/terms/questions/catalog", exact=True)).to_be_visible()
5 changes: 0 additions & 5 deletions rdmo/management/tests/helpers_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,3 @@ def verbose_name_plural(self) -> str:
ModelHelper(Task),
ModelHelper(View),
)


def delete_all_objects(db_models: list):
for db_model in db_models:
db_model.objects.all().delete()
46 changes: 24 additions & 22 deletions rdmo/management/tests/test_import_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
get_changed_elements,
parse_xml_and_import_elements,
)
from .helpers_models import delete_all_objects
from .helpers_xml import read_xml_and_parse_to_root_and_elements

fields_to_be_changed = (('comment',),)
Expand Down Expand Up @@ -51,8 +50,8 @@
]


def test_create_optionsets(db, settings):
delete_all_objects([OptionSet, Option])
def test_create_optionsets(db, settings, delete_all_objects):
delete_all_objects(OptionSet, Option)

xml_file = Path(settings.BASE_DIR) / 'xml' / 'elements' / 'optionsets.xml'
elements, root, imported_elements = parse_xml_and_import_elements(xml_file)
Expand All @@ -71,10 +70,11 @@ def test_create_optionsets(db, settings):
'option_optionsets__order').values_list('uri',flat=True)
assert options_uris == list(db_ordered_options_uris)

def test_update_optionsets(db, settings):
xml_file = Path(settings.BASE_DIR) / 'xml' / 'elements' / 'optionsets.xml'
def test_update_optionsets(db, settings, delete_all_objects):
delete_all_objects(OptionSet, Option)

# Arrange, import the optionsets.xml
delete_all_objects([OptionSet, Option])
xml_file = Path(settings.BASE_DIR) / 'xml' / 'elements' / 'optionsets.xml'
elements, root, imported_elements = parse_xml_and_import_elements(xml_file)
assert OptionSet.objects.count() == 4
assert Option.objects.count() == 9
Expand All @@ -90,8 +90,8 @@ def test_update_optionsets(db, settings):


@pytest.mark.parametrize('updated_fields', fields_to_be_changed)
def test_update_optionsets_with_changed_fields(db, settings, updated_fields):
delete_all_objects([OptionSet, Option])
def test_update_optionsets_with_changed_fields(db, settings, updated_fields, delete_all_objects):
delete_all_objects(OptionSet, Option)

xml_file = Path(settings.BASE_DIR) / 'xml' / 'elements' / 'optionsets.xml'
elements, root, imported_elements = parse_xml_and_import_elements(xml_file)
Expand All @@ -112,10 +112,11 @@ def test_update_optionsets_with_changed_fields(db, settings, updated_fields):
assert test[ImportElementFields.DIFF] == imported[ImportElementFields.DIFF]


def test_update_optionsets_from_changed_xml(db, settings):
def test_update_optionsets_from_changed_xml(db, settings, delete_all_objects):
# Arrange, start test with fresh options in db
delete_all_objects(OptionSet, Option)

# Arrange, import the optionsets.xml
delete_all_objects([OptionSet, Option])
xml_file = Path(settings.BASE_DIR) / 'xml' / 'elements' / 'optionsets.xml'
parse_xml_and_import_elements(xml_file)
assert OptionSet.objects.count() + Option.objects.count() == 13
Expand Down Expand Up @@ -158,9 +159,9 @@ def test_update_optionsets_from_changed_xml(db, settings):
assert len([i for i in imported_elements_2 if i[ImportElementFields.WARNINGS]]) == 2


def test_create_options(db, settings):
# Arrange
Option.objects.all().delete()
def test_create_options(db, settings, delete_all_objects):
delete_all_objects(Option)

# Act
xml_file = Path(settings.BASE_DIR) / 'xml' / 'elements' / 'options.xml'
elements, root, imported_elements = parse_xml_and_import_elements(xml_file)
Expand All @@ -170,9 +171,9 @@ def test_create_options(db, settings):
assert all(element[ImportElementFields.UPDATED] is False for element in imported_elements)


def test_update_options(db, settings):
# Arrange
Option.objects.all().delete()
def test_update_options(db, settings, delete_all_objects):
delete_all_objects(Option)

xml_file = Path(settings.BASE_DIR) / 'xml' / 'elements' / 'options.xml'
parse_xml_and_import_elements(xml_file)
assert Option.objects.count() == 9
Expand All @@ -186,8 +187,8 @@ def test_update_options(db, settings):


@pytest.mark.parametrize('updated_fields', fields_to_be_changed)
def test_update_options_with_changed_fields(db, settings, updated_fields):
delete_all_objects([OptionSet, Option])
def test_update_options_with_changed_fields(db, settings, updated_fields, delete_all_objects):
delete_all_objects(OptionSet, Option)

xml_file = Path(settings.BASE_DIR) / 'xml' / 'elements' / 'options.xml'
elements, root, imported_elements = parse_xml_and_import_elements(xml_file)
Expand All @@ -206,8 +207,8 @@ def test_update_options_with_changed_fields(db, settings, updated_fields):
assert test[ImportElementFields.DIFF] == imported[ImportElementFields.DIFF]


def test_create_legacy_options(db, settings):
delete_all_objects([OptionSet, Option])
def test_create_legacy_options(db, settings, delete_all_objects):
delete_all_objects(OptionSet, Option)

xml_file = Path(settings.BASE_DIR) / 'xml' / 'elements' / 'legacy' / 'options.xml'

Expand All @@ -230,8 +231,9 @@ def test_create_legacy_options(db, settings):
assert options_uris == list(db_ordered_options_uris)


def test_update_legacy_options(db, settings):
delete_all_objects([OptionSet, Option])
def test_update_legacy_options(db, settings, delete_all_objects):
delete_all_objects(OptionSet, Option)

xml_file = Path(settings.BASE_DIR) / 'xml' / 'elements' / 'legacy' / 'options.xml'
parse_xml_and_import_elements(xml_file)
assert OptionSet.objects.count() == 4
Expand Down
Loading