| 
									
										
										
										
											2020-09-26 10:59:27 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Copyright Microsoft Corporation. All rights reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import assert from 'assert'; | 
					
						
							|  |  |  | import childProcess from 'child_process'; | 
					
						
							|  |  |  | import fs from 'fs'; | 
					
						
							|  |  |  | import path from 'path'; | 
					
						
							|  |  |  | import util from 'util'; | 
					
						
							| 
									
										
										
										
											2020-09-30 16:52:21 -07:00
										 |  |  | import os from 'os'; | 
					
						
							| 
									
										
										
										
											2021-02-08 14:39:05 -08:00
										 |  |  | import type { Browser, BrowserContext, BrowserType, Page } from '../index'; | 
					
						
							| 
									
										
										
										
											2020-09-26 10:59:27 -07:00
										 |  |  | import { installCoverageHooks } from './coverage'; | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  | import { folio as httpFolio } from './http.fixtures'; | 
					
						
							|  |  |  | import { folio as playwrightFolio } from './playwright.fixtures'; | 
					
						
							| 
									
										
										
										
											2020-11-20 15:19:39 -08:00
										 |  |  | import { PlaywrightClient } from '../lib/remote/playwrightClient'; | 
					
						
							| 
									
										
										
										
											2021-02-12 09:05:32 -08:00
										 |  |  | import { start } from '../lib/outofprocess'; | 
					
						
							| 
									
										
										
										
											2021-03-17 10:31:35 +08:00
										 |  |  | import { removeFolders } from '../lib/utils/utils'; | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  | export { expect, config } from 'folio'; | 
					
						
							| 
									
										
										
										
											2020-09-26 10:59:27 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-30 16:52:21 -07:00
										 |  |  | const mkdtempAsync = util.promisify(fs.mkdtemp); | 
					
						
							| 
									
										
										
										
											2020-09-26 10:59:27 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-05 21:40:56 -07:00
										 |  |  | const getExecutablePath = browserName => { | 
					
						
							|  |  |  |   if (browserName === 'chromium' && process.env.CRPATH) | 
					
						
							|  |  |  |     return process.env.CRPATH; | 
					
						
							|  |  |  |   if (browserName === 'firefox' && process.env.FFPATH) | 
					
						
							|  |  |  |     return process.env.FFPATH; | 
					
						
							|  |  |  |   if (browserName === 'webkit' && process.env.WKPATH) | 
					
						
							|  |  |  |     return process.env.WKPATH; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-20 15:19:39 -08:00
										 |  |  | type ModeParameters = { | 
					
						
							|  |  |  |   mode: 'default' | 'driver' | 'service'; | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | type WorkerFixtures = { | 
					
						
							|  |  |  |   toImpl: (rpcObject: any) => any; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | type TestFixtures = { | 
					
						
							| 
									
										
										
										
											2020-09-26 10:59:27 -07:00
										 |  |  |   createUserDataDir: () => Promise<string>; | 
					
						
							| 
									
										
										
										
											2020-10-05 17:03:24 -07:00
										 |  |  |   launchPersistent: (options?: Parameters<BrowserType<Browser>['launchPersistentContext']>[1]) => Promise<{ context: BrowserContext, page: Page }>; | 
					
						
							| 
									
										
										
										
											2020-09-26 10:59:27 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-20 15:19:39 -08:00
										 |  |  | const fixtures = playwrightFolio.union(httpFolio).extend<TestFixtures, WorkerFixtures, ModeParameters>(); | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-20 15:19:39 -08:00
										 |  |  | fixtures.mode.initParameter('Testing mode', process.env.PWMODE as any || 'default'); | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-12 13:48:56 -07:00
										 |  |  | fixtures.createUserDataDir.init(async ({ }, run) => { | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  |   const dirs: string[] = []; | 
					
						
							|  |  |  |   async function createUserDataDir() { | 
					
						
							|  |  |  |   // We do not put user data dir in testOutputPath,
 | 
					
						
							|  |  |  |   // because we do not want to upload them as test result artifacts.
 | 
					
						
							|  |  |  |   //
 | 
					
						
							|  |  |  |   // Additionally, it is impossible to upload user data dir after test run:
 | 
					
						
							|  |  |  |   // - Firefox removes lock file later, presumably from another watchdog process?
 | 
					
						
							|  |  |  |   // - WebKit has circular symlinks that makes CI go crazy.
 | 
					
						
							|  |  |  |     const dir = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-')); | 
					
						
							|  |  |  |     dirs.push(dir); | 
					
						
							|  |  |  |     return dir; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   await run(createUserDataDir); | 
					
						
							| 
									
										
										
										
											2021-03-17 10:31:35 +08:00
										 |  |  |   await removeFolders(dirs); | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-13 13:18:36 -07:00
										 |  |  | fixtures.launchPersistent.init(async ({ createUserDataDir, browserOptions, browserType }, run) => { | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  |   let context; | 
					
						
							|  |  |  |   async function launchPersistent(options) { | 
					
						
							|  |  |  |     if (context) | 
					
						
							|  |  |  |       throw new Error('can only launch one persitent context'); | 
					
						
							|  |  |  |     const userDataDir = await createUserDataDir(); | 
					
						
							| 
									
										
										
										
											2020-10-13 13:18:36 -07:00
										 |  |  |     context = await browserType.launchPersistentContext(userDataDir, { ...browserOptions, ...options }); | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  |     const page = context.pages()[0]; | 
					
						
							|  |  |  |     return { context, page }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   await run(launchPersistent); | 
					
						
							|  |  |  |   if (context) | 
					
						
							|  |  |  |     await context.close(); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-15 08:07:57 -07:00
										 |  |  | fixtures.browserOptions.override(async ({ browserName, headful, slowMo, browserChannel }, run) => { | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  |   const executablePath = getExecutablePath(browserName); | 
					
						
							|  |  |  |   if (executablePath) | 
					
						
							|  |  |  |     console.error(`Using executable at ${executablePath}`); | 
					
						
							|  |  |  |   await run({ | 
					
						
							| 
									
										
										
										
											2021-03-15 08:07:57 -07:00
										 |  |  |     channel: browserChannel, | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  |     executablePath, | 
					
						
							|  |  |  |     handleSIGINT: false, | 
					
						
							|  |  |  |     slowMo, | 
					
						
							|  |  |  |     headless: !headful, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-20 15:19:39 -08:00
										 |  |  | fixtures.playwright.override(async ({ browserName, testWorkerIndex, platform, mode }, run) => { | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  |   assert(platform); // Depend on platform to generate all tests.
 | 
					
						
							|  |  |  |   const { coverage, uninstall } = installCoverageHooks(browserName); | 
					
						
							| 
									
										
										
										
											2021-02-11 17:46:54 -08:00
										 |  |  |   require('../lib/utils/utils').setUnderTest(); | 
					
						
							| 
									
										
										
										
											2020-11-20 15:19:39 -08:00
										 |  |  |   if (mode === 'driver') { | 
					
						
							| 
									
										
										
										
											2021-02-12 09:05:32 -08:00
										 |  |  |     const playwrightObject = await start(); | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  |     await run(playwrightObject); | 
					
						
							| 
									
										
										
										
											2021-02-12 09:05:32 -08:00
										 |  |  |     await playwrightObject.stop(); | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  |     await teardownCoverage(); | 
					
						
							| 
									
										
										
										
											2020-11-20 15:19:39 -08:00
										 |  |  |   } else if (mode === 'service') { | 
					
						
							|  |  |  |     const port = 9407 + testWorkerIndex * 2; | 
					
						
							|  |  |  |     const spawnedProcess = childProcess.fork(path.join(__dirname, '..', 'lib', 'service.js'), [String(port)], { | 
					
						
							|  |  |  |       stdio: 'pipe' | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-12-10 15:01:30 -08:00
										 |  |  |     spawnedProcess.stderr.pipe(process.stderr); | 
					
						
							| 
									
										
										
										
											2021-02-01 11:43:26 -08:00
										 |  |  |     await new Promise<void>(f => { | 
					
						
							| 
									
										
										
										
											2020-11-20 15:19:39 -08:00
										 |  |  |       spawnedProcess.stdout.on('data', data => { | 
					
						
							|  |  |  |         if (data.toString().includes('Listening on')) | 
					
						
							|  |  |  |           f(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     spawnedProcess.unref(); | 
					
						
							|  |  |  |     const onExit = (exitCode, signal) => { | 
					
						
							|  |  |  |       throw new Error(`Server closed with exitCode=${exitCode} signal=${signal}`); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     spawnedProcess.on('exit', onExit); | 
					
						
							|  |  |  |     const client = await PlaywrightClient.connect(`ws://localhost:${port}/ws`); | 
					
						
							|  |  |  |     await run(client.playwright()); | 
					
						
							|  |  |  |     await client.close(); | 
					
						
							| 
									
										
										
										
											2020-12-10 15:01:30 -08:00
										 |  |  |     spawnedProcess.removeListener('exit', onExit); | 
					
						
							|  |  |  |     const processExited = new Promise(f => spawnedProcess.on('exit', f)); | 
					
						
							| 
									
										
										
										
											2020-11-20 15:19:39 -08:00
										 |  |  |     spawnedProcess.kill(); | 
					
						
							| 
									
										
										
										
											2020-12-10 15:01:30 -08:00
										 |  |  |     await processExited; | 
					
						
							| 
									
										
										
										
											2020-11-20 15:19:39 -08:00
										 |  |  |     await teardownCoverage(); | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  |   } else { | 
					
						
							|  |  |  |     const playwright = require('../index'); | 
					
						
							|  |  |  |     await run(playwright); | 
					
						
							|  |  |  |     await teardownCoverage(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async function teardownCoverage() { | 
					
						
							|  |  |  |     uninstall(); | 
					
						
							|  |  |  |     const coveragePath = path.join(__dirname, 'coverage-report', testWorkerIndex + '.json'); | 
					
						
							|  |  |  |     const coverageJSON = [...coverage.keys()].filter(key => coverage.get(key)); | 
					
						
							|  |  |  |     await fs.promises.mkdir(path.dirname(coveragePath), { recursive: true }); | 
					
						
							|  |  |  |     await fs.promises.writeFile(coveragePath, JSON.stringify(coverageJSON, undefined, 2), 'utf8'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-12 13:48:56 -07:00
										 |  |  | fixtures.toImpl.init(async ({ playwright }, run) => { | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  |   await run((playwright as any)._toImpl); | 
					
						
							| 
									
										
										
										
											2020-10-12 13:48:56 -07:00
										 |  |  | }, { scope: 'worker' }); | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-12 13:48:56 -07:00
										 |  |  | fixtures.testParametersPathSegment.override(async ({ browserName }, run) => { | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  |   await run(browserName); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const folio = fixtures.build(); | 
					
						
							| 
									
										
										
										
											2020-09-26 10:59:27 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  | folio.generateParametrizedTests( | 
					
						
							| 
									
										
										
										
											2020-10-05 21:40:56 -07:00
										 |  |  |     'platform', | 
					
						
							|  |  |  |     process.env.PWTESTREPORT ? ['win32', 'darwin', 'linux'] : [process.platform as ('win32' | 'linux' | 'darwin')]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  | export const it = folio.it; | 
					
						
							|  |  |  | export const fit = folio.fit; | 
					
						
							| 
									
										
										
										
											2020-10-12 13:48:56 -07:00
										 |  |  | export const test = folio.test; | 
					
						
							| 
									
										
										
										
											2020-10-12 09:16:02 -07:00
										 |  |  | export const xit = folio.xit; | 
					
						
							|  |  |  | export const describe = folio.describe; | 
					
						
							|  |  |  | export const beforeEach = folio.beforeEach; | 
					
						
							|  |  |  | export const afterEach = folio.afterEach; | 
					
						
							|  |  |  | export const beforeAll = folio.beforeAll; | 
					
						
							|  |  |  | export const afterAll = folio.afterAll; |