| 
									
										
										
										
											2023-03-12 15:18:47 -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'; | 
					
						
							|  |  |  | import * as os from 'os'; | 
					
						
							|  |  |  | import * as path from 'path'; | 
					
						
							|  |  |  | import type { TestChildProcess } from '../config/commonFixtures'; | 
					
						
							| 
									
										
										
										
											2023-07-18 10:58:07 -07:00
										 |  |  | import { cleanEnv, cliEntrypoint, test as base, writeFiles } from './playwright-test-fixtures'; | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  | import type { Files, RunOptions } from './playwright-test-fixtures'; | 
					
						
							| 
									
										
										
										
											2023-07-19 17:50:25 +02:00
										 |  |  | import type { Browser, BrowserType, Page, TestInfo } from './stable-test-runner'; | 
					
						
							| 
									
										
										
										
											2023-03-20 13:45:35 -07:00
										 |  |  | import { createGuid } from '../../packages/playwright-core/src/utils/crypto'; | 
					
						
							| 
									
										
										
										
											2023-10-26 19:41:00 +02:00
										 |  |  | import { removeFolders } from '../../packages/playwright-core/src/utils/fileUtils'; | 
					
						
							| 
									
										
										
										
											2023-03-20 13:45:35 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | type Latch = { | 
					
						
							|  |  |  |   blockingCode: string; | 
					
						
							|  |  |  |   open: () => void; | 
					
						
							| 
									
										
										
										
											2023-03-20 17:12:02 -07:00
										 |  |  |   close: () => void; | 
					
						
							| 
									
										
										
										
											2023-03-20 13:45:35 -07:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-19 17:50:25 +02:00
										 |  |  | type UIModeOptions = RunOptions & { | 
					
						
							|  |  |  |   useWeb?: boolean | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  | type Fixtures = { | 
					
						
							| 
									
										
										
										
											2023-07-19 17:50:25 +02:00
										 |  |  |   runUITest: (files: Files, env?: NodeJS.ProcessEnv, options?: UIModeOptions) => Promise<{ page: Page, testProcess: TestChildProcess }>; | 
					
						
							| 
									
										
										
										
											2023-03-20 13:45:35 -07:00
										 |  |  |   createLatch: () => Latch; | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 17:12:02 -07:00
										 |  |  | export function dumpTestTree(page: Page, options: { time?: boolean } = {}): () => Promise<string> { | 
					
						
							|  |  |  |   return () => page.getByTestId('test-tree').evaluate(async (treeElement, options) => { | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  |     function iconName(iconElement: Element): string { | 
					
						
							|  |  |  |       const icon = iconElement.className.replace('codicon codicon-', ''); | 
					
						
							|  |  |  |       if (icon === 'chevron-right') | 
					
						
							|  |  |  |         return '►'; | 
					
						
							|  |  |  |       if (icon === 'chevron-down') | 
					
						
							|  |  |  |         return '▼'; | 
					
						
							|  |  |  |       if (icon === 'blank') | 
					
						
							|  |  |  |         return ' '; | 
					
						
							|  |  |  |       if (icon === 'circle-outline') | 
					
						
							|  |  |  |         return '◯'; | 
					
						
							| 
									
										
										
										
											2023-03-13 12:14:51 -07:00
										 |  |  |       if (icon === 'circle-slash') | 
					
						
							|  |  |  |         return '⊘'; | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  |       if (icon === 'check') | 
					
						
							|  |  |  |         return '✅'; | 
					
						
							|  |  |  |       if (icon === 'error') | 
					
						
							|  |  |  |         return '❌'; | 
					
						
							| 
									
										
										
										
											2023-03-13 12:14:51 -07:00
										 |  |  |       if (icon === 'eye') | 
					
						
							|  |  |  |         return '👁'; | 
					
						
							|  |  |  |       if (icon === 'loading') | 
					
						
							|  |  |  |         return '↻'; | 
					
						
							| 
									
										
										
										
											2023-03-20 17:12:02 -07:00
										 |  |  |       if (icon === 'clock') | 
					
						
							|  |  |  |         return '🕦'; | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  |       return icon; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const result: string[] = []; | 
					
						
							|  |  |  |     const listItems = treeElement.querySelectorAll('[role=listitem]'); | 
					
						
							|  |  |  |     for (const listItem of listItems) { | 
					
						
							|  |  |  |       const iconElements = listItem.querySelectorAll('.codicon'); | 
					
						
							|  |  |  |       const treeIcon = iconName(iconElements[0]); | 
					
						
							|  |  |  |       const statusIcon = iconName(iconElements[1]); | 
					
						
							|  |  |  |       const indent = listItem.querySelectorAll('.list-view-indent').length; | 
					
						
							| 
									
										
										
										
											2023-03-13 12:14:51 -07:00
										 |  |  |       const watch = listItem.querySelector('.toolbar-button.eye.toggled') ? ' 👁' : ''; | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  |       const selected = listItem.classList.contains('selected') ? ' <=' : ''; | 
					
						
							| 
									
										
										
										
											2024-03-26 01:06:22 +01:00
										 |  |  |       const title = listItem.querySelector('.ui-mode-list-item-title').childNodes[0].textContent; | 
					
						
							| 
									
										
										
										
											2023-04-19 16:51:42 -07:00
										 |  |  |       const timeElement = options.time ? listItem.querySelector('.ui-mode-list-item-time') : undefined; | 
					
						
							| 
									
										
										
										
											2023-08-25 12:43:50 -07:00
										 |  |  |       const time = timeElement ? ' ' + timeElement.textContent.replace(/[.\d]+m?s/, 'XXms') : ''; | 
					
						
							| 
									
										
										
										
											2023-03-20 17:12:02 -07:00
										 |  |  |       result.push('    ' + '  '.repeat(indent) + treeIcon + ' ' + statusIcon + ' ' + title + time + watch + selected); | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     return '\n' + result.join('\n') + '\n  '; | 
					
						
							| 
									
										
										
										
											2023-03-20 17:12:02 -07:00
										 |  |  |   }, options); | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const test = base | 
					
						
							|  |  |  |     .extend<Fixtures>({ | 
					
						
							| 
									
										
										
										
											2023-07-19 17:50:25 +02:00
										 |  |  |       runUITest: async ({ childProcess, headless }, use, testInfo: TestInfo) => { | 
					
						
							| 
									
										
										
										
											2023-03-19 14:50:09 -07:00
										 |  |  |         if (process.env.CI) | 
					
						
							|  |  |  |           testInfo.slow(); | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  |         const cacheDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'playwright-test-cache-')); | 
					
						
							|  |  |  |         let testProcess: TestChildProcess | undefined; | 
					
						
							|  |  |  |         let browser: Browser | undefined; | 
					
						
							| 
									
										
										
										
											2023-07-19 17:50:25 +02:00
										 |  |  |         await use(async (files: Files, env: NodeJS.ProcessEnv = {}, options: UIModeOptions = {}) => { | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  |           const baseDir = await writeFiles(testInfo, files, true); | 
					
						
							|  |  |  |           testProcess = childProcess({ | 
					
						
							| 
									
										
										
										
											2023-07-19 17:50:25 +02:00
										 |  |  |             command: ['node', cliEntrypoint, 'test', (options.useWeb ? '--ui-host=127.0.0.1' : '--ui'), '--workers=1', ...(options.additionalArgs || [])], | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  |             env: { | 
					
						
							|  |  |  |               ...cleanEnv(env), | 
					
						
							|  |  |  |               PWTEST_UNDER_TEST: '1', | 
					
						
							|  |  |  |               PWTEST_CACHE_DIR: cacheDir, | 
					
						
							|  |  |  |               PWTEST_HEADED_FOR_TEST: headless ? '0' : '1', | 
					
						
							| 
									
										
										
										
											2023-03-17 16:15:20 +01:00
										 |  |  |               PWTEST_PRINT_WS_ENDPOINT: '1', | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  |             }, | 
					
						
							|  |  |  |             cwd: options.cwd ? path.resolve(baseDir, options.cwd) : baseDir, | 
					
						
							|  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2023-07-19 17:50:25 +02:00
										 |  |  |           let page: Page; | 
					
						
							|  |  |  |           // We want to have ToT playwright-core here, since we install it's browsers and otherwise
 | 
					
						
							|  |  |  |           // don't have the right browser revision (ToT revisions != stable-test-runner revisions).
 | 
					
						
							|  |  |  |           const chromium: BrowserType = require('../../packages/playwright-core').chromium; | 
					
						
							|  |  |  |           if (options.useWeb) { | 
					
						
							|  |  |  |             await testProcess.waitForOutput('Listening on'); | 
					
						
							|  |  |  |             const line = testProcess.output.split('\n').find(l => l.includes('Listening on')); | 
					
						
							|  |  |  |             const uiAddress = line!.split(' ')[2]; | 
					
						
							|  |  |  |             browser = await chromium.launch(); | 
					
						
							|  |  |  |             page = await browser.newPage(); | 
					
						
							|  |  |  |             await page.goto(uiAddress); | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             await testProcess.waitForOutput('DevTools listening on'); | 
					
						
							|  |  |  |             const line = testProcess.output.split('\n').find(l => l.includes('DevTools listening on')); | 
					
						
							|  |  |  |             const wsEndpoint = line!.split(' ')[3]; | 
					
						
							|  |  |  |             browser = await chromium.connectOverCDP(wsEndpoint); | 
					
						
							|  |  |  |             const [context] = browser.contexts(); | 
					
						
							|  |  |  |             [page] = context.pages(); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2023-03-29 13:57:19 -07:00
										 |  |  |           return { page, testProcess }; | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  |         }); | 
					
						
							|  |  |  |         await browser?.close(); | 
					
						
							| 
									
										
										
										
											2023-04-12 16:37:24 +00:00
										 |  |  |         await testProcess?.kill('SIGINT'); | 
					
						
							| 
									
										
										
										
											2023-10-26 19:41:00 +02:00
										 |  |  |         await removeFolders([cacheDir]); | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  |       }, | 
					
						
							| 
									
										
										
										
											2023-03-20 13:45:35 -07:00
										 |  |  |       createLatch: async ({}, use, testInfo) => { | 
					
						
							|  |  |  |         await use(() => { | 
					
						
							|  |  |  |           const latchFile = path.join(testInfo.project.outputDir, createGuid() + '.latch'); | 
					
						
							|  |  |  |           return { | 
					
						
							|  |  |  |             blockingCode: `await ((${waitForLatch})(${JSON.stringify(latchFile)}))`, | 
					
						
							|  |  |  |             open: () => fs.writeFileSync(latchFile, 'ok'), | 
					
						
							| 
									
										
										
										
											2023-03-20 17:12:02 -07:00
										 |  |  |             close: () => fs.unlinkSync(latchFile), | 
					
						
							| 
									
										
										
										
											2023-03-20 13:45:35 -07:00
										 |  |  |           }; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2023-03-12 15:18:47 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-28 14:18:46 -07:00
										 |  |  | import { expect as baseExpect } from './stable-test-runner'; | 
					
						
							| 
									
										
										
										
											2023-05-05 15:12:18 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Slow tests are 90s.
 | 
					
						
							|  |  |  | export const expect = baseExpect.configure({ timeout: process.env.CI ? 75000 : 25000 }); | 
					
						
							| 
									
										
										
										
											2023-05-18 11:28:28 -07:00
										 |  |  | export const retries = process.env.CI ? 3 : 0; | 
					
						
							| 
									
										
										
										
											2023-03-20 13:45:35 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | async function waitForLatch(latchFile: string) { | 
					
						
							|  |  |  |   const fs = require('fs'); | 
					
						
							|  |  |  |   while (!fs.existsSync(latchFile)) | 
					
						
							|  |  |  |     await new Promise(f => setTimeout(f, 250)); | 
					
						
							|  |  |  | } |