react-native-web-serial-api 0.0.3 → 0.2.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.
Files changed (177) hide show
  1. package/README.md +198 -104
  2. package/TESTING.md +542 -0
  3. package/android/build.gradle +16 -2
  4. package/android/src/main/java/dev/webserialapi/NativeUsbSerialModule.java +74 -11
  5. package/android/src/main/java/dev/webserialapi/PortPickerActivity.java +61 -59
  6. package/bin/expose-serial.js +205 -0
  7. package/lib/commonjs/UsbSerial.js +58 -26
  8. package/lib/commonjs/UsbSerial.js.map +1 -1
  9. package/lib/commonjs/WebSerial.js +273 -77
  10. package/lib/commonjs/WebSerial.js.map +1 -1
  11. package/lib/commonjs/index.js +15 -3
  12. package/lib/commonjs/index.js.map +1 -1
  13. package/lib/commonjs/lib/dom-exception.js +176 -0
  14. package/lib/commonjs/lib/dom-exception.js.map +1 -0
  15. package/lib/commonjs/lib/event-target.js +140 -0
  16. package/lib/commonjs/lib/event-target.js.map +1 -0
  17. package/lib/commonjs/lib/promise.js +23 -0
  18. package/lib/commonjs/lib/promise.js.map +1 -0
  19. package/lib/commonjs/lib/web-streams.js +42 -0
  20. package/lib/commonjs/lib/web-streams.js.map +1 -0
  21. package/lib/commonjs/testing/device-fixture.js +70 -0
  22. package/lib/commonjs/testing/device-fixture.js.map +1 -0
  23. package/lib/commonjs/testing/expose.js +91 -0
  24. package/lib/commonjs/testing/expose.js.map +1 -0
  25. package/lib/commonjs/testing/harness.js +98 -0
  26. package/lib/commonjs/testing/harness.js.map +1 -0
  27. package/lib/commonjs/testing/in-memory-serial-transport.js +653 -0
  28. package/lib/commonjs/testing/in-memory-serial-transport.js.map +1 -0
  29. package/lib/commonjs/testing/index.js +153 -0
  30. package/lib/commonjs/testing/index.js.map +1 -0
  31. package/lib/commonjs/testing/install-in-memory-serial-transport.js +54 -0
  32. package/lib/commonjs/testing/install-in-memory-serial-transport.js.map +1 -0
  33. package/lib/commonjs/testing/serial-client.js +277 -0
  34. package/lib/commonjs/testing/serial-client.js.map +1 -0
  35. package/lib/commonjs/testing/simulated-device.js +164 -0
  36. package/lib/commonjs/testing/simulated-device.js.map +1 -0
  37. package/lib/commonjs/testing/test-suite.js +142 -0
  38. package/lib/commonjs/testing/test-suite.js.map +1 -0
  39. package/lib/commonjs/transport.js +61 -0
  40. package/lib/commonjs/transport.js.map +1 -0
  41. package/lib/commonjs/websocket/WebSocketSerialTransport.js +659 -0
  42. package/lib/commonjs/websocket/WebSocketSerialTransport.js.map +1 -0
  43. package/lib/commonjs/websocket/bridge.js +234 -0
  44. package/lib/commonjs/websocket/bridge.js.map +1 -0
  45. package/lib/commonjs/websocket/index.js +33 -0
  46. package/lib/commonjs/websocket/index.js.map +1 -0
  47. package/lib/commonjs/websocket/protocol.js +55 -0
  48. package/lib/commonjs/websocket/protocol.js.map +1 -0
  49. package/lib/commonjs/websocket/serial-device-bridge.js +130 -0
  50. package/lib/commonjs/websocket/serial-device-bridge.js.map +1 -0
  51. package/lib/typescript/src/UsbSerial.d.ts +24 -67
  52. package/lib/typescript/src/UsbSerial.d.ts.map +1 -1
  53. package/lib/typescript/src/WebSerial.d.ts +16 -7
  54. package/lib/typescript/src/WebSerial.d.ts.map +1 -1
  55. package/lib/typescript/src/index.d.ts +3 -1
  56. package/lib/typescript/src/index.d.ts.map +1 -1
  57. package/lib/typescript/src/lib/dom-exception.d.ts +100 -0
  58. package/lib/typescript/src/lib/dom-exception.d.ts.map +1 -0
  59. package/lib/typescript/src/lib/event-target.d.ts +55 -0
  60. package/lib/typescript/src/lib/event-target.d.ts.map +1 -0
  61. package/lib/typescript/src/lib/promise.d.ts +11 -0
  62. package/lib/typescript/src/lib/promise.d.ts.map +1 -0
  63. package/lib/typescript/src/lib/web-streams.d.ts +9 -0
  64. package/lib/typescript/src/lib/web-streams.d.ts.map +1 -0
  65. package/lib/typescript/src/testing/device-fixture.d.ts +70 -0
  66. package/lib/typescript/src/testing/device-fixture.d.ts.map +1 -0
  67. package/lib/typescript/src/testing/expose.d.ts +71 -0
  68. package/lib/typescript/src/testing/expose.d.ts.map +1 -0
  69. package/lib/typescript/src/testing/harness.d.ts +34 -0
  70. package/lib/typescript/src/testing/harness.d.ts.map +1 -0
  71. package/lib/typescript/src/testing/in-memory-serial-transport.d.ts +216 -0
  72. package/lib/typescript/src/testing/in-memory-serial-transport.d.ts.map +1 -0
  73. package/lib/typescript/src/testing/index.d.ts +33 -0
  74. package/lib/typescript/src/testing/index.d.ts.map +1 -0
  75. package/lib/typescript/src/testing/install-in-memory-serial-transport.d.ts +25 -0
  76. package/lib/typescript/src/testing/install-in-memory-serial-transport.d.ts.map +1 -0
  77. package/lib/typescript/src/testing/serial-client.d.ts +62 -0
  78. package/lib/typescript/src/testing/serial-client.d.ts.map +1 -0
  79. package/lib/typescript/src/testing/simulated-device.d.ts +127 -0
  80. package/lib/typescript/src/testing/simulated-device.d.ts.map +1 -0
  81. package/lib/typescript/src/testing/test-suite.d.ts +75 -0
  82. package/lib/typescript/src/testing/test-suite.d.ts.map +1 -0
  83. package/lib/typescript/src/transport.d.ts +131 -0
  84. package/lib/typescript/src/transport.d.ts.map +1 -0
  85. package/lib/typescript/src/websocket/WebSocketSerialTransport.d.ts +111 -0
  86. package/lib/typescript/src/websocket/WebSocketSerialTransport.d.ts.map +1 -0
  87. package/lib/typescript/src/websocket/bridge.d.ts +66 -0
  88. package/lib/typescript/src/websocket/bridge.d.ts.map +1 -0
  89. package/lib/typescript/src/websocket/index.d.ts +19 -0
  90. package/lib/typescript/src/websocket/index.d.ts.map +1 -0
  91. package/lib/typescript/src/websocket/protocol.d.ts +64 -0
  92. package/lib/typescript/src/websocket/protocol.d.ts.map +1 -0
  93. package/lib/typescript/src/websocket/serial-device-bridge.d.ts +32 -0
  94. package/lib/typescript/src/websocket/serial-device-bridge.d.ts.map +1 -0
  95. package/package.json +57 -3
  96. package/src/UsbSerial.ts +65 -90
  97. package/src/WebSerial.ts +351 -113
  98. package/src/index.ts +6 -8
  99. package/src/lib/dom-exception.ts +129 -60
  100. package/src/lib/event-target.ts +58 -21
  101. package/src/lib/promise.ts +7 -7
  102. package/src/lib/web-streams.ts +43 -0
  103. package/src/testing/device-fixture.ts +150 -0
  104. package/src/testing/expose.ts +147 -0
  105. package/src/testing/harness.ts +124 -0
  106. package/src/testing/in-memory-serial-transport.ts +840 -0
  107. package/src/testing/index.ts +90 -0
  108. package/src/testing/install-in-memory-serial-transport.ts +65 -0
  109. package/src/testing/serial-client.ts +313 -0
  110. package/src/testing/simulated-device.ts +193 -0
  111. package/src/testing/test-suite.ts +186 -0
  112. package/src/transport.ts +200 -0
  113. package/src/websocket/WebSocketSerialTransport.ts +796 -0
  114. package/src/websocket/bridge.ts +299 -0
  115. package/src/websocket/index.ts +38 -0
  116. package/src/websocket/protocol.ts +101 -0
  117. package/src/websocket/serial-device-bridge.ts +160 -0
  118. package/babel.config.js +0 -3
  119. package/biome.json +0 -35
  120. package/example/.watchmanconfig +0 -1
  121. package/example/App.tsx +0 -71
  122. package/example/__tests__/App.test.tsx +0 -16
  123. package/example/__tests__/connectEvents.test.tsx +0 -81
  124. package/example/__tests__/getPorts.test.tsx +0 -140
  125. package/example/android/app/build.gradle +0 -120
  126. package/example/android/app/debug.keystore +0 -0
  127. package/example/android/app/proguard-rules.pro +0 -10
  128. package/example/android/app/src/debug/AndroidManifest.xml +0 -9
  129. package/example/android/app/src/main/AndroidManifest.xml +0 -38
  130. package/example/android/app/src/main/java/dev/uzlopak/MainActivity.kt +0 -22
  131. package/example/android/app/src/main/java/dev/uzlopak/MainApplication.kt +0 -41
  132. package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +0 -37
  133. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  134. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  135. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  136. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  137. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  138. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  139. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  140. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  141. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  142. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  143. package/example/android/app/src/main/res/values/strings.xml +0 -3
  144. package/example/android/app/src/main/res/values/styles.xml +0 -9
  145. package/example/android/build.gradle +0 -22
  146. package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  147. package/example/android/gradle/wrapper/gradle-wrapper.properties +0 -7
  148. package/example/android/gradle.properties +0 -47
  149. package/example/android/gradlew +0 -252
  150. package/example/android/gradlew.bat +0 -94
  151. package/example/android/settings.gradle +0 -6
  152. package/example/app.json +0 -4
  153. package/example/babel.config.js +0 -21
  154. package/example/biome.json +0 -47
  155. package/example/deploy.sh +0 -11
  156. package/example/index.html +0 -26
  157. package/example/index.js +0 -9
  158. package/example/index.web.js +0 -8
  159. package/example/jest.config.js +0 -12
  160. package/example/metro.config.js +0 -58
  161. package/example/package-lock.json +0 -14510
  162. package/example/package.json +0 -48
  163. package/example/react-native.config.js +0 -17
  164. package/example/src/components/AppBar.tsx +0 -73
  165. package/example/src/components/Menu.tsx +0 -90
  166. package/example/src/components/SingleChoiceDialog.tsx +0 -120
  167. package/example/src/screens/ConnectScreen.tsx +0 -195
  168. package/example/src/screens/DevicesScreen.tsx +0 -252
  169. package/example/src/screens/TerminalScreen.tsx +0 -572
  170. package/example/src/settings.ts +0 -43
  171. package/example/src/theme.ts +0 -19
  172. package/example/src/util/TextUtil.ts +0 -129
  173. package/example/tsconfig.json +0 -10
  174. package/example/vite.config.mjs +0 -55
  175. package/scripts/deploy-release.sh +0 -127
  176. package/tsconfig.build.json +0 -7
  177. package/tsconfig.json +0 -20
@@ -0,0 +1,216 @@
1
+ /**
2
+ * InMemorySerialTransport — an in-memory {@link SerialTransport} for tests and
3
+ * on-device demos.
4
+ *
5
+ * It implements the exact same interface the production `UsbSerialModule` does,
6
+ * but talks to simulated devices instead of real USB hardware. Because it has
7
+ * **no `react-native` dependency**, the same instance drives:
8
+ *
9
+ * - Jest/Node unit tests and the conformance suite,
10
+ * - the example app's "virtual device" mode on a real Android device, and
11
+ * - the example app running in a browser (react-native-web).
12
+ *
13
+ * Inject it via `new Serial(transport)` or globally with `setUsbSerial(transport)`.
14
+ *
15
+ * @example
16
+ * const transport = new InMemorySerialTransport();
17
+ * transport.addDevice(new LoopbackDevice(), {hasPermission: true});
18
+ * const serial = new Serial(transport);
19
+ * const [port] = await serial.getPorts();
20
+ * await port.open({baudRate: 115200});
21
+ * // writes to port.writable now come back on port.readable (echo)
22
+ */
23
+ import type { ConnectEvent, ControlLine, DataEvent, ErrorEvent, FlowControl, OpenOptions, PortFilter, PortId, PortPickerLabels, SerialTransport, Subscription } from '../transport';
24
+ import type { SimulatedDevice } from './simulated-device';
25
+ /** Transport-side knobs when registering a {@link SimulatedDevice}. */
26
+ export type DeviceOptions = {
27
+ /** Whether the app already holds USB permission. Defaults to false. */
28
+ hasPermission?: boolean;
29
+ /** Defaults to 0. A USB device may expose several ports. */
30
+ portNumber?: number;
31
+ /** Override the device's serialNumber for enumeration. */
32
+ serialNumber?: string;
33
+ /**
34
+ * Cross-wire output signals onto inputs the way a null-modem/loopback plug
35
+ * would (DTR→DSR+DCD, RTS→CTS) so getSignals() reflects setSignals().
36
+ * Defaults to true.
37
+ */
38
+ loopbackSignals?: boolean;
39
+ /**
40
+ * When the port is opened with hardware (RTS/CTS) flow control, the device
41
+ * de-asserts CTS once this many bytes have been written without the receiver
42
+ * draining — modelling a full receive buffer. Defaults to 256.
43
+ */
44
+ flowControlThreshold?: number;
45
+ };
46
+ export type InMemorySerialTransportOptions = {
47
+ /** Devices to register on construction (same as calling addDevice). */
48
+ devices?: SimulatedDevice[];
49
+ /**
50
+ * Delay (ms) applied to async operations and to inbound data delivery.
51
+ * 0 (default) resolves on a microtask — deterministic for Jest. A small
52
+ * positive value makes streaming feel realistic on a device.
53
+ */
54
+ latencyMs?: number;
55
+ /**
56
+ * Whether showPortPicker() grants USB permission to the chosen device,
57
+ * mirroring the real Android picker. Defaults to true.
58
+ */
59
+ autoGrantPermission?: boolean;
60
+ /**
61
+ * If set, inbound data larger than this is delivered as several `onData`
62
+ * events of at most this many bytes — modelling how a real serial port hands
63
+ * data up in chunks. 0/undefined delivers each write's reply in one event.
64
+ */
65
+ chunkSize?: number;
66
+ };
67
+ /** Operations whose next invocation can be made to fail (error injection). */
68
+ export type FailableOp = 'open' | 'close' | 'write' | 'startReading' | 'stopReading' | 'setSignals' | 'getSignals';
69
+ type OutputSignals = {
70
+ dtr: boolean;
71
+ rts: boolean;
72
+ brk: boolean;
73
+ };
74
+ type InputSignals = {
75
+ dcd: boolean;
76
+ cts: boolean;
77
+ ri: boolean;
78
+ dsr: boolean;
79
+ };
80
+ /**
81
+ * A simulated USB-serial device. Returned by {@link InMemorySerialTransport.addDevice}.
82
+ * The mutable fields and the helper methods let a test or demo drive the device
83
+ * the way physical hardware (and a human plugging cables) otherwise would.
84
+ */
85
+ export declare class DeviceHandle {
86
+ #private;
87
+ readonly usbVendorId: number;
88
+ readonly usbProductId: number;
89
+ readonly portNumber: number;
90
+ serialNumber: string;
91
+ /** Reassigned on every (re)attach, mirroring Android's behaviour. */
92
+ deviceId: number;
93
+ attached: boolean;
94
+ hasPermission: boolean;
95
+ isOpen: boolean;
96
+ reading: boolean;
97
+ /** The hosted behaviour. */
98
+ readonly simulatedDevice: SimulatedDevice;
99
+ loopbackSignals: boolean;
100
+ flowControl: FlowControl;
101
+ flowControlThreshold: number;
102
+ openOptions: Required<OpenOptions> | null;
103
+ /**
104
+ * When non-null, the device delivers at most this many bytes before raising a
105
+ * `BufferOverrunError` — models a fixed-size receive buffer overflowing.
106
+ */
107
+ overrunLimit: number | null;
108
+ _rxDelivered: number;
109
+ _overran: boolean;
110
+ _hwWritten: number;
111
+ readonly output: OutputSignals;
112
+ readonly input: InputSignals;
113
+ /** Every byte frame the host has written to this device, in order. */
114
+ readonly written: number[][];
115
+ constructor(transport: InMemorySerialTransport, deviceId: number, device: SimulatedDevice, options: DeviceOptions);
116
+ /** Push inbound bytes to the host as if the device sent them unprompted. */
117
+ push(bytes: number[] | Uint8Array): void;
118
+ /**
119
+ * Raise a read error on the host's readable stream. `name` is the W3C error
120
+ * type (e.g. "BreakError", "BufferOverrunError"); the current polyfill ignores
121
+ * it and surfaces "NetworkError" regardless (a documented spec gap).
122
+ */
123
+ emitError(message: string, name?: string): void;
124
+ /**
125
+ * Make the device deliver at most `bytes` bytes and then raise a
126
+ * `BufferOverrunError`, modelling a receive buffer of that size overflowing.
127
+ */
128
+ overrunAfter(bytes: number): this;
129
+ /** Make the next call to `op` reject once (error injection). */
130
+ failNext(op: FailableOp): this;
131
+ /** @internal consume a queued failure for `op`. */
132
+ _consumeFail(op: FailableOp): boolean;
133
+ /** Directly set device-asserted input signals (DCD/CTS/RI/DSR). */
134
+ setInputSignals(signals: Partial<InputSignals>): void;
135
+ /** Physically attach (or re-attach) this device — fires "connect". */
136
+ attach(): void;
137
+ /** Physically detach this device — fires "disconnect". */
138
+ detach(): void;
139
+ /** Simulate an unplug while open: errors the open stream, then disconnects. */
140
+ loseDevice(): void;
141
+ /**
142
+ * Resolve when the host opens this port (immediately if already open). Lets a
143
+ * test `await` the app connecting before driving the device.
144
+ */
145
+ whenOpened(): Promise<Required<OpenOptions>>;
146
+ /** Resolve when the host closes this port (immediately if not open). */
147
+ whenClosed(): Promise<void>;
148
+ /** @internal The transport calls this right after the port opens. */
149
+ _notifyOpen(options: Required<OpenOptions>): void;
150
+ /** @internal The transport calls this right after the port closes/detaches. */
151
+ _notifyClose(): void;
152
+ }
153
+ type Listener<E> = (event: E) => void;
154
+ /**
155
+ * In-memory transport backing one or more {@link DeviceHandle}s.
156
+ */
157
+ export declare class InMemorySerialTransport implements SerialTransport {
158
+ #private;
159
+ constructor(options?: InMemorySerialTransportOptions);
160
+ /** All devices known to the transport (attached or not). */
161
+ get devices(): readonly DeviceHandle[];
162
+ /**
163
+ * Register a {@link SimulatedDevice}. It starts attached but does not fire
164
+ * "connect"; its identity (usbVendorId/usbProductId/serialNumber) is read from
165
+ * the device, and `options` carries the transport-side knobs.
166
+ */
167
+ addDevice(simulatedDevice: SimulatedDevice, options?: DeviceOptions): DeviceHandle;
168
+ /** Remove a device entirely; detaches it first if attached. */
169
+ removeDevice(device: DeviceHandle): void;
170
+ /** (Re)attach a device, assigning it a fresh deviceId, and fire "connect". */
171
+ attach(device: DeviceHandle): void;
172
+ /** Detach a device and fire "disconnect"; any open port becomes closed. */
173
+ detach(device: DeviceHandle, lost?: boolean): void;
174
+ /** Simulate an unplug while open: error the stream first, then disconnect. */
175
+ loseDevice(device: DeviceHandle): void;
176
+ /** Script the next showPortPicker() resolution (a device or a predicate). */
177
+ selectNextPort(target: DeviceHandle | ((d: DeviceHandle) => boolean)): void;
178
+ /** Make the next showPortPicker() reject (user cancelled / no port). */
179
+ rejectNextPortPicker(): void;
180
+ /** @internal deliver inbound bytes to the host's readable stream. */
181
+ _deliver(device: DeviceHandle, data: number[]): void;
182
+ /** @internal raise a read error for a device's open port. */
183
+ _error(device: DeviceHandle, message: string, name?: string): void;
184
+ findAllDrivers(): Promise<ReadonlyArray<PortId>>;
185
+ showPortPicker(filter: ReadonlyArray<PortFilter>, _labels?: PortPickerLabels): Promise<PortId>;
186
+ requestPermission(deviceId: number): Promise<boolean>;
187
+ open(deviceId: number, portNumber: number, options: OpenOptions): Promise<void>;
188
+ close(deviceId: number, portNumber: number): Promise<void>;
189
+ isOpen(deviceId: number, portNumber: number): boolean;
190
+ write(deviceId: number, portNumber: number, data: number[], _timeout?: number): Promise<void>;
191
+ startReading(deviceId: number, portNumber: number): Promise<void>;
192
+ stopReading(deviceId: number, portNumber: number): Promise<void>;
193
+ setParameters(deviceId: number, portNumber: number, options: OpenOptions): Promise<void>;
194
+ setDTR(deviceId: number, portNumber: number, value: boolean): Promise<void>;
195
+ setRTS(deviceId: number, portNumber: number, value: boolean): Promise<void>;
196
+ setBreak(deviceId: number, portNumber: number, value: boolean): Promise<void>;
197
+ getDTR(deviceId: number, portNumber: number): Promise<boolean>;
198
+ getRTS(deviceId: number, portNumber: number): Promise<boolean>;
199
+ getCD(deviceId: number, portNumber: number): Promise<boolean>;
200
+ getCTS(deviceId: number, portNumber: number): Promise<boolean>;
201
+ getRI(deviceId: number, portNumber: number): Promise<boolean>;
202
+ getDSR(deviceId: number, portNumber: number): Promise<boolean>;
203
+ getControlLines(deviceId: number, portNumber: number): Promise<ControlLine[]>;
204
+ getSupportedControlLines(_deviceId: number, _portNumber: number): Promise<ControlLine[]>;
205
+ setFlowControl(deviceId: number, portNumber: number, flowControl: FlowControl): Promise<void>;
206
+ getFlowControl(deviceId: number, portNumber: number): Promise<FlowControl>;
207
+ getSupportedFlowControl(_deviceId: number, _portNumber: number): Promise<FlowControl[]>;
208
+ purgeHwBuffers(_deviceId: number, _portNumber: number, _purgeWriteBuffers: boolean, _purgeReadBuffers: boolean): Promise<void>;
209
+ getSerial(deviceId: number, portNumber: number): Promise<string>;
210
+ onData(listener: Listener<DataEvent>): Subscription;
211
+ onError(listener: Listener<ErrorEvent>): Subscription;
212
+ onConnect(listener: Listener<ConnectEvent>): Subscription;
213
+ onDisconnect(listener: Listener<ConnectEvent>): Subscription;
214
+ }
215
+ export {};
216
+ //# sourceMappingURL=in-memory-serial-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory-serial-transport.d.ts","sourceRoot":"","sources":["../../../../src/testing/in-memory-serial-transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Testing & on-device-demo entry point.
3
+ *
4
+ * Imported via the package subpath:
5
+ *
6
+ * import {InMemorySerialTransport, installInMemorySerialTransport}
7
+ * from 'react-native-web-serial-api/testing';
8
+ *
9
+ * These are reusable building blocks for *consumers'* tests and demos — an
10
+ * in-memory transport and an authorable `SimulatedDevice` peripheral model — so
11
+ * they ship with the package. The library's own spec-compliance suite is NOT
12
+ * here: it lives in `src/__tests__/conformance-suite.ts` (test-only, excluded
13
+ * from the build and the published package). None of this is in the main bundle.
14
+ */
15
+ export type { SerialTransport } from '../transport';
16
+ export { getUsbSerial, resetUsbSerial, setUsbSerial } from '../UsbSerial';
17
+ export type { DeviceFixtureOptions, MountedDeviceFixture, MountedDeviceFixtures, } from './device-fixture';
18
+ export { createDeviceFixture } from './device-fixture';
19
+ export type { ExposedDevice, ExposeSimulatedDeviceOptions, WebSocketServerCtor, WebSocketServerLike, } from './expose';
20
+ export { exposeSimulatedDevice } from './expose';
21
+ export type { ByteReader, RejectionExpectation } from './harness';
22
+ export { assert, assertEqual, assertRejects, bytesEqual, errorMessage, readBytes, withTimeout, } from './harness';
23
+ export type { DeviceOptions, FailableOp, InMemorySerialTransportOptions, } from './in-memory-serial-transport';
24
+ export { DeviceHandle, InMemorySerialTransport, } from './in-memory-serial-transport';
25
+ export type { InstallInMemorySerialTransportOptions, SimulatedTransportDevice, } from './install-in-memory-serial-transport';
26
+ export { installInMemorySerialTransport } from './install-in-memory-serial-transport';
27
+ export type { ReadOptions, SerialClientOptions } from './serial-client';
28
+ export { createSerialClient, SerialClient, } from './serial-client';
29
+ export type { HostSignals, SerialInputSignals, SimulatedDeviceHost, SimulatedDeviceIdentity, SimulatedDeviceOpenOptions, } from './simulated-device';
30
+ export { LineBufferedDevice, LoopbackDevice, SimulatedDevice, SinkDevice, } from './simulated-device';
31
+ export type { RunTestSuiteOptions, SerialTest, SerialTestProgress, SerialTestResult, TestClient, } from './test-suite';
32
+ export { compareTestResults, runTestSuite } from './test-suite';
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,YAAY,EAAC,eAAe,EAAC,MAAM,cAAc,CAAC;AAGlD,OAAO,EAAC,YAAY,EAAE,cAAc,EAAE,YAAY,EAAC,MAAM,cAAc,CAAC;AAExE,YAAY,EACV,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAC,mBAAmB,EAAC,MAAM,kBAAkB,CAAC;AAGrD,YAAY,EACV,aAAa,EACb,4BAA4B,EAC5B,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAC,qBAAqB,EAAC,MAAM,UAAU,CAAC;AAE/C,YAAY,EAAC,UAAU,EAAE,oBAAoB,EAAC,MAAM,WAAW,CAAC;AAChE,OAAO,EACL,MAAM,EACN,WAAW,EACX,aAAa,EACb,UAAU,EACV,YAAY,EACZ,SAAS,EACT,WAAW,GACZ,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,aAAa,EACb,UAAU,EACV,8BAA8B,GAC/B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,YAAY,EACZ,uBAAuB,GACxB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,qCAAqC,EACrC,wBAAwB,GACzB,MAAM,sCAAsC,CAAC;AAE9C,OAAO,EAAC,8BAA8B,EAAC,MAAM,sCAAsC,CAAC;AAEpF,YAAY,EAAC,WAAW,EAAE,mBAAmB,EAAC,MAAM,iBAAiB,CAAC;AACtE,OAAO,EACL,kBAAkB,EAClB,YAAY,GACb,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,UAAU,GACX,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EACV,mBAAmB,EACnB,UAAU,EACV,kBAAkB,EAClB,gBAAgB,EAChB,UAAU,GACX,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,kBAAkB,EAAE,YAAY,EAAC,MAAM,cAAc,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { DeviceOptions, InMemorySerialTransportOptions } from './in-memory-serial-transport';
2
+ import { InMemorySerialTransport } from './in-memory-serial-transport';
3
+ import { SimulatedDevice } from './simulated-device';
4
+ /** A device to register: a SimulatedDevice, optionally with transport options. */
5
+ export type SimulatedTransportDevice = SimulatedDevice | {
6
+ device: SimulatedDevice;
7
+ options?: DeviceOptions;
8
+ };
9
+ export type InstallInMemorySerialTransportOptions = {
10
+ /** The simulated devices to expose. */
11
+ devices: SimulatedTransportDevice[];
12
+ /** When false, no mock is installed and `null` is returned. Defaults to true. */
13
+ enabled?: boolean;
14
+ /** Transport-level options (latency, chunkSize, …). */
15
+ transport?: InMemorySerialTransportOptions;
16
+ };
17
+ /**
18
+ * Build an {@link InMemorySerialTransport} from `devices` and install it globally
19
+ * via {@link setUsbSerial}. SimulatedDevices default to granted USB permission (so
20
+ * they show up immediately); pass the `{device, options}` form to override.
21
+ * Returns the transport (handy for driving devices in-test), or `null` when
22
+ * disabled.
23
+ */
24
+ export declare function installInMemorySerialTransport(options: InstallInMemorySerialTransportOptions): InMemorySerialTransport | null;
25
+ //# sourceMappingURL=install-in-memory-serial-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-in-memory-serial-transport.d.ts","sourceRoot":"","sources":["../../../../src/testing/install-in-memory-serial-transport.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,aAAa,EACb,8BAA8B,EAC/B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAC,uBAAuB,EAAC,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAC,eAAe,EAAC,MAAM,oBAAoB,CAAC;AAEnD,kFAAkF;AAClF,MAAM,MAAM,wBAAwB,GAChC,eAAe,GACf;IAAC,MAAM,EAAE,eAAe,CAAC;IAAC,OAAO,CAAC,EAAE,aAAa,CAAA;CAAC,CAAC;AAEvD,MAAM,MAAM,qCAAqC,GAAG;IAClD,uCAAuC;IACvC,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,iFAAiF;IACjF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uDAAuD;IACvD,SAAS,CAAC,EAAE,8BAA8B,CAAC;CAC5C,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,qCAAqC,GAC7C,uBAAuB,GAAG,IAAI,CAiBhC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * A fluent request/response client over a {@link SerialPort} — the host-side
3
+ * driver every serial test otherwise hand-rolls (reader + writer, a pending
4
+ * byte buffer, framed reads with timeouts). Works on ANY SerialPort: the
5
+ * in-memory {@link InMemorySerialTransport}, a real USB device, or a
6
+ * WebSocket-backed one — so the same test runs in Jest and on a device.
7
+ *
8
+ * @example
9
+ * const {client} = await mountDeviceFixture(new MyDevice());
10
+ * await client.open({baudRate: 115200});
11
+ * await client.write('PING\n');
12
+ * expect(await client.readLine()).toBe('PONG');
13
+ * await client.close();
14
+ */
15
+ import type { SerialOptions, SerialPort } from '../WebSerial';
16
+ export type ReadOptions = {
17
+ /** Per-call timeout in ms (overrides the client default). */
18
+ timeout?: number;
19
+ };
20
+ export type SerialClientOptions = {
21
+ /** Default per-read timeout in ms. Default 2000. */
22
+ defaultTimeoutMs?: number;
23
+ };
24
+ export declare class SerialClient {
25
+ #private;
26
+ constructor(port: SerialPort, options?: SerialClientOptions);
27
+ get port(): SerialPort;
28
+ get isOpen(): boolean;
29
+ /** True once the underlying stream has ended (device closed or lost). */
30
+ get ended(): boolean;
31
+ /** Open the port (idempotent) and acquire reader + writer; start reading. */
32
+ open(options?: SerialOptions): Promise<this>;
33
+ /** Write bytes (string → char codes & 0xff). */
34
+ write(data: number[] | Uint8Array | string): Promise<void>;
35
+ /** Read exactly `n` bytes (fewer only if the stream ends first). */
36
+ readBytes(n: number, options?: ReadOptions): Promise<Uint8Array>;
37
+ /** Read up to and including `delimiter`; returns the slice (incl. delimiter). */
38
+ readUntil(delimiter: number[] | Uint8Array | string, options?: ReadOptions): Promise<Uint8Array>;
39
+ /** Read one `\n`-terminated line as text (trailing CR/LF stripped). */
40
+ readLine(options?: ReadOptions): Promise<string>;
41
+ /**
42
+ * Frame a message: `predicate(buffer)` returns the number of leading bytes the
43
+ * next frame occupies (consumed and returned), or `false` to wait for more.
44
+ */
45
+ readMatching(predicate: (buffer: Uint8Array) => number | false, options?: ReadOptions): Promise<Uint8Array>;
46
+ /**
47
+ * Resolve with the next chunk of buffered inbound bytes (one or more), or an
48
+ * empty array once the stream has ended. Rejects on timeout. Use this to layer
49
+ * your own framing/decoder (SLIP, COBS, length-prefix, …) on the byte stream;
50
+ * pair it with {@link ended} to stop when the device goes away.
51
+ */
52
+ readAvailable(options?: ReadOptions): Promise<Uint8Array>;
53
+ /** Assert no inbound bytes arrive for `ms`. Rejects if any do. */
54
+ expectIdle(ms: number): Promise<void>;
55
+ /** Drop any buffered-but-unread inbound bytes. */
56
+ drain(): void;
57
+ /** Release reader + writer locks and close the port. Safe to call twice. */
58
+ close(): Promise<void>;
59
+ }
60
+ /** Create a {@link SerialClient} for any SerialPort (virtual, real, or WS). */
61
+ export declare function createSerialClient(port: SerialPort, options?: SerialClientOptions): SerialClient;
62
+ //# sourceMappingURL=serial-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serial-client.d.ts","sourceRoot":"","sources":["../../../../src/testing/serial-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAC,aAAa,EAAE,UAAU,EAAC,MAAM,cAAc,CAAC;AAG5D,MAAM,MAAM,WAAW,GAAG;IACxB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,oDAAoD;IACpD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAaF,qBAAa,YAAY;;gBAcX,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,mBAAwB;IAK/D,IAAI,IAAI,IAAI,UAAU,CAErB;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,yEAAyE;IACzE,IAAI,KAAK,IAAI,OAAO,CAEnB;IAED,6EAA6E;IACvE,IAAI,CAAC,OAAO,GAAE,aAAkC,GAAG,OAAO,CAAC,IAAI,CAAC;IAiGtE,gDAAgD;IAC1C,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhE,oEAAoE;IACpE,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IASpE,iFAAiF;IACjF,SAAS,CACP,SAAS,EAAE,MAAM,EAAE,GAAG,UAAU,GAAG,MAAM,EACzC,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,CAAC;IAatB,uEAAuE;IACjE,QAAQ,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAQ1D;;;OAGG;IACH,YAAY,CACV,SAAS,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,MAAM,GAAG,KAAK,EACjD,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,CAAC;IAStB;;;;;OAKG;IACH,aAAa,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IAS7D,kEAAkE;IAC5D,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB3C,kDAAkD;IAClD,KAAK,IAAI,IAAI;IAIb,4EAA4E;IACtE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAqC7B;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,UAAU,EAChB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,YAAY,CAEd"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * SimulatedDevice — author a complete simulated serial peripheral.
3
+ *
4
+ * Extend the class and override the lifecycle hooks to model a real device's
5
+ * whole behaviour (firmware/protocol): react to `open()`, to bytes the host
6
+ * writes, to control-signal changes, and stream data back over time. It is
7
+ * hosted by an {@link InMemorySerialTransport} ({@link ./virtual-serial}) and is
8
+ * free of any `react-native` dependency, so the same device runs under Jest,
9
+ * in a browser, and inside a React Native app on a device/emulator (for E2E).
10
+ *
11
+ * @example
12
+ * class Thermometer extends SimulatedDevice {
13
+ * usbVendorId = 0x0403;
14
+ * usbProductId = 0x6001;
15
+ * #timer?: ReturnType<typeof setInterval>;
16
+ * onOpen() {
17
+ * this.#timer = setInterval(() => this.send(`${20 + Math.random()}C\r\n`), 1000);
18
+ * }
19
+ * onData(bytes) { // host wrote a command
20
+ * if (String.fromCharCode(...bytes).trim() === 'ID?') this.send('ACME-TEMP\r\n');
21
+ * }
22
+ * onClose() { clearInterval(this.#timer); }
23
+ * }
24
+ * transport.addDevice(new Thermometer(), {hasPermission: true});
25
+ */
26
+ import type { OpenOptions } from '../transport';
27
+ /** The negotiated connection parameters (native parity code: 0 none/1 odd/2 even). */
28
+ export type SimulatedDeviceOpenOptions = Required<OpenOptions>;
29
+ /** Device-asserted input signals — what the host reads via getSignals(). */
30
+ export type SerialInputSignals = {
31
+ dataCarrierDetect?: boolean;
32
+ clearToSend?: boolean;
33
+ ringIndicator?: boolean;
34
+ dataSetReady?: boolean;
35
+ };
36
+ /** Host-asserted output signals (DTR/RTS/break) the device observes. */
37
+ export type HostSignals = {
38
+ dataTerminalReady: boolean;
39
+ requestToSend: boolean;
40
+ break: boolean;
41
+ };
42
+ /**
43
+ * The handle a {@link SimulatedDevice} uses to talk back to the host. Provided by
44
+ * the transport; you normally use the `protected` helpers on `SimulatedDevice`
45
+ * rather than this directly.
46
+ */
47
+ export interface SimulatedDeviceHost {
48
+ readonly deviceId: number;
49
+ readonly portNumber: number;
50
+ readonly isOpen: boolean;
51
+ readonly openOptions: SimulatedDeviceOpenOptions | null;
52
+ send(bytes: number[]): void;
53
+ raiseError(message: string, name?: string): void;
54
+ setSignals(signals: SerialInputSignals): void;
55
+ }
56
+ /** Coerce a payload into a byte array (string → char codes, & 0xff). */
57
+ export declare function toBytes(data: number[] | Uint8Array | string): number[];
58
+ /**
59
+ * Base class for a simulated serial peripheral. Override the `on*` hooks you
60
+ * care about (all default to no-ops and may be async) and use the `protected`
61
+ * helpers to drive the host.
62
+ */
63
+ export declare abstract class SimulatedDevice {
64
+ #private;
65
+ /** USB Vendor ID this device reports for enumeration. */
66
+ abstract readonly usbVendorId: number;
67
+ /** USB Product ID this device reports for enumeration. */
68
+ abstract readonly usbProductId: number;
69
+ /** Optional USB serial number. */
70
+ readonly serialNumber?: string;
71
+ /** @internal Bind the transport host (called by InMemorySerialTransport). */
72
+ _bind(host: SimulatedDeviceHost): void;
73
+ /** Send bytes to the host (they appear on `port.readable`). */
74
+ protected send(data: number[] | Uint8Array | string): void;
75
+ /**
76
+ * Raise a typed read error on the host's readable stream. `name` is the W3C
77
+ * error type, e.g. "BreakError", "BufferOverrunError", "FramingError",
78
+ * "ParityError" (defaults to "NetworkError").
79
+ */
80
+ protected raiseError(message: string, name?: string): void;
81
+ /** Set device-asserted input signals (DCD/CTS/RI/DSR) the host can read. */
82
+ protected setSignals(signals: SerialInputSignals): void;
83
+ /** The parameters the host opened the port with, or null when closed. */
84
+ protected get openOptions(): SimulatedDeviceOpenOptions | null;
85
+ protected get deviceId(): number;
86
+ protected get portNumber(): number;
87
+ /** The host opened the port. */
88
+ onOpen(_options: SimulatedDeviceOpenOptions): void | Promise<void>;
89
+ /** The host wrote bytes to the device. */
90
+ onData(_data: Uint8Array): void | Promise<void>;
91
+ /** The host changed DTR/RTS/break. */
92
+ onHostSignals(_signals: HostSignals): void | Promise<void>;
93
+ /** The host closed the port. */
94
+ onClose(): void | Promise<void>;
95
+ }
96
+ /** Optional USB identity for built-in devices. */
97
+ export type SimulatedDeviceIdentity = {
98
+ usbVendorId?: number;
99
+ usbProductId?: number;
100
+ serialNumber?: string;
101
+ };
102
+ /** A loopback device: every byte written is echoed straight back. */
103
+ export declare class LoopbackDevice extends SimulatedDevice {
104
+ readonly usbVendorId: number;
105
+ readonly usbProductId: number;
106
+ readonly serialNumber?: string;
107
+ constructor(identity?: SimulatedDeviceIdentity);
108
+ onData(data: Uint8Array): void;
109
+ }
110
+ /**
111
+ * Base for a line-oriented command/response device: buffers incoming bytes and
112
+ * calls {@link onLine} for each `\n`-terminated line (trailing CR/LF stripped).
113
+ */
114
+ export declare abstract class LineBufferedDevice extends SimulatedDevice {
115
+ #private;
116
+ /** Handle one line received from the host. */
117
+ abstract onLine(line: string): void | Promise<void>;
118
+ onData(data: Uint8Array): void;
119
+ }
120
+ /** A device that accepts writes but never sends anything back. */
121
+ export declare class SinkDevice extends SimulatedDevice {
122
+ readonly usbVendorId: number;
123
+ readonly usbProductId: number;
124
+ readonly serialNumber?: string;
125
+ constructor(identity?: SimulatedDeviceIdentity);
126
+ }
127
+ //# sourceMappingURL=simulated-device.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simulated-device.d.ts","sourceRoot":"","sources":["../../../../src/testing/simulated-device.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * A runtime-agnostic suite runner for serial device tests. Write a set of
3
+ * `SerialTest`s once and run them anywhere a {@link SerialPort} exists:
4
+ *
5
+ * - in Jest against a {@link DeviceHandle} simulator (in-memory),
6
+ * - on a device / emulator over a real or WebSocket-backed port, and
7
+ * - against BOTH, then {@link compareTestResults} to prove the device matches
8
+ * the simulator case-for-case.
9
+ *
10
+ * Each test receives an opened, host-side {@link SerialClient}; by default one
11
+ * client is shared across the whole suite (open once, close at the end), which
12
+ * matches how a request/response protocol session behaves. Pass `shared: false`
13
+ * to open and close a fresh client per test. The runner never throws — it
14
+ * collects a {@link SerialTestResult} per case — so it is safe to drive an
15
+ * on-device Self-Test screen.
16
+ *
17
+ * @example One suite, two transports, compared
18
+ * const sim = await runTestSuite(myTests, await virtualPort());
19
+ * const dev = await runTestSuite(myTests, realPort);
20
+ * const rows = compareTestResults(sim, dev); // a row passes when both agree
21
+ */
22
+ import type { SerialOptions, SerialPort } from '../WebSerial';
23
+ import { SerialClient } from './serial-client';
24
+ export type SerialTestResult = {
25
+ name: string;
26
+ passed: boolean;
27
+ error?: string;
28
+ durationMs: number;
29
+ };
30
+ /** One test case. `run` receives an already-opened client (typically per the
31
+ * suite's {@link TestClient}; `SerialClient` by default). */
32
+ export type SerialTest<C = SerialClient> = {
33
+ name: string;
34
+ run(client: C): Promise<void>;
35
+ };
36
+ /** Progress hooks so a UI can render results live as each test completes. */
37
+ export type SerialTestProgress = {
38
+ /** Called just before a test starts running. */
39
+ onStart?: (name: string, index: number, total: number) => void;
40
+ /** Called after each test completes (pass or fail). */
41
+ onResult?: (result: SerialTestResult) => void;
42
+ };
43
+ /**
44
+ * How to build (and tear down) the per-suite client from a port. Provide this to
45
+ * run a higher-level protocol client (e.g. an HCI / NMEA framer built on a
46
+ * {@link SerialClient}) instead of the raw client. `connect` must also open the
47
+ * port; `disconnect` must release it (a `SerialClient.close()` does both).
48
+ */
49
+ export type TestClient<C> = {
50
+ connect(port: SerialPort): Promise<C>;
51
+ disconnect(client: C): Promise<void>;
52
+ };
53
+ export type RunTestSuiteOptions<C = SerialClient> = {
54
+ /** `SerialOptions` for the default client's `open()`. Default {baudRate: 115200}. */
55
+ open?: SerialOptions;
56
+ /** Open/close a fresh client per test instead of sharing one. Default false. */
57
+ shared?: boolean;
58
+ /** Build a protocol client over the port (defaults to an opened SerialClient). */
59
+ client?: TestClient<C>;
60
+ progress?: SerialTestProgress;
61
+ };
62
+ /**
63
+ * Run `tests` against an already-acquired `port`, collecting one result per
64
+ * case. With the default (shared) client the port is opened once and closed at
65
+ * the end; with `shared: false` each test gets a fresh open/close. Never throws.
66
+ */
67
+ export declare function runTestSuite<C = SerialClient>(tests: SerialTest<C>[], port: SerialPort, options?: RunTestSuiteOptions<C>): Promise<SerialTestResult[]>;
68
+ /**
69
+ * Compare two runs of the same suite case-by-case. A row passes when both runs
70
+ * agree (both passed or both failed), so the `candidate` is judged equivalent to
71
+ * the `reference` rather than judged on its own. Cases the candidate produced
72
+ * that the reference never ran are surfaced as failing `candidate: …` rows.
73
+ */
74
+ export declare function compareTestResults(reference: SerialTestResult[], candidate: SerialTestResult[]): SerialTestResult[];
75
+ //# sourceMappingURL=test-suite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-suite.d.ts","sourceRoot":"","sources":["../../../../src/testing/test-suite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAE,UAAU,EAAC,MAAM,cAAc,CAAC;AAE5D,OAAO,EAAC,YAAY,EAAC,MAAM,iBAAiB,CAAC;AAE7C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;6DAC6D;AAC7D,MAAM,MAAM,UAAU,CAAC,CAAC,GAAG,YAAY,IAAI;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B,CAAC;AAEF,6EAA6E;AAC7E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,gDAAgD;IAChD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/D,uDAAuD;IACvD,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC/C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI;IAC1B,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACtC,UAAU,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,mBAAmB,CAAC,CAAC,GAAG,YAAY,IAAI;IAClD,qFAAqF;IACrF,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,gFAAgF;IAChF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,kFAAkF;IAClF,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACvB,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B,CAAC;AAaF;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,CAAC,GAAG,YAAY,EACjD,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,EACtB,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,mBAAmB,CAAC,CAAC,CAAM,GACnC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAyD7B;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,SAAS,EAAE,gBAAgB,EAAE,GAC5B,gBAAgB,EAAE,CA4BpB"}