@react-native-harness/bridge 1.2.0-rc.1 → 1.3.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/dist/binary-transfer.d.ts.map +1 -1
- package/dist/client.d.ts +29 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +66 -61
- package/dist/server.d.ts +41 -21
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +142 -109
- package/dist/shared/test-collector.d.ts +5 -3
- package/dist/shared/test-collector.d.ts.map +1 -1
- package/dist/shared/test-context.d.ts +21 -0
- package/dist/shared/test-context.d.ts.map +1 -0
- package/dist/shared/test-context.js +1 -0
- package/dist/shared.d.ts +3 -2
- package/dist/shared.d.ts.map +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/binary-transfer.ts +1 -1
- package/src/client.ts +80 -42
- package/src/server.ts +219 -175
- package/src/shared/test-collector.ts +7 -3
- package/src/shared/test-context.ts +21 -0
- package/src/shared.ts +7 -4
package/src/server.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { WebSocketServer, type WebSocket } from 'ws';
|
|
2
|
-
import { type
|
|
3
|
-
import { logger } from '@react-native-harness/tools';
|
|
2
|
+
import { createBirpc, type BirpcReturn } from 'birpc';
|
|
4
3
|
import { EventEmitter } from 'node:events';
|
|
5
4
|
import type { Server as HttpServer } from 'node:http';
|
|
6
5
|
import type { Server as HttpsServer } from 'node:https';
|
|
@@ -8,231 +7,276 @@ import fs from 'node:fs/promises';
|
|
|
8
7
|
import os from 'node:os';
|
|
9
8
|
import path from 'node:path';
|
|
10
9
|
import { randomUUID } from 'node:crypto';
|
|
10
|
+
import { logger } from '@react-native-harness/tools';
|
|
11
11
|
import { BinaryStore, parseBinaryFrame } from './binary-transfer.js';
|
|
12
|
+
import { deserialize, serialize } from './serializer.js';
|
|
13
|
+
import { DeviceNotRespondingError } from './errors.js';
|
|
14
|
+
import { matchImageSnapshot } from './image-snapshot.js';
|
|
12
15
|
import type {
|
|
13
16
|
BridgeServerFunctions,
|
|
14
17
|
BridgeClientFunctions,
|
|
15
18
|
DeviceDescriptor,
|
|
16
19
|
BridgeEvents,
|
|
17
|
-
ImageSnapshotOptions,
|
|
18
|
-
HarnessContext,
|
|
19
20
|
BinaryDataReference,
|
|
20
21
|
FileReference,
|
|
22
|
+
HarnessContext,
|
|
23
|
+
TestExecutionOptions,
|
|
24
|
+
TestSuiteResult,
|
|
21
25
|
} from './shared.js';
|
|
22
|
-
import { deserialize, serialize } from './serializer.js';
|
|
23
|
-
import { DeviceNotRespondingError } from './errors.js';
|
|
24
|
-
import { matchImageSnapshot } from './image-snapshot.js';
|
|
25
26
|
|
|
26
27
|
export { DeviceNotRespondingError } from './errors.js';
|
|
28
|
+
|
|
27
29
|
const bridgeLogger = logger.child('bridge');
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
};
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Public types
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Represents a single app session — one app launch to the next restart.
|
|
37
|
+
* Obtained via HarnessBridge.nextConnection().
|
|
38
|
+
*/
|
|
39
|
+
export type AppConnection = {
|
|
40
|
+
readonly device: DeviceDescriptor;
|
|
41
|
+
runTests: (path: string, options: TestExecutionOptions) => Promise<TestSuiteResult>;
|
|
37
42
|
};
|
|
38
43
|
|
|
39
|
-
type
|
|
40
|
-
|
|
44
|
+
export type HarnessBridgeEvents = {
|
|
45
|
+
/** Fired when the app connects and calls reportReady. */
|
|
46
|
+
connected: (connection: AppConnection) => void;
|
|
47
|
+
/** Fired when the app's WebSocket closes. */
|
|
48
|
+
disconnected: () => void;
|
|
49
|
+
/** Fired for every test/bundler event the app emits. */
|
|
50
|
+
event: (event: BridgeEvents) => void;
|
|
41
51
|
};
|
|
42
52
|
|
|
43
|
-
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
53
|
+
type TransportOptions =
|
|
54
|
+
| { noServer: true }
|
|
55
|
+
| { port: number; host?: string }
|
|
56
|
+
| { server: HttpServer | HttpsServer; path?: string };
|
|
57
|
+
|
|
58
|
+
export type HarnessBridgeOptions = TransportOptions & {
|
|
48
59
|
timeout?: number;
|
|
49
60
|
context: HarnessContext;
|
|
50
61
|
};
|
|
51
62
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
ws: WebSocketServer;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
) => void;
|
|
69
|
-
off: <T extends keyof
|
|
70
|
-
event: T,
|
|
71
|
-
listener: BridgeServerEvents[T]
|
|
72
|
-
) => void;
|
|
63
|
+
/**
|
|
64
|
+
* The persistent CLI-side bridge. Spans the full test run regardless of how
|
|
65
|
+
* many times the app is restarted. Each restart produces a new AppConnection
|
|
66
|
+
* via nextConnection().
|
|
67
|
+
*/
|
|
68
|
+
export type HarnessBridge = {
|
|
69
|
+
/** The underlying WebSocket server, used to attach to Metro's HTTP server. */
|
|
70
|
+
readonly ws: WebSocketServer;
|
|
71
|
+
/** The currently active app connection, null if the app is not connected. */
|
|
72
|
+
readonly connection: AppConnection | null;
|
|
73
|
+
/**
|
|
74
|
+
* Resolves with the next AppConnection once the app connects and reports
|
|
75
|
+
* ready. Register this waiter before restarting the app so no ready signal
|
|
76
|
+
* is missed. Rejects if the supplied signal is aborted.
|
|
77
|
+
*/
|
|
78
|
+
nextConnection: (signal?: AbortSignal) => Promise<AppConnection>;
|
|
79
|
+
on: <T extends keyof HarnessBridgeEvents>(event: T, listener: HarnessBridgeEvents[T]) => void;
|
|
80
|
+
off: <T extends keyof HarnessBridgeEvents>(event: T, listener: HarnessBridgeEvents[T]) => void;
|
|
73
81
|
dispose: () => void;
|
|
74
82
|
};
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const wss =
|
|
82
|
-
'port' in transport
|
|
83
|
-
? await new Promise<WebSocketServer>((resolve) => {
|
|
84
|
-
const server = new WebSocketServer(
|
|
85
|
-
{
|
|
86
|
-
port: transport.port,
|
|
87
|
-
host: transport.host ?? '0.0.0.0',
|
|
88
|
-
},
|
|
89
|
-
() => {
|
|
90
|
-
resolve(server);
|
|
91
|
-
}
|
|
92
|
-
);
|
|
93
|
-
})
|
|
94
|
-
: new WebSocketServer(
|
|
95
|
-
'server' in transport
|
|
96
|
-
? {
|
|
97
|
-
server: transport.server,
|
|
98
|
-
path: transport.path,
|
|
99
|
-
}
|
|
100
|
-
: {
|
|
101
|
-
noServer: true,
|
|
102
|
-
}
|
|
103
|
-
);
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Helpers
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
const createWss = (transport: TransportOptions): Promise<WebSocketServer> => {
|
|
104
89
|
if ('port' in transport) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
90
|
+
return new Promise<WebSocketServer>((resolve) => {
|
|
91
|
+
const wss: WebSocketServer = new WebSocketServer(
|
|
92
|
+
{ port: transport.port, host: transport.host ?? '0.0.0.0' },
|
|
93
|
+
() => resolve(wss),
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return Promise.resolve<WebSocketServer>(
|
|
98
|
+
new WebSocketServer(
|
|
99
|
+
'server' in transport
|
|
100
|
+
? { server: transport.server, path: transport.path }
|
|
101
|
+
: { noServer: true },
|
|
102
|
+
),
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const receiveScreenshot = async (
|
|
107
|
+
binaryStore: BinaryStore,
|
|
108
|
+
reference: BinaryDataReference,
|
|
109
|
+
): Promise<FileReference> => {
|
|
110
|
+
const data = binaryStore.get(reference.transferId);
|
|
111
|
+
if (!data) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`Binary data for transfer ${reference.transferId} not found or expired`,
|
|
110
114
|
);
|
|
111
|
-
} else {
|
|
112
|
-
bridgeLogger.debug('bridge server created in noServer mode');
|
|
113
115
|
}
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
116
|
+
binaryStore.delete(reference.transferId);
|
|
117
|
+
const file = path.join(os.tmpdir(), `harness-screenshot-${randomUUID()}.png`);
|
|
118
|
+
await fs.writeFile(file, data);
|
|
119
|
+
return { path: file };
|
|
120
|
+
};
|
|
117
121
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
},
|
|
122
|
-
emitEvent: (_, data) => {
|
|
123
|
-
emitter.emit('event', data);
|
|
124
|
-
},
|
|
125
|
-
'device.screenshot.receive': async (
|
|
126
|
-
reference: BinaryDataReference,
|
|
127
|
-
metadata: { width: number; height: number }
|
|
128
|
-
) => {
|
|
129
|
-
const data = binaryStore.get(reference.transferId);
|
|
130
|
-
if (!data) {
|
|
131
|
-
throw new Error(
|
|
132
|
-
`Binary data for transfer ${reference.transferId} not found or expired`
|
|
133
|
-
);
|
|
134
|
-
}
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// Factory
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
135
125
|
|
|
136
|
-
|
|
137
|
-
|
|
126
|
+
export const createHarnessBridge = async (
|
|
127
|
+
options: HarnessBridgeOptions,
|
|
128
|
+
): Promise<HarnessBridge> => {
|
|
129
|
+
const { timeout, context, ...transport } = options;
|
|
130
|
+
const wss = await createWss(transport);
|
|
131
|
+
bridgeLogger.debug('bridge server ready');
|
|
138
132
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
133
|
+
const emitter = new EventEmitter();
|
|
134
|
+
let currentConnection: AppConnection | null = null;
|
|
135
|
+
const connectionWaiters: Array<{
|
|
136
|
+
resolve: (c: AppConnection) => void;
|
|
137
|
+
reject: (e: unknown) => void;
|
|
138
|
+
}> = [];
|
|
145
139
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
},
|
|
152
|
-
'test.matchImageSnapshot': async (
|
|
153
|
-
screenshot: FileReference,
|
|
154
|
-
testPath: string,
|
|
155
|
-
options: ImageSnapshotOptions
|
|
156
|
-
) => {
|
|
157
|
-
return await matchImageSnapshot(
|
|
158
|
-
screenshot,
|
|
159
|
-
testPath,
|
|
160
|
-
options,
|
|
161
|
-
context.platform.name
|
|
162
|
-
);
|
|
163
|
-
},
|
|
164
|
-
};
|
|
140
|
+
wss.on('connection', (ws: WebSocket) => {
|
|
141
|
+
bridgeLogger.debug('app connected');
|
|
142
|
+
const binaryStore = new BinaryStore();
|
|
143
|
+
let readyConnection: AppConnection | null = null;
|
|
144
|
+
let disconnected = false;
|
|
165
145
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
146
|
+
const serverFunctions: BridgeServerFunctions = {
|
|
147
|
+
reportReady: (device) => {
|
|
148
|
+
const conn: AppConnection = {
|
|
149
|
+
device,
|
|
150
|
+
runTests: (testPath, opts) => rpc.runTests(testPath, opts),
|
|
151
|
+
};
|
|
152
|
+
readyConnection = conn;
|
|
153
|
+
currentConnection = conn;
|
|
154
|
+
bridgeLogger.debug(
|
|
155
|
+
'app ready: platform=%s model=%s',
|
|
156
|
+
device.platform,
|
|
157
|
+
device.model,
|
|
158
|
+
);
|
|
159
|
+
emitter.emit('connected', conn);
|
|
160
|
+
for (const { resolve } of connectionWaiters.splice(0)) resolve(conn);
|
|
175
161
|
},
|
|
176
|
-
|
|
177
|
-
|
|
162
|
+
emitEvent: (_, data) => {
|
|
163
|
+
emitter.emit('event', data);
|
|
178
164
|
},
|
|
179
|
-
|
|
180
|
-
|
|
165
|
+
'device.screenshot.receive': (ref) => receiveScreenshot(binaryStore, ref),
|
|
166
|
+
'test.matchImageSnapshot': (screenshot, testPath, opts) =>
|
|
167
|
+
matchImageSnapshot(screenshot, testPath, opts, context.platform.name),
|
|
168
|
+
};
|
|
181
169
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
bridgeLogger.debug('client disconnected');
|
|
186
|
-
|
|
187
|
-
// TODO: Remove channel when connection is closed.
|
|
188
|
-
clients.delete(ws);
|
|
189
|
-
emitter.emit('disconnect');
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
group.updateChannels((channels) => {
|
|
193
|
-
channels.push({
|
|
170
|
+
const rpc: BirpcReturn<BridgeClientFunctions, BridgeServerFunctions> = createBirpc<BridgeClientFunctions, BridgeServerFunctions>(
|
|
171
|
+
serverFunctions,
|
|
172
|
+
{
|
|
194
173
|
post: (data) => ws.send(data),
|
|
195
174
|
on: (handler) => {
|
|
196
175
|
ws.on(
|
|
197
176
|
'message',
|
|
198
|
-
(
|
|
177
|
+
(msg: Buffer | ArrayBuffer | Buffer[], isBinary: boolean) => {
|
|
199
178
|
if (isBinary) {
|
|
200
|
-
const uint8Array = new Uint8Array(event as any);
|
|
201
179
|
try {
|
|
202
|
-
const
|
|
180
|
+
const messageBuffer = Array.isArray(msg)
|
|
181
|
+
? Buffer.concat(msg)
|
|
182
|
+
: Buffer.isBuffer(msg)
|
|
183
|
+
? msg
|
|
184
|
+
: Buffer.from(msg);
|
|
185
|
+
const { transferId, data } = parseBinaryFrame(
|
|
186
|
+
new Uint8Array(messageBuffer),
|
|
187
|
+
);
|
|
203
188
|
binaryStore.add(transferId, data);
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
bridgeLogger.warn('failed to parse binary frame', error);
|
|
189
|
+
} catch (err) {
|
|
190
|
+
bridgeLogger.warn('failed to parse binary frame: %s', err);
|
|
207
191
|
}
|
|
192
|
+
} else {
|
|
193
|
+
handler(msg.toString());
|
|
208
194
|
}
|
|
209
|
-
|
|
210
|
-
handler(message);
|
|
211
|
-
}
|
|
195
|
+
},
|
|
212
196
|
);
|
|
213
197
|
},
|
|
214
198
|
serialize,
|
|
215
199
|
deserialize,
|
|
216
|
-
|
|
200
|
+
timeout,
|
|
201
|
+
onFunctionError: (error, functionName, args) => {
|
|
202
|
+
bridgeLogger.error(
|
|
203
|
+
'rpc function failed: %s args=%o',
|
|
204
|
+
functionName,
|
|
205
|
+
args,
|
|
206
|
+
);
|
|
207
|
+
throw error;
|
|
208
|
+
},
|
|
209
|
+
onTimeoutError: (fn, args) => {
|
|
210
|
+
throw new DeviceNotRespondingError(fn, args);
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const disconnect = (reason?: Error) => {
|
|
216
|
+
if (disconnected) return;
|
|
217
|
+
disconnected = true;
|
|
218
|
+
|
|
219
|
+
bridgeLogger.debug('app disconnected');
|
|
220
|
+
binaryStore.dispose();
|
|
221
|
+
if (currentConnection === readyConnection) {
|
|
222
|
+
currentConnection = null;
|
|
223
|
+
}
|
|
224
|
+
rpc.$close(reason ?? new Error('App bridge disconnected'));
|
|
225
|
+
emitter.emit('disconnected');
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
ws.on('close', () => {
|
|
229
|
+
disconnect();
|
|
217
230
|
});
|
|
218
|
-
});
|
|
219
231
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
wss.close();
|
|
226
|
-
emitter.removeAllListeners();
|
|
227
|
-
binaryStore.dispose();
|
|
228
|
-
};
|
|
232
|
+
ws.on('error', (error) => {
|
|
233
|
+
disconnect(error instanceof Error ? error : new Error('App bridge socket error'));
|
|
234
|
+
});
|
|
235
|
+
});
|
|
229
236
|
|
|
230
237
|
return {
|
|
231
|
-
ws
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
238
|
+
get ws() {
|
|
239
|
+
return wss;
|
|
240
|
+
},
|
|
241
|
+
get connection() {
|
|
242
|
+
return currentConnection;
|
|
243
|
+
},
|
|
244
|
+
nextConnection: (signal) => {
|
|
245
|
+
if (signal?.aborted) {
|
|
246
|
+
return Promise.reject(
|
|
247
|
+
signal.reason ?? new DOMException('Aborted', 'AbortError'),
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
// If the app already connected before this call (e.g. fast simulator
|
|
251
|
+
// startup between startAttempt and waitForReady), return it immediately
|
|
252
|
+
// rather than waiting for a second reportReady that will never come.
|
|
253
|
+
if (currentConnection) {
|
|
254
|
+
return Promise.resolve(currentConnection);
|
|
255
|
+
}
|
|
256
|
+
return new Promise((resolve, reject) => {
|
|
257
|
+
const entry = { resolve, reject };
|
|
258
|
+
connectionWaiters.push(entry);
|
|
259
|
+
signal?.addEventListener(
|
|
260
|
+
'abort',
|
|
261
|
+
() => {
|
|
262
|
+
const idx = connectionWaiters.indexOf(entry);
|
|
263
|
+
if (idx !== -1) connectionWaiters.splice(idx, 1);
|
|
264
|
+
reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));
|
|
265
|
+
},
|
|
266
|
+
{ once: true },
|
|
267
|
+
);
|
|
268
|
+
});
|
|
269
|
+
},
|
|
270
|
+
on: (event, listener) => emitter.on(event, listener),
|
|
271
|
+
off: (event, listener) => emitter.off(event, listener),
|
|
272
|
+
dispose: () => {
|
|
273
|
+
bridgeLogger.debug('disposing bridge');
|
|
274
|
+
for (const { reject } of connectionWaiters.splice(0)) {
|
|
275
|
+
reject(new Error('Bridge disposed'));
|
|
276
|
+
}
|
|
277
|
+
for (const client of wss.clients) client.terminate();
|
|
278
|
+
wss.close();
|
|
279
|
+
emitter.removeAllListeners();
|
|
280
|
+
},
|
|
237
281
|
};
|
|
238
282
|
};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import type { HarnessTestContext } from './test-context.js';
|
|
2
|
+
|
|
1
3
|
export type TestStatus = 'active' | 'skipped' | 'todo';
|
|
2
4
|
|
|
3
|
-
export type TestFn = () => void | Promise<void>;
|
|
5
|
+
export type TestFn = (context: HarnessTestContext) => void | Promise<void>;
|
|
6
|
+
|
|
7
|
+
export type SuiteHookFn = () => void | Promise<void>;
|
|
4
8
|
|
|
5
9
|
export type TestCase = {
|
|
6
10
|
name: string;
|
|
@@ -13,8 +17,8 @@ export type TestSuite = {
|
|
|
13
17
|
tests: TestCase[];
|
|
14
18
|
suites: TestSuite[];
|
|
15
19
|
parent?: TestSuite;
|
|
16
|
-
beforeAll:
|
|
17
|
-
afterAll:
|
|
20
|
+
beforeAll: SuiteHookFn[];
|
|
21
|
+
afterAll: SuiteHookFn[];
|
|
18
22
|
beforeEach: TestFn[];
|
|
19
23
|
afterEach: TestFn[];
|
|
20
24
|
status?: TestStatus;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type HarnessTaskContext = {
|
|
2
|
+
name: string;
|
|
3
|
+
type: 'test';
|
|
4
|
+
mode: 'run' | 'skip' | 'todo';
|
|
5
|
+
file: {
|
|
6
|
+
name: string;
|
|
7
|
+
};
|
|
8
|
+
suite: {
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type HarnessTestContext = {
|
|
14
|
+
task: HarnessTaskContext;
|
|
15
|
+
onTestFailed: (fn: () => void | Promise<void>) => void;
|
|
16
|
+
onTestFinished: (fn: () => void | Promise<void>) => void;
|
|
17
|
+
skip: {
|
|
18
|
+
(note?: string): never;
|
|
19
|
+
(condition: boolean, note?: string): void;
|
|
20
|
+
};
|
|
21
|
+
};
|
package/src/shared.ts
CHANGED
|
@@ -81,7 +81,13 @@ export type {
|
|
|
81
81
|
TestSuite,
|
|
82
82
|
TestCase,
|
|
83
83
|
CollectionResult,
|
|
84
|
+
TestFn,
|
|
85
|
+
SuiteHookFn,
|
|
84
86
|
} from './shared/test-collector.js';
|
|
87
|
+
export type {
|
|
88
|
+
HarnessTaskContext,
|
|
89
|
+
HarnessTestContext,
|
|
90
|
+
} from './shared/test-context.js';
|
|
85
91
|
export type {
|
|
86
92
|
TestRunnerEvents,
|
|
87
93
|
TestRunnerFileStartedEvent,
|
|
@@ -149,10 +155,7 @@ export type ScreenshotData = BinaryDataReference;
|
|
|
149
155
|
|
|
150
156
|
export type BridgeServerFunctions = {
|
|
151
157
|
reportReady: (device: DeviceDescriptor) => void;
|
|
152
|
-
emitEvent:
|
|
153
|
-
event: TEvent['type'],
|
|
154
|
-
data: TEvent
|
|
155
|
-
) => void;
|
|
158
|
+
emitEvent: (event: BridgeEvents['type'], data: BridgeEvents) => void;
|
|
156
159
|
'device.screenshot.receive': (
|
|
157
160
|
reference: BinaryDataReference,
|
|
158
161
|
metadata: { width: number; height: number }
|