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.
- skipper_playwright-0.1.0/.gitignore +50 -0
- skipper_playwright-0.1.0/PKG-INFO +55 -0
- skipper_playwright-0.1.0/README.md +37 -0
- skipper_playwright-0.1.0/pyproject.toml +33 -0
- skipper_playwright-0.1.0/src/skipper_playwright/__init__.py +5 -0
- skipper_playwright-0.1.0/src/skipper_playwright/test.py +112 -0
|
@@ -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,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)
|