1
1
import repl , { type REPLServer } from "node:repl" ;
2
+ import net from "node:net" ;
3
+ import { PassThrough } from "node:stream" ;
2
4
import { EventEmitter } from "node:events" ;
3
5
import proxyquire from "proxyquire" ;
4
6
import chalk from "chalk" ;
@@ -11,12 +13,17 @@ import type { ExistingBrowser as ExistingBrowserOriginal } from "src/browser/exi
11
13
12
14
describe ( '"switchToRepl" command' , ( ) => {
13
15
const sandbox = sinon . createSandbox ( ) ;
16
+ const stdinStub = new PassThrough ( ) ;
17
+ const stdoutStub = new PassThrough ( ) ;
18
+ const originalStdin = process . stdin ;
19
+ const originalStdout = process . stdout ;
14
20
15
21
let ExistingBrowser : typeof ExistingBrowserOriginal ;
16
22
let logStub : SinonStub ;
17
23
let warnStub : SinonStub ;
18
24
let webdriverioAttachStub : SinonStub ;
19
25
let clientBridgeBuildStub ;
26
+ let netCreateServerCb : ( socket : net . Socket ) => void ;
20
27
21
28
const initBrowser_ = ( {
22
29
browser = mkBrowser_ ( undefined , undefined , ExistingBrowser ) ,
@@ -39,6 +46,28 @@ describe('"switchToRepl" command', () => {
39
46
return replServer ;
40
47
} ;
41
48
49
+ const mkNetServer_ = ( ) : net . Server => {
50
+ const netServer = new EventEmitter ( ) as net . Server ;
51
+ netServer . listen = sandbox . stub ( ) . named ( "listen" ) . returnsThis ( ) ;
52
+ netServer . close = sandbox . stub ( ) . named ( "close" ) . returnsThis ( ) ;
53
+
54
+ ( sandbox . stub ( net , "createServer" ) as SinonStub ) . callsFake ( cb => {
55
+ netCreateServerCb = cb ;
56
+ return netServer ;
57
+ } ) ;
58
+
59
+ return netServer ;
60
+ } ;
61
+
62
+ const mkSocket_ = ( ) : net . Socket => {
63
+ const socket = new EventEmitter ( ) as net . Socket ;
64
+
65
+ socket . write = sandbox . stub ( ) . named ( "write" ) . returns ( true ) ;
66
+ socket . end = sandbox . stub ( ) . named ( "end" ) . returnsThis ( ) ;
67
+
68
+ return socket ;
69
+ } ;
70
+
42
71
const switchToRepl_ = async ( {
43
72
session = mkSessionStub_ ( ) ,
44
73
replServer = mkReplServer_ ( ) ,
@@ -72,9 +101,24 @@ describe('"switchToRepl" command', () => {
72
101
73
102
sandbox . stub ( RuntimeConfig , "getInstance" ) . returns ( { replMode : { enabled : false } , extend : sinon . stub ( ) } ) ;
74
103
sandbox . stub ( process , "chdir" ) ;
104
+
105
+ Object . defineProperty ( process , "stdin" , {
106
+ value : stdinStub ,
107
+ configurable : true ,
108
+ } ) ;
109
+ Object . defineProperty ( process , "stdout" , {
110
+ value : stdoutStub ,
111
+ configurable : true ,
112
+ } ) ;
113
+ sandbox . stub ( stdoutStub , "write" ) ;
75
114
} ) ;
76
115
77
- afterEach ( ( ) => sandbox . restore ( ) ) ;
116
+ afterEach ( ( ) => {
117
+ sandbox . restore ( ) ;
118
+
119
+ Object . defineProperty ( process , "stdin" , { value : originalStdin } ) ;
120
+ Object . defineProperty ( process , "sdout" , { value : originalStdout } ) ;
121
+ } ) ;
78
122
79
123
it ( "should add command" , async ( ) => {
80
124
const session = mkSessionStub_ ( ) ;
@@ -97,8 +141,14 @@ describe('"switchToRepl" command', () => {
97
141
} ) ;
98
142
99
143
describe ( "in REPL mode" , async ( ) => {
144
+ let netServer ! : net . Server ;
145
+
100
146
beforeEach ( ( ) => {
101
- ( RuntimeConfig . getInstance as SinonStub ) . returns ( { replMode : { enabled : true } , extend : sinon . stub ( ) } ) ;
147
+ netServer = mkNetServer_ ( ) ;
148
+ ( RuntimeConfig . getInstance as SinonStub ) . returns ( {
149
+ replMode : { enabled : true , port : 12345 } ,
150
+ extend : sinon . stub ( ) ,
151
+ } ) ;
102
152
} ) ;
103
153
104
154
it ( "should inform that user entered to repl server before run it" , async ( ) => {
@@ -107,12 +157,13 @@ describe('"switchToRepl" command', () => {
107
157
await initBrowser_ ( { session } ) ;
108
158
await switchToRepl_ ( { session } ) ;
109
159
110
- assert . callOrder (
111
- ( logStub as SinonStub ) . withArgs (
112
- chalk . yellow ( "You have entered to REPL mode via terminal (test execution timeout is disabled)." ) ,
160
+ assert . calledOnceWith (
161
+ logStub ,
162
+ chalk . yellow (
163
+ "You have entered to REPL mode via terminal (test execution timeout is disabled). Port to connect to REPL from other terminals: 12345" ,
113
164
) ,
114
- repl . start as SinonStub ,
115
165
) ;
166
+ assert . callOrder ( logStub as SinonStub , repl . start as SinonStub ) ;
116
167
} ) ;
117
168
118
169
it ( "should change cwd to test directory before run repl server" , async ( ) => {
@@ -256,5 +307,101 @@ describe('"switchToRepl" command', () => {
256
307
} ) ;
257
308
} ) ;
258
309
} ) ;
310
+
311
+ describe . only ( "net server" , ( ) => {
312
+ it ( "should create server with listen port from runtime config" , async ( ) => {
313
+ const runtimeCfg = { replMode : { enabled : true , port : 33333 } , extend : sinon . stub ( ) } ;
314
+ ( RuntimeConfig . getInstance as SinonStub ) . returns ( runtimeCfg ) ;
315
+
316
+ const session = mkSessionStub_ ( ) ;
317
+
318
+ await initBrowser_ ( { session } ) ;
319
+ await switchToRepl_ ( { session } ) ;
320
+
321
+ assert . calledOnceWith ( netServer . listen , 33333 ) ;
322
+ } ) ;
323
+
324
+ it ( "should broadcast message from stdin to connected sockets" , async ( ) => {
325
+ const socket1 = mkSocket_ ( ) ;
326
+ const socket2 = mkSocket_ ( ) ;
327
+ const session = mkSessionStub_ ( ) ;
328
+
329
+ await initBrowser_ ( { session } ) ;
330
+ await switchToRepl_ ( { session } ) ;
331
+
332
+ netCreateServerCb ( socket1 ) ;
333
+ netCreateServerCb ( socket2 ) ;
334
+ stdinStub . write ( "o.O" ) ;
335
+
336
+ assert . calledOnceWith ( socket1 . write , "o.O" ) ;
337
+ assert . calledOnceWith ( socket2 . write , "o.O" ) ;
338
+ } ) ;
339
+
340
+ it ( "should broadcast message from socket to other sockets and stdin" , async ( ) => {
341
+ const socket1 = mkSocket_ ( ) ;
342
+ const socket2 = mkSocket_ ( ) ;
343
+ const session = mkSessionStub_ ( ) ;
344
+
345
+ await initBrowser_ ( { session } ) ;
346
+ await switchToRepl_ ( { session } ) ;
347
+
348
+ netCreateServerCb ( socket1 ) ;
349
+ netCreateServerCb ( socket2 ) ;
350
+ socket1 . emit ( "data" , Buffer . from ( "o.O" ) ) ;
351
+
352
+ assert . notCalled ( socket1 . write as SinonStub ) ;
353
+ assert . calledOnceWith ( socket2 . write , "o.O" ) ;
354
+ assert . calledOnceWith ( process . stdout . write , "o.O" ) ;
355
+ } ) ;
356
+
357
+ it ( "should not broadcast message to closed socket" , async ( ) => {
358
+ const socket1 = mkSocket_ ( ) ;
359
+ const socket2 = mkSocket_ ( ) ;
360
+ const session = mkSessionStub_ ( ) ;
361
+
362
+ await initBrowser_ ( { session } ) ;
363
+ await switchToRepl_ ( { session } ) ;
364
+
365
+ netCreateServerCb ( socket1 ) ;
366
+ netCreateServerCb ( socket2 ) ;
367
+
368
+ socket1 . emit ( "close" ) ;
369
+ stdinStub . write ( "o.O" ) ;
370
+
371
+ assert . notCalled ( socket1 . write as SinonStub ) ;
372
+ assert . calledOnceWith ( socket2 . write , "o.O" ) ;
373
+ } ) ;
374
+
375
+ it ( "should close net server on exit from repl" , async ( ) => {
376
+ const session = mkSessionStub_ ( ) ;
377
+ const replServer = mkReplServer_ ( ) ;
378
+
379
+ await initBrowser_ ( { session } ) ;
380
+ const promise = session . switchToRepl ( ) ;
381
+ replServer . emit ( "exit" ) ;
382
+ await promise ;
383
+
384
+ assert . calledOnceWith ( netServer . close ) ;
385
+ } ) ;
386
+
387
+ it ( "should end sockets on exit from repl" , async ( ) => {
388
+ const socket1 = mkSocket_ ( ) ;
389
+ const socket2 = mkSocket_ ( ) ;
390
+ const session = mkSessionStub_ ( ) ;
391
+ const replServer = mkReplServer_ ( ) ;
392
+
393
+ await initBrowser_ ( { session } ) ;
394
+ const promise = session . switchToRepl ( ) ;
395
+
396
+ netCreateServerCb ( socket1 ) ;
397
+ netCreateServerCb ( socket2 ) ;
398
+
399
+ replServer . emit ( "exit" ) ;
400
+ await promise ;
401
+
402
+ assert . calledOnceWith ( socket1 . end , "The server was closed after the REPL was exited" ) ;
403
+ assert . calledOnceWith ( socket2 . end , "The server was closed after the REPL was exited" ) ;
404
+ } ) ;
405
+ } ) ;
259
406
} ) ;
260
407
} ) ;
0 commit comments