| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Copyright (c) Microsoft Corporation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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 * as fs from 'fs'; | 
					
						
							| 
									
										
										
										
											2021-06-08 11:22:07 -07:00
										 |  |  | import * as path from 'path'; | 
					
						
							| 
									
										
										
										
											2021-08-09 18:09:11 -07:00
										 |  |  | import type { LaunchOptions, BrowserContextOptions, Page, BrowserContext, BrowserType } from '../../types/types'; | 
					
						
							| 
									
										
										
										
											2021-07-19 12:20:24 -05:00
										 |  |  | import type { TestType, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions } from '../../types/test'; | 
					
						
							| 
									
										
										
										
											2021-06-06 20:18:47 -07:00
										 |  |  | import { rootTestType } from './testType'; | 
					
						
							| 
									
										
										
										
											2021-06-15 10:06:49 -07:00
										 |  |  | import { createGuid, removeFolders } from '../utils/utils'; | 
					
						
							| 
									
										
										
										
											2021-06-06 20:18:47 -07:00
										 |  |  | export { expect } from './expect'; | 
					
						
							|  |  |  | export const _baseTest: TestType<{}, {}> = rootTestType.test; | 
					
						
							| 
									
										
										
										
											2021-06-15 10:06:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-09 18:09:11 -07:00
										 |  |  | type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions & { | 
					
						
							|  |  |  |   _combinedContextOptions: BrowserContextOptions, | 
					
						
							|  |  |  |   _setupContextOptionsAndArtifacts: void; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | type WorkerAndFileFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & { | 
					
						
							|  |  |  |   _browserType: BrowserType; | 
					
						
							|  |  |  |   _artifactsDir: () => string, | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2021-06-15 10:06:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-09 18:09:11 -07:00
										 |  |  | export const test = _baseTest.extend<TestFixtures, WorkerAndFileFixtures>({ | 
					
						
							| 
									
										
										
										
											2021-06-04 20:54:58 -07:00
										 |  |  |   defaultBrowserType: [ 'chromium', { scope: 'worker' } ], | 
					
						
							|  |  |  |   browserName: [ ({ defaultBrowserType }, use) => use(defaultBrowserType), { scope: 'worker' } ], | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  |   playwright: [ require('../inprocess'), { scope: 'worker' } ], | 
					
						
							|  |  |  |   headless: [ undefined, { scope: 'worker' } ], | 
					
						
							|  |  |  |   channel: [ undefined, { scope: 'worker' } ], | 
					
						
							|  |  |  |   launchOptions: [ {}, { scope: 'worker' } ], | 
					
						
							| 
									
										
										
										
											2021-08-09 18:09:11 -07:00
										 |  |  |   screenshot: [ 'off', { scope: 'worker' } ], | 
					
						
							|  |  |  |   video: [ 'off', { scope: 'worker' } ], | 
					
						
							|  |  |  |   trace: [ 'off', { scope: 'worker' } ], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _artifactsDir: [async ({}, use, workerInfo) => { | 
					
						
							|  |  |  |     let dir: string | undefined; | 
					
						
							|  |  |  |     await use(() => { | 
					
						
							|  |  |  |       if (!dir) { | 
					
						
							|  |  |  |         dir = path.join(workerInfo.project.outputDir, '.playwright-artifacts-' + workerInfo.workerIndex); | 
					
						
							|  |  |  |         fs.mkdirSync(dir, { recursive: true }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return dir; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     if (dir) | 
					
						
							|  |  |  |       await removeFolders([dir]); | 
					
						
							|  |  |  |   }, { scope: 'worker' }], | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-09 18:09:11 -07:00
										 |  |  |   _browserType: [async ({ playwright, browserName, headless, channel, launchOptions }, use) => { | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  |     if (!['chromium', 'firefox', 'webkit'].includes(browserName)) | 
					
						
							|  |  |  |       throw new Error(`Unexpected browserName "${browserName}", must be one of "chromium", "firefox" or "webkit"`); | 
					
						
							| 
									
										
										
										
											2021-08-09 18:09:11 -07:00
										 |  |  |     const browserType = playwright[browserName]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  |     const options: LaunchOptions = { | 
					
						
							|  |  |  |       handleSIGINT: false, | 
					
						
							| 
									
										
										
										
											2021-06-24 23:28:01 -07:00
										 |  |  |       timeout: 0, | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  |       ...launchOptions, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     if (headless !== undefined) | 
					
						
							|  |  |  |       options.headless = headless; | 
					
						
							|  |  |  |     if (channel !== undefined) | 
					
						
							|  |  |  |       options.channel = channel; | 
					
						
							| 
									
										
										
										
											2021-08-09 18:09:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     (browserType as any)._defaultLaunchOptions = options; | 
					
						
							|  |  |  |     await use(browserType); | 
					
						
							|  |  |  |     (browserType as any)._defaultLaunchOptions = undefined; | 
					
						
							|  |  |  |   }, { scope: 'worker' }], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   browser: [ async ({ _browserType }, use) => { | 
					
						
							|  |  |  |     const browser = await _browserType.launch(); | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  |     await use(browser); | 
					
						
							|  |  |  |     await browser.close(); | 
					
						
							|  |  |  |   }, { scope: 'worker' } ], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   acceptDownloads: undefined, | 
					
						
							|  |  |  |   bypassCSP: undefined, | 
					
						
							|  |  |  |   colorScheme: undefined, | 
					
						
							|  |  |  |   deviceScaleFactor: undefined, | 
					
						
							|  |  |  |   extraHTTPHeaders: undefined, | 
					
						
							|  |  |  |   geolocation: undefined, | 
					
						
							|  |  |  |   hasTouch: undefined, | 
					
						
							|  |  |  |   httpCredentials: undefined, | 
					
						
							|  |  |  |   ignoreHTTPSErrors: undefined, | 
					
						
							|  |  |  |   isMobile: undefined, | 
					
						
							|  |  |  |   javaScriptEnabled: undefined, | 
					
						
							|  |  |  |   locale: undefined, | 
					
						
							|  |  |  |   offline: undefined, | 
					
						
							|  |  |  |   permissions: undefined, | 
					
						
							|  |  |  |   proxy: undefined, | 
					
						
							|  |  |  |   storageState: undefined, | 
					
						
							|  |  |  |   timezoneId: undefined, | 
					
						
							|  |  |  |   userAgent: undefined, | 
					
						
							|  |  |  |   viewport: undefined, | 
					
						
							| 
									
										
										
										
											2021-07-29 21:03:50 -07:00
										 |  |  |   actionTimeout: undefined, | 
					
						
							|  |  |  |   navigationTimeout: undefined, | 
					
						
							| 
									
										
										
										
											2021-07-07 20:19:42 +02:00
										 |  |  |   baseURL: async ({ }, use) => { | 
					
						
							|  |  |  |     await use(process.env.PLAYWRIGHT_TEST_BASE_URL); | 
					
						
							|  |  |  |   }, | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  |   contextOptions: {}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-09 18:09:11 -07:00
										 |  |  |   _combinedContextOptions: async ({ | 
					
						
							| 
									
										
										
										
											2021-07-29 21:03:50 -07:00
										 |  |  |     acceptDownloads, | 
					
						
							|  |  |  |     bypassCSP, | 
					
						
							|  |  |  |     colorScheme, | 
					
						
							|  |  |  |     deviceScaleFactor, | 
					
						
							|  |  |  |     extraHTTPHeaders, | 
					
						
							|  |  |  |     hasTouch, | 
					
						
							|  |  |  |     geolocation, | 
					
						
							|  |  |  |     httpCredentials, | 
					
						
							|  |  |  |     ignoreHTTPSErrors, | 
					
						
							|  |  |  |     isMobile, | 
					
						
							|  |  |  |     javaScriptEnabled, | 
					
						
							|  |  |  |     locale, | 
					
						
							|  |  |  |     offline, | 
					
						
							|  |  |  |     permissions, | 
					
						
							|  |  |  |     proxy, | 
					
						
							|  |  |  |     storageState, | 
					
						
							|  |  |  |     viewport, | 
					
						
							|  |  |  |     timezoneId, | 
					
						
							|  |  |  |     userAgent, | 
					
						
							|  |  |  |     baseURL, | 
					
						
							|  |  |  |     contextOptions, | 
					
						
							| 
									
										
										
										
											2021-08-09 18:09:11 -07:00
										 |  |  |   }, use) => { | 
					
						
							| 
									
										
										
										
											2021-07-29 14:03:58 -07:00
										 |  |  |     const options: BrowserContextOptions = {}; | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  |     if (acceptDownloads !== undefined) | 
					
						
							|  |  |  |       options.acceptDownloads = acceptDownloads; | 
					
						
							|  |  |  |     if (bypassCSP !== undefined) | 
					
						
							|  |  |  |       options.bypassCSP = bypassCSP; | 
					
						
							|  |  |  |     if (colorScheme !== undefined) | 
					
						
							|  |  |  |       options.colorScheme = colorScheme; | 
					
						
							|  |  |  |     if (deviceScaleFactor !== undefined) | 
					
						
							|  |  |  |       options.deviceScaleFactor = deviceScaleFactor; | 
					
						
							|  |  |  |     if (extraHTTPHeaders !== undefined) | 
					
						
							|  |  |  |       options.extraHTTPHeaders = extraHTTPHeaders; | 
					
						
							|  |  |  |     if (geolocation !== undefined) | 
					
						
							|  |  |  |       options.geolocation = geolocation; | 
					
						
							|  |  |  |     if (hasTouch !== undefined) | 
					
						
							|  |  |  |       options.hasTouch = hasTouch; | 
					
						
							|  |  |  |     if (httpCredentials !== undefined) | 
					
						
							|  |  |  |       options.httpCredentials = httpCredentials; | 
					
						
							|  |  |  |     if (ignoreHTTPSErrors !== undefined) | 
					
						
							|  |  |  |       options.ignoreHTTPSErrors = ignoreHTTPSErrors; | 
					
						
							|  |  |  |     if (isMobile !== undefined) | 
					
						
							|  |  |  |       options.isMobile = isMobile; | 
					
						
							|  |  |  |     if (javaScriptEnabled !== undefined) | 
					
						
							|  |  |  |       options.javaScriptEnabled = javaScriptEnabled; | 
					
						
							|  |  |  |     if (locale !== undefined) | 
					
						
							|  |  |  |       options.locale = locale; | 
					
						
							|  |  |  |     if (offline !== undefined) | 
					
						
							|  |  |  |       options.offline = offline; | 
					
						
							|  |  |  |     if (permissions !== undefined) | 
					
						
							|  |  |  |       options.permissions = permissions; | 
					
						
							|  |  |  |     if (proxy !== undefined) | 
					
						
							|  |  |  |       options.proxy = proxy; | 
					
						
							|  |  |  |     if (storageState !== undefined) | 
					
						
							|  |  |  |       options.storageState = storageState; | 
					
						
							|  |  |  |     if (timezoneId !== undefined) | 
					
						
							|  |  |  |       options.timezoneId = timezoneId; | 
					
						
							|  |  |  |     if (userAgent !== undefined) | 
					
						
							|  |  |  |       options.userAgent = userAgent; | 
					
						
							|  |  |  |     if (viewport !== undefined) | 
					
						
							|  |  |  |       options.viewport = viewport; | 
					
						
							| 
									
										
										
										
											2021-07-07 20:19:42 +02:00
										 |  |  |     if (baseURL !== undefined) | 
					
						
							|  |  |  |       options.baseURL = baseURL; | 
					
						
							| 
									
										
										
										
											2021-08-09 18:09:11 -07:00
										 |  |  |     await use({ | 
					
						
							|  |  |  |       ...contextOptions, | 
					
						
							|  |  |  |       ...options, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _setupContextOptionsAndArtifacts: [async ({ _browserType, _combinedContextOptions, _artifactsDir, trace, screenshot, actionTimeout, navigationTimeout }, use, testInfo) => { | 
					
						
							|  |  |  |     testInfo.snapshotSuffix = process.platform; | 
					
						
							|  |  |  |     if (process.env.PWDEBUG) | 
					
						
							|  |  |  |       testInfo.setTimeout(0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (trace === 'retry-with-trace') | 
					
						
							|  |  |  |       trace = 'on-first-retry'; | 
					
						
							|  |  |  |     const captureTrace = (trace === 'on' || trace === 'retain-on-failure' || (trace === 'on-first-retry' && testInfo.retry === 1)); | 
					
						
							|  |  |  |     const temporaryTraceFiles: string[] = []; | 
					
						
							|  |  |  |     const temporaryScreenshots: string[] = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const onDidCreateContext = async (context: BrowserContext) => { | 
					
						
							|  |  |  |       context.setDefaultTimeout(actionTimeout || 0); | 
					
						
							|  |  |  |       context.setDefaultNavigationTimeout(navigationTimeout || actionTimeout || 0); | 
					
						
							|  |  |  |       if (captureTrace) | 
					
						
							|  |  |  |         await context.tracing.start({ screenshots: true, snapshots: true }); | 
					
						
							|  |  |  |       (context as any)._csi = { | 
					
						
							|  |  |  |         onApiCall: (name: string) => { | 
					
						
							|  |  |  |           return (testInfo as any)._addStep('pw:api', name); | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const onWillCloseContext = async (context: BrowserContext) => { | 
					
						
							|  |  |  |       if (captureTrace) { | 
					
						
							|  |  |  |         // Export trace for now. We'll know whether we have to preserve it
 | 
					
						
							|  |  |  |         // after the test finishes.
 | 
					
						
							|  |  |  |         const tracePath = path.join(_artifactsDir(), createGuid() + '.zip'); | 
					
						
							|  |  |  |         temporaryTraceFiles.push(tracePath); | 
					
						
							|  |  |  |         await (context.tracing as any)._export({ path: tracePath }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (screenshot === 'on' || screenshot === 'only-on-failure') { | 
					
						
							|  |  |  |         // Capture screenshot for now. We'll know whether we have to preserve them
 | 
					
						
							|  |  |  |         // after the test finishes.
 | 
					
						
							|  |  |  |         await Promise.all(context.pages().map(async page => { | 
					
						
							|  |  |  |           const screenshotPath = path.join(_artifactsDir(), createGuid() + '.png'); | 
					
						
							|  |  |  |           temporaryScreenshots.push(screenshotPath); | 
					
						
							|  |  |  |           await page.screenshot({ timeout: 5000, path: screenshotPath }).catch(() => {}); | 
					
						
							|  |  |  |         })); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 1. Setup instrumentation and process existing contexts.
 | 
					
						
							|  |  |  |     const oldOnDidCreateContext = (_browserType as any)._onDidCreateContext; | 
					
						
							|  |  |  |     (_browserType as any)._onDidCreateContext = onDidCreateContext; | 
					
						
							|  |  |  |     (_browserType as any)._onWillCloseContext = onWillCloseContext; | 
					
						
							|  |  |  |     (_browserType as any)._defaultContextOptions = _combinedContextOptions; | 
					
						
							|  |  |  |     const existingContexts = Array.from((_browserType as any)._contexts) as BrowserContext[]; | 
					
						
							|  |  |  |     await Promise.all(existingContexts.map(onDidCreateContext)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 2. Run the test.
 | 
					
						
							|  |  |  |     await use(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 3. Determine whether we need the artifacts.
 | 
					
						
							|  |  |  |     const testFailed = testInfo.status !== testInfo.expectedStatus; | 
					
						
							|  |  |  |     const isHook = testInfo.title === 'beforeAll' || testInfo.title === 'afterAll'; | 
					
						
							|  |  |  |     const preserveTrace = captureTrace && !isHook && (trace === 'on' || (testFailed && trace === 'retain-on-failure') || (trace === 'on-first-retry' && testInfo.retry === 1)); | 
					
						
							|  |  |  |     const captureScreenshots = !isHook && (screenshot === 'on' || (screenshot === 'only-on-failure' && testFailed)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const traceAttachments: string[] = []; | 
					
						
							|  |  |  |     const addTraceAttachment = () => { | 
					
						
							|  |  |  |       const tracePath = testInfo.outputPath(`trace${traceAttachments.length ? '-' + traceAttachments.length : ''}.zip`); | 
					
						
							|  |  |  |       traceAttachments.push(tracePath); | 
					
						
							|  |  |  |       testInfo.attachments.push({ name: 'trace', path: tracePath, contentType: 'application/zip' }); | 
					
						
							|  |  |  |       return tracePath; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const screenshotAttachments: string[] = []; | 
					
						
							|  |  |  |     const addScreenshotAttachment = () => { | 
					
						
							|  |  |  |       const screenshotPath = testInfo.outputPath(`test-${testFailed ? 'failed' : 'finished'}-${screenshotAttachments.length + 1}.png`); | 
					
						
							|  |  |  |       screenshotAttachments.push(screenshotPath); | 
					
						
							|  |  |  |       testInfo.attachments.push({ name: 'screenshot', path: screenshotPath, contentType: 'image/png' }); | 
					
						
							|  |  |  |       return screenshotPath; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 4. Cleanup instrumentation.
 | 
					
						
							|  |  |  |     const leftoverContexts = Array.from((_browserType as any)._contexts) as BrowserContext[]; | 
					
						
							|  |  |  |     (_browserType as any)._onDidCreateContext = oldOnDidCreateContext; | 
					
						
							|  |  |  |     (_browserType as any)._onWillCloseContext = undefined; | 
					
						
							|  |  |  |     (_browserType as any)._defaultContextOptions = undefined; | 
					
						
							|  |  |  |     leftoverContexts.forEach(context => (context as any)._csi = undefined); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 5. Collect artifacts from any non-closed contexts.
 | 
					
						
							|  |  |  |     await Promise.all(leftoverContexts.map(async context => { | 
					
						
							|  |  |  |       if (preserveTrace) | 
					
						
							|  |  |  |         await (context.tracing as any)._export({ path: addTraceAttachment() }); | 
					
						
							|  |  |  |       if (captureScreenshots) | 
					
						
							|  |  |  |         await Promise.all(context.pages().map(page => page.screenshot({ timeout: 5000, path: addScreenshotAttachment() }).catch(() => {}))); | 
					
						
							|  |  |  |     })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 6. Either remove or attach temporary traces and screenshots for contexts closed
 | 
					
						
							|  |  |  |     // before the test has finished.
 | 
					
						
							|  |  |  |     await Promise.all(temporaryTraceFiles.map(async file => { | 
					
						
							|  |  |  |       if (preserveTrace) | 
					
						
							|  |  |  |         await fs.promises.rename(file, addTraceAttachment()).catch(() => {}); | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         await fs.promises.unlink(file).catch(() => {}); | 
					
						
							|  |  |  |     })); | 
					
						
							|  |  |  |     await Promise.all(temporaryScreenshots.map(async file => { | 
					
						
							|  |  |  |       if (captureScreenshots) | 
					
						
							|  |  |  |         await fs.promises.rename(file, addScreenshotAttachment()).catch(() => {}); | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         await fs.promises.unlink(file).catch(() => {}); | 
					
						
							|  |  |  |     })); | 
					
						
							|  |  |  |   }, { auto: true }], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   createContext: async ({ browser, video, _artifactsDir }, use, testInfo) => { | 
					
						
							|  |  |  |     let videoMode = typeof video === 'string' ? video : video.mode; | 
					
						
							|  |  |  |     if (videoMode === 'retry-with-video') | 
					
						
							|  |  |  |       videoMode = 'on-first-retry'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const captureVideo = (videoMode === 'on' || videoMode === 'retain-on-failure' || (videoMode === 'on-first-retry' && testInfo.retry === 1)); | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-29 14:03:58 -07:00
										 |  |  |     const allContexts: BrowserContext[] = []; | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  |     const allPages: Page[] = []; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-29 14:03:58 -07:00
										 |  |  |     await use(async (additionalOptions = {}) => { | 
					
						
							|  |  |  |       let recordVideoDir: string | null = null; | 
					
						
							|  |  |  |       const recordVideoSize = typeof video === 'string' ? undefined : video.size; | 
					
						
							| 
									
										
										
										
											2021-08-09 18:09:11 -07:00
										 |  |  |       if (captureVideo) | 
					
						
							|  |  |  |         recordVideoDir = _artifactsDir(); | 
					
						
							| 
									
										
										
										
											2021-07-29 14:03:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-30 16:07:02 -07:00
										 |  |  |       const combinedOptions: BrowserContextOptions = { | 
					
						
							| 
									
										
										
										
											2021-07-29 14:03:58 -07:00
										 |  |  |         recordVideo: recordVideoDir ? { dir: recordVideoDir, size: recordVideoSize } : undefined, | 
					
						
							|  |  |  |         ...additionalOptions, | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       const context = await browser.newContext(combinedOptions); | 
					
						
							|  |  |  |       context.on('page', page => allPages.push(page)); | 
					
						
							| 
									
										
										
										
											2021-06-08 11:22:07 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-29 14:03:58 -07:00
										 |  |  |       allContexts.push(context); | 
					
						
							|  |  |  |       return context; | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-09 18:09:11 -07:00
										 |  |  |     const prependToError = (testInfo.status === 'timedOut' && allContexts.length) ? | 
					
						
							| 
									
										
										
										
											2021-07-29 14:03:58 -07:00
										 |  |  |       formatPendingCalls((allContexts[0] as any)._connection.pendingProtocolCalls()) : ''; | 
					
						
							|  |  |  |     await Promise.all(allContexts.map(context => context.close())); | 
					
						
							| 
									
										
										
										
											2021-06-17 15:09:38 -07:00
										 |  |  |     if (prependToError) { | 
					
						
							|  |  |  |       if (!testInfo.error) { | 
					
						
							|  |  |  |         testInfo.error = { value: prependToError }; | 
					
						
							|  |  |  |       } else if (testInfo.error.message) { | 
					
						
							|  |  |  |         testInfo.error.message = prependToError + testInfo.error.message; | 
					
						
							|  |  |  |         if (testInfo.error.stack) | 
					
						
							|  |  |  |           testInfo.error.stack = prependToError + testInfo.error.stack; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-09 18:09:11 -07:00
										 |  |  |     const testFailed = testInfo.status !== testInfo.expectedStatus; | 
					
						
							| 
									
										
										
										
											2021-06-16 16:05:30 -07:00
										 |  |  |     const preserveVideo = captureVideo && (videoMode === 'on' || (testFailed && videoMode === 'retain-on-failure') || (videoMode === 'on-first-retry' && testInfo.retry === 1)); | 
					
						
							|  |  |  |     if (preserveVideo) { | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  |       await Promise.all(allPages.map(async page => { | 
					
						
							| 
									
										
										
										
											2021-06-16 07:51:54 -07:00
										 |  |  |         const v = page.video(); | 
					
						
							|  |  |  |         if (!v) | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  |           return; | 
					
						
							| 
									
										
										
										
											2021-06-10 22:23:02 -07:00
										 |  |  |         try { | 
					
						
							| 
									
										
										
										
											2021-06-16 07:51:54 -07:00
										 |  |  |           const videoPath = await v.path(); | 
					
						
							| 
									
										
										
										
											2021-08-03 17:07:57 -07:00
										 |  |  |           const savedPath = testInfo.outputPath(path.basename(videoPath)); | 
					
						
							|  |  |  |           await v.saveAs(savedPath); | 
					
						
							|  |  |  |           testInfo.attachments.push({ name: 'video', path: savedPath, contentType: 'video/webm' }); | 
					
						
							| 
									
										
										
										
											2021-06-10 22:23:02 -07:00
										 |  |  |         } catch (e) { | 
					
						
							| 
									
										
										
										
											2021-06-15 10:06:49 -07:00
										 |  |  |           // Silent catch empty videos.
 | 
					
						
							| 
									
										
										
										
											2021-06-10 22:23:02 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  |       })); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-09 18:09:11 -07:00
										 |  |  |   context: async ({ createContext }, use, testInfo) => { | 
					
						
							|  |  |  |     if (testInfo.title === 'beforeAll' || testInfo.title === 'afterAll') | 
					
						
							|  |  |  |       throw new Error(`"context" and "page" fixtures are not suppoted in ${testInfo.title}. Use browser.newContext() instead.`); | 
					
						
							| 
									
										
										
										
											2021-07-29 14:03:58 -07:00
										 |  |  |     await use(await createContext()); | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-03 08:07:55 -07:00
										 |  |  |   page: async ({ context }, use) => { | 
					
						
							|  |  |  |     await use(await context.newPage()); | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | export default test; | 
					
						
							| 
									
										
										
										
											2021-06-17 15:09:38 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-19 12:20:24 -05:00
										 |  |  | function formatPendingCalls(calls: ProtocolCall[]) { | 
					
						
							| 
									
										
										
										
											2021-06-17 15:09:38 -07:00
										 |  |  |   if (!calls.length) | 
					
						
							|  |  |  |     return ''; | 
					
						
							|  |  |  |   return 'Pending operations:\n' + calls.map(call => { | 
					
						
							| 
									
										
										
										
											2021-07-19 12:20:24 -05:00
										 |  |  |     const frame = call.stack && call.stack[0] ? formatStackFrame(call.stack[0]) : '<unknown>'; | 
					
						
							| 
									
										
										
										
											2021-06-17 15:09:38 -07:00
										 |  |  |     return `  - ${call.apiName} at ${frame}\n`; | 
					
						
							|  |  |  |   }).join('') + '\n'; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-19 12:20:24 -05:00
										 |  |  | function formatStackFrame(frame: StackFrame) { | 
					
						
							|  |  |  |   const file = path.relative(process.cwd(), frame.file) || path.basename(frame.file); | 
					
						
							| 
									
										
										
										
											2021-06-17 15:09:38 -07:00
										 |  |  |   return `${file}:${frame.line || 1}:${frame.column || 1}`; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type StackFrame = { | 
					
						
							|  |  |  |   file: string, | 
					
						
							|  |  |  |   line?: number, | 
					
						
							|  |  |  |   column?: number, | 
					
						
							|  |  |  |   function?: string, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type ProtocolCall = { | 
					
						
							|  |  |  |   stack?: StackFrame[], | 
					
						
							|  |  |  |   apiName?: string, | 
					
						
							|  |  |  | }; |