From 2919a331915bf1197e266a1467d67f9634148352 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 22:51:20 +0100 Subject: [PATCH 01/69] and new hooks for result logging --- src/conftest.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/conftest.py b/src/conftest.py index 5c0bcee..7e714ee 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -5,18 +5,22 @@ # 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. - +import os.path from pathlib import Path import pytest from dirmanager import DirManager from dotenv import dotenv_values +from playwright.sync_api import BrowserContext pytest_plugins = [ "setup.setup_authentik", ] +TIMEOUT = 5000 + + def pytest_addoption(parser): parser.addoption( "--env_file", @@ -62,3 +66,54 @@ def STATES(dirmanager) -> Path: @pytest.fixture(scope="session", autouse=True) def RESULTS(dirmanager) -> Path: return dirmanager.dirs["results"] + + +# log failed tests + + +@pytest.hookimpl(tryfirst=True, hookwrapper=True) +def pytest_runtest_makereport(item, call): + # execute all other hooks to obtain the report object + outcome = yield + rep = outcome.get_result() + + # we only look at actual failing test calls, not setup/teardown + if rep.when == "call" and rep.failed: + mode = "a" if os.path.exists("failures") else "w" + with open("failures", mode) as f: + f.write(rep.longreprtext + "\n") + + +# trace chrome on failed test + + +# inspired from https://docs.pytest.org/en/6.2.x/example/simple.html#request-example +""" +@pytest.hookimpl(tryfirst=True, hookwrapper=True) +def pytest_runtest_makereport(item, call): + outcome = yield + rep = outcome.get_result() + # set a report attribute for each phase of a call, which can + # be "setup", "call", "teardown" + setattr(item, "rep_" + rep.when, rep) + + +@pytest.fixture +def setup(request, context: BrowserContext): + # before test + context.tracing.start(screenshots=True, snapshots=True, sources=True) + context.set_default_timeout(TIMEOUT) + + # test execution + yield context + + # after test + if request.node.rep_setup.failed: + # test setup failed + pass + else: + if request.node.rep_call.failed: + # test execution failed + filename = "trace-" + request.node.originalname + ".zip" + context.tracing.stop(path=filename) +""" -- 2.47.2 From 3d2f4d939da88dca7ed99aa7e83bf41f0941dbb0 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 23:23:04 +0100 Subject: [PATCH 02/69] make paths absolute --- src/dirmanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dirmanager.py b/src/dirmanager.py index dfc149d..9d10940 100644 --- a/src/dirmanager.py +++ b/src/dirmanager.py @@ -18,7 +18,7 @@ class DirManager: def __init__(self, tests_dir: Path, session_id: str): # root test dir - self.tests_dir = tests_dir + self.tests_dir = tests_dir.resolve() self.session_id = session_id self.dirs = self._get_all_dirs() -- 2.47.2 From c71a4db8cba6ff4bf2461b527f00fc6afcc84f5e Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 23:23:50 +0100 Subject: [PATCH 03/69] add tests dir --- .gitignore | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 4395fbc..0efe464 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ __pycache__/ -*.json -*.zip +tests/ TestResults/ .vscode/ *.pyc +*.json +*.zip credentials* \ No newline at end of file -- 2.47.2 From ab8f068a5e07d75ddd41663e8377719a75235364 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 23:25:16 +0100 Subject: [PATCH 04/69] traceback of failed test gets saved via hook --- src/conftest.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/conftest.py b/src/conftest.py index 7e714ee..f8185d4 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -5,12 +5,12 @@ # 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. -import os.path from pathlib import Path import pytest from dirmanager import DirManager from dotenv import dotenv_values +from icecream import ic from playwright.sync_api import BrowserContext pytest_plugins = [ @@ -79,8 +79,12 @@ def pytest_runtest_makereport(item, call): # we only look at actual failing test calls, not setup/teardown if rep.when == "call" and rep.failed: - mode = "a" if os.path.exists("failures") else "w" - with open("failures", mode) as f: + # saves traceback as .txt for failed test + filename = f"failed-{item.nodeid}.txt" + filename = filename.replace("/", "-") + filename = filename.replace("::", "-") + filepath = item.funcargs["RESULTS"] / filename + with open(filepath, "a") as f: f.write(rep.longreprtext + "\n") -- 2.47.2 From 48d44b945fb62ccceffff8451b009438ffe174ab Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 23:27:06 +0100 Subject: [PATCH 05/69] add dir_manager to Runner --- src/runner.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/runner.py b/src/runner.py index 9e0a09b..ce45ab4 100644 --- a/src/runner.py +++ b/src/runner.py @@ -2,6 +2,7 @@ from pathlib import Path from typing import Callable, Optional, TypedDict import pytest +from dirmanager import DirManager from icecream import ic @@ -19,6 +20,7 @@ class Runner: self.dotenv_path = dotenv_path self.tests_dir = tests_dir self.session_id = session_id + self.dir_manager = DirManager(tests_dir, session_id) ic(f"creating instance of {self.__class__.__name__}") assert self.test_dir_name is not None -- 2.47.2 From 665fd34ddd23cd849c0c289f83fecd3e4b6366f5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Nov 2023 23:31:30 +0100 Subject: [PATCH 06/69] WIP create user session in setup_authentik --- src/setup/setup_authentik.py | 54 +++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/setup/setup_authentik.py b/src/setup/setup_authentik.py index 0c7307b..f3acd9c 100644 --- a/src/setup/setup_authentik.py +++ b/src/setup/setup_authentik.py @@ -20,7 +20,7 @@ def check_for(locator: Locator): @pytest.fixture(scope="session", autouse=True) -def admin_login(browser: Browser, dotenv_config, STATES): +def create_admin_login(browser: Browser, dotenv_config, STATES): # ic(dotenv_config) # go to page @@ -45,3 +45,55 @@ def admin_login(browser: Browser, dotenv_config, STATES): context.storage_state(path=f"{STATES}/admin_state.json") page.close() context.close() + + +def create_invite_link(page): + url = "https://" + dotenv_config["DOMAIN"] + page.goto(url) + 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 + + +def create_user(context, invitelink): + 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") + + +@pytest.fixture(scope="session", autouse=True) +def create_user_session(browser: Browser, admin_login): + admin_context = browser.new_context(storage_state=f"{STATES}/admin_state.json")) + # admin_context = setup_context(browser, f"{STATES}/admin_state.json") + admin_page = admin_context.new_page() + invitelink = create_invite_link(admin_page) + admin_context.tracing.stop(path=f"{RECORDS}/create_invite_link.zip") + admin_context.close() + user_context = setup_context(browser) + create_user(user_context, invitelink) + user_context.tracing.stop(path=f"{RECORDS}/create_user.zip") + user_context.close() -- 2.47.2 From e18e0ea99e04e803a8147008fc7bdeb188e3a018 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 23 Nov 2023 00:38:14 +0100 Subject: [PATCH 07/69] add dependencies for runners --- src/runner.py | 1 + src/tests_wordpress/runner_wordpress.py | 1 + 2 files changed, 2 insertions(+) diff --git a/src/runner.py b/src/runner.py index ce45ab4..c048d33 100644 --- a/src/runner.py +++ b/src/runner.py @@ -15,6 +15,7 @@ class Runner: test_dir_name: Optional[Path] = None main_test_name: Optional[str] = None sub_tests: list[SubTest] = [] + dependencies: list[str] = [] def __init__(self, dotenv_path: Path, tests_dir: Path, session_id: str): self.dotenv_path = dotenv_path diff --git a/src/tests_wordpress/runner_wordpress.py b/src/tests_wordpress/runner_wordpress.py index 3d60aa2..00c7642 100644 --- a/src/tests_wordpress/runner_wordpress.py +++ b/src/tests_wordpress/runner_wordpress.py @@ -18,3 +18,4 @@ class RunnerWordpress(Runner): SubTest(condition=condition_always_false, test_file="test_wordpress_feature1.py"), SubTest(condition=condition_always_true, test_file="conftest.py"), ] + dependencies: list[str] = ["authentik"] -- 2.47.2 From cc2bb37b64338ecdee96318a18dff31527e4cbb3 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 23 Nov 2023 00:41:02 +0100 Subject: [PATCH 08/69] wip files --- src/tests_authentik/authentik_plugin.py | 3 +++ src/tests_authentik/authentik_setup_nonpytest.py | 1 + 2 files changed, 4 insertions(+) create mode 100644 src/tests_authentik/authentik_plugin.py create mode 100644 src/tests_authentik/authentik_setup_nonpytest.py diff --git a/src/tests_authentik/authentik_plugin.py b/src/tests_authentik/authentik_plugin.py new file mode 100644 index 0000000..14f31c9 --- /dev/null +++ b/src/tests_authentik/authentik_plugin.py @@ -0,0 +1,3 @@ +# will be loaded in conftest.py +# will provide context for other tests (wordpress etc.) +# that depend on authentik (which is all of them) diff --git a/src/tests_authentik/authentik_setup_nonpytest.py b/src/tests_authentik/authentik_setup_nonpytest.py new file mode 100644 index 0000000..a4d8b5c --- /dev/null +++ b/src/tests_authentik/authentik_setup_nonpytest.py @@ -0,0 +1 @@ +# put authentik setup here and run at beginning of runner -- 2.47.2 From 1181c26595fb98399860519e97a8eb70209e7563 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 23 Nov 2023 00:42:15 +0100 Subject: [PATCH 09/69] load authentik_plugin in conftest --- src/conftest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/conftest.py b/src/conftest.py index f8185d4..b80840e 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -14,7 +14,8 @@ from icecream import ic from playwright.sync_api import BrowserContext pytest_plugins = [ - "setup.setup_authentik", + # "setup.setup_authentik", + "tests_authentik.authentik_plugin" ] -- 2.47.2 From fbe9e323ecd2fa200e39820127d5fb98f25cefe0 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 23 Nov 2023 00:43:02 +0100 Subject: [PATCH 10/69] add failing test --- src/tests_authentik/test_authentik_dummy.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tests_authentik/test_authentik_dummy.py b/src/tests_authentik/test_authentik_dummy.py index d20e1cf..bddb561 100644 --- a/src/tests_authentik/test_authentik_dummy.py +++ b/src/tests_authentik/test_authentik_dummy.py @@ -1,2 +1,6 @@ def test_true(): assert 1 + 1 == 2 + + +def test_not_true(): + assert 1 + 1 == 3 -- 2.47.2 From 2e4d0247cf9c70e5145a4089f55136f7b63ab99b Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 23 Nov 2023 00:47:28 +0100 Subject: [PATCH 11/69] add run_setup --- src/main.py | 16 +++++++++------- src/runner.py | 3 +++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main.py b/src/main.py index 3100621..a05431b 100644 --- a/src/main.py +++ b/src/main.py @@ -20,6 +20,9 @@ class TestRunner(Protocol): def __init__(self, dotenv_path: Path, tests_dir: Path, session_id: str): ... + def run_setup(self): + ... + def run_tests(self): ... @@ -42,10 +45,13 @@ class Wrapper: 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) + self.runners: list[TestRunner] = self._load_runners(self.env_files) + for runner in self.runners: + runner.run_setup() + for runner in self.runners: + runner.run_tests() - def load_runners(self, env_files: list[Path]) -> list[TestRunner]: + 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) @@ -53,10 +59,6 @@ class Wrapper: 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""" diff --git a/src/runner.py b/src/runner.py index c048d33..fc7b40f 100644 --- a/src/runner.py +++ b/src/runner.py @@ -55,6 +55,9 @@ class Runner: def show_files(self): ic(list(self.root_dir.glob("*"))) + def run_setup(self): + pass + def run_tests(self): self._run_main_test() for sub_test in self.sub_tests: -- 2.47.2 From 702791e5cdf60d92577b62c90ce8a354ee6dcccb Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 10:27:54 +0100 Subject: [PATCH 12/69] runner is executed authentik setup als function, without pytest --- src/runner.py | 2 + .../setup_authentik.py | 2 + .../authentik_setup_nonpytest.py | 1 - ...uthentik_plugin.py => plugin_authentik.py} | 0 src/tests_authentik/runner_authentik.py | 5 + src/tests_authentik/setup_authentik.py | 102 ++++++++++++++++++ ...thentik_setup.py => test_authentik_old.py} | 0 7 files changed, 111 insertions(+), 1 deletion(-) rename src/{setup => setup-deprecated}/setup_authentik.py (98%) delete mode 100644 src/tests_authentik/authentik_setup_nonpytest.py rename src/tests_authentik/{authentik_plugin.py => plugin_authentik.py} (100%) create mode 100644 src/tests_authentik/setup_authentik.py rename src/tests_authentik/{test_authentik_setup.py => test_authentik_old.py} (100%) diff --git a/src/runner.py b/src/runner.py index fc7b40f..b987e60 100644 --- a/src/runner.py +++ b/src/runner.py @@ -3,6 +3,7 @@ from typing import Callable, Optional, TypedDict import pytest from dirmanager import DirManager +from dotenv import dotenv_values from icecream import ic @@ -19,6 +20,7 @@ class Runner: def __init__(self, dotenv_path: Path, tests_dir: Path, session_id: str): self.dotenv_path = dotenv_path + self.config: dict[str, str] = dotenv_values(dotenv_path) self.tests_dir = tests_dir self.session_id = session_id self.dir_manager = DirManager(tests_dir, session_id) diff --git a/src/setup/setup_authentik.py b/src/setup-deprecated/setup_authentik.py similarity index 98% rename from src/setup/setup_authentik.py rename to src/setup-deprecated/setup_authentik.py index f3acd9c..acb7e1b 100644 --- a/src/setup/setup_authentik.py +++ b/src/setup-deprecated/setup_authentik.py @@ -19,6 +19,8 @@ def check_for(locator: Locator): expect(locator).to_be_visible(timeout=TIMEOUT) + +# todo: why is this a fixture? to get dotenv_config @pytest.fixture(scope="session", autouse=True) def create_admin_login(browser: Browser, dotenv_config, STATES): # ic(dotenv_config) diff --git a/src/tests_authentik/authentik_setup_nonpytest.py b/src/tests_authentik/authentik_setup_nonpytest.py deleted file mode 100644 index a4d8b5c..0000000 --- a/src/tests_authentik/authentik_setup_nonpytest.py +++ /dev/null @@ -1 +0,0 @@ -# put authentik setup here and run at beginning of runner diff --git a/src/tests_authentik/authentik_plugin.py b/src/tests_authentik/plugin_authentik.py similarity index 100% rename from src/tests_authentik/authentik_plugin.py rename to src/tests_authentik/plugin_authentik.py diff --git a/src/tests_authentik/runner_authentik.py b/src/tests_authentik/runner_authentik.py index 1ef97b8..13292b3 100644 --- a/src/tests_authentik/runner_authentik.py +++ b/src/tests_authentik/runner_authentik.py @@ -1,6 +1,7 @@ from pathlib import Path from runner import Runner, SubTest +from tests_authentik.setup_authentik import setup_authentik def condition_always_true(dotenv_path: Path) -> bool: @@ -14,3 +15,7 @@ def condition_always_false(dotenv_path: Path) -> bool: class RunnerAuthentik(Runner): test_dir_name = "tests_authentik" main_test_name = "test_authentik_dummy.py" + + def run_setup(self): + # run setup + setup_authentik(self.config, self.dir_manager.dirs["states"]) diff --git a/src/tests_authentik/setup_authentik.py b/src/tests_authentik/setup_authentik.py new file mode 100644 index 0000000..986a9e1 --- /dev/null +++ b/src/tests_authentik/setup_authentik.py @@ -0,0 +1,102 @@ +# put authentik setup here and run at beginning of runner +import json +from pathlib import Path + +from icecream import ic +from playwright.sync_api import BrowserContext, Locator, Page, expect, sync_playwright + +cred_file = Path("../credentials.json") +with open(cred_file, "r") as f: + CREDENTIALS = json.load(f) + +ic("RUNNING SETUP") +ic(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) + + +def setup_authentik(dotenv_config: dict[str, str], STATES: Path): + with sync_playwright() as p: + browser = p.chromium.launch() + admin_context = browser.new_context() + admin_context.set_default_timeout(TIMEOUT) + user_context = browser.new_context() + user_context.set_default_timeout(TIMEOUT) + create_admin_login(admin_context, dotenv_config, STATES) + create_user_session(admin_context, user_context, STATES) + + +def create_admin_login(context: BrowserContext, dotenv_config: dict[str, str], STATES: Path): + # go to page + 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() + + +def create_invite_link(page: Page, dotenv_config: dict[str, str]): + url = "https://" + dotenv_config["DOMAIN"] + page.goto(url) + 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 + + +def create_user(context: BrowserContext, invitelink, STATES: Path): + 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") + + +def create_user_session(admin_context: BrowserContext, user_context: BrowserContext, STATES: Path): + # admin_context = browser.new_context(storage_state=f"{STATES}/admin_state.json")) + # admin_context = setup_context(browser, f"{STATES}/admin_state.json") + admin_page = admin_context.new_page() + invitelink = create_invite_link(admin_page) + + # create user + create_user(user_context, invitelink, STATES) diff --git a/src/tests_authentik/test_authentik_setup.py b/src/tests_authentik/test_authentik_old.py similarity index 100% rename from src/tests_authentik/test_authentik_setup.py rename to src/tests_authentik/test_authentik_old.py -- 2.47.2 From ffd9f29b7a72a40ef01f83a388b3397fedb759e6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 10:35:42 +0100 Subject: [PATCH 13/69] change interface or dirmanager --- src/dirmanager.py | 37 ++++++++++++++----------- src/tests_authentik/runner_authentik.py | 2 +- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/dirmanager.py b/src/dirmanager.py index 9d10940..8b8c16b 100644 --- a/src/dirmanager.py +++ b/src/dirmanager.py @@ -18,27 +18,32 @@ class DirManager: def __init__(self, tests_dir: Path, session_id: str): # root test dir - self.tests_dir = tests_dir.resolve() + self._tests_dir = tests_dir.resolve() 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) + self.create_dirs(self._tests_dir, exist_ok=True) + self.create_dirs([self.SESSION, self.RECORDS, self.STATES, self.RESULTS]) - 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 + @property + def TESTS(self): + return self._tests_dir - def _get_subdirs(self, session_dir: Path): - return { - "records": session_dir / Path("records"), - "states": session_dir / Path("states"), - "results": session_dir / Path("results"), - } + @property + def SESSION(self): + return self._tests_dir / f"test-{self.session_id}" + + @property + def RECORDS(self): + return self.SESSION / Path("records") + + @property + def STATES(self): + return self.SESSION / Path("states") + + @property + def RESULTS(self): + return self.SESSION / Path("results") @staticmethod def create_dirs(dirs: Path | list[Path] | dict[str, Path], exist_ok=False): diff --git a/src/tests_authentik/runner_authentik.py b/src/tests_authentik/runner_authentik.py index 13292b3..efd82fe 100644 --- a/src/tests_authentik/runner_authentik.py +++ b/src/tests_authentik/runner_authentik.py @@ -18,4 +18,4 @@ class RunnerAuthentik(Runner): def run_setup(self): # run setup - setup_authentik(self.config, self.dir_manager.dirs["states"]) + setup_authentik(self.config, self.dir_manager.STATES) -- 2.47.2 From 9d13e9a4aa81b2fb4ac9af98d738c4d15dfbf3b4 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 10:36:14 +0100 Subject: [PATCH 14/69] use new interface --- src/conftest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/conftest.py b/src/conftest.py index b80840e..03035a0 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -56,17 +56,17 @@ def dotenv_config(request) -> dict[str, str]: @pytest.fixture(scope="session", autouse=True) def RECORDS(dirmanager) -> Path: assert isinstance(dirmanager, DirManager) - return dirmanager.dirs["records"] + return dirmanager.RECORDS @pytest.fixture(scope="session", autouse=True) def STATES(dirmanager) -> Path: - return dirmanager.dirs["states"] + return dirmanager.STATES @pytest.fixture(scope="session", autouse=True) def RESULTS(dirmanager) -> Path: - return dirmanager.dirs["results"] + return dirmanager.RESULTS # log failed tests -- 2.47.2 From e64e5e4bc9dd4fdd6ec48427b2a3d1bd570e0826 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 11:01:04 +0100 Subject: [PATCH 15/69] setup_authentik almost wrunning --- src/tests_authentik/setup_authentik.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/tests_authentik/setup_authentik.py b/src/tests_authentik/setup_authentik.py index 986a9e1..26eab78 100644 --- a/src/tests_authentik/setup_authentik.py +++ b/src/tests_authentik/setup_authentik.py @@ -21,6 +21,8 @@ def check_for(locator: Locator): def setup_authentik(dotenv_config: dict[str, str], STATES: Path): + """run directly as function from RUNNER""" + with sync_playwright() as p: browser = p.chromium.launch() admin_context = browser.new_context() @@ -28,7 +30,7 @@ def setup_authentik(dotenv_config: dict[str, str], STATES: Path): user_context = browser.new_context() user_context.set_default_timeout(TIMEOUT) create_admin_login(admin_context, dotenv_config, STATES) - create_user_session(admin_context, user_context, STATES) + create_user_session(admin_context, user_context, dotenv_config, STATES) def create_admin_login(context: BrowserContext, dotenv_config: dict[str, str], STATES: Path): @@ -51,10 +53,11 @@ def create_admin_login(context: BrowserContext, dotenv_config: dict[str, str], S # save state context.storage_state(path=f"{STATES}/admin_state.json") page.close() - context.close() + # context.close() -def create_invite_link(page: Page, dotenv_config: dict[str, str]): +def create_invite_link(admin_context: BrowserContext, dotenv_config: dict[str, str]): + page = admin_context.new_page() url = "https://" + dotenv_config["DOMAIN"] page.goto(url) page.get_by_role("link", name="Admin Interface").click() @@ -75,6 +78,7 @@ def create_invite_link(page: Page, dotenv_config: dict[str, str]): def create_user(context: BrowserContext, invitelink, STATES: Path): + # warning: only works on german site page = context.new_page() page.goto(invitelink) page.get_by_placeholder("Benutzername").click() @@ -92,11 +96,10 @@ def create_user(context: BrowserContext, invitelink, STATES: Path): context.storage_state(path=f"{STATES}/user_state.json") -def create_user_session(admin_context: BrowserContext, user_context: BrowserContext, STATES: Path): - # admin_context = browser.new_context(storage_state=f"{STATES}/admin_state.json")) - # admin_context = setup_context(browser, f"{STATES}/admin_state.json") - admin_page = admin_context.new_page() - invitelink = create_invite_link(admin_page) +def create_user_session( + admin_context: BrowserContext, user_context: BrowserContext, dotenv_config: dict[str, str], STATES: Path +): + invitelink = create_invite_link(admin_context, dotenv_config) # create user create_user(user_context, invitelink, STATES) -- 2.47.2 From 7c4d853985da5fe772ce6e0efc52784e4709017f Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 11:03:41 +0100 Subject: [PATCH 16/69] remove demo env file --- src/blog.dev.local-it.cloud.env | 71 --------------------------------- 1 file changed, 71 deletions(-) delete mode 100644 src/blog.dev.local-it.cloud.env diff --git a/src/blog.dev.local-it.cloud.env b/src/blog.dev.local-it.cloud.env deleted file mode 100644 index 600aaf6..0000000 --- a/src/blog.dev.local-it.cloud.env +++ /dev/null @@ -1,71 +0,0 @@ -################################################################################ -# 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 -- 2.47.2 From a4bdb2e8680c0b6460696377c2073f813200ec6c Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 11:23:40 +0100 Subject: [PATCH 17/69] add test_setup_authentik --- src/tests_authentik/setup_authentik.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/tests_authentik/setup_authentik.py b/src/tests_authentik/setup_authentik.py index 26eab78..4691f2c 100644 --- a/src/tests_authentik/setup_authentik.py +++ b/src/tests_authentik/setup_authentik.py @@ -3,7 +3,7 @@ import json from pathlib import Path from icecream import ic -from playwright.sync_api import BrowserContext, Locator, Page, expect, sync_playwright +from playwright.sync_api import Browser, BrowserContext, Locator, Page, expect, sync_playwright cred_file = Path("../credentials.json") with open(cred_file, "r") as f: @@ -20,6 +20,17 @@ def check_for(locator: Locator): expect(locator).to_be_visible(timeout=TIMEOUT) +def test_setup_authentik(browser): # WIP + """run with pytest""" + + admin_context = browser.new_context() + admin_context.set_default_timeout(TIMEOUT) + user_context = browser.new_context() + user_context.set_default_timeout(TIMEOUT) + create_admin_login(admin_context, dotenv_config, STATES) + create_user_session(admin_context, user_context, dotenv_config, STATES) + + def setup_authentik(dotenv_config: dict[str, str], STATES: Path): """run directly as function from RUNNER""" -- 2.47.2 From 2cb4a9d526aeb598301b24088ae1d5c6aa623b75 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 11:30:52 +0100 Subject: [PATCH 18/69] Easier setup by executing main from root working directory --- main.py | 6 ++++++ src/conftest.py | 3 ++- src/runner.py | 5 +++-- src/setup-deprecated/setup_authentik.py | 2 +- src/tests_authentik/runner_authentik.py | 4 ++-- src/tests_authentik/setup_authentik.py | 2 +- src/tests_wordpress/conftest.py | 2 +- src/tests_wordpress/runner_wordpress.py | 2 +- src/{main.py => wrapper.py} | 18 ++++++------------ 9 files changed, 23 insertions(+), 21 deletions(-) create mode 100644 main.py rename src/{main.py => wrapper.py} (80%) diff --git a/main.py b/main.py new file mode 100644 index 0000000..4b0289a --- /dev/null +++ b/main.py @@ -0,0 +1,6 @@ +from src.wrapper import ENV_FILES, Wrapper + +session_id = Wrapper.get_session_id() +wrapper = Wrapper(ENV_FILES, session_id=session_id) +wrapper.setup_test() +wrapper.run_test() diff --git a/src/conftest.py b/src/conftest.py index 03035a0..55b819f 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -8,11 +8,12 @@ from pathlib import Path import pytest -from dirmanager import DirManager from dotenv import dotenv_values from icecream import ic from playwright.sync_api import BrowserContext +from src.dirmanager import DirManager + pytest_plugins = [ # "setup.setup_authentik", "tests_authentik.authentik_plugin" diff --git a/src/runner.py b/src/runner.py index b987e60..07c8848 100644 --- a/src/runner.py +++ b/src/runner.py @@ -2,10 +2,11 @@ from pathlib import Path from typing import Callable, Optional, TypedDict import pytest -from dirmanager import DirManager from dotenv import dotenv_values from icecream import ic +from src.dirmanager import DirManager + class SubTest(TypedDict): condition: Callable[[Path], bool] @@ -13,7 +14,7 @@ class SubTest(TypedDict): class Runner: - test_dir_name: Optional[Path] = None + test_dir_name: Optional[str] = None main_test_name: Optional[str] = None sub_tests: list[SubTest] = [] dependencies: list[str] = [] diff --git a/src/setup-deprecated/setup_authentik.py b/src/setup-deprecated/setup_authentik.py index acb7e1b..3166063 100644 --- a/src/setup-deprecated/setup_authentik.py +++ b/src/setup-deprecated/setup_authentik.py @@ -5,7 +5,7 @@ import pytest from icecream import ic from playwright.sync_api import Browser, Locator, expect -cred_file = Path("../credentials.json") +cred_file = Path("credentials.json") with open(cred_file, "r") as f: CREDENTIALS = json.load(f) diff --git a/src/tests_authentik/runner_authentik.py b/src/tests_authentik/runner_authentik.py index efd82fe..bf49077 100644 --- a/src/tests_authentik/runner_authentik.py +++ b/src/tests_authentik/runner_authentik.py @@ -1,7 +1,7 @@ from pathlib import Path -from runner import Runner, SubTest -from tests_authentik.setup_authentik import setup_authentik +from src.runner import Runner, SubTest +from src.tests_authentik.setup_authentik import setup_authentik def condition_always_true(dotenv_path: Path) -> bool: diff --git a/src/tests_authentik/setup_authentik.py b/src/tests_authentik/setup_authentik.py index 4691f2c..c88d221 100644 --- a/src/tests_authentik/setup_authentik.py +++ b/src/tests_authentik/setup_authentik.py @@ -5,7 +5,7 @@ from pathlib import Path from icecream import ic from playwright.sync_api import Browser, BrowserContext, Locator, Page, expect, sync_playwright -cred_file = Path("../credentials.json") +cred_file = Path("credentials.json") with open(cred_file, "r") as f: CREDENTIALS = json.load(f) diff --git a/src/tests_wordpress/conftest.py b/src/tests_wordpress/conftest.py index d78bb85..0dd75fc 100644 --- a/src/tests_wordpress/conftest.py +++ b/src/tests_wordpress/conftest.py @@ -12,7 +12,7 @@ from playwright.sync_api import Browser, Locator, expect, sync_playwright # browser = playwright.chromium.launch(headless=False) -cred_file = Path("../credentials.json") +cred_file = Path("credentials.json") with open(cred_file, "r") as f: CREDENTIALS = json.load(f) diff --git a/src/tests_wordpress/runner_wordpress.py b/src/tests_wordpress/runner_wordpress.py index 00c7642..eddccfa 100644 --- a/src/tests_wordpress/runner_wordpress.py +++ b/src/tests_wordpress/runner_wordpress.py @@ -1,6 +1,6 @@ from pathlib import Path -from runner import Runner, SubTest +from src.runner import Runner, SubTest def condition_always_true(dotenv_path: Path) -> bool: diff --git a/src/main.py b/src/wrapper.py similarity index 80% rename from src/main.py rename to src/wrapper.py index a05431b..b524969 100644 --- a/src/main.py +++ b/src/wrapper.py @@ -2,17 +2,18 @@ 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 + +from src.dirmanager import DirManager +from src.tests_authentik.runner_authentik import RunnerAuthentik +from src.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 + Path("envfiles/login.test.dev.local-it.cloud.env"), # authentik + # Path("envfiles/blog.test.dev.local-it.cloud.env"), # wordpress ] @@ -69,10 +70,3 @@ class Wrapper: 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() -- 2.47.2 From 660c5fd712bd1e8def6e939ac662a7b52fd6551f Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 11:41:46 +0100 Subject: [PATCH 19/69] remove plugins from conftest, breaks test discovery --- src/conftest.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/conftest.py b/src/conftest.py index 55b819f..b507141 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -14,10 +14,10 @@ from playwright.sync_api import BrowserContext from src.dirmanager import DirManager -pytest_plugins = [ - # "setup.setup_authentik", - "tests_authentik.authentik_plugin" -] +# pytest_plugins = [ +# "setup.setup_authentik", +# "src.tests_authentik.authentik_plugin" +# ] TIMEOUT = 5000 -- 2.47.2 From bbea3e0970e4c228251e5cc2b3c02ddee1b6a15e Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 11:43:47 +0100 Subject: [PATCH 20/69] __init__.py so src dir is recognized as module --- src/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/__init__.py diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 -- 2.47.2 From 6605287a33a08f735f626909b37298513c263d57 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 11:53:31 +0100 Subject: [PATCH 21/69] rename so that discovered test file doesnt exist twice --- ...ik.py => deprecated-setup_authentik-deprecated.py} | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) rename src/setup-deprecated/{setup_authentik.py => deprecated-setup_authentik-deprecated.py} (93%) diff --git a/src/setup-deprecated/setup_authentik.py b/src/setup-deprecated/deprecated-setup_authentik-deprecated.py similarity index 93% rename from src/setup-deprecated/setup_authentik.py rename to src/setup-deprecated/deprecated-setup_authentik-deprecated.py index 3166063..4ac4fd5 100644 --- a/src/setup-deprecated/setup_authentik.py +++ b/src/setup-deprecated/deprecated-setup_authentik-deprecated.py @@ -19,7 +19,6 @@ def check_for(locator: Locator): expect(locator).to_be_visible(timeout=TIMEOUT) - # todo: why is this a fixture? to get dotenv_config @pytest.fixture(scope="session", autouse=True) def create_admin_login(browser: Browser, dotenv_config, STATES): @@ -74,10 +73,10 @@ def create_user(context, invitelink): 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.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() @@ -89,7 +88,7 @@ def create_user(context, invitelink): @pytest.fixture(scope="session", autouse=True) def create_user_session(browser: Browser, admin_login): - admin_context = browser.new_context(storage_state=f"{STATES}/admin_state.json")) + admin_context = browser.new_context(storage_state=f"{STATES}/admin_state.json") # admin_context = setup_context(browser, f"{STATES}/admin_state.json") admin_page = admin_context.new_page() invitelink = create_invite_link(admin_page) -- 2.47.2 From aece37a6046bc3659207f90ed5d551e34e99f61c Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 11:54:38 +0100 Subject: [PATCH 22/69] pytest: discover files with name setup*.py --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 4a80e86..e9cc623 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,3 +17,6 @@ package-dir = {"" = "src"} [tool.ruff] line-length = 120 target-version = "py311" + +[tool.pytest.ini_options] +python_files = test_*.py setup*.py \ No newline at end of file -- 2.47.2 From bc9d0a3f47ee2d2ab6f50b08713123fab5ea60de Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 11:57:29 +0100 Subject: [PATCH 23/69] fix formatting --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e9cc623..d6f9e14 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,4 +19,4 @@ line-length = 120 target-version = "py311" [tool.pytest.ini_options] -python_files = test_*.py setup*.py \ No newline at end of file +python_files = "test_*.py setup*.py" \ No newline at end of file -- 2.47.2 From 23b81317c51a723210ba301b741b18b93fae3393 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 12:11:34 +0100 Subject: [PATCH 24/69] move get_session_id to utils --- main.py | 3 ++- src/utils.py | 7 +++++++ src/wrapper.py | 5 ----- 3 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 src/utils.py diff --git a/main.py b/main.py index 4b0289a..5942916 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,7 @@ +from src.utils import get_session_id from src.wrapper import ENV_FILES, Wrapper -session_id = Wrapper.get_session_id() +session_id = get_session_id() wrapper = Wrapper(ENV_FILES, session_id=session_id) wrapper.setup_test() wrapper.run_test() diff --git a/src/utils.py b/src/utils.py new file mode 100644 index 0000000..3f96269 --- /dev/null +++ b/src/utils.py @@ -0,0 +1,7 @@ +from datetime import datetime + + +@staticmethod +def get_session_id() -> str: + current_datetime = datetime.now() + return current_datetime.strftime("%Y-%m-%d-%H-%M-%S") diff --git a/src/wrapper.py b/src/wrapper.py index b524969..128e74f 100644 --- a/src/wrapper.py +++ b/src/wrapper.py @@ -65,8 +65,3 @@ class Wrapper: """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") -- 2.47.2 From befad97cc10be3d336518c4d25d41739ba1de433 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 12:22:45 +0100 Subject: [PATCH 25/69] add asserts to pytest command line arguments --- src/conftest.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/conftest.py b/src/conftest.py index b507141..6f46277 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -41,14 +41,17 @@ def pytest_addoption(parser): @pytest.fixture(scope="session", autouse=True) def dirmanager(request) -> DirManager: tests_dir = request.config.getoption("--tests_dir") + assert tests_dir is not None, "required pytest command line argument not given" tests_dir = Path(tests_dir) session_id = request.config.getoption("--session_id") + assert session_id is not None, "required pytest command line argument not given" 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") + assert dotenv_path is not None, "required pytest command line argument not given" dotenv_path = Path(dotenv_path) assert dotenv_path.is_file() return dotenv_values(dotenv_path) -- 2.47.2 From 826e4001502ec12549d468d612676ff0b287f956 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 14:07:29 +0100 Subject: [PATCH 26/69] add main setup --- src/runner.py | 8 ++++++-- src/tests_authentik/runner_authentik.py | 7 +++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/runner.py b/src/runner.py index 07c8848..c697379 100644 --- a/src/runner.py +++ b/src/runner.py @@ -15,6 +15,7 @@ class SubTest(TypedDict): class Runner: test_dir_name: Optional[str] = None + main_setup_name: Optional[str] = None main_test_name: Optional[str] = None sub_tests: list[SubTest] = [] dependencies: list[str] = [] @@ -31,9 +32,12 @@ class Runner: self.root_dir = Path(__file__).parent def _run_main_test(self): + if isinstance(self.main_setup_name, str): + full_path = self.root_dir / self.test_dir_name / self.main_setup_name + self._run_pytest(full_path) 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) + full_path = self.root_dir / self.test_dir_name / self.main_test_name + self._run_pytest(full_path) def _run_pytest(self, full_test_path: Path): """runs pytest programmatically diff --git a/src/tests_authentik/runner_authentik.py b/src/tests_authentik/runner_authentik.py index bf49077..8ad5eca 100644 --- a/src/tests_authentik/runner_authentik.py +++ b/src/tests_authentik/runner_authentik.py @@ -1,7 +1,8 @@ from pathlib import Path from src.runner import Runner, SubTest -from src.tests_authentik.setup_authentik import setup_authentik + +# from src.tests_authentik.setup_authentik import setup_authentik def condition_always_true(dotenv_path: Path) -> bool: @@ -14,8 +15,10 @@ def condition_always_false(dotenv_path: Path) -> bool: class RunnerAuthentik(Runner): test_dir_name = "tests_authentik" + main_setup_name = "setup_authentik.py" main_test_name = "test_authentik_dummy.py" def run_setup(self): # run setup - setup_authentik(self.config, self.dir_manager.STATES) + # setup_authentik(self.config, self.dir_manager.STATES) + pass -- 2.47.2 From dfe63687a34f3a101a24616aa718a009fabcb2e7 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 14:15:25 +0100 Subject: [PATCH 27/69] make tests dir absolut --- src/wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wrapper.py b/src/wrapper.py index 128e74f..9274b6a 100644 --- a/src/wrapper.py +++ b/src/wrapper.py @@ -9,7 +9,7 @@ from src.dirmanager import DirManager from src.tests_authentik.runner_authentik import RunnerAuthentik from src.tests_wordpress.runner_wordpress import RunnerWordpress -TESTS_DIR = Path("../tests") +TESTS_DIR = Path("../tests").resolve() ENV_FILES = [ Path("envfiles/login.test.dev.local-it.cloud.env"), # authentik -- 2.47.2 From d6b4feb9b64d28ba348725cdb4dbf1c180066a82 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 14:15:46 +0100 Subject: [PATCH 28/69] make authentic setup a test a gain --- src/tests_authentik/setup_authentik.py | 56 +++++++------------------- 1 file changed, 14 insertions(+), 42 deletions(-) diff --git a/src/tests_authentik/setup_authentik.py b/src/tests_authentik/setup_authentik.py index c88d221..66a9070 100644 --- a/src/tests_authentik/setup_authentik.py +++ b/src/tests_authentik/setup_authentik.py @@ -1,9 +1,8 @@ -# put authentik setup here and run at beginning of runner import json from pathlib import Path from icecream import ic -from playwright.sync_api import Browser, BrowserContext, Locator, Page, expect, sync_playwright +from playwright.sync_api import Browser, BrowserContext, expect cred_file = Path("credentials.json") with open(cred_file, "r") as f: @@ -16,35 +15,7 @@ TESTUSER = {"username": "testuser", "name": "Test User", "password": "test123", TIMEOUT = 5000 -def check_for(locator: Locator): - expect(locator).to_be_visible(timeout=TIMEOUT) - - -def test_setup_authentik(browser): # WIP - """run with pytest""" - - admin_context = browser.new_context() - admin_context.set_default_timeout(TIMEOUT) - user_context = browser.new_context() - user_context.set_default_timeout(TIMEOUT) - create_admin_login(admin_context, dotenv_config, STATES) - create_user_session(admin_context, user_context, dotenv_config, STATES) - - -def setup_authentik(dotenv_config: dict[str, str], STATES: Path): - """run directly as function from RUNNER""" - - with sync_playwright() as p: - browser = p.chromium.launch() - admin_context = browser.new_context() - admin_context.set_default_timeout(TIMEOUT) - user_context = browser.new_context() - user_context.set_default_timeout(TIMEOUT) - create_admin_login(admin_context, dotenv_config, STATES) - create_user_session(admin_context, user_context, dotenv_config, STATES) - - -def create_admin_login(context: BrowserContext, dotenv_config: dict[str, str], STATES: Path): +def test_create_admin_login(context: BrowserContext, dotenv_config: dict[str, str], STATES: Path): # go to page page = context.new_page() url = "https://" + dotenv_config["DOMAIN"] @@ -53,18 +24,16 @@ def create_admin_login(context: BrowserContext, dotenv_config: dict[str, str], S # check welcome message welcome_message = dotenv_config.get("welcome_message") if welcome_message: - check_for(page.get_by_text(welcome_message)) + expect(page.get_by_text(welcome_message)).to_be_visible(timeout=TIMEOUT) # 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")) + expect(page.locator("ak-library")).to_be_visible(timeout=TIMEOUT) # save state context.storage_state(path=f"{STATES}/admin_state.json") - page.close() - # context.close() def create_invite_link(admin_context: BrowserContext, dotenv_config: dict[str, str]): @@ -103,14 +72,17 @@ def create_user(context: BrowserContext, invitelink, STATES: Path): 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") + expect(page.locator("ak-library")).to_be_visible() -def create_user_session( - admin_context: BrowserContext, user_context: BrowserContext, dotenv_config: dict[str, str], STATES: Path -): - invitelink = create_invite_link(admin_context, dotenv_config) +def test_create_user_session(browser: Browser, dotenv_config: dict[str, str], STATES: Path): + # create invite_link + admin_context = browser.new_context(storage_state=f"{STATES}/admin_state.json") + admin_context.set_default_timeout(TIMEOUT) + invite_link = create_invite_link(admin_context, dotenv_config) # create user - create_user(user_context, invitelink, STATES) + user_context = browser.new_context() + user_context.set_default_timeout(TIMEOUT) + create_user(user_context, invite_link, STATES) + user_context.storage_state(path=f"{STATES}/user_state.json") -- 2.47.2 From d6f90b1d648ac96626f0bba007b95d8d81e0e0ac Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 14:16:22 +0100 Subject: [PATCH 29/69] simplify dir creation --- src/conftest.py | 4 +++- src/dirmanager.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/conftest.py b/src/conftest.py index 6f46277..992f497 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -45,7 +45,9 @@ def dirmanager(request) -> DirManager: tests_dir = Path(tests_dir) session_id = request.config.getoption("--session_id") assert session_id is not None, "required pytest command line argument not given" - return DirManager(tests_dir=tests_dir, session_id=session_id) + dirmanager = DirManager(tests_dir=tests_dir, session_id=session_id) + dirmanager.create_all_dirs() + return dirmanager @pytest.fixture(scope="session", autouse=True) diff --git a/src/dirmanager.py b/src/dirmanager.py index 8b8c16b..20c13fc 100644 --- a/src/dirmanager.py +++ b/src/dirmanager.py @@ -23,7 +23,7 @@ class DirManager: def create_all_dirs(self): self.create_dirs(self._tests_dir, exist_ok=True) - self.create_dirs([self.SESSION, self.RECORDS, self.STATES, self.RESULTS]) + self.create_dirs([self.SESSION, self.RECORDS, self.STATES, self.RESULTS], exist_ok=True) @property def TESTS(self): -- 2.47.2 From 32d61cd297d93e48d5d3d0ceaa4f18da6e405a6b Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 14:37:49 +0100 Subject: [PATCH 30/69] create pytest arguments dynamically --- src/runner.py | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/runner.py b/src/runner.py index c697379..75e3597 100644 --- a/src/runner.py +++ b/src/runner.py @@ -45,19 +45,39 @@ class Runner: 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, - ] - ) + + command_arguments = [] + + command_arguments.append("-v") + command_arguments.append("-rp") + command_arguments.append(str(full_test_path)) + + command_arguments.append("--env_file") + command_arguments.append(str(self.dotenv_path)) + + command_arguments.append("--tests_dir") + command_arguments.append(str(self.tests_dir)) + + command_arguments.append("--session_id") + command_arguments.append(self.session_id) + + # artifacts dir + output = self.dir_manager.RESULTS / full_test_path.stem + command_arguments.append("--output") + command_arguments.append(str(output)) + + # tracing + command_arguments.append("--tracing") + command_arguments.append("on") + + # disable capturing + # pytest -s # disable all capturing + command_arguments.append("-s") + + # headed + command_arguments.append("--headed") + + pytest.main(command_arguments) def show_files(self): ic(list(self.root_dir.glob("*"))) -- 2.47.2 From 8e240d3f5039b0f1206df0a49fe2c95bcba75848 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 14:40:51 +0100 Subject: [PATCH 31/69] FIX TESTS_DIR --- src/wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wrapper.py b/src/wrapper.py index 9274b6a..700a452 100644 --- a/src/wrapper.py +++ b/src/wrapper.py @@ -9,7 +9,7 @@ from src.dirmanager import DirManager from src.tests_authentik.runner_authentik import RunnerAuthentik from src.tests_wordpress.runner_wordpress import RunnerWordpress -TESTS_DIR = Path("../tests").resolve() +TESTS_DIR = Path("./tests").resolve() ENV_FILES = [ Path("envfiles/login.test.dev.local-it.cloud.env"), # authentik -- 2.47.2 From b4ad3a84ac971a5660bee8f42db37c52f0cbe418 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 15:19:25 +0100 Subject: [PATCH 32/69] make user_session setup only use context fixture, not browser --- src/tests_authentik/setup_authentik.py | 32 +++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/tests_authentik/setup_authentik.py b/src/tests_authentik/setup_authentik.py index 66a9070..4602d2b 100644 --- a/src/tests_authentik/setup_authentik.py +++ b/src/tests_authentik/setup_authentik.py @@ -75,14 +75,30 @@ def create_user(context: BrowserContext, invitelink, STATES: Path): expect(page.locator("ak-library")).to_be_visible() -def test_create_user_session(browser: Browser, dotenv_config: dict[str, str], STATES: Path): +# def test_create_user_session(browser: Browser, dotenv_config: dict[str, str], STATES: Path): +# # create invite_link +# admin_context = browser.new_context(storage_state=f"{STATES}/admin_state.json") +# admin_context.set_default_timeout(TIMEOUT) +# invite_link = create_invite_link(admin_context, dotenv_config) + +# # create user +# user_context = browser.new_context() +# user_context.set_default_timeout(TIMEOUT) +# create_user(user_context, invite_link, STATES) +# user_context.storage_state(path=f"{STATES}/user_state.json") + + +def test_create_user_session_one_context(context: BrowserContext, dotenv_config: dict[str, str], STATES: Path): # create invite_link - admin_context = browser.new_context(storage_state=f"{STATES}/admin_state.json") - admin_context.set_default_timeout(TIMEOUT) - invite_link = create_invite_link(admin_context, dotenv_config) + context.set_default_timeout(TIMEOUT) + storage_state = json.loads(f"{STATES}/admin_state.json") + # admin_context = browser.new_context(storage_state=f"{STATES}/admin_state.json") + context.add_cookies(*storage_state["cookies"]) + invite_link = create_invite_link(context, dotenv_config) # create user - user_context = browser.new_context() - user_context.set_default_timeout(TIMEOUT) - create_user(user_context, invite_link, STATES) - user_context.storage_state(path=f"{STATES}/user_state.json") + context.clear_cookies() + # user_context = browser.new_context() + context.set_default_timeout(TIMEOUT) + create_user(context, invite_link, STATES) + context.storage_state(path=f"{STATES}/user_state.json") -- 2.47.2 From 1088235518905b4f5865e09a09b32ec60a69aaa3 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 15:49:12 +0100 Subject: [PATCH 33/69] loading of admin cookies works now --- src/tests_authentik/setup_authentik.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/tests_authentik/setup_authentik.py b/src/tests_authentik/setup_authentik.py index 4602d2b..e398334 100644 --- a/src/tests_authentik/setup_authentik.py +++ b/src/tests_authentik/setup_authentik.py @@ -2,7 +2,7 @@ import json from pathlib import Path from icecream import ic -from playwright.sync_api import Browser, BrowserContext, expect +from playwright.sync_api import BrowserContext, expect cred_file = Path("credentials.json") with open(cred_file, "r") as f: @@ -89,11 +89,14 @@ def create_user(context: BrowserContext, invitelink, STATES: Path): def test_create_user_session_one_context(context: BrowserContext, dotenv_config: dict[str, str], STATES: Path): - # create invite_link context.set_default_timeout(TIMEOUT) - storage_state = json.loads(f"{STATES}/admin_state.json") - # admin_context = browser.new_context(storage_state=f"{STATES}/admin_state.json") - context.add_cookies(*storage_state["cookies"]) + + # load admin cookies + state_file = STATES / "admin_state.json" + storage_state = json.loads(state_file.read_bytes()) + context.add_cookies(storage_state["cookies"]) + + # create invite_link invite_link = create_invite_link(context, dotenv_config) # create user -- 2.47.2 From d483bf3a0f31e76f5aa5a819b46e8877617e62bd Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 17:50:06 +0100 Subject: [PATCH 34/69] remove old function with browser fixture --- src/tests_authentik/setup_authentik.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/tests_authentik/setup_authentik.py b/src/tests_authentik/setup_authentik.py index e398334..09495d3 100644 --- a/src/tests_authentik/setup_authentik.py +++ b/src/tests_authentik/setup_authentik.py @@ -75,20 +75,7 @@ def create_user(context: BrowserContext, invitelink, STATES: Path): expect(page.locator("ak-library")).to_be_visible() -# def test_create_user_session(browser: Browser, dotenv_config: dict[str, str], STATES: Path): -# # create invite_link -# admin_context = browser.new_context(storage_state=f"{STATES}/admin_state.json") -# admin_context.set_default_timeout(TIMEOUT) -# invite_link = create_invite_link(admin_context, dotenv_config) - -# # create user -# user_context = browser.new_context() -# user_context.set_default_timeout(TIMEOUT) -# create_user(user_context, invite_link, STATES) -# user_context.storage_state(path=f"{STATES}/user_state.json") - - -def test_create_user_session_one_context(context: BrowserContext, dotenv_config: dict[str, str], STATES: Path): +def test_create_user_session(context: BrowserContext, dotenv_config: dict[str, str], STATES: Path): context.set_default_timeout(TIMEOUT) # load admin cookies -- 2.47.2 From ad95ed649f4e217bfd164bb98446b3d97805c2f1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 17:51:13 +0100 Subject: [PATCH 35/69] cleanup --- src/conftest.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/conftest.py b/src/conftest.py index 992f497..ddb9885 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -9,17 +9,9 @@ from pathlib import Path import pytest from dotenv import dotenv_values -from icecream import ic -from playwright.sync_api import BrowserContext from src.dirmanager import DirManager -# pytest_plugins = [ -# "setup.setup_authentik", -# "src.tests_authentik.authentik_plugin" -# ] - - TIMEOUT = 5000 @@ -56,7 +48,7 @@ def dotenv_config(request) -> dict[str, str]: assert dotenv_path is not None, "required pytest command line argument not given" dotenv_path = Path(dotenv_path) assert dotenv_path.is_file() - return dotenv_values(dotenv_path) + return dotenv_values(dotenv_path) # type: ignore @pytest.fixture(scope="session", autouse=True) @@ -75,11 +67,10 @@ def RESULTS(dirmanager) -> Path: return dirmanager.RESULTS -# log failed tests - - @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): + """saves traceback when test fails""" + # execute all other hooks to obtain the report object outcome = yield rep = outcome.get_result() -- 2.47.2 From f4cb16a162b73f3ab04037cd39d458082c7b9aa7 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 17:51:29 +0100 Subject: [PATCH 36/69] remove tracing stuff, use --trace instead --- src/conftest.py | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/src/conftest.py b/src/conftest.py index ddb9885..f4f8f50 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -84,38 +84,3 @@ def pytest_runtest_makereport(item, call): filepath = item.funcargs["RESULTS"] / filename with open(filepath, "a") as f: f.write(rep.longreprtext + "\n") - - -# trace chrome on failed test - - -# inspired from https://docs.pytest.org/en/6.2.x/example/simple.html#request-example -""" -@pytest.hookimpl(tryfirst=True, hookwrapper=True) -def pytest_runtest_makereport(item, call): - outcome = yield - rep = outcome.get_result() - # set a report attribute for each phase of a call, which can - # be "setup", "call", "teardown" - setattr(item, "rep_" + rep.when, rep) - - -@pytest.fixture -def setup(request, context: BrowserContext): - # before test - context.tracing.start(screenshots=True, snapshots=True, sources=True) - context.set_default_timeout(TIMEOUT) - - # test execution - yield context - - # after test - if request.node.rep_setup.failed: - # test setup failed - pass - else: - if request.node.rep_call.failed: - # test execution failed - filename = "trace-" + request.node.originalname + ".zip" - context.tracing.stop(path=filename) -""" -- 2.47.2 From f9baecba30f09dee479d5d7d53e078d6c1a83538 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 17:54:13 +0100 Subject: [PATCH 37/69] WIP: set environment variables in main --- main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/main.py b/main.py index 5942916..4d18d4d 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,12 @@ +import os + from src.utils import get_session_id from src.wrapper import ENV_FILES, Wrapper +# Set environment variables + +os.environ["PWDEBUG"] = "1" + session_id = get_session_id() wrapper = Wrapper(ENV_FILES, session_id=session_id) wrapper.setup_test() -- 2.47.2 From 0a977abf65ffcc3b7804af5185f2f1e85d4c8837 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 17:56:11 +0100 Subject: [PATCH 38/69] move ENV_Files to main.py --- main.py | 15 +++++++++++++-- src/wrapper.py | 11 +---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/main.py b/main.py index 4d18d4d..dc2a66c 100644 --- a/main.py +++ b/main.py @@ -1,11 +1,22 @@ import os +from pathlib import Path from src.utils import get_session_id -from src.wrapper import ENV_FILES, Wrapper +from src.wrapper import Wrapper + +ENV_FILES = [ + Path("envfiles/login.test.dev.local-it.cloud.env"), # authentik + # Path("envfiles/blog.test.dev.local-it.cloud.env"), # wordpress +] + +TESTS_DIR = Path("./tests").resolve() + # Set environment variables -os.environ["PWDEBUG"] = "1" +# os.environ["PWDEBUG"] = "1" +os.environ["TESTS_DIR"] = str(TESTS_DIR) + session_id = get_session_id() wrapper = Wrapper(ENV_FILES, session_id=session_id) diff --git a/src/wrapper.py b/src/wrapper.py index 700a452..5b40bd8 100644 --- a/src/wrapper.py +++ b/src/wrapper.py @@ -1,21 +1,12 @@ -from datetime import datetime from pathlib import Path from typing import Protocol from dotenv import dotenv_values -from icecream import ic from src.dirmanager import DirManager from src.tests_authentik.runner_authentik import RunnerAuthentik from src.tests_wordpress.runner_wordpress import RunnerWordpress -TESTS_DIR = Path("./tests").resolve() - -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): @@ -55,7 +46,7 @@ class Wrapper: 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) + config: dict[str, str] = dotenv_values(env_file) # type: ignore RunnerClass = RUNNER_DICT[config["TYPE"]] runners.append(RunnerClass(dotenv_path=env_file, tests_dir=TESTS_DIR, session_id=self.session_id)) return runners -- 2.47.2 From be6403de4d1678c5e2788e3e6a1caa37022f93a1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 18:02:46 +0100 Subject: [PATCH 39/69] disable dummy test --- src/tests_authentik/runner_authentik.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests_authentik/runner_authentik.py b/src/tests_authentik/runner_authentik.py index 8ad5eca..5e14660 100644 --- a/src/tests_authentik/runner_authentik.py +++ b/src/tests_authentik/runner_authentik.py @@ -16,7 +16,7 @@ def condition_always_false(dotenv_path: Path) -> bool: class RunnerAuthentik(Runner): test_dir_name = "tests_authentik" main_setup_name = "setup_authentik.py" - main_test_name = "test_authentik_dummy.py" + # main_test_name = "test_authentik_dummy.py" def run_setup(self): # run setup -- 2.47.2 From 305f485a23c2641d72c41d81ee7a195c7a065906 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 18:04:22 +0100 Subject: [PATCH 40/69] cleanup --- src/runner.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/runner.py b/src/runner.py index 75e3597..a6fb286 100644 --- a/src/runner.py +++ b/src/runner.py @@ -22,7 +22,7 @@ class Runner: def __init__(self, dotenv_path: Path, tests_dir: Path, session_id: str): self.dotenv_path = dotenv_path - self.config: dict[str, str] = dotenv_values(dotenv_path) + self.config: dict[str, str] = dotenv_values(dotenv_path) # type: ignore self.tests_dir = tests_dir self.session_id = session_id self.dir_manager = DirManager(tests_dir, session_id) @@ -68,14 +68,17 @@ class Runner: # tracing command_arguments.append("--tracing") + # command_arguments.append("retain-on-failure") command_arguments.append("on") + # warning: https://github.com/microsoft/playwright-pytest/issues/111 + # --output only works with the given context and page fixture # disable capturing # pytest -s # disable all capturing - command_arguments.append("-s") + # command_arguments.append("-s") # headed - command_arguments.append("--headed") + # command_arguments.append("--headed") pytest.main(command_arguments) -- 2.47.2 From 76a79c11b22846db97b15d9843ffb7f45907e9cd Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 18:08:47 +0100 Subject: [PATCH 41/69] move credentials to environment variables, defined in main --- main.py | 8 ++++++++ src/tests_authentik/setup_authentik.py | 14 +++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/main.py b/main.py index dc2a66c..00903eb 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,4 @@ +import json import os from pathlib import Path @@ -17,6 +18,13 @@ TESTS_DIR = Path("./tests").resolve() # os.environ["PWDEBUG"] = "1" os.environ["TESTS_DIR"] = str(TESTS_DIR) +cred_file = Path("credentials.json") +with open(cred_file, "r") as f: + CREDENTIALS = json.load(f) + +os.environ["ADMIN_USER"] = CREDENTIALS["admin_user"] +os.environ["ADMIN_PASS"] = CREDENTIALS["admin_pass"] + session_id = get_session_id() wrapper = Wrapper(ENV_FILES, session_id=session_id) diff --git a/src/tests_authentik/setup_authentik.py b/src/tests_authentik/setup_authentik.py index 09495d3..713a8b5 100644 --- a/src/tests_authentik/setup_authentik.py +++ b/src/tests_authentik/setup_authentik.py @@ -1,15 +1,11 @@ import json +import os from pathlib import Path -from icecream import ic from playwright.sync_api import BrowserContext, expect -cred_file = Path("credentials.json") -with open(cred_file, "r") as f: - CREDENTIALS = json.load(f) - -ic("RUNNING SETUP") -ic(CREDENTIALS) +ADMIN_USER = os.environ["ADMIN_USER"] +ADMIN_PASS = os.environ["ADMIN_PASS"] TESTUSER = {"username": "testuser", "name": "Test User", "password": "test123", "email": "test@example.com"} TIMEOUT = 5000 @@ -27,8 +23,8 @@ def test_create_admin_login(context: BrowserContext, dotenv_config: dict[str, st expect(page.get_by_text(welcome_message)).to_be_visible(timeout=TIMEOUT) # login - page.locator('input[name="uidField"]').fill(CREDENTIALS["admin"]) - page.locator('ak-stage-identification input[name="password"]').fill(CREDENTIALS["admin_pw"]) + page.locator('input[name="uidField"]').fill(ADMIN_USER) + page.locator('ak-stage-identification input[name="password"]').fill(ADMIN_USER) page.get_by_role("button", name="Log In").click() expect(page.locator("ak-library")).to_be_visible(timeout=TIMEOUT) -- 2.47.2 From 21105a001bc623613fb224df4a67788e053a3133 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 18:09:00 +0100 Subject: [PATCH 42/69] make dirmanager support str --- src/dirmanager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dirmanager.py b/src/dirmanager.py index 20c13fc..57491c3 100644 --- a/src/dirmanager.py +++ b/src/dirmanager.py @@ -16,8 +16,10 @@ class DirManager: ... """ - def __init__(self, tests_dir: Path, session_id: str): + def __init__(self, tests_dir: Path | str, session_id: str): # root test dir + if isinstance(tests_dir, str): + tests_dir = Path(tests_dir) self._tests_dir = tests_dir.resolve() self.session_id = session_id -- 2.47.2 From 349f2a510a28b85f642fac5bd0d7551e77b9eaab Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 18:11:16 +0100 Subject: [PATCH 43/69] rename stuff --- src/dirmanager.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/dirmanager.py b/src/dirmanager.py index 57491c3..5f04212 100644 --- a/src/dirmanager.py +++ b/src/dirmanager.py @@ -16,24 +16,24 @@ class DirManager: ... """ - def __init__(self, tests_dir: Path | str, session_id: str): + def __init__(self, output_dir: Path | str, session_id: str): # root test dir - if isinstance(tests_dir, str): - tests_dir = Path(tests_dir) - self._tests_dir = tests_dir.resolve() + if isinstance(output_dir, str): + output_dir = Path(output_dir) + self._output_dir = output_dir.resolve() self.session_id = session_id def create_all_dirs(self): - self.create_dirs(self._tests_dir, exist_ok=True) + self.create_dirs(self._output_dir, exist_ok=True) self.create_dirs([self.SESSION, self.RECORDS, self.STATES, self.RESULTS], exist_ok=True) @property - def TESTS(self): - return self._tests_dir + def OUTPUT(self): + return self._output_dir @property def SESSION(self): - return self._tests_dir / f"test-{self.session_id}" + return self._output_dir / f"test-{self.session_id}" @property def RECORDS(self): -- 2.47.2 From 26f5b3427926fa11b75b54efbd593f247f95c509 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 18:12:43 +0100 Subject: [PATCH 44/69] add output dir as argument to wrapper --- main.py | 5 ++--- src/wrapper.py | 8 +++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/main.py b/main.py index 00903eb..c8f2bf8 100644 --- a/main.py +++ b/main.py @@ -10,13 +10,12 @@ ENV_FILES = [ # Path("envfiles/blog.test.dev.local-it.cloud.env"), # wordpress ] -TESTS_DIR = Path("./tests").resolve() +OUTPUT_DIR = Path("./test-output").resolve() # Set environment variables # os.environ["PWDEBUG"] = "1" -os.environ["TESTS_DIR"] = str(TESTS_DIR) cred_file = Path("credentials.json") with open(cred_file, "r") as f: @@ -27,6 +26,6 @@ os.environ["ADMIN_PASS"] = CREDENTIALS["admin_pass"] session_id = get_session_id() -wrapper = Wrapper(ENV_FILES, session_id=session_id) +wrapper = Wrapper(ENV_FILES, output_dir=OUTPUT_DIR, session_id=session_id) wrapper.setup_test() wrapper.run_test() diff --git a/src/wrapper.py b/src/wrapper.py index 5b40bd8..28b0b7e 100644 --- a/src/wrapper.py +++ b/src/wrapper.py @@ -1,3 +1,4 @@ +import os from pathlib import Path from typing import Protocol @@ -27,13 +28,14 @@ RUNNER_DICT: dict[str, type[TestRunner]] = { class Wrapper: - def __init__(self, env_files: list[Path], session_id: str): + def __init__(self, env_files: list[Path], output_dir: Path, session_id: str): self.env_files = env_files self.check_env_files(self.env_files) + self.output_dir = output_dir 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 = DirManager(output_dir=self.output_dir, session_id=self.session_id) self.dir_manager.create_all_dirs() def run_test(self): @@ -48,7 +50,7 @@ class Wrapper: for env_file in env_files: config: dict[str, str] = dotenv_values(env_file) # type: ignore RunnerClass = RUNNER_DICT[config["TYPE"]] - runners.append(RunnerClass(dotenv_path=env_file, tests_dir=TESTS_DIR, session_id=self.session_id)) + runners.append(RunnerClass(dotenv_path=env_file, tests_dir=self.output_dir, session_id=self.session_id)) return runners @staticmethod -- 2.47.2 From 3354859ec1205a6595f483b5c55d8648d5c055f9 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 18:12:52 +0100 Subject: [PATCH 45/69] update argument name --- src/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conftest.py b/src/conftest.py index f4f8f50..95965d6 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -37,7 +37,7 @@ def dirmanager(request) -> DirManager: tests_dir = Path(tests_dir) session_id = request.config.getoption("--session_id") assert session_id is not None, "required pytest command line argument not given" - dirmanager = DirManager(tests_dir=tests_dir, session_id=session_id) + dirmanager = DirManager(output_dir=tests_dir, session_id=session_id) dirmanager.create_all_dirs() return dirmanager -- 2.47.2 From 8418e5aeb1b41e8c71bae6141343545a789628ea Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 18:13:49 +0100 Subject: [PATCH 46/69] rename tests_dir to output_dir --- src/conftest.py | 10 +++++----- src/runner.py | 10 +++++----- src/wrapper.py | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/conftest.py b/src/conftest.py index 95965d6..36f5451 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -21,7 +21,7 @@ def pytest_addoption(parser): action="store", ) parser.addoption( - "--tests_dir", + "--output_dir", action="store", ) parser.addoption( @@ -32,12 +32,12 @@ def pytest_addoption(parser): @pytest.fixture(scope="session", autouse=True) def dirmanager(request) -> DirManager: - tests_dir = request.config.getoption("--tests_dir") - assert tests_dir is not None, "required pytest command line argument not given" - tests_dir = Path(tests_dir) + output_dir = request.config.getoption("--output_dir") + assert output_dir is not None, "required pytest command line argument not given" + output_dir = Path(output_dir) session_id = request.config.getoption("--session_id") assert session_id is not None, "required pytest command line argument not given" - dirmanager = DirManager(output_dir=tests_dir, session_id=session_id) + dirmanager = DirManager(output_dir=output_dir, session_id=session_id) dirmanager.create_all_dirs() return dirmanager diff --git a/src/runner.py b/src/runner.py index a6fb286..977760f 100644 --- a/src/runner.py +++ b/src/runner.py @@ -20,12 +20,12 @@ class Runner: sub_tests: list[SubTest] = [] dependencies: list[str] = [] - def __init__(self, dotenv_path: Path, tests_dir: Path, session_id: str): + def __init__(self, dotenv_path: Path, output_dir: Path, session_id: str): self.dotenv_path = dotenv_path self.config: dict[str, str] = dotenv_values(dotenv_path) # type: ignore - self.tests_dir = tests_dir + self.output_dir = output_dir self.session_id = session_id - self.dir_manager = DirManager(tests_dir, session_id) + self.dir_manager = DirManager(output_dir, session_id) ic(f"creating instance of {self.__class__.__name__}") assert self.test_dir_name is not None @@ -55,8 +55,8 @@ class Runner: command_arguments.append("--env_file") command_arguments.append(str(self.dotenv_path)) - command_arguments.append("--tests_dir") - command_arguments.append(str(self.tests_dir)) + command_arguments.append("--output_dir") + command_arguments.append(str(self.output_dir)) command_arguments.append("--session_id") command_arguments.append(self.session_id) diff --git a/src/wrapper.py b/src/wrapper.py index 28b0b7e..5b1bd30 100644 --- a/src/wrapper.py +++ b/src/wrapper.py @@ -10,7 +10,7 @@ from src.tests_wordpress.runner_wordpress import RunnerWordpress class TestRunner(Protocol): - def __init__(self, dotenv_path: Path, tests_dir: Path, session_id: str): + def __init__(self, dotenv_path: Path, output_dir: Path, session_id: str): ... def run_setup(self): @@ -50,7 +50,7 @@ class Wrapper: for env_file in env_files: config: dict[str, str] = dotenv_values(env_file) # type: ignore RunnerClass = RUNNER_DICT[config["TYPE"]] - runners.append(RunnerClass(dotenv_path=env_file, tests_dir=self.output_dir, session_id=self.session_id)) + runners.append(RunnerClass(dotenv_path=env_file, output_dir=self.output_dir, session_id=self.session_id)) return runners @staticmethod -- 2.47.2 From c9844bc688b6e6ce249ffdf66bf2429ed1c94c7c Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 18:31:24 +0100 Subject: [PATCH 47/69] improve runner commands --- src/runner.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/runner.py b/src/runner.py index 977760f..710d4b6 100644 --- a/src/runner.py +++ b/src/runner.py @@ -48,8 +48,8 @@ class Runner: command_arguments = [] - command_arguments.append("-v") - command_arguments.append("-rp") + # command_arguments.append("-v") + # command_arguments.append("-rx") command_arguments.append(str(full_test_path)) command_arguments.append("--env_file") @@ -73,8 +73,7 @@ class Runner: # warning: https://github.com/microsoft/playwright-pytest/issues/111 # --output only works with the given context and page fixture - # disable capturing - # pytest -s # disable all capturing + # Disable capturing. With -s set, prints will go to console as if pytest is not there. # command_arguments.append("-s") # headed -- 2.47.2 From 050be3709fe619f28aec2c34f473431eccdb7e29 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 20:58:23 +0100 Subject: [PATCH 48/69] use fixture from authentik in wordpress --- src/tests_authentik/fixtures_authentik.py | 21 ++++++++++++ src/tests_wordpress/conftest.py | 29 +--------------- src/tests_wordpress/runner_wordpress.py | 3 +- .../test_wordpress_feature1.py | 33 +++++++++++-------- 4 files changed, 42 insertions(+), 44 deletions(-) create mode 100644 src/tests_authentik/fixtures_authentik.py diff --git a/src/tests_authentik/fixtures_authentik.py b/src/tests_authentik/fixtures_authentik.py new file mode 100644 index 0000000..4a67d49 --- /dev/null +++ b/src/tests_authentik/fixtures_authentik.py @@ -0,0 +1,21 @@ +import json +from pathlib import Path + +import pytest +from playwright.sync_api import BrowserContext + + +@pytest.fixture +def admin_session(context: BrowserContext, STATES: Path) -> BrowserContext: + state_file = STATES / "admin_state.json" + storage_state = json.loads(state_file.read_bytes()) + context.add_cookies(storage_state["cookies"]) + return context + + +@pytest.fixture +def user_session(context: BrowserContext, STATES: Path) -> BrowserContext: + state_file = STATES / "user_state.json" + storage_state = json.loads(state_file.read_bytes()) + context.add_cookies(storage_state["cookies"]) + return context diff --git a/src/tests_wordpress/conftest.py b/src/tests_wordpress/conftest.py index 0dd75fc..dcd6fde 100644 --- a/src/tests_wordpress/conftest.py +++ b/src/tests_wordpress/conftest.py @@ -1,28 +1 @@ -# 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 +from src.tests_authentik.fixtures_authentik import admin_session, user_session diff --git a/src/tests_wordpress/runner_wordpress.py b/src/tests_wordpress/runner_wordpress.py index eddccfa..b0fef11 100644 --- a/src/tests_wordpress/runner_wordpress.py +++ b/src/tests_wordpress/runner_wordpress.py @@ -15,7 +15,6 @@ 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"), + SubTest(condition=condition_always_true, test_file="test_wordpress_feature1.py"), ] dependencies: list[str] = ["authentik"] diff --git a/src/tests_wordpress/test_wordpress_feature1.py b/src/tests_wordpress/test_wordpress_feature1.py index 865fd93..7f81d8c 100644 --- a/src/tests_wordpress/test_wordpress_feature1.py +++ b/src/tests_wordpress/test_wordpress_feature1.py @@ -1,26 +1,31 @@ import re from icecream import ic -from playwright.sync_api import Page, expect +from playwright.sync_api import BrowserContext, Page, expect -def test_one(config): - ic(config) +def test_demo(admin_session: BrowserContext): + admin_session.new_page() 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_one(config): +# ic(config) +# assert 1 + 1 == 2 -def test_get_started_link(page: Page): - page.goto("https://playwright.dev/") +# def test_has_title(page: Page): +# page.goto("https://playwright.dev/") - # Click the get started link. - page.get_by_role("link", name="Get started").click() +# # Expect a title "to contain" a substring. +# expect(page).to_have_title(re.compile("Playwright")) - # Expects page to have a heading with the name of Installation. - expect(page.get_by_role("heading", name="Installation")).to_be_visible() + +# 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() -- 2.47.2 From f3b23e5182227838e1a00a66e814736d0c98236d Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 20:58:30 +0100 Subject: [PATCH 49/69] update --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0efe464..46d7f87 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ __pycache__/ -tests/ +test-output/ TestResults/ .vscode/ *.pyc -- 2.47.2 From 772bc1b94ae8c1ed0fe629f3207f435eae0cbb00 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 22:14:34 +0100 Subject: [PATCH 50/69] improve setup with regex --- src/tests_authentik/setup_authentik.py | 41 +++++++++++++++++++++----- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/tests_authentik/setup_authentik.py b/src/tests_authentik/setup_authentik.py index 713a8b5..9e9c29f 100644 --- a/src/tests_authentik/setup_authentik.py +++ b/src/tests_authentik/setup_authentik.py @@ -1,5 +1,6 @@ import json import os +import re from pathlib import Path from playwright.sync_api import BrowserContext, expect @@ -7,6 +8,7 @@ from playwright.sync_api import BrowserContext, expect ADMIN_USER = os.environ["ADMIN_USER"] ADMIN_PASS = os.environ["ADMIN_PASS"] + TESTUSER = {"username": "testuser", "name": "Test User", "password": "test123", "email": "test@example.com"} TIMEOUT = 5000 @@ -24,7 +26,7 @@ def test_create_admin_login(context: BrowserContext, dotenv_config: dict[str, st # login page.locator('input[name="uidField"]').fill(ADMIN_USER) - page.locator('ak-stage-identification input[name="password"]').fill(ADMIN_USER) + page.locator('ak-stage-identification input[name="password"]').fill(ADMIN_PASS) page.get_by_role("button", name="Log In").click() expect(page.locator("ak-library")).to_be_visible(timeout=TIMEOUT) @@ -37,18 +39,41 @@ def create_invite_link(admin_context: BrowserContext, dotenv_config: dict[str, s url = "https://" + dotenv_config["DOMAIN"] page.goto(url) 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() + + # regex approach (shorter) + page.get_by_role("button", name=re.compile(r"Directory|Verzeichnis")).click() + # the or approach + # button_dir_en = page.get_by_role("button", name="Directory") + # page.get_by_role("button").get_by_text(re.compile("Verzeichnis|Directory")).click() + # button_dir_de = page.get_by_text("Verzeichnis") + # button_dir_en.or_(button_dir_de).click() + + nav = page.locator("ak-sidebar-item", has_text=re.compile(r"Directory|Verzeichnis")) + nav.get_by_role("link", name=re.compile(r"Users|Benutzer")).click() + + nav = page.locator("ak-sidebar-item", has_text="Directory") + nav.get_by_role("link", name=re.compile(r"Invitations|Einladungen")).click() + + # button_invitation_en = page.get_by_role("link", name="Invitations") + # button_invitation_de = page.get_by_text("Einladungen") + # button_invitation_en.or_(button_invitation_de).click() + + page.get_by_role("cell", name=re.compile(r"Keine Objekte|objects")).get_by_role( + "button" + ).click() # todo: confirm "objects" for en lang + page.locator('input[name="name"]').click() - linkname = "testlink9433" + linkname = "test_link_123" 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() + page.get_by_role("option", name=re.compile(r"invitation-enrollment-flow")).click() + + page.get_by_role("dialog").get_by_role("button", name=re.compile(r"Erstellen|create")).click() # confirm "create" + # 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() + # page.get_by_text(linkname).click() invitelink = linklocator.get_by_role("textbox").get_attribute(name="value") return invitelink -- 2.47.2 From 3b7c56a5b337f3caa4b7ead4510254a543bf1d82 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 23:34:16 +0100 Subject: [PATCH 51/69] remove old setup routine in runner --- src/runner.py | 3 --- src/tests_authentik/runner_authentik.py | 5 ----- src/wrapper.py | 5 ----- 3 files changed, 13 deletions(-) diff --git a/src/runner.py b/src/runner.py index 710d4b6..16e7661 100644 --- a/src/runner.py +++ b/src/runner.py @@ -84,9 +84,6 @@ class Runner: def show_files(self): ic(list(self.root_dir.glob("*"))) - def run_setup(self): - pass - def run_tests(self): self._run_main_test() for sub_test in self.sub_tests: diff --git a/src/tests_authentik/runner_authentik.py b/src/tests_authentik/runner_authentik.py index 5e14660..ffbad52 100644 --- a/src/tests_authentik/runner_authentik.py +++ b/src/tests_authentik/runner_authentik.py @@ -17,8 +17,3 @@ class RunnerAuthentik(Runner): test_dir_name = "tests_authentik" main_setup_name = "setup_authentik.py" # main_test_name = "test_authentik_dummy.py" - - def run_setup(self): - # run setup - # setup_authentik(self.config, self.dir_manager.STATES) - pass diff --git a/src/wrapper.py b/src/wrapper.py index 5b1bd30..161900d 100644 --- a/src/wrapper.py +++ b/src/wrapper.py @@ -13,9 +13,6 @@ class TestRunner(Protocol): def __init__(self, dotenv_path: Path, output_dir: Path, session_id: str): ... - def run_setup(self): - ... - def run_tests(self): ... @@ -40,8 +37,6 @@ class Wrapper: def run_test(self): self.runners: list[TestRunner] = self._load_runners(self.env_files) - for runner in self.runners: - runner.run_setup() for runner in self.runners: runner.run_tests() -- 2.47.2 From 6bceee946a9acddb22ceebf8fbac8b8cf1b4981b Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 23:40:08 +0100 Subject: [PATCH 52/69] add dependency check --- src/runner.py | 13 +++++++++++++ src/tests_authentik/runner_authentik.py | 1 + src/tests_wordpress/runner_wordpress.py | 1 + 3 files changed, 15 insertions(+) diff --git a/src/runner.py b/src/runner.py index 16e7661..f34e97a 100644 --- a/src/runner.py +++ b/src/runner.py @@ -14,6 +14,7 @@ class SubTest(TypedDict): class Runner: + name: Optional[str] = None test_dir_name: Optional[str] = None main_setup_name: Optional[str] = None main_test_name: Optional[str] = None @@ -85,6 +86,7 @@ class Runner: ic(list(self.root_dir.glob("*"))) def run_tests(self): + self._check_dependencies() self._run_main_test() for sub_test in self.sub_tests: condition_function = sub_test["condition"] @@ -92,3 +94,14 @@ class Runner: test_name = sub_test["test_file"] full_test_path = self.root_dir / self.test_dir_name / test_name self._run_pytest(full_test_path) + self._create_complete_file() + + def _create_complete_file(self): + file_path = self.dir_manager.RESULTS / self.name + with open(file_path, "w") as _: + pass # pass does nothing, so the file remains empty + + def _check_dependencies(self): + for dependencie in self.dependencies: + ic(f"checking {dependencie}") + assert dependencie in self.dir_manager.RESULTS.glob("*") diff --git a/src/tests_authentik/runner_authentik.py b/src/tests_authentik/runner_authentik.py index ffbad52..fbdf44c 100644 --- a/src/tests_authentik/runner_authentik.py +++ b/src/tests_authentik/runner_authentik.py @@ -14,6 +14,7 @@ def condition_always_false(dotenv_path: Path) -> bool: class RunnerAuthentik(Runner): + name = "authentik" test_dir_name = "tests_authentik" main_setup_name = "setup_authentik.py" # main_test_name = "test_authentik_dummy.py" diff --git a/src/tests_wordpress/runner_wordpress.py b/src/tests_wordpress/runner_wordpress.py index b0fef11..de9e501 100644 --- a/src/tests_wordpress/runner_wordpress.py +++ b/src/tests_wordpress/runner_wordpress.py @@ -12,6 +12,7 @@ def condition_always_false(dotenv_path: Path) -> bool: class RunnerWordpress(Runner): + name = "wordpress" test_dir_name = "tests_wordpress" # main_test_name = "test_wordpress.py" sub_tests = [ -- 2.47.2 From 765537394533f922fa8b350174aae8a960ca578e Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Nov 2023 23:40:27 +0100 Subject: [PATCH 53/69] remove show_files function --- src/runner.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/runner.py b/src/runner.py index f34e97a..b95f634 100644 --- a/src/runner.py +++ b/src/runner.py @@ -82,9 +82,6 @@ class Runner: pytest.main(command_arguments) - def show_files(self): - ic(list(self.root_dir.glob("*"))) - def run_tests(self): self._check_dependencies() self._run_main_test() -- 2.47.2 From f8683ac322e16377d62e3d4534995e6690a14758 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 12:06:46 +0100 Subject: [PATCH 54/69] add comment --- main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/main.py b/main.py index c8f2bf8..1ea922a 100644 --- a/main.py +++ b/main.py @@ -5,6 +5,12 @@ from pathlib import Path from src.utils import get_session_id from src.wrapper import Wrapper +# The env file list is the input to testing framework. each env file triggers +# the execution of one test Runner and provides configuration to the tests +# inside the runner. There can be dependencies, for example wordpress requires +# that authentik ran first to create the admin session and the user session. +# At the moment, wrong ordering results in unsuccessful test (wrong ordering +# would be wordpress env file is before authentik env file). ENV_FILES = [ Path("envfiles/login.test.dev.local-it.cloud.env"), # authentik # Path("envfiles/blog.test.dev.local-it.cloud.env"), # wordpress -- 2.47.2 From a07d593796013cc481d09e648c5bbed38dc2add3 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 14:07:12 +0100 Subject: [PATCH 55/69] authentik setup fully working --- src/tests_authentik/setup_authentik.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/tests_authentik/setup_authentik.py b/src/tests_authentik/setup_authentik.py index 9e9c29f..04ae645 100644 --- a/src/tests_authentik/setup_authentik.py +++ b/src/tests_authentik/setup_authentik.py @@ -10,7 +10,7 @@ ADMIN_PASS = os.environ["ADMIN_PASS"] TESTUSER = {"username": "testuser", "name": "Test User", "password": "test123", "email": "test@example.com"} -TIMEOUT = 5000 +TIMEOUT = 10000 def test_create_admin_login(context: BrowserContext, dotenv_config: dict[str, str], STATES: Path): @@ -22,13 +22,13 @@ def test_create_admin_login(context: BrowserContext, dotenv_config: dict[str, st # check welcome message welcome_message = dotenv_config.get("welcome_message") if welcome_message: - expect(page.get_by_text(welcome_message)).to_be_visible(timeout=TIMEOUT) + expect(page.get_by_text(welcome_message)).to_be_visible() # login page.locator('input[name="uidField"]').fill(ADMIN_USER) page.locator('ak-stage-identification input[name="password"]').fill(ADMIN_PASS) page.get_by_role("button", name="Log In").click() - expect(page.locator("ak-library")).to_be_visible(timeout=TIMEOUT) + expect(page.locator("ak-library")).to_be_visible() # save state context.storage_state(path=f"{STATES}/admin_state.json") @@ -40,9 +40,9 @@ def create_invite_link(admin_context: BrowserContext, dotenv_config: dict[str, s page.goto(url) page.get_by_role("link", name="Admin Interface").click() - # regex approach (shorter) + # regex approach (shorter): page.get_by_role("button", name=re.compile(r"Directory|Verzeichnis")).click() - # the or approach + # the or approach: # button_dir_en = page.get_by_role("button", name="Directory") # page.get_by_role("button").get_by_text(re.compile("Verzeichnis|Directory")).click() # button_dir_de = page.get_by_text("Verzeichnis") @@ -51,13 +51,10 @@ def create_invite_link(admin_context: BrowserContext, dotenv_config: dict[str, s nav = page.locator("ak-sidebar-item", has_text=re.compile(r"Directory|Verzeichnis")) nav.get_by_role("link", name=re.compile(r"Users|Benutzer")).click() - nav = page.locator("ak-sidebar-item", has_text="Directory") + nav = page.locator("ak-sidebar-item", has_text=re.compile(r"Directory|Verzeichnis")) nav.get_by_role("link", name=re.compile(r"Invitations|Einladungen")).click() - # button_invitation_en = page.get_by_role("link", name="Invitations") - # button_invitation_de = page.get_by_text("Einladungen") - # button_invitation_en.or_(button_invitation_de).click() - + # todo: only works if no links have been created yet (empty list) page.get_by_role("cell", name=re.compile(r"Keine Objekte|objects")).get_by_role( "button" ).click() # todo: confirm "objects" for en lang @@ -68,8 +65,8 @@ def create_invite_link(admin_context: BrowserContext, dotenv_config: dict[str, s page.get_by_placeholder("Wählen Sie ein Objekt aus.").click() page.get_by_role("option", name=re.compile(r"invitation-enrollment-flow")).click() - page.get_by_role("dialog").get_by_role("button", name=re.compile(r"Erstellen|create")).click() # confirm "create" - # page.get_by_text("Erstellen", exact=True).first.click() + # force, because else we get "intercepts pointer events" + page.locator("footer").locator("ak-spinner-button").first.click(force=True) linklocator = page.get_by_role("rowgroup").filter(has=page.get_by_text(linkname)) linklocator.locator(".fa-angle-down").click() @@ -109,7 +106,5 @@ def test_create_user_session(context: BrowserContext, dotenv_config: dict[str, s # create user context.clear_cookies() - # user_context = browser.new_context() - context.set_default_timeout(TIMEOUT) create_user(context, invite_link, STATES) context.storage_state(path=f"{STATES}/user_state.json") -- 2.47.2 From bfcd75162bdaba793cd1b85a91dc46603ff6c387 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 14:19:21 +0100 Subject: [PATCH 56/69] fix _check_dependencies --- src/runner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runner.py b/src/runner.py index b95f634..fa09fbf 100644 --- a/src/runner.py +++ b/src/runner.py @@ -99,6 +99,6 @@ class Runner: pass # pass does nothing, so the file remains empty def _check_dependencies(self): + finished_tests = [result.name for result in self.dir_manager.RESULTS.glob("*")] for dependencie in self.dependencies: - ic(f"checking {dependencie}") - assert dependencie in self.dir_manager.RESULTS.glob("*") + assert dependencie in finished_tests -- 2.47.2 From 818d2ca8beff65d73bfec566805f7db7591d9fcf Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 14:19:35 +0100 Subject: [PATCH 57/69] only trace on fail --- src/runner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runner.py b/src/runner.py index fa09fbf..701ab6f 100644 --- a/src/runner.py +++ b/src/runner.py @@ -69,8 +69,8 @@ class Runner: # tracing command_arguments.append("--tracing") - # command_arguments.append("retain-on-failure") - command_arguments.append("on") + command_arguments.append("retain-on-failure") + # command_arguments.append("on") # warning: https://github.com/microsoft/playwright-pytest/issues/111 # --output only works with the given context and page fixture -- 2.47.2 From 0b7057135f0d15a6f43e4e573be4720b50d61c2b Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 14:21:39 +0100 Subject: [PATCH 58/69] add progress dir --- src/dirmanager.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/dirmanager.py b/src/dirmanager.py index 5f04212..e5066fa 100644 --- a/src/dirmanager.py +++ b/src/dirmanager.py @@ -8,9 +8,10 @@ class DirManager: The structures is as follows: tests dir/ session_dir-1/ + progress records - states results + states session_dir-2/ records ... @@ -25,7 +26,7 @@ class DirManager: def create_all_dirs(self): self.create_dirs(self._output_dir, exist_ok=True) - self.create_dirs([self.SESSION, self.RECORDS, self.STATES, self.RESULTS], exist_ok=True) + self.create_dirs([self.SESSION, self.RECORDS, self.STATES, self.RESULTS, self.PROGRESS], exist_ok=True) @property def OUTPUT(self): @@ -47,6 +48,10 @@ class DirManager: def RESULTS(self): return self.SESSION / Path("results") + @property + def PROGRESS(self): + return self.SESSION / Path("progress") + @staticmethod def create_dirs(dirs: Path | list[Path] | dict[str, Path], exist_ok=False): match dirs: -- 2.47.2 From a2718084bc71f7108849438450ce9acbb8beadd4 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 14:23:22 +0100 Subject: [PATCH 59/69] rename var holding DirManager --- src/runner.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/runner.py b/src/runner.py index 701ab6f..652f0be 100644 --- a/src/runner.py +++ b/src/runner.py @@ -26,7 +26,7 @@ class Runner: self.config: dict[str, str] = dotenv_values(dotenv_path) # type: ignore self.output_dir = output_dir self.session_id = session_id - self.dir_manager = DirManager(output_dir, session_id) + self.DIRS = DirManager(output_dir, session_id) ic(f"creating instance of {self.__class__.__name__}") assert self.test_dir_name is not None @@ -63,7 +63,7 @@ class Runner: command_arguments.append(self.session_id) # artifacts dir - output = self.dir_manager.RESULTS / full_test_path.stem + output = self.DIRS.RESULTS / full_test_path.stem command_arguments.append("--output") command_arguments.append(str(output)) @@ -94,11 +94,11 @@ class Runner: self._create_complete_file() def _create_complete_file(self): - file_path = self.dir_manager.RESULTS / self.name + file_path = self.DIRS.RESULTS / self.name with open(file_path, "w") as _: - pass # pass does nothing, so the file remains empty + pass # create empty file def _check_dependencies(self): - finished_tests = [result.name for result in self.dir_manager.RESULTS.glob("*")] + finished_tests = [result.name for result in self.DIRS.RESULTS.glob("*")] for dependencie in self.dependencies: assert dependencie in finished_tests -- 2.47.2 From d67f56bce75ee0963cfff821aedaad7e5588cefb Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 14:25:31 +0100 Subject: [PATCH 60/69] remove subdir fixtures --- src/conftest.py | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/src/conftest.py b/src/conftest.py index 36f5451..93b2ada 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -31,7 +31,16 @@ def pytest_addoption(parser): @pytest.fixture(scope="session", autouse=True) -def dirmanager(request) -> DirManager: +def DIR(request) -> DirManager: + """Fixture holding test directories + + DIR.OUTPUT + DIR.SESSION + DIR.RECORDS + DIR.STATES + DIR.RESULTS + DIR.PROGRESS""" + output_dir = request.config.getoption("--output_dir") assert output_dir is not None, "required pytest command line argument not given" output_dir = Path(output_dir) @@ -51,22 +60,6 @@ def dotenv_config(request) -> dict[str, str]: return dotenv_values(dotenv_path) # type: ignore -@pytest.fixture(scope="session", autouse=True) -def RECORDS(dirmanager) -> Path: - assert isinstance(dirmanager, DirManager) - return dirmanager.RECORDS - - -@pytest.fixture(scope="session", autouse=True) -def STATES(dirmanager) -> Path: - return dirmanager.STATES - - -@pytest.fixture(scope="session", autouse=True) -def RESULTS(dirmanager) -> Path: - return dirmanager.RESULTS - - @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): """saves traceback when test fails""" -- 2.47.2 From 9494f8fd441b2c2bc2ee451d81d7b4f7d9b72b27 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 14:25:51 +0100 Subject: [PATCH 61/69] small changes --- src/tests_authentik/setup_authentik.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tests_authentik/setup_authentik.py b/src/tests_authentik/setup_authentik.py index 04ae645..bc7fbf6 100644 --- a/src/tests_authentik/setup_authentik.py +++ b/src/tests_authentik/setup_authentik.py @@ -75,9 +75,9 @@ def create_invite_link(admin_context: BrowserContext, dotenv_config: dict[str, s return invitelink -def create_user(context: BrowserContext, invitelink, STATES: Path): +def create_user(user_context: BrowserContext, invitelink): # warning: only works on german site - page = context.new_page() + page = user_context.new_page() page.goto(invitelink) page.get_by_placeholder("Benutzername").click() page.get_by_placeholder("Benutzername").fill(TESTUSER["username"]) @@ -106,5 +106,5 @@ def test_create_user_session(context: BrowserContext, dotenv_config: dict[str, s # create user context.clear_cookies() - create_user(context, invite_link, STATES) + create_user(context, invite_link) context.storage_state(path=f"{STATES}/user_state.json") -- 2.47.2 From d1fbc403c0094851edb48f132983a7b6498002f2 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 14:26:50 +0100 Subject: [PATCH 62/69] use new DIR fixture --- src/tests_authentik/setup_authentik.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/tests_authentik/setup_authentik.py b/src/tests_authentik/setup_authentik.py index bc7fbf6..dfa91d2 100644 --- a/src/tests_authentik/setup_authentik.py +++ b/src/tests_authentik/setup_authentik.py @@ -1,10 +1,11 @@ import json import os import re -from pathlib import Path from playwright.sync_api import BrowserContext, expect +from src.dirmanager import DirManager + ADMIN_USER = os.environ["ADMIN_USER"] ADMIN_PASS = os.environ["ADMIN_PASS"] @@ -13,7 +14,7 @@ TESTUSER = {"username": "testuser", "name": "Test User", "password": "test123", TIMEOUT = 10000 -def test_create_admin_login(context: BrowserContext, dotenv_config: dict[str, str], STATES: Path): +def test_create_admin_login(context: BrowserContext, dotenv_config: dict[str, str], DIR: DirManager): # go to page page = context.new_page() url = "https://" + dotenv_config["DOMAIN"] @@ -31,7 +32,7 @@ def test_create_admin_login(context: BrowserContext, dotenv_config: dict[str, st expect(page.locator("ak-library")).to_be_visible() # save state - context.storage_state(path=f"{STATES}/admin_state.json") + context.storage_state(path=f"{DIR.STATES}/admin_state.json") def create_invite_link(admin_context: BrowserContext, dotenv_config: dict[str, str]): @@ -93,11 +94,11 @@ def create_user(user_context: BrowserContext, invitelink): expect(page.locator("ak-library")).to_be_visible() -def test_create_user_session(context: BrowserContext, dotenv_config: dict[str, str], STATES: Path): +def test_create_user_session(context: BrowserContext, dotenv_config: dict[str, str], DIR: DirManager): context.set_default_timeout(TIMEOUT) # load admin cookies - state_file = STATES / "admin_state.json" + state_file = DIR.STATES / "admin_state.json" storage_state = json.loads(state_file.read_bytes()) context.add_cookies(storage_state["cookies"]) @@ -107,4 +108,4 @@ def test_create_user_session(context: BrowserContext, dotenv_config: dict[str, s # create user context.clear_cookies() create_user(context, invite_link) - context.storage_state(path=f"{STATES}/user_state.json") + context.storage_state(path=f"{DIR.STATES}/user_state.json") -- 2.47.2 From 387d5f182238dddedce0e21ef5387446bdde3d95 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 14:27:17 +0100 Subject: [PATCH 63/69] use new DIR fixture --- src/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conftest.py b/src/conftest.py index 93b2ada..143dd86 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -74,6 +74,6 @@ def pytest_runtest_makereport(item, call): filename = f"failed-{item.nodeid}.txt" filename = filename.replace("/", "-") filename = filename.replace("::", "-") - filepath = item.funcargs["RESULTS"] / filename + filepath = item.funcargs["DIR"].RESULTS / filename with open(filepath, "a") as f: f.write(rep.longreprtext + "\n") -- 2.47.2 From f38763d9128bf60457ef261327fbed4e4934bcb1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 14:28:52 +0100 Subject: [PATCH 64/69] use new DIR fixture --- src/tests_authentik/fixtures_authentik.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/tests_authentik/fixtures_authentik.py b/src/tests_authentik/fixtures_authentik.py index 4a67d49..3d26907 100644 --- a/src/tests_authentik/fixtures_authentik.py +++ b/src/tests_authentik/fixtures_authentik.py @@ -4,18 +4,20 @@ from pathlib import Path import pytest from playwright.sync_api import BrowserContext +from src.dirmanager import DirManager + @pytest.fixture -def admin_session(context: BrowserContext, STATES: Path) -> BrowserContext: - state_file = STATES / "admin_state.json" +def admin_session(context: BrowserContext, DIR: DirManager) -> BrowserContext: + state_file = DIR.STATES / "admin_state.json" storage_state = json.loads(state_file.read_bytes()) context.add_cookies(storage_state["cookies"]) return context @pytest.fixture -def user_session(context: BrowserContext, STATES: Path) -> BrowserContext: - state_file = STATES / "user_state.json" +def user_session(context: BrowserContext, DIR: Path) -> BrowserContext: + state_file = DIR.STATES / "user_state.json" storage_state = json.loads(state_file.read_bytes()) context.add_cookies(storage_state["cookies"]) return context -- 2.47.2 From 2ec7c6fd30f7826c1f77479fc1c25c64e663a65f Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 14:29:19 +0100 Subject: [PATCH 65/69] fixup --- src/tests_authentik/fixtures_authentik.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tests_authentik/fixtures_authentik.py b/src/tests_authentik/fixtures_authentik.py index 3d26907..bcd106d 100644 --- a/src/tests_authentik/fixtures_authentik.py +++ b/src/tests_authentik/fixtures_authentik.py @@ -1,5 +1,4 @@ import json -from pathlib import Path import pytest from playwright.sync_api import BrowserContext @@ -16,7 +15,7 @@ def admin_session(context: BrowserContext, DIR: DirManager) -> BrowserContext: @pytest.fixture -def user_session(context: BrowserContext, DIR: Path) -> BrowserContext: +def user_session(context: BrowserContext, DIR: DirManager) -> BrowserContext: state_file = DIR.STATES / "user_state.json" storage_state = json.loads(state_file.read_bytes()) context.add_cookies(storage_state["cookies"]) -- 2.47.2 From 851c06f7950514d2aeb0bf1d1752db70914f8260 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 15:04:10 +0100 Subject: [PATCH 66/69] put progress files in PROGRESS dir --- src/runner.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/runner.py b/src/runner.py index 652f0be..b050bfc 100644 --- a/src/runner.py +++ b/src/runner.py @@ -83,7 +83,7 @@ class Runner: pytest.main(command_arguments) def run_tests(self): - self._check_dependencies() + self._check_dependencies_finished() self._run_main_test() for sub_test in self.sub_tests: condition_function = sub_test["condition"] @@ -91,14 +91,16 @@ class Runner: test_name = sub_test["test_file"] full_test_path = self.root_dir / self.test_dir_name / test_name self._run_pytest(full_test_path) - self._create_complete_file() + self._create_progress_file() - def _create_complete_file(self): - file_path = self.DIRS.RESULTS / self.name + def _create_progress_file(self): + """create progress file to indicated finished test""" + file_path = self.DIRS.PROGRESS / self.name with open(file_path, "w") as _: pass # create empty file - def _check_dependencies(self): - finished_tests = [result.name for result in self.DIRS.RESULTS.glob("*")] + def _check_dependencies_finished(self): + """look for progress file of dependencies to confirm they have ran""" + finished_tests = [result.name for result in self.DIRS.PROGRESS.glob("*")] for dependencie in self.dependencies: assert dependencie in finished_tests -- 2.47.2 From 61a02aa18711da7ba4f9964c7ef351aa55c775b1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 16:07:25 +0100 Subject: [PATCH 67/69] cleanup --- src/runner.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/runner.py b/src/runner.py index b050bfc..2aa814b 100644 --- a/src/runner.py +++ b/src/runner.py @@ -57,12 +57,15 @@ class Runner: command_arguments.append(str(self.dotenv_path)) command_arguments.append("--output_dir") - command_arguments.append(str(self.output_dir)) + command_arguments.append(str(self.DIRS.OUTPUT)) command_arguments.append("--session_id") command_arguments.append(self.session_id) # artifacts dir + # warning: https://github.com/microsoft/playwright-pytest/issues/111 + # --output only works with the given context and page fixture + # folder needs to be unique! traces will not appear, if every pytest run has same output dir output = self.DIRS.RESULTS / full_test_path.stem command_arguments.append("--output") command_arguments.append(str(output)) @@ -71,8 +74,6 @@ class Runner: command_arguments.append("--tracing") command_arguments.append("retain-on-failure") # command_arguments.append("on") - # warning: https://github.com/microsoft/playwright-pytest/issues/111 - # --output only works with the given context and page fixture # Disable capturing. With -s set, prints will go to console as if pytest is not there. # command_arguments.append("-s") -- 2.47.2 From cad8e3417a0e5ad7635db030367f5b76e2bcd45e Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 16:57:36 +0100 Subject: [PATCH 68/69] add check_if_user_exists --- src/tests_authentik/setup_authentik.py | 43 ++++++++++++++++---------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/src/tests_authentik/setup_authentik.py b/src/tests_authentik/setup_authentik.py index dfa91d2..51706a3 100644 --- a/src/tests_authentik/setup_authentik.py +++ b/src/tests_authentik/setup_authentik.py @@ -2,6 +2,7 @@ import json import os import re +from icecream import ic from playwright.sync_api import BrowserContext, expect from src.dirmanager import DirManager @@ -35,24 +36,28 @@ def test_create_admin_login(context: BrowserContext, dotenv_config: dict[str, st context.storage_state(path=f"{DIR.STATES}/admin_state.json") +def check_if_user_exists(admin_context: BrowserContext, dotenv_config: dict[str, str]): + # go to admin page + page = admin_context.new_page() + url = "https://" + dotenv_config["DOMAIN"] + page.goto(url) + page.get_by_role("link", name="Admin Interface").click() + nav = page.locator("ak-sidebar-item", has_text=re.compile(r"Directory|Verzeichnis")) + nav.click() + nav.get_by_role("link", name=re.compile(r"Users|Benutzer")).click() + result = page.get_by_text(TESTUSER["username"]).is_visible(timeout=TIMEOUT) + return result + + def create_invite_link(admin_context: BrowserContext, dotenv_config: dict[str, str]): + # go to admin page page = admin_context.new_page() url = "https://" + dotenv_config["DOMAIN"] page.goto(url) page.get_by_role("link", name="Admin Interface").click() - # regex approach (shorter): - page.get_by_role("button", name=re.compile(r"Directory|Verzeichnis")).click() - # the or approach: - # button_dir_en = page.get_by_role("button", name="Directory") - # page.get_by_role("button").get_by_text(re.compile("Verzeichnis|Directory")).click() - # button_dir_de = page.get_by_text("Verzeichnis") - # button_dir_en.or_(button_dir_de).click() - - nav = page.locator("ak-sidebar-item", has_text=re.compile(r"Directory|Verzeichnis")) - nav.get_by_role("link", name=re.compile(r"Users|Benutzer")).click() - nav = page.locator("ak-sidebar-item", has_text=re.compile(r"Directory|Verzeichnis")) + nav.click() nav.get_by_role("link", name=re.compile(r"Invitations|Einladungen")).click() # todo: only works if no links have been created yet (empty list) @@ -102,10 +107,16 @@ def test_create_user_session(context: BrowserContext, dotenv_config: dict[str, s storage_state = json.loads(state_file.read_bytes()) context.add_cookies(storage_state["cookies"]) - # create invite_link - invite_link = create_invite_link(context, dotenv_config) + if check_if_user_exists(context, dotenv_config): + # just login with user + pass + context.clear_cookies() + else: + ## create user + # create invite_link + invite_link = create_invite_link(context, dotenv_config) + # create user + context.clear_cookies() + create_user(context, invite_link) - # create user - context.clear_cookies() - create_user(context, invite_link) context.storage_state(path=f"{DIR.STATES}/user_state.json") -- 2.47.2 From 1d316ea744dcba6b9f303ba64a194b40f3ea0712 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 27 Nov 2023 16:58:07 +0100 Subject: [PATCH 69/69] enable all tests --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index 1ea922a..1461740 100644 --- a/main.py +++ b/main.py @@ -13,7 +13,7 @@ from src.wrapper import Wrapper # would be wordpress env file is before authentik env file). 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 ] OUTPUT_DIR = Path("./test-output").resolve() -- 2.47.2