1- import { BirpcReturn , createBirpc } from 'birpc' ;
2- import type { BridgeClientFunctions , BridgeServerFunctions } from './shared.js' ;
1+ import { createBirpc } from 'birpc' ;
32import { deserialize , serialize } from './serializer.js' ;
4- import { createBinaryFrame } from './binary-transfer.js' ;
3+ import { createBinaryFrame , generateTransferId } from './binary-transfer.js' ;
4+ import type {
5+ BridgeClientFunctions ,
6+ BridgeServerFunctions ,
7+ DeviceDescriptor ,
8+ BridgeEvents ,
9+ FileReference ,
10+ ImageSnapshotOptions ,
11+ TestExecutionOptions ,
12+ TestSuiteResult ,
13+ } from './shared.js' ;
514
6- export type BridgeClient = {
7- rpc : BirpcReturn < BridgeServerFunctions , BridgeClientFunctions > ;
15+ // ---------------------------------------------------------------------------
16+ // Public types
17+ // ---------------------------------------------------------------------------
18+
19+ /** Handlers the app must implement for the CLI to call into. */
20+ export type HarnessCallbacks = {
21+ runTests : ( path : string , options : TestExecutionOptions ) => Promise < TestSuiteResult > ;
22+ } ;
23+
24+ /** The app-side handle returned by connectToHarness. */
25+ export type HarnessHandle = {
26+ /** Call once when the app is initialised and ready to run tests. */
27+ reportReady : ( device : DeviceDescriptor ) => void ;
28+ /** Forward a test or bundler event to the CLI. */
29+ emitEvent : ( event : BridgeEvents ) => void ;
30+ /** Send a screenshot to the CLI and receive a file reference for snapshot comparison. */
31+ transferScreenshot : (
32+ data : Uint8Array ,
33+ metadata : { width : number ; height : number } ,
34+ ) => Promise < FileReference > ;
35+ /** Request an image snapshot comparison on the CLI. */
36+ matchImageSnapshot : (
37+ screenshot : FileReference ,
38+ testPath : string ,
39+ options : ImageSnapshotOptions ,
40+ runner : string ,
41+ ) => Promise < { pass : boolean ; message : string } > ;
842 disconnect : ( ) => void ;
9- sendBinary : ( transferId : number , data : Uint8Array ) => void ;
1043} ;
1144
12- const getBridgeClient = async (
45+ // ---------------------------------------------------------------------------
46+ // Factory
47+ // ---------------------------------------------------------------------------
48+
49+ /**
50+ * Connect the app to the CLI harness bridge.
51+ *
52+ * Pass the handlers the CLI can call (runTests). Returns a HarnessHandle
53+ * exposing the operations the app needs to drive a test run. The binary
54+ * transfer protocol and RPC wiring are fully encapsulated.
55+ */
56+ export const connectToHarness = (
1357 url : string ,
14- handlers : BridgeClientFunctions ,
15- ) : Promise < BridgeClient > => {
16- return new Promise ( ( resolve , reject ) => {
58+ callbacks : HarnessCallbacks ,
59+ ) : Promise < HarnessHandle > =>
60+ new Promise ( ( resolve , reject ) => {
1761 const ws = new WebSocket ( url ) ;
1862 ws . binaryType = 'arraybuffer' ;
1963 let settled = false ;
@@ -24,11 +68,8 @@ const getBridgeClient = async (
2468 ws . removeEventListener ( 'close' , handleClose ) ;
2569 } ;
2670
27- const rejectConnection = ( message : string ) => {
28- if ( settled ) {
29- return ;
30- }
31-
71+ const fail = ( message : string ) => {
72+ if ( settled ) return ;
3273 settled = true ;
3374 cleanup ( ) ;
3475 reject ( new Error ( message ) ) ;
@@ -39,56 +80,53 @@ const getBridgeClient = async (
3980 cleanup ( ) ;
4081
4182 const rpc = createBirpc < BridgeServerFunctions , BridgeClientFunctions > (
42- handlers ,
83+ callbacks ,
4384 {
4485 post : ( data ) => ws . send ( data ) ,
4586 on : ( handler ) => {
46- ws . addEventListener ( 'message' , ( event : any ) => {
47- if ( typeof event . data === 'string' ) {
48- handler ( event . data ) ;
49- }
87+ ws . addEventListener ( 'message' , ( event : MessageEvent < string | ArrayBuffer > ) => {
88+ if ( typeof event . data === 'string' ) handler ( event . data ) ;
5089 } ) ;
5190 } ,
5291 serialize,
5392 deserialize,
5493 } ,
5594 ) ;
5695
57- const client : BridgeClient = {
58- rpc,
59- disconnect : ( ) => {
60- ws . close ( ) ;
61- } ,
62- sendBinary : ( transferId : number , data : Uint8Array ) => {
63- const frame = createBinaryFrame ( transferId , data ) ;
64- ws . send ( frame ) ;
96+ resolve ( {
97+ reportReady : ( device ) => void rpc . reportReady ( device ) ,
98+ emitEvent : ( event ) => void rpc . emitEvent ( event . type , event ) ,
99+ transferScreenshot : async ( data , metadata ) => {
100+ const transferId = generateTransferId ( ) ;
101+ ws . send ( createBinaryFrame ( transferId , data ) ) ;
102+ return rpc [ 'device.screenshot.receive' ] (
103+ { type : 'binary' , transferId, size : data . length , mimeType : 'image/png' } ,
104+ metadata ,
105+ ) ;
65106 } ,
66- } ;
67-
68- resolve ( client ) ;
107+ matchImageSnapshot : ( screenshot , testPath , options , runner ) =>
108+ rpc [ 'test.matchImageSnapshot' ] ( screenshot , testPath , options , runner ) ,
109+ disconnect : ( ) => ws . close ( ) ,
110+ } ) ;
69111 } ;
70112
71113 const handleError = ( event : Event & { message ?: string } ) => {
72- const reason =
73- typeof event . message === 'string' && event . message . length > 0
114+ const detail =
115+ typeof event . message === 'string' && event . message
74116 ? `: ${ event . message } `
75117 : '' ;
76-
77- rejectConnection (
78- `Failed to connect to the Harness bridge at ${ url } ${ reason } ` ,
79- ) ;
118+ fail ( `Failed to connect to Harness at ${ url } ${ detail } ` ) ;
80119 } ;
81120
82121 const handleClose = ( event : CloseEvent ) => {
83- rejectConnection (
84- `Harness bridge connection to ${ url } closed before it became ready (code ${ event . code } ${ event . reason ? `, reason: ${ event . reason } ` : '' } )` ,
122+ fail (
123+ `Harness connection at ${ url } closed before becoming ready (code ${ event . code } ${
124+ event . reason ? `, reason: ${ event . reason } ` : ''
125+ } )`,
85126 ) ;
86127 } ;
87128
88129 ws . addEventListener ( 'open' , handleOpen ) ;
89130 ws . addEventListener ( 'error' , handleError ) ;
90131 ws . addEventListener ( 'close' , handleClose ) ;
91132 } ) ;
92- } ;
93-
94- export { getBridgeClient } ;
0 commit comments