16
16
*/
17
17
18
18
const path = require ( 'path' ) ;
19
- const { spawn} = require ( 'child_process' ) ;
19
+ const { spawn, execSync } = require ( 'child_process' ) ;
20
20
21
- module . exports . describe = function ( { testRunner, expect, product, playwrightPath, FFOX , CHROMIUM , WEBKIT } ) {
21
+ module . exports . describe = function ( { testRunner, expect, product, playwright , playwrightPath, defaultBrowserOptions , WIN , FFOX , CHROMIUM , WEBKIT } ) {
22
22
const { describe, xdescribe, fdescribe} = testRunner ;
23
23
const { it, fit, xit, dit} = testRunner ;
24
24
const { beforeAll, beforeEach, afterAll, afterEach} = testRunner ;
25
25
26
+ async function testSignal ( action ) {
27
+ const options = Object . assign ( { } , defaultBrowserOptions , {
28
+ // Disable DUMPIO to cleanly read stdout.
29
+ dumpio : false ,
30
+ webSocket : true ,
31
+ handleSIGINT : true ,
32
+ handleSIGTERM : true ,
33
+ handleSIGHUP : true ,
34
+ } ) ;
35
+ const res = spawn ( 'node' , [ path . join ( __dirname , 'fixtures' , 'closeme.js' ) , playwrightPath , product , JSON . stringify ( options ) ] ) ;
36
+ let wsEndPointCallback ;
37
+ const wsEndPointPromise = new Promise ( x => wsEndPointCallback = x ) ;
38
+ let output = '' ;
39
+ let browserExitCode = 'none' ;
40
+ let browserSignal = 'none' ;
41
+ let browserPid ;
42
+ res . stdout . on ( 'data' , data => {
43
+ output += data ;
44
+ // Uncomment to debug these tests.
45
+ // console.log(data.toString());
46
+ let match = output . match ( / b r o w s e r W S : ( .+ ) : b r o w s e r W S / ) ;
47
+ if ( match )
48
+ wsEndPointCallback ( match [ 1 ] ) ;
49
+ match = output . match ( / b r o w s e r C l o s e : ( [ ^ : ] + ) : ( [ ^ : ] + ) : b r o w s e r C l o s e / ) ;
50
+ if ( match ) {
51
+ browserExitCode = match [ 1 ] ;
52
+ browserSignal = match [ 2 ] ;
53
+ }
54
+ match = output . match ( / b r o w s e r P i d : ( [ ^ : ] + ) : b r o w s e r P i d / ) ;
55
+ if ( match )
56
+ browserPid = + match [ 1 ] ;
57
+ } ) ;
58
+ res . on ( 'error' , ( ...args ) => console . log ( "ERROR" , ...args ) ) ;
59
+ const browser = await playwright . connect ( { browserWSEndpoint : await wsEndPointPromise } ) ;
60
+ const promises = [
61
+ new Promise ( resolve => browser . once ( 'disconnected' , resolve ) ) ,
62
+ new Promise ( resolve => res . on ( 'exit' , resolve ) ) ,
63
+ ] ;
64
+ action ( res , browserPid ) ;
65
+ const [ , exitCode ] = await Promise . all ( promises ) ;
66
+ return { exitCode, browserSignal, browserExitCode, output } ;
67
+ }
68
+
26
69
describe ( 'Fixtures' , function ( ) {
27
70
it ( 'dumpio option should work with webSocket option' , async ( { server} ) => {
28
71
let dumpioData = '' ;
@@ -38,5 +81,82 @@ module.exports.describe = function({testRunner, expect, product, playwrightPath,
38
81
await new Promise ( resolve => res . on ( 'close' , resolve ) ) ;
39
82
expect ( dumpioData ) . toContain ( 'message from dumpio' ) ;
40
83
} ) ;
84
+ it ( 'should close the browser when the node process closes' , async ( ) => {
85
+ const result = await testSignal ( child => {
86
+ if ( process . platform === 'win32' )
87
+ execSync ( `taskkill /pid ${ child . pid } /T /F` ) ;
88
+ else
89
+ process . kill ( child . pid ) ;
90
+ } ) ;
91
+ expect ( result . exitCode ) . toBe ( WIN ? 1 : 0 ) ;
92
+ // We might not get browser exitCode in time when killing the parent node process,
93
+ // so we don't check it here.
94
+ } ) ;
95
+ if ( ! WIN ) {
96
+ // Cannot reliably send signals on Windows.
97
+ it ( 'should report browser close signal' , async ( ) => {
98
+ const result = await testSignal ( ( child , browserPid ) => {
99
+ process . kill ( browserPid ) ;
100
+ process . kill ( child . pid , 'SIGINT' ) ;
101
+ } ) ;
102
+ expect ( result . exitCode ) . toBe ( 130 ) ;
103
+ expect ( result . browserExitCode ) . toBe ( 'null' ) ;
104
+ expect ( result . browserSignal ) . toBe ( 'SIGTERM' ) ;
105
+ } ) ;
106
+ it ( 'should report browser close signal 2' , async ( ) => {
107
+ const result = await testSignal ( ( child , browserPid ) => {
108
+ process . kill ( browserPid , 'SIGKILL' ) ;
109
+ process . kill ( child . pid , 'SIGINT' ) ;
110
+ } ) ;
111
+ expect ( result . exitCode ) . toBe ( 130 ) ;
112
+ expect ( result . browserExitCode ) . toBe ( 'null' ) ;
113
+ expect ( result . browserSignal ) . toBe ( 'SIGKILL' ) ;
114
+ } ) ;
115
+ it ( 'should close the browser on SIGINT' , async ( ) => {
116
+ const result = await testSignal ( child => process . kill ( child . pid , 'SIGINT' ) ) ;
117
+ expect ( result . exitCode ) . toBe ( 130 ) ;
118
+ expect ( result . browserExitCode ) . toBe ( '0' ) ;
119
+ expect ( result . browserSignal ) . toBe ( 'null' ) ;
120
+ } ) ;
121
+ it ( 'should close the browser on SIGTERM' , async ( ) => {
122
+ const result = await testSignal ( child => process . kill ( child . pid , 'SIGTERM' ) ) ;
123
+ expect ( result . exitCode ) . toBe ( 0 ) ;
124
+ expect ( result . browserExitCode ) . toBe ( '0' ) ;
125
+ expect ( result . browserSignal ) . toBe ( 'null' ) ;
126
+ } ) ;
127
+ it ( 'should close the browser on SIGHUP' , async ( ) => {
128
+ const result = await testSignal ( child => process . kill ( child . pid , 'SIGHUP' ) ) ;
129
+ expect ( result . exitCode ) . toBe ( 0 ) ;
130
+ expect ( result . browserExitCode ) . toBe ( '0' ) ;
131
+ expect ( result . browserSignal ) . toBe ( 'null' ) ;
132
+ } ) ;
133
+ it ( 'should kill the browser on double SIGINT' , async ( ) => {
134
+ const result = await testSignal ( child => {
135
+ process . kill ( child . pid , 'SIGINT' ) ;
136
+ process . kill ( child . pid , 'SIGINT' ) ;
137
+ } ) ;
138
+ expect ( result . exitCode ) . toBe ( 130 ) ;
139
+ // TODO: ideally, we would expect the SIGKILL on the browser from
140
+ // force kill, but that's racy with sending two signals.
141
+ } ) ;
142
+ it ( 'should kill the browser on SIGINT + SIGTERM' , async ( ) => {
143
+ const result = await testSignal ( child => {
144
+ process . kill ( child . pid , 'SIGINT' ) ;
145
+ process . kill ( child . pid , 'SIGTERM' ) ;
146
+ } ) ;
147
+ expect ( result . exitCode ) . toBe ( 130 ) ;
148
+ // TODO: ideally, we would expect the SIGKILL on the browser from
149
+ // force kill, but that's racy with sending two signals.
150
+ } ) ;
151
+ it ( 'should kill the browser on SIGTERM + SIGINT' , async ( ) => {
152
+ const result = await testSignal ( child => {
153
+ process . kill ( child . pid , 'SIGTERM' ) ;
154
+ process . kill ( child . pid , 'SIGINT' ) ;
155
+ } ) ;
156
+ expect ( result . exitCode ) . toBe ( 130 ) ;
157
+ // TODO: ideally, we would expect the SIGKILL on the browser from
158
+ // force kill, but that's racy with sending two signals.
159
+ } ) ;
160
+ }
41
161
} ) ;
42
162
} ;
0 commit comments