You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(Obscura): self-managed server lifecycle with auto port selection
Obscura now manages its own obscura serve process the same way Playwright
manages its own browser: leaving endpoint unset resolves a binary
(binaryPath, then OBSCURA_PATH, then PATH), spawns it on a free port, and
kills it on teardown, so run-workers instances never collide on a port.
Setting endpoint explicitly switches to attach-only mode; if no binary can
be resolved but something already answers on the conventional :9222, the
helper courtesy-attaches instead of failing. CI stops pre-starting obscura
serve and exercises self-launch via OBSCURA_PATH instead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|`input`|`synthetic`| coordinate-click navigation is unreliable over CDP on Obscura even on rendering builds (no `frameNavigated` event, stale `page.url()`); `click` always takes the `forceClick` path — on Obscura, `click` and `forceClick` are the same thing |
51
-
|`xpathPolyfill`|`auto`| probed per binary/page: Obscura's native `document.evaluate` still doesn't support attribute selection or `not()`, so the polyfill is used until that lands |
|`input`|`synthetic`| coordinate-click navigation is unreliable over CDP on Obscura even on rendering builds (no `frameNavigated` event, stale `page.url()`); `click` always takes the `forceClick` path — on Obscura, `click` and `forceClick` are the same thing |
60
+
|`xpathPolyfill`|`auto`| probed per binary/page: Obscura's native `document.evaluate` still doesn't support attribute selection or `not()`, so the polyfill is used until that lands |
52
61
53
62
`capabilities.layout`/`capabilities.screenshot`/`capabilities.xpath` are intentionally left
54
63
unset here — `CDPBrowser._probeCapabilities` detects them at runtime from the actual binary
@@ -74,30 +83,45 @@ Set them explicitly in your own config to skip probing or to force a mode.
74
83
This helper should be configured in codecept.conf.js. It accepts everything `CDPBrowser`
75
84
accepts (see its config table), plus:
76
85
77
-
Type: [object][2]
86
+
Type: [object][6]
78
87
79
88
### Properties
80
89
81
-
*`binaryPath`**[string][3]?** path to the `obscura` executable. When set, `_connect` spawns
82
-
`obscura serve --port <port> --allow-private-network` before connecting and kills that process
83
-
in `_finishTest`. When unset, Obscura is assumed to already be running (e.g. started by hand, or
84
-
by CI) and this helper only connects to it.
85
-
*`port`**[number][4]?** port `obscura serve` listens on, and the port used to build the
86
-
default `endpoint`.
87
-
*`serverStartTimeout`**[number][4]?** milliseconds to wait for a spawned `obscura serve`
90
+
*`endpoint`**[string][4]?** explicit CDP endpoint. Setting this switches the helper to ATTACH
91
+
mode: it only connects, and never spawns or kills a process, no matter what else is configured.
92
+
Leave it unset for SELF-MANAGED mode (see below).
93
+
*`binaryPath`**[string][4]?** path to the `obscura` executable, used in SELF-MANAGED mode
94
+
(`endpoint` unset). Checked before `OBSCURA_PATH` and `PATH`.
95
+
*`port`**[number][3]?** port `obscura serve` listens on, in SELF-MANAGED mode. When unset, a
96
+
free port is picked automatically, which is what makes `run-workers` collision-free — every
97
+
worker gets its own instance on its own port with zero config.
98
+
*`serverStartTimeout`**[number][3]?** milliseconds to wait for a spawned `obscura serve`
88
99
to answer `/json/version` before `_connect` gives up.
89
100
90
101
91
102
92
103
## Example
93
104
94
105
```js
95
-
// inside codecept.conf.js
106
+
// inside codecept.conf.js — SELF-LAUNCH mode (recommended): the helper finds/starts/stops
107
+
// obscura serve on its own, on a free port. Ideal for run-workers: every worker gets its own
108
+
// instance with no config.
96
109
{
97
110
helpers: {
98
111
Obscura: {
99
112
url:'http://localhost',
100
-
binaryPath:'/usr/local/bin/obscura',
113
+
}
114
+
}
115
+
}
116
+
```
117
+
118
+
```js
119
+
// ATTACH mode — connect to an Obscura instance you manage yourself (remote host, container, etc.)
120
+
{
121
+
helpers: {
122
+
Obscura: {
123
+
url:'http://localhost',
124
+
endpoint:'http://127.0.0.1:9222',
101
125
}
102
126
}
103
127
}
@@ -111,22 +135,57 @@ Type: [object][2]
111
135
112
136
### _connect
113
137
114
-
Spawns `obscura serve` when `options.binaryPath` is set and no server was spawned yet, waits
115
-
for it to accept connections, then connects as `CDPBrowser._connect` normally would.
138
+
In ATTACH mode, connects exactly as `CDPBrowser._connect` would. In SELF-MANAGED mode,
139
+
resolves and spawns `obscura serve` (or courtesy-attaches to an already-running one on
140
+
:9222) exactly once via `_resolveSelfManaged`, then connects.
116
141
117
-
A spawn failure (e.g. a bad `binaryPath`) is delivered asynchronously by Node as an `error`
142
+
A spawn failure (e.g. a bad binary) is delivered asynchronously by Node as an `error`
118
143
event; it is recorded on `this.serverError` and surfaced as a rejection from `_waitForServer`
119
144
instead of crashing the process as an uncaught exception.
120
145
146
+
### _findFreePort
147
+
148
+
Picks a free TCP port on 127.0.0.1 by briefly listening on port 0 and reading back the OS-assigned
149
+
port. Used as the SELF-LAUNCH default when `options.port` isn't explicitly set, so multiple
150
+
`run-workers` workers never collide on the same port.
151
+
152
+
Returns **[Promise][2]<[number][3]>** a free port.
153
+
121
154
### _finishTest
122
155
123
156
Closes the CDP connection (via `CDPBrowser._finishTest`), then kills the `obscura serve`
124
-
process spawned by `_connect`, if any. Runs in a `finally` so the process is always reaped
125
-
even if closing the CDP connection throws. Sends `SIGTERM` first and waits for the process to
126
-
exit; a process that ignores `SIGTERM` is escalated to `SIGKILL` after 5s. The promise only
127
-
resolves once the child has actually exited (confirmed via the `exit` event, not merely once
128
-
`SIGKILL` was sent — the kernel needs a moment to reap it), with a final safety-net timeout so
129
-
a stuck child can never keep the event loop alive even if that confirmation is somehow lost.
157
+
process spawned by `_connect`, if any (never runs in ATTACH or COURTESY-ATTACH mode, since
158
+
`this.serverProcess` is only ever set in SELF-LAUNCH mode). Runs in a `finally` so the process
159
+
is always reaped even if closing the CDP connection throws. Sends `SIGTERM` first and waits for
160
+
the process to exit; a process that ignores `SIGTERM` is escalated to `SIGKILL` after 5s. The
161
+
promise only resolves once the child has actually exited (confirmed via the `exit` event, not
162
+
merely once `SIGKILL` was sent — the kernel needs a moment to reap it), with a final safety-net
163
+
timeout so a stuck child can never keep the event loop alive even if that confirmation is
164
+
somehow lost.
165
+
166
+
### _probeUp
167
+
168
+
Probes a `/json/version`-style URL with a short timeout, used for the COURTESY-ATTACH check.
169
+
170
+
#### Parameters
171
+
172
+
*`url`**[string][4]** 
173
+
174
+
Returns **[Promise][2]<[boolean][5]>** true if the URL answered.
175
+
176
+
### _resolveBinary
177
+
178
+
Resolves the `obscura` binary to spawn, in priority order: `options.binaryPath`, then the
179
+
`OBSCURA_PATH` environment variable, then `obscura` on `PATH` (via `which`).
180
+
181
+
Returns **([string][4] | null)** an absolute or relative path to the binary, or null if none resolved.
182
+
183
+
### _resolveSelfManaged
184
+
185
+
Resolves how to reach Obscura when no explicit `endpoint` was configured, trying, in order:
186
+
spawn a binary (`binaryPath` config, then `OBSCURA_PATH` env, then `obscura` on `PATH`),
187
+
courtesy-attach to `http://127.0.0.1:9222` if something already answers there, or throw a
188
+
loud, actionable error. Sets `this.options.endpoint` as a side effect.
130
189
131
190
### _waitForServer
132
191
@@ -135,8 +194,12 @@ is set by the spawned process' `error` event, or `options.serverStartTimeout` el
0 commit comments