refactoring (#13)
* general project refactoring * various small improvements * improve imap fixture with helper functions and typing * add wordpress send email setup * add wordpress receive email test * add various documentation Reviewed-on: local-it-infrastructure/e2e_tests#13 Co-authored-by: Daniel <d.brummerloh@gmail.com> Co-committed-by: Daniel <d.brummerloh@gmail.com>
This commit is contained in:
parent
41a042f07d
commit
d1ff1183a5
29 changed files with 323 additions and 175 deletions
29
README.md
29
README.md
|
|
@ -2,19 +2,19 @@
|
||||||
|
|
||||||
Pytest-Abra is an installable python package design to test instances created with [abra](https://docs.coopcloud.tech/abra/). After installation, you will have two things:
|
Pytest-Abra is an installable python package design to test instances created with [abra](https://docs.coopcloud.tech/abra/). After installation, you will have two things:
|
||||||
|
|
||||||
- `abratest` CLI command
|
- `abratest` CLI command. *Used to initialize the testing.*
|
||||||
|
|
||||||
- `pytest-abra` Pytest plugin
|
- `pytest-abra` Pytest plugin. *Automatically loads custom fixtures in any pytest (see `pytest_abra/custom_fixtures.py`)*
|
||||||
|
|
||||||
## CLI (abratest)
|
## CLI (`abratest`)
|
||||||
|
|
||||||
The easiest way to call abratest is via the helper script in `main.py`. You can also directly call abratest via terminal, but you will have to make sure that the requirements below are met. To do that, you can call `abratest` with:
|
`abratest` can be called via terminal:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
abratest [arguments]
|
abratest [arguments]
|
||||||
```
|
```
|
||||||
|
|
||||||
The cli command abratest has 3 **required arguments**:
|
To run successfully, very specific arguments are required. The easiest way to use abratest is with the helper script in `main.py`. Of yourse you can implement a similar helper script in the language of your liking. The cli command `abratest` has 3 **required arguments**:
|
||||||
|
|
||||||
- `--env_paths`: list of the .env files used in the test
|
- `--env_paths`: list of the .env files used in the test
|
||||||
- `--recipes_dir`: directory of all available abra recipes
|
- `--recipes_dir`: directory of all available abra recipes
|
||||||
|
|
@ -66,12 +66,20 @@ DIR recipes_dir [contains abra recipes]
|
||||||
└── [pytest_files]
|
└── [pytest_files]
|
||||||
```
|
```
|
||||||
|
|
||||||
The class `RunnerWordpress` will be automatically imported by `importlib`, which is equivalent to
|
The class `RunnerWordpress` will be automatically imported using `importlib` library, which is equivalent to the code below. Note that `recipes_dir` will be added to sys.path automatically for the import to work and that every `Runner` class matching `recipes_dir.rglob("*/runner*.py")` will be imported.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from wordpress.tests_wordpress.runner_wordpress import RunnerWordpress
|
from wordpress.tests_wordpress.runner_wordpress import RunnerWordpress
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### output_dir [string]
|
||||||
|
|
||||||
|
Path to the directory where all test outputs are stored (test report, tracebacks, playwright traces etc.)
|
||||||
|
|
||||||
|
```
|
||||||
|
abratest --output_dir /path/to/output
|
||||||
|
```
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
To use pytest-abra, follow these steps:
|
To use pytest-abra, follow these steps:
|
||||||
|
|
@ -108,7 +116,7 @@ Run the script with
|
||||||
python main.py
|
python main.py
|
||||||
```
|
```
|
||||||
|
|
||||||
# 2.2 Run with Docker
|
## 2.2 Run with Docker
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose build # build the image
|
docker compose build # build the image
|
||||||
|
|
@ -128,12 +136,13 @@ Force rebuild without cache
|
||||||
docker-compose build --no-cache
|
docker-compose build --no-cache
|
||||||
```
|
```
|
||||||
|
|
||||||
## Codegen
|
## Playwright Debug & Codegen
|
||||||
|
|
||||||
Use playwright codegen to create code for new testes easily https://playwright.dev/python/docs/codegen
|
Use playwright debug mode or codegen to create testing code easily by recording browser actions https://playwright.dev/python/docs/codegen
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
playwright codegen demo.playwright.dev/todomvc
|
abratest --debug # launch your tests in debug mode
|
||||||
|
playwright codegen demo.playwright.dev/todomvc # visit given url in codegen mode
|
||||||
```
|
```
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@ from playwright.sync_api import BrowserContext, expect
|
||||||
from pytest_abra.dir_manager import DirManager
|
from pytest_abra.dir_manager import DirManager
|
||||||
|
|
||||||
|
|
||||||
def test_wordpress(admin_session: BrowserContext, dotenv_config: dict[str, str], DIR: DirManager):
|
def test_wordpress(admin_session: BrowserContext, env_config: dict[str, str], DIR: DirManager):
|
||||||
page_authentik = admin_session.new_page()
|
page_authentik = admin_session.new_page()
|
||||||
with page_authentik.expect_popup() as event_context:
|
with page_authentik.expect_popup() as event_context:
|
||||||
page_authentik.get_by_role("link", name="Wordpress").click()
|
page_authentik.get_by_role("link", name="Wordpress").click()
|
||||||
page_wordpress = event_context.value
|
page_wordpress = event_context.value
|
||||||
|
|
||||||
expect(page_wordpress.locator("#wpcontent")).to_be_visible()
|
expect(page_wordpress.locator("#wpcontent")).to_be_visible()
|
||||||
if "locale" in dotenv_config and "de" in dotenv_config["locale"]:
|
if "locale" in env_config and "de" in env_config["locale"]:
|
||||||
expect(page_wordpress.get_by_role("heading")).to_have_text("Willkommen bei WordPress!")
|
expect(page_wordpress.get_by_role("heading")).to_have_text("Willkommen bei WordPress!")
|
||||||
|
|
|
||||||
19
prototyping/dependency_injection.py
Normal file
19
prototyping/dependency_injection.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
a = 2
|
||||||
|
b = 3
|
||||||
|
c = 4
|
||||||
|
|
||||||
|
|
||||||
|
def func(a: int, c: int) -> int:
|
||||||
|
return a + c
|
||||||
|
|
||||||
|
|
||||||
|
arg_names = inspect.getfullargspec(func).args
|
||||||
|
print(arg_names) # ['a', 'c']
|
||||||
|
|
||||||
|
arguments = {arg: globals()[arg] for arg in arg_names if arg in globals()}
|
||||||
|
print(arguments) # {'a': 2, 'c': 4}
|
||||||
|
|
||||||
|
result = func(**arguments)
|
||||||
|
print(result) # 6
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
# %%
|
|
||||||
import email
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
from email.header import decode_header
|
|
||||||
from imaplib import IMAP4, IMAP4_SSL
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# -------------------------------- credentials ------------------------------- #
|
|
||||||
|
|
||||||
cred_file = Path("../credentials.json")
|
|
||||||
with open(cred_file, "r") as f:
|
|
||||||
CREDENTIALS = json.load(f)
|
|
||||||
|
|
||||||
for key, value in CREDENTIALS.items():
|
|
||||||
os.environ[key] = value
|
|
||||||
|
|
||||||
IMAP_HOST = os.environ["IMAP_HOST"]
|
|
||||||
IMAP_PORT = os.environ["IMAP_PORT"]
|
|
||||||
IMAP_USER = os.environ["IMAP_USER"]
|
|
||||||
IMAP_PASS = os.environ["IMAP_PASS"]
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------- imap ----------------------------------- #
|
|
||||||
|
|
||||||
|
|
||||||
with IMAP4_SSL(host=IMAP_HOST) as imap_server:
|
|
||||||
imap_server.login(IMAP_USER, IMAP_PASS)
|
|
||||||
imap_server.select("INBOX")
|
|
||||||
|
|
||||||
# Search for all emails in the folder
|
|
||||||
status, email_ids = imap_server.search(None, "ALL")
|
|
||||||
email_ids = email_ids[0].split()
|
|
||||||
|
|
||||||
# Fetch email details using the retrieved IDs
|
|
||||||
for email_id in email_ids:
|
|
||||||
result, data = imap_server.fetch(email_id, "(RFC822)")
|
|
||||||
raw_email = data[0][1] # Raw content of the email
|
|
||||||
email_message = email.message_from_bytes(raw_email)
|
|
||||||
|
|
||||||
# Extract the subject
|
|
||||||
subject_encoded = email_message.get("Subject")
|
|
||||||
decoded_subject = decode_header(subject_encoded)[0][0]
|
|
||||||
|
|
||||||
if isinstance(decoded_subject, bytes):
|
|
||||||
decoded_subject = decoded_subject.decode()
|
|
||||||
|
|
||||||
# Print or use the subject as needed
|
|
||||||
print("Subject:", decoded_subject)
|
|
||||||
80
prototyping/structure.md
Normal file
80
prototyping/structure.md
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
|
||||||
|
|
||||||
|
Abratest has 3 required inputs, but most importantly the test configuration is done through the .env files given with the --env_paths argument. So let's say we want to run abratest with these 3 .env files:
|
||||||
|
|
||||||
|
- config1.env [of TYPE authentik]
|
||||||
|
|
||||||
|
- config2.env [of TYPE wordpress]
|
||||||
|
|
||||||
|
- config3.env [of TYPE wordpress]
|
||||||
|
|
||||||
|
Now we run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
abratest --env_paths path/config1.env;path/config2.env;path/config3.env [...other args]
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
abratest -> create Coordinator() instance
|
||||||
|
└── Coordinator() -> create Runner() subclass instances
|
||||||
|
├── RunnerAuthentik() [based on config1.env, loaded
|
||||||
|
│ │ from abra/recipes/authentik]
|
||||||
|
│ │ # RunnerAuthentik with 3 test files:
|
||||||
|
│ ├── RUN pytest path/setup_authentik.py
|
||||||
|
│ ├── RUN pytest path/test_authentik_1.py
|
||||||
|
│ └── RUN pytest path/test_authentik_2.py
|
||||||
|
├── RunnerWordpress() [based on config2.env, loaded
|
||||||
|
│ │ from abra/recipes/wordpress]
|
||||||
|
│ │ # RunnerWordpress with 1 test file
|
||||||
|
│ ├── RUN pytest path/setup_authentik.py
|
||||||
|
│ ├── RUN pytest path/test_authentik_1.py
|
||||||
|
│ └── RUN pytest path/test_authentik_2.py
|
||||||
|
└── RunnerWordpress() [based on config3.env, loaded
|
||||||
|
│ from abra/recipes/wordpress]
|
||||||
|
│ # RunnerWordpress with 1 test file
|
||||||
|
├── RUN pytest path/setup_authentik.py
|
||||||
|
├── RUN pytest path/test_authentik_1.py
|
||||||
|
└── RUN pytest path/test_authentik_2.py
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Coordinator will take care of the correct order of the tests. In general, tests are placed in one of 3 categories: `setups`, `tests` and `cleanups`. To associate a test with one of these categories, place the Test in the corresponding list of the Runner class, i.e. Runner.setups = [test] or Runner.tests = [test]. The execution order will be.
|
||||||
|
|
||||||
|
> [setups] ➔ [tests] ➔ [cleanups]
|
||||||
|
|
||||||
|
|
||||||
|
Furthermore, some `Runner` classes can depend on others. For example, `RunnerWordpress` depends on `RunnerAuthentik`. Therefore, `Coordinator` will make sure that `RunnerAuthentik` runs before `RunnerWordpress`. We will end up with with this order:
|
||||||
|
|
||||||
|
| # | Runner | Type |
|
||||||
|
| --- | -------------- | -------- |
|
||||||
|
| 1. | Authentik | setups |
|
||||||
|
| 2. | Wordpress-1 | setups |
|
||||||
|
| 3. | Wordpress-2 | setups |
|
||||||
|
| 4. | Authentik | tests |
|
||||||
|
| 5. | Wordpress-1 | tests |
|
||||||
|
| 6. | Wordpress-2 | tests |
|
||||||
|
| 7. | Authentik | cleanups |
|
||||||
|
| 8. | Wordpress-1 | cleanups |
|
||||||
|
| 9. | Wordpress-2 | cleanups |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
To comprehend this process, let's examine a simplified rendition of the `RunnerWordpress` class. Within it, there exist two setup scripts and two test scripts, one of which operates conditionally.
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
class RunnerWordpress(Runner):
|
||||||
|
env_type = "wordpress"
|
||||||
|
dependencies = ["authentik"]
|
||||||
|
setups = [
|
||||||
|
Test(test_file="setup_wordpress_1.py"),
|
||||||
|
Test(test_file="setup_wordpress_2.py"),
|
||||||
|
]
|
||||||
|
tests = [
|
||||||
|
Test(test_file="test_wordpress.py"),
|
||||||
|
Test(condition=condition_function, test_file="test_wordpress_conditional.py"),
|
||||||
|
]
|
||||||
|
cleanups = []
|
||||||
|
```
|
||||||
|
|
@ -26,7 +26,7 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.entry-points.pytest11]
|
[project.entry-points.pytest11]
|
||||||
pytest_abra = "pytest_abra.pytest_abra"
|
pytest_abra = "pytest_abra.custom_fixtures"
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
abratest = "pytest_abra.cli:run"
|
abratest = "pytest_abra.cli:run"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
from pytest_abra.coordinator import Coordinator
|
||||||
|
from pytest_abra.dir_manager import DirManager
|
||||||
|
from pytest_abra.runner import ConditionArgs, Runner, Test
|
||||||
|
from pytest_abra.utils import BaseUrl
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"Coordinator",
|
||||||
|
"ConditionArgs",
|
||||||
|
"Runner",
|
||||||
|
"Test",
|
||||||
|
"DirManager",
|
||||||
|
"BaseUrl",
|
||||||
|
]
|
||||||
|
|
@ -4,7 +4,7 @@ from pathlib import Path
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from pytest_abra.coordinator import Coordinator
|
from pytest_abra import Coordinator
|
||||||
from pytest_abra.dir_manager import DirManager
|
from pytest_abra.dir_manager import DirManager
|
||||||
from pytest_abra.utils import get_datetime_string
|
from pytest_abra.utils import get_datetime_string
|
||||||
|
|
||||||
|
|
@ -18,7 +18,7 @@ def run():
|
||||||
parser.add_argument("--debug", action="store_true", help="Enable Playwright debug mode")
|
parser.add_argument("--debug", action="store_true", help="Enable Playwright debug mode")
|
||||||
parser.add_argument("--resume", action="store_true", help="Re-run the most recent test, skipping passed tests")
|
parser.add_argument("--resume", action="store_true", help="Re-run the most recent test, skipping passed tests")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
ENV_FILES = [Path(s) for s in args.env_paths.split(";")]
|
env_paths = [Path(s) for s in args.env_paths.split(";")]
|
||||||
|
|
||||||
# -------------------------- enable playwright debug ------------------------- #
|
# -------------------------- enable playwright debug ------------------------- #
|
||||||
|
|
||||||
|
|
@ -43,7 +43,7 @@ def run():
|
||||||
# ---------------------------- initialize and run ---------------------------- #
|
# ---------------------------- initialize and run ---------------------------- #
|
||||||
|
|
||||||
coordinator = Coordinator(
|
coordinator = Coordinator(
|
||||||
env_paths_list=ENV_FILES,
|
env_paths=env_paths,
|
||||||
output_dir=args.output_dir,
|
output_dir=args.output_dir,
|
||||||
session_id=session_id,
|
session_id=session_id,
|
||||||
recipes_dir=args.recipes_dir,
|
recipes_dir=args.recipes_dir,
|
||||||
|
|
|
||||||
|
|
@ -15,21 +15,21 @@ from pytest_abra.utils import rmtree
|
||||||
class Coordinator:
|
class Coordinator:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
env_paths_list: list[Path],
|
env_paths: list[Path],
|
||||||
output_dir: Path,
|
output_dir: Path,
|
||||||
session_id: str,
|
session_id: str,
|
||||||
recipes_dir: Path,
|
recipes_dir: Path,
|
||||||
timeout: int,
|
timeout: int,
|
||||||
) -> None:
|
) -> None:
|
||||||
# logging
|
# logging
|
||||||
out_string = "".join([e.name + "\n" for e in env_paths_list])
|
out_string = "".join([e.name + "\n" for e in env_paths])
|
||||||
out_string += f"output_dir = {output_dir}\n"
|
out_string += f"output_dir = {output_dir}\n"
|
||||||
out_string += f"session_id = {session_id}"
|
out_string += f"session_id = {session_id}"
|
||||||
logger.info(f"initialize Coordinator instance with\nenv_paths_list =\n{out_string}")
|
logger.info(f"initialize Coordinator instance with\nenv_paths_list =\n{out_string}")
|
||||||
|
|
||||||
self.RUNNER_DICT = self.create_runner_dict(recipes_dir)
|
self.RUNNER_DICT = self.create_runner_dict(recipes_dir)
|
||||||
self.DIR = DirManager(output_dir=output_dir, session_id=session_id, recipes_dir=recipes_dir)
|
self.DIR = DirManager(output_dir=output_dir, session_id=session_id, recipes_dir=recipes_dir)
|
||||||
self.ENV = EnvManager(env_paths_list, self.RUNNER_DICT)
|
self.ENV = EnvManager(env_paths=env_paths, RUNNER_DICT=self.RUNNER_DICT)
|
||||||
self.TIMEOUT = timeout
|
self.TIMEOUT = timeout
|
||||||
|
|
||||||
def setup_test(self) -> None:
|
def setup_test(self) -> None:
|
||||||
|
|
@ -52,7 +52,7 @@ class Coordinator:
|
||||||
"""Creates an instance of the correct Runner class for each given env file"""
|
"""Creates an instance of the correct Runner class for each given env file"""
|
||||||
runners: list[Runner] = []
|
runners: list[Runner] = []
|
||||||
for index, env_file in enumerate(env_files):
|
for index, env_file in enumerate(env_files):
|
||||||
RunnerClass = self.RUNNER_DICT[env_file.config["TYPE"]]
|
RunnerClass = self.RUNNER_DICT[env_file.env_config["TYPE"]]
|
||||||
runners.append(RunnerClass(coordinator=self, runner_index=index))
|
runners.append(RunnerClass(coordinator=self, runner_index=index))
|
||||||
return runners
|
return runners
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,14 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from imaplib import IMAP4_SSL
|
from datetime import datetime, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Protocol, TypedDict
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from dotenv import dotenv_values
|
from dotenv import dotenv_values
|
||||||
|
from icecream import ic
|
||||||
|
from imbox import Imbox # type: ignore
|
||||||
from playwright.sync_api import BrowserContext, expect
|
from playwright.sync_api import BrowserContext, expect
|
||||||
from pytest import Parser
|
from pytest import Parser
|
||||||
|
|
||||||
|
|
@ -62,39 +65,90 @@ def DIR(request) -> DirManager:
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def ENV_FILES(DIR: DirManager) -> dict[int, EnvFile]:
|
def env_files(DIR: DirManager) -> list[EnvFile]:
|
||||||
out: dict[int, EnvFile] = dict()
|
"""list of EnvFile objects created from the given env files"""
|
||||||
|
|
||||||
|
env_files_dict: dict[int, EnvFile] = dict()
|
||||||
for env_path in DIR.ENV_FILES.glob("*.env"):
|
for env_path in DIR.ENV_FILES.glob("*.env"):
|
||||||
config: dict[str, str] = dotenv_values(env_path) # type: ignore
|
config: dict[str, str] = dotenv_values(env_path) # type: ignore
|
||||||
env_type = config["TYPE"]
|
env_type = config["TYPE"]
|
||||||
result = re.search(r"(\d+)-*", env_path.name)
|
result = re.search(r"(\d+)-*", env_path.name)
|
||||||
assert result
|
assert result
|
||||||
runner_index = int(result[1])
|
runner_index = int(result[1])
|
||||||
out[runner_index] = EnvFile(env_path=env_path, config=config, env_type=env_type)
|
env_files_dict[runner_index] = EnvFile(env_path=env_path, env_config=config, env_type=env_type)
|
||||||
return out
|
keys = list(env_files_dict.keys())
|
||||||
|
keys.sort()
|
||||||
|
return [env_files_dict[key] for key in keys]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def dotenv_config(request, ENV_FILES: dict[int, EnvFile]) -> dict[str, str]:
|
def env_config(request, env_files: list[EnvFile]) -> dict[str, str]:
|
||||||
|
"""Current env_config"""
|
||||||
runner_index = request.config.getoption("--runner_index")
|
runner_index = request.config.getoption("--runner_index")
|
||||||
return ENV_FILES[runner_index].config
|
return env_files[runner_index].env_config
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def URL(dotenv_config: dict[str, str]) -> BaseUrl:
|
def URL(env_config: dict[str, str]) -> BaseUrl:
|
||||||
return BaseUrl(netloc=dotenv_config["DOMAIN"])
|
"""BaseUrl object based on current DOMAIN"""
|
||||||
|
return BaseUrl(netloc=env_config["DOMAIN"])
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def imap_ssl_email_client() -> None:
|
def imap_client() -> None:
|
||||||
|
"""imap email client using credentials from environment variables"""
|
||||||
|
|
||||||
assert os.environ["IMAP_HOST"]
|
assert os.environ["IMAP_HOST"]
|
||||||
assert os.environ["IMAP_PORT"]
|
assert os.environ["IMAP_PORT"]
|
||||||
assert os.environ["IMAP_USER"]
|
assert os.environ["IMAP_USER"]
|
||||||
assert os.environ["IMAP_PASS"]
|
assert os.environ["IMAP_PASS"]
|
||||||
port = int(os.environ["IMAP_PORT"])
|
|
||||||
imap_client = IMAP4_SSL(host=os.environ["IMAP_HOST"], port=port)
|
imbox = Imbox(
|
||||||
imap_client.login(os.environ["IMAP_USER"], os.environ["IMAP_PASS"])
|
hostname=os.environ["IMAP_HOST"],
|
||||||
imap_client.select("INBOX")
|
port=os.environ["IMAP_PORT"],
|
||||||
yield imap_client
|
username=os.environ["IMAP_USER"],
|
||||||
imap_client.close()
|
password=os.environ["IMAP_PASS"],
|
||||||
imap_client.logout()
|
ssl=True,
|
||||||
|
ssl_context=None,
|
||||||
|
starttls=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
yield imbox
|
||||||
|
|
||||||
|
imbox.logout()
|
||||||
|
|
||||||
|
|
||||||
|
class Body(TypedDict):
|
||||||
|
plain: list
|
||||||
|
html: list
|
||||||
|
|
||||||
|
|
||||||
|
class Message(Protocol):
|
||||||
|
sent_from: list
|
||||||
|
sent_to: list
|
||||||
|
subject: str
|
||||||
|
headers: list
|
||||||
|
date: str
|
||||||
|
body: Body
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def imap_recent_messages(imap_client: Imbox) -> list[Message]:
|
||||||
|
"""Get all messages from [n_minutes] ago till now.
|
||||||
|
|
||||||
|
# iterate with
|
||||||
|
for uid, message in messages:
|
||||||
|
print(uid, message.subject, message.date)"""
|
||||||
|
|
||||||
|
N_MINUTES = 30
|
||||||
|
|
||||||
|
n_minutes_ago = datetime.now() - timedelta(minutes=N_MINUTES)
|
||||||
|
uids: list[bytes] = []
|
||||||
|
messages: list[Message] = []
|
||||||
|
# for uid, message in imap_client.messages(date__gt=n_minutes_ago):
|
||||||
|
for uid, message in imap_client.messages():
|
||||||
|
ic("one time")
|
||||||
|
uids.append(uid)
|
||||||
|
messages.append(message)
|
||||||
|
|
||||||
|
return messages
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
print("wooooorking")
|
|
||||||
|
|
@ -10,11 +10,11 @@ class DirManager:
|
||||||
|
|
||||||
The structures is as follows:
|
The structures is as follows:
|
||||||
tests dir/
|
tests dir/
|
||||||
session_dir-1/
|
session_id-1/
|
||||||
records
|
records
|
||||||
results
|
results
|
||||||
states
|
states
|
||||||
session_dir-2/
|
session_id-2/
|
||||||
records
|
records
|
||||||
...
|
...
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import NamedTuple
|
from typing import TYPE_CHECKING, NamedTuple
|
||||||
|
|
||||||
from dotenv import dotenv_values
|
from dotenv import dotenv_values
|
||||||
|
|
||||||
from pytest_abra.dir_manager import DirManager
|
if TYPE_CHECKING:
|
||||||
from pytest_abra.runner import Runner
|
from pytest_abra.dir_manager import DirManager
|
||||||
|
from pytest_abra.runner import Runner
|
||||||
|
|
||||||
|
|
||||||
class EnvFile(NamedTuple):
|
class EnvFile(NamedTuple):
|
||||||
env_path: Path
|
env_path: Path
|
||||||
config: dict[str, str]
|
env_config: dict[str, str]
|
||||||
env_type: str
|
env_type: str
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
|
|
@ -23,8 +24,8 @@ class DependencyRule(NamedTuple):
|
||||||
|
|
||||||
|
|
||||||
class EnvManager:
|
class EnvManager:
|
||||||
def __init__(self, env_paths_list: list[Path], RUNNER_DICT: dict[str, type["Runner"]]):
|
def __init__(self, env_paths: list[Path], RUNNER_DICT: dict[str, type["Runner"]]):
|
||||||
self.env_files: list[EnvFile] = self._get_env_files(env_paths_list)
|
self.env_files: list[EnvFile] = self._get_env_files(env_paths)
|
||||||
self.dependency_rules: list[DependencyRule] = self._get_dependency_rules(self.env_files, RUNNER_DICT)
|
self.dependency_rules: list[DependencyRule] = self._get_dependency_rules(self.env_files, RUNNER_DICT)
|
||||||
self.env_files = self.sort_env_files_by_rule(self.env_files, self.dependency_rules)
|
self.env_files = self.sort_env_files_by_rule(self.env_files, self.dependency_rules)
|
||||||
|
|
||||||
|
|
@ -37,7 +38,7 @@ class EnvManager:
|
||||||
config: dict[str, str] = dotenv_values(env_path) # type: ignore
|
config: dict[str, str] = dotenv_values(env_path) # type: ignore
|
||||||
assert "TYPE" in config, f"the env file {env_path} does not specify the required TYPE key."
|
assert "TYPE" in config, f"the env file {env_path} does not specify the required TYPE key."
|
||||||
env_type = config["TYPE"]
|
env_type = config["TYPE"]
|
||||||
env_files.append(EnvFile(env_path=env_path, config=config, env_type=env_type))
|
env_files.append(EnvFile(env_path=env_path, env_config=config, env_type=env_type))
|
||||||
return env_files
|
return env_files
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -92,7 +93,7 @@ class EnvManager:
|
||||||
"Could not resolve test order. This is possibly due to a circular dependency (a on b, b on c, c on a)"
|
"Could not resolve test order. This is possibly due to a circular dependency (a on b, b on c, c on a)"
|
||||||
)
|
)
|
||||||
|
|
||||||
def copy_env_files(self, DIR: DirManager) -> None:
|
def copy_env_files(self, DIR: "DirManager") -> None:
|
||||||
"""Copies all env files to STATES/env_files. Files will be renamed to
|
"""Copies all env files to STATES/env_files. Files will be renamed to
|
||||||
<index>-<env_type>-<original_name>
|
<index>-<env_type>-<original_name>
|
||||||
00-authentik-login.test.dev.local-it.cloud.env"""
|
00-authentik-login.test.dev.local-it.cloud.env"""
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING, Callable
|
from typing import TYPE_CHECKING, Callable, NamedTuple
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
@ -10,10 +10,16 @@ if TYPE_CHECKING:
|
||||||
from pytest_abra.env_manager import EnvFile
|
from pytest_abra.env_manager import EnvFile
|
||||||
|
|
||||||
|
|
||||||
|
class ConditionArgs(NamedTuple):
|
||||||
|
env_config: dict[str, str]
|
||||||
|
runner_index: int
|
||||||
|
env_files: list["EnvFile"]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Test:
|
class Test:
|
||||||
test_file: str
|
test_file: str
|
||||||
condition: Callable[[dict[str, str]], bool] | None = None
|
condition: Callable[[ConditionArgs], bool] | None = None
|
||||||
prevent_skip: bool = False
|
prevent_skip: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -25,17 +31,13 @@ class Runner:
|
||||||
dependencies: list[str] = []
|
dependencies: list[str] = []
|
||||||
|
|
||||||
def __init__(self, coordinator: "Coordinator", runner_index: int):
|
def __init__(self, coordinator: "Coordinator", runner_index: int):
|
||||||
self.coordinator = coordinator # needed?
|
self.coordinator = coordinator
|
||||||
self.runner_index = runner_index # needed?
|
self.runner_index = runner_index
|
||||||
|
|
||||||
self.DIR = coordinator.DIR
|
self.DIR = coordinator.DIR
|
||||||
self.ENV = coordinator.ENV
|
self.ENV = coordinator.ENV
|
||||||
self.RUNNER_DICT = coordinator.RUNNER_DICT
|
self.RUNNER_DICT = coordinator.RUNNER_DICT
|
||||||
|
|
||||||
self.env_file: EnvFile = self.ENV.env_files[self.runner_index]
|
|
||||||
self.dotenv_path = self.env_file.env_path
|
|
||||||
self.config = self.env_file.config
|
|
||||||
|
|
||||||
logger.info(f"creating instance of {self.__class__.__name__}")
|
logger.info(f"creating instance of {self.__class__.__name__}")
|
||||||
|
|
||||||
def run_setups(self):
|
def run_setups(self):
|
||||||
|
|
@ -81,16 +83,28 @@ class Runner:
|
||||||
logger.info(f"skipping {identifier_string}, test has passed")
|
logger.info(f"skipping {identifier_string}, test has passed")
|
||||||
return
|
return
|
||||||
|
|
||||||
if test.condition and not test.condition(self.config):
|
if test.condition:
|
||||||
# test condition is defined but not met
|
condition_result = self._run_condition(test.condition)
|
||||||
logger.info(f"skipping {identifier_string}, test condition is not met")
|
if not condition_result:
|
||||||
return
|
# test condition is defined but not met
|
||||||
|
logger.info(f"skipping {identifier_string}, test condition is not met")
|
||||||
|
return
|
||||||
|
|
||||||
# test condition is undefined or not met
|
# test condition is undefined or not met
|
||||||
logger.info(f"running {identifier_string}")
|
logger.info(f"running {identifier_string}")
|
||||||
result = self._call_pytest(full_test_path)
|
result = self._call_pytest(full_test_path)
|
||||||
self._create_result_file(result=result, identifier_string=identifier_string)
|
self._create_result_file(result=result, identifier_string=identifier_string)
|
||||||
|
|
||||||
|
def _run_condition(self, condition_function: Callable[[ConditionArgs], bool]):
|
||||||
|
"""run the test condition function with multiple arguments"""
|
||||||
|
# more arguments can be added later without changing the function signature
|
||||||
|
conditon_args = ConditionArgs(
|
||||||
|
env_files=self.ENV.env_files,
|
||||||
|
runner_index=self.runner_index,
|
||||||
|
env_config=self.ENV.env_files[self.runner_index].env_config,
|
||||||
|
)
|
||||||
|
return condition_function(conditon_args)
|
||||||
|
|
||||||
def _is_test_passed(self, identifier_string: str, remove_existing: bool = False) -> bool:
|
def _is_test_passed(self, identifier_string: str, remove_existing: bool = False) -> bool:
|
||||||
"""returns True if the selected test matching identifier_string already passed
|
"""returns True if the selected test matching identifier_string already passed
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ from urllib.parse import urlunparse
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BaseUrl:
|
class BaseUrl:
|
||||||
|
"""utility class to create a url string with urllib"""
|
||||||
|
|
||||||
netloc: str
|
netloc: str
|
||||||
scheme: str = "https"
|
scheme: str = "https"
|
||||||
path: str = ""
|
path: str = ""
|
||||||
|
|
@ -33,8 +35,3 @@ def rmtree(root_dir: Path):
|
||||||
child.unlink()
|
child.unlink()
|
||||||
|
|
||||||
root_dir.rmdir()
|
root_dir.rmdir()
|
||||||
|
|
||||||
|
|
||||||
def make_url(domain: str) -> str:
|
|
||||||
"""adds 'http://' at the beginning of a string"""
|
|
||||||
return "https://" + domain
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,4 @@
|
||||||
from pytest_abra.runner import Runner, Test
|
from pytest_abra import Runner, Test
|
||||||
|
|
||||||
|
|
||||||
def condition_always_true(dotenv_config: dict[str, str]) -> bool:
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def condition_always_false(dotenv_config: dict[str, str]) -> bool:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class RunnerAuthentik(Runner):
|
class RunnerAuthentik(Runner):
|
||||||
|
|
|
||||||
|
|
@ -14,20 +14,20 @@ ADMIN_PASS = os.environ["ADMIN_PASS"]
|
||||||
TESTUSER = {"username": "testuser", "name": "Test User", "password": "test123", "email": "test@example.com"}
|
TESTUSER = {"username": "testuser", "name": "Test User", "password": "test123", "email": "test@example.com"}
|
||||||
|
|
||||||
|
|
||||||
def setup_admin_state(context: BrowserContext, dotenv_config: dict[str, str], DIR: DirManager):
|
def setup_admin_state(context: BrowserContext, env_config: dict[str, str], DIR: DirManager):
|
||||||
# go to page
|
# go to page
|
||||||
page = context.new_page()
|
page = context.new_page()
|
||||||
url = "https://" + dotenv_config["DOMAIN"]
|
url = "https://" + env_config["DOMAIN"]
|
||||||
page.goto(url)
|
page.goto(url)
|
||||||
|
|
||||||
# check welcome message
|
# check welcome message
|
||||||
welcome_message = dotenv_config.get("welcome_message")
|
welcome_message = env_config.get("welcome_message")
|
||||||
if welcome_message:
|
if welcome_message:
|
||||||
expect(page.get_by_text(welcome_message)).to_be_visible()
|
expect(page.get_by_text(welcome_message)).to_be_visible()
|
||||||
|
|
||||||
# login
|
# login
|
||||||
page.locator('input[name="uidField"]').fill(ADMIN_USER)
|
page.locator("input[name='uidField']").fill(ADMIN_USER)
|
||||||
page.locator('ak-stage-identification input[name="password"]').fill(ADMIN_PASS)
|
page.locator("ak-stage-identification input[name='password']").fill(ADMIN_PASS)
|
||||||
page.get_by_role("button", name="Log In").click()
|
page.get_by_role("button", name="Log In").click()
|
||||||
expect(page.locator("ak-library")).to_be_visible()
|
expect(page.locator("ak-library")).to_be_visible()
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ def setup_admin_state(context: BrowserContext, dotenv_config: dict[str, str], DI
|
||||||
context.storage_state(path=DIR.STATES / "authentik_admin_state.json")
|
context.storage_state(path=DIR.STATES / "authentik_admin_state.json")
|
||||||
|
|
||||||
|
|
||||||
def check_if_user_exists(admin_context: BrowserContext, dotenv_config: dict[str, str], URL: BaseUrl):
|
def check_if_user_exists(admin_context: BrowserContext, env_config: dict[str, str], URL: BaseUrl):
|
||||||
# go to admin page
|
# go to admin page
|
||||||
page = admin_context.new_page()
|
page = admin_context.new_page()
|
||||||
page.goto(URL.get())
|
page.goto(URL.get())
|
||||||
|
|
@ -49,7 +49,7 @@ def check_if_user_exists(admin_context: BrowserContext, dotenv_config: dict[str,
|
||||||
return user.is_visible()
|
return user.is_visible()
|
||||||
|
|
||||||
|
|
||||||
def create_invite_link(admin_context: BrowserContext, dotenv_config: dict[str, str], URL: BaseUrl):
|
def create_invite_link(admin_context: BrowserContext, env_config: dict[str, str], URL: BaseUrl):
|
||||||
# go to admin page
|
# go to admin page
|
||||||
page = admin_context.new_page()
|
page = admin_context.new_page()
|
||||||
page.goto(URL.get())
|
page.goto(URL.get())
|
||||||
|
|
@ -98,19 +98,19 @@ def create_user(user_context: BrowserContext, invitelink):
|
||||||
expect(page.locator("ak-library")).to_be_visible()
|
expect(page.locator("ak-library")).to_be_visible()
|
||||||
|
|
||||||
|
|
||||||
def setup_user_state(context: BrowserContext, dotenv_config: dict[str, str], DIR: DirManager, URL: BaseUrl):
|
def setup_user_state(context: BrowserContext, env_config: dict[str, str], DIR: DirManager, URL: BaseUrl):
|
||||||
# load admin cookies to context
|
# load admin cookies to context
|
||||||
state_file = DIR.STATES / "authentik_admin_state.json"
|
state_file = DIR.STATES / "authentik_admin_state.json"
|
||||||
storage_state = json.loads(state_file.read_bytes())
|
storage_state = json.loads(state_file.read_bytes())
|
||||||
context.add_cookies(storage_state["cookies"])
|
context.add_cookies(storage_state["cookies"])
|
||||||
|
|
||||||
if check_if_user_exists(context, dotenv_config, URL):
|
if check_if_user_exists(context, env_config, URL):
|
||||||
# just login with user
|
# just login with user
|
||||||
pass
|
pass
|
||||||
context.clear_cookies()
|
context.clear_cookies()
|
||||||
else:
|
else:
|
||||||
# get invite_link
|
# get invite_link
|
||||||
invite_link = create_invite_link(context, dotenv_config, URL)
|
invite_link = create_invite_link(context, env_config, URL)
|
||||||
# create user
|
# create user
|
||||||
context.clear_cookies()
|
context.clear_cookies()
|
||||||
create_user(context, invite_link)
|
create_user(context, invite_link)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,4 @@
|
||||||
from pytest_abra.runner import Runner, Test
|
from pytest_abra import Runner, Test
|
||||||
|
|
||||||
|
|
||||||
def condition_always_false(dotenv_config: dict[str, str]) -> bool:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class RunnerNextcloud(Runner):
|
class RunnerNextcloud(Runner):
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import pytest
|
||||||
from playwright.sync_api import Page, expect
|
from playwright.sync_api import Page, expect
|
||||||
|
|
||||||
|
|
||||||
def test_nextcloud_quota(nextcloud_admin_page: Page, dotenv_config: dict[str, str]):
|
def test_nextcloud_quota(nextcloud_admin_page: Page, env_config: dict[str, str]):
|
||||||
"""Test Nextcloud"""
|
"""Tests if the quota set in .env file matches the actual quota shown on the page within 10%"""
|
||||||
if dotenv_config.get("DEFAULT_QUOTA"):
|
if env_config.get("DEFAULT_QUOTA"):
|
||||||
# get quota from website
|
# get quota from website
|
||||||
quota_string = nextcloud_admin_page.get_by_text(
|
quota_string = nextcloud_admin_page.get_by_text(
|
||||||
re.compile(r"\d*,\d .* \d*,\d")
|
re.compile(r"\d*,\d .* \d*,\d")
|
||||||
|
|
@ -17,7 +17,7 @@ def test_nextcloud_quota(nextcloud_admin_page: Page, dotenv_config: dict[str, st
|
||||||
quota_website = float(out_number)
|
quota_website = float(out_number)
|
||||||
|
|
||||||
# get quota from env
|
# get quota from env
|
||||||
quota_config_string = dotenv_config["DEFAULT_QUOTA"] # "100 MB"
|
quota_config_string = env_config["DEFAULT_QUOTA"] # "100 MB"
|
||||||
assert "MB" in quota_config_string
|
assert "MB" in quota_config_string
|
||||||
quota_config = float(quota_config_string.strip("MB"))
|
quota_config = float(quota_config_string.strip("MB"))
|
||||||
|
|
||||||
|
|
@ -27,6 +27,6 @@ def test_nextcloud_quota(nextcloud_admin_page: Page, dotenv_config: dict[str, st
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip
|
@pytest.mark.skip
|
||||||
def test_nextcloud_apps(nextcloud_admin_page: Page, dotenv_config: dict[str, str]):
|
def test_nextcloud_apps(nextcloud_admin_page: Page, env_config: dict[str, str]):
|
||||||
for app in dotenv_config["nc_apps"]:
|
for app in env_config["nc_apps"]:
|
||||||
expect(nextcloud_admin_page.get_by_role("link", name=app)).to_be_visible()
|
expect(nextcloud_admin_page.get_by_role("link", name=app)).to_be_visible()
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from dotenv import dotenv_values
|
|
||||||
from playwright.sync_api import BrowserContext, Page
|
from playwright.sync_api import BrowserContext, Page
|
||||||
|
|
||||||
from pytest_abra.dir_manager import DirManager
|
from pytest_abra import BaseUrl, DirManager
|
||||||
|
|
||||||
pytest_plugins = "authentik.tests_authentik.fixtures_authentik"
|
pytest_plugins = "authentik.tests_authentik.fixtures_authentik"
|
||||||
|
|
||||||
|
|
@ -18,10 +17,7 @@ def wordpress_admin_context(context: BrowserContext, DIR: DirManager) -> Browser
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def wordpress_admin_page(wordpress_admin_context: BrowserContext, DIR: DirManager) -> Page:
|
def wordpress_admin_page(wordpress_admin_context: BrowserContext, URL: BaseUrl) -> Page:
|
||||||
page = wordpress_admin_context.new_page()
|
page = wordpress_admin_context.new_page()
|
||||||
env_file = DIR.ENV_FILES / "wordpress"
|
page.goto(URL.get())
|
||||||
config: dict[str, str] = dotenv_values(env_file) # type: ignore
|
|
||||||
url = "https://" + config["DOMAIN"]
|
|
||||||
page.goto(url)
|
|
||||||
return page
|
return page
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,21 @@
|
||||||
from pytest_abra.runner import Runner, Test
|
from pytest_abra import ConditionArgs, Runner, Test
|
||||||
|
|
||||||
|
|
||||||
def condition_always_true(dotenv_config: dict[str, str]) -> bool:
|
def condition_has_locale(args: ConditionArgs) -> bool:
|
||||||
return True
|
env_config = args.env_config
|
||||||
|
if "de" in env_config.get("LOCALE", ""):
|
||||||
|
return True
|
||||||
def condition_always_false(dotenv_config: dict[str, str]) -> bool:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def condition_has_locale(dotenv_config: dict[str, str]) -> bool:
|
|
||||||
if "LOCALE" in dotenv_config:
|
|
||||||
if "de" in dotenv_config["LOCALE"]:
|
|
||||||
return True
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class RunnerWordpress(Runner):
|
class RunnerWordpress(Runner):
|
||||||
env_type = "wordpress"
|
env_type = "wordpress"
|
||||||
dependencies = ["authentik"]
|
dependencies = ["authentik"]
|
||||||
setups = [Test(test_file="setup_wordpress.py")]
|
setups = [
|
||||||
tests = [
|
Test(test_file="setup_wordpress.py"),
|
||||||
Test(test_file="test_wordpress.py"),
|
Test(test_file="setup_wordpress_trigger_email.py"),
|
||||||
Test(condition=condition_has_locale, test_file="test_wordpress_localization.py"),
|
]
|
||||||
|
tests = [
|
||||||
|
Test(test_file="test_wordpress_receive_email.py", prevent_skip=True),
|
||||||
|
# Test(condition=condition_has_locale, test_file="test_wordpress_localization.py"),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ from playwright.sync_api import BrowserContext, Page, expect
|
||||||
from pytest_abra.dir_manager import DirManager
|
from pytest_abra.dir_manager import DirManager
|
||||||
|
|
||||||
|
|
||||||
def test_visit_from_domain(authentik_admin_context: BrowserContext, dotenv_config: dict[str, str]):
|
def test_visit_from_domain(authentik_admin_context: BrowserContext, env_config: dict[str, str]):
|
||||||
"""visit wordpress directly with admin_session, expect not to be logged in"""
|
"""visit wordpress directly with admin_session, expect not to be logged in"""
|
||||||
page = authentik_admin_context.new_page()
|
page = authentik_admin_context.new_page()
|
||||||
url = "https://" + dotenv_config["DOMAIN"]
|
url = "https://" + env_config["DOMAIN"]
|
||||||
page.goto(url)
|
page.goto(url)
|
||||||
with pytest.raises(AssertionError):
|
with pytest.raises(AssertionError):
|
||||||
# look for admin bar
|
# look for admin bar
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from playwright.sync_api import Page, expect
|
||||||
|
|
||||||
|
from pytest_abra import BaseUrl
|
||||||
|
|
||||||
|
|
||||||
|
def setup_trigger_email(wordpress_admin_page: Page, URL: BaseUrl):
|
||||||
|
"""change profile email to EMAIL to trigger email"""
|
||||||
|
page = wordpress_admin_page
|
||||||
|
page.goto(URL.get("wp-admin/profile.php"))
|
||||||
|
EMAIL = os.environ["IMAP_EMAIL"]
|
||||||
|
print(EMAIL)
|
||||||
|
# breakpoint()
|
||||||
|
page.pause()
|
||||||
|
page.locator("input[id='email']").fill(EMAIL)
|
||||||
|
page.locator("input[id='submit']").click()
|
||||||
|
|
||||||
|
expect(page.locator("div.notice").get_by_text(EMAIL)).to_be_visible()
|
||||||
|
|
@ -5,11 +5,11 @@ from playwright.sync_api import BrowserContext, expect
|
||||||
from pytest_abra.dir_manager import DirManager
|
from pytest_abra.dir_manager import DirManager
|
||||||
|
|
||||||
|
|
||||||
def test_welcome_message(context: BrowserContext, dotenv_config: dict[str, str], DIR: DirManager):
|
def test_welcome_message(context: BrowserContext, env_config: dict[str, str], DIR: DirManager):
|
||||||
page = context.new_page()
|
page = context.new_page()
|
||||||
url = "https://" + dotenv_config["DOMAIN"]
|
url = "https://" + env_config["DOMAIN"]
|
||||||
page.goto(url)
|
page.goto(url)
|
||||||
|
|
||||||
expect(page.locator(".wp-block-heading")).to_be_visible()
|
expect(page.locator(".wp-block-heading")).to_be_visible()
|
||||||
if "locale" in dotenv_config and "de" in dotenv_config["locale"]:
|
if "locale" in env_config and "de" in env_config["locale"]:
|
||||||
expect(page.get_by_role("heading")).to_have_text("Willkommen bei WordPress!")
|
expect(page.get_by_role("heading")).to_have_text("Willkommen bei WordPress!")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
from icecream import ic
|
||||||
|
|
||||||
|
from pytest_abra.custom_fixtures import Message
|
||||||
|
|
||||||
|
|
||||||
|
def test_demo(imap_recent_messages: list[Message]):
|
||||||
|
for message in imap_recent_messages:
|
||||||
|
print(dir(message))
|
||||||
|
ic(message.subject)
|
||||||
|
ic(message.body["plain"])
|
||||||
|
|
||||||
|
exit()
|
||||||
|
assert False
|
||||||
|
|
@ -20,7 +20,7 @@ def test_complex_sorting() -> None:
|
||||||
]
|
]
|
||||||
|
|
||||||
demo_types = ["a", "b", "c", "d", "e", "f", "g"]
|
demo_types = ["a", "b", "c", "d", "e", "f", "g"]
|
||||||
env_files = [EnvFile(env_type=t, env_path=Path(), config=dict()) for t in demo_types]
|
env_files = [EnvFile(env_type=t, env_path=Path(), env_config=dict()) for t in demo_types]
|
||||||
EnvManager.sort_env_files_by_rule
|
EnvManager.sort_env_files_by_rule
|
||||||
sorted_env_files = EnvManager.sort_env_files_by_rule(env_files, demo_rules)
|
sorted_env_files = EnvManager.sort_env_files_by_rule(env_files, demo_rules)
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ def test_circular_import() -> None:
|
||||||
]
|
]
|
||||||
|
|
||||||
demo_types = ["a", "b", "c"]
|
demo_types = ["a", "b", "c"]
|
||||||
env_files = [EnvFile(env_type=t, env_path=Path(), config=dict()) for t in demo_types]
|
env_files = [EnvFile(env_type=t, env_path=Path(), env_config=dict()) for t in demo_types]
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
EnvManager.sort_env_files_by_rule(env_files, demo_rules)
|
EnvManager.sort_env_files_by_rule(env_files, demo_rules)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue