new test framework #1
31 changed files with 769 additions and 6 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -2,3 +2,6 @@ __pycache__/
|
|||
*.json
|
||||
*.zip
|
||||
TestResults/
|
||||
.vscode/
|
||||
*.pyc
|
||||
credentials*
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "envfiles"]
|
||||
path = envfiles
|
||||
url = ssh://git@git.local-it.org:2222/local-it-infrastructure/dev.local-it.cloud.git
|
||||
12
Dockerfile
Normal file
12
Dockerfile
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
FROM python:3.12-slim
|
||||
|
||||
RUN pip install --no-cache-dir pytest-playwright
|
||||
|
||||
RUN playwright install
|
||||
|
||||
RUN playwright install-deps
|
||||
|
||||
COPY ./requirements.txt ./
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
WORKDIR /code/src
|
||||
21
README.md
21
README.md
|
|
@ -1,11 +1,20 @@
|
|||
# Readme
|
||||
|
||||
# Installation
|
||||
```bash
|
||||
docker compose build
|
||||
docker compose run --rm app python ./main.py
|
||||
docker compose run --rm app pytest
|
||||
# docker-compose up
|
||||
```
|
||||
|
||||
pip install pytest-playwright
|
||||
playwright install
|
||||
Force rebuild with cache
|
||||
|
||||
# Run Tests:
|
||||
```bash
|
||||
docker-compose up --build
|
||||
```
|
||||
|
||||
pytest -k nextcloud
|
||||
Force rebuild wtihtout cache
|
||||
|
||||
playwright show-trace trace.zip
|
||||
```bash
|
||||
docker-compose build --no-cache
|
||||
```
|
||||
|
|
|
|||
6
docker-compose.yml
Normal file
6
docker-compose.yml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
services:
|
||||
app:
|
||||
build: .
|
||||
container_name: python-env
|
||||
volumes:
|
||||
- .:/code
|
||||
1
envfiles
Submodule
1
envfiles
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit a8375f6fc7a285a1000b5553be47eaf19b0be0a6
|
||||
11
previous-work/README.md
Normal file
11
previous-work/README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
# Installation
|
||||
|
||||
pip install pytest-playwright
|
||||
playwright install
|
||||
|
||||
# Run Tests:
|
||||
|
||||
pytest -k nextcloud
|
||||
|
||||
playwright show-trace trace.zip
|
||||
19
pyproject.toml
Normal file
19
pyproject.toml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
[project]
|
||||
name = "locit-testing"
|
||||
version = "0.1.0"
|
||||
requires-python = "~=3.11"
|
||||
dependencies = [
|
||||
"pytest == 7.4.3",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"ruff >= 0.1.3",
|
||||
]
|
||||
|
||||
[tool.setuptools]
|
||||
package-dir = {"" = "src"}
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 120
|
||||
target-version = "py311"
|
||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pytest
|
||||
pytest-playwright
|
||||
python-dotenv
|
||||
icecream
|
||||
71
src/blog.dev.local-it.cloud.env
Normal file
71
src/blog.dev.local-it.cloud.env
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
################################################################################
|
||||
# DO NOT EDIT THIS FILE, IT IS AUTOMATICALLY GENERATED AND WILL BE OVERWRITTEN #
|
||||
################################################################################
|
||||
|
||||
TYPE=wordpress
|
||||
TIMEOUT=300
|
||||
ENABLE_AUTO_UPDATE=true
|
||||
COMPOSE_FILE="compose.yml"
|
||||
|
||||
# Setup Wordpress settings on each deploy:
|
||||
#POST_DEPLOY_CMDS="app core_install"
|
||||
|
||||
DOMAIN=blog.dev.local-it.cloud
|
||||
## Domain aliases
|
||||
#EXTRA_DOMAINS=', `www.blog.dev.local-it.cloud`'
|
||||
LETS_ENCRYPT_ENV=production
|
||||
|
||||
TITLE="My Example Blog"
|
||||
LOCALE=de_DE
|
||||
ADMIN_EMAIL=admin@example.com
|
||||
|
||||
# Every new user is per default subscriber, uncomment to change it
|
||||
DEFAULT_USER_ROLE=administrator
|
||||
|
||||
#WORDPRESS_DEBUG=true
|
||||
|
||||
## Additional extensions
|
||||
#PHP_EXTENSIONS="calendar"
|
||||
|
||||
SECRET_DB_ROOT_PASSWORD_VERSION=v1
|
||||
SECRET_DB_PASSWORD_VERSION=v1
|
||||
|
||||
# Mostly for compatibility with existing database dumps...
|
||||
#WORDPRESS_TABLE_PREFIX=wp_
|
||||
|
||||
# Multisite
|
||||
#WORDPRESS_CONFIG_EXTRA="\
|
||||
#define('WP_CACHE', false);\
|
||||
#define('WP_ALLOW_MULTISITE', true );"
|
||||
|
||||
# Multisite phase 2 (see README)
|
||||
#WORDPRESS_CONFIG_EXTRA="define('MULTISITE', true); define('SUBDOMAIN_INSTALL', true); define('DOMAIN_CURRENT_SITE', '${DOMAIN}'); define('PATH_CURRENT_SITE', '/'); define('SITE_ID_CURRENT_SITE', 1); define('BLOG_ID_CURRENT_SITE', 1); define('FORCE_SSL_ADMIN', true ); define('COOKIE_DOMAIN', \$_SERVER['HTTP_HOST']);"
|
||||
|
||||
# Local SMTP relay
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.mailrelay.yml"
|
||||
SMTP_HOST=mail.local-it.org
|
||||
MAIL_FROM=noreply@local-it.org
|
||||
|
||||
# Remote SMTP relay
|
||||
COMPOSE_FILE="$COMPOSE_FILE:compose.smtp.yml"
|
||||
SMTP_HOST=mail.local-it.org
|
||||
MAIL_FROM=noreply@local-it.org
|
||||
SMTP_PORT=587
|
||||
SMTP_AUTH=on
|
||||
SMTP_TLS=on
|
||||
SECRET_SMTP_PASSWORD_VERSION=v1
|
||||
|
||||
# Authentik SSO
|
||||
COMPOSE_FILE="$COMPOSE_FILE:compose.authentik.yml"
|
||||
AUTHENTIK_DOMAIN=dev.local-it.cloud
|
||||
SECRET_AUTHENTIK_SECRET_VERSION=v1
|
||||
SECRET_AUTHENTIK_ID_VERSION=v1
|
||||
LOGIN_TYPE='auto'
|
||||
|
||||
# 🚩🚩 dangerous, use only for development sites!
|
||||
|
||||
# Allow remote connections to db
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.public-db.yml
|
||||
|
||||
# Wide-open CORS
|
||||
#CORS_ALLOW_ALL=1
|
||||
64
src/conftest.py
Normal file
64
src/conftest.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# regarding conftest:
|
||||
# If you have conftest.py files which do not reside in a python package directory
|
||||
# (i.e. one containing an __init__.py) then “import conftest” can be ambiguous
|
||||
# because there might be other conftest.py files as well on your PYTHONPATH or
|
||||
# sys.path. It is thus good practise for projects to either put conftest.py under
|
||||
# a package scope or to never import anything from a conftest.py file.
|
||||
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from dirmanager import DirManager
|
||||
from dotenv import dotenv_values
|
||||
|
||||
pytest_plugins = [
|
||||
"setup.setup_authentik",
|
||||
]
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption(
|
||||
"--env_file",
|
||||
action="store",
|
||||
)
|
||||
parser.addoption(
|
||||
"--tests_dir",
|
||||
action="store",
|
||||
)
|
||||
parser.addoption(
|
||||
"--session_id",
|
||||
action="store",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def dirmanager(request) -> DirManager:
|
||||
tests_dir = request.config.getoption("--tests_dir")
|
||||
tests_dir = Path(tests_dir)
|
||||
session_id = request.config.getoption("--session_id")
|
||||
return DirManager(tests_dir=tests_dir, session_id=session_id)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def dotenv_config(request) -> dict[str, str]:
|
||||
dotenv_path = request.config.getoption("--env_file")
|
||||
dotenv_path = Path(dotenv_path)
|
||||
assert dotenv_path.is_file()
|
||||
return dotenv_values(dotenv_path)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def RECORDS(dirmanager) -> Path:
|
||||
assert isinstance(dirmanager, DirManager)
|
||||
return dirmanager.dirs["records"]
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def STATES(dirmanager) -> Path:
|
||||
return dirmanager.dirs["states"]
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def RESULTS(dirmanager) -> Path:
|
||||
return dirmanager.dirs["results"]
|
||||
53
src/dirmanager.py
Normal file
53
src/dirmanager.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
from pathlib import Path
|
||||
|
||||
|
||||
class DirManager:
|
||||
"""Manages directories for the tests and should be used to create and find
|
||||
and use the correct directories.
|
||||
|
||||
The structures is as follows:
|
||||
tests dir/
|
||||
session_dir-1/
|
||||
records
|
||||
states
|
||||
results
|
||||
session_dir-2/
|
||||
records
|
||||
...
|
||||
"""
|
||||
|
||||
def __init__(self, tests_dir: Path, session_id: str):
|
||||
# root test dir
|
||||
self.tests_dir = tests_dir
|
||||
self.session_id = session_id
|
||||
|
||||
self.dirs = self._get_all_dirs()
|
||||
|
||||
def create_all_dirs(self):
|
||||
self.create_dirs(self.tests_dir, exist_ok=True)
|
||||
self.create_dirs(self.dirs)
|
||||
|
||||
def _get_all_dirs(self):
|
||||
dirs = {}
|
||||
dirs["session"] = self.tests_dir / f"test-{self.session_id}"
|
||||
dirs.update(self._get_subdirs(session_dir=dirs["session"]))
|
||||
return dirs
|
||||
|
||||
def _get_subdirs(self, session_dir: Path):
|
||||
return {
|
||||
"records": session_dir / Path("records"),
|
||||
"states": session_dir / Path("states"),
|
||||
"results": session_dir / Path("results"),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def create_dirs(dirs: Path | list[Path] | dict[str, Path], exist_ok=False):
|
||||
match dirs:
|
||||
case Path():
|
||||
dirs.mkdir(exist_ok=exist_ok)
|
||||
case list():
|
||||
for d in dirs:
|
||||
d.mkdir(exist_ok=exist_ok)
|
||||
case dict():
|
||||
for d in dirs.values():
|
||||
d.mkdir(exist_ok=exist_ok)
|
||||
76
src/main.py
Normal file
76
src/main.py
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Protocol
|
||||
|
||||
from dirmanager import DirManager
|
||||
from dotenv import dotenv_values
|
||||
from icecream import ic
|
||||
from tests_authentik.runner_authentik import RunnerAuthentik
|
||||
from tests_wordpress.runner_wordpress import RunnerWordpress
|
||||
|
||||
TESTS_DIR = Path("../tests")
|
||||
|
||||
ENV_FILES = [
|
||||
Path("../envfiles/login.test.dev.local-it.cloud.env"), # authentik
|
||||
# Path("../envfiles/blog.test.dev.local-it.cloud.env"), # wordpress
|
||||
]
|
||||
|
||||
|
||||
class TestRunner(Protocol):
|
||||
def __init__(self, dotenv_path: Path, tests_dir: Path, session_id: str):
|
||||
...
|
||||
|
||||
def run_tests(self):
|
||||
...
|
||||
|
||||
|
||||
# Register all runners here. A .env file with TYPE=authentik will be ran with RunnerAuthentik
|
||||
RUNNER_DICT: dict[str, type[TestRunner]] = {
|
||||
"authentik": RunnerAuthentik,
|
||||
"wordpress": RunnerWordpress,
|
||||
}
|
||||
|
||||
|
||||
class Wrapper:
|
||||
def __init__(self, env_files: list[Path], session_id: str):
|
||||
self.env_files = env_files
|
||||
self.check_env_files(self.env_files)
|
||||
self.session_id = session_id
|
||||
|
||||
def setup_test(self):
|
||||
self.dir_manager = DirManager(tests_dir=TESTS_DIR, session_id=self.session_id)
|
||||
self.dir_manager.create_all_dirs()
|
||||
|
||||
def run_test(self):
|
||||
self.runners: list[TestRunner] = self.load_runners(self.env_files)
|
||||
self.run_tests(self.runners)
|
||||
|
||||
def load_runners(self, env_files: list[Path]) -> list[TestRunner]:
|
||||
runners = []
|
||||
for env_file in env_files:
|
||||
config: dict[str, str] = dotenv_values(env_file)
|
||||
RunnerClass = RUNNER_DICT[config["TYPE"]]
|
||||
runners.append(RunnerClass(dotenv_path=env_file, tests_dir=TESTS_DIR, session_id=self.session_id))
|
||||
return runners
|
||||
|
||||
def run_tests(self, runners: list[TestRunner]):
|
||||
for runner in runners:
|
||||
runner.run_tests()
|
||||
|
||||
@staticmethod
|
||||
def check_env_files(env_files: list[Path]):
|
||||
"""checks if file exist for every file in list"""
|
||||
for env_file in env_files:
|
||||
assert env_file.is_file()
|
||||
|
||||
@staticmethod
|
||||
def get_session_id() -> str:
|
||||
current_datetime = datetime.now()
|
||||
return current_datetime.strftime("%Y-%m-%d-%H-%M-%S")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
session_id = Wrapper.get_session_id()
|
||||
wrapper = Wrapper(ENV_FILES, session_id=session_id)
|
||||
wrapper.setup_test()
|
||||
wrapper.run_test()
|
||||
62
src/runner.py
Normal file
62
src/runner.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
from pathlib import Path
|
||||
from typing import Callable, Optional, TypedDict
|
||||
|
||||
import pytest
|
||||
from icecream import ic
|
||||
|
||||
|
||||
class SubTest(TypedDict):
|
||||
condition: Callable[[Path], bool]
|
||||
test_file: str
|
||||
|
||||
|
||||
class Runner:
|
||||
test_dir_name: Optional[Path] = None
|
||||
main_test_name: Optional[str] = None
|
||||
sub_tests: list[SubTest] = []
|
||||
|
||||
def __init__(self, dotenv_path: Path, tests_dir: Path, session_id: str):
|
||||
self.dotenv_path = dotenv_path
|
||||
self.tests_dir = tests_dir
|
||||
self.session_id = session_id
|
||||
|
||||
ic(f"creating instance of {self.__class__.__name__}")
|
||||
assert self.test_dir_name is not None
|
||||
self.root_dir = Path(__file__).parent
|
||||
|
||||
def _run_main_test(self):
|
||||
if isinstance(self.main_test_name, str):
|
||||
full_test_path = self.root_dir / self.test_dir_name / self.main_test_name
|
||||
self._run_pytest(full_test_path)
|
||||
|
||||
def _run_pytest(self, full_test_path: Path):
|
||||
"""runs pytest programmatically
|
||||
|
||||
will run all tests in the file at full_test_path with some command line arguments"""
|
||||
|
||||
ic(f"running test: {full_test_path}")
|
||||
pytest.main(
|
||||
[
|
||||
"-v",
|
||||
"-rp",
|
||||
str(full_test_path),
|
||||
"--env_file",
|
||||
str(self.dotenv_path),
|
||||
"--tests_dir",
|
||||
str(self.tests_dir),
|
||||
"--session_id",
|
||||
self.session_id,
|
||||
]
|
||||
)
|
||||
|
||||
def show_files(self):
|
||||
ic(list(self.root_dir.glob("*")))
|
||||
|
||||
def run_tests(self):
|
||||
self._run_main_test()
|
||||
for sub_test in self.sub_tests:
|
||||
condition_function = sub_test["condition"]
|
||||
if condition_function(self.dotenv_path):
|
||||
test_name = sub_test["test_file"]
|
||||
full_test_path = self.root_dir / self.test_dir_name / test_name
|
||||
self._run_pytest(full_test_path)
|
||||
47
src/setup/setup_authentik.py
Normal file
47
src/setup/setup_authentik.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from icecream import ic
|
||||
from playwright.sync_api import Browser, Locator, expect
|
||||
|
||||
cred_file = Path("../credentials.json")
|
||||
with open(cred_file, "r") as f:
|
||||
CREDENTIALS = json.load(f)
|
||||
|
||||
print(CREDENTIALS)
|
||||
|
||||
TESTUSER = {"username": "testuser", "name": "Test User", "password": "test123", "email": "test@example.com"}
|
||||
TIMEOUT = 5000
|
||||
|
||||
|
||||
def check_for(locator: Locator):
|
||||
expect(locator).to_be_visible(timeout=TIMEOUT)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def admin_login(browser: Browser, dotenv_config, STATES):
|
||||
# ic(dotenv_config)
|
||||
|
||||
# go to page
|
||||
context = browser.new_context()
|
||||
context.set_default_timeout(TIMEOUT)
|
||||
page = context.new_page()
|
||||
url = "https://" + dotenv_config["DOMAIN"]
|
||||
page.goto(url)
|
||||
|
||||
# check welcome message
|
||||
welcome_message = dotenv_config.get("welcome_message")
|
||||
if welcome_message:
|
||||
check_for(page.get_by_text(welcome_message))
|
||||
|
||||
# login
|
||||
page.locator('input[name="uidField"]').fill(CREDENTIALS["admin"])
|
||||
page.locator('ak-stage-identification input[name="password"]').fill(CREDENTIALS["admin_pw"])
|
||||
page.get_by_role("button", name="Log In").click()
|
||||
check_for(page.locator("ak-library"))
|
||||
|
||||
# save state
|
||||
context.storage_state(path=f"{STATES}/admin_state.json")
|
||||
page.close()
|
||||
context.close()
|
||||
16
src/tests_authentik/runner_authentik.py
Normal file
16
src/tests_authentik/runner_authentik.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from pathlib import Path
|
||||
|
||||
from runner import Runner, SubTest
|
||||
|
||||
|
||||
def condition_always_true(dotenv_path: Path) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def condition_always_false(dotenv_path: Path) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
class RunnerAuthentik(Runner):
|
||||
test_dir_name = "tests_authentik"
|
||||
main_test_name = "test_authentik_dummy.py"
|
||||
2
src/tests_authentik/test_authentik_dummy.py
Normal file
2
src/tests_authentik/test_authentik_dummy.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
def test_true():
|
||||
assert 1 + 1 == 2
|
||||
179
src/tests_authentik/test_authentik_setup.py
Normal file
179
src/tests_authentik/test_authentik_setup.py
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
## this file will not be used later
|
||||
## split into
|
||||
# -> setup.setup_authentic.py
|
||||
# and
|
||||
# -> tests
|
||||
|
||||
import pytest
|
||||
from icecream import ic
|
||||
from playwright.sync_api import Browser, Locator, expect
|
||||
|
||||
# playwright = sync_playwright().start()
|
||||
# browser = playwright.chromium.launch(headless=False)
|
||||
|
||||
|
||||
testuser = {"username": "testuser", "name": "Test User", "password": "test123", "email": "test@example.com"}
|
||||
|
||||
|
||||
TIMEOUT = 5000
|
||||
|
||||
|
||||
def check_for(locator: Locator):
|
||||
expect(locator).to_be_visible(timeout=TIMEOUT)
|
||||
|
||||
|
||||
def setup_context(browser, state_file=None):
|
||||
if state_file:
|
||||
context = browser.new_context(storage_state=state_file)
|
||||
else:
|
||||
context = browser.new_context()
|
||||
context.set_default_timeout(TIMEOUT)
|
||||
return context
|
||||
|
||||
|
||||
""" Test Authentik Login and DE Locale """
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def admin_login(browser: Browser, dotenv_config, STATES):
|
||||
# ic(dotenv_config)
|
||||
CONFIG = dotenv_config
|
||||
context = setup_context(browser)
|
||||
page = context.new_page()
|
||||
url = "https://" + CONFIG["DOMAIN"]
|
||||
ic(url)
|
||||
page.goto(url)
|
||||
welcome_message = CONFIG.get("welcome_message")
|
||||
if welcome_message:
|
||||
check_for(page.get_by_text(welcome_message))
|
||||
if CONFIG["locale"] == "de":
|
||||
check_for(page.get_by_text("Benutzername oder Passwort vergessen?"))
|
||||
check_for(page.get_by_text("E-Mail or Anmeldename"))
|
||||
check_for(page.get_by_text("Passwort", exact=True))
|
||||
page.locator('input[name="uidField"]').fill(CONFIG["admin"])
|
||||
page.locator('ak-stage-identification input[name="password"]').fill(CONFIG["admin_pw"])
|
||||
page.get_by_role("button", name="Log In").click()
|
||||
check_for(page.locator("ak-library"))
|
||||
if CONFIG["locale"] == "de":
|
||||
check_for(page.get_by_text("Meine Anwendungen"))
|
||||
context.storage_state(path=f"{STATES}/admin_state.json")
|
||||
page.close()
|
||||
context.close()
|
||||
|
||||
|
||||
""" Create User """
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def init_create_user(browser: Browser, dotenv_config, STATES):
|
||||
admin_context = setup_context(browser, f"{STATES}/admin_state.json")
|
||||
admin_page = admin_context.new_page()
|
||||
invitelink = create_invite_link(admin_page, dotenv_config)
|
||||
admin_context.close()
|
||||
user_context = setup_context(browser)
|
||||
create_user(user_context, invitelink)
|
||||
user_context.close()
|
||||
|
||||
|
||||
""" Delete User """
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def post_delete_user(browser: Browser, dotenv_config, RECORDS, STATES):
|
||||
yield
|
||||
context = browser.new_context(storage_state=f"{STATES}/admin_state.json")
|
||||
context.tracing.start(screenshots=True, snapshots=True, sources=True)
|
||||
context.set_default_timeout(TIMEOUT)
|
||||
page = context.new_page()
|
||||
# delete_nextcloud_user(page)
|
||||
delete_authentik_user(page, dotenv_config)
|
||||
context.tracing.stop(path=f"{RECORDS}/delete_user.zip")
|
||||
|
||||
|
||||
""" Create Invite Link """
|
||||
|
||||
|
||||
def create_invite_link(page, dotenv_config):
|
||||
CONFIG = dotenv_config
|
||||
page.goto(CONFIG["domain"])
|
||||
page.get_by_role("link", name="Admin Interface").click()
|
||||
page.get_by_text("Verzeichnis").click()
|
||||
page.get_by_text("Benutzer").nth(2).click()
|
||||
page.get_by_text("Einladungen").click()
|
||||
page.get_by_role("button", name="Erstellen").first.click()
|
||||
page.locator('input[name="name"]').click()
|
||||
linkname = "testlink9433"
|
||||
page.locator('input[name="name"]').fill(linkname)
|
||||
page.get_by_placeholder("Wählen Sie ein Objekt aus.").click()
|
||||
page.get_by_role("option", name="invitation-enrollment-flow invitation-enrollment-flow").click()
|
||||
page.get_by_text("Erstellen", exact=True).first.click()
|
||||
linklocator = page.get_by_role("rowgroup").filter(has=page.get_by_text(linkname))
|
||||
linklocator.locator(".fa-angle-down").click()
|
||||
invitelink = linklocator.get_by_role("textbox").get_attribute(name="value")
|
||||
return invitelink
|
||||
|
||||
|
||||
""" Create User from invitelink """
|
||||
|
||||
|
||||
def create_user(context, invitelink, STATES):
|
||||
page = context.new_page()
|
||||
page.goto(invitelink)
|
||||
page.get_by_placeholder("Benutzername").click()
|
||||
page.get_by_placeholder("Benutzername").fill(testuser["username"])
|
||||
page.locator('input[name="name"]').click()
|
||||
page.locator('input[name="name"]').fill(testuser["name"])
|
||||
page.locator('input[name="email"]').click()
|
||||
page.locator('input[name="email"]').fill(testuser["email"])
|
||||
page.get_by_placeholder("Passwort", exact=True).click()
|
||||
page.get_by_placeholder("Passwort", exact=True).fill(testuser["password"])
|
||||
page.get_by_placeholder("Passwort (wiederholen)").click()
|
||||
page.get_by_placeholder("Passwort (wiederholen)").fill(testuser["password"])
|
||||
page.get_by_role("button", name="Weiter").click()
|
||||
check_for(page.locator("ak-library"))
|
||||
context.storage_state(path=f"{STATES}/user_state.json")
|
||||
|
||||
|
||||
""" Delete Authentik Account """
|
||||
|
||||
|
||||
def delete_authentik_user(page, dotenv_config):
|
||||
CONFIG = dotenv_config
|
||||
page.goto(CONFIG["domain"])
|
||||
page.get_by_role("link", name="Admin Interface").click()
|
||||
page.get_by_text("Verzeichnis").click()
|
||||
page.get_by_text("Benutzer").nth(2).click()
|
||||
page.get_by_role("row").filter(has=page.get_by_text(testuser["username"])).get_by_role("checkbox").click()
|
||||
page.get_by_role("button", name="Löschen").click()
|
||||
page.get_by_role("dialog").get_by_role("button", name="Löschen").click()
|
||||
check_for(page.get_by_text("1 Benutzer erfolgreich gelöscht"))
|
||||
|
||||
|
||||
""" Reuse Authentik Admin Session """
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def admin_session(browser: Browser, dotenv_config, STATES):
|
||||
CONFIG = dotenv_config
|
||||
context = setup_context(browser, f"{STATES}/admin_state.json")
|
||||
page = context.new_page()
|
||||
page.goto(CONFIG["domain"])
|
||||
yield context, page
|
||||
context.close()
|
||||
|
||||
|
||||
""" Reuse Authentik User Session """
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def user_session(browser: Browser, dotenv_config, STATES):
|
||||
CONFIG = dotenv_config
|
||||
context = setup_context(browser, f"{STATES}/user_state.json")
|
||||
page = context.new_page()
|
||||
page.goto(CONFIG["domain"])
|
||||
yield context, page
|
||||
context.close()
|
||||
|
||||
|
||||
def test_true():
|
||||
assert 1 + 1 == 2
|
||||
28
src/tests_wordpress/conftest.py
Normal file
28
src/tests_wordpress/conftest.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# this conftest cannot be executed directly if there is a second conftest
|
||||
# on a higher level. might work if other tests are executed though
|
||||
# -> at least bad for debugging
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import Browser, Locator, expect, sync_playwright
|
||||
|
||||
# playwright = sync_playwright().start()
|
||||
# browser = playwright.chromium.launch(headless=False)
|
||||
|
||||
|
||||
cred_file = Path("../credentials.json")
|
||||
with open(cred_file, "r") as f:
|
||||
CREDENTIALS = json.load(f)
|
||||
|
||||
print(CREDENTIALS)
|
||||
|
||||
RECORDS = Path("records")
|
||||
RECORDS.mkdir(exist_ok=True)
|
||||
STATES = Path("states")
|
||||
STATES.mkdir(exist_ok=True)
|
||||
|
||||
|
||||
def test_dummy():
|
||||
assert 1 + 1 == 2
|
||||
20
src/tests_wordpress/runner_wordpress.py
Normal file
20
src/tests_wordpress/runner_wordpress.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
from pathlib import Path
|
||||
|
||||
from runner import Runner, SubTest
|
||||
|
||||
|
||||
def condition_always_true(dotenv_path: Path) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def condition_always_false(dotenv_path: Path) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
class RunnerWordpress(Runner):
|
||||
test_dir_name = "tests_wordpress"
|
||||
# main_test_name = "test_wordpress.py"
|
||||
sub_tests = [
|
||||
SubTest(condition=condition_always_false, test_file="test_wordpress_feature1.py"),
|
||||
SubTest(condition=condition_always_true, test_file="conftest.py"),
|
||||
]
|
||||
29
src/tests_wordpress/test_wordpress.py
Normal file
29
src/tests_wordpress/test_wordpress.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import re
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
|
||||
def test_one():
|
||||
assert 1 + 1 == 2
|
||||
|
||||
|
||||
def test_two():
|
||||
assert 2 + 1 == 3
|
||||
|
||||
|
||||
def test_has_title(page: Page):
|
||||
page.goto("https://playwright.dev/")
|
||||
|
||||
# Expect a title "to contain" a substring.
|
||||
expect(page).to_have_title(re.compile("Playwright"))
|
||||
|
||||
|
||||
def test_get_started_link(page: Page):
|
||||
page.goto("https://playwright.dev/")
|
||||
|
||||
# Click the get started link.
|
||||
page.get_by_role("link", name="Get started").click()
|
||||
|
||||
# Expects page to have a heading with the name of Installation.
|
||||
expect(page.get_by_role("heading", name="Installation")).to_be_visible()
|
||||
26
src/tests_wordpress/test_wordpress_feature1.py
Normal file
26
src/tests_wordpress/test_wordpress_feature1.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import re
|
||||
|
||||
from icecream import ic
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
|
||||
def test_one(config):
|
||||
ic(config)
|
||||
assert 1 + 1 == 2
|
||||
|
||||
|
||||
def test_has_title(page: Page):
|
||||
page.goto("https://playwright.dev/")
|
||||
|
||||
# Expect a title "to contain" a substring.
|
||||
expect(page).to_have_title(re.compile("Playwright"))
|
||||
|
||||
|
||||
def test_get_started_link(page: Page):
|
||||
page.goto("https://playwright.dev/")
|
||||
|
||||
# Click the get started link.
|
||||
page.get_by_role("link", name="Get started").click()
|
||||
|
||||
# Expects page to have a heading with the name of Installation.
|
||||
expect(page.get_by_role("heading", name="Installation")).to_be_visible()
|
||||
22
src/tests_wordpress/test_wordpress_localization.py
Normal file
22
src/tests_wordpress/test_wordpress_localization.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# WIP localization
|
||||
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
|
||||
def test_has_title(page: Page):
|
||||
page.goto("https://playwright.dev/")
|
||||
|
||||
# Expect a title "to contain" a substring.
|
||||
expect(page).to_have_title(re.compile("Playwright"))
|
||||
|
||||
|
||||
def test_wordpress(admin_session):
|
||||
context, page = admin_session
|
||||
with page.expect_popup() as info:
|
||||
page.get_by_role("link", name="Wordpress").click()
|
||||
|
||||
wordpress = info.value
|
||||
check_for(wordpress.locator("#wpcontent"))
|
||||
if CONFIG["locale"] == "de":
|
||||
check_for(wordpress.get_by_role("heading", name="Willkommen bei WordPress!"))
|
||||
context.tracing.stop(path=f"{RECORDS}/wordpress.zip")
|
||||
Loading…
Add table
Add a link
Reference in a new issue