Skip to content

Commit

Permalink
test: add test_shows_validation_errors E2E test
Browse files Browse the repository at this point in the history
Ref: #119
  • Loading branch information
tumidi committed Oct 31, 2024
1 parent b663ba6 commit 29912da
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 27 deletions.
29 changes: 28 additions & 1 deletion tests/e2e/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@

import asyncio
import threading
from collections.abc import Iterator
from collections.abc import Callable, Iterator
from pathlib import Path
from typing import Any, TypeVar, cast

import pytest
from selenium import webdriver

from questionpy import Attempt, NeedsManualScoringError, Question
from questionpy_common.environment import PackageInitFunction
from questionpy_common.manifest import Manifest
from questionpy_sdk.webserver.app import WebServer
from questionpy_server.worker.runtime.package_location import FunctionPackageLocation


@pytest.fixture
Expand Down Expand Up @@ -41,3 +46,25 @@ def _start_runner_thread(sdk_web_server: WebServer) -> None:
app_thread = threading.Thread(target=start_runner, args=(sdk_web_server,))
app_thread.daemon = True # Set the thread as a daemon to automatically stop when main thread exits
app_thread.start()


_C = TypeVar("_C", bound=Callable)


def use_package(init_fun: PackageInitFunction, manifest: Manifest | None = None) -> Callable[[_C], _C]:
def decorator(wrapped: _C) -> _C:
cast(Any, wrapped).qpy_package_location = FunctionPackageLocation.from_function(init_fun, manifest)
return wrapped

return decorator


class _NoopAttempt(Attempt):
def _compute_score(self) -> float:
raise NeedsManualScoringError

formulation = ""


class _NoopQuestion(Question):
attempt_class = _NoopAttempt
61 changes: 61 additions & 0 deletions tests/e2e/test_options_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.ui import WebDriverWait

from questionpy import Package, QuestionTypeWrapper
from questionpy.form import FormModel, checkbox, group, repeat, section
from questionpy_common.api.qtype import QuestionTypeInterface

from .conftest import _NoopQuestion, use_package


def package_init(package: Package) -> QuestionTypeInterface:
class Group(FormModel):
checkbox: bool = checkbox("Checkbox", required=True)

class Repetition(FormModel):
group: Group = group("Group", Group)

class PackageForm(FormModel):
checkbox: bool = checkbox("Checkbox", required=True)
repetition: list[Repetition] = repeat(Repetition)
group: Group = group("Group", Group)
section: Group = section("Section", Group)

class PackageQuestion(_NoopQuestion):
options: PackageForm

return QuestionTypeWrapper(PackageQuestion, package)


def assert_hidden(driver: webdriver.Chrome, selector: str) -> None:
assert not driver.find_element(By.CSS_SELECTOR, f"{selector} .errors").is_displayed()


def await_and_assert_msg(driver: webdriver.Chrome, selector: str) -> None:
msg_selector = f"{selector} .errors"
el_error = WebDriverWait(driver, 5).until(ec.visibility_of_element_located((By.CSS_SELECTOR, msg_selector)))
assert el_error.text == "Field required"


@pytest.mark.usefixtures("_start_runner_thread")
@use_package(package_init)
def test_shows_validation_errors(driver: webdriver.Chrome, url: str) -> None:
driver.get(url)

# Assert error messages are hidden.
assert_hidden(driver, "#general_checkbox")
assert_hidden(driver, "#general_repetition #general_repetition_1_group")
assert_hidden(driver, "#general_group #general_group_checkbox")
assert_hidden(driver, "#section #section_checkbox")

# Submit form.
driver.find_element(By.ID, "submit-options-button").click()

# Assert error messages are shown.
await_and_assert_msg(driver, "#general_checkbox")
await_and_assert_msg(driver, "#general_repetition #general_repetition_1_group")
await_and_assert_msg(driver, "#general_group #general_group_checkbox")
await_and_assert_msg(driver, "#section #section_checkbox")
28 changes: 2 additions & 26 deletions tests/e2e/test_e2e.py → tests/e2e/test_templates.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,19 @@
# This file is part of the QuestionPy SDK. (https://questionpy.org)
# The QuestionPy SDK is free software released under terms of the MIT license. See LICENSE.md.
# (c) Technische Universität Berlin, innoCampus <[email protected]>
from collections.abc import Callable
from typing import Any, TypeVar, cast

import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait

from questionpy import Attempt, NeedsManualScoringError, Package, Question, QuestionTypeWrapper
from questionpy import Package, QuestionTypeWrapper
from questionpy.form import FormModel, checkbox, repeat
from questionpy_common.api.qtype import QuestionTypeInterface
from questionpy_common.environment import PackageInitFunction
from questionpy_common.manifest import Manifest
from questionpy_server.worker.runtime.package_location import FunctionPackageLocation


class _NoopAttempt(Attempt):
def _compute_score(self) -> float:
raise NeedsManualScoringError

formulation = ""


class _NoopQuestion(Question):
attempt_class = _NoopAttempt
from .conftest import _NoopQuestion, use_package


def package_1_init(package: Package) -> QuestionTypeInterface:
Expand Down Expand Up @@ -62,17 +49,6 @@ class Package3Question(_NoopQuestion):
return QuestionTypeWrapper(Package3Question, package)


_C = TypeVar("_C", bound=Callable)


def use_package(init_fun: PackageInitFunction, manifest: Manifest | None = None) -> Callable[[_C], _C]:
def decorator(wrapped: _C) -> _C:
cast(Any, wrapped).qpy_package_location = FunctionPackageLocation.from_function(init_fun, manifest)
return wrapped

return decorator


@pytest.mark.usefixtures("_start_runner_thread")
class TestTemplates:
@use_package(package_1_init)
Expand Down

0 comments on commit 29912da

Please sign in to comment.