craftdriver 1.2.0 → 1.4.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/CHANGELOG.md +20 -0
- package/README.md +2 -1
- package/dist/cli/dispatcher.d.ts.map +1 -1
- package/dist/cli/dispatcher.js +13 -1
- package/dist/cli/dispatcher.js.map +1 -1
- package/dist/cli/mcp/tools.d.ts +1 -2
- package/dist/cli/mcp/tools.d.ts.map +1 -1
- package/dist/cli/mcp/tools.js +18 -0
- package/dist/cli/mcp/tools.js.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/browser.d.ts +156 -27
- package/dist/lib/browser.d.ts.map +1 -1
- package/dist/lib/browser.js +474 -212
- package/dist/lib/browser.js.map +1 -1
- package/dist/lib/capabilities.d.ts +35 -0
- package/dist/lib/capabilities.d.ts.map +1 -0
- package/dist/lib/capabilities.js +58 -0
- package/dist/lib/capabilities.js.map +1 -0
- package/dist/lib/chrome.d.ts +7 -2
- package/dist/lib/chrome.d.ts.map +1 -1
- package/dist/lib/chrome.js +10 -1
- package/dist/lib/chrome.js.map +1 -1
- package/dist/lib/driverManager.d.ts +92 -0
- package/dist/lib/driverManager.d.ts.map +1 -1
- package/dist/lib/driverManager.js +229 -10
- package/dist/lib/driverManager.js.map +1 -1
- package/dist/lib/electron.d.ts +60 -0
- package/dist/lib/electron.d.ts.map +1 -0
- package/dist/lib/electron.js +85 -0
- package/dist/lib/electron.js.map +1 -0
- package/dist/lib/electronDeeplink.d.ts +22 -0
- package/dist/lib/electronDeeplink.d.ts.map +1 -0
- package/dist/lib/electronDeeplink.js +118 -0
- package/dist/lib/electronDeeplink.js.map +1 -0
- package/dist/lib/electronDiagnostics.d.ts +49 -0
- package/dist/lib/electronDiagnostics.d.ts.map +1 -0
- package/dist/lib/electronDiagnostics.js +195 -0
- package/dist/lib/electronDiagnostics.js.map +1 -0
- package/dist/lib/electronDialogMock.d.ts +51 -0
- package/dist/lib/electronDialogMock.d.ts.map +1 -0
- package/dist/lib/electronDialogMock.js +106 -0
- package/dist/lib/electronDialogMock.js.map +1 -0
- package/dist/lib/electronFuses.d.ts +13 -0
- package/dist/lib/electronFuses.d.ts.map +1 -0
- package/dist/lib/electronFuses.js +118 -0
- package/dist/lib/electronFuses.js.map +1 -0
- package/dist/lib/electronMainBridge.d.ts +28 -0
- package/dist/lib/electronMainBridge.d.ts.map +1 -0
- package/dist/lib/electronMainBridge.js +270 -0
- package/dist/lib/electronMainBridge.js.map +1 -0
- package/dist/lib/electronMainLogs.d.ts +81 -0
- package/dist/lib/electronMainLogs.d.ts.map +1 -0
- package/dist/lib/electronMainLogs.js +176 -0
- package/dist/lib/electronMainLogs.js.map +1 -0
- package/dist/lib/electronMock.d.ts +52 -0
- package/dist/lib/electronMock.d.ts.map +1 -0
- package/dist/lib/electronMock.js +96 -0
- package/dist/lib/electronMock.js.map +1 -0
- package/dist/lib/electronRemote.d.ts +106 -0
- package/dist/lib/electronRemote.d.ts.map +1 -0
- package/dist/lib/electronRemote.js +406 -0
- package/dist/lib/electronRemote.js.map +1 -0
- package/dist/lib/electronVersions.d.ts +34 -0
- package/dist/lib/electronVersions.d.ts.map +1 -0
- package/dist/lib/electronVersions.js +90 -0
- package/dist/lib/electronVersions.js.map +1 -0
- package/dist/lib/errors.d.ts +27 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +27 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/launchTarget.d.ts +30 -0
- package/dist/lib/launchTarget.d.ts.map +1 -0
- package/dist/lib/launchTarget.js +112 -0
- package/dist/lib/launchTarget.js.map +1 -0
- package/dist/lib/page.d.ts +1 -0
- package/dist/lib/page.d.ts.map +1 -1
- package/dist/lib/page.js +1 -1
- package/dist/lib/page.js.map +1 -1
- package/dist/lib/service.d.ts +21 -0
- package/dist/lib/service.d.ts.map +1 -1
- package/dist/lib/service.js +94 -6
- package/dist/lib/service.js.map +1 -1
- package/dist/lib/tracing.d.ts +20 -4
- package/dist/lib/tracing.d.ts.map +1 -1
- package/dist/lib/tracing.js +38 -3
- package/dist/lib/tracing.js.map +1 -1
- package/dist/lib/vibiumTrace.d.ts +14 -0
- package/dist/lib/vibiumTrace.d.ts.map +1 -0
- package/dist/lib/vibiumTrace.js +372 -0
- package/dist/lib/vibiumTrace.js.map +1 -0
- package/docs/api-reference.md +38 -2
- package/docs/browser-api.md +7 -1
- package/docs/electron.md +419 -0
- package/docs/error-codes.md +5 -0
- package/docs/index.md +4 -0
- package/docs/mcp.md +17 -3
- package/docs/public/recipes/trace-failing-test.html +16 -0
- package/docs/public/recipes/vitest-vibium-trace.html +16 -0
- package/docs/public/traces/vitest-login.zip +0 -0
- package/docs/recipes/debug-failing-tests-with-traces.md +117 -0
- package/docs/recipes/electron-app-from-another-repo.md +136 -0
- package/docs/recipes/electron-deep-link.md +117 -0
- package/docs/recipes/electron-mock-apis.md +88 -0
- package/docs/recipes/electron-native-dialog.md +105 -0
- package/docs/recipes.md +29 -10
- package/docs/tracing.md +102 -24
- package/package.json +8 -8
- package/skills/craftdriver/cheatsheet.md +4 -2
- package/skills/craftdriver/patterns.md +2 -1
- package/docs/recipes/trace-failing-test.md +0 -51
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main-process console/error capture for Electron — the `browser.electron.mainLogs`
|
|
3
|
+
* counterpart to the renderer's `browser.logs`. It buffers what the app's **main
|
|
4
|
+
* process** writes to `console.*`, delivered over the same Node inspector socket as
|
|
5
|
+
* `executeMain` via CDP `Runtime.consoleAPICalled` events (see ElectronMainBridge).
|
|
6
|
+
*
|
|
7
|
+
* Why console-based (measured against the real app, 2026-07-11): in the Electron
|
|
8
|
+
* main process `Runtime.exceptionThrown` is *not* a reliable error signal — Electron
|
|
9
|
+
* installs its own uncaught-exception handling, and unhandled promise rejections are
|
|
10
|
+
* emitted by Node as `UnhandledPromiseRejectionWarning`s routed through
|
|
11
|
+
* `console.error`. So `console.error(...)` (and those warnings) are captured as
|
|
12
|
+
* error-level entries; any `exceptionThrown` that *does* arrive is surfaced too, as a
|
|
13
|
+
* best-effort `type: 'exception'` entry.
|
|
14
|
+
*
|
|
15
|
+
* Capture is always-on once the session is launched with `electron.mainProcess: true`
|
|
16
|
+
* (the bridge connects and enables the Runtime domain at launch), mirroring the
|
|
17
|
+
* always-on renderer log capture.
|
|
18
|
+
*/
|
|
19
|
+
import { CraftdriverError, ErrorCode } from './errors.js';
|
|
20
|
+
import { DEFAULT_NAVIGATION_TIMEOUT_MS } from './timing.js';
|
|
21
|
+
const MAX_LOGS = 1000;
|
|
22
|
+
/** Map a CDP console `type` to a normalized level (aligned with @wdio/electron-service). */
|
|
23
|
+
function mapConsoleLevel(type) {
|
|
24
|
+
switch (type) {
|
|
25
|
+
case 'error':
|
|
26
|
+
case 'assert':
|
|
27
|
+
return 'error';
|
|
28
|
+
case 'warning':
|
|
29
|
+
return 'warn';
|
|
30
|
+
case 'info':
|
|
31
|
+
return 'info';
|
|
32
|
+
case 'debug':
|
|
33
|
+
case 'trace':
|
|
34
|
+
return 'debug';
|
|
35
|
+
case 'log':
|
|
36
|
+
return 'log';
|
|
37
|
+
default:
|
|
38
|
+
return 'log';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function deserializeArg(o) {
|
|
42
|
+
if (o.type === 'undefined')
|
|
43
|
+
return undefined;
|
|
44
|
+
if (o.subtype === 'null')
|
|
45
|
+
return null;
|
|
46
|
+
if (o.type === 'string' || o.type === 'number' || o.type === 'boolean')
|
|
47
|
+
return o.value;
|
|
48
|
+
// objects, functions, errors, bigint, …: the description is the portable, console-like form.
|
|
49
|
+
return o.description ?? (o.subtype ? `[${o.subtype}]` : `[${o.type}]`);
|
|
50
|
+
}
|
|
51
|
+
function argToText(o) {
|
|
52
|
+
if (o.type === 'undefined')
|
|
53
|
+
return 'undefined';
|
|
54
|
+
if (o.subtype === 'null')
|
|
55
|
+
return 'null';
|
|
56
|
+
if (o.type === 'string' || o.type === 'number' || o.type === 'boolean')
|
|
57
|
+
return String(o.value);
|
|
58
|
+
return o.description ?? (o.subtype ? `[${o.subtype}]` : `[${o.type}]`);
|
|
59
|
+
}
|
|
60
|
+
function formatStackTrace(trace) {
|
|
61
|
+
if (!trace?.callFrames?.length)
|
|
62
|
+
return undefined;
|
|
63
|
+
return trace.callFrames
|
|
64
|
+
.map((f) => ` at ${f.functionName || '<anonymous>'} (${f.url ?? ''}:${f.lineNumber ?? 0}:${f.columnNumber ?? 0})`)
|
|
65
|
+
.join('\n');
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Buffers and dispatches main-process log entries. Reachable via
|
|
69
|
+
* `browser.electron.mainLogs`; its public surface mirrors the renderer
|
|
70
|
+
* {@link LogMonitor} so console monitoring reads the same in both processes.
|
|
71
|
+
*/
|
|
72
|
+
export class ElectronMainLogMonitor {
|
|
73
|
+
#logs = [];
|
|
74
|
+
#handlers = new Set();
|
|
75
|
+
#errorHandlers = new Set();
|
|
76
|
+
/** Feed a CDP `Runtime.consoleAPICalled` event. */
|
|
77
|
+
ingestConsoleEvent(params) {
|
|
78
|
+
const args = params.args ?? [];
|
|
79
|
+
this.#record({
|
|
80
|
+
type: 'console',
|
|
81
|
+
level: mapConsoleLevel(params.type),
|
|
82
|
+
text: args.map(argToText).join(' '),
|
|
83
|
+
args: args.map(deserializeArg),
|
|
84
|
+
timestamp: params.timestamp ? new Date(params.timestamp) : new Date(),
|
|
85
|
+
method: params.type,
|
|
86
|
+
stackTrace: formatStackTrace(params.stackTrace),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/** Feed a CDP `Runtime.exceptionThrown` event (best-effort in Electron main). */
|
|
90
|
+
ingestExceptionEvent(params) {
|
|
91
|
+
const details = params.exceptionDetails;
|
|
92
|
+
const text = details?.exception?.description ?? details?.text ?? 'uncaught exception';
|
|
93
|
+
this.#record({
|
|
94
|
+
type: 'exception',
|
|
95
|
+
level: 'error',
|
|
96
|
+
text,
|
|
97
|
+
args: [],
|
|
98
|
+
timestamp: params.timestamp ? new Date(params.timestamp) : new Date(),
|
|
99
|
+
stackTrace: formatStackTrace(details?.stackTrace),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/** All captured entries (console + exceptions), oldest first. */
|
|
103
|
+
getLogs() {
|
|
104
|
+
return [...this.#logs];
|
|
105
|
+
}
|
|
106
|
+
/** Only error-level entries: `console.error`/`console.assert` and every reported exception. */
|
|
107
|
+
getErrors() {
|
|
108
|
+
return this.#logs.filter((l) => l.level === 'error');
|
|
109
|
+
}
|
|
110
|
+
/** Drop everything captured so far. */
|
|
111
|
+
clearLogs() {
|
|
112
|
+
this.#logs = [];
|
|
113
|
+
}
|
|
114
|
+
/** Subscribe to every entry. Returns an unsubscribe function. */
|
|
115
|
+
onLog(handler) {
|
|
116
|
+
this.#handlers.add(handler);
|
|
117
|
+
return () => this.#handlers.delete(handler);
|
|
118
|
+
}
|
|
119
|
+
/** Subscribe to error-level entries only. Returns an unsubscribe function. */
|
|
120
|
+
onError(handler) {
|
|
121
|
+
this.#errorHandlers.add(handler);
|
|
122
|
+
return () => this.#errorHandlers.delete(handler);
|
|
123
|
+
}
|
|
124
|
+
/** Resolve with the first entry matching `predicate` (checked against already-buffered entries first). */
|
|
125
|
+
waitForLog(predicate, timeout = DEFAULT_NAVIGATION_TIMEOUT_MS) {
|
|
126
|
+
return this.#waitFor(this.#logs, this.onLog.bind(this), predicate, timeout, 'log');
|
|
127
|
+
}
|
|
128
|
+
/** Resolve with the first error-level entry matching `predicate` (default: any error). */
|
|
129
|
+
waitForError(predicate = () => true, timeout = DEFAULT_NAVIGATION_TIMEOUT_MS) {
|
|
130
|
+
return this.#waitFor(this.getErrors(), this.onError.bind(this), predicate, timeout, 'error');
|
|
131
|
+
}
|
|
132
|
+
/** Throw if any error-level entry was captured — a fail-fast assertion for tests. */
|
|
133
|
+
assertNoErrors() {
|
|
134
|
+
const errors = this.getErrors();
|
|
135
|
+
if (errors.length > 0) {
|
|
136
|
+
throw new CraftdriverError(ErrorCode.ELECTRON_MAIN_THREW, `Electron main process reported ${errors.length} error(s):\n${errors.map((e) => e.text).join('\n')}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
#record(log) {
|
|
140
|
+
this.#logs.push(log);
|
|
141
|
+
if (this.#logs.length > MAX_LOGS)
|
|
142
|
+
this.#logs.shift();
|
|
143
|
+
this.#notify(this.#handlers, log);
|
|
144
|
+
if (log.level === 'error')
|
|
145
|
+
this.#notify(this.#errorHandlers, log);
|
|
146
|
+
}
|
|
147
|
+
#notify(handlers, log) {
|
|
148
|
+
for (const handler of handlers) {
|
|
149
|
+
try {
|
|
150
|
+
handler(log);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// A subscriber's own failure must never break capture or other subscribers.
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
#waitFor(buffered, subscribe, predicate, timeout, kind) {
|
|
158
|
+
const existing = buffered.find(predicate);
|
|
159
|
+
if (existing)
|
|
160
|
+
return Promise.resolve(existing);
|
|
161
|
+
return new Promise((resolve, reject) => {
|
|
162
|
+
const timer = setTimeout(() => {
|
|
163
|
+
unsubscribe();
|
|
164
|
+
reject(new CraftdriverError(ErrorCode.TIMEOUT, `Timed out after ${timeout}ms waiting for a matching Electron main-process ${kind}.`));
|
|
165
|
+
}, timeout);
|
|
166
|
+
const unsubscribe = subscribe((log) => {
|
|
167
|
+
if (!predicate(log))
|
|
168
|
+
return;
|
|
169
|
+
clearTimeout(timer);
|
|
170
|
+
unsubscribe();
|
|
171
|
+
resolve(log);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=electronMainLogs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"electronMainLogs.js","sourceRoot":"","sources":["../../src/lib/electronMainLogs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** One recorded invocation of a mocked Electron main-process method. */
|
|
2
|
+
export interface ElectronMockCall {
|
|
3
|
+
/** The (JSON-safe) arguments the app passed to the mocked method. */
|
|
4
|
+
args: unknown[];
|
|
5
|
+
}
|
|
6
|
+
interface MainExecutor {
|
|
7
|
+
executeMain<T = unknown>(fn: (electron: unknown, ...args: unknown[]) => T | Promise<T>, ...args: unknown[]): Promise<T>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A replacement for a single `electron.<api>.<fn>` main-process method, returned
|
|
11
|
+
* by `browser.electron.mock()`. Records every call, returns a scripted value, and
|
|
12
|
+
* restores the original method on `restore()` or `browser.quit()`.
|
|
13
|
+
*
|
|
14
|
+
* This is the general primitive behind the typed `mockDialog()` convenience: use
|
|
15
|
+
* it for any `electron` module method (`app.getPath`, `shell.openExternal`,
|
|
16
|
+
* `safeStorage.encryptString`, …) whose real behavior you want to replace during a
|
|
17
|
+
* test.
|
|
18
|
+
*/
|
|
19
|
+
export interface ElectronMock {
|
|
20
|
+
/** The `electron` module namespace, e.g. `'app'`, `'shell'`, `'dialog'`. */
|
|
21
|
+
readonly api: string;
|
|
22
|
+
/** The method name on that namespace, e.g. `'getPath'`, `'openExternal'`. */
|
|
23
|
+
readonly fn: string;
|
|
24
|
+
/** Every recorded call, oldest first. Args are JSON-safe copies. */
|
|
25
|
+
getCalls(): Promise<ElectronMockCall[]>;
|
|
26
|
+
/** How many times the mocked method was called. */
|
|
27
|
+
getCallCount(): Promise<number>;
|
|
28
|
+
/** Re-script the value the mocked method returns from now on. */
|
|
29
|
+
mockReturnValue(value: unknown): Promise<void>;
|
|
30
|
+
/** Clear recorded calls without changing the scripted return value. */
|
|
31
|
+
clearCalls(): Promise<void>;
|
|
32
|
+
/** Restore the original method. Safe to call more than once. */
|
|
33
|
+
restore(): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
/** Internal implementation; users receive the public {@link ElectronMock} interface. */
|
|
36
|
+
export declare class ElectronMockHandle implements ElectronMock {
|
|
37
|
+
#private;
|
|
38
|
+
readonly api: string;
|
|
39
|
+
readonly fn: string;
|
|
40
|
+
constructor(executor: MainExecutor, api: string, fn: string, id: string, onRestore: () => void);
|
|
41
|
+
getCalls(): Promise<ElectronMockCall[]>;
|
|
42
|
+
getCallCount(): Promise<number>;
|
|
43
|
+
mockReturnValue(value: unknown): Promise<void>;
|
|
44
|
+
clearCalls(): Promise<void>;
|
|
45
|
+
restore(): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
/** Reject obviously wrong `api`/`fn` before touching the main process. */
|
|
48
|
+
export declare function validateMockTarget(api: string, fn: string): void;
|
|
49
|
+
/** The `Symbol.for` key string, exported so {@link ElectronRemote} can install the registry. */
|
|
50
|
+
export declare const ELECTRON_MOCK_REGISTRY_KEY = "craftdriver.electron.apiMocks";
|
|
51
|
+
export {};
|
|
52
|
+
//# sourceMappingURL=electronMock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"electronMock.d.ts","sourceRoot":"","sources":["../../src/lib/electronMock.ts"],"names":[],"mappings":"AAEA,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB;AAED,UAAU,YAAY;IACpB,WAAW,CAAC,CAAC,GAAG,OAAO,EACrB,EAAE,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAC7D,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,CAAC,CAAC,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,QAAQ,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACxC,mDAAmD;IACnD,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,iEAAiE;IACjE,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,uEAAuE;IACvE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,gEAAgE;IAChE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAKD,wFAAwF;AACxF,qBAAa,kBAAmB,YAAW,YAAY;;IACrD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBAQlB,QAAQ,EAAE,YAAY,EACtB,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,IAAI;IAUjB,QAAQ,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAmBvC,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAI/B,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAyB9C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAoC/B;AAED,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAahE;AAED,gGAAgG;AAChG,eAAO,MAAM,0BAA0B,kCAAsB,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { CraftdriverError, ErrorCode } from './errors.js';
|
|
2
|
+
/** Symbol under which every active mock's state lives in the main process. */
|
|
3
|
+
const REGISTRY_SYMBOL_KEY = 'craftdriver.electron.apiMocks';
|
|
4
|
+
/** Internal implementation; users receive the public {@link ElectronMock} interface. */
|
|
5
|
+
export class ElectronMockHandle {
|
|
6
|
+
api;
|
|
7
|
+
fn;
|
|
8
|
+
#key;
|
|
9
|
+
#id;
|
|
10
|
+
#executor;
|
|
11
|
+
#onRestore;
|
|
12
|
+
#restored = false;
|
|
13
|
+
constructor(executor, api, fn, id, onRestore) {
|
|
14
|
+
this.#executor = executor;
|
|
15
|
+
this.api = api;
|
|
16
|
+
this.fn = fn;
|
|
17
|
+
this.#key = `${api}.${fn}`;
|
|
18
|
+
this.#id = id;
|
|
19
|
+
this.#onRestore = onRestore;
|
|
20
|
+
}
|
|
21
|
+
async getCalls() {
|
|
22
|
+
this.#assertActive();
|
|
23
|
+
return this.#executor.executeMain((_electron, registryKey, entryKey, id) => {
|
|
24
|
+
const key = Symbol.for(registryKey);
|
|
25
|
+
const registry = globalThis[key];
|
|
26
|
+
const entry = registry?.[entryKey];
|
|
27
|
+
if (!entry || entry.id !== id)
|
|
28
|
+
throw new Error(`Mock ${entryKey} is no longer active.`);
|
|
29
|
+
return entry.calls;
|
|
30
|
+
}, REGISTRY_SYMBOL_KEY, this.#key, this.#id);
|
|
31
|
+
}
|
|
32
|
+
async getCallCount() {
|
|
33
|
+
return (await this.getCalls()).length;
|
|
34
|
+
}
|
|
35
|
+
async mockReturnValue(value) {
|
|
36
|
+
this.#assertActive();
|
|
37
|
+
// `value` rides across the wire as an executeMain arg (validated JSON-safe there).
|
|
38
|
+
// `undefined` can't cross that validation, so carry a presence flag and let the
|
|
39
|
+
// callback restore undefined — makes mockReturnValue(undefined) work too.
|
|
40
|
+
const hasValue = value !== undefined;
|
|
41
|
+
await this.#executor.executeMain((_electron, registryKey, entryKey, id, present, next) => {
|
|
42
|
+
const key = Symbol.for(registryKey);
|
|
43
|
+
const registry = globalThis[key];
|
|
44
|
+
const entry = registry?.[entryKey];
|
|
45
|
+
if (!entry || entry.id !== id)
|
|
46
|
+
throw new Error(`Mock ${entryKey} is no longer active.`);
|
|
47
|
+
entry.result = present ? next : undefined;
|
|
48
|
+
}, REGISTRY_SYMBOL_KEY, this.#key, this.#id, hasValue, hasValue ? value : null);
|
|
49
|
+
}
|
|
50
|
+
async clearCalls() {
|
|
51
|
+
this.#assertActive();
|
|
52
|
+
await this.#executor.executeMain((_electron, registryKey, entryKey, id) => {
|
|
53
|
+
const key = Symbol.for(registryKey);
|
|
54
|
+
const registry = globalThis[key];
|
|
55
|
+
const entry = registry?.[entryKey];
|
|
56
|
+
if (!entry || entry.id !== id)
|
|
57
|
+
throw new Error(`Mock ${entryKey} is no longer active.`);
|
|
58
|
+
entry.calls.length = 0;
|
|
59
|
+
}, REGISTRY_SYMBOL_KEY, this.#key, this.#id);
|
|
60
|
+
}
|
|
61
|
+
async restore() {
|
|
62
|
+
if (this.#restored)
|
|
63
|
+
return;
|
|
64
|
+
await this.#executor.executeMain((electron, registryKey, apiName, funcName, id) => {
|
|
65
|
+
const entryKey = `${apiName}.${funcName}`;
|
|
66
|
+
const key = Symbol.for(registryKey);
|
|
67
|
+
const registry = globalThis[key];
|
|
68
|
+
const entry = registry?.[entryKey];
|
|
69
|
+
if (!entry || entry.id !== id)
|
|
70
|
+
return;
|
|
71
|
+
const api = electron[apiName];
|
|
72
|
+
if (api && api[funcName] === entry.replacement)
|
|
73
|
+
api[funcName] = entry.original;
|
|
74
|
+
delete registry[entryKey];
|
|
75
|
+
}, REGISTRY_SYMBOL_KEY, this.api, this.fn, this.#id);
|
|
76
|
+
this.#restored = true;
|
|
77
|
+
this.#onRestore();
|
|
78
|
+
}
|
|
79
|
+
#assertActive() {
|
|
80
|
+
if (this.#restored) {
|
|
81
|
+
throw new CraftdriverError(ErrorCode.STATE_INVALID, `The electron.${this.#key} mock has already been restored.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** Reject obviously wrong `api`/`fn` before touching the main process. */
|
|
86
|
+
export function validateMockTarget(api, fn) {
|
|
87
|
+
if (typeof api !== 'string' || api.length === 0) {
|
|
88
|
+
throw new CraftdriverError(ErrorCode.INVALID_ARGUMENT, 'electron.mock(api, fn): api must be a non-empty string (e.g. "app", "shell", "dialog").');
|
|
89
|
+
}
|
|
90
|
+
if (typeof fn !== 'string' || fn.length === 0) {
|
|
91
|
+
throw new CraftdriverError(ErrorCode.INVALID_ARGUMENT, `electron.mock('${api}', fn): fn must be a non-empty string (e.g. "getPath", "openExternal").`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** The `Symbol.for` key string, exported so {@link ElectronRemote} can install the registry. */
|
|
95
|
+
export const ELECTRON_MOCK_REGISTRY_KEY = REGISTRY_SYMBOL_KEY;
|
|
96
|
+
//# sourceMappingURL=electronMock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"electronMock.js","sourceRoot":"","sources":["../../src/lib/electronMock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { ElectronMainLogMonitor } from './electronMainLogs.js';
|
|
2
|
+
import { type ElectronDialogMock, type ElectronMessageBoxResult, type ElectronOpenDialogResult, type ElectronSaveDialogResult } from './electronDialogMock.js';
|
|
3
|
+
import { type ElectronMock } from './electronMock.js';
|
|
4
|
+
export interface ElectronMainConnectInfo {
|
|
5
|
+
host: string;
|
|
6
|
+
port: number;
|
|
7
|
+
}
|
|
8
|
+
/** Extra context ElectronRemote needs beyond the inspector endpoint. */
|
|
9
|
+
export interface ElectronRemoteOptions {
|
|
10
|
+
/** The launched app's executable path — required to route deep links on Windows. */
|
|
11
|
+
appBinaryPath?: string;
|
|
12
|
+
}
|
|
13
|
+
/** A function evaluated in the Electron main process; receives the `electron` module. */
|
|
14
|
+
export type MainProcessCallback<T> = (electron: unknown, ...args: unknown[]) => T | Promise<T>;
|
|
15
|
+
/**
|
|
16
|
+
* Run code in the Electron main process via `browser.electron.executeMain`.
|
|
17
|
+
* Named `executeMain` (not WDIO's `execute`) so the process boundary is loud:
|
|
18
|
+
* `browser.evaluate()` is the renderer, `executeMain()` the main process.
|
|
19
|
+
*/
|
|
20
|
+
export declare class ElectronRemote {
|
|
21
|
+
#private;
|
|
22
|
+
/** `connect` is undefined for non-Electron sessions or when `mainProcess` wasn't enabled. */
|
|
23
|
+
constructor(connect?: ElectronMainConnectInfo, options?: ElectronRemoteOptions);
|
|
24
|
+
/**
|
|
25
|
+
* Captured **main-process** console output and errors — the main-process
|
|
26
|
+
* counterpart to `browser.logs`. Populated from launch when the session was
|
|
27
|
+
* started with `electron: { mainProcess: true }` (empty otherwise). See
|
|
28
|
+
* {@link ElectronMainLogMonitor}.
|
|
29
|
+
*
|
|
30
|
+
* ```ts
|
|
31
|
+
* await browser.electron.executeMain((electron) => console.log('ready', electron.app.getName()));
|
|
32
|
+
* const [entry] = browser.electron.mainLogs.getLogs();
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
get mainLogs(): ElectronMainLogMonitor;
|
|
36
|
+
/**
|
|
37
|
+
* Open the main-process bridge now (rather than lazily on first `executeMain`)
|
|
38
|
+
* so `mainLogs` captures from launch. No-op without a main-process inspector;
|
|
39
|
+
* best-effort — the caller (Browser.launch) swallows failures so a disabled
|
|
40
|
+
* inspector fuse never breaks renderer automation.
|
|
41
|
+
*/
|
|
42
|
+
connect(): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Run `fn` in the Electron **main process**. `fn` receives the `electron`
|
|
45
|
+
* module first, then your `args` (which must be JSON-serializable). The return
|
|
46
|
+
* value is passed back by value, so it must be JSON-serializable too.
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
49
|
+
* const version = await browser.electron.executeMain(
|
|
50
|
+
* (electron) => electron.app.getVersion(),
|
|
51
|
+
* );
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
executeMain<T = unknown>(fn: MainProcessCallback<T>, ...args: unknown[]): Promise<T>;
|
|
55
|
+
/**
|
|
56
|
+
* Replace any `electron.<api>.<fn>` **main-process** method with a scripted
|
|
57
|
+
* return value and call recorder. The general primitive behind the typed
|
|
58
|
+
* {@link mockDialog} convenience — reach for it for `shell.openExternal`,
|
|
59
|
+
* `app.getPath`, `safeStorage.encryptString`, and other Electron APIs.
|
|
60
|
+
*
|
|
61
|
+
* The app is not modified: the replacement lives in its main process for this
|
|
62
|
+
* session and is restored by `mock.restore()` or `browser.quit()`. The scripted
|
|
63
|
+
* value is returned **as-is** (not wrapped in a Promise), so it works for both
|
|
64
|
+
* synchronous methods and `await`ed asynchronous ones. Provide the resolved
|
|
65
|
+
* value for an async method; recorded call args and the return value must be
|
|
66
|
+
* JSON-serializable.
|
|
67
|
+
*
|
|
68
|
+
* ```ts
|
|
69
|
+
* const openExternal = await browser.electron.mock('shell', 'openExternal', true);
|
|
70
|
+
* await browser.click(By.testId('open-docs'));
|
|
71
|
+
* expect(await openExternal.getCalls()).toEqual([{ args: ['https://example.com/docs'] }]);
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
mock(api: string, fn: string, returnValue?: unknown): Promise<ElectronMock>;
|
|
75
|
+
/**
|
|
76
|
+
* Open a custom-protocol **deep link** (`myapp://…`) against the running app,
|
|
77
|
+
* exactly as the OS would when another app or the browser hands off the URL —
|
|
78
|
+
* so your app's real `open-url` (macOS) / `second-instance` (Windows, Linux)
|
|
79
|
+
* handler runs. Fire-and-forget: it resolves once the OS launcher is spawned;
|
|
80
|
+
* assert the effect through your app afterwards (e.g. `waitForLog`, a renderer
|
|
81
|
+
* assertion, or `executeMain`).
|
|
82
|
+
*
|
|
83
|
+
* Your app must register the protocol (`app.setAsDefaultProtocolClient(...)`)
|
|
84
|
+
* and take the single-instance lock. On Windows and Linux a raw deep link would
|
|
85
|
+
* spawn a *second* instance; craftdriver appends the running app's user-data dir
|
|
86
|
+
* as a `userData` query parameter so the second instance can route the URL to
|
|
87
|
+
* the test instance (auto-detected via the main process when available).
|
|
88
|
+
*
|
|
89
|
+
* ```ts
|
|
90
|
+
* await browser.electron.triggerDeeplink('myapp://open?file=test.txt');
|
|
91
|
+
* const entry = await browser.electron.mainLogs.waitForLog((l) => l.text.includes('open-url'));
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
triggerDeeplink(url: string): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Replace an asynchronous native Electron dialog with a deterministic result.
|
|
97
|
+
* The app is not modified: the replacement lives in its main process for this
|
|
98
|
+
* session and is restored by `mock.restore()` or `browser.quit()`.
|
|
99
|
+
*/
|
|
100
|
+
mockDialog(method: 'showOpenDialog', result: ElectronOpenDialogResult): Promise<ElectronDialogMock>;
|
|
101
|
+
mockDialog(method: 'showSaveDialog', result: ElectronSaveDialogResult): Promise<ElectronDialogMock>;
|
|
102
|
+
mockDialog(method: 'showMessageBox', result: ElectronMessageBoxResult): Promise<ElectronDialogMock>;
|
|
103
|
+
/** Close the underlying inspector connection. Called from `Browser.quit()`. */
|
|
104
|
+
close(): Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=electronRemote.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"electronRemote.d.ts","sourceRoot":"","sources":["../../src/lib/electronRemote.ts"],"names":[],"mappings":"AAcA,OAAO,EACL,sBAAsB,EAGvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAEL,KAAK,kBAAkB,EAEvB,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAE9B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAEL,KAAK,YAAY,EAElB,MAAM,mBAAmB,CAAC;AAS3B,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wEAAwE;AACxE,MAAM,WAAW,qBAAqB;IACpC,oFAAoF;IACpF,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,yFAAyF;AACzF,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AA8D/F;;;;GAIG;AACH,qBAAa,cAAc;;IASzB,6FAA6F;gBACjF,OAAO,CAAC,EAAE,uBAAuB,EAAE,OAAO,CAAC,EAAE,qBAAqB;IAK9E;;;;;;;;;;OAUG;IACH,IAAI,QAAQ,IAAI,sBAAsB,CAErC;IAED;;;;;OAKG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK9B;;;;;;;;;;OAUG;IACG,WAAW,CAAC,CAAC,GAAG,OAAO,EAAE,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC;IA2B1F;;;;;;;;;;;;;;;;;;OAkBG;IACG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC;IA6FjF;;;;;;;;;;;;;;;;;;OAkBG;IACG,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BjD;;;;OAIG;IACG,UAAU,CACd,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,kBAAkB,CAAC;IACxB,UAAU,CACd,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,kBAAkB,CAAC;IACxB,UAAU,CACd,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,kBAAkB,CAAC;IA6J9B,+EAA+E;IACzE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAU7B"}
|