| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Copyright 2019 Google Inc. All rights reserved. | 
					
						
							| 
									
										
										
										
											2019-12-10 13:21:51 -08:00
										 |  |  |  * Modifications copyright (c) Microsoft Corporation. | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const path = require('path'); | 
					
						
							| 
									
										
										
										
											2020-01-28 13:07:53 -08:00
										 |  |  | const {spawn, execSync} = require('child_process'); | 
					
						
							| 
									
										
										
										
											2020-04-16 18:46:24 -07:00
										 |  |  | const {FFOX, CHROMIUM, WEBKIT, WIN, LINUX} = require('./utils').testOptions(browserType); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-20 14:58:27 -07:00
										 |  |  | class Wrapper { | 
					
						
							|  |  |  |   constructor(state, extraOptions) { | 
					
						
							|  |  |  |     this._output = new Map(); | 
					
						
							|  |  |  |     this._outputCallback = new Map(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this._browserType = state.browserType; | 
					
						
							|  |  |  |     const launchOptions = {...state.defaultBrowserOptions, | 
					
						
							|  |  |  |       handleSIGINT: true, | 
					
						
							|  |  |  |       handleSIGTERM: true, | 
					
						
							|  |  |  |       handleSIGHUP: true, | 
					
						
							|  |  |  |       executablePath: state.browserType.executablePath(), | 
					
						
							|  |  |  |       logger: undefined, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     const options = { | 
					
						
							| 
									
										
										
										
											2020-05-23 00:03:57 -07:00
										 |  |  |       playwrightFile: path.join(state.playwrightPath, 'index'), | 
					
						
							| 
									
										
										
										
											2020-05-20 14:58:27 -07:00
										 |  |  |       browserTypeName: state.browserType.name(), | 
					
						
							|  |  |  |       launchOptions, | 
					
						
							|  |  |  |       ...extraOptions, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     this._child = spawn('node', [path.join(__dirname, 'fixtures', 'closeme.js'), JSON.stringify(options)]); | 
					
						
							|  |  |  |     this._child.on('error', (...args) => console.log("ERROR", ...args)); | 
					
						
							|  |  |  |     this._exitPromise = new Promise(resolve => this._child.on('exit', resolve)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     let outputString = ''; | 
					
						
							|  |  |  |     this._child.stdout.on('data', data => { | 
					
						
							|  |  |  |       outputString += data.toString(); | 
					
						
							|  |  |  |       // Uncomment to debug.
 | 
					
						
							|  |  |  |       // console.log(data.toString());
 | 
					
						
							|  |  |  |       let match; | 
					
						
							|  |  |  |       while (match = outputString.match(/\(([^()]+)=>([^()]+)\)/)) { | 
					
						
							|  |  |  |         const key = match[1]; | 
					
						
							|  |  |  |         const value = match[2]; | 
					
						
							|  |  |  |         this._addOutput(key, value); | 
					
						
							|  |  |  |         outputString = outputString.substring(match.index + match[0].length); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _addOutput(key, value) { | 
					
						
							|  |  |  |     this._output.set(key, value); | 
					
						
							|  |  |  |     const cb = this._outputCallback.get(key); | 
					
						
							|  |  |  |     this._outputCallback.delete(key); | 
					
						
							|  |  |  |     if (cb) | 
					
						
							|  |  |  |       cb(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async out(key) { | 
					
						
							|  |  |  |     if (!this._output.has(key)) | 
					
						
							|  |  |  |       await new Promise(f => this._outputCallback.set(key, f)); | 
					
						
							|  |  |  |     return this._output.get(key); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async connect() { | 
					
						
							|  |  |  |     const wsEndpoint = await this.out('wsEndpoint'); | 
					
						
							|  |  |  |     const browser = await this._browserType.connect({ wsEndpoint }); | 
					
						
							|  |  |  |     this._exitAndDisconnectPromise = Promise.all([ | 
					
						
							|  |  |  |       this._exitPromise, | 
					
						
							|  |  |  |       new Promise(resolve => browser.once('disconnected', resolve)), | 
					
						
							|  |  |  |     ]).then(([exitCode]) => exitCode); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   child() { | 
					
						
							|  |  |  |     return this._child; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async childExitCode() { | 
					
						
							|  |  |  |     return await this._exitAndDisconnectPromise; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function setup(state, options = {}) { | 
					
						
							|  |  |  |   const wrapper = new Wrapper(state, options); | 
					
						
							|  |  |  |   await wrapper.connect(); | 
					
						
							|  |  |  |   return wrapper; | 
					
						
							| 
									
										
										
										
											2020-04-08 15:19:09 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 15:19:09 -07:00
										 |  |  | describe('Fixtures', function() { | 
					
						
							| 
									
										
										
										
											2020-04-12 22:19:26 -07:00
										 |  |  |   it.slow()('should close the browser when the node process closes', async state => { | 
					
						
							| 
									
										
										
										
											2020-05-20 14:58:27 -07:00
										 |  |  |     const wrapper = await setup(state); | 
					
						
							|  |  |  |     if (WIN) | 
					
						
							|  |  |  |       execSync(`taskkill /pid ${wrapper.child().pid} /T /F`); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       process.kill(wrapper.child().pid); | 
					
						
							|  |  |  |     expect(await wrapper.childExitCode()).toBe(WIN ? 1 : 0); | 
					
						
							| 
									
										
										
										
											2020-04-08 15:19:09 -07:00
										 |  |  |     // We might not get browser exitCode in time when killing the parent node process,
 | 
					
						
							|  |  |  |     // so we don't check it here.
 | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-01-28 13:07:53 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-13 18:33:07 -07:00
										 |  |  |   describe.skip(WIN).skip(!HEADLESS)('signals', () => { | 
					
						
							| 
									
										
										
										
											2020-04-08 15:19:09 -07:00
										 |  |  |     // Cannot reliably send signals on Windows.
 | 
					
						
							| 
									
										
										
										
											2020-04-12 22:19:26 -07:00
										 |  |  |     it.slow()('should report browser close signal', async state => { | 
					
						
							| 
									
										
										
										
											2020-05-20 14:58:27 -07:00
										 |  |  |       const wrapper = await setup(state); | 
					
						
							|  |  |  |       const pid = await wrapper.out('pid'); | 
					
						
							|  |  |  |       process.kill(-pid, 'SIGTERM'); | 
					
						
							|  |  |  |       expect(await wrapper.out('exitCode')).toBe('null'); | 
					
						
							|  |  |  |       expect(await wrapper.out('signal')).toBe('SIGTERM'); | 
					
						
							|  |  |  |       process.kill(wrapper.child().pid); | 
					
						
							|  |  |  |       await wrapper.childExitCode(); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-04-12 22:19:26 -07:00
										 |  |  |     it.slow()('should report browser close signal 2', async state => { | 
					
						
							| 
									
										
										
										
											2020-05-20 14:58:27 -07:00
										 |  |  |       const wrapper = await setup(state); | 
					
						
							|  |  |  |       const pid = await wrapper.out('pid'); | 
					
						
							|  |  |  |       process.kill(-pid, 'SIGKILL'); | 
					
						
							|  |  |  |       expect(await wrapper.out('exitCode')).toBe('null'); | 
					
						
							|  |  |  |       expect(await wrapper.out('signal')).toBe('SIGKILL'); | 
					
						
							|  |  |  |       process.kill(wrapper.child().pid); | 
					
						
							|  |  |  |       await wrapper.childExitCode(); | 
					
						
							| 
									
										
										
										
											2020-01-28 13:07:53 -08:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-04-12 22:19:26 -07:00
										 |  |  |     it.slow()('should close the browser on SIGINT', async state => { | 
					
						
							| 
									
										
										
										
											2020-05-20 14:58:27 -07:00
										 |  |  |       const wrapper = await setup(state); | 
					
						
							|  |  |  |       process.kill(wrapper.child().pid, 'SIGINT'); | 
					
						
							|  |  |  |       expect(await wrapper.out('exitCode')).toBe('0'); | 
					
						
							|  |  |  |       expect(await wrapper.out('signal')).toBe('null'); | 
					
						
							|  |  |  |       expect(await wrapper.childExitCode()).toBe(130); | 
					
						
							| 
									
										
										
										
											2020-04-08 15:19:09 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-04-12 22:19:26 -07:00
										 |  |  |     it.slow()('should close the browser on SIGTERM', async state => { | 
					
						
							| 
									
										
										
										
											2020-05-20 14:58:27 -07:00
										 |  |  |       const wrapper = await setup(state); | 
					
						
							|  |  |  |       process.kill(wrapper.child().pid, 'SIGTERM'); | 
					
						
							|  |  |  |       expect(await wrapper.out('exitCode')).toBe('0'); | 
					
						
							|  |  |  |       expect(await wrapper.out('signal')).toBe('null'); | 
					
						
							|  |  |  |       expect(await wrapper.childExitCode()).toBe(0); | 
					
						
							| 
									
										
										
										
											2020-04-08 15:19:09 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-04-12 22:19:26 -07:00
										 |  |  |     it.slow()('should close the browser on SIGHUP', async state => { | 
					
						
							| 
									
										
										
										
											2020-05-20 14:58:27 -07:00
										 |  |  |       const wrapper = await setup(state); | 
					
						
							|  |  |  |       process.kill(wrapper.child().pid, 'SIGHUP'); | 
					
						
							|  |  |  |       expect(await wrapper.out('exitCode')).toBe('0'); | 
					
						
							|  |  |  |       expect(await wrapper.out('signal')).toBe('null'); | 
					
						
							|  |  |  |       expect(await wrapper.childExitCode()).toBe(0); | 
					
						
							| 
									
										
										
										
											2020-04-08 15:19:09 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-04-12 22:19:26 -07:00
										 |  |  |     it.slow()('should kill the browser on double SIGINT', async state => { | 
					
						
							| 
									
										
										
										
											2020-05-20 14:58:27 -07:00
										 |  |  |       const wrapper = await setup(state, { stallOnClose: true }); | 
					
						
							|  |  |  |       process.kill(wrapper.child().pid, 'SIGINT'); | 
					
						
							|  |  |  |       await wrapper.out('stalled'); | 
					
						
							|  |  |  |       process.kill(wrapper.child().pid, 'SIGINT'); | 
					
						
							|  |  |  |       expect(await wrapper.out('exitCode')).toBe('null'); | 
					
						
							|  |  |  |       expect(await wrapper.out('signal')).toBe('SIGKILL'); | 
					
						
							|  |  |  |       expect(await wrapper.childExitCode()).toBe(130); | 
					
						
							| 
									
										
										
										
											2020-04-08 15:19:09 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-05-20 14:58:27 -07:00
										 |  |  |     it.slow()('should kill the browser on SIGINT + SIGTERM', async state => { | 
					
						
							|  |  |  |       const wrapper = await setup(state, { stallOnClose: true }); | 
					
						
							|  |  |  |       process.kill(wrapper.child().pid, 'SIGINT'); | 
					
						
							|  |  |  |       await wrapper.out('stalled'); | 
					
						
							|  |  |  |       process.kill(wrapper.child().pid, 'SIGTERM'); | 
					
						
							|  |  |  |       expect(await wrapper.out('exitCode')).toBe('null'); | 
					
						
							|  |  |  |       expect(await wrapper.out('signal')).toBe('SIGKILL'); | 
					
						
							|  |  |  |       expect(await wrapper.childExitCode()).toBe(0); | 
					
						
							| 
									
										
										
										
											2020-04-08 15:19:09 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-05-20 14:58:27 -07:00
										 |  |  |     it.slow()('should kill the browser on SIGTERM + SIGINT', async state => { | 
					
						
							|  |  |  |       const wrapper = await setup(state, { stallOnClose: true }); | 
					
						
							|  |  |  |       process.kill(wrapper.child().pid, 'SIGTERM'); | 
					
						
							|  |  |  |       await wrapper.out('stalled'); | 
					
						
							|  |  |  |       process.kill(wrapper.child().pid, 'SIGINT'); | 
					
						
							|  |  |  |       expect(await wrapper.out('exitCode')).toBe('null'); | 
					
						
							|  |  |  |       expect(await wrapper.out('signal')).toBe('SIGKILL'); | 
					
						
							|  |  |  |       expect(await wrapper.childExitCode()).toBe(130); | 
					
						
							| 
									
										
										
										
											2020-03-27 20:29:24 -07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-11-18 18:18:28 -08:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-04-08 15:19:09 -07:00
										 |  |  | }); | 
					
						
							| 
									
										
										
										
											2020-05-27 14:26:44 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | describe('StackTrace', () => { | 
					
						
							|  |  |  |   it('caller file path', async state => { | 
					
						
							| 
									
										
										
										
											2020-06-11 18:18:33 -07:00
										 |  |  |     const stackTrace = require(path.join(state.playwrightPath, 'lib', 'utils', 'stackTrace')); | 
					
						
							| 
									
										
										
										
											2020-05-27 14:26:44 -07:00
										 |  |  |     const callme = require('./fixtures/callback'); | 
					
						
							|  |  |  |     const filePath = callme(() => { | 
					
						
							|  |  |  |       return stackTrace.getCallerFilePath(path.join(__dirname, 'fixtures') + path.sep); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     expect(filePath).toBe(__filename); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }); |