cellenium 0.3.2__tar.gz → 0.3.4__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.
- {cellenium-0.3.2 → cellenium-0.3.4}/PKG-INFO +1 -1
- {cellenium-0.3.2 → cellenium-0.3.4}/README.md +209 -209
- {cellenium-0.3.2 → cellenium-0.3.4}/pyproject.toml +45 -45
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/ai/agents/vision/crew.py +15 -2
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/api/vision.py +5 -3
- {cellenium-0.3.2 → cellenium-0.3.4}/tests/e2e/test_google_search.py +4 -2
- cellenium-0.3.4/tests/test_vision_agent.py +37 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/tests/test_vision_api.py +68 -68
- {cellenium-0.3.2 → cellenium-0.3.4}/uv.lock +3865 -3865
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/bin/checklist.js +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/commands/cellenium/artifact.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/commands/cellenium/checklist/before-startup.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/commands/cellenium/checklist/cruising.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/commands/cellenium/checklist/fordec.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/commands/cellenium/checklist/landing.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/commands/cellenium/evaluator.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/commands/cellenium/executor.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/commands/cellenium/locator-writer.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/commands/cellenium/prd-reader.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/commands/cellenium/test-spec.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/commands/cellenium/web.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/commands/cellenium/writer.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/cellenium/artifact/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/cellenium/before-startup/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/cellenium/cruising/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/cellenium/emergency/fordec/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/cellenium/evaluator/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/cellenium/executor/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/cellenium/landing/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/cellenium/locator-writer/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/cellenium/prd-reader/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/cellenium/test-spec/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/cellenium/web/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/cellenium/writer/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/checklist/before-startup/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/checklist/cruising/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/checklist/emergency/fordec/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/skills/checklist/landing/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.claude/test/install.test.js +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.env.example +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/.gitignore +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/LICENSE +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/__init__.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/ai/__init__.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/ai/agents/__init__.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/ai/agents/vision/__init__.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/ai/agents/vision/config/agents.yaml +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/ai/agents/vision/config/tasks.yaml +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/ai/agents/vision/schemas.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/ai/agents/vision/skills/evaluator/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/ai/agents/vision/skills/vision/SKILL.md +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/ai/agents/vision/tools/vision.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/ai/config.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/ai/tools/__init__.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/ai/tools/image_encoder.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/api/__init__.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/cli.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/engine/__init__.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/engine/manager.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/engine/page_engine.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/functions/__init__.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/functions/locators.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/functions/logger.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/functions/sheets.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/settings.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/src/cellenium/templates/env.template +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/tests/e2e/conftest.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/tests/test_cli.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/tests/test_google_sheet_locators.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/tests/test_page_engine.py +0 -0
- {cellenium-0.3.2 → cellenium-0.3.4}/tests/test_settings.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cellenium
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Playwright browser automation with a Google Sheets Page Object Model and CrewAI vision analysis
|
|
5
5
|
Project-URL: Homepage, https://github.com/MedviJenka/cellenium-playwright
|
|
6
6
|
License-File: LICENSE
|
|
@@ -1,209 +1,209 @@
|
|
|
1
|
-
# Cellenium Playwright
|
|
2
|
-
|
|
3
|
-
Cellenium Playwright is an experimental Python browser-automation toolkit built on [Playwright](https://playwright.dev/python/). It provides browser lifecycle management, a Google-Sheets-backed Page Object Model (POM), screenshot capture, and components for structured visual analysis with CrewAI.
|
|
4
|
-
|
|
5
|
-
The repository also contains **TestFlow**, a set of Claude Code skills that turns product requirements and technical specifications into traceable pytest tests, validated UI locators, execution evidence, and a test-quality verdict.
|
|
6
|
-
|
|
7
|
-
> **Status:** active development. The browser and locator layers are usable, but the checked-in vision test is still a scaffold and does not currently pass. Install it from PyPI with `pip install cellenium` (or `uv add cellenium`), then run `playwright install chromium`. Configuration (`.env`, `credentials.json`) is read from your project's working directory.
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
11
|
-
- Synchronous Chromium, Firefox, or WebKit sessions through Playwright.
|
|
12
|
-
- Reusable `PageEngine` helpers for navigation, locators, screenshots, scrolling, dropdowns, and tabs.
|
|
13
|
-
- Page Object Model data read directly from Google Sheets at runtime.
|
|
14
|
-
- Locator strategies for name, XPath, ID, CSS, class name, link text, and tag name.
|
|
15
|
-
- Structured image-analysis components built with CrewAI and Pydantic.
|
|
16
|
-
- Parallel pytest execution in CI on Python 3.13 and 3.14.
|
|
17
|
-
- Optional TestFlow skills for end-to-end pytest test generation and evaluation.
|
|
18
|
-
|
|
19
|
-
## Requirements
|
|
20
|
-
|
|
21
|
-
- Python 3.13 or 3.14
|
|
22
|
-
- [`uv`](https://docs.astral.sh/uv/)
|
|
23
|
-
- A Playwright-supported browser
|
|
24
|
-
- A Google service account for resolving locators
|
|
25
|
-
- An OpenAI-compatible model and API key when using the vision components
|
|
26
|
-
|
|
27
|
-
## Quick start
|
|
28
|
-
|
|
29
|
-
Clone the repository and install the locked dependencies:
|
|
30
|
-
|
|
31
|
-
```sh
|
|
32
|
-
git clone https://github.com/MedviJenka/cellenium-playwright.git
|
|
33
|
-
cd cellenium-playwright
|
|
34
|
-
uv sync --frozen
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Install Chromium:
|
|
38
|
-
|
|
39
|
-
```sh
|
|
40
|
-
uv run playwright install chromium
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
On Ubuntu or another Debian-based CI runner, install Chromium and its system dependencies together:
|
|
44
|
-
|
|
45
|
-
```sh
|
|
46
|
-
uv run playwright install --with-deps chromium
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Configuration
|
|
50
|
-
|
|
51
|
-
In the project where you use cellenium, run:
|
|
52
|
-
|
|
53
|
-
```sh
|
|
54
|
-
cellenium init # writes a .env template, adds .env and credentials.json to .gitignore, installs Chromium and the Claude Code commands
|
|
55
|
-
cellenium claude # (re)installs the /cellenium:* Claude Code commands and skills into .claude/
|
|
56
|
-
cellenium doctor # checks settings, credentials.json, Google Sheet access, and that Chromium launches
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
`cellenium init --with-deps` also installs Chromium's system libraries (Linux/CI). `cellenium doctor --offline` skips opening the sheet; it exits non-zero when a check fails, so it can gate CI.
|
|
60
|
-
|
|
61
|
-
`cellenium claude --global` installs the commands into `~/.claude` so they are available in every project; locally modified files are kept unless you pass `--force`. Restart Claude Code after installing. `cellenium init --skip-claude` skips this step.
|
|
62
|
-
|
|
63
|
-
Settings are read from `.env` in the working directory or from environment variables:
|
|
64
|
-
|
|
65
|
-
```dotenv
|
|
66
|
-
GOOGLE_SHEETS=<spreadsheet-edit-url>
|
|
67
|
-
CREDENTIALS_JSON=credentials.json
|
|
68
|
-
OPENAI_MODEL=<model-name>
|
|
69
|
-
OPENAI_API_KEY=<optional-api-key>
|
|
70
|
-
LOGFIRE_TOKEN=<optional-logfire-token>
|
|
71
|
-
TEST_HEADLESS=true
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
`GOOGLE_SHEETS` selects the locator spreadsheet. When `LOGFIRE_TOKEN` is omitted, logging remains local and no data is sent to Logfire.
|
|
75
|
-
|
|
76
|
-
Save the Google service-account key as `credentials.json` and share the locator spreadsheet with its `client_email` as a viewer (`cellenium doctor` prints the address). Never commit `.env` or `credentials.json`.
|
|
77
|
-
|
|
78
|
-
## Google Sheets Page Object Model
|
|
79
|
-
|
|
80
|
-
Each worksheet tab represents one screen. The first row must contain these columns:
|
|
81
|
-
|
|
82
|
-
| Column | Purpose |
|
|
83
|
-
|---|---|
|
|
84
|
-
| `name` | Logical element name used by tests |
|
|
85
|
-
| `locator` | Locator strategy such as `NAME`, `XPATH`, `ID`, or `CSS` |
|
|
86
|
-
| `type` | Locator value; this name is retained for compatibility with the source sheet |
|
|
87
|
-
| `actions` | Optional metadata |
|
|
88
|
-
| `comments` | Optional notes |
|
|
89
|
-
|
|
90
|
-
Example worksheet:
|
|
91
|
-
|
|
92
|
-
| name | locator | type | actions | comments |
|
|
93
|
-
|---|---|---|---|---|
|
|
94
|
-
| search | NAME | q | fill | Google search field |
|
|
95
|
-
| button | NAME | btnK | click | Search button |
|
|
96
|
-
|
|
97
|
-
`PageEngine(screen="Google")` reads locator rows directly from the worksheet tab
|
|
98
|
-
named `Google`. No generated JSON mapping or synchronization step is required.
|
|
99
|
-
|
|
100
|
-
## Browser automation
|
|
101
|
-
|
|
102
|
-
```python
|
|
103
|
-
from src.core.engine.page_engine import PageEngine
|
|
104
|
-
|
|
105
|
-
engine = PageEngine(screen="Google", headless=True)
|
|
106
|
-
|
|
107
|
-
try:
|
|
108
|
-
engine.get_web("https://www.google.com")
|
|
109
|
-
engine.get_element("search").fill("cats")
|
|
110
|
-
engine.get_element("button").click()
|
|
111
|
-
screenshot = engine.get_screenshot("google-results")
|
|
112
|
-
print(screenshot)
|
|
113
|
-
finally:
|
|
114
|
-
engine.teardown()
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
`screen="Google"` selects the worksheet tab named `Google`. With multiple tabs, pass the page's tab name when constructing `PageEngine` (for example, `Google`, `Heroku`, or `ST`). Missing worksheets, missing element names, and unsupported locator strategies raise explicit exceptions.
|
|
118
|
-
|
|
119
|
-
## Tests and linting
|
|
120
|
-
|
|
121
|
-
Run the test suite:
|
|
122
|
-
|
|
123
|
-
```sh
|
|
124
|
-
uv run --frozen pytest src/tests tests -v -n auto --dist loadscope
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
`src/tests/test_google_search.py` exercises the live browser flow. The unit regressions in `tests/` cover screenshot persistence and direct worksheet selection without contacting external services.
|
|
128
|
-
|
|
129
|
-
Run the blocking CI lint checks:
|
|
130
|
-
|
|
131
|
-
```sh
|
|
132
|
-
uvx --from flake8==7.4.1 flake8 main.py settings.py src \
|
|
133
|
-
--count --select=E9,F63,F7,F82 --show-source --statistics
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
The GitHub Actions workflow in `.github/workflows/python-package.yml` performs locked dependency installation, installs Chromium, runs lint checks, and executes the tests under Xvfb for Python 3.13 and 3.14.
|
|
137
|
-
|
|
138
|
-
## TestFlow Claude Code skills
|
|
139
|
-
|
|
140
|
-
TestFlow separates pytest creation into focused stages:
|
|
141
|
-
|
|
142
|
-
```text
|
|
143
|
-
PRD → test specification → UI locators → pytest writer → executor → evaluator
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
| Order | Skill | Purpose |
|
|
147
|
-
|---:|---|---|
|
|
148
|
-
| Optional | `web` | Explore a live application in a real browser and produce reproducible bug evidence |
|
|
149
|
-
| 1 | `prd-reader` | Extract explicit, derived, and unknown test requirements from the PRD |
|
|
150
|
-
| 2 | `test-spec` | Design traceable pytest cases, boundaries, fixtures, and execution prerequisites |
|
|
151
|
-
| 3 | `locator-writer` | For UI cases, validate stable Playwright selectors and upsert elements into Google Sheets |
|
|
152
|
-
| 4 | `writer` | Implement deterministic, behavior-focused pytest tests |
|
|
153
|
-
| 5 | `executor` | Run collection, targeted tests, related tests, and classify failures |
|
|
154
|
-
| 6 | `evaluator` | Evaluate traceability, regression sensitivity, coverage quality, and execution evidence |
|
|
155
|
-
|
|
156
|
-
`artifact` orchestrates the complete pipeline and repeats only the affected downstream stages when evaluation finds a defect.
|
|
157
|
-
|
|
158
|
-
`web` is a report-only exploratory entry point. Verified findings become regression candidates for `test-spec`; it does not modify application code.
|
|
159
|
-
|
|
160
|
-
Validate the Claude Code plugin metadata:
|
|
161
|
-
|
|
162
|
-
```sh
|
|
163
|
-
claude plugin validate .
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
Run Claude Code from the repository and invoke either the complete pipeline or an individual stage:
|
|
167
|
-
|
|
168
|
-
```text
|
|
169
|
-
/cellenium:web
|
|
170
|
-
/cellenium:artifact
|
|
171
|
-
/cellenium:prd-reader
|
|
172
|
-
/cellenium:test-spec
|
|
173
|
-
/cellenium:locator-writer
|
|
174
|
-
/cellenium:writer
|
|
175
|
-
/cellenium:executor
|
|
176
|
-
/cellenium:evaluator
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
The skills are manual-only. The locator writer previews changes before replacing existing Google Sheet selectors and verifies selectors against the live UI.
|
|
180
|
-
|
|
181
|
-
The separate checklist skills live under `.claude/skills/checklist/`: `before-startup` (system ready to build tests), `cruising` (everything in place, critical errors and potential bugs), `landing` (final production readiness), and the standalone `fordec` emergency decision skill.
|
|
182
|
-
|
|
183
|
-
## Project layout
|
|
184
|
-
|
|
185
|
-
```text
|
|
186
|
-
.github/workflows/ GitHub Actions CI
|
|
187
|
-
.claude/skills/cellenium/ Pytest TestFlow skill definitions
|
|
188
|
-
.claude/skills/checklist/ Checklist skills (before-startup, cruising, landing, fordec)
|
|
189
|
-
.claude/commands/cellenium/ TestFlow slash-command entries
|
|
190
|
-
.claude-plugin/plugin.json Claude Code plugin metadata
|
|
191
|
-
src/core/engine/ Playwright browser and page engines
|
|
192
|
-
src/core/functions/ Locator resolution and direct Google Sheets access
|
|
193
|
-
src/core/ai/ CrewAI configuration and vision components
|
|
194
|
-
src/tests/ Browser test scenarios
|
|
195
|
-
settings.py Environment-backed runtime configuration
|
|
196
|
-
pyproject.toml Python requirements and dependencies
|
|
197
|
-
uv.lock Reproducible dependency lockfile
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
## Known limitations
|
|
201
|
-
|
|
202
|
-
- The checked-in vision test uses a placeholder `VisionAssertion` without a `run()` implementation.
|
|
203
|
-
- The sample end-to-end test depends on Google and is not hermetic.
|
|
204
|
-
- Configuration currently requires fields that are reserved but not consumed by every component.
|
|
205
|
-
- The project does not yet define a wheel/sdist build or publishing workflow.
|
|
206
|
-
|
|
207
|
-
## License
|
|
208
|
-
|
|
209
|
-
Licensed under the [ISC License](LICENSE). Copyright © 2026 Jenia Petrusenko.
|
|
1
|
+
# Cellenium Playwright
|
|
2
|
+
|
|
3
|
+
Cellenium Playwright is an experimental Python browser-automation toolkit built on [Playwright](https://playwright.dev/python/). It provides browser lifecycle management, a Google-Sheets-backed Page Object Model (POM), screenshot capture, and components for structured visual analysis with CrewAI.
|
|
4
|
+
|
|
5
|
+
The repository also contains **TestFlow**, a set of Claude Code skills that turns product requirements and technical specifications into traceable pytest tests, validated UI locators, execution evidence, and a test-quality verdict.
|
|
6
|
+
|
|
7
|
+
> **Status:** active development. The browser and locator layers are usable, but the checked-in vision test is still a scaffold and does not currently pass. Install it from PyPI with `pip install cellenium` (or `uv add cellenium`), then run `playwright install chromium`. Configuration (`.env`, `credentials.json`) is read from your project's working directory.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Synchronous Chromium, Firefox, or WebKit sessions through Playwright.
|
|
12
|
+
- Reusable `PageEngine` helpers for navigation, locators, screenshots, scrolling, dropdowns, and tabs.
|
|
13
|
+
- Page Object Model data read directly from Google Sheets at runtime.
|
|
14
|
+
- Locator strategies for name, XPath, ID, CSS, class name, link text, and tag name.
|
|
15
|
+
- Structured image-analysis components built with CrewAI and Pydantic.
|
|
16
|
+
- Parallel pytest execution in CI on Python 3.13 and 3.14.
|
|
17
|
+
- Optional TestFlow skills for end-to-end pytest test generation and evaluation.
|
|
18
|
+
|
|
19
|
+
## Requirements
|
|
20
|
+
|
|
21
|
+
- Python 3.13 or 3.14
|
|
22
|
+
- [`uv`](https://docs.astral.sh/uv/)
|
|
23
|
+
- A Playwright-supported browser
|
|
24
|
+
- A Google service account for resolving locators
|
|
25
|
+
- An OpenAI-compatible model and API key when using the vision components
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
Clone the repository and install the locked dependencies:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
git clone https://github.com/MedviJenka/cellenium-playwright.git
|
|
33
|
+
cd cellenium-playwright
|
|
34
|
+
uv sync --frozen
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Install Chromium:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
uv run playwright install chromium
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
On Ubuntu or another Debian-based CI runner, install Chromium and its system dependencies together:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
uv run playwright install --with-deps chromium
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Configuration
|
|
50
|
+
|
|
51
|
+
In the project where you use cellenium, run:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
cellenium init # writes a .env template, adds .env and credentials.json to .gitignore, installs Chromium and the Claude Code commands
|
|
55
|
+
cellenium claude # (re)installs the /cellenium:* Claude Code commands and skills into .claude/
|
|
56
|
+
cellenium doctor # checks settings, credentials.json, Google Sheet access, and that Chromium launches
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`cellenium init --with-deps` also installs Chromium's system libraries (Linux/CI). `cellenium doctor --offline` skips opening the sheet; it exits non-zero when a check fails, so it can gate CI.
|
|
60
|
+
|
|
61
|
+
`cellenium claude --global` installs the commands into `~/.claude` so they are available in every project; locally modified files are kept unless you pass `--force`. Restart Claude Code after installing. `cellenium init --skip-claude` skips this step.
|
|
62
|
+
|
|
63
|
+
Settings are read from `.env` in the working directory or from environment variables:
|
|
64
|
+
|
|
65
|
+
```dotenv
|
|
66
|
+
GOOGLE_SHEETS=<spreadsheet-edit-url>
|
|
67
|
+
CREDENTIALS_JSON=credentials.json
|
|
68
|
+
OPENAI_MODEL=<model-name>
|
|
69
|
+
OPENAI_API_KEY=<optional-api-key>
|
|
70
|
+
LOGFIRE_TOKEN=<optional-logfire-token>
|
|
71
|
+
TEST_HEADLESS=true
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`GOOGLE_SHEETS` selects the locator spreadsheet. When `LOGFIRE_TOKEN` is omitted, logging remains local and no data is sent to Logfire.
|
|
75
|
+
|
|
76
|
+
Save the Google service-account key as `credentials.json` and share the locator spreadsheet with its `client_email` as a viewer (`cellenium doctor` prints the address). Never commit `.env` or `credentials.json`.
|
|
77
|
+
|
|
78
|
+
## Google Sheets Page Object Model
|
|
79
|
+
|
|
80
|
+
Each worksheet tab represents one screen. The first row must contain these columns:
|
|
81
|
+
|
|
82
|
+
| Column | Purpose |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `name` | Logical element name used by tests |
|
|
85
|
+
| `locator` | Locator strategy such as `NAME`, `XPATH`, `ID`, or `CSS` |
|
|
86
|
+
| `type` | Locator value; this name is retained for compatibility with the source sheet |
|
|
87
|
+
| `actions` | Optional metadata |
|
|
88
|
+
| `comments` | Optional notes |
|
|
89
|
+
|
|
90
|
+
Example worksheet:
|
|
91
|
+
|
|
92
|
+
| name | locator | type | actions | comments |
|
|
93
|
+
|---|---|---|---|---|
|
|
94
|
+
| search | NAME | q | fill | Google search field |
|
|
95
|
+
| button | NAME | btnK | click | Search button |
|
|
96
|
+
|
|
97
|
+
`PageEngine(screen="Google")` reads locator rows directly from the worksheet tab
|
|
98
|
+
named `Google`. No generated JSON mapping or synchronization step is required.
|
|
99
|
+
|
|
100
|
+
## Browser automation
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from src.core.engine.page_engine import PageEngine
|
|
104
|
+
|
|
105
|
+
engine = PageEngine(screen="Google", headless=True)
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
engine.get_web("https://www.google.com")
|
|
109
|
+
engine.get_element("search").fill("cats")
|
|
110
|
+
engine.get_element("button").click()
|
|
111
|
+
screenshot = engine.get_screenshot("google-results")
|
|
112
|
+
print(screenshot)
|
|
113
|
+
finally:
|
|
114
|
+
engine.teardown()
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`screen="Google"` selects the worksheet tab named `Google`. With multiple tabs, pass the page's tab name when constructing `PageEngine` (for example, `Google`, `Heroku`, or `ST`). Missing worksheets, missing element names, and unsupported locator strategies raise explicit exceptions.
|
|
118
|
+
|
|
119
|
+
## Tests and linting
|
|
120
|
+
|
|
121
|
+
Run the test suite:
|
|
122
|
+
|
|
123
|
+
```sh
|
|
124
|
+
uv run --frozen pytest src/tests tests -v -n auto --dist loadscope
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`src/tests/test_google_search.py` exercises the live browser flow. The unit regressions in `tests/` cover screenshot persistence and direct worksheet selection without contacting external services.
|
|
128
|
+
|
|
129
|
+
Run the blocking CI lint checks:
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
uvx --from flake8==7.4.1 flake8 main.py settings.py src \
|
|
133
|
+
--count --select=E9,F63,F7,F82 --show-source --statistics
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The GitHub Actions workflow in `.github/workflows/python-package.yml` performs locked dependency installation, installs Chromium, runs lint checks, and executes the tests under Xvfb for Python 3.13 and 3.14.
|
|
137
|
+
|
|
138
|
+
## TestFlow Claude Code skills
|
|
139
|
+
|
|
140
|
+
TestFlow separates pytest creation into focused stages:
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
PRD → test specification → UI locators → pytest writer → executor → evaluator
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
| Order | Skill | Purpose |
|
|
147
|
+
|---:|---|---|
|
|
148
|
+
| Optional | `web` | Explore a live application in a real browser and produce reproducible bug evidence |
|
|
149
|
+
| 1 | `prd-reader` | Extract explicit, derived, and unknown test requirements from the PRD |
|
|
150
|
+
| 2 | `test-spec` | Design traceable pytest cases, boundaries, fixtures, and execution prerequisites |
|
|
151
|
+
| 3 | `locator-writer` | For UI cases, validate stable Playwright selectors and upsert elements into Google Sheets |
|
|
152
|
+
| 4 | `writer` | Implement deterministic, behavior-focused pytest tests |
|
|
153
|
+
| 5 | `executor` | Run collection, targeted tests, related tests, and classify failures |
|
|
154
|
+
| 6 | `evaluator` | Evaluate traceability, regression sensitivity, coverage quality, and execution evidence |
|
|
155
|
+
|
|
156
|
+
`artifact` orchestrates the complete pipeline and repeats only the affected downstream stages when evaluation finds a defect.
|
|
157
|
+
|
|
158
|
+
`web` is a report-only exploratory entry point. Verified findings become regression candidates for `test-spec`; it does not modify application code.
|
|
159
|
+
|
|
160
|
+
Validate the Claude Code plugin metadata:
|
|
161
|
+
|
|
162
|
+
```sh
|
|
163
|
+
claude plugin validate .
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Run Claude Code from the repository and invoke either the complete pipeline or an individual stage:
|
|
167
|
+
|
|
168
|
+
```text
|
|
169
|
+
/cellenium:web
|
|
170
|
+
/cellenium:artifact
|
|
171
|
+
/cellenium:prd-reader
|
|
172
|
+
/cellenium:test-spec
|
|
173
|
+
/cellenium:locator-writer
|
|
174
|
+
/cellenium:writer
|
|
175
|
+
/cellenium:executor
|
|
176
|
+
/cellenium:evaluator
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The skills are manual-only. The locator writer previews changes before replacing existing Google Sheet selectors and verifies selectors against the live UI.
|
|
180
|
+
|
|
181
|
+
The separate checklist skills live under `.claude/skills/checklist/`: `before-startup` (system ready to build tests), `cruising` (everything in place, critical errors and potential bugs), `landing` (final production readiness), and the standalone `fordec` emergency decision skill.
|
|
182
|
+
|
|
183
|
+
## Project layout
|
|
184
|
+
|
|
185
|
+
```text
|
|
186
|
+
.github/workflows/ GitHub Actions CI
|
|
187
|
+
.claude/skills/cellenium/ Pytest TestFlow skill definitions
|
|
188
|
+
.claude/skills/checklist/ Checklist skills (before-startup, cruising, landing, fordec)
|
|
189
|
+
.claude/commands/cellenium/ TestFlow slash-command entries
|
|
190
|
+
.claude-plugin/plugin.json Claude Code plugin metadata
|
|
191
|
+
src/core/engine/ Playwright browser and page engines
|
|
192
|
+
src/core/functions/ Locator resolution and direct Google Sheets access
|
|
193
|
+
src/core/ai/ CrewAI configuration and vision components
|
|
194
|
+
src/tests/ Browser test scenarios
|
|
195
|
+
settings.py Environment-backed runtime configuration
|
|
196
|
+
pyproject.toml Python requirements and dependencies
|
|
197
|
+
uv.lock Reproducible dependency lockfile
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Known limitations
|
|
201
|
+
|
|
202
|
+
- The checked-in vision test uses a placeholder `VisionAssertion` without a `run()` implementation.
|
|
203
|
+
- The sample end-to-end test depends on Google and is not hermetic.
|
|
204
|
+
- Configuration currently requires fields that are reserved but not consumed by every component.
|
|
205
|
+
- The project does not yet define a wheel/sdist build or publishing workflow.
|
|
206
|
+
|
|
207
|
+
## License
|
|
208
|
+
|
|
209
|
+
Licensed under the [ISC License](LICENSE). Copyright © 2026 Jenia Petrusenko.
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "cellenium"
|
|
3
|
-
version = "0.3.
|
|
4
|
-
description = "Playwright browser automation with a Google Sheets Page Object Model and CrewAI vision analysis"
|
|
5
|
-
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.13"
|
|
7
|
-
dependencies = [
|
|
8
|
-
"crewai>=1.15.23",
|
|
9
|
-
"fastapi>=0.142.2",
|
|
10
|
-
"gspread>=6.2.1",
|
|
11
|
-
"importlib-metadata>=8.0.0",
|
|
12
|
-
"logfire>=5.1.1",
|
|
13
|
-
"oauth2client>=4.1.3",
|
|
14
|
-
"pillow>=12.3.0",
|
|
15
|
-
"playwright>=1.63.0",
|
|
16
|
-
"pydantic-settings>=2.15.0",
|
|
17
|
-
"uvicorn>=0.54.0",
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
[dependency-groups]
|
|
21
|
-
dev = [
|
|
22
|
-
"pytest>=9.1.1",
|
|
23
|
-
"pytest-xdist>=3.8.0",
|
|
24
|
-
]
|
|
25
|
-
|
|
26
|
-
[project.scripts]
|
|
27
|
-
cellenium = "cellenium.cli:main"
|
|
28
|
-
|
|
29
|
-
[project.urls]
|
|
30
|
-
Homepage = "https://github.com/MedviJenka/cellenium-playwright"
|
|
31
|
-
|
|
32
|
-
[build-system]
|
|
33
|
-
requires = ["hatchling"]
|
|
34
|
-
build-backend = "hatchling.build"
|
|
35
|
-
|
|
36
|
-
[tool.hatch.build.targets.wheel]
|
|
37
|
-
packages = ["src/cellenium"]
|
|
38
|
-
|
|
39
|
-
# Ship the Claude Code commands and skills inside the package; `cellenium claude` installs them.
|
|
40
|
-
[tool.hatch.build.targets.wheel.force-include]
|
|
41
|
-
".claude/commands/cellenium" = "cellenium/claude/commands/cellenium"
|
|
42
|
-
".claude/skills/cellenium" = "cellenium/claude/skills/cellenium"
|
|
43
|
-
|
|
44
|
-
[tool.pytest.ini_options]
|
|
45
|
-
pythonpath = ["src"]
|
|
1
|
+
[project]
|
|
2
|
+
name = "cellenium"
|
|
3
|
+
version = "0.3.4"
|
|
4
|
+
description = "Playwright browser automation with a Google Sheets Page Object Model and CrewAI vision analysis"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"crewai>=1.15.23",
|
|
9
|
+
"fastapi>=0.142.2",
|
|
10
|
+
"gspread>=6.2.1",
|
|
11
|
+
"importlib-metadata>=8.0.0",
|
|
12
|
+
"logfire>=5.1.1",
|
|
13
|
+
"oauth2client>=4.1.3",
|
|
14
|
+
"pillow>=12.3.0",
|
|
15
|
+
"playwright>=1.63.0",
|
|
16
|
+
"pydantic-settings>=2.15.0",
|
|
17
|
+
"uvicorn>=0.54.0",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[dependency-groups]
|
|
21
|
+
dev = [
|
|
22
|
+
"pytest>=9.1.1",
|
|
23
|
+
"pytest-xdist>=3.8.0",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.scripts]
|
|
27
|
+
cellenium = "cellenium.cli:main"
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/MedviJenka/cellenium-playwright"
|
|
31
|
+
|
|
32
|
+
[build-system]
|
|
33
|
+
requires = ["hatchling"]
|
|
34
|
+
build-backend = "hatchling.build"
|
|
35
|
+
|
|
36
|
+
[tool.hatch.build.targets.wheel]
|
|
37
|
+
packages = ["src/cellenium"]
|
|
38
|
+
|
|
39
|
+
# Ship the Claude Code commands and skills inside the package; `cellenium claude` installs them.
|
|
40
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
41
|
+
".claude/commands/cellenium" = "cellenium/claude/commands/cellenium"
|
|
42
|
+
".claude/skills/cellenium" = "cellenium/claude/skills/cellenium"
|
|
43
|
+
|
|
44
|
+
[tool.pytest.ini_options]
|
|
45
|
+
pythonpath = ["src"]
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from asyncio import get_running_loop
|
|
2
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
1
3
|
from pathlib import Path
|
|
2
4
|
from cellenium.ai.config import AgentConfig
|
|
3
5
|
from crewai import Agent, Crew, Task
|
|
@@ -25,8 +27,19 @@ class Vision(AgentConfig):
|
|
|
25
27
|
return Crew(agents=self.agents, tasks=self.tasks, verbose=True)
|
|
26
28
|
|
|
27
29
|
|
|
28
|
-
def run_vision_agent(image: list[str], prompt: str) ->
|
|
29
|
-
|
|
30
|
+
def run_vision_agent(image: list[str], prompt: str) -> dict:
|
|
31
|
+
inputs = {'image': image, 'prompt': prompt}
|
|
32
|
+
|
|
33
|
+
def kickoff() -> dict:
|
|
34
|
+
return Vision().crew().kickoff(inputs).pydantic.model_dump()
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
get_running_loop()
|
|
38
|
+
except RuntimeError:
|
|
39
|
+
return kickoff()
|
|
40
|
+
|
|
41
|
+
with ThreadPoolExecutor(max_workers=1) as executor:
|
|
42
|
+
return executor.submit(kickoff).result()
|
|
30
43
|
|
|
31
44
|
|
|
32
45
|
if __name__ == '__main__':
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from asyncio import to_thread
|
|
1
2
|
from fastapi import FastAPI, APIRouter, UploadFile, File
|
|
2
3
|
from fastapi.responses import JSONResponse
|
|
3
4
|
from contextlib import asynccontextmanager
|
|
@@ -6,13 +7,14 @@ from tempfile import NamedTemporaryFile
|
|
|
6
7
|
from typing import AsyncGenerator
|
|
7
8
|
from cellenium.ai.agents.vision.crew import run_vision_agent
|
|
8
9
|
from cellenium.functions.logger import Logger
|
|
9
|
-
from
|
|
10
|
+
from cellenium.settings import get_config
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
log = Logger(name='vision-service')
|
|
13
14
|
|
|
14
15
|
Config = get_config()
|
|
15
16
|
|
|
17
|
+
|
|
16
18
|
@asynccontextmanager
|
|
17
19
|
async def lifespan(_: FastAPI) -> AsyncGenerator:
|
|
18
20
|
log.fire(f'{log.name} service started')
|
|
@@ -31,7 +33,7 @@ async def vision(prompt: str, image: UploadFile = File(...)) -> JSONResponse:
|
|
|
31
33
|
temp_file.write(await image.read())
|
|
32
34
|
|
|
33
35
|
try:
|
|
34
|
-
response = run_vision_agent
|
|
36
|
+
response = await to_thread(run_vision_agent, prompt=prompt, image=[str(image_path)])
|
|
35
37
|
finally:
|
|
36
38
|
image_path.unlink(missing_ok=True)
|
|
37
39
|
|
|
@@ -64,4 +66,4 @@ def docs() -> JSONResponse:
|
|
|
64
66
|
|
|
65
67
|
if __name__ == '__main__':
|
|
66
68
|
import uvicorn
|
|
67
|
-
uvicorn.run(app=app, host='0.0.0.0', port=
|
|
69
|
+
uvicorn.run(app=app, host='0.0.0.0', port=8888, use_colors=True)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import pytest
|
|
2
|
+
from cellenium.ai.agents.vision.crew import run_vision_agent
|
|
2
3
|
from cellenium.engine.page_engine import PageEngine
|
|
3
4
|
from cellenium.functions.logger import Logger
|
|
4
5
|
|
|
@@ -13,5 +14,6 @@ class TestGoogleSearch:
|
|
|
13
14
|
engine.get_web("https://www.google.com")
|
|
14
15
|
engine.get_element("search").fill("cats")
|
|
15
16
|
engine.get_element("button").click()
|
|
16
|
-
engine.get_screenshot()
|
|
17
|
-
|
|
17
|
+
sc = engine.get_screenshot()
|
|
18
|
+
response = run_vision_agent(prompt='what is displayed?', image=[sc])
|
|
19
|
+
print(response)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from types import SimpleNamespace
|
|
3
|
+
|
|
4
|
+
from cellenium.ai.agents.vision import crew as vision_crew
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_run_vision_agent_moves_sync_kickoff_outside_active_event_loop(monkeypatch):
|
|
8
|
+
inputs = {"image": ["screenshot.png"], "prompt": "what is displayed?"}
|
|
9
|
+
expected = {"description": "search results"}
|
|
10
|
+
calls = []
|
|
11
|
+
|
|
12
|
+
class FakeCrew:
|
|
13
|
+
def kickoff(self, received_inputs: dict):
|
|
14
|
+
try:
|
|
15
|
+
asyncio.get_running_loop()
|
|
16
|
+
except RuntimeError:
|
|
17
|
+
calls.append(received_inputs)
|
|
18
|
+
return SimpleNamespace(
|
|
19
|
+
pydantic=SimpleNamespace(model_dump=lambda: expected)
|
|
20
|
+
)
|
|
21
|
+
raise RuntimeError(
|
|
22
|
+
"Agent execution was invoked synchronously from within a running event loop."
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
class FakeVision:
|
|
26
|
+
def crew(self):
|
|
27
|
+
return FakeCrew()
|
|
28
|
+
|
|
29
|
+
monkeypatch.setattr(vision_crew, "Vision", FakeVision)
|
|
30
|
+
|
|
31
|
+
async def invoke_from_running_loop():
|
|
32
|
+
return vision_crew.run_vision_agent(**inputs)
|
|
33
|
+
|
|
34
|
+
response = asyncio.run(invoke_from_running_loop())
|
|
35
|
+
|
|
36
|
+
assert response == expected
|
|
37
|
+
assert calls == [inputs]
|