11import repl , { type REPLServer } from "node:repl" ;
2+ import net from "node:net" ;
3+ import { PassThrough } from "node:stream" ;
24import { EventEmitter } from "node:events" ;
35import proxyquire from "proxyquire" ;
46import chalk from "chalk" ;
@@ -11,12 +13,17 @@ import type { ExistingBrowser as ExistingBrowserOriginal } from "src/browser/exi
1113
1214describe ( '"switchToRepl" command' , ( ) => {
1315 const sandbox = sinon . createSandbox ( ) ;
16+ const stdinStub = new PassThrough ( ) ;
17+ const stdoutStub = new PassThrough ( ) ;
18+ const originalStdin = process . stdin ;
19+ const originalStdout = process . stdout ;
1420
1521 let ExistingBrowser : typeof ExistingBrowserOriginal ;
1622 let logStub : SinonStub ;
1723 let warnStub : SinonStub ;
1824 let webdriverioAttachStub : SinonStub ;
1925 let clientBridgeBuildStub ;
26+ let netCreateServerCb : ( socket : net . Socket ) => void ;
2027
2128 const initBrowser_ = ( {
2229 browser = mkBrowser_ ( undefined , undefined , ExistingBrowser ) ,
@@ -39,6 +46,28 @@ describe('"switchToRepl" command', () => {
3946 return replServer ;
4047 } ;
4148
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+
4271 const switchToRepl_ = async ( {
4372 session = mkSessionStub_ ( ) ,
4473 replServer = mkReplServer_ ( ) ,
@@ -72,9 +101,24 @@ describe('"switchToRepl" command', () => {
72101
73102 sandbox . stub ( RuntimeConfig , "getInstance" ) . returns ( { replMode : { enabled : false } , extend : sinon . stub ( ) } ) ;
74103 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" ) ;
75114 } ) ;
76115
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+ } ) ;
78122
79123 it ( "should add command" , async ( ) => {
80124 const session = mkSessionStub_ ( ) ;
@@ -97,8 +141,14 @@ describe('"switchToRepl" command', () => {
97141 } ) ;
98142
99143 describe ( "in REPL mode" , async ( ) => {
144+ let netServer ! : net . Server ;
145+
100146 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+ } ) ;
102152 } ) ;
103153
104154 it ( "should inform that user entered to repl server before run it" , async ( ) => {
@@ -107,12 +157,13 @@ describe('"switchToRepl" command', () => {
107157 await initBrowser_ ( { session } ) ;
108158 await switchToRepl_ ( { session } ) ;
109159
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" ,
113164 ) ,
114- repl . start as SinonStub ,
115165 ) ;
166+ assert . callOrder ( logStub as SinonStub , repl . start as SinonStub ) ;
116167 } ) ;
117168
118169 it ( "should change cwd to test directory before run repl server" , async ( ) => {
@@ -256,5 +307,101 @@ describe('"switchToRepl" command', () => {
256307 } ) ;
257308 } ) ;
258309 } ) ;
310+
311+ describe ( "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+ } ) ;
259406 } ) ;
260407} ) ;
0 commit comments