@speqkit/plugin-playwright
v0.9.0
Published
Browser steps, scoped browser/context/page resources and screenshot artifacts.
Downloads
564
Readme
@speqkit/plugin-playwright
Browser steps, scoped browser / context / page resources, and screenshots
attached as artifacts.
# .speq/speq.yaml
plugins: [yaml, http, cli, playwright]
playwright:
browser: chromium # chromium | firefox | webkit
headless: true
baseUrl: https://example.com
viewport: { width: 1280, height: 720 }name: the browser reaches the page
steps:
- id: home
type: browser.open
url: /
- id: shot
type: browser.screenshot
name: home.png
fullPage: true
assert:
- type: title_is
expected: Example Domain
- type: visible
selector: h1Playwright itself is an optional peer dependency, imported the moment a browser is first needed. A repository that only runs HTTP smoke checks never downloads a browser because this package exists.
pnpm add -D playwright && pnpm exec playwright install chromiumSteps and assertions
| Step | Input | Result |
| --- | --- | --- |
| browser.open | url, waitUntil? | url, title, status |
| browser.click | selector, timeout? | selector, url |
| browser.fill | selector, value | selector, value |
| browser.press | selector, key | selector, key |
| browser.wait_for | selector, state? | selector |
| browser.text | selector | text — readable later as ${id.text} |
| browser.screenshot | name?, fullPage? | attaches a PNG |
Assertions: visible, text_contains, title_is, url_contains.
Resources
| Name | Scope | Lifetime |
| --- | --- | --- |
| browser | run | one process for the entire run |
| browser.context | test | fresh cookies and storage per test |
| page | test | closed when the test ends, pass or fail |
Nothing here manages that lifetime. The plugin declares it and the kernel acquires lazily, caches per scope and tears down in reverse order — which is also why a suite that crashes does not leave browsers running.
Why this plugin exists
It is the second half of the architecture gate. @speqkit/plugin-loop proved
control flow can live outside the kernel; this one exercises the two parts of
the spine the loop never touched.
Scoped resources — passed, no kernel change. Three scopes, a resource
depending on a resource, config reaching setup, reverse teardown, and an
assertion reaching for page without a step in front of it. All of it was
already expressible through the published API.
Binary artifacts — the kernel had to keep a promise it had already made.
ExecContext.attach was on the contract from day one, but the body went
nowhere: the event carried a byte count and the bytes were dropped. There was
nothing to fix in the plugin. What changed is that path was added to the
artifact.attached event and the kernel now writes the file — an added field,
a minor, and attach(name, body, contentType) is byte-for-byte the same call.
Suite scope was declared and never opened. ResourceScope published three
scopes and the runner opened two, so a suite resource failed with "scope is
not open here". Fixed in the runner; no API change.
What this plugin still cannot do
Screenshot on failure. The obvious feature, and it is not implementable
from here. A test:after hook receives a HookPayload with no status on it
and no way to attach anything, so the plugin cannot see that a test failed or
hand back the picture proving it. That is a genuine gap in the spine, and the
fix is a design decision rather than a workaround: either HookPayload carries
the outcome and an attach, or failure capture becomes a step-level concern with
a try/catch step type. It is recorded here instead of being smuggled in.
