This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-release.git
The following commit(s) were added to refs/heads/main by this push:
new 3afe58c Allow slow browser tests to be skipped
3afe58c is described below
commit 3afe58c369cc91f4e082c6ed2e0eb0902f4184e7
Author: Sean B. Palmer <[email protected]>
AuthorDate: Mon Apr 14 18:46:31 2025 +0100
Allow slow browser tests to be skipped
---
Makefile | 2 +-
playwright/test.py | 215 +++++++++++++++++++++++++++++------------------------
2 files changed, 117 insertions(+), 100 deletions(-)
diff --git a/Makefile b/Makefile
index 592293b..061db2a 100644
--- a/Makefile
+++ b/Makefile
@@ -55,7 +55,7 @@ run-dev:
BIND=127.0.0.1:4443 scripts/run
run-playwright:
- docker run --net=host -it atr-playwright python3 test.py
+ docker run --net=host -it atr-playwright python3 test.py --skip-slow
run-staging:
BIND=127.0.0.1:8443 scripts/run
diff --git a/playwright/test.py b/playwright/test.py
index fd6d865..da9917b 100644
--- a/playwright/test.py
+++ b/playwright/test.py
@@ -26,7 +26,8 @@ import re
import socket
import subprocess
import urllib.parse
-from typing import Final
+from collections.abc import Callable
+from typing import Any, Final
import netifaces
import rich.logging
@@ -91,6 +92,11 @@ def main() -> None:
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
help="Set the logging level, default is INFO",
)
+ parser.add_argument(
+ "--skip-slow",
+ action="store_true",
+ help="Skip slow tests",
+ )
args = parser.parse_args()
log_level = getattr(logging, args.log.upper(), logging.INFO)
@@ -103,10 +109,10 @@ def main() -> None:
)
logging.debug(f"Log level set to {args.log.upper()}")
- run_tests()
+ run_tests(args.skip_slow)
-def run_tests() -> None:
+def run_tests(skip_slow: bool) -> None:
if (credentials := get_credentials()) is None:
logging.error("Cannot run tests: no credentials provided")
return
@@ -117,7 +123,7 @@ def run_tests() -> None:
try:
browser = p.chromium.launch()
context = browser.new_context(ignore_https_errors=True)
- run_tests_in_context(context, credentials)
+ run_tests_in_context(context, credentials, skip_slow)
except Exception as e:
logging.error(f"Error during page interaction: {e}", exc_info=True)
@@ -128,13 +134,21 @@ def run_tests() -> None:
browser.close()
-def run_tests_in_context(context: sync_api.BrowserContext, credentials:
Credentials) -> None:
+def run_tests_in_context(context: sync_api.BrowserContext, credentials:
Credentials, skip_slow: bool) -> None:
ssh_keys_generate()
page = context.new_page()
- test_all(page, credentials)
+ test_all(page, credentials, skip_slow)
logging.info("Tests finished successfully")
+def run_tests_skipping_slow(tests: list[Callable[..., Any]], page:
sync_api.Page, skip_slow: bool) -> None:
+ for test in tests:
+ if skip_slow and ("slow" in test.__annotations__):
+ logging.info(f"Skipping slow test: {test.__name__}")
+ continue
+ test(page)
+
+
def show_default_gateway_ip() -> None:
match get_default_gateway_ip():
case str(ip_address):
@@ -143,6 +157,11 @@ def show_default_gateway_ip() -> None:
logging.warning("Could not determine gateway IP")
+def slow(func: Callable[..., Any]) -> Callable[..., Any]:
+ func.__annotations__["slow"] = True
+ return func
+
+
def ssh_keys_generate() -> None:
ssh_key_path = _SSH_KEY_PATH
ssh_dir = os.path.dirname(ssh_key_path)
@@ -158,7 +177,7 @@ def ssh_keys_generate() -> None:
os.makedirs(ssh_dir, mode=0o700, exist_ok=True)
logging.info(f"Generating new SSH key at {ssh_key_path}")
- result = subprocess.run(
+ subprocess.run(
["ssh-keygen", "-t", "ed25519", "-f", ssh_key_path, "-N", ""],
check=True,
capture_output=True,
@@ -172,30 +191,37 @@ def ssh_keys_generate() -> None:
logging.error(f"ssh-keygen stderr: {e.stderr}")
raise RuntimeError("SSH key generation failed") from e
- if result.returncode != 0:
- logging.error(f"ssh-keygen returned {result.returncode}")
- logging.error(f"ssh-keygen stdout: {result.stdout}")
- logging.error(f"ssh-keygen stderr: {result.stderr}")
- raise RuntimeError("SSH key generation failed")
-
-def test_all(page: sync_api.Page, credentials: Credentials) -> None:
+def test_all(page: sync_api.Page, credentials: Credentials, skip_slow: bool)
-> None:
test_login(page, credentials)
test_tidy_up(page)
- test_lifecycle(page)
- test_projects(page)
- test_ssh(page)
-
-def test_lifecycle(page: sync_api.Page) -> None:
- test_lifecycle_01_add_draft(page)
- test_lifecycle_02_check_draft_added(page)
- test_lifecycle_03_add_file(page)
- test_lifecycle_04_promote_to_candidate(page)
- test_lifecycle_05_vote_on_candidate(page)
- test_lifecycle_06_resolve_vote(page)
- test_lifecycle_07_promote_preview(page)
- test_lifecycle_08_release_exists(page)
+ # Declare all tests
+ tests = {}
+ tests["lifecycle"] = [
+ test_lifecycle_01_add_draft,
+ test_lifecycle_02_check_draft_added,
+ test_lifecycle_03_add_file,
+ test_lifecycle_04_promote_to_candidate,
+ test_lifecycle_05_vote_on_candidate,
+ test_lifecycle_06_resolve_vote,
+ test_lifecycle_07_promote_preview,
+ test_lifecycle_08_release_exists,
+ ]
+ tests["projects"] = [
+ test_projects_01_update,
+ test_projects_02_check_directory,
+ test_projects_03_add_project,
+ ]
+ tests["ssh"] = [
+ test_ssh_01_add_key,
+ ]
+
+ # Order between our tests must be preserved
+ # Insertion order is reliable since Python 3.6
+ # Therefore iteration over tests matches the insertion order above
+ for key in tests:
+ run_tests_skipping_slow(tests[key], page, skip_slow)
def test_lifecycle_01_add_draft(page: sync_api.Page) -> None:
@@ -432,12 +458,7 @@ def test_login(page: sync_api.Page, credentials:
Credentials) -> None:
logging.info("Login actions completed successfully")
-def test_projects(page: sync_api.Page) -> None:
- test_projects_01_update(page)
- test_projects_02_check_directory(page)
- test_projects_03_add_project(page)
-
-
+@slow
def test_projects_01_update(page: sync_api.Page) -> None:
logging.info("Navigating to the admin update projects page")
go_to_path(page, "/admin/projects/update")
@@ -501,6 +522,68 @@ def test_projects_03_add_project(page: sync_api.Page) ->
None:
logging.info("Project title confirmed on view page")
+def test_ssh_01_add_key(page: sync_api.Page) -> None:
+ logging.info("Starting SSH key addition test")
+ go_to_path(page, "/")
+
+ logging.info("Navigating to Your Public Keys page")
+ page.locator('a[href="/keys"]:has-text("Your public keys")').click()
+ wait_for_path(page, "/keys")
+ logging.info("Navigated to Your Public Keys page")
+
+ logging.info("Clicking Add an SSH key button")
+ page.locator('a[href="/keys/ssh/add"]:has-text("Add an SSH key")').click()
+ wait_for_path(page, "/keys/ssh/add")
+ logging.info("Navigated to Add SSH Key page")
+
+ public_key_path = f"{_SSH_KEY_PATH}.pub"
+ try:
+ logging.info(f"Reading public key from {public_key_path}")
+ with open(public_key_path, encoding="utf-8") as f:
+ public_key_content = f.read().strip()
+ logging.info("Public key read successfully")
+ except OSError as e:
+ logging.error(f"Failed to read public key file {public_key_path}: {e}")
+ raise RuntimeError("Failed to read public key file") from e
+
+ logging.info("Pasting public key into textarea")
+ page.locator('textarea[name="key"]').fill(public_key_content)
+
+ logging.info("Submitting the Add SSH key form")
+ page.locator('input[type="submit"][value="Add SSH key"]').click()
+
+ logging.info("Waiting for navigation back to /keys page")
+ wait_for_path(page, "/keys")
+ logging.info("Navigated back to /keys page")
+
+ try:
+ logging.info("Calculating expected SSH key fingerprint using
ssh-keygen -lf")
+ result = subprocess.run(
+ ["ssh-keygen", "-lf", public_key_path],
+ check=True,
+ capture_output=True,
+ text=True,
+ )
+ fingerprint_output = result.stdout.strip()
+ match = re.search(r"SHA256:([\w\+/=]+)", fingerprint_output)
+ if not match:
+ logging.error(f"Could not parse fingerprint from ssh-keygen
output: {fingerprint_output}")
+ raise RuntimeError("Failed to parse SSH key fingerprint")
+ expected_fingerprint = f"SHA256:{match.group(1)}"
+ logging.info(f"Expected fingerprint: {expected_fingerprint}")
+
+ except (subprocess.CalledProcessError, FileNotFoundError, RuntimeError) as
e:
+ logging.error(f"Failed to get SSH key fingerprint: {e}")
+ if isinstance(e, subprocess.CalledProcessError):
+ logging.error(f"ssh-keygen stderr: {e.stderr}")
+ raise RuntimeError("Failed to get SSH key fingerprint") from e
+
+ logging.info("Verifying that the added SSH key fingerprint is visible")
+ key_card_locator =
page.locator(f'div.card:has(td:has-text("{expected_fingerprint}"))')
+ sync_api.expect(key_card_locator).to_be_visible()
+ logging.info("SSH key fingerprint verified successfully on /keys page")
+
+
def test_tidy_up(page: sync_api.Page) -> None:
test_tidy_up_release(page)
test_tidy_up_project(page)
@@ -577,72 +660,6 @@ def test_tidy_up_release(page: sync_api.Page) -> None:
logging.info("Could not find the tooling-0.1 release, no deletion
needed")
-def test_ssh(page: sync_api.Page) -> None:
- test_ssh_01_add_key(page)
-
-
-def test_ssh_01_add_key(page: sync_api.Page) -> None:
- logging.info("Starting SSH key addition test")
- go_to_path(page, "/")
-
- logging.info("Navigating to Your Public Keys page")
- page.locator('a[href="/keys"]:has-text("Your public keys")').click()
- wait_for_path(page, "/keys")
- logging.info("Navigated to Your Public Keys page")
-
- logging.info("Clicking Add an SSH key button")
- page.locator('a[href="/keys/ssh/add"]:has-text("Add an SSH key")').click()
- wait_for_path(page, "/keys/ssh/add")
- logging.info("Navigated to Add SSH Key page")
-
- public_key_path = f"{_SSH_KEY_PATH}.pub"
- try:
- logging.info(f"Reading public key from {public_key_path}")
- with open(public_key_path, encoding="utf-8") as f:
- public_key_content = f.read().strip()
- logging.info("Public key read successfully")
- except OSError as e:
- logging.error(f"Failed to read public key file {public_key_path}: {e}")
- raise RuntimeError("Failed to read public key file") from e
-
- logging.info("Pasting public key into textarea")
- page.locator('textarea[name="key"]').fill(public_key_content)
-
- logging.info("Submitting the Add SSH key form")
- page.locator('input[type="submit"][value="Add SSH key"]').click()
-
- logging.info("Waiting for navigation back to /keys page")
- wait_for_path(page, "/keys")
- logging.info("Navigated back to /keys page")
-
- try:
- logging.info("Calculating expected SSH key fingerprint using
ssh-keygen -lf")
- result = subprocess.run(
- ["ssh-keygen", "-lf", public_key_path],
- check=True,
- capture_output=True,
- text=True,
- )
- fingerprint_output = result.stdout.strip()
- match = re.search(r"SHA256:([\w\+/=]+)", fingerprint_output)
- if not match:
- logging.error(f"Could not parse fingerprint from ssh-keygen
output: {fingerprint_output}")
- raise RuntimeError("Failed to parse SSH key fingerprint")
- expected_fingerprint = f"SHA256:{match.group(1)}"
- logging.info(f"Expected fingerprint: {expected_fingerprint}")
-
- except (subprocess.CalledProcessError, FileNotFoundError, RuntimeError) as
e:
- logging.error(f"Failed to get SSH key fingerprint: {e}")
- if isinstance(e, subprocess.CalledProcessError):
- logging.error(f"ssh-keygen stderr: {e.stderr}")
- raise RuntimeError("Failed to get SSH key fingerprint") from e
-
- logging.info("Verifying that the added SSH key fingerprint is visible")
- key_card_locator =
page.locator(f'div.card:has(td:has-text("{expected_fingerprint}"))')
- sync_api.expect(key_card_locator).to_be_visible()
- logging.info("SSH key fingerprint verified successfully on /keys page")
-
-
def wait_for_path(page: sync_api.Page, path: str) -> None:
page.wait_for_load_state()
parsed_url = urllib.parse.urlparse(page.url)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]