skipper-playwright 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,50 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.pyd
6
+ .Python
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ *.egg
11
+ .eggs/
12
+
13
+ # Virtual environments
14
+ .venv/
15
+ venv/
16
+ env/
17
+
18
+ # uv
19
+ .uv/
20
+
21
+ # Testing
22
+ .pytest_cache/
23
+ .coverage
24
+ htmlcov/
25
+ .tox/
26
+ .nox/
27
+
28
+ # Type checking
29
+ .mypy_cache/
30
+
31
+ # IDE
32
+ .idea/
33
+ .vscode/
34
+ *.swp
35
+ *.swo
36
+
37
+ # Temp files
38
+ *.tmp
39
+ /tmp/
40
+
41
+ # Credentials (never commit)
42
+ service-account-skipper-bot.json
43
+ *.json.bak
44
+
45
+ # Environment
46
+ .env
47
+ .env.local
48
+
49
+ # macOS
50
+ .DS_Store
@@ -0,0 +1,55 @@
1
+ Metadata-Version: 2.4
2
+ Name: skipper-playwright
3
+ Version: 0.1.0
4
+ Summary: Playwright integration for Skipper test-gating via Google Spreadsheet
5
+ Project-URL: Homepage, https://github.com/get-skipper/skipper-python
6
+ Project-URL: Repository, https://github.com/get-skipper/skipper-python
7
+ License: MIT
8
+ Keywords: google-sheets,playwright,skipper,test-gating,testing
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Software Development :: Testing
14
+ Requires-Python: >=3.10
15
+ Requires-Dist: pytest>=7.0
16
+ Requires-Dist: skipper-core>=0.1.0
17
+ Description-Content-Type: text/markdown
18
+
19
+ # skipper-playwright
20
+
21
+ Playwright integration for [Skipper](https://github.com/get-skipper/skipper-python) test-gating via Google Spreadsheet.
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pip install skipper-playwright playwright
27
+ playwright install
28
+ ```
29
+
30
+ ## Setup
31
+
32
+ Inherit from `SkipperSyncTest` instead of writing plain test functions:
33
+
34
+ ```python
35
+ from skipper_playwright import SkipperSyncTest
36
+ from skipper_core import SkipperConfig, FileCredentials
37
+ from playwright.sync_api import Page
38
+
39
+ class LoginTests(SkipperSyncTest):
40
+ skipper_config = SkipperConfig(
41
+ spreadsheet_id="YOUR_SPREADSHEET_ID",
42
+ credentials=FileCredentials("./service-account-skipper-bot.json"),
43
+ sheet_name="skipper-python",
44
+ )
45
+
46
+ def test_login(self, page: Page):
47
+ page.goto("https://example.com")
48
+ ...
49
+ ```
50
+
51
+ ## Test ID Format
52
+
53
+ `tests/test_login.py > ClassName > test_method_name`
54
+
55
+ See the [root README](../../README.md) for full documentation.
@@ -0,0 +1,37 @@
1
+ # skipper-playwright
2
+
3
+ Playwright integration for [Skipper](https://github.com/get-skipper/skipper-python) test-gating via Google Spreadsheet.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install skipper-playwright playwright
9
+ playwright install
10
+ ```
11
+
12
+ ## Setup
13
+
14
+ Inherit from `SkipperSyncTest` instead of writing plain test functions:
15
+
16
+ ```python
17
+ from skipper_playwright import SkipperSyncTest
18
+ from skipper_core import SkipperConfig, FileCredentials
19
+ from playwright.sync_api import Page
20
+
21
+ class LoginTests(SkipperSyncTest):
22
+ skipper_config = SkipperConfig(
23
+ spreadsheet_id="YOUR_SPREADSHEET_ID",
24
+ credentials=FileCredentials("./service-account-skipper-bot.json"),
25
+ sheet_name="skipper-python",
26
+ )
27
+
28
+ def test_login(self, page: Page):
29
+ page.goto("https://example.com")
30
+ ...
31
+ ```
32
+
33
+ ## Test ID Format
34
+
35
+ `tests/test_login.py > ClassName > test_method_name`
36
+
37
+ See the [root README](../../README.md) for full documentation.
@@ -0,0 +1,33 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "skipper-playwright"
7
+ version = "0.1.0"
8
+ description = "Playwright integration for Skipper test-gating via Google Spreadsheet"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.10"
12
+ keywords = ["testing", "test-gating", "google-sheets", "skipper", "playwright"]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Python :: 3",
18
+ "Topic :: Software Development :: Testing",
19
+ ]
20
+ dependencies = [
21
+ "skipper-core>=0.1.0",
22
+ "pytest>=7.0",
23
+ ]
24
+
25
+ [tool.uv.sources]
26
+ skipper-core = { workspace = true }
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/get-skipper/skipper-python"
30
+ Repository = "https://github.com/get-skipper/skipper-python"
31
+
32
+ [tool.hatch.build.targets.wheel]
33
+ packages = ["src/skipper_playwright"]
@@ -0,0 +1,5 @@
1
+ """skipper-playwright — Playwright integration for Skipper test-gating."""
2
+
3
+ from .test import SkipperSyncTest
4
+
5
+ __all__ = ["SkipperSyncTest"]
@@ -0,0 +1,112 @@
1
+ """skipper-playwright — Playwright base class for Skipper test-gating."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect
6
+ import os
7
+ import threading
8
+ from typing import Any
9
+
10
+ import pytest
11
+ from skipper_core import (
12
+ CacheManager,
13
+ SkipperConfig,
14
+ SkipperResolver,
15
+ build_test_id,
16
+ mode_from_env,
17
+ )
18
+ from skipper_core.writer import SheetsWriter
19
+
20
+ _cache_manager = CacheManager()
21
+
22
+
23
+ class SkipperSyncTest:
24
+ """Base class for synchronous Playwright tests (pytest-playwright style).
25
+
26
+ Inherit from this class instead of using raw pytest functions to get
27
+ automatic test-gating. Set ``skipper_config`` as a class attribute.
28
+
29
+ Example::
30
+
31
+ from playwright.sync_api import Page
32
+
33
+ class LoginTests(SkipperSyncTest):
34
+ skipper_config = SkipperConfig(
35
+ spreadsheet_id="YOUR_SPREADSHEET_ID",
36
+ credentials=FileCredentials("./service-account-skipper-bot.json"),
37
+ sheet_name="skipper-python",
38
+ )
39
+
40
+ def test_login(self, page: Page):
41
+ ...
42
+ """
43
+
44
+ skipper_config: SkipperConfig # set by subclass
45
+
46
+ _skipper_resolver: SkipperResolver | None = None
47
+ _skipper_cache_dir: str | None = None
48
+ _skipper_discovered: list[str]
49
+ _skipper_lock: threading.Lock
50
+
51
+ @classmethod
52
+ def setup_class(cls) -> None:
53
+ cls._skipper_discovered = []
54
+ cls._skipper_lock = threading.Lock()
55
+
56
+ cfg = getattr(cls, "skipper_config", None)
57
+ if cfg is None:
58
+ return
59
+
60
+ cache_file = os.getenv("SKIPPER_CACHE_FILE")
61
+ if cache_file:
62
+ data = _cache_manager.read_resolver_cache(cache_file)
63
+ cls._skipper_resolver = SkipperResolver.from_marshal_cache(data)
64
+ return
65
+
66
+ resolver = SkipperResolver(cfg)
67
+ resolver.initialize()
68
+ cls._skipper_resolver = resolver
69
+
70
+ data = resolver.marshal_cache()
71
+ cache_dir = _cache_manager.write_resolver_cache(data)
72
+ cls._skipper_cache_dir = cache_dir
73
+ os.environ["SKIPPER_CACHE_FILE"] = os.path.join(cache_dir, "cache.json")
74
+ os.environ["SKIPPER_DISCOVERED_DIR"] = cache_dir
75
+
76
+ @classmethod
77
+ def teardown_class(cls) -> None:
78
+ try:
79
+ cfg = getattr(cls, "skipper_config", None)
80
+ if cfg is not None and mode_from_env().value == "sync":
81
+ with cls._skipper_lock:
82
+ ids = list(cls._skipper_discovered)
83
+
84
+ if cls._skipper_cache_dir:
85
+ _cache_manager.write_discovered_ids(cls._skipper_cache_dir, ids)
86
+ all_ids = _cache_manager.merge_discovered_ids(cls._skipper_cache_dir)
87
+ else:
88
+ all_ids = ids
89
+
90
+ writer = SheetsWriter(cfg)
91
+ writer.sync(all_ids)
92
+ finally:
93
+ if cls._skipper_cache_dir:
94
+ _cache_manager.cleanup(cls._skipper_cache_dir)
95
+ cls._skipper_cache_dir = None
96
+
97
+ def setup_method(self, method: Any) -> None:
98
+ if self._skipper_resolver is None:
99
+ return
100
+
101
+ file_path = inspect.getfile(type(self))
102
+ test_id = build_test_id(file_path, [type(self).__name__, method.__name__])
103
+
104
+ with self._skipper_lock:
105
+ self._skipper_discovered.append(test_id)
106
+
107
+ if not self._skipper_resolver.is_test_enabled(test_id):
108
+ until = self._skipper_resolver.get_disabled_until(test_id)
109
+ msg = "[skipper] Test disabled"
110
+ if until is not None:
111
+ msg += f" until {until.strftime('%Y-%m-%d')}"
112
+ pytest.skip(msg)