From b84bd808425a4b789cd4a2de47b292ebe147a7d6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 15:19:33 +0100 Subject: [PATCH 01/47] move previous work --- .gitignore | 1 + README.md => previous-work/README.md | 0 authentik_test.py => previous-work/authentik_test.py | 0 config.yaml => previous-work/config.yaml | 0 conftest.py => previous-work/conftest.py | 0 nextcloud_test.py => previous-work/nextcloud_test.py | 0 pytest.ini => previous-work/pytest.ini | 0 vikunja_test.py => previous-work/vikunja_test.py | 0 wekan_test.py => previous-work/wekan_test.py | 0 wordpress_test.py => previous-work/wordpress_test.py | 0 10 files changed, 1 insertion(+) rename README.md => previous-work/README.md (100%) rename authentik_test.py => previous-work/authentik_test.py (100%) rename config.yaml => previous-work/config.yaml (100%) rename conftest.py => previous-work/conftest.py (100%) rename nextcloud_test.py => previous-work/nextcloud_test.py (100%) rename pytest.ini => previous-work/pytest.ini (100%) rename vikunja_test.py => previous-work/vikunja_test.py (100%) rename wekan_test.py => previous-work/wekan_test.py (100%) rename wordpress_test.py => previous-work/wordpress_test.py (100%) diff --git a/.gitignore b/.gitignore index 47caf4d..a94067b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ __pycache__/ *.json *.zip TestResults/ +.vscode/ \ No newline at end of file diff --git a/README.md b/previous-work/README.md similarity index 100% rename from README.md rename to previous-work/README.md diff --git a/authentik_test.py b/previous-work/authentik_test.py similarity index 100% rename from authentik_test.py rename to previous-work/authentik_test.py diff --git a/config.yaml b/previous-work/config.yaml similarity index 100% rename from config.yaml rename to previous-work/config.yaml diff --git a/conftest.py b/previous-work/conftest.py similarity index 100% rename from conftest.py rename to previous-work/conftest.py diff --git a/nextcloud_test.py b/previous-work/nextcloud_test.py similarity index 100% rename from nextcloud_test.py rename to previous-work/nextcloud_test.py diff --git a/pytest.ini b/previous-work/pytest.ini similarity index 100% rename from pytest.ini rename to previous-work/pytest.ini diff --git a/vikunja_test.py b/previous-work/vikunja_test.py similarity index 100% rename from vikunja_test.py rename to previous-work/vikunja_test.py diff --git a/wekan_test.py b/previous-work/wekan_test.py similarity index 100% rename from wekan_test.py rename to previous-work/wekan_test.py diff --git a/wordpress_test.py b/previous-work/wordpress_test.py similarity index 100% rename from wordpress_test.py rename to previous-work/wordpress_test.py -- 2.47.2 From 9411fe61ee230dce8142e4487c0ee1207430bcfa Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 15:20:43 +0100 Subject: [PATCH 02/47] initial commit --- .gitignore | 3 +- Dockerfile | 10 +++ README.md | 18 +++++ docker-compose.yml | 6 ++ requirements.txt | 3 + src/blog.dev.local-it.cloud.env | 71 +++++++++++++++++++ src/test_wrapper.py | 42 +++++++++++ src/tests_wordpress/conftest.py | 20 ++++++ src/tests_wordpress/test_wordpress.py | 29 ++++++++ .../test_wordpress_feature1.py | 26 +++++++ src/tests_wordpress/testrunner_wordpress.py | 45 ++++++++++++ 11 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 docker-compose.yml create mode 100644 requirements.txt create mode 100644 src/blog.dev.local-it.cloud.env create mode 100644 src/test_wrapper.py create mode 100644 src/tests_wordpress/conftest.py create mode 100644 src/tests_wordpress/test_wordpress.py create mode 100644 src/tests_wordpress/test_wordpress_feature1.py create mode 100644 src/tests_wordpress/testrunner_wordpress.py diff --git a/.gitignore b/.gitignore index a94067b..0a48c23 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ __pycache__/ *.json *.zip TestResults/ -.vscode/ \ No newline at end of file +.vscode/ +*.pyc \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..67103e7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.12-slim + +WORKDIR /code + +COPY ./requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +RUN playwright install + +RUN playwright install-deps diff --git a/README.md b/README.md new file mode 100644 index 0000000..7094ef1 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# Readme + +```bash +docker compose run --rm app pytest +# docker-compose up +``` + +Force rebuild with cache + +```bash +docker-compose up --build +``` + +Force rebuild wtihtout cache + +```bash +docker-compose build --no-cache +``` diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5da046e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,6 @@ +services: + app: + build: . + container_name: python-env + volumes: + - .:/code diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c10699b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +pytest +pytest-playwright +python-dotenv \ No newline at end of file diff --git a/src/blog.dev.local-it.cloud.env b/src/blog.dev.local-it.cloud.env new file mode 100644 index 0000000..600aaf6 --- /dev/null +++ b/src/blog.dev.local-it.cloud.env @@ -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 diff --git a/src/test_wrapper.py b/src/test_wrapper.py new file mode 100644 index 0000000..74afda3 --- /dev/null +++ b/src/test_wrapper.py @@ -0,0 +1,42 @@ +from pathlib import Path +from typing import Protocol + +from dotenv import dotenv_values +from icecream import ic +from tests_wordpress.testrunner_wordpress import TestRunnerWordpress + + +class TestRunner(Protocol): + def run_tests(): + pass + + +WRAPPER_DICT: dict[str, type[TestRunner]] = {"wordpress": TestRunnerWordpress} + + +class TestWrapper: + def __init__(self, dotenv_path: Path): + self.dotenv_path = dotenv_path + self.root_dir = self.dotenv_path.parent + self.show_files() + self.load_dotenv() + self.load_test_wrapper() + + def show_files(self): + ic(list(self.root_dir.glob("*"))) + + def load_dotenv(self): + assert self.dotenv_path.is_file() + self.config = dotenv_values(self.dotenv_path) + + def load_test_wrapper(self): + config_type = self.config["TYPE"] + ic(config_type) + WrapperClass = WRAPPER_DICT[config_type] + wrapper = WrapperClass(self.dotenv_path) + wrapper.run_tests() + + +if __name__ == "__main__": + dotenv_path = Path(__file__).parent / "./blog.dev.local-it.cloud.env" + t = TestWrapper(dotenv_path) diff --git a/src/tests_wordpress/conftest.py b/src/tests_wordpress/conftest.py new file mode 100644 index 0000000..9d0a65a --- /dev/null +++ b/src/tests_wordpress/conftest.py @@ -0,0 +1,20 @@ +from pathlib import Path + +import pytest +from dotenv import dotenv_values + + +def pytest_addoption(parser): + parser.addoption( + "--env_file_path", + action="store", + ) + + +@pytest.fixture +def config(request): + dotenv_path = request.config.getoption("--env_file_path") + dotenv_path = Path(dotenv_path) + assert dotenv_path.is_file() + config = dotenv_values(dotenv_path) + return config diff --git a/src/tests_wordpress/test_wordpress.py b/src/tests_wordpress/test_wordpress.py new file mode 100644 index 0000000..5d5b407 --- /dev/null +++ b/src/tests_wordpress/test_wordpress.py @@ -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() diff --git a/src/tests_wordpress/test_wordpress_feature1.py b/src/tests_wordpress/test_wordpress_feature1.py new file mode 100644 index 0000000..865fd93 --- /dev/null +++ b/src/tests_wordpress/test_wordpress_feature1.py @@ -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() diff --git a/src/tests_wordpress/testrunner_wordpress.py b/src/tests_wordpress/testrunner_wordpress.py new file mode 100644 index 0000000..d375588 --- /dev/null +++ b/src/tests_wordpress/testrunner_wordpress.py @@ -0,0 +1,45 @@ +from pathlib import Path + +import pytest +from dotenv import dotenv_values +from icecream import ic + + +class TestRunnerWordpress: + def __init__(self, dotenv_path: Path): + self.dotenv_path = dotenv_path + self.show_files() + self.load_dotenv() + # self.run_pytest_dir_wp() + # self.run_pytest_single_wp() + self.run_pytest_single_wp_feature1() + + def show_files(self): + ic(list(self.root_dir.glob("*"))) + + def load_dotenv(self): + dotenv_path = self.root_dir / "./blog.dev.local-it.cloud.env" + assert dotenv_path.is_file() + config = dotenv_values(dotenv_path) + ic(config) + + def run_pytest_dir_wp(self): + pytest.main([self.root_dir / "tests_wordpress"]) + + def run_pytest_single_wp(self): + pytest.main( + [ + self.root_dir / "tests_wordpress" / "test_wordpress.py", + "--env_file_path", + self.dotenv_path, + ] + ) + + def run_pytest_single_wp_feature1(self): + pytest.main( + [ + self.root_dir / "tests_wordpress" / "test_wordpress_feature1.py", + "--env_file_path", + self.dotenv_path, + ] + ) -- 2.47.2 From 8a252df91fd8af2e6f150992df9edbd1e9d73e67 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 21:00:14 +0100 Subject: [PATCH 03/47] change install order --- Dockerfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 67103e7..3f2f854 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,9 +2,11 @@ FROM python:3.12-slim WORKDIR /code -COPY ./requirements.txt ./ -RUN pip install --no-cache-dir -r requirements.txt +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 -- 2.47.2 From 2cd2c3c9c5e77b3622223a1a4691eaa2b2603d2f Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 21:00:45 +0100 Subject: [PATCH 04/47] add bash commands --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7094ef1..6856534 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Readme ```bash +docker compose build +docker compose run --rm app python ./src/ docker compose run --rm app pytest # docker-compose up ``` -- 2.47.2 From 85200a632257e60945c3d5db02b98c0645c75cf3 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 21:33:53 +0100 Subject: [PATCH 05/47] add icecream to requirements for debugging --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c10699b..66c69c3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ pytest pytest-playwright -python-dotenv \ No newline at end of file +python-dotenv +icecream \ No newline at end of file -- 2.47.2 From 2d9ca21fec95c430dbd548b9a561ca453e87f829 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 21:34:06 +0100 Subject: [PATCH 06/47] add Runner class, test infrastructure --- src/test_wrapper.py | 10 +-- src/tests_wordpress/runner_wordpress.py | 87 +++++++++++++++++++++ src/tests_wordpress/testrunner_wordpress.py | 45 ----------- 3 files changed, 92 insertions(+), 50 deletions(-) create mode 100644 src/tests_wordpress/runner_wordpress.py delete mode 100644 src/tests_wordpress/testrunner_wordpress.py diff --git a/src/test_wrapper.py b/src/test_wrapper.py index 74afda3..aa713c5 100644 --- a/src/test_wrapper.py +++ b/src/test_wrapper.py @@ -3,7 +3,7 @@ from typing import Protocol from dotenv import dotenv_values from icecream import ic -from tests_wordpress.testrunner_wordpress import TestRunnerWordpress +from tests_wordpress.runner_wordpress import RunnerWordpress class TestRunner(Protocol): @@ -11,7 +11,7 @@ class TestRunner(Protocol): pass -WRAPPER_DICT: dict[str, type[TestRunner]] = {"wordpress": TestRunnerWordpress} +RUNNER_DICT: dict[str, type[TestRunner]] = {"wordpress": RunnerWordpress} class TestWrapper: @@ -32,9 +32,9 @@ class TestWrapper: def load_test_wrapper(self): config_type = self.config["TYPE"] ic(config_type) - WrapperClass = WRAPPER_DICT[config_type] - wrapper = WrapperClass(self.dotenv_path) - wrapper.run_tests() + RunnerClass = RUNNER_DICT[config_type] + runner = RunnerClass(self.dotenv_path) + runner.run_tests() if __name__ == "__main__": diff --git a/src/tests_wordpress/runner_wordpress.py b/src/tests_wordpress/runner_wordpress.py new file mode 100644 index 0000000..13b0f00 --- /dev/null +++ b/src/tests_wordpress/runner_wordpress.py @@ -0,0 +1,87 @@ +from pathlib import Path +from typing import Callable, Optional, TypedDict + +import pytest +from dotenv import dotenv_values +from icecream import ic + + +class SubTest(TypedDict): + condition: Callable[[Path], bool] + test_file: str + + +def condition_feature1(dotenv_path: Path) -> bool: + """returns true if feature 1 should be executed based on the dotenv file given as a Path""" + + +def condition_always_true(dotenv_path: Path) -> bool: + return True + + +class Runner: + main_test_name: Optional[str] = None + sub_tests: list[SubTest] = [] + + def __init__(self, dotenv_path: Path): + self.dotenv_path = dotenv_path + self.root_dir = self.dotenv_path.parent + self.show_files() + self.load_dotenv() + + def _run_main_test(self): + if isinstance(self.main_test_name, str): + self._run_pytest(self.main_test_name) + + def _run_pytest(self, test_name: str): + pytest.main( + [ + self.root_dir / "tests_wordpress" / test_name, + "--env_file_path", + self.dotenv_path, + ] + ) + + def run_tests(self): + self._run_main_test() + for c in self.sub_tests: + condition_function = c["condition"] + if condition_function(): + test_name = c["test_file"] + self._run_pytest(test_name) + + +class RunnerWordpress(Runner): + main_test_name = "test_wordpress.py" + sub_tests = [ + SubTest(condition=condition_always_true, test_file="test_wordpress_feature1.py") + ] + + def show_files(self): + ic(list(self.root_dir.glob("*"))) + + def load_dotenv(self): + assert self.dotenv_path.is_file() + config = dotenv_values(self.dotenv_path) + ic(config) + + def run_pytest_dir_wp(self): + pytest.main([self.root_dir / "tests_wordpress"]) + + def run_pytest_single_wp(self): + pytest.main( + [ + self.root_dir / "tests_wordpress" / "test_wordpress.py", + "--env_file_path", + self.dotenv_path, + ] + ) + + def run_pytest_single_wp_feature1(self): + pytest.main( + [ + self.root_dir / "tests_wordpress" / "test_wordpress_feature1.py", + "--env_file_path", + self.dotenv_path, + ] + ) diff --git a/src/tests_wordpress/testrunner_wordpress.py b/src/tests_wordpress/testrunner_wordpress.py deleted file mode 100644 index d375588..0000000 --- a/src/tests_wordpress/testrunner_wordpress.py +++ /dev/null @@ -1,45 +0,0 @@ -from pathlib import Path - -import pytest -from dotenv import dotenv_values -from icecream import ic - - -class TestRunnerWordpress: - def __init__(self, dotenv_path: Path): - self.dotenv_path = dotenv_path - self.show_files() - self.load_dotenv() - # self.run_pytest_dir_wp() - # self.run_pytest_single_wp() - self.run_pytest_single_wp_feature1() - - def show_files(self): - ic(list(self.root_dir.glob("*"))) - - def load_dotenv(self): - dotenv_path = self.root_dir / "./blog.dev.local-it.cloud.env" - assert dotenv_path.is_file() - config = dotenv_values(dotenv_path) - ic(config) - - def run_pytest_dir_wp(self): - pytest.main([self.root_dir / "tests_wordpress"]) - - def run_pytest_single_wp(self): - pytest.main( - [ - self.root_dir / "tests_wordpress" / "test_wordpress.py", - "--env_file_path", - self.dotenv_path, - ] - ) - - def run_pytest_single_wp_feature1(self): - pytest.main( - [ - self.root_dir / "tests_wordpress" / "test_wordpress_feature1.py", - "--env_file_path", - self.dotenv_path, - ] - ) -- 2.47.2 From bee347f13fd326fe33545e7a0957ac38c16eb5bc Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 21:35:01 +0100 Subject: [PATCH 07/47] move show_files --- src/tests_wordpress/runner_wordpress.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/tests_wordpress/runner_wordpress.py b/src/tests_wordpress/runner_wordpress.py index 13b0f00..664f68b 100644 --- a/src/tests_wordpress/runner_wordpress.py +++ b/src/tests_wordpress/runner_wordpress.py @@ -26,7 +26,6 @@ class Runner: def __init__(self, dotenv_path: Path): self.dotenv_path = dotenv_path self.root_dir = self.dotenv_path.parent - self.show_files() self.load_dotenv() def _run_main_test(self): @@ -42,6 +41,9 @@ class Runner: ] ) + def show_files(self): + ic(list(self.root_dir.glob("*"))) + def run_tests(self): self._run_main_test() for c in self.sub_tests: @@ -57,9 +59,6 @@ class RunnerWordpress(Runner): SubTest(condition=condition_always_true, test_file="test_wordpress_feature1.py") ] - def show_files(self): - ic(list(self.root_dir.glob("*"))) - def load_dotenv(self): assert self.dotenv_path.is_file() config = dotenv_values(self.dotenv_path) -- 2.47.2 From 3fe9ed28db339bc8990ea616220c20d70ff8e85b Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 21:39:19 +0100 Subject: [PATCH 08/47] cleanup --- src/tests_wordpress/runner_wordpress.py | 40 +++++-------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/src/tests_wordpress/runner_wordpress.py b/src/tests_wordpress/runner_wordpress.py index 664f68b..b15db2a 100644 --- a/src/tests_wordpress/runner_wordpress.py +++ b/src/tests_wordpress/runner_wordpress.py @@ -20,13 +20,14 @@ def condition_always_true(dotenv_path: Path) -> bool: class Runner: + test_dir: Optional[str] = None main_test_name: Optional[str] = None sub_tests: list[SubTest] = [] def __init__(self, dotenv_path: Path): + assert self.test_dir is not None self.dotenv_path = dotenv_path - self.root_dir = self.dotenv_path.parent - self.load_dotenv() + # self.root_dir = self.dotenv_path.parent def _run_main_test(self): if isinstance(self.main_test_name, str): @@ -35,7 +36,7 @@ class Runner: def _run_pytest(self, test_name: str): pytest.main( [ - self.root_dir / "tests_wordpress" / test_name, + self.root_dir / self.test_dir / test_name, "--env_file_path", self.dotenv_path, ] @@ -46,41 +47,16 @@ class Runner: def run_tests(self): self._run_main_test() - for c in self.sub_tests: - condition_function = c["condition"] + for sub_test in self.sub_tests: + condition_function = sub_test["condition"] if condition_function(): - test_name = c["test_file"] + test_name = sub_test["test_file"] self._run_pytest(test_name) class RunnerWordpress(Runner): + test_dir = "tests_wordpress" main_test_name = "test_wordpress.py" sub_tests = [ SubTest(condition=condition_always_true, test_file="test_wordpress_feature1.py") ] - - def load_dotenv(self): - assert self.dotenv_path.is_file() - config = dotenv_values(self.dotenv_path) - ic(config) - - def run_pytest_dir_wp(self): - pytest.main([self.root_dir / "tests_wordpress"]) - - def run_pytest_single_wp(self): - pytest.main( - [ - self.root_dir / "tests_wordpress" / "test_wordpress.py", - "--env_file_path", - self.dotenv_path, - ] - ) - - def run_pytest_single_wp_feature1(self): - pytest.main( - [ - self.root_dir / "tests_wordpress" / "test_wordpress_feature1.py", - "--env_file_path", - self.dotenv_path, - ] - ) -- 2.47.2 From 224ad2ea85004b0d400c2fc6c882882204bf135e Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 21:50:40 +0100 Subject: [PATCH 09/47] everything working --- src/runner.py | 45 +++++++++++++++++++++++ src/tests_wordpress/runner_wordpress.py | 47 ++----------------------- 2 files changed, 47 insertions(+), 45 deletions(-) create mode 100644 src/runner.py diff --git a/src/runner.py b/src/runner.py new file mode 100644 index 0000000..661db54 --- /dev/null +++ b/src/runner.py @@ -0,0 +1,45 @@ +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): + assert self.test_dir_name is not None + self.root_dir = Path(__file__).parent + self.dotenv_path = dotenv_path + + def _run_main_test(self): + if isinstance(self.main_test_name, str): + self._run_pytest(self.main_test_name) + + def _run_pytest(self, test_name: str): + pytest.main( + [ + self.root_dir / self.test_dir_name / test_name, + "--env_file_path", + self.dotenv_path, + ] + ) + + 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"] + self._run_pytest(test_name) diff --git a/src/tests_wordpress/runner_wordpress.py b/src/tests_wordpress/runner_wordpress.py index b15db2a..75dc6b1 100644 --- a/src/tests_wordpress/runner_wordpress.py +++ b/src/tests_wordpress/runner_wordpress.py @@ -1,14 +1,6 @@ from pathlib import Path -from typing import Callable, Optional, TypedDict -import pytest -from dotenv import dotenv_values -from icecream import ic - - -class SubTest(TypedDict): - condition: Callable[[Path], bool] - test_file: str +from runner import Runner, SubTest def condition_feature1(dotenv_path: Path) -> bool: @@ -19,43 +11,8 @@ def condition_always_true(dotenv_path: Path) -> bool: return True -class Runner: - test_dir: Optional[str] = None - main_test_name: Optional[str] = None - sub_tests: list[SubTest] = [] - - def __init__(self, dotenv_path: Path): - assert self.test_dir is not None - self.dotenv_path = dotenv_path - # self.root_dir = self.dotenv_path.parent - - def _run_main_test(self): - if isinstance(self.main_test_name, str): - self._run_pytest(self.main_test_name) - - def _run_pytest(self, test_name: str): - pytest.main( - [ - self.root_dir / self.test_dir / test_name, - "--env_file_path", - self.dotenv_path, - ] - ) - - 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(): - test_name = sub_test["test_file"] - self._run_pytest(test_name) - - class RunnerWordpress(Runner): - test_dir = "tests_wordpress" + test_dir_name = "tests_wordpress" main_test_name = "test_wordpress.py" sub_tests = [ SubTest(condition=condition_always_true, test_file="test_wordpress_feature1.py") -- 2.47.2 From 534eb229c9fd7285efc824e77f7f869218275b1b Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 21:54:25 +0100 Subject: [PATCH 10/47] refactoring and cleanup --- src/test_wrapper.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/test_wrapper.py b/src/test_wrapper.py index aa713c5..eaf2382 100644 --- a/src/test_wrapper.py +++ b/src/test_wrapper.py @@ -17,24 +17,19 @@ RUNNER_DICT: dict[str, type[TestRunner]] = {"wordpress": RunnerWordpress} class TestWrapper: def __init__(self, dotenv_path: Path): self.dotenv_path = dotenv_path - self.root_dir = self.dotenv_path.parent - self.show_files() - self.load_dotenv() - self.load_test_wrapper() + self.config = self.load_config_from_dotenv(dotenv_path) + runner = self.load_runner() + runner.run_tests() - def show_files(self): - ic(list(self.root_dir.glob("*"))) + def load_config_from_dotenv(self, dotenv_path: Path): + assert dotenv_path.is_file() + return dotenv_values(self.dotenv_path) - def load_dotenv(self): - assert self.dotenv_path.is_file() - self.config = dotenv_values(self.dotenv_path) - - def load_test_wrapper(self): + def load_runner(self): config_type = self.config["TYPE"] ic(config_type) RunnerClass = RUNNER_DICT[config_type] - runner = RunnerClass(self.dotenv_path) - runner.run_tests() + return RunnerClass(self.dotenv_path) if __name__ == "__main__": -- 2.47.2 From fa4c84037496b37650712d4ef9e3809230570907 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 21:58:53 +0100 Subject: [PATCH 11/47] move conftest to root level --- src/{tests_wordpress => }/conftest.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{tests_wordpress => }/conftest.py (100%) diff --git a/src/tests_wordpress/conftest.py b/src/conftest.py similarity index 100% rename from src/tests_wordpress/conftest.py rename to src/conftest.py -- 2.47.2 From 1faaf4700a279bf91d79d6a9bbb173ffc472e22a Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 22:09:39 +0100 Subject: [PATCH 12/47] refactoring --- src/test_wrapper.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/test_wrapper.py b/src/test_wrapper.py index eaf2382..6327920 100644 --- a/src/test_wrapper.py +++ b/src/test_wrapper.py @@ -7,31 +7,30 @@ from tests_wordpress.runner_wordpress import RunnerWordpress class TestRunner(Protocol): + def __init__(self, dotenv_path: Path): + ... + def run_tests(): - pass + ... RUNNER_DICT: dict[str, type[TestRunner]] = {"wordpress": RunnerWordpress} -class TestWrapper: +class Wrapper: def __init__(self, dotenv_path: Path): - self.dotenv_path = dotenv_path - self.config = self.load_config_from_dotenv(dotenv_path) - runner = self.load_runner() + assert dotenv_path.is_file() + config = dotenv_values(dotenv_path) + runner = self.load_runner(config, dotenv_path) runner.run_tests() - def load_config_from_dotenv(self, dotenv_path: Path): - assert dotenv_path.is_file() - return dotenv_values(self.dotenv_path) - - def load_runner(self): - config_type = self.config["TYPE"] + def load_runner(self, config, dotenv_path): + config_type = config["TYPE"] ic(config_type) RunnerClass = RUNNER_DICT[config_type] - return RunnerClass(self.dotenv_path) + return RunnerClass(dotenv_path) if __name__ == "__main__": dotenv_path = Path(__file__).parent / "./blog.dev.local-it.cloud.env" - t = TestWrapper(dotenv_path) + t = Wrapper(dotenv_path) -- 2.47.2 From c7d0ba92732d35f6d57b0989c854676c597f48ee Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 22:16:11 +0100 Subject: [PATCH 13/47] improve typing --- src/test_wrapper.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test_wrapper.py b/src/test_wrapper.py index 6327920..8c60908 100644 --- a/src/test_wrapper.py +++ b/src/test_wrapper.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Protocol +from typing import Optional, Protocol from dotenv import dotenv_values from icecream import ic @@ -20,11 +20,13 @@ RUNNER_DICT: dict[str, type[TestRunner]] = {"wordpress": RunnerWordpress} class Wrapper: def __init__(self, dotenv_path: Path): assert dotenv_path.is_file() - config = dotenv_values(dotenv_path) + config: dict[str, Optional[str]] = dotenv_values(dotenv_path) runner = self.load_runner(config, dotenv_path) runner.run_tests() - def load_runner(self, config, dotenv_path): + def load_runner( + self, config: dict[str, Optional[str]], dotenv_path: Path + ) -> TestRunner: config_type = config["TYPE"] ic(config_type) RunnerClass = RUNNER_DICT[config_type] -- 2.47.2 From 14c0c073e441424a6ded31031fa2da8577142f37 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Nov 2023 22:19:33 +0100 Subject: [PATCH 14/47] improve typing --- src/test_wrapper.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/test_wrapper.py b/src/test_wrapper.py index 8c60908..7731daf 100644 --- a/src/test_wrapper.py +++ b/src/test_wrapper.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Optional, Protocol +from typing import Protocol from dotenv import dotenv_values from icecream import ic @@ -10,7 +10,7 @@ class TestRunner(Protocol): def __init__(self, dotenv_path: Path): ... - def run_tests(): + def run_tests(self): ... @@ -20,13 +20,11 @@ RUNNER_DICT: dict[str, type[TestRunner]] = {"wordpress": RunnerWordpress} class Wrapper: def __init__(self, dotenv_path: Path): assert dotenv_path.is_file() - config: dict[str, Optional[str]] = dotenv_values(dotenv_path) + config: dict[str, str] = dotenv_values(dotenv_path) runner = self.load_runner(config, dotenv_path) runner.run_tests() - def load_runner( - self, config: dict[str, Optional[str]], dotenv_path: Path - ) -> TestRunner: + def load_runner(self, config: dict[str, str], dotenv_path: Path) -> TestRunner: config_type = config["TYPE"] ic(config_type) RunnerClass = RUNNER_DICT[config_type] -- 2.47.2 From 9306da813b17975e049c5881cf427b9c13c30461 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 11:36:44 +0100 Subject: [PATCH 15/47] add creds --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0a48c23..4395fbc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ __pycache__/ *.zip TestResults/ .vscode/ -*.pyc \ No newline at end of file +*.pyc +credentials* \ No newline at end of file -- 2.47.2 From 336a82cd24b72160ae00a96cc935a3f2206c640f Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 11:44:27 +0100 Subject: [PATCH 16/47] change line length to 120 --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..d2dd016 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[tool.ruff] +line-length = 120 +target-version = "py312" -- 2.47.2 From 9207d88176cd801ab2547757a0af4d96ca2f7f4f Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 12:20:31 +0100 Subject: [PATCH 17/47] WIP manage test artifacts in unique folders via global fixtures in conftest --- src/conftest.py | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/conftest.py b/src/conftest.py index 9d0a65a..3013e87 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -1,3 +1,4 @@ +from datetime import datetime from pathlib import Path import pytest @@ -12,9 +13,35 @@ def pytest_addoption(parser): @pytest.fixture -def config(request): +def dotenv_config(request) -> dict[str, str]: dotenv_path = request.config.getoption("--env_file_path") dotenv_path = Path(dotenv_path) assert dotenv_path.is_file() - config = dotenv_values(dotenv_path) - return config + return dotenv_values(dotenv_path) + + +@pytest.fixture(scope="session", autouse=True) +def session_id() -> str: + current_datetime = datetime.now() + return current_datetime.strftime("%Y-%m-%d-%H-%M") + + +@pytest.fixture(scope="session", autouse=True) +def TEST_DIR(session_id) -> Path: + all_tests_dir = Path("tests") + all_tests_dir.mkdir(exist_ok=True) + test_dir = all_tests_dir / Path(f"test_{session_id}") + test_dir.mkdir() + return test_dir + + +@pytest.fixture(scope="session", autouse=True) +def RECORDS(TEST_DIR) -> Path: + records = TEST_DIR / Path("records") + return records.mkdir() + + +@pytest.fixture(scope="session", autouse=True) +def STATES(TEST_DIR) -> Path: + states = TEST_DIR / Path("states") + return states.mkdir() -- 2.47.2 From cde80e511064b740bc33321170e943a352ce3e92 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 12:20:44 +0100 Subject: [PATCH 18/47] add v and rp arguments in run_pytest --- src/runner.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/runner.py b/src/runner.py index 661db54..bf054ed 100644 --- a/src/runner.py +++ b/src/runner.py @@ -27,6 +27,8 @@ class Runner: def _run_pytest(self, test_name: str): pytest.main( [ + "-v", + "-rp", self.root_dir / self.test_dir_name / test_name, "--env_file_path", self.dotenv_path, -- 2.47.2 From d5b9eda7b163301499c94686177360d4f694bb1f Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 12:22:20 +0100 Subject: [PATCH 19/47] add comment to conftest --- src/tests_wordpress/conftest.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/tests_wordpress/conftest.py diff --git a/src/tests_wordpress/conftest.py b/src/tests_wordpress/conftest.py new file mode 100644 index 0000000..d78bb85 --- /dev/null +++ b/src/tests_wordpress/conftest.py @@ -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 -- 2.47.2 From 4b6da547b872b7d40958d32160d939838e7f1f49 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 12:22:42 +0100 Subject: [PATCH 20/47] disable main test, improve defualt conditions --- src/tests_wordpress/runner_wordpress.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/tests_wordpress/runner_wordpress.py b/src/tests_wordpress/runner_wordpress.py index 75dc6b1..3d60aa2 100644 --- a/src/tests_wordpress/runner_wordpress.py +++ b/src/tests_wordpress/runner_wordpress.py @@ -3,17 +3,18 @@ from pathlib import Path from runner import Runner, SubTest -def condition_feature1(dotenv_path: Path) -> bool: - """returns true if feature 1 should be executed based on the dotenv file given as a Path""" - - 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" + # main_test_name = "test_wordpress.py" sub_tests = [ - SubTest(condition=condition_always_true, test_file="test_wordpress_feature1.py") + SubTest(condition=condition_always_false, test_file="test_wordpress_feature1.py"), + SubTest(condition=condition_always_true, test_file="conftest.py"), ] -- 2.47.2 From 2e3e92c538ff89f0fe560fc8035fb6105b4302c9 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 12:29:52 +0100 Subject: [PATCH 21/47] rename to main --- src/{test_wrapper.py => main.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{test_wrapper.py => main.py} (100%) diff --git a/src/test_wrapper.py b/src/main.py similarity index 100% rename from src/test_wrapper.py rename to src/main.py -- 2.47.2 From 3cb78cb2a56e3abac01e665b5b19721a301af73c Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 12:30:04 +0100 Subject: [PATCH 22/47] add more stuff to pyproject.toml --- pyproject.toml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d2dd016..4a80e86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +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 = "py312" +target-version = "py311" -- 2.47.2 From 65aa461d5af7f0f040bc2e5b266600d88c6e1833 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 12:30:13 +0100 Subject: [PATCH 23/47] add runner for authentik --- src/tests_authentik/runner_authentik.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/tests_authentik/runner_authentik.py diff --git a/src/tests_authentik/runner_authentik.py b/src/tests_authentik/runner_authentik.py new file mode 100644 index 0000000..c8ae216 --- /dev/null +++ b/src/tests_authentik/runner_authentik.py @@ -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_setup.py" -- 2.47.2 From 25a087db9d3b0b8ea797eef223631bae0a859b15 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 12:30:36 +0100 Subject: [PATCH 24/47] add authentik setup, this is base for all other testing --- src/tests_authentik/test_authentik_setup.py | 165 ++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 src/tests_authentik/test_authentik_setup.py diff --git a/src/tests_authentik/test_authentik_setup.py b/src/tests_authentik/test_authentik_setup.py new file mode 100644 index 0000000..5aac515 --- /dev/null +++ b/src/tests_authentik/test_authentik_setup.py @@ -0,0 +1,165 @@ +import pytest +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): + CONFIG = dotenv_config + context = setup_context(browser) + page = context.new_page() + page.goto(CONFIG["domain"]) + 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() -- 2.47.2 From a7778b0e5efa400e9ad54087811bf868935e9e39 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 12:35:32 +0100 Subject: [PATCH 25/47] add envfiles as submodule --- .gitmodules | 3 +++ envfiles | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 envfiles diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2cb2d96 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "envfiles"] + path = envfiles + url = ssh://git@git.local-it.org:2222/local-it-infrastructure/dev.local-it.cloud.git diff --git a/envfiles b/envfiles new file mode 160000 index 0000000..a8375f6 --- /dev/null +++ b/envfiles @@ -0,0 +1 @@ +Subproject commit a8375f6fc7a285a1000b5553be47eaf19b0be0a6 -- 2.47.2 From f277d95f2cde9c74519397da3bfab07b7f1163a6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 12:53:09 +0100 Subject: [PATCH 26/47] move test dir creation to main.py --- src/main.py | 52 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/src/main.py b/src/main.py index 7731daf..d4812c2 100644 --- a/src/main.py +++ b/src/main.py @@ -1,10 +1,22 @@ +from datetime import datetime from pathlib import Path from typing import Protocol 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 +] + +ic(ENV_FILES[0].is_file()) +ic(ENV_FILES[1].is_file()) + class TestRunner(Protocol): def __init__(self, dotenv_path: Path): @@ -18,13 +30,47 @@ RUNNER_DICT: dict[str, type[TestRunner]] = {"wordpress": RunnerWordpress} class Wrapper: - def __init__(self, dotenv_path: Path): + def __init__(self, env_files: list[Path]): + self.env_files = env_files + + self.setup_tests() + return + + dotenv_path = self.env_files[0] assert dotenv_path.is_file() config: dict[str, str] = dotenv_values(dotenv_path) - runner = self.load_runner(config, dotenv_path) + runner = self.load_runners(config, dotenv_path) runner.run_tests() - def load_runner(self, config: dict[str, str], dotenv_path: Path) -> TestRunner: + def setup_tests(self): + session_id = self.get_session_id() + test_dir = self.create_test_dir(session_id) + + @staticmethod + def get_session_id() -> str: + current_datetime = datetime.now() + return current_datetime.strftime("%Y-%m-%d-%H-%M") + + @staticmethod + def create_test_dir(session_id: str) -> Path: + TESTS_DIR.mkdir(exist_ok=True) + test_dir = TESTS_DIR / Path(f"test_{session_id}") + test_dir.mkdir() + return test_dir + + @staticmethod + def create_records_dir(test_dir: Path) -> Path: + records = test_dir / Path("records") + records.mkdir() + return records + + @staticmethod + def create_states_dir(test_dir: Path) -> Path: + states = test_dir / Path("states") + states.mkdir() + return states + + def load_runners(self, config: dict[str, str], dotenv_path: Path) -> TestRunner: config_type = config["TYPE"] ic(config_type) RunnerClass = RUNNER_DICT[config_type] -- 2.47.2 From 18c695d178627b4f8900b3662a870151eb7bcd13 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 13:14:12 +0100 Subject: [PATCH 27/47] improve dir creation --- src/main.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main.py b/src/main.py index d4812c2..1c4e814 100644 --- a/src/main.py +++ b/src/main.py @@ -7,7 +7,7 @@ from icecream import ic from tests_authentik.runner_authentik import RunnerAuthentik from tests_wordpress.runner_wordpress import RunnerWordpress -TESTS_DIR = Path("tests") +TESTS_DIR = Path("../tests") ENV_FILES = [ Path("../envfiles/login.test.dev.local-it.cloud.env"), # authentik @@ -32,6 +32,7 @@ RUNNER_DICT: dict[str, type[TestRunner]] = {"wordpress": RunnerWordpress} class Wrapper: def __init__(self, env_files: list[Path]): self.env_files = env_files + self.runners: list[TestRunner] = [] self.setup_tests() return @@ -42,9 +43,17 @@ class Wrapper: runner = self.load_runners(config, dotenv_path) runner.run_tests() + def load_runners(self, config: dict[str, str], dotenv_path: Path) -> TestRunner: + config_type = config["TYPE"] + ic(config_type) + RunnerClass = RUNNER_DICT[config_type] + return RunnerClass(dotenv_path) + def setup_tests(self): session_id = self.get_session_id() test_dir = self.create_test_dir(session_id) + self.create_records_dir(test_dir) + self.create_states_dir(test_dir) @staticmethod def get_session_id() -> str: @@ -70,13 +79,6 @@ class Wrapper: states.mkdir() return states - def load_runners(self, config: dict[str, str], dotenv_path: Path) -> TestRunner: - config_type = config["TYPE"] - ic(config_type) - RunnerClass = RUNNER_DICT[config_type] - return RunnerClass(dotenv_path) - if __name__ == "__main__": - dotenv_path = Path(__file__).parent / "./blog.dev.local-it.cloud.env" - t = Wrapper(dotenv_path) + t = Wrapper(ENV_FILES) -- 2.47.2 From c794d330aba5a4892910b4e26979a3848a2c6ea8 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 13:23:47 +0100 Subject: [PATCH 28/47] add code for different runners --- src/main.py | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/main.py b/src/main.py index 1c4e814..6816c61 100644 --- a/src/main.py +++ b/src/main.py @@ -32,28 +32,37 @@ RUNNER_DICT: dict[str, type[TestRunner]] = {"wordpress": RunnerWordpress} class Wrapper: def __init__(self, env_files: list[Path]): self.env_files = env_files - self.runners: list[TestRunner] = [] - self.setup_tests() - return + self.setup_test_dir() - dotenv_path = self.env_files[0] - assert dotenv_path.is_file() - config: dict[str, str] = dotenv_values(dotenv_path) - runner = self.load_runners(config, dotenv_path) - runner.run_tests() + self.check_env_files(self.env_files) + self.runners: list[TestRunner] = self.load_runners(self.env_files) + self.run_tests(self.runners) - def load_runners(self, config: dict[str, str], dotenv_path: Path) -> TestRunner: - config_type = config["TYPE"] - ic(config_type) - RunnerClass = RUNNER_DICT[config_type] - return RunnerClass(dotenv_path) + 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(env_file)) + return runners - def setup_tests(self): + 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() + + def setup_test_dir(self): session_id = self.get_session_id() test_dir = self.create_test_dir(session_id) self.create_records_dir(test_dir) self.create_states_dir(test_dir) + self.create_results_dir(test_dir) @staticmethod def get_session_id() -> str: @@ -79,6 +88,12 @@ class Wrapper: states.mkdir() return states + @staticmethod + def create_results_dir(test_dir: Path) -> Path: + results = test_dir / Path("results") + results.mkdir() + return results + if __name__ == "__main__": t = Wrapper(ENV_FILES) -- 2.47.2 From 9da2f2526d381737c19f91df56b1a409e112ea0a Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 14:02:29 +0100 Subject: [PATCH 29/47] use DirManager --- src/main.py | 92 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 39 deletions(-) diff --git a/src/main.py b/src/main.py index 6816c61..4ea53cf 100644 --- a/src/main.py +++ b/src/main.py @@ -11,12 +11,9 @@ 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 + # Path("../envfiles/blog.test.dev.local-it.cloud.env"), # wordpress ] -ic(ENV_FILES[0].is_file()) -ic(ENV_FILES[1].is_file()) - class TestRunner(Protocol): def __init__(self, dotenv_path: Path): @@ -26,16 +23,28 @@ class TestRunner(Protocol): ... -RUNNER_DICT: dict[str, type[TestRunner]] = {"wordpress": RunnerWordpress} +# 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]): self.env_files = env_files - - self.setup_test_dir() - self.check_env_files(self.env_files) + + session_id = self.get_session_id() + self.setup_test(session_id) + + self.run_test() + + def setup_test(self, session_id: str): + self.dir_manager = DirManager(root_tests_dir=TESTS_DIR, session_id=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) @@ -57,42 +66,47 @@ class Wrapper: for env_file in env_files: assert env_file.is_file() - def setup_test_dir(self): - session_id = self.get_session_id() - test_dir = self.create_test_dir(session_id) - self.create_records_dir(test_dir) - self.create_states_dir(test_dir) - self.create_results_dir(test_dir) - @staticmethod def get_session_id() -> str: current_datetime = datetime.now() - return current_datetime.strftime("%Y-%m-%d-%H-%M") + return current_datetime.strftime("%Y-%m-%d-%H-%M-%S") + + +class DirManager: + def __init__(self, root_tests_dir: Path, session_id: str): + # root test dir + self.root_tests_dir = root_tests_dir + self.session_id = session_id + + self.get_all_dirs() + + def get_all_dirs(self): + self.session_dir = self.root_tests_dir / f"test-{self.session_id}" + self.session_sub_dirs = self.get_subdirs(self.session_dir) + + def create_all_dirs(self): + self.create_dirs(self.root_tests_dir, exist_ok=True) + self.create_dirs(self.session_dir) + self.create_dirs(self.session_sub_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_test_dir(session_id: str) -> Path: - TESTS_DIR.mkdir(exist_ok=True) - test_dir = TESTS_DIR / Path(f"test_{session_id}") - test_dir.mkdir() - return test_dir - - @staticmethod - def create_records_dir(test_dir: Path) -> Path: - records = test_dir / Path("records") - records.mkdir() - return records - - @staticmethod - def create_states_dir(test_dir: Path) -> Path: - states = test_dir / Path("states") - states.mkdir() - return states - - @staticmethod - def create_results_dir(test_dir: Path) -> Path: - results = test_dir / Path("results") - results.mkdir() - return results + 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) if __name__ == "__main__": -- 2.47.2 From 4534ee892b2834da86d0bbb998555291456b51f2 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 14:02:38 +0100 Subject: [PATCH 30/47] add logging --- src/runner.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/runner.py b/src/runner.py index bf054ed..bf28b96 100644 --- a/src/runner.py +++ b/src/runner.py @@ -16,6 +16,7 @@ class Runner: sub_tests: list[SubTest] = [] def __init__(self, dotenv_path: Path): + ic(f"creating instance of {self.__class__.__name__}") assert self.test_dir_name is not None self.root_dir = Path(__file__).parent self.dotenv_path = dotenv_path @@ -25,6 +26,7 @@ class Runner: self._run_pytest(self.main_test_name) def _run_pytest(self, test_name: str): + ic(f"running test: {test_name}") pytest.main( [ "-v", -- 2.47.2 From d4e9a50ca627ed1fff2a2b37e487a5b2edb577eb Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 14:04:24 +0100 Subject: [PATCH 31/47] move dirmanager to its own file --- src/dirmanager.py | 38 ++++++++++++++++++++++++++++++++++++++ src/main.py | 38 +------------------------------------- 2 files changed, 39 insertions(+), 37 deletions(-) create mode 100644 src/dirmanager.py diff --git a/src/dirmanager.py b/src/dirmanager.py new file mode 100644 index 0000000..e5a395e --- /dev/null +++ b/src/dirmanager.py @@ -0,0 +1,38 @@ +from pathlib import Path + + +class DirManager: + def __init__(self, root_tests_dir: Path, session_id: str): + # root test dir + self.root_tests_dir = root_tests_dir + self.session_id = session_id + + self.get_all_dirs() + + def get_all_dirs(self): + self.session_dir = self.root_tests_dir / f"test-{self.session_id}" + self.session_sub_dirs = self.get_subdirs(self.session_dir) + + def create_all_dirs(self): + self.create_dirs(self.root_tests_dir, exist_ok=True) + self.create_dirs(self.session_dir) + self.create_dirs(self.session_sub_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) diff --git a/src/main.py b/src/main.py index 4ea53cf..322814f 100644 --- a/src/main.py +++ b/src/main.py @@ -2,6 +2,7 @@ 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 @@ -72,42 +73,5 @@ class Wrapper: return current_datetime.strftime("%Y-%m-%d-%H-%M-%S") -class DirManager: - def __init__(self, root_tests_dir: Path, session_id: str): - # root test dir - self.root_tests_dir = root_tests_dir - self.session_id = session_id - - self.get_all_dirs() - - def get_all_dirs(self): - self.session_dir = self.root_tests_dir / f"test-{self.session_id}" - self.session_sub_dirs = self.get_subdirs(self.session_dir) - - def create_all_dirs(self): - self.create_dirs(self.root_tests_dir, exist_ok=True) - self.create_dirs(self.session_dir) - self.create_dirs(self.session_sub_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) - - if __name__ == "__main__": t = Wrapper(ENV_FILES) -- 2.47.2 From 0d1dd4ca17fc935b63a81482c3adb97fd57ab46f Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 14:23:17 +0100 Subject: [PATCH 32/47] update fixtures in conftest, add --tests_dir and --session_id parser options --- src/conftest.py | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/conftest.py b/src/conftest.py index 3013e87..65c3a36 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -1,47 +1,52 @@ -from datetime import datetime from pathlib import Path import pytest +from dirmanager import DirManager from dotenv import dotenv_values def pytest_addoption(parser): parser.addoption( - "--env_file_path", + "--env_file", + action="store", + ) + parser.addoption( + "--tests_dir", + action="store", + ) + parser.addoption( + "--session_id", action="store", ) -@pytest.fixture +@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_path") + 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 session_id() -> str: - current_datetime = datetime.now() - return current_datetime.strftime("%Y-%m-%d-%H-%M") +def RECORDS(dirmanager) -> Path: + assert isinstance(dirmanager, DirManager) + return dirmanager.get_all_dirs()["records"] @pytest.fixture(scope="session", autouse=True) -def TEST_DIR(session_id) -> Path: - all_tests_dir = Path("tests") - all_tests_dir.mkdir(exist_ok=True) - test_dir = all_tests_dir / Path(f"test_{session_id}") - test_dir.mkdir() - return test_dir +def STATES(dirmanager) -> Path: + return dirmanager.get_all_dirs()["states"] @pytest.fixture(scope="session", autouse=True) -def RECORDS(TEST_DIR) -> Path: - records = TEST_DIR / Path("records") - return records.mkdir() - - -@pytest.fixture(scope="session", autouse=True) -def STATES(TEST_DIR) -> Path: - states = TEST_DIR / Path("states") - return states.mkdir() +def RESULTS(dirmanager) -> Path: + return dirmanager.get_all_dirs()["results"] -- 2.47.2 From ea51e508d7566d862824cdb694fbb5e2763e4e16 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 14:23:35 +0100 Subject: [PATCH 33/47] update docstring and argname of Dirmanager --- src/dirmanager.py | 22 ++++++++++++++++++---- src/main.py | 2 +- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/dirmanager.py b/src/dirmanager.py index e5a395e..4fa4844 100644 --- a/src/dirmanager.py +++ b/src/dirmanager.py @@ -2,19 +2,33 @@ from pathlib import Path class DirManager: - def __init__(self, root_tests_dir: Path, session_id: str): + """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.root_tests_dir = root_tests_dir + self.tests_dir = tests_dir self.session_id = session_id self.get_all_dirs() def get_all_dirs(self): - self.session_dir = self.root_tests_dir / f"test-{self.session_id}" + self.session_dir = self.tests_dir / f"test-{self.session_id}" self.session_sub_dirs = self.get_subdirs(self.session_dir) def create_all_dirs(self): - self.create_dirs(self.root_tests_dir, exist_ok=True) + self.create_dirs(self.tests_dir, exist_ok=True) self.create_dirs(self.session_dir) self.create_dirs(self.session_sub_dirs) diff --git a/src/main.py b/src/main.py index 322814f..cfda325 100644 --- a/src/main.py +++ b/src/main.py @@ -42,7 +42,7 @@ class Wrapper: self.run_test() def setup_test(self, session_id: str): - self.dir_manager = DirManager(root_tests_dir=TESTS_DIR, session_id=session_id) + self.dir_manager = DirManager(tests_dir=TESTS_DIR, session_id=session_id) self.dir_manager.create_all_dirs() def run_test(self): -- 2.47.2 From f865cda6b4c0cc3d4db919345faf18735ce24446 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 14:33:00 +0100 Subject: [PATCH 34/47] restructure Wrapper, update TestRunner protocol --- src/main.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main.py b/src/main.py index cfda325..c9e220c 100644 --- a/src/main.py +++ b/src/main.py @@ -17,7 +17,7 @@ ENV_FILES = [ class TestRunner(Protocol): - def __init__(self, dotenv_path: Path): + def __init__(self, dotenv_path: Path, tests_dir: Path, session_id: str): ... def run_tests(self): @@ -32,17 +32,14 @@ RUNNER_DICT: dict[str, type[TestRunner]] = { class Wrapper: - def __init__(self, env_files: list[Path]): + 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 - session_id = self.get_session_id() - self.setup_test(session_id) - self.run_test() - - def setup_test(self, session_id: str): - self.dir_manager = DirManager(tests_dir=TESTS_DIR, 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): @@ -55,6 +52,7 @@ class Wrapper: config: dict[str, str] = dotenv_values(env_file) RunnerClass = RUNNER_DICT[config["TYPE"]] runners.append(RunnerClass(env_file)) + RunnerClass(dotenv_path=env_file, tests_dir=TESTS_DIR, session_id=) return runners def run_tests(self, runners: list[TestRunner]): @@ -74,4 +72,8 @@ class Wrapper: if __name__ == "__main__": - t = Wrapper(ENV_FILES) + session_id = Wrapper.get_session_id() + wrapper = Wrapper(ENV_FILES) + wrapper.setup_test() + wrapper.run_test() + -- 2.47.2 From 8f827d7f7bba0cb69b5cc73c75982c4110335b5c Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 14:33:30 +0100 Subject: [PATCH 35/47] update RunnerClass initialization --- src/main.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main.py b/src/main.py index c9e220c..fea032c 100644 --- a/src/main.py +++ b/src/main.py @@ -37,7 +37,6 @@ class Wrapper: 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() @@ -51,8 +50,7 @@ class Wrapper: for env_file in env_files: config: dict[str, str] = dotenv_values(env_file) RunnerClass = RUNNER_DICT[config["TYPE"]] - runners.append(RunnerClass(env_file)) - RunnerClass(dotenv_path=env_file, tests_dir=TESTS_DIR, session_id=) + 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]): @@ -76,4 +74,3 @@ if __name__ == "__main__": wrapper = Wrapper(ENV_FILES) wrapper.setup_test() wrapper.run_test() - -- 2.47.2 From 5ce20f0b81f64e59ddf9dd8efc563ee4d1afad7b Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 14:33:51 +0100 Subject: [PATCH 36/47] use new pytest command args in Runner --- src/runner.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/runner.py b/src/runner.py index bf28b96..d8078b5 100644 --- a/src/runner.py +++ b/src/runner.py @@ -15,25 +15,36 @@ class Runner: main_test_name: Optional[str] = None sub_tests: list[SubTest] = [] - def __init__(self, dotenv_path: Path): + 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 - self.dotenv_path = dotenv_path def _run_main_test(self): if isinstance(self.main_test_name, str): self._run_pytest(self.main_test_name) - def _run_pytest(self, test_name: str): - ic(f"running test: {test_name}") + 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", - self.root_dir / self.test_dir_name / test_name, - "--env_file_path", - self.dotenv_path, + str(full_test_path), + "--env_file", + str(self.dotenv_path), + "--tests_dir", + str(self.tests_dir), + "--session_id", + self.session_id, ] ) @@ -46,4 +57,5 @@ class Runner: condition_function = sub_test["condition"] if condition_function(self.dotenv_path): test_name = sub_test["test_file"] - self._run_pytest(test_name) + full_test_path = self.root_dir / self.test_dir_name / test_name + self._run_pytest(full_test_path) -- 2.47.2 From f93ea27a6f22729598292cb7da1c8ee9ef2f31d1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 14:34:14 +0100 Subject: [PATCH 37/47] init Wrapper with session_id --- src/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.py b/src/main.py index fea032c..3100621 100644 --- a/src/main.py +++ b/src/main.py @@ -71,6 +71,6 @@ class Wrapper: if __name__ == "__main__": session_id = Wrapper.get_session_id() - wrapper = Wrapper(ENV_FILES) + wrapper = Wrapper(ENV_FILES, session_id=session_id) wrapper.setup_test() wrapper.run_test() -- 2.47.2 From a9850f792a9ca3f8ed1d55e9bd585f7dbbbad04e Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 14:45:48 +0100 Subject: [PATCH 38/47] fix path for _run_main_test --- src/runner.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/runner.py b/src/runner.py index d8078b5..9e0a09b 100644 --- a/src/runner.py +++ b/src/runner.py @@ -26,7 +26,8 @@ class Runner: def _run_main_test(self): if isinstance(self.main_test_name, str): - self._run_pytest(self.main_test_name) + 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 -- 2.47.2 From 746605fc62f12b3c1ad62ff8c5386bc3a0e8c6ba Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 14:51:08 +0100 Subject: [PATCH 39/47] change dir logic --- src/dirmanager.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/dirmanager.py b/src/dirmanager.py index 4fa4844..dfc149d 100644 --- a/src/dirmanager.py +++ b/src/dirmanager.py @@ -21,18 +21,19 @@ class DirManager: self.tests_dir = tests_dir self.session_id = session_id - self.get_all_dirs() - - def get_all_dirs(self): - self.session_dir = self.tests_dir / f"test-{self.session_id}" - self.session_sub_dirs = self.get_subdirs(self.session_dir) + self.dirs = self._get_all_dirs() def create_all_dirs(self): self.create_dirs(self.tests_dir, exist_ok=True) - self.create_dirs(self.session_dir) - self.create_dirs(self.session_sub_dirs) + self.create_dirs(self.dirs) - def get_subdirs(self, session_dir: Path): + 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"), -- 2.47.2 From 493b44fe0f6df5676888c7cb830ead040f84b1ff Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 14:51:36 +0100 Subject: [PATCH 40/47] use new dir logic --- src/conftest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/conftest.py b/src/conftest.py index 65c3a36..43b4a2a 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -39,14 +39,14 @@ def dotenv_config(request) -> dict[str, str]: @pytest.fixture(scope="session", autouse=True) def RECORDS(dirmanager) -> Path: assert isinstance(dirmanager, DirManager) - return dirmanager.get_all_dirs()["records"] + return dirmanager.dirs["records"] @pytest.fixture(scope="session", autouse=True) def STATES(dirmanager) -> Path: - return dirmanager.get_all_dirs()["states"] + return dirmanager.dirs["states"] @pytest.fixture(scope="session", autouse=True) def RESULTS(dirmanager) -> Path: - return dirmanager.get_all_dirs()["results"] + return dirmanager.dirs["results"] -- 2.47.2 From 0e1aaa136cb46df6274160b82a3fa5c33c680dc1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 15:11:18 +0100 Subject: [PATCH 41/47] use dummy test --- src/tests_authentik/runner_authentik.py | 2 +- src/tests_authentik/test_authentik_dummy.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 src/tests_authentik/test_authentik_dummy.py diff --git a/src/tests_authentik/runner_authentik.py b/src/tests_authentik/runner_authentik.py index c8ae216..1ef97b8 100644 --- a/src/tests_authentik/runner_authentik.py +++ b/src/tests_authentik/runner_authentik.py @@ -13,4 +13,4 @@ def condition_always_false(dotenv_path: Path) -> bool: class RunnerAuthentik(Runner): test_dir_name = "tests_authentik" - main_test_name = "test_authentik_setup.py" + main_test_name = "test_authentik_dummy.py" diff --git a/src/tests_authentik/test_authentik_dummy.py b/src/tests_authentik/test_authentik_dummy.py new file mode 100644 index 0000000..d20e1cf --- /dev/null +++ b/src/tests_authentik/test_authentik_dummy.py @@ -0,0 +1,2 @@ +def test_true(): + assert 1 + 1 == 2 -- 2.47.2 From 5fea54a604b47bff34b5e6dd7e28edbee3aa0b14 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 15:11:46 +0100 Subject: [PATCH 42/47] add wip wp localization test --- .../test_wordpress_localization.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/tests_wordpress/test_wordpress_localization.py diff --git a/src/tests_wordpress/test_wordpress_localization.py b/src/tests_wordpress/test_wordpress_localization.py new file mode 100644 index 0000000..8fb2c30 --- /dev/null +++ b/src/tests_wordpress/test_wordpress_localization.py @@ -0,0 +1,20 @@ +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") -- 2.47.2 From 387977a54adcdf3f8a60ae57de198e54b3bdbbdb Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 15:23:18 +0100 Subject: [PATCH 43/47] authentic setup is now handled via pytest plugin --- src/conftest.py | 4 +++ src/setup/setup_authentik.py | 47 ++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/setup/setup_authentik.py diff --git a/src/conftest.py b/src/conftest.py index 43b4a2a..86ad681 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -4,6 +4,10 @@ import pytest from dirmanager import DirManager from dotenv import dotenv_values +pytest_plugins = [ + "setup.setup_authentik", +] + def pytest_addoption(parser): parser.addoption( diff --git a/src/setup/setup_authentik.py b/src/setup/setup_authentik.py new file mode 100644 index 0000000..0c7307b --- /dev/null +++ b/src/setup/setup_authentik.py @@ -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() -- 2.47.2 From aa913445f65bdc69a5679942e5f94b0ffabd9ac2 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 15:33:48 +0100 Subject: [PATCH 44/47] add important comment to conftest --- src/conftest.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/conftest.py b/src/conftest.py index 86ad681..5c0bcee 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -1,3 +1,11 @@ +# 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 -- 2.47.2 From ee1acebf3d0d4db69a802b848ee328d160100223 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 15:34:54 +0100 Subject: [PATCH 45/47] add comments --- src/tests_authentik/test_authentik_setup.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/tests_authentik/test_authentik_setup.py b/src/tests_authentik/test_authentik_setup.py index 5aac515..36a22f3 100644 --- a/src/tests_authentik/test_authentik_setup.py +++ b/src/tests_authentik/test_authentik_setup.py @@ -1,4 +1,11 @@ +## 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() @@ -29,10 +36,13 @@ def setup_context(browser, state_file=None): @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() - page.goto(CONFIG["domain"]) + 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)) @@ -163,3 +173,7 @@ def user_session(browser: Browser, dotenv_config, STATES): page.goto(CONFIG["domain"]) yield context, page context.close() + + +def test_true(): + assert 1 + 1 == 2 -- 2.47.2 From 012cd1d36aeb7573107453df3573fbe413e60289 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 21:27:43 +0100 Subject: [PATCH 46/47] change working dir --- Dockerfile | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3f2f854..662da0f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,5 @@ FROM python:3.12-slim -WORKDIR /code - RUN pip install --no-cache-dir pytest-playwright RUN playwright install @@ -10,3 +8,5 @@ RUN playwright install-deps COPY ./requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt + +WORKDIR /code/src \ No newline at end of file diff --git a/README.md b/README.md index 6856534..acf5183 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ```bash docker compose build -docker compose run --rm app python ./src/ +docker compose run --rm app python ./main.py docker compose run --rm app pytest # docker-compose up ``` -- 2.47.2 From 9a7ae2cec633cf7bda99ff2704af935eed6be382 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 21:35:46 +0100 Subject: [PATCH 47/47] add WIP tag --- src/tests_wordpress/test_wordpress_localization.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tests_wordpress/test_wordpress_localization.py b/src/tests_wordpress/test_wordpress_localization.py index 8fb2c30..ca41ffe 100644 --- a/src/tests_wordpress/test_wordpress_localization.py +++ b/src/tests_wordpress/test_wordpress_localization.py @@ -1,3 +1,5 @@ +# WIP localization + from playwright.sync_api import Page, expect -- 2.47.2