@wdio/nightwatch-devtools 1.5.1 → 1.6.0
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.
- package/README.md +129 -35
- package/dist/{bidi-FIHBBGT4.js → bidi-5ALZJJNC.js} +2 -2
- package/dist/{chunk-DKSBYMPM.js → chunk-DNCL4MP7.js} +1 -1
- package/dist/chunk-DNCL4MP7.js.map +1 -0
- package/dist/index.d.ts +42 -44
- package/dist/index.js +3013 -739
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
- package/dist/chunk-DKSBYMPM.js.map +0 -1
- /package/dist/{bidi-FIHBBGT4.js.map → bidi-5ALZJJNC.js.map} +0 -0
package/README.md
CHANGED
|
@@ -8,6 +8,46 @@ npm install @wdio/nightwatch-devtools
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
**1. Install the package** in your Nightwatch project:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @wdio/nightwatch-devtools
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**2. Wire the plugin into your Nightwatch config** via the `globals` field:
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
// nightwatch.conf.cjs
|
|
23
|
+
const nightwatchDevtools = require('@wdio/nightwatch-devtools').default
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
src_folders: ['tests'],
|
|
27
|
+
|
|
28
|
+
test_settings: {
|
|
29
|
+
default: {
|
|
30
|
+
desiredCapabilities: {
|
|
31
|
+
browserName: 'chrome',
|
|
32
|
+
// Required for network request capture
|
|
33
|
+
'goog:loggingPrefs': { performance: 'ALL' }
|
|
34
|
+
},
|
|
35
|
+
globals: nightwatchDevtools({ port: 3000 })
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**3. Run your tests as normal** — the DevTools UI opens automatically in a new browser window:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
nightwatch
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
No changes to your test files are needed.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
11
51
|
## Setup
|
|
12
52
|
|
|
13
53
|
### Standard Nightwatch (mocha-style)
|
|
@@ -76,15 +116,9 @@ module.exports = {
|
|
|
76
116
|
|
|
77
117
|
---
|
|
78
118
|
|
|
79
|
-
## Configuration
|
|
119
|
+
## Configuration
|
|
80
120
|
|
|
81
|
-
|
|
82
|
-
|--------|------|---------|-------------|
|
|
83
|
-
| `port` | `number` | `3000` | Port for the DevTools backend server. Auto-incremented if already in use. |
|
|
84
|
-
| `hostname` | `string` | `'localhost'` | Hostname the backend server binds to. |
|
|
85
|
-
| `screencast` | `ScreencastOptions` | `{ enabled: false }` | Session video recording — live mode only (see [Screencast](#screencast)). |
|
|
86
|
-
| `bidi` | `boolean` | `false` | Opt into WebDriver BiDi capture for browser console + JS exceptions + network. Requires `webSocketUrl: true` in your capabilities and a BiDi-capable chromedriver. When attached, the per-command Chrome perf-log network path is gated off so requests don't duplicate. |
|
|
87
|
-
| `mode` | `'live' \| 'trace'` | `'live'` | `'live'` opens the DevTools UI window; `'trace'` skips the UI and writes a `trace-<sessionId>.zip` next to your `nightwatch.conf.cjs` at run end. See [Trace mode](../../README.md#-trace-mode-tracezip). |
|
|
121
|
+
Options are passed to the plugin through the `globals` field — `globals: nightwatchDevtools({ ... })`. **All options are optional**; the plugin runs fine with `nightwatchDevtools({})`. See [Reference](#reference) for the full option table.
|
|
88
122
|
|
|
89
123
|
```javascript
|
|
90
124
|
globals: nightwatchDevtools({
|
|
@@ -96,6 +130,69 @@ globals: nightwatchDevtools({
|
|
|
96
130
|
|
|
97
131
|
---
|
|
98
132
|
|
|
133
|
+
## Live Mode
|
|
134
|
+
|
|
135
|
+
In the default `mode: 'live'`, the plugin launches the DevTools dashboard in a new browser window when your tests run, streaming each test's commands, screenshots, console logs, and network activity in real time. See the [dashboard docs](https://webdriver.io/docs/devtools/dashboard) for a walkthrough of the UI.
|
|
136
|
+
|
|
137
|
+
### Preserve & Rerun (Compare)
|
|
138
|
+
|
|
139
|
+
Available for Nightwatch — same dashboard UI as WebdriverIO. The "compare with rerun" flow snapshots the failing run, re-launches the test with `DEVTOOLS_RERUN_LABEL` set (the plugin filters down to just that test name on the rerun), and the dashboard shows the two runs side-by-side aligned by command.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Trace Mode
|
|
144
|
+
|
|
145
|
+
Set `mode: 'trace'` to skip the DevTools UI window and instead write a portable, self-contained trace artifact at run end — for offline replay, CI artifact collection, or AI-agent diffing. The backend port-bind, the UI window, and the live-only `screencast` option are all skipped in trace mode.
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
globals: nightwatchDevtools({
|
|
149
|
+
mode: 'trace',
|
|
150
|
+
traceFormat: 'ndjson-directory' // optional; default 'zip'
|
|
151
|
+
})
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Nightwatch emits the **same normalized trace** as the WebdriverIO and Selenium adapters (all three share `@wdio/devtools-core`), so the archive format and the `show-trace` player are identical no matter which adapter produced it. A Nightwatch trace carries the full per-action capture — a screenshot, the depth-indented accessibility-tree snapshot, the interactable-element list, and the Markdown transcript — so it opens in the player with DOM/snapshot time-travel, the **A11y** and **Transcript** tabs, an **Errors** tab surfacing expect/step failures with jump-to-source, the pick-locator element overlay, and (for Cucumber) **Feature → Scenario → Step** nesting.
|
|
155
|
+
|
|
156
|
+
Open a trace with the `show-trace` bin, shipped with this package (no extra dependency):
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
npx show-trace test-results/trace-<sessionId>.zip # in a project that installs this adapter
|
|
160
|
+
pnpm show-trace test-results/trace-<sessionId>.zip # from the devtools monorepo
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
For the full artifact contents, the `traceGranularity` / `tracePolicy` reference, and the player walkthrough, see [Trace mode](../../README.md#-trace-mode-tracezip) and [Viewing traces](../../README.md#viewing-traces) in the root README, and the [Trace Mode docs](https://webdriver.io/docs/devtools/wdio/trace-mode).
|
|
164
|
+
|
|
165
|
+
### Per-test slicing & the BDD `describe/it` caveat
|
|
166
|
+
|
|
167
|
+
The per-test options — `traceGranularity: 'test'`, and the `tracePolicy`, `screenshot`, and `video` options that pair with it — need a per-test hook to cut each test's slice. The **exports-object (mocha-style)** interface and **Cucumber** (per-scenario hooks) expose one, so they get real per-test slicing. The **BDD `describe/it`** interface — the style used by this package's bundled example — is the exception: Nightwatch runs each `it()` internally and fires the plugin's per-test hook only once per module, so `traceGranularity: 'test'` collapses to a single **session-scoped** slice keyed to the first test. The artifacts manifest still lists every testcase with its correct state; only the per-test slice/artifact keying collapses. Session- and spec-granularity traces are unaffected.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## BiDi capture
|
|
172
|
+
|
|
173
|
+
Enable WebDriver BiDi capture for browser console messages, JS exceptions, and network requests. Equivalent to the path selenium-devtools uses — both adapters call the same `attachBidiHandlers` in `@wdio/devtools-core`.
|
|
174
|
+
|
|
175
|
+
```javascript
|
|
176
|
+
globals: nightwatchDevtools({
|
|
177
|
+
port: 3000,
|
|
178
|
+
bidi: true
|
|
179
|
+
})
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
You also need `webSocketUrl: true` in your capabilities so chromedriver actually exposes the BiDi channel:
|
|
183
|
+
|
|
184
|
+
```javascript
|
|
185
|
+
desiredCapabilities: {
|
|
186
|
+
browserName: 'chrome',
|
|
187
|
+
'webSocketUrl': true, // ← enables BiDi
|
|
188
|
+
'goog:chromeOptions': { /* ... */ }
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
When attached, the per-command Chrome performance-log network capture path is gated off so requests don't appear twice in the dashboard. If `webSocketUrl` is missing or the chromedriver version doesn't expose BiDi, the attach silently fails and the perf-log fallback continues to work.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
99
196
|
## Screencast
|
|
100
197
|
|
|
101
198
|
Record a continuous `.webm` video of the browser session. The recording starts on the first session the plugin sees and is finalized in Nightwatch's `after()` hook, writing `nightwatch-video-<sessionId>.webm` to the directory of the test file that just ran. Falls back to the directory containing `nightwatch.conf.*` if the test file path isn't known, and to `process.cwd()` as a last resort. Directories under `node_modules/` are skipped.
|
|
@@ -130,6 +227,28 @@ The encoded video is sent to the DevTools dashboard via the `screencast` WS scop
|
|
|
130
227
|
|
|
131
228
|
---
|
|
132
229
|
|
|
230
|
+
## Reference
|
|
231
|
+
|
|
232
|
+
| Option | Type | Default | Description |
|
|
233
|
+
|--------|------|---------|-------------|
|
|
234
|
+
| `port` | `number` | `3000` | Port for the DevTools backend server. Auto-incremented if already in use. |
|
|
235
|
+
| `hostname` | `string` | `'localhost'` | Hostname the backend server binds to. |
|
|
236
|
+
| `screencast` | `ScreencastOptions` | `{ enabled: false }` | Session video recording — live mode only (see [Screencast](#screencast)). |
|
|
237
|
+
| `bidi` | `boolean` | `false` | Opt into WebDriver BiDi capture for browser console + JS exceptions + network. Requires `webSocketUrl: true` in your capabilities and a BiDi-capable chromedriver. When attached, the per-command Chrome perf-log network path is gated off so requests don't duplicate. |
|
|
238
|
+
| `mode` | `'live' \| 'trace'` | `'live'` | `'live'` opens the DevTools UI window; `'trace'` skips the UI and writes a `trace-<sessionId>.zip` (or directory) under a `test-results/` directory at run end (base dir resolved from the test file dir → config dir → cwd). See [Trace mode](#trace-mode). |
|
|
239
|
+
| `traceFormat` | `'zip' \| 'ndjson-directory'` | `'zip'` | Trace artifact layout — `zip` writes a single archive, `ndjson-directory` unpacks the same files into `trace-<sessionId>/`. Only applies when `mode: 'trace'`. |
|
|
240
|
+
| `traceGranularity` | `'session' \| 'spec' \| 'test'` | `'session'` | One trace per session / spec file / test. `'test'` is what the per-test `screenshot` / `video` artifacts attach to. Only applies when `mode: 'trace'`. **Caveat:** the BDD `describe/it` interface collapses to a single session-scoped slice (see [Per-test slicing](#per-test-slicing--the-bdd-describeit-caveat)). |
|
|
241
|
+
| `tracePolicy` | `'on' \| 'retain-on-failure' \| 'retain-on-first-failure' \| 'on-first-retry' \| 'on-all-retries' \| 'retain-on-failure-and-retries'` | `'on'` | Which traces to keep. Pairs with `traceGranularity: 'test'`. Only applies when `mode: 'trace'`. |
|
|
242
|
+
| `filmstrip` | `boolean` | `true` | Record a dense, continuous screencast filmstrip into the trace for scrubbable playback in the trace player — not just one frame per action. Runs the screencast recorder (polling mode on Nightwatch) for the session. Only applies when `mode: 'trace'`. |
|
|
243
|
+
| `screenshot` | `'off' \| 'on' \| 'only-on-failure'` | `'off'` | Per-test screenshot. Trace mode + `traceGranularity: 'test'` only. **Produce-only** — the PNG is written to the trace output dir (and listed in the artifacts manifest when `emitArtifactsManifest: true`); it is NOT attached inline to Allure (see note below). |
|
|
244
|
+
| `video` | `'off' \| TraceRetentionPolicy` | `'off'` | Per-test video slice, retained per the given policy (e.g. `'retain-on-failure'`). Trace mode + `traceGranularity: 'test'` only. Setting a non-`off` policy starts the screencast recorder itself — you do **not** also need `filmstrip` or `screencast.enabled`; the recorder runs continuously for the session and each test's slice is cut from it by wall-clock window. **Produce-only** — the `.webm` is written to the trace output dir (and listed in the manifest when `emitArtifactsManifest: true`); NOT attached inline to Allure. |
|
|
245
|
+
| `emitArtifactsManifest` | `boolean` | `false` | Write the `devtools-artifacts-<sessionId>.json` manifest — the generic index reporters/CI consume to discover produced artifacts — next to the trace. **Opt-in for Nightwatch**: it has no live Allure signal to auto-detect against (`nightwatch-allure` is post-hoc), so unlike WDIO/Selenium it never auto-enables. Trace mode only. |
|
|
246
|
+
| `captureAssertions` | `boolean` | `true` | Capture assertions as trace action rows — `node:assert` plus native `browser.assert.*` / `browser.verify.*`, including negated `.not.*` matchers. Passing assertions render green, failing ones red with the error. Set `false` to opt out. |
|
|
247
|
+
|
|
248
|
+
> **Inline Allure attachment is not supported for Nightwatch.** Its official `nightwatch-allure` reporter is post-hoc (no live attach API), and `allure-js-commons`' `attachment()` no-ops in a Nightwatch run. So `screenshot`/`video` artifacts are *produced* (files, plus the artifacts manifest when `emitArtifactsManifest: true`) in the trace output dir but not attached to an Allure test. Per-test slicing (and therefore these artifacts) is meaningful for the cucumber and exports-object interfaces; the BDD `describe/it` interface collapses to session granularity, so the gate no-ops there.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
133
252
|
## Examples
|
|
134
253
|
|
|
135
254
|
Working examples are included in this package:
|
|
@@ -159,36 +278,11 @@ Nightwatch does not provide the same depth of framework hooks as WebdriverIO, so
|
|
|
159
278
|
| Limited test context | `browser.currentTest` provides less metadata than the WDIO runner context; test names and file paths require additional heuristics. |
|
|
160
279
|
| Flat suite nesting | Nightwatch does not natively support multiply-nested `describe` blocks; the plugin reports a maximum of two levels. |
|
|
161
280
|
| Delayed result availability | Test results are only finalised in `afterEach`, not available mid-test. |
|
|
281
|
+
| Per-test trace slicing (BDD `describe/it`) | The BDD interface fires the plugin's per-test hook once per module, so `traceGranularity: 'test'` collapses to one session-scoped slice. The exports-object (mocha-style) and Cucumber interfaces get real per-test slicing. See [Per-test slicing](#per-test-slicing--the-bdd-describeit-caveat). |
|
|
282
|
+
| Produce-only trace artifacts | Per-test `screenshot` / `video` files are written to the trace output dir (and the manifest when `emitArtifactsManifest: true`) but not attached inline to Allure — Nightwatch has no live Allure attach API. |
|
|
162
283
|
|
|
163
284
|
Overall feature parity with the WebdriverIO DevTools service is approximately **80–90%**.
|
|
164
285
|
|
|
165
|
-
### Preserve & Rerun (Compare)
|
|
166
|
-
|
|
167
|
-
Available for Nightwatch — same dashboard UI as WebdriverIO. The "compare with rerun" flow snapshots the failing run, re-launches the test with `DEVTOOLS_RERUN_LABEL` set (the plugin filters down to just that test name on the rerun), and the dashboard shows the two runs side-by-side aligned by command.
|
|
168
|
-
|
|
169
|
-
### BiDi capture (opt-in)
|
|
170
|
-
|
|
171
|
-
Enable WebDriver BiDi capture for browser console messages, JS exceptions, and network requests. Equivalent to the path selenium-devtools uses — both adapters call the same `attachBidiHandlers` in `@wdio/devtools-core`.
|
|
172
|
-
|
|
173
|
-
```javascript
|
|
174
|
-
globals: nightwatchDevtools({
|
|
175
|
-
port: 3000,
|
|
176
|
-
bidi: true
|
|
177
|
-
})
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
You also need `webSocketUrl: true` in your capabilities so chromedriver actually exposes the BiDi channel:
|
|
181
|
-
|
|
182
|
-
```javascript
|
|
183
|
-
desiredCapabilities: {
|
|
184
|
-
browserName: 'chrome',
|
|
185
|
-
'webSocketUrl': true, // ← enables BiDi
|
|
186
|
-
'goog:chromeOptions': { /* ... */ }
|
|
187
|
-
}
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
When attached, the per-command Chrome performance-log network capture path is gated off so requests don't appear twice in the dashboard. If `webSocketUrl` is missing or the chromedriver version doesn't expose BiDi, the attach silently fails and the perf-log fallback continues to work.
|
|
191
|
-
|
|
192
286
|
## :page_facing_up: License
|
|
193
287
|
|
|
194
288
|
[MIT](/LICENSE)
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
chromeLogLevelToLogLevel,
|
|
4
4
|
errorMessage,
|
|
5
5
|
getRequestType
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-DNCL4MP7.js";
|
|
7
7
|
|
|
8
8
|
// src/bidi.ts
|
|
9
9
|
import logger from "@wdio/logger";
|
|
@@ -226,4 +226,4 @@ export {
|
|
|
226
226
|
attachBidiHandlers2 as attachBidiHandlers,
|
|
227
227
|
buildBidiSinks
|
|
228
228
|
};
|
|
229
|
-
//# sourceMappingURL=bidi-
|
|
229
|
+
//# sourceMappingURL=bidi-5ALZJJNC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../core/src/error.ts","../../core/src/console.ts","../../core/src/net.ts"],"sourcesContent":["/** Plain-object shape of an Error after `serializeError`. */\nexport interface SerializedError {\n name: string\n message: string\n stack?: string\n}\n\n/**\n * Coerce an unknown value (caught exception, framework-supplied error\n * object, string, etc.) into an Error instance. Used at adapter command\n * boundaries where caught values can be anything — Error subclasses,\n * thrown strings, framework objects with a `.message` — and downstream\n * code wants a stable `Error` to inspect and serialize.\n */\nexport function toError(value: unknown): Error {\n if (value instanceof Error) {\n return value\n }\n if (\n value !== null &&\n typeof value === 'object' &&\n typeof (value as { message?: unknown }).message === 'string'\n ) {\n const e = new Error((value as { message: string }).message)\n const name = (value as { name?: unknown }).name\n if (typeof name === 'string') {\n e.name = name\n }\n return e\n }\n return new Error(String(value))\n}\n\n/**\n * Extract a printable message from a caught value. Equivalent to reading\n * `.message` on an Error, but degrades cleanly when the thrown value is a\n * string, a plain object, undefined, or anything else — `(err as Error).message`\n * silently returns `undefined` in those cases and yields useless log output.\n */\nexport function errorMessage(value: unknown): string {\n if (value instanceof Error) {\n return value.message\n }\n if (typeof value === 'string') {\n return value\n }\n if (\n value !== null &&\n typeof value === 'object' &&\n typeof (value as { message?: unknown }).message === 'string'\n ) {\n return (value as { message: string }).message\n }\n if (value === undefined || value === null) {\n return 'unknown error'\n }\n try {\n return String(value)\n } catch {\n return 'unknown error'\n }\n}\n\n/**\n * True when an error message signals the WebDriver session is gone or\n * unreachable — expected when deferred/async capture work races session\n * teardown. Adapters use it to swallow post-quit errors instead of logging noise.\n */\nexport function isSessionGoneError(message: string): boolean {\n return (\n message.includes('ECONNREFUSED') ||\n message.includes('no such session') ||\n message.includes('invalid session id')\n )\n}\n\n/**\n * Normalize an Error to a plain object so its fields survive `JSON.stringify`\n * over the WS bridge. Error instances have `message`/`name`/`stack` as\n * non-enumerable, which `JSON.stringify` would drop.\n *\n * Returns `undefined` when the input is undefined so callers can pass through\n * possibly-undefined values without an extra branch.\n */\nexport function serializeError(\n error: Error | undefined\n): SerializedError | undefined {\n if (!error) {\n return undefined\n }\n return { name: error.name, message: error.message, stack: error.stack }\n}\n","import type { ConsoleLog, LogLevel, LogSource } from '@wdio/devtools-shared'\n\n/**\n * Console methods we intercept to forward test/runner-process output into the\n * UI Console tab.\n */\nexport const CONSOLE_METHODS = ['log', 'info', 'warn', 'error'] as const\n\n/**\n * Strips ANSI escape sequences (colour codes, cursor moves, etc.) from\n * terminal output so the UI Console renders plain text. The pattern accepts\n * any trailing letter, not just `m`, so cursor/style sequences are handled\n * too.\n */\nexport const ANSI_REGEX = /\\x1b\\[[?]?[0-9;]*[A-Za-z]/g\n\nexport function stripAnsi(text: string): string {\n return text.replace(ANSI_REGEX, '')\n}\n\n/**\n * Log-level detection patterns, applied in priority order (highest to\n * lowest). The first matching pattern wins.\n */\nexport const LOG_LEVEL_PATTERNS: ReadonlyArray<{\n level: 'trace' | 'debug' | 'info' | 'warn' | 'error'\n pattern: RegExp\n}> = [\n { level: 'trace', pattern: /\\btrace\\b/i },\n { level: 'debug', pattern: /\\bdebug\\b/i },\n { level: 'info', pattern: /\\binfo\\b/i },\n { level: 'warn', pattern: /\\bwarn(ing)?\\b/i },\n { level: 'error', pattern: /\\berror\\b/i }\n] as const\n\n/** Visual indicators that suggest error-level logs in unstructured output. */\nexport const ERROR_INDICATORS = ['✗', 'failed', 'failure'] as const\n\n/**\n * Matches the leading Braille spinner glyphs that runners (Nightwatch CLI,\n * Selenium tooling) emit for in-place progress updates. Adapters skip lines\n * that match this so the dashboard's Console tab isn't flooded with frames.\n */\nexport const SPINNER_RE = /^[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/u\n\n/**\n * Filter out terminal/stream lines that would feed back into the WS bridge\n * and cause an infinite forwarding loop: pino JSON output, [SESSION] markers,\n * backend logger lines, Jest console framing, and bare stack-frame lines.\n *\n * Adapters call this from their stream-patch before forwarding lines to the\n * UI Console tab. Combine with SPINNER_RE for full noise filtering.\n */\nexport function isInternalStreamLine(line: string): boolean {\n const t = line.trim()\n if (t.startsWith('{\"') || t.startsWith('[SESSION]')) {\n return true\n }\n if (t.includes('@wdio/devtools-backend')) {\n return true\n }\n if (/^console\\.(log|info|warn|error|debug|trace)$/.test(t)) {\n return true\n }\n if (/^at\\s.+:\\d+:\\d+\\)?$/.test(t)) {\n return true\n }\n return false\n}\n\n/** Enum-style accessor for the canonical LogSource values from shared. */\nexport const LOG_SOURCES = {\n BROWSER: 'browser',\n TEST: 'test',\n TERMINAL: 'terminal'\n} as const satisfies Record<string, LogSource>\n\nexport type { LogSource } from '@wdio/devtools-shared'\n\n/**\n * Classify a line of unstructured terminal output by scanning for log-level\n * keywords. Falls back to `'log'` when no pattern matches.\n */\nexport function detectLogLevel(text: string): LogLevel {\n const normalised = stripAnsi(text).toLowerCase()\n for (const { level, pattern } of LOG_LEVEL_PATTERNS) {\n if (pattern.test(normalised)) {\n return level\n }\n }\n if (ERROR_INDICATORS.some((i) => normalised.includes(i.toLowerCase()))) {\n return 'error'\n }\n return 'log'\n}\n\n/** Build a ConsoleLog entry tagged with the supplied source. */\nexport function createConsoleLogEntry(\n type: LogLevel,\n args: unknown[],\n source: LogSource = LOG_SOURCES.TEST\n): ConsoleLog {\n return { timestamp: Date.now(), type, args, source }\n}\n\n/**\n * Map raw Chrome browser-log entries (the shape returned by both\n * `driver.manage().logs().get('browser')` in selenium-webdriver and\n * `browser.getLog('browser')` in nightwatch) into the dashboard's typed\n * ConsoleLog shape, tagged as source='browser'. Each entry's Chrome level\n * (`SEVERE` / `WARNING` / `INFO` / `DEBUG`) is normalised through\n * {@link chromeLogLevelToLogLevel}.\n */\nexport function mapChromeBrowserLogs(\n entries: Array<{ level: unknown; message: string; timestamp: number }>\n): ConsoleLog[] {\n return entries.map((entry) => ({\n timestamp: entry.timestamp,\n type: chromeLogLevelToLogLevel(\n entry.level as string | { value?: number; name?: string }\n ),\n args: [entry.message],\n source: LOG_SOURCES.BROWSER\n }))\n}\n\n/**\n * Map a Chrome DevTools log-level string (or `{name, value}` object) to our\n * `LogLevel` union. Used by CDP/BiDi consumers that surface browser-side\n * console output through SEVERE/WARNING/INFO/DEBUG severity names.\n */\nexport function chromeLogLevelToLogLevel(\n level: string | { value?: number; name?: string }\n): LogLevel {\n const levelName = (\n typeof level === 'object' ? (level?.name ?? '') : (level ?? '')\n ).toUpperCase()\n switch (levelName) {\n case 'SEVERE':\n return 'error'\n case 'WARNING':\n return 'warn'\n case 'INFO':\n return 'info'\n case 'DEBUG':\n return 'debug'\n default:\n return 'log'\n }\n}\n","import * as net from 'node:net'\n\n/**\n * Return true if the given TCP port on `hostname` cannot be bound for\n * listening (already in use, or otherwise unavailable).\n */\nexport function isPortInUse(port: number, hostname: string): Promise<boolean> {\n return new Promise((resolve) => {\n const server = net.createServer()\n server.once('error', () => resolve(true))\n server.once('listening', () => server.close(() => resolve(false)))\n server.listen(port, hostname)\n })\n}\n\n/**\n * Walk upward from `startPort` until a free port is found and return it.\n * Silent: callers that want to log retries should wrap this themselves.\n */\nexport async function findFreePort(\n startPort: number,\n hostname: string\n): Promise<number> {\n let port = startPort\n while (await isPortInUse(port, hostname)) {\n port++\n }\n return port\n}\n\n/**\n * Classify an HTTP request into the categories the dashboard's Network tab\n * uses, preferring the response `mimeType` and falling back to URL extension\n * heuristics. Unknown shapes return `'xhr'`.\n */\nexport function getRequestType(url: string, mimeType?: string): string {\n const contentType = mimeType?.toLowerCase() ?? ''\n const urlLower = url.toLowerCase()\n if (contentType.includes('text/html')) {\n return 'document'\n }\n if (contentType.includes('text/css')) {\n return 'stylesheet'\n }\n if (\n contentType.includes('javascript') ||\n contentType.includes('ecmascript')\n ) {\n return 'script'\n }\n if (contentType.includes('image/')) {\n return 'image'\n }\n if (contentType.includes('font/') || contentType.includes('woff')) {\n return 'font'\n }\n if (contentType.includes('application/json')) {\n return 'fetch'\n }\n if (urlLower.endsWith('.html') || urlLower.endsWith('.htm')) {\n return 'document'\n }\n if (urlLower.endsWith('.css')) {\n return 'stylesheet'\n }\n if (urlLower.endsWith('.js') || urlLower.endsWith('.mjs')) {\n return 'script'\n }\n if (/\\.(png|jpg|jpeg|gif|svg|webp|ico)$/.test(urlLower)) {\n return 'image'\n }\n if (/\\.(woff|woff2|ttf|eot|otf)$/.test(urlLower)) {\n return 'font'\n }\n return 'xhr'\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BaseDevToolsOptions, TraceScreenshotPolicy, TraceVideoPolicy, CucumberPickle, CucumberPickleStep } from '@wdio/devtools-shared';
|
|
2
2
|
|
|
3
|
+
interface NightwatchTestCase {
|
|
4
|
+
passed: number;
|
|
5
|
+
failed: number;
|
|
6
|
+
errors: number;
|
|
7
|
+
skipped: number;
|
|
8
|
+
time: string;
|
|
9
|
+
assertions: unknown[];
|
|
10
|
+
}
|
|
11
|
+
/** Nightwatch's per-test results bag. Loose by design — fields vary across
|
|
12
|
+
* Nightwatch versions. We read only the pieces we need; everything else
|
|
13
|
+
* flows through as `unknown`. */
|
|
14
|
+
interface NightwatchTestResults {
|
|
15
|
+
errors?: number;
|
|
16
|
+
failed?: number;
|
|
17
|
+
passed?: number;
|
|
18
|
+
skipped?: number;
|
|
19
|
+
testcases?: Record<string, NightwatchTestCase>;
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
/** `browser.currentTest` shape — Nightwatch documents this informally. */
|
|
23
|
+
interface NightwatchCurrentTest {
|
|
24
|
+
name?: string;
|
|
25
|
+
module?: string;
|
|
26
|
+
group?: string;
|
|
27
|
+
results?: NightwatchTestResults;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
3
30
|
/** Nightwatch `eventHub` shape — only `runner` + `on()` are documented; the
|
|
4
31
|
* rest of the public surface is `unknown` to us. */
|
|
5
32
|
interface NightwatchEventHub {
|
|
@@ -7,16 +34,7 @@ interface NightwatchEventHub {
|
|
|
7
34
|
on(event: string, listener: (data: unknown) => void): void;
|
|
8
35
|
[key: string]: unknown;
|
|
9
36
|
}
|
|
10
|
-
interface DevToolsOptions {
|
|
11
|
-
port?: number;
|
|
12
|
-
hostname?: string;
|
|
13
|
-
/**
|
|
14
|
-
* Screencast recording options. When enabled, a continuous video of the
|
|
15
|
-
* browser session is recorded and saved as a .webm file at the end of the
|
|
16
|
-
* test run. Polling mode only on Nightwatch (no CDP push); works on every
|
|
17
|
-
* browser Nightwatch supports.
|
|
18
|
-
*/
|
|
19
|
-
screencast?: ScreencastOptions;
|
|
37
|
+
interface DevToolsOptions extends BaseDevToolsOptions {
|
|
20
38
|
/**
|
|
21
39
|
* Enable WebDriver BiDi capture (browser console + JS exceptions + network
|
|
22
40
|
* via `selenium-webdriver/bidi`). Requires `webSocketUrl: true` in your
|
|
@@ -25,15 +43,16 @@ interface DevToolsOptions {
|
|
|
25
43
|
* entries. Defaults to `false` — opt-in.
|
|
26
44
|
*/
|
|
27
45
|
bidi?: boolean;
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
|
|
46
|
+
/** Per-test screenshot capture, produced to the trace output dir + manifest.
|
|
47
|
+
* `off` (default) | `on` | `only-on-failure`. Trace mode +
|
|
48
|
+
* `traceGranularity: 'test'` only. Produce-only: Nightwatch has no live
|
|
49
|
+
* Allure attach API, so artifacts are not attached inline. */
|
|
50
|
+
screenshot?: TraceScreenshotPolicy;
|
|
51
|
+
/** Per-test video (screencast slice) capture, produced to the trace output
|
|
52
|
+
* dir + manifest per the given retention policy. `off` (default) or a
|
|
53
|
+
* retention policy. Trace mode + `traceGranularity: 'test'` only.
|
|
54
|
+
* Produce-only (no inline Allure attach). */
|
|
55
|
+
video?: TraceVideoPolicy;
|
|
37
56
|
}
|
|
38
57
|
interface NightwatchBrowser {
|
|
39
58
|
url: (url: string) => Promise<unknown>;
|
|
@@ -53,12 +72,7 @@ interface NightwatchBrowser {
|
|
|
53
72
|
};
|
|
54
73
|
[key: string]: unknown;
|
|
55
74
|
};
|
|
56
|
-
currentTest?:
|
|
57
|
-
name?: string;
|
|
58
|
-
module?: string;
|
|
59
|
-
group?: string;
|
|
60
|
-
[key: string]: unknown;
|
|
61
|
-
};
|
|
75
|
+
currentTest?: NightwatchCurrentTest;
|
|
62
76
|
results?: unknown;
|
|
63
77
|
queue?: unknown;
|
|
64
78
|
}
|
|
@@ -76,24 +90,8 @@ interface NightwatchBrowser {
|
|
|
76
90
|
* the ctx (they can't reach the plugin's private fields directly).
|
|
77
91
|
*/
|
|
78
92
|
|
|
79
|
-
/**
|
|
80
|
-
*
|
|
81
|
-
interface CucumberPickleStep {
|
|
82
|
-
text?: string;
|
|
83
|
-
astNodeIds?: string[];
|
|
84
|
-
location?: {
|
|
85
|
-
line?: number;
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
interface CucumberPickle {
|
|
89
|
-
uri?: string;
|
|
90
|
-
name?: string;
|
|
91
|
-
location?: {
|
|
92
|
-
line?: number;
|
|
93
|
-
};
|
|
94
|
-
astNodeIds?: string[];
|
|
95
|
-
steps?: CucumberPickleStep[];
|
|
96
|
-
}
|
|
93
|
+
/** Cucumber's result shape varies across major versions; we pin only the
|
|
94
|
+
* status field we read. Pickle shapes come from shared. */
|
|
97
95
|
interface CucumberResult {
|
|
98
96
|
status?: string;
|
|
99
97
|
}
|