behave-steplib 1.0.1.dev0__py3-none-any.whl
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.
- behave_steplib-1.0.1.dev0.dist-info/METADATA +303 -0
- behave_steplib-1.0.1.dev0.dist-info/RECORD +44 -0
- behave_steplib-1.0.1.dev0.dist-info/WHEEL +4 -0
- behave_steplib-1.0.1.dev0.dist-info/entry_points.txt +8 -0
- behave_steplib-1.0.1.dev0.dist-info/licenses/LICENSE +21 -0
- steplib/__init__.py +18 -0
- steplib/_version.py +24 -0
- steplib/behave.py +58 -0
- steplib/cli/__init__.py +1 -0
- steplib/cli/formatters.py +166 -0
- steplib/cli/main.py +136 -0
- steplib/core/__init__.py +29 -0
- steplib/core/decorators.py +92 -0
- steplib/core/discovery.py +111 -0
- steplib/core/ecosystem.py +98 -0
- steplib/core/exceptions.py +42 -0
- steplib/core/i18n.py +71 -0
- steplib/core/metadata.py +47 -0
- steplib/core/params.py +121 -0
- steplib/core/registry.py +162 -0
- steplib/core/state.py +68 -0
- steplib/core/validation.py +97 -0
- steplib/modules/__init__.py +1 -0
- steplib/modules/api/__init__.py +7 -0
- steplib/modules/api/actions.py +152 -0
- steplib/modules/api/client.py +183 -0
- steplib/modules/api/context.py +43 -0
- steplib/modules/api/steps.py +261 -0
- steplib/modules/api/transforms.py +161 -0
- steplib/modules/db/__init__.py +7 -0
- steplib/modules/db/actions.py +68 -0
- steplib/modules/db/client.py +51 -0
- steplib/modules/db/context.py +33 -0
- steplib/modules/db/steps.py +120 -0
- steplib/modules/kafka/__init__.py +7 -0
- steplib/modules/kafka/actions.py +113 -0
- steplib/modules/kafka/context.py +33 -0
- steplib/modules/kafka/steps.py +132 -0
- steplib/modules/web/__init__.py +7 -0
- steplib/modules/web/actions.py +66 -0
- steplib/modules/web/client.py +102 -0
- steplib/modules/web/context.py +30 -0
- steplib/modules/web/steps.py +138 -0
- steplib/py.typed +0 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: behave-steplib
|
|
3
|
+
Version: 1.0.1.dev0
|
|
4
|
+
Summary: Librería de steps reusable para behave con soporte multitecnología e i18n.
|
|
5
|
+
Project-URL: Homepage, https://github.com/MathiasPaulenko/behave-steplib
|
|
6
|
+
Project-URL: Documentation, https://mathiaspaulenko.github.io/behave-steplib
|
|
7
|
+
Project-URL: Repository, https://github.com/MathiasPaulenko/behave-steplib
|
|
8
|
+
Project-URL: Issues, https://github.com/MathiasPaulenko/behave-steplib/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/MathiasPaulenko/behave-steplib/blob/main/CHANGELOG.md
|
|
10
|
+
Author: Mathias Paulenko
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: api,bdd,behave,db,i18n,kafka,steps,web
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Testing
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Requires-Dist: behave>=1.3.0
|
|
26
|
+
Requires-Dist: parse>=1.19
|
|
27
|
+
Requires-Dist: typer>=0.12
|
|
28
|
+
Provides-Extra: all
|
|
29
|
+
Requires-Dist: behave-data>=1.0.2; extra == 'all'
|
|
30
|
+
Requires-Dist: behave-kit>=1.3.1; extra == 'all'
|
|
31
|
+
Requires-Dist: behave-tables>=1.3.1; extra == 'all'
|
|
32
|
+
Requires-Dist: httpx>=0.27; extra == 'all'
|
|
33
|
+
Requires-Dist: kafka-python-ng>=2.0; extra == 'all'
|
|
34
|
+
Requires-Dist: selenium>=4.0; extra == 'all'
|
|
35
|
+
Requires-Dist: sqlalchemy>=2.0; extra == 'all'
|
|
36
|
+
Provides-Extra: api
|
|
37
|
+
Requires-Dist: httpx>=0.27; extra == 'api'
|
|
38
|
+
Provides-Extra: data
|
|
39
|
+
Requires-Dist: behave-data>=1.0.2; extra == 'data'
|
|
40
|
+
Provides-Extra: db
|
|
41
|
+
Requires-Dist: sqlalchemy>=2.0; extra == 'db'
|
|
42
|
+
Provides-Extra: dev
|
|
43
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
44
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
45
|
+
Requires-Dist: pre-commit>=3.7; extra == 'dev'
|
|
46
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
47
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
48
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
49
|
+
Requires-Dist: responses>=0.25; extra == 'dev'
|
|
50
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
51
|
+
Requires-Dist: twine>=5.0; extra == 'dev'
|
|
52
|
+
Provides-Extra: docs
|
|
53
|
+
Requires-Dist: furo>=2024.0; extra == 'docs'
|
|
54
|
+
Requires-Dist: myst-parser>=3.0; extra == 'docs'
|
|
55
|
+
Requires-Dist: sphinx-autodoc-typehints>=2.0; extra == 'docs'
|
|
56
|
+
Requires-Dist: sphinx>=7.0; extra == 'docs'
|
|
57
|
+
Provides-Extra: kafka
|
|
58
|
+
Requires-Dist: kafka-python-ng>=2.0; extra == 'kafka'
|
|
59
|
+
Provides-Extra: kit
|
|
60
|
+
Requires-Dist: behave-kit>=1.3.1; extra == 'kit'
|
|
61
|
+
Provides-Extra: tables
|
|
62
|
+
Requires-Dist: behave-tables>=1.3.1; extra == 'tables'
|
|
63
|
+
Provides-Extra: web
|
|
64
|
+
Requires-Dist: selenium>=4.0; extra == 'web'
|
|
65
|
+
Description-Content-Type: text/markdown
|
|
66
|
+
|
|
67
|
+
# behave-steplib
|
|
68
|
+
|
|
69
|
+
Reusable step libraries for [Behave](https://github.com/behave/behave) BDD — share, discover and install step definitions across projects. Zero mandatory dependencies; each technology is an optional extra.
|
|
70
|
+
|
|
71
|
+
[](https://github.com/MathiasPaulenko/behave-steplib/actions/workflows/ci.yml)
|
|
72
|
+
[](https://github.com/MathiasPaulenko/behave-steplib/actions/workflows/release.yml)
|
|
73
|
+
[](https://pypi.org/project/behave-steplib/)
|
|
74
|
+
[](https://www.python.org/downloads/)
|
|
75
|
+
[](https://opensource.org/licenses/MIT)
|
|
76
|
+
|
|
77
|
+
## Why behave-steplib?
|
|
78
|
+
|
|
79
|
+
Writing BDD step definitions for HTTP APIs, web browsers, databases and Kafka is repetitive. Every project re-implements the same "send a request", "check the status code", "query the database" steps. behave-steplib provides a curated, typed, multilingual library of reusable steps that you install once and share across projects.
|
|
80
|
+
|
|
81
|
+
- **Modular** — `api`, `web`, `db`, `kafka` modules activated via extras and lazy imports. Install only what you need.
|
|
82
|
+
- **Auto-registered** — `autoload(context)` discovers every installed step via Python entry points and registers it with behave in one line.
|
|
83
|
+
- **Multilingual** — steps defined in English with `es` and `pt` translations; all patterns are registered with behave so matching works regardless of the language used in feature files.
|
|
84
|
+
- **Typed** — full type hints, `mypy --strict` clean, `py.typed` marker included.
|
|
85
|
+
- **CLI** — `steplib list / show / validate / init` powered by Typer for inspecting and validating your step library from the terminal.
|
|
86
|
+
- **Pluggable** — third-party packages can register steps via the `steplib.plugins` entry point group; `autoload` discovers them automatically.
|
|
87
|
+
- **Ecosystem** — integrates with `behave-kit` (soft assertions), `behave-tables` (table conversion) and `behave-data` (test data loading) when installed.
|
|
88
|
+
- **Backends** — each module supports multiple backends (e.g. stdlib/httpx/requests for API, selenium for web) selectable at autoload time.
|
|
89
|
+
|
|
90
|
+
## Installation
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pip install behave-steplib # core only (behave, parse, typer)
|
|
94
|
+
pip install behave-steplib[api] # + httpx HTTP client
|
|
95
|
+
pip install "behave-steplib[api,web,db,kafka]" # + all technology extras
|
|
96
|
+
pip install "behave-steplib[all]" # + every technology extra
|
|
97
|
+
pip install behave-steplib[dev] # + pytest, ruff, mypy, build, twine
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
| Extra | Packages | Description |
|
|
101
|
+
|-------|----------|-------------|
|
|
102
|
+
| `[api]` | `httpx` | HTTP API testing with httpx |
|
|
103
|
+
| `[web]` | `selenium` | Browser testing with Selenium |
|
|
104
|
+
| `[db]` | `sqlalchemy` | Database testing with SQLAlchemy |
|
|
105
|
+
| `[kafka]` | `kafka-python-ng` | Kafka producer/consumer testing |
|
|
106
|
+
| `[kit]` | `behave-kit` | Soft assertions, typed context, fixtures |
|
|
107
|
+
| `[data]` | `behave-data` | Test data loading (CSV, JSON, YAML, Excel) |
|
|
108
|
+
| `[tables]` | `behave-tables` | Table conversion helpers |
|
|
109
|
+
| `[dev]` | pytest, ruff, mypy, build, twine | Development tools |
|
|
110
|
+
| `[docs]` | sphinx, furo, myst-parser | Documentation tools |
|
|
111
|
+
| `[all]` | api, web, db, kafka, kit, data, tables | Everything except dev/docs |
|
|
112
|
+
|
|
113
|
+
## Quickstart
|
|
114
|
+
|
|
115
|
+
### Level 1 — Automatic wiring
|
|
116
|
+
|
|
117
|
+
Add three hooks to your `environment.py` and every installed step is wired automatically:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
# features/environment.py
|
|
121
|
+
from steplib.behave import autoload
|
|
122
|
+
|
|
123
|
+
def before_all(context):
|
|
124
|
+
context.steplib = autoload(context)
|
|
125
|
+
|
|
126
|
+
def before_scenario(context, scenario):
|
|
127
|
+
context.steplib.reset()
|
|
128
|
+
|
|
129
|
+
def after_scenario(context, scenario):
|
|
130
|
+
context.steplib.cleanup()
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Or generate it with the CLI:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
steplib init
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Level 2 — Explicit load
|
|
140
|
+
|
|
141
|
+
Load only the modules you need by dotted path:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from steplib.behave import load
|
|
145
|
+
|
|
146
|
+
def before_all(context):
|
|
147
|
+
context.steplib = load(context, "steplib.modules.api.steps")
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Level 3 — Filtered autoload
|
|
151
|
+
|
|
152
|
+
When multiple extras are installed, narrow which steps are active:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from steplib.behave import autoload
|
|
156
|
+
|
|
157
|
+
def before_all(context):
|
|
158
|
+
context.steplib = autoload(
|
|
159
|
+
context,
|
|
160
|
+
categories=["api"],
|
|
161
|
+
backends={"api": "httpx"},
|
|
162
|
+
)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Example feature
|
|
166
|
+
|
|
167
|
+
```gherkin
|
|
168
|
+
Feature: API health check
|
|
169
|
+
|
|
170
|
+
Scenario: GET users returns 200
|
|
171
|
+
Given the API base url is "https://api.example.com"
|
|
172
|
+
When I send a GET request to "/users"
|
|
173
|
+
Then the response status is 200
|
|
174
|
+
And the response body is valid JSON
|
|
175
|
+
And the JSON path "$.users[0].name" equals "Ada"
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Multilingual features
|
|
179
|
+
|
|
180
|
+
Steps are defined in English and translated to Spanish and Portuguese. All patterns are registered with behave — no language switch needed:
|
|
181
|
+
|
|
182
|
+
```gherkin
|
|
183
|
+
# es
|
|
184
|
+
Cuando envío una petición GET a "/users"
|
|
185
|
+
Entonces el estado de la respuesta es 200
|
|
186
|
+
|
|
187
|
+
# pt
|
|
188
|
+
Quando envio uma requisição GET para "/users"
|
|
189
|
+
Então o status da resposta é 200
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Modules
|
|
193
|
+
|
|
194
|
+
### API
|
|
195
|
+
|
|
196
|
+
HTTP API testing with stdlib (urllib), httpx or requests backends.
|
|
197
|
+
|
|
198
|
+
```gherkin
|
|
199
|
+
Given the API base url is "https://api.example.com"
|
|
200
|
+
When I send a GET request to "/users"
|
|
201
|
+
Then the response status is 200
|
|
202
|
+
And the JSON path "$.users[0].name" equals "Ada"
|
|
203
|
+
And the response header "Content-Type" is "application/json"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Web
|
|
207
|
+
|
|
208
|
+
Browser testing with Selenium (Chrome, Firefox, headless).
|
|
209
|
+
|
|
210
|
+
```gherkin
|
|
211
|
+
Given the web base url is "https://example.com"
|
|
212
|
+
When I navigate to "/login"
|
|
213
|
+
Then the page title is "Login"
|
|
214
|
+
And the element id "username" is present
|
|
215
|
+
And the page contains "Sign In"
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### DB
|
|
219
|
+
|
|
220
|
+
Database testing with SQLAlchemy (SQLite, PostgreSQL, MySQL, ...).
|
|
221
|
+
|
|
222
|
+
```gherkin
|
|
223
|
+
Given the database connection string is "sqlite:///test.db"
|
|
224
|
+
When I execute the SQL query "SELECT * FROM users"
|
|
225
|
+
Then the query returns 3 rows
|
|
226
|
+
And the column "name" in the first row equals "Ada"
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Kafka
|
|
230
|
+
|
|
231
|
+
Kafka producer and consumer testing with kafka-python-ng.
|
|
232
|
+
|
|
233
|
+
```gherkin
|
|
234
|
+
Given the Kafka bootstrap servers are "localhost:9092"
|
|
235
|
+
When I produce a message to topic "events" with key "id" and value "hello"
|
|
236
|
+
And I consume messages from topic "events"
|
|
237
|
+
Then the consumed messages count is 1
|
|
238
|
+
And a consumed message contains "hello"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## CLI
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
steplib list # list all registered steps
|
|
245
|
+
steplib list --category api # filter by category
|
|
246
|
+
steplib list --backend httpx # filter by backend
|
|
247
|
+
steplib list --json # output as JSON
|
|
248
|
+
steplib show "I send a {method} request to {url}"
|
|
249
|
+
steplib validate # validate step contracts
|
|
250
|
+
steplib init # generate features/environment.py
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Writing custom steps
|
|
254
|
+
|
|
255
|
+
Use the `@step` decorator to define your own steps with full metadata:
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
from steplib import Param, step
|
|
259
|
+
|
|
260
|
+
@step(
|
|
261
|
+
"the invoice total is {total:f}",
|
|
262
|
+
category="invoice",
|
|
263
|
+
description="Assert the invoice total matches.",
|
|
264
|
+
parameters=[Param("total", type=float, required=True)],
|
|
265
|
+
example='Then the invoice total is 19.99',
|
|
266
|
+
i18n={
|
|
267
|
+
"es": "el total de la factura es {total:f}",
|
|
268
|
+
"pt": "o total da fatura é {total:f}",
|
|
269
|
+
},
|
|
270
|
+
tags=["invoice"],
|
|
271
|
+
version="1.0.0",
|
|
272
|
+
)
|
|
273
|
+
def step_invoice_total(context, total):
|
|
274
|
+
assert context.invoice.total == total
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Register steps in a `register(registry)` function and declare an entry point:
|
|
278
|
+
|
|
279
|
+
```toml
|
|
280
|
+
# pyproject.toml
|
|
281
|
+
[project.entry-points."steplib.plugins"]
|
|
282
|
+
mycompany = "mycompany.steps:register"
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Once installed, `autoload(context)` discovers your package automatically.
|
|
286
|
+
|
|
287
|
+
## Development
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
make dev # install with api, dev and docs extras
|
|
291
|
+
make lint # ruff + mypy --strict
|
|
292
|
+
make test-cov # pytest with >=80% coverage gate
|
|
293
|
+
make docs-build # build Sphinx documentation
|
|
294
|
+
make build # build sdist + wheel
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Documentation
|
|
298
|
+
|
|
299
|
+
Full documentation is available at <https://mathiaspaulenko.github.io/behave-steplib>.
|
|
300
|
+
|
|
301
|
+
## License
|
|
302
|
+
|
|
303
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
steplib/__init__.py,sha256=uHZuzdMz369B9Us648sLdwqo7qCR4S7fz9YXsafTfZ8,398
|
|
2
|
+
steplib/_version.py,sha256=Y_mQou6Ma9bKGw0wGkSUgyT333rlwHalgPFv_DCW8mY,533
|
|
3
|
+
steplib/behave.py,sha256=hyheeo7XjHbjWEXdud3UCAaQcV5HBK4kEC-9lNVELt0,1682
|
|
4
|
+
steplib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
steplib/cli/__init__.py,sha256=x-E6fqvwCyB59vrcFkgK1jbdwmE11-a7Hc2zvGwK_eM,47
|
|
6
|
+
steplib/cli/formatters.py,sha256=PMFP0NmTf1NsryEhcE7Lg5iU2C81R7rxt7l-jHltzCQ,4903
|
|
7
|
+
steplib/cli/main.py,sha256=2D1cTApiJ8Vo2YHU-hdIu3Mjp6AQ_86maCB13NR9rZ4,3920
|
|
8
|
+
steplib/core/__init__.py,sha256=Ipdf6bddOFExw6FzX0ndHU5Q2vpRAMGLNtDDwQLUF5k,716
|
|
9
|
+
steplib/core/decorators.py,sha256=3mWIWgO9HSsIY4_eHi4AFPVRUnWoBw0SSWdWRmYx5wc,3152
|
|
10
|
+
steplib/core/discovery.py,sha256=4mAlk-_RptfOJwX4BkWlICd1x8gelb8kRKE26SSYsDc,3675
|
|
11
|
+
steplib/core/ecosystem.py,sha256=P3Fo3sqFdrlmoxz-fuXuspH-45CHoBGt4_izb5pza0Q,2838
|
|
12
|
+
steplib/core/exceptions.py,sha256=3RA4cPnaa4ogYFaDCUamBv7metqwP-C7g592JUpC6Cc,1469
|
|
13
|
+
steplib/core/i18n.py,sha256=Qy8t8IYf0jrLel4TU5-uSjr6pNd4F6tQBxJoUC_dDTY,2356
|
|
14
|
+
steplib/core/metadata.py,sha256=LAzpV1sDlglymScIDktz3ScaMNB5sAelBjJfO-edA9E,1443
|
|
15
|
+
steplib/core/params.py,sha256=JGp9Ucrmc2smHCBsS0UHn_o5HYqX1Smn72Lkz_ok5tM,3554
|
|
16
|
+
steplib/core/registry.py,sha256=DycbyUj8_4UzJmWQTtvodIMUlawNbv4Jcbbk4YwiBrQ,5789
|
|
17
|
+
steplib/core/state.py,sha256=TVpq00E6uNyMbU69mV_F-eXzPnhNVY3DbtxNsbvCb-g,2014
|
|
18
|
+
steplib/core/validation.py,sha256=UwRCdMp50J3y2YYu0Gfr5SBGvGnxlmZySMpYxeMvRRE,3338
|
|
19
|
+
steplib/modules/__init__.py,sha256=Sfv7_PZXOAJnJFrODZ5XmbaAY3gx_7J6y5ytZNqxWjg,59
|
|
20
|
+
steplib/modules/api/__init__.py,sha256=qR4iUPbYCUyA9hs5R8yt2HvCvlOlHBqIF3EyAxwCEoM,146
|
|
21
|
+
steplib/modules/api/actions.py,sha256=4cdnG1_weBBQyAZV5o9UQp5eczQvRbTDtnJdjf1b5tI,5130
|
|
22
|
+
steplib/modules/api/client.py,sha256=81C3oTW3svQMHJnkL18vHA-b5Kh1JJxzHwBrp5kcauI,5226
|
|
23
|
+
steplib/modules/api/context.py,sha256=oebor5xzT4K03OLqEQCW_kj-IE89fQ9M2pp5ep_u0v0,1384
|
|
24
|
+
steplib/modules/api/steps.py,sha256=ldfHLBmG2VmB17TfQrOQMRG6fdD5m9nV1G8IeQPBykU,8353
|
|
25
|
+
steplib/modules/api/transforms.py,sha256=7DDp2S5iJ43UJa2S_57QM0TPhUQXLLVlvQcu6CPVR7g,4840
|
|
26
|
+
steplib/modules/db/__init__.py,sha256=OaUBBRuzdETaB0qLY_tP1p_tWoUXidfqtSYJjci3nho,164
|
|
27
|
+
steplib/modules/db/actions.py,sha256=W3Ax4O6LFTEf1AjX5LIp22_C8M3OQq7KJgYUrNyl-sA,2093
|
|
28
|
+
steplib/modules/db/client.py,sha256=hYhfuWX4Aci9PXmgLWNhLCEZ_dFrITjBV_lXvyrWbAc,1841
|
|
29
|
+
steplib/modules/db/context.py,sha256=0t92VVPCfcczdZlWv07YGimXlxwRWMqeRtpnq1w72Ug,1007
|
|
30
|
+
steplib/modules/db/steps.py,sha256=Lg9w5A4Nd9aRlKH22XmI_CUe0KiK3_msjFdCATKWdnc,3843
|
|
31
|
+
steplib/modules/kafka/__init__.py,sha256=PLsmP7fIXSGPKfEknjJZwjD-PrXcwxRYY2Lqj5EhLSQ,169
|
|
32
|
+
steplib/modules/kafka/actions.py,sha256=LrFgpZrrCVnQ4l8Fjali4j4SpntVWwVNDTcT3H-A2FI,3427
|
|
33
|
+
steplib/modules/kafka/context.py,sha256=R7PgMFFRF_J-Jq_BqXRmT_2xXkn3g9hof6P_Tvr_t0I,1031
|
|
34
|
+
steplib/modules/kafka/steps.py,sha256=hbvUkFiS_QliIVJ65BBMFvBrehJRr9ijLT-7nssYcvA,4223
|
|
35
|
+
steplib/modules/web/__init__.py,sha256=mGDM_4D2Xa04gBwiLkCPrtBzdCcgFf9y9w6qgMuDS4Y,163
|
|
36
|
+
steplib/modules/web/actions.py,sha256=a7bv96_2AOOau_hvCKUek2ByO72yw9BYldCdhmCVedI,2505
|
|
37
|
+
steplib/modules/web/client.py,sha256=Pu8ADRhvob5izdvkitIW-MFZCyMHNbAOGUamjf9o0qA,3276
|
|
38
|
+
steplib/modules/web/context.py,sha256=ySz-wz1jHyOzycybfodCfibDh7Je0MEzfyD9UZ-bGDc,843
|
|
39
|
+
steplib/modules/web/steps.py,sha256=h5VYIl31yKMmhPhWD1W0nYFP1r5fdzDC7cup4eyqjAI,3872
|
|
40
|
+
behave_steplib-1.0.1.dev0.dist-info/METADATA,sha256=TK3WfqUtUkJO1mpSJ-roOiXOwbGFE5izdcWlHrpLrA0,10890
|
|
41
|
+
behave_steplib-1.0.1.dev0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
42
|
+
behave_steplib-1.0.1.dev0.dist-info/entry_points.txt,sha256=tYJ2rN6gq8_reyWHDPBg8MklVdJnPfGNRZdZAH9QROk,235
|
|
43
|
+
behave_steplib-1.0.1.dev0.dist-info/licenses/LICENSE,sha256=LLr8AP65kT83seFEcknE3-76LL7oikrP87U33tZKYro,1073
|
|
44
|
+
behave_steplib-1.0.1.dev0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mathias Paulenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
steplib/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""behave-steplib — reusable step libraries for Behave BDD."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
from steplib._version import __version__ as __version__
|
|
7
|
+
except ImportError: # pragma: no cover
|
|
8
|
+
__version__ = "0.0.0+unknown"
|
|
9
|
+
|
|
10
|
+
from steplib.core import Param, StepInfo, StepRegistry, step
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"Param",
|
|
14
|
+
"StepInfo",
|
|
15
|
+
"StepRegistry",
|
|
16
|
+
"__version__",
|
|
17
|
+
"step",
|
|
18
|
+
]
|
steplib/_version.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '1.0.1.dev0'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 1, 'dev0')
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
steplib/behave.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Integration helpers for behave's ``environment.py``.
|
|
2
|
+
|
|
3
|
+
``before_all`` and ``after_scenario`` can be imported directly as behave
|
|
4
|
+
hooks. ``before_scenario`` must be written by the user (it calls
|
|
5
|
+
``context.steplib.reset()``).
|
|
6
|
+
|
|
7
|
+
Usage::
|
|
8
|
+
|
|
9
|
+
# features/environment.py
|
|
10
|
+
from steplib.behave import after_scenario, before_all
|
|
11
|
+
|
|
12
|
+
def before_scenario(context, scenario):
|
|
13
|
+
context.steplib.reset()
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from steplib.core.discovery import autoload as _autoload
|
|
21
|
+
from steplib.core.discovery import load as _load
|
|
22
|
+
from steplib.core.state import SteplibState
|
|
23
|
+
|
|
24
|
+
__all__ = ["SteplibState", "after_scenario", "autoload", "before_all", "load"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def autoload(
|
|
28
|
+
context: Any,
|
|
29
|
+
categories: list[str] | None = None,
|
|
30
|
+
backends: dict[str, str] | None = None,
|
|
31
|
+
) -> SteplibState:
|
|
32
|
+
"""Load all installed steplib plugins and attach state to *context*.
|
|
33
|
+
|
|
34
|
+
See :func:`steplib.core.discovery.autoload` for details.
|
|
35
|
+
"""
|
|
36
|
+
return _autoload(context, categories=categories, backends=backends)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def load(context: Any, *modules: str) -> SteplibState:
|
|
40
|
+
"""Load specific step modules by dotted path.
|
|
41
|
+
|
|
42
|
+
See :func:`steplib.core.discovery.load` for details.
|
|
43
|
+
"""
|
|
44
|
+
return _load(context, *modules)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def before_all(context: Any) -> SteplibState:
|
|
48
|
+
"""Run autoload and attach steplib state to *context*."""
|
|
49
|
+
state = autoload(context)
|
|
50
|
+
context.steplib = state
|
|
51
|
+
return state
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def after_scenario(context: Any, scenario: Any) -> None:
|
|
55
|
+
"""Clean up steplib resources after a scenario."""
|
|
56
|
+
state = getattr(context, "steplib", None)
|
|
57
|
+
if state is not None and hasattr(state, "cleanup"):
|
|
58
|
+
state.cleanup()
|
steplib/cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI package: steplib terminal interface."""
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""Output formatters for the steplib CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from steplib.core.metadata import StepInfo
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def format_step_table(steps: list[StepInfo]) -> str:
|
|
12
|
+
"""Format a list of steps as a simple aligned table.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
steps: The step metadata entries to render.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
A multi-line string with columns: PATTERN, CATEGORY, BACKEND, DESCRIPTION.
|
|
19
|
+
|
|
20
|
+
"""
|
|
21
|
+
if not steps:
|
|
22
|
+
return "No steps found."
|
|
23
|
+
|
|
24
|
+
headers = ("PATTERN", "CATEGORY", "BACKEND", "DESCRIPTION")
|
|
25
|
+
rows: list[tuple[str, str, str, str]] = []
|
|
26
|
+
for info in steps:
|
|
27
|
+
rows.append((
|
|
28
|
+
info.pattern,
|
|
29
|
+
info.category,
|
|
30
|
+
info.backend or "-",
|
|
31
|
+
(info.description or "").split("\n")[0][:60],
|
|
32
|
+
))
|
|
33
|
+
|
|
34
|
+
col_widths = [
|
|
35
|
+
max(len(str(row[i])) for row in [headers, *rows])
|
|
36
|
+
for i in range(len(headers))
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
lines: list[str] = []
|
|
40
|
+
header_line = " ".join(
|
|
41
|
+
headers[i].ljust(col_widths[i]) for i in range(len(headers))
|
|
42
|
+
)
|
|
43
|
+
lines.append(header_line)
|
|
44
|
+
lines.append(" ".join("-" * col_widths[i] for i in range(len(headers))))
|
|
45
|
+
for row in rows:
|
|
46
|
+
lines.append(" ".join(
|
|
47
|
+
str(row[i]).ljust(col_widths[i]) for i in range(len(headers))
|
|
48
|
+
))
|
|
49
|
+
return "\n".join(lines)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def format_step_detail(info: StepInfo) -> str:
|
|
53
|
+
"""Format a single step's metadata for detailed display.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
info: The step metadata to render.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
A multi-line string with all metadata fields.
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
lines: list[str] = [
|
|
63
|
+
f"Pattern: {info.pattern}",
|
|
64
|
+
f"Category: {info.category}",
|
|
65
|
+
f"Backend: {info.backend or '-'}",
|
|
66
|
+
f"Module: {info.module}",
|
|
67
|
+
f"Function: {info.qualified_name}",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
if info.description:
|
|
71
|
+
lines.append(f"Description: {info.description}")
|
|
72
|
+
if info.example:
|
|
73
|
+
lines.append(f"Example: {info.example}")
|
|
74
|
+
if info.tags:
|
|
75
|
+
lines.append(f"Tags: {', '.join(info.tags)}")
|
|
76
|
+
if info.version:
|
|
77
|
+
lines.append(f"Version: {info.version}")
|
|
78
|
+
if info.deprecated:
|
|
79
|
+
lines.append(f"Deprecated: {info.deprecated}")
|
|
80
|
+
if info.requires:
|
|
81
|
+
lines.append(f"Requires: {', '.join(info.requires)}")
|
|
82
|
+
|
|
83
|
+
if info.parameters:
|
|
84
|
+
lines.append("Parameters:")
|
|
85
|
+
for param in info.parameters:
|
|
86
|
+
type_name = (
|
|
87
|
+
param.type.__name__ if isinstance(param.type, type)
|
|
88
|
+
else str(param.type)
|
|
89
|
+
)
|
|
90
|
+
default_hint = f", default {param.default!r}" if param.default is not None else ""
|
|
91
|
+
required_hint = " (required)" if param.required else ""
|
|
92
|
+
lines.append(
|
|
93
|
+
f" - {param.name}: {type_name}{required_hint}{default_hint}"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
if info.i18n:
|
|
97
|
+
lines.append("Translations:")
|
|
98
|
+
for lang, translated in info.i18n.items():
|
|
99
|
+
lines.append(f" [{lang}] {translated}")
|
|
100
|
+
|
|
101
|
+
return "\n".join(lines)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def format_step_json(steps: list[StepInfo]) -> str:
|
|
105
|
+
"""Format a list of steps as a JSON array.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
steps: The step metadata entries to serialize.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
A JSON string with pattern, category, backend, and description per step.
|
|
112
|
+
|
|
113
|
+
"""
|
|
114
|
+
data: list[dict[str, Any]] = []
|
|
115
|
+
for info in steps:
|
|
116
|
+
data.append({
|
|
117
|
+
"pattern": info.pattern,
|
|
118
|
+
"category": info.category,
|
|
119
|
+
"backend": info.backend,
|
|
120
|
+
"description": info.description,
|
|
121
|
+
"module": info.module,
|
|
122
|
+
"tags": info.tags,
|
|
123
|
+
"version": info.version,
|
|
124
|
+
"deprecated": info.deprecated,
|
|
125
|
+
"example": info.example,
|
|
126
|
+
"i18n": info.i18n,
|
|
127
|
+
})
|
|
128
|
+
return json.dumps(data, indent=2, ensure_ascii=False)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def format_step_detail_json(info: StepInfo) -> str:
|
|
132
|
+
"""Format a single step's metadata as JSON.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
info: The step metadata to serialize.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
A JSON string with all metadata fields.
|
|
139
|
+
|
|
140
|
+
"""
|
|
141
|
+
data: dict[str, Any] = {
|
|
142
|
+
"pattern": info.pattern,
|
|
143
|
+
"category": info.category,
|
|
144
|
+
"backend": info.backend,
|
|
145
|
+
"description": info.description,
|
|
146
|
+
"module": info.module,
|
|
147
|
+
"function": info.qualified_name,
|
|
148
|
+
"example": info.example,
|
|
149
|
+
"tags": info.tags,
|
|
150
|
+
"version": info.version,
|
|
151
|
+
"deprecated": info.deprecated,
|
|
152
|
+
"requires": info.requires,
|
|
153
|
+
"i18n": info.i18n,
|
|
154
|
+
"parameters": [
|
|
155
|
+
{
|
|
156
|
+
"name": p.name,
|
|
157
|
+
"type": p.type.__name__ if isinstance(p.type, type) else str(p.type),
|
|
158
|
+
"required": p.required,
|
|
159
|
+
"default": p.default,
|
|
160
|
+
"description": p.description,
|
|
161
|
+
"choices": p.choices,
|
|
162
|
+
}
|
|
163
|
+
for p in info.parameters
|
|
164
|
+
],
|
|
165
|
+
}
|
|
166
|
+
return json.dumps(data, indent=2, ensure_ascii=False)
|