@testivai/witness-webdriverio 0.1.1
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/LICENSE +21 -0
- package/README.md +142 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +31 -0
- package/dist/service.d.ts +36 -0
- package/dist/service.js +74 -0
- package/dist/types.d.ts +51 -0
- package/dist/types.js +5 -0
- package/dist/witness.d.ts +31 -0
- package/dist/witness.js +68 -0
- package/package.json +75 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 TestivAI
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# @testivai/witness-webdriverio
|
|
2
|
+
|
|
3
|
+
WebdriverIO adapter for [TestivAI Witness](https://github.com/mcbuddy/testivai-oss) — local-first visual regression testing with pixel + DOM comparison. No account required.
|
|
4
|
+
|
|
5
|
+
Pairs with `@testivai/witness` (the local CLI + diff engine + HTML report) and shares the `.testivai/baselines/` layout used by `@testivai/witness-playwright`. Switching between Playwright and WebdriverIO produces the same baselines, the same report, and the same approval workflow.
|
|
6
|
+
|
|
7
|
+
## Why an adapter and not the `testivai run` sidecar?
|
|
8
|
+
|
|
9
|
+
The framework-agnostic `testivai run` mode launches your test process and attaches to Chrome via CDP. That works but is brittle: framework-specific Chrome launch flags, target selection across iframes, signal handling. This adapter sidesteps all of that by using WebdriverIO's native `browser.takeScreenshot()` + `browser.execute()`. No port polling, no race conditions.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -D @testivai/witness @testivai/witness-webdriverio
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`webdriverio@>=8` is a peer dependency (you should already have it).
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
### 1. Tell TestivAI to run in local mode
|
|
22
|
+
|
|
23
|
+
Create `.testivai/config.json` at your project root:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"mode": "local",
|
|
28
|
+
"threshold": 0.1,
|
|
29
|
+
"reportDir": "visual-report",
|
|
30
|
+
"autoOpen": false
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This file is the local-mode marker. Without it, the adapter logs a warning and skips report generation (cloud mode upload is not yet implemented for WDIO).
|
|
35
|
+
|
|
36
|
+
### 2. Register the service in `wdio.conf.ts`
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { TestivaiService } from '@testivai/witness-webdriverio/service';
|
|
40
|
+
|
|
41
|
+
export const config = {
|
|
42
|
+
// ...
|
|
43
|
+
services: [
|
|
44
|
+
[TestivaiService, { quiet: false }],
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The service runs after all tests complete. It reads `.testivai/temp/` (where `witness()` wrote captures), diffs against `.testivai/baselines/`, and produces `visual-report/` with `index.html` + `results.json`.
|
|
50
|
+
|
|
51
|
+
### 3. Capture inside your tests
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { testivai } from '@testivai/witness-webdriverio';
|
|
55
|
+
|
|
56
|
+
describe('Homepage', () => {
|
|
57
|
+
it('looks correct', async () => {
|
|
58
|
+
await browser.url('https://example.com');
|
|
59
|
+
await testivai.witness(browser, 'homepage');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('checkout flow first step', async () => {
|
|
63
|
+
await browser.url('https://example.com/checkout');
|
|
64
|
+
await testivai.witness(browser, 'checkout-step-1');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 4. Run
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx wdio run wdio.conf.ts
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- **First run**: baselines are written to `.testivai/baselines/<name>/`. Commit them with `git add .testivai/baselines`.
|
|
76
|
+
- **Later runs**: screenshots are diffed and a self-contained HTML report is written to `./visual-report/`.
|
|
77
|
+
|
|
78
|
+
## Approving changes
|
|
79
|
+
|
|
80
|
+
When the report shows changed snapshots:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# Review them in visual-report/index.html
|
|
84
|
+
open visual-report/index.html
|
|
85
|
+
|
|
86
|
+
# Approve a single snapshot
|
|
87
|
+
npx testivai approve "homepage"
|
|
88
|
+
|
|
89
|
+
# Approve everything that changed
|
|
90
|
+
npx testivai approve --all
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Approve writes the temp capture over the baseline and backs the previous baseline up to `.testivai/baselines/<name>/.previous/`. `npx testivai approve --undo "homepage"` reverts.
|
|
94
|
+
|
|
95
|
+
## DOM noise hint
|
|
96
|
+
|
|
97
|
+
When pixels differ but the page DOM is structurally identical, the report flags the change as "likely render noise" (anti-aliasing, font hinting, sub-pixel layout). When the DOM is also different, the report shows added / removed / attribute-change counts so you can decide whether the change is intentional. Same signal as the Playwright adapter — both write `dom.html` alongside the screenshot.
|
|
98
|
+
|
|
99
|
+
DOM capture happens automatically. To skip it for a single snapshot:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
await testivai.witness(browser, 'no-dom', { skipDom: true });
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## API
|
|
106
|
+
|
|
107
|
+
### `testivai.witness(browser, name, options?)`
|
|
108
|
+
|
|
109
|
+
Captures a screenshot + DOM and writes them as a temp snapshot.
|
|
110
|
+
|
|
111
|
+
| Param | Type | Description |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| `browser` | WebdriverIO browser | Must expose `takeScreenshot()` and (for DOM) `execute()`. |
|
|
114
|
+
| `name` | string | Snapshot name. Becomes `.testivai/temp/<name>/` and the key in the report. |
|
|
115
|
+
| `options.skipDom` | boolean | Skip DOM capture for this snapshot. |
|
|
116
|
+
|
|
117
|
+
### `TestivaiService`
|
|
118
|
+
|
|
119
|
+
Class registered as a WDIO service. Implements `onComplete`.
|
|
120
|
+
|
|
121
|
+
| Option | Type | Default | Description |
|
|
122
|
+
|---|---|---|---|
|
|
123
|
+
| `projectRoot` | string | `process.cwd()` | Project root for the test run. |
|
|
124
|
+
| `reportDir` | string | local config (`visual-report`) | Override the report directory. |
|
|
125
|
+
| `threshold` | number | local config (`0.1`) | Diff threshold (0–1). |
|
|
126
|
+
| `autoOpen` | boolean | `false` | Auto-open the report in a browser. |
|
|
127
|
+
| `quiet` | boolean | `false` | Suppress all logging except errors. |
|
|
128
|
+
|
|
129
|
+
## Status
|
|
130
|
+
|
|
131
|
+
- **Local mode**: stable
|
|
132
|
+
- **Cloud mode** (upload to TestivAI hosted): not yet implemented in this adapter — coming in a later iteration. The Playwright adapter has both lanes today; the WDIO adapter will follow once local is broadly used.
|
|
133
|
+
|
|
134
|
+
## Links
|
|
135
|
+
|
|
136
|
+
- Repo: https://github.com/mcbuddy/testivai-oss
|
|
137
|
+
- Plan: see `docs/OSS_PLAN_V3.md` in `mcbuddy/testivai-monorepo`
|
|
138
|
+
- Issues: https://github.com/mcbuddy/testivai-oss/issues
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @testivai/witness-webdriverio
|
|
3
|
+
*
|
|
4
|
+
* WebdriverIO adapter for the TestivAI Witness OSS toolkit. Pairs with
|
|
5
|
+
* `@testivai/witness` (the local CLI + diff engine) and shares the same
|
|
6
|
+
* `.testivai/baselines/` layout as `@testivai/witness-playwright`.
|
|
7
|
+
*
|
|
8
|
+
* import { testivai } from '@testivai/witness-webdriverio';
|
|
9
|
+
* import { TestivaiService } from '@testivai/witness-webdriverio/service';
|
|
10
|
+
*/
|
|
11
|
+
import { witness } from './witness';
|
|
12
|
+
export { witness } from './witness';
|
|
13
|
+
export { TestivaiService, default as DefaultTestivaiService } from './service';
|
|
14
|
+
export type { WitnessBrowser, WitnessOptions, TestivaiServiceOptions } from './types';
|
|
15
|
+
/**
|
|
16
|
+
* Convenience namespace mirroring @testivai/witness-playwright's API:
|
|
17
|
+
*
|
|
18
|
+
* await testivai.witness(browser, 'homepage');
|
|
19
|
+
*/
|
|
20
|
+
export declare const testivai: {
|
|
21
|
+
witness: typeof witness;
|
|
22
|
+
};
|
|
23
|
+
export declare const VERSION: string;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @testivai/witness-webdriverio
|
|
4
|
+
*
|
|
5
|
+
* WebdriverIO adapter for the TestivAI Witness OSS toolkit. Pairs with
|
|
6
|
+
* `@testivai/witness` (the local CLI + diff engine) and shares the same
|
|
7
|
+
* `.testivai/baselines/` layout as `@testivai/witness-playwright`.
|
|
8
|
+
*
|
|
9
|
+
* import { testivai } from '@testivai/witness-webdriverio';
|
|
10
|
+
* import { TestivaiService } from '@testivai/witness-webdriverio/service';
|
|
11
|
+
*/
|
|
12
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
+
};
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.VERSION = exports.testivai = exports.DefaultTestivaiService = exports.TestivaiService = exports.witness = void 0;
|
|
17
|
+
const witness_1 = require("./witness");
|
|
18
|
+
var witness_2 = require("./witness");
|
|
19
|
+
Object.defineProperty(exports, "witness", { enumerable: true, get: function () { return witness_2.witness; } });
|
|
20
|
+
var service_1 = require("./service");
|
|
21
|
+
Object.defineProperty(exports, "TestivaiService", { enumerable: true, get: function () { return service_1.TestivaiService; } });
|
|
22
|
+
Object.defineProperty(exports, "DefaultTestivaiService", { enumerable: true, get: function () { return __importDefault(service_1).default; } });
|
|
23
|
+
/**
|
|
24
|
+
* Convenience namespace mirroring @testivai/witness-playwright's API:
|
|
25
|
+
*
|
|
26
|
+
* await testivai.witness(browser, 'homepage');
|
|
27
|
+
*/
|
|
28
|
+
exports.testivai = {
|
|
29
|
+
witness: witness_1.witness,
|
|
30
|
+
};
|
|
31
|
+
exports.VERSION = require('../package.json').version;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @testivai/witness-webdriverio — WDIO service
|
|
3
|
+
*
|
|
4
|
+
* Registered in wdio.conf.ts so the report is generated automatically
|
|
5
|
+
* after the test suite finishes:
|
|
6
|
+
*
|
|
7
|
+
* import { TestivaiService } from '@testivai/witness-webdriverio/service';
|
|
8
|
+
*
|
|
9
|
+
* export const config = {
|
|
10
|
+
* services: [[TestivaiService, { quiet: false }]],
|
|
11
|
+
* // ...
|
|
12
|
+
* };
|
|
13
|
+
*
|
|
14
|
+
* The service does ONE thing in `onComplete`: detect local mode, then
|
|
15
|
+
* call `generateReport()` from @testivai/witness/report. There is no
|
|
16
|
+
* cloud upload path in this iteration — the OSS goal is standalone-
|
|
17
|
+
* complete local mode. If a future cloud upload is added, it slots in
|
|
18
|
+
* here without changing the witness() capture function.
|
|
19
|
+
*/
|
|
20
|
+
import type { TestivaiServiceOptions } from './types';
|
|
21
|
+
export declare class TestivaiService {
|
|
22
|
+
private readonly options;
|
|
23
|
+
constructor(serviceOptions?: TestivaiServiceOptions);
|
|
24
|
+
/**
|
|
25
|
+
* WebdriverIO calls onComplete after all worker processes finish.
|
|
26
|
+
* Signature is intentionally loose — we only consume the parts we need
|
|
27
|
+
* and remain forward-compatible with WDIO 8 and 9.
|
|
28
|
+
*/
|
|
29
|
+
onComplete(_exitCode?: number, _config?: unknown, _capabilities?: unknown, _results?: unknown): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Default export so consumers can use the WDIO services-by-class form
|
|
33
|
+
* directly: `services: [TestivaiService]`. The class form is also
|
|
34
|
+
* compatible with the tuple form for passing options.
|
|
35
|
+
*/
|
|
36
|
+
export default TestivaiService;
|
package/dist/service.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @testivai/witness-webdriverio — WDIO service
|
|
4
|
+
*
|
|
5
|
+
* Registered in wdio.conf.ts so the report is generated automatically
|
|
6
|
+
* after the test suite finishes:
|
|
7
|
+
*
|
|
8
|
+
* import { TestivaiService } from '@testivai/witness-webdriverio/service';
|
|
9
|
+
*
|
|
10
|
+
* export const config = {
|
|
11
|
+
* services: [[TestivaiService, { quiet: false }]],
|
|
12
|
+
* // ...
|
|
13
|
+
* };
|
|
14
|
+
*
|
|
15
|
+
* The service does ONE thing in `onComplete`: detect local mode, then
|
|
16
|
+
* call `generateReport()` from @testivai/witness/report. There is no
|
|
17
|
+
* cloud upload path in this iteration — the OSS goal is standalone-
|
|
18
|
+
* complete local mode. If a future cloud upload is added, it slots in
|
|
19
|
+
* here without changing the witness() capture function.
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.TestivaiService = void 0;
|
|
23
|
+
const witness_1 = require("@testivai/witness");
|
|
24
|
+
class TestivaiService {
|
|
25
|
+
constructor(serviceOptions = {}) {
|
|
26
|
+
this.options = serviceOptions;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* WebdriverIO calls onComplete after all worker processes finish.
|
|
30
|
+
* Signature is intentionally loose — we only consume the parts we need
|
|
31
|
+
* and remain forward-compatible with WDIO 8 and 9.
|
|
32
|
+
*/
|
|
33
|
+
async onComplete(_exitCode, _config, _capabilities, _results) {
|
|
34
|
+
const projectRoot = this.options.projectRoot ?? process.cwd();
|
|
35
|
+
const log = (msg) => {
|
|
36
|
+
if (!this.options.quiet) {
|
|
37
|
+
// eslint-disable-next-line no-console
|
|
38
|
+
console.log(`[testivai] ${msg}`);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
if (!(0, witness_1.isLocalMode)(projectRoot)) {
|
|
42
|
+
log('Cloud mode is not yet supported by @testivai/witness-webdriverio — skipping report generation. ' +
|
|
43
|
+
'To enable local mode, create .testivai/config.json with {"mode":"local"}.');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const reportData = (0, witness_1.generateReport)({
|
|
48
|
+
projectRoot,
|
|
49
|
+
reportDir: this.options.reportDir,
|
|
50
|
+
threshold: this.options.threshold,
|
|
51
|
+
autoOpen: this.options.autoOpen ?? false,
|
|
52
|
+
});
|
|
53
|
+
const { summary } = reportData;
|
|
54
|
+
log(`Visual report: total=${summary.total} passed=${summary.passed} ` +
|
|
55
|
+
`changed=${summary.changed} new=${summary.newSnapshots}`);
|
|
56
|
+
if (summary.changed > 0 || summary.newSnapshots > 0) {
|
|
57
|
+
log('To approve: npx testivai approve --all');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
// Reporting failure must not crash the WDIO run after all tests
|
|
62
|
+
// already passed. Log and exit cleanly.
|
|
63
|
+
// eslint-disable-next-line no-console
|
|
64
|
+
console.error('[testivai] Failed to generate visual report:', err);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.TestivaiService = TestivaiService;
|
|
69
|
+
/**
|
|
70
|
+
* Default export so consumers can use the WDIO services-by-class form
|
|
71
|
+
* directly: `services: [TestivaiService]`. The class form is also
|
|
72
|
+
* compatible with the tuple form for passing options.
|
|
73
|
+
*/
|
|
74
|
+
exports.default = TestivaiService;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types for @testivai/witness-webdriverio.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Minimal subset of the WebdriverIO `browser` object the witness function
|
|
6
|
+
* relies on. Defining a structural type here means consumers can pass any
|
|
7
|
+
* WDIO-shaped object (sync or async, real or mocked) without us pulling
|
|
8
|
+
* `webdriverio`'s heavy global type into our public surface.
|
|
9
|
+
*/
|
|
10
|
+
export interface WitnessBrowser {
|
|
11
|
+
/**
|
|
12
|
+
* Capture a full-page screenshot of the active session. Returns a base64
|
|
13
|
+
* PNG, matching the WebdriverIO `browser.takeScreenshot()` return shape.
|
|
14
|
+
*/
|
|
15
|
+
takeScreenshot(): Promise<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Execute a script in the page context. We only use the no-arg form.
|
|
18
|
+
* Type kept loose to accept WDIO 8/9 signature variants.
|
|
19
|
+
*/
|
|
20
|
+
execute<T = unknown>(script: string | ((...args: any[]) => T), ...args: any[]): Promise<T>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Options for a single witness() call. All optional.
|
|
24
|
+
*/
|
|
25
|
+
export interface WitnessOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Skip DOM capture even if the adapter would normally write dom.html
|
|
28
|
+
* alongside the screenshot. Use for pages where DOM serialization is
|
|
29
|
+
* known to be expensive or unstable.
|
|
30
|
+
*/
|
|
31
|
+
skipDom?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Configuration passed to the WDIO service.
|
|
35
|
+
*
|
|
36
|
+
* Local mode is detected automatically from `.testivai/config.json` —
|
|
37
|
+
* these options exist only to override the defaults that
|
|
38
|
+
* generateReport() picks up from that file.
|
|
39
|
+
*/
|
|
40
|
+
export interface TestivaiServiceOptions {
|
|
41
|
+
/** Project root for the test run. Defaults to `process.cwd()`. */
|
|
42
|
+
projectRoot?: string;
|
|
43
|
+
/** Override the report directory. Defaults to local config (`visual-report`). */
|
|
44
|
+
reportDir?: string;
|
|
45
|
+
/** Override the diff threshold (0–1). Defaults to local config (0.1). */
|
|
46
|
+
threshold?: number;
|
|
47
|
+
/** Auto-open the rendered report in a browser. Defaults to local config. */
|
|
48
|
+
autoOpen?: boolean;
|
|
49
|
+
/** Suppress all adapter logging except errors. */
|
|
50
|
+
quiet?: boolean;
|
|
51
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @testivai/witness-webdriverio — capture function
|
|
3
|
+
*
|
|
4
|
+
* The user-facing call inside a WebdriverIO test:
|
|
5
|
+
*
|
|
6
|
+
* import { testivai } from '@testivai/witness-webdriverio';
|
|
7
|
+
* await testivai.witness(browser, 'homepage');
|
|
8
|
+
*
|
|
9
|
+
* Captures a full-page screenshot via `browser.takeScreenshot()` and the
|
|
10
|
+
* page DOM via `browser.execute(() => document.documentElement.outerHTML)`,
|
|
11
|
+
* then writes both into `.testivai/temp/<name>/` using the same
|
|
12
|
+
* BaselineStore layout as @testivai/witness-playwright. The TestivaiService
|
|
13
|
+
* (registered in wdio.conf.ts) runs `compareAll` + `generateReport` after
|
|
14
|
+
* all tests finish.
|
|
15
|
+
*
|
|
16
|
+
* Architectural choice: we rely on WebdriverIO's native `takeScreenshot`
|
|
17
|
+
* and `execute` APIs rather than opening a CDP session. This is the whole
|
|
18
|
+
* point of the per-framework adapter — using the framework's stable public
|
|
19
|
+
* API instead of fighting Chrome-launch coordination.
|
|
20
|
+
*/
|
|
21
|
+
import type { WitnessBrowser, WitnessOptions } from './types';
|
|
22
|
+
/**
|
|
23
|
+
* Capture a screenshot + DOM and write them as a temp snapshot.
|
|
24
|
+
*
|
|
25
|
+
* @param browser WebdriverIO browser-like object (must expose
|
|
26
|
+
* `takeScreenshot()` and `execute()`).
|
|
27
|
+
* @param name Snapshot name. Becomes the directory under
|
|
28
|
+
* `.testivai/temp/<name>/` and the key in the rendered report.
|
|
29
|
+
* @param options Per-call overrides.
|
|
30
|
+
*/
|
|
31
|
+
export declare function witness(browser: WitnessBrowser, name: string, options?: WitnessOptions): Promise<void>;
|
package/dist/witness.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @testivai/witness-webdriverio — capture function
|
|
4
|
+
*
|
|
5
|
+
* The user-facing call inside a WebdriverIO test:
|
|
6
|
+
*
|
|
7
|
+
* import { testivai } from '@testivai/witness-webdriverio';
|
|
8
|
+
* await testivai.witness(browser, 'homepage');
|
|
9
|
+
*
|
|
10
|
+
* Captures a full-page screenshot via `browser.takeScreenshot()` and the
|
|
11
|
+
* page DOM via `browser.execute(() => document.documentElement.outerHTML)`,
|
|
12
|
+
* then writes both into `.testivai/temp/<name>/` using the same
|
|
13
|
+
* BaselineStore layout as @testivai/witness-playwright. The TestivaiService
|
|
14
|
+
* (registered in wdio.conf.ts) runs `compareAll` + `generateReport` after
|
|
15
|
+
* all tests finish.
|
|
16
|
+
*
|
|
17
|
+
* Architectural choice: we rely on WebdriverIO's native `takeScreenshot`
|
|
18
|
+
* and `execute` APIs rather than opening a CDP session. This is the whole
|
|
19
|
+
* point of the per-framework adapter — using the framework's stable public
|
|
20
|
+
* API instead of fighting Chrome-launch coordination.
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.witness = witness;
|
|
24
|
+
const witness_1 = require("@testivai/witness");
|
|
25
|
+
/**
|
|
26
|
+
* Capture a screenshot + DOM and write them as a temp snapshot.
|
|
27
|
+
*
|
|
28
|
+
* @param browser WebdriverIO browser-like object (must expose
|
|
29
|
+
* `takeScreenshot()` and `execute()`).
|
|
30
|
+
* @param name Snapshot name. Becomes the directory under
|
|
31
|
+
* `.testivai/temp/<name>/` and the key in the rendered report.
|
|
32
|
+
* @param options Per-call overrides.
|
|
33
|
+
*/
|
|
34
|
+
async function witness(browser, name, options = {}) {
|
|
35
|
+
if (!name || typeof name !== 'string') {
|
|
36
|
+
throw new Error('testivai.witness: snapshot name is required and must be a non-empty string');
|
|
37
|
+
}
|
|
38
|
+
if (!browser || typeof browser.takeScreenshot !== 'function') {
|
|
39
|
+
throw new Error('testivai.witness: browser argument must expose takeScreenshot()');
|
|
40
|
+
}
|
|
41
|
+
// 1. Capture screenshot (base64 PNG → Buffer)
|
|
42
|
+
const base64 = await browser.takeScreenshot();
|
|
43
|
+
if (typeof base64 !== 'string' || base64.length === 0) {
|
|
44
|
+
throw new Error(`testivai.witness("${name}"): browser.takeScreenshot() returned an empty value`);
|
|
45
|
+
}
|
|
46
|
+
const screenshot = Buffer.from(base64, 'base64');
|
|
47
|
+
// 2. Capture DOM (best-effort — never break the screenshot path)
|
|
48
|
+
let dom;
|
|
49
|
+
if (!options.skipDom && typeof browser.execute === 'function') {
|
|
50
|
+
try {
|
|
51
|
+
const result = await browser.execute(
|
|
52
|
+
// Inline string form for maximum WDIO sync/async compatibility.
|
|
53
|
+
// The function-form across WDIO 8/9 has subtle typing differences;
|
|
54
|
+
// the string form is stable.
|
|
55
|
+
'return document.documentElement.outerHTML;');
|
|
56
|
+
if (typeof result === 'string' && result.length > 0) {
|
|
57
|
+
dom = result;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Suppressed by design. DOM capture failure means the noise hint
|
|
62
|
+
// is unavailable for this snapshot; pixel diff still works.
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// 3. Write to .testivai/temp/<name>/
|
|
66
|
+
const store = new witness_1.BaselineStore(process.cwd());
|
|
67
|
+
store.writeTemp(name, screenshot, dom);
|
|
68
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@testivai/witness-webdriverio",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "WebdriverIO adapter for TestivAI Witness — local-first visual regression with pixel + DOM comparison",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./service": {
|
|
13
|
+
"types": "./dist/service.d.ts",
|
|
14
|
+
"default": "./dist/service.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"webdriverio",
|
|
19
|
+
"wdio",
|
|
20
|
+
"wdio-service",
|
|
21
|
+
"visual-regression",
|
|
22
|
+
"testing",
|
|
23
|
+
"testivai"
|
|
24
|
+
],
|
|
25
|
+
"author": "TestivAI",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/mcbuddy/testivai-oss.git",
|
|
30
|
+
"directory": "packages/webdriverio"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/mcbuddy/testivai-oss/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/mcbuddy/testivai-oss/tree/main/packages/webdriverio#readme",
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE"
|
|
43
|
+
],
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18.0.0"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"webdriverio": ">=8.0.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"webdriverio": {
|
|
52
|
+
"optional": false
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@testivai/witness": "1.0.3"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/jest": "^29.0.0",
|
|
60
|
+
"@types/node": "^20.0.0",
|
|
61
|
+
"jest": "^29.0.0",
|
|
62
|
+
"ts-jest": "^29.2.5",
|
|
63
|
+
"typescript": "^5.0.0",
|
|
64
|
+
"webdriverio": "^9.0.0"
|
|
65
|
+
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"build": "tsc",
|
|
68
|
+
"clean": "rm -rf dist",
|
|
69
|
+
"dev": "tsc --watch",
|
|
70
|
+
"test": "jest",
|
|
71
|
+
"test:unit": "jest",
|
|
72
|
+
"test:watch": "jest --watch",
|
|
73
|
+
"test:coverage": "jest --coverage"
|
|
74
|
+
}
|
|
75
|
+
}
|