| 
									
										
										
										
											2023-04-07 13:47:52 -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 type { HttpServer } from '../../packages/playwright-core/src/utils'; | 
					
						
							|  |  |  |  | import { startHtmlReportServer } from '../../packages/playwright-test/lib/reporters/html'; | 
					
						
							|  |  |  |  | import { type CliRunResult, type RunOptions, stripAnsi } from './playwright-test-fixtures'; | 
					
						
							|  |  |  |  | import { cleanEnv, cliEntrypoint, expect, test as baseTest } from './playwright-test-fixtures'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const DOES_NOT_SUPPORT_UTF8_IN_TERMINAL = process.platform === 'win32' && process.env.TERM_PROGRAM !== 'vscode' && !process.env.WT_SESSION; | 
					
						
							|  |  |  |  | const POSITIVE_STATUS_MARK = DOES_NOT_SUPPORT_UTF8_IN_TERMINAL ? 'ok' : '✓ '; | 
					
						
							|  |  |  |  | const NEGATIVE_STATUS_MARK = DOES_NOT_SUPPORT_UTF8_IN_TERMINAL ? 'x ' : '✘ '; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const test = baseTest.extend<{ | 
					
						
							|  |  |  |  |   showReport: (reportFolder?: string) => Promise<void>, | 
					
						
							|  |  |  |  |   mergeReports: (reportFolder: string, env?: NodeJS.ProcessEnv, options?: RunOptions) => Promise<CliRunResult> | 
					
						
							|  |  |  |  |       }>({ | 
					
						
							| 
									
										
										
										
											2023-04-24 17:34:09 -07:00
										 |  |  |  |         showReport: async ({ page }, use) => { | 
					
						
							| 
									
										
										
										
											2023-04-07 13:47:52 -07:00
										 |  |  |  |           let server: HttpServer | undefined; | 
					
						
							|  |  |  |  |           await use(async (reportFolder?: string) => { | 
					
						
							| 
									
										
										
										
											2023-04-24 17:34:09 -07:00
										 |  |  |  |             reportFolder ??=  test.info().outputPath('playwright-report'); | 
					
						
							| 
									
										
										
										
											2023-04-07 13:47:52 -07:00
										 |  |  |  |             server = startHtmlReportServer(reportFolder) as HttpServer; | 
					
						
							|  |  |  |  |             const location = await server.start(); | 
					
						
							|  |  |  |  |             await page.goto(location); | 
					
						
							|  |  |  |  |           }); | 
					
						
							|  |  |  |  |           await server?.stop(); | 
					
						
							|  |  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2023-04-24 17:34:09 -07:00
										 |  |  |  |         mergeReports: async ({ childProcess }, use) => { | 
					
						
							| 
									
										
										
										
											2023-04-07 13:47:52 -07:00
										 |  |  |  |           await use(async (reportFolder: string, env: NodeJS.ProcessEnv = {}, options: RunOptions = {}) => { | 
					
						
							|  |  |  |  |             const command = ['node', cliEntrypoint, 'merge-reports', reportFolder]; | 
					
						
							|  |  |  |  |             if (options.additionalArgs) | 
					
						
							|  |  |  |  |               command.push(...options.additionalArgs); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             const testProcess = childProcess({ | 
					
						
							|  |  |  |  |               command, | 
					
						
							|  |  |  |  |               env: cleanEnv(env), | 
					
						
							| 
									
										
										
										
											2023-04-24 17:34:09 -07:00
										 |  |  |  |               cwd: test.info().outputDir, | 
					
						
							| 
									
										
										
										
											2023-04-07 13:47:52 -07:00
										 |  |  |  |             }); | 
					
						
							|  |  |  |  |             const { exitCode } = await testProcess.exited; | 
					
						
							|  |  |  |  |             return { exitCode, output: testProcess.output.toString() }; | 
					
						
							|  |  |  |  |           }); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-07 17:48:54 -07:00
										 |  |  |  | test.use({ channel: 'chrome' }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-01 12:58:40 -07:00
										 |  |  |  | const echoReporterJs = `
 | 
					
						
							|  |  |  |  | class EchoReporter { | 
					
						
							|  |  |  |  |   onBegin(config, suite) { | 
					
						
							|  |  |  |  |     console.log('onBegin'); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   onTestBegin(test, result) { | 
					
						
							|  |  |  |  |     console.log('onTestBegin'); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   onStdOut(chunk, test, result) { | 
					
						
							|  |  |  |  |     console.log('onStdOut'); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   onStdErr(chunk, test, result) { | 
					
						
							|  |  |  |  |     console.log('onStdErr'); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   onTestEnd(test, result) { | 
					
						
							|  |  |  |  |     console.log('onTestEnd'); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   onEnd(result) { | 
					
						
							|  |  |  |  |     console.log('onEnd'); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   onExit() { | 
					
						
							|  |  |  |  |     console.log('onExit'); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   onError(error) { | 
					
						
							|  |  |  |  |     console.log('onError'); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   onStepBegin(test, result, step) { | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   onStepEnd(test, result, step) { | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | module.exports = EchoReporter; | 
					
						
							|  |  |  |  | `;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | test('should call methods in right order', async ({ runInlineTest, mergeReports, showReport, page }) => { | 
					
						
							|  |  |  |  |   test.slow(); | 
					
						
							|  |  |  |  |   const reportDir = test.info().outputPath('blob-report'); | 
					
						
							|  |  |  |  |   const files = { | 
					
						
							|  |  |  |  |     'echo-reporter.js': echoReporterJs, | 
					
						
							|  |  |  |  |     'playwright.config.ts': `
 | 
					
						
							|  |  |  |  |       module.exports = { | 
					
						
							|  |  |  |  |         retries: 1, | 
					
						
							|  |  |  |  |         reporter: [['blob', { outputDir: '${reportDir.replace(/\\/g, '/')}' }]] | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'a.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       test('math 1', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1 + 1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('failing 1', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('flaky 1', async ({}) => { | 
					
						
							|  |  |  |  |         expect(test.info().retry).toBe(1); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 1', async ({}) => {}); | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'b.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       test('math 2', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1 + 1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('failing 2', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 2', async ({}) => {}); | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'c.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       test('math 3', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1 + 1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('flaky 2', async ({}) => { | 
					
						
							|  |  |  |  |         expect(test.info().retry).toBe(1); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 3', async ({}) => {}); | 
					
						
							|  |  |  |  |     `
 | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  |   await runInlineTest(files, { shard: `1/3` }); | 
					
						
							|  |  |  |  |   await runInlineTest(files, { shard: `3/3` }); | 
					
						
							|  |  |  |  |   const reportFiles = await fs.promises.readdir(reportDir); | 
					
						
							|  |  |  |  |   reportFiles.sort(); | 
					
						
							|  |  |  |  |   expect(reportFiles).toEqual(['report-1-of-3.zip', 'report-3-of-3.zip']); | 
					
						
							|  |  |  |  |   const { exitCode, output } = await mergeReports(reportDir, {}, { additionalArgs: ['--reporter', test.info().outputPath('echo-reporter.js')] }); | 
					
						
							|  |  |  |  |   expect(exitCode).toBe(0); | 
					
						
							|  |  |  |  |   const lines = output.split('\n').filter(l => l.trim().length); | 
					
						
							|  |  |  |  |   expect(lines[0]).toBe('onBegin'); | 
					
						
							|  |  |  |  |   expect(lines).toContain('onTestBegin'); | 
					
						
							|  |  |  |  |   expect(lines).toContain('onEnd'); | 
					
						
							|  |  |  |  |   expect(lines).toContain('onExit'); | 
					
						
							|  |  |  |  |   expect(lines.indexOf('onBegin')).toBeLessThan(lines.indexOf('onTestBegin')); | 
					
						
							|  |  |  |  |   expect(lines[lines.length - 2]).toBe('onEnd'); | 
					
						
							|  |  |  |  |   expect(lines[lines.length - 1]).toBe('onExit'); | 
					
						
							|  |  |  |  |   expect(lines.filter(l => l === 'onBegin').length).toBe(1); | 
					
						
							|  |  |  |  |   expect(lines.filter(l => l === 'onEnd').length).toBe(1); | 
					
						
							|  |  |  |  |   expect(lines.filter(l => l === 'onExit').length).toBe(1); | 
					
						
							|  |  |  |  | }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-07 13:47:52 -07:00
										 |  |  |  | test('should merge into html', async ({ runInlineTest, mergeReports, showReport, page }) => { | 
					
						
							| 
									
										
										
										
											2023-04-20 09:36:38 -07:00
										 |  |  |  |   test.slow(); | 
					
						
							| 
									
										
										
										
											2023-04-07 13:47:52 -07:00
										 |  |  |  |   const reportDir = test.info().outputPath('blob-report'); | 
					
						
							|  |  |  |  |   const files = { | 
					
						
							|  |  |  |  |     'playwright.config.ts': `
 | 
					
						
							|  |  |  |  |       module.exports = { | 
					
						
							|  |  |  |  |         retries: 1, | 
					
						
							|  |  |  |  |         reporter: [['blob', { outputDir: '${reportDir.replace(/\\/g, '/')}' }]] | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'a.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       test('math 1', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1 + 1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('failing 1', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('flaky 1', async ({}) => { | 
					
						
							|  |  |  |  |         expect(test.info().retry).toBe(1); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 1', async ({}) => {}); | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'b.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       test('math 2', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1 + 1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('failing 2', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 2', async ({}) => {}); | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'c.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       test('math 3', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1 + 1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('flaky 2', async ({}) => { | 
					
						
							|  |  |  |  |         expect(test.info().retry).toBe(1); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 3', async ({}) => {}); | 
					
						
							|  |  |  |  |     `
 | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  |   const totalShards = 3; | 
					
						
							|  |  |  |  |   for (let i = 0; i < totalShards; i++) | 
					
						
							|  |  |  |  |     await runInlineTest(files, { shard: `${i + 1}/${totalShards}` }); | 
					
						
							|  |  |  |  |   const reportFiles = await fs.promises.readdir(reportDir); | 
					
						
							|  |  |  |  |   reportFiles.sort(); | 
					
						
							|  |  |  |  |   expect(reportFiles).toEqual(['report-1-of-3.zip', 'report-2-of-3.zip', 'report-3-of-3.zip']); | 
					
						
							| 
									
										
										
										
											2023-05-01 12:58:40 -07:00
										 |  |  |  |   const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] }); | 
					
						
							| 
									
										
										
										
											2023-04-07 13:47:52 -07:00
										 |  |  |  |   expect(exitCode).toBe(0); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-01 12:58:40 -07:00
										 |  |  |  |   expect(output).toContain('To open last HTML report run:'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-24 17:34:09 -07:00
										 |  |  |  |   await showReport(); | 
					
						
							| 
									
										
										
										
											2023-04-07 13:47:52 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   await expect(page.locator('.subnav-item:has-text("All") .counter')).toHaveText('10'); | 
					
						
							|  |  |  |  |   await expect(page.locator('.subnav-item:has-text("Passed") .counter')).toHaveText('3'); | 
					
						
							|  |  |  |  |   await expect(page.locator('.subnav-item:has-text("Failed") .counter')).toHaveText('2'); | 
					
						
							|  |  |  |  |   await expect(page.locator('.subnav-item:has-text("Flaky") .counter')).toHaveText('2'); | 
					
						
							|  |  |  |  |   await expect(page.locator('.subnav-item:has-text("Skipped") .counter')).toHaveText('3'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   await expect(page.locator('.test-file-test .test-file-title')).toHaveText( | 
					
						
							|  |  |  |  |       ['failing 1', 'flaky 1', 'math 1', 'skipped 1', 'failing 2', 'math 2', 'skipped 2', 'flaky 2', 'math 3', 'skipped 3']); | 
					
						
							|  |  |  |  | }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | test('be able to merge incomplete shards', async ({ runInlineTest, mergeReports, showReport, page }) => { | 
					
						
							| 
									
										
										
										
											2023-04-20 09:36:38 -07:00
										 |  |  |  |   test.slow(); | 
					
						
							| 
									
										
										
										
											2023-04-07 13:47:52 -07:00
										 |  |  |  |   const reportDir = test.info().outputPath('blob-report'); | 
					
						
							|  |  |  |  |   const files = { | 
					
						
							|  |  |  |  |     'playwright.config.ts': `
 | 
					
						
							|  |  |  |  |       module.exports = { | 
					
						
							|  |  |  |  |         retries: 1, | 
					
						
							|  |  |  |  |         reporter: [['blob', { outputDir: '${reportDir.replace(/\\/g, '/')}' }]] | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'a.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       test('math 1', async ({}) => { | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('failing 1', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 1', async ({}) => {}); | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'b.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       test('math 2', async ({}) => { }); | 
					
						
							|  |  |  |  |       test('failing 2', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 2', async ({}) => {}); | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'c.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       test('math 3', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1 + 1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('flaky 2', async ({}) => { | 
					
						
							|  |  |  |  |         expect(test.info().retry).toBe(1); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 3', async ({}) => {}); | 
					
						
							|  |  |  |  |     `
 | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  |   await runInlineTest(files, { shard: `1/3` }); | 
					
						
							|  |  |  |  |   await runInlineTest(files, { shard: `3/3` }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   const reportFiles = await fs.promises.readdir(reportDir); | 
					
						
							|  |  |  |  |   reportFiles.sort(); | 
					
						
							|  |  |  |  |   expect(reportFiles).toEqual(['report-1-of-3.zip', 'report-3-of-3.zip']); | 
					
						
							| 
									
										
										
										
											2023-05-01 12:58:40 -07:00
										 |  |  |  |   const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] }); | 
					
						
							| 
									
										
										
										
											2023-04-07 13:47:52 -07:00
										 |  |  |  |   expect(exitCode).toBe(0); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-24 17:34:09 -07:00
										 |  |  |  |   await showReport(); | 
					
						
							| 
									
										
										
										
											2023-04-07 13:47:52 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   await expect(page.locator('.subnav-item:has-text("All") .counter')).toHaveText('6'); | 
					
						
							|  |  |  |  |   await expect(page.locator('.subnav-item:has-text("Passed") .counter')).toHaveText('2'); | 
					
						
							|  |  |  |  |   await expect(page.locator('.subnav-item:has-text("Failed") .counter')).toHaveText('1'); | 
					
						
							|  |  |  |  |   await expect(page.locator('.subnav-item:has-text("Flaky") .counter')).toHaveText('1'); | 
					
						
							|  |  |  |  |   await expect(page.locator('.subnav-item:has-text("Skipped") .counter')).toHaveText('2'); | 
					
						
							|  |  |  |  | }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | test('merge into list report by default', async ({ runInlineTest, mergeReports }) => { | 
					
						
							|  |  |  |  |   const reportDir = test.info().outputPath('blob-report'); | 
					
						
							|  |  |  |  |   const files = { | 
					
						
							|  |  |  |  |     'playwright.config.ts': `
 | 
					
						
							|  |  |  |  |       module.exports = { | 
					
						
							|  |  |  |  |         retries: 1, | 
					
						
							|  |  |  |  |         reporter: [['blob', { outputDir: '${reportDir.replace(/\\/g, '/')}' }]] | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'a.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       test('math 1', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1 + 1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('failing 1', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('flaky 1', async ({}) => { | 
					
						
							|  |  |  |  |         expect(test.info().retry).toBe(1); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 1', async ({}) => {}); | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'b.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       test('math 2', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1 + 1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('failing 2', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 2', async ({}) => {}); | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'c.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       test('math 3', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1 + 1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('flaky 2', async ({}) => { | 
					
						
							|  |  |  |  |         expect(test.info().retry).toBe(1); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 3', async ({}) => {}); | 
					
						
							|  |  |  |  |     `
 | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   const totalShards = 3; | 
					
						
							|  |  |  |  |   for (let i = 0; i < totalShards; i++) | 
					
						
							|  |  |  |  |     await runInlineTest(files, { shard: `${i + 1}/${totalShards}` }); | 
					
						
							|  |  |  |  |   const reportFiles = await fs.promises.readdir(reportDir); | 
					
						
							|  |  |  |  |   reportFiles.sort(); | 
					
						
							|  |  |  |  |   expect(reportFiles).toEqual(['report-1-of-3.zip', 'report-2-of-3.zip', 'report-3-of-3.zip']); | 
					
						
							|  |  |  |  |   const { exitCode, output } = await mergeReports(reportDir, { PW_TEST_DEBUG_REPORTERS: '1', PW_TEST_DEBUG_REPORTERS_PRINT_STEPS: '1', PWTEST_TTY_WIDTH: '80' }, { additionalArgs: ['--reporter', 'list'] }); | 
					
						
							|  |  |  |  |   expect(exitCode).toBe(0); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   const text = stripAnsi(output); | 
					
						
							|  |  |  |  |   expect(text).toContain('Running 10 tests using 3 workers'); | 
					
						
							|  |  |  |  |   const lines = text.split('\n').filter(l => l.match(/^\d :/)).map(l => l.replace(/\d+ms/, 'Xms')); | 
					
						
							|  |  |  |  |   expect(lines).toEqual([ | 
					
						
							|  |  |  |  |     `0 :      1 a.test.js:3:11 › math 1`, | 
					
						
							|  |  |  |  |     `0 :   ${POSITIVE_STATUS_MARK} 1 a.test.js:3:11 › math 1 (Xms)`, | 
					
						
							|  |  |  |  |     `1 :      2 a.test.js:6:11 › failing 1`, | 
					
						
							|  |  |  |  |     `1 :   ${NEGATIVE_STATUS_MARK} 2 a.test.js:6:11 › failing 1 (Xms)`, | 
					
						
							|  |  |  |  |     `2 :      3 a.test.js:6:11 › failing 1 (retry #1)`, | 
					
						
							|  |  |  |  |     `2 :   ${NEGATIVE_STATUS_MARK} 3 a.test.js:6:11 › failing 1 (retry #1) (Xms)`, | 
					
						
							|  |  |  |  |     `3 :      4 a.test.js:9:11 › flaky 1`, | 
					
						
							|  |  |  |  |     `3 :   ${NEGATIVE_STATUS_MARK} 4 a.test.js:9:11 › flaky 1 (Xms)`, | 
					
						
							|  |  |  |  |     `4 :      5 a.test.js:9:11 › flaky 1 (retry #1)`, | 
					
						
							|  |  |  |  |     `4 :   ${POSITIVE_STATUS_MARK} 5 a.test.js:9:11 › flaky 1 (retry #1) (Xms)`, | 
					
						
							|  |  |  |  |     `5 :      6 a.test.js:12:12 › skipped 1`, | 
					
						
							|  |  |  |  |     `5 :   -  6 a.test.js:12:12 › skipped 1`, | 
					
						
							|  |  |  |  |     `6 :      7 b.test.js:3:11 › math 2`, | 
					
						
							|  |  |  |  |     `6 :   ${POSITIVE_STATUS_MARK} 7 b.test.js:3:11 › math 2 (Xms)`, | 
					
						
							|  |  |  |  |     `7 :      8 b.test.js:6:11 › failing 2`, | 
					
						
							|  |  |  |  |     `7 :   ${NEGATIVE_STATUS_MARK} 8 b.test.js:6:11 › failing 2 (Xms)`, | 
					
						
							|  |  |  |  |     `8 :      9 b.test.js:6:11 › failing 2 (retry #1)`, | 
					
						
							|  |  |  |  |     `8 :   ${NEGATIVE_STATUS_MARK} 9 b.test.js:6:11 › failing 2 (retry #1) (Xms)`, | 
					
						
							|  |  |  |  |     `9 :      10 b.test.js:9:12 › skipped 2`, | 
					
						
							|  |  |  |  |     `9 :   -  10 b.test.js:9:12 › skipped 2` | 
					
						
							|  |  |  |  |   ]); | 
					
						
							|  |  |  |  | }); | 
					
						
							| 
									
										
										
										
											2023-04-24 17:34:09 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | test('preserve attachments', async ({ runInlineTest, mergeReports, showReport, page }) => { | 
					
						
							|  |  |  |  |   test.slow(); | 
					
						
							|  |  |  |  |   const reportDir = test.info().outputPath('blob-report'); | 
					
						
							|  |  |  |  |   const files = { | 
					
						
							|  |  |  |  |     'playwright.config.ts': `
 | 
					
						
							|  |  |  |  |       module.exports = { | 
					
						
							|  |  |  |  |         retries: 1, | 
					
						
							|  |  |  |  |         reporter: [['blob', { outputDir: '${reportDir.replace(/\\/g, '/')}' }]] | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'a.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       import fs from 'fs'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       test('first', async ({}) => { | 
					
						
							|  |  |  |  |         const attachmentPath = test.info().outputPath('foo.txt'); | 
					
						
							|  |  |  |  |         fs.writeFileSync(attachmentPath, 'hello!'); | 
					
						
							|  |  |  |  |         await test.info().attach('file-attachment', {path: attachmentPath}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         console.log('console info'); | 
					
						
							|  |  |  |  |         console.error('console error'); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('failing 1', async ({}) => { | 
					
						
							|  |  |  |  |         await test.info().attach('text-attachment', { body: 'hi!' }); | 
					
						
							|  |  |  |  |         expect(1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 1', async ({}) => {}); | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'b.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       test('math 2', async ({}) => { }); | 
					
						
							|  |  |  |  |       test('failing 2', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 2', async ({}) => {}); | 
					
						
							|  |  |  |  |     `
 | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  |   await runInlineTest(files, { shard: `1/2` }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   const reportFiles = await fs.promises.readdir(reportDir); | 
					
						
							|  |  |  |  |   reportFiles.sort(); | 
					
						
							|  |  |  |  |   expect(reportFiles).toEqual(['report-1-of-2.zip']); | 
					
						
							| 
									
										
										
										
											2023-05-01 12:58:40 -07:00
										 |  |  |  |   const { exitCode } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] }); | 
					
						
							| 
									
										
										
										
											2023-04-24 17:34:09 -07:00
										 |  |  |  |   expect(exitCode).toBe(0); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   await showReport(); | 
					
						
							|  |  |  |  |   await page.getByText('first').click(); | 
					
						
							|  |  |  |  |   await expect(page.getByText('file-attachment')).toBeVisible(); | 
					
						
							|  |  |  |  |   await page.goBack(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   await page.getByText('failing 1').click(); | 
					
						
							|  |  |  |  |   await expect(page.getByText('\'text-attachment\', { body: \'hi!\'')).toBeVisible(); | 
					
						
							| 
									
										
										
										
											2023-04-27 09:15:24 -07:00
										 |  |  |  | }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | test('multiple output reports', async ({ runInlineTest, mergeReports, showReport, page }) => { | 
					
						
							|  |  |  |  |   test.slow(); | 
					
						
							|  |  |  |  |   const reportDir = test.info().outputPath('blob-report'); | 
					
						
							|  |  |  |  |   const files = { | 
					
						
							|  |  |  |  |     'playwright.config.ts': `
 | 
					
						
							|  |  |  |  |       module.exports = { | 
					
						
							|  |  |  |  |         retries: 1, | 
					
						
							|  |  |  |  |         reporter: [['blob', { outputDir: '${reportDir.replace(/\\/g, '/')}' }]] | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'a.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       import fs from 'fs'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       test('first', async ({}) => { | 
					
						
							|  |  |  |  |         const attachmentPath = test.info().outputPath('foo.txt'); | 
					
						
							|  |  |  |  |         fs.writeFileSync(attachmentPath, 'hello!'); | 
					
						
							|  |  |  |  |         await test.info().attach('file-attachment', {path: attachmentPath}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         console.log('console info'); | 
					
						
							|  |  |  |  |         console.error('console error'); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('failing 1', async ({}) => { | 
					
						
							|  |  |  |  |         await test.info().attach('text-attachment', { body: 'hi!' }); | 
					
						
							|  |  |  |  |         expect(1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 1', async ({}) => {}); | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'b.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       test('math 2', async ({}) => { }); | 
					
						
							|  |  |  |  |       test('failing 2', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 2', async ({}) => {}); | 
					
						
							|  |  |  |  |     `
 | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  |   await runInlineTest(files, { shard: `1/2` }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   const reportFiles = await fs.promises.readdir(reportDir); | 
					
						
							|  |  |  |  |   reportFiles.sort(); | 
					
						
							|  |  |  |  |   expect(reportFiles).toEqual(['report-1-of-2.zip']); | 
					
						
							| 
									
										
										
										
											2023-05-01 12:58:40 -07:00
										 |  |  |  |   const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never', 'PW_TEST_DEBUG_REPORTERS': '1' }, { additionalArgs: ['--reporter', 'html,line'] }); | 
					
						
							| 
									
										
										
										
											2023-04-27 09:15:24 -07:00
										 |  |  |  |   expect(exitCode).toBe(0); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // Check that line reporter was called.
 | 
					
						
							|  |  |  |  |   const text = stripAnsi(output); | 
					
						
							|  |  |  |  |   expect(text).toContain('Running 3 tests using 1 worker'); | 
					
						
							|  |  |  |  |   expect(text).toContain('[1/3] a.test.js:5:11 › first'); | 
					
						
							|  |  |  |  |   expect(text).toContain('a.test.js:13:11 › failing 1 (retry #1)'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // Check html report presence.
 | 
					
						
							|  |  |  |  |   await showReport(); | 
					
						
							|  |  |  |  |   await expect(page.getByText('first')).toBeVisible(); | 
					
						
							| 
									
										
										
										
											2023-04-27 15:16:18 -07:00
										 |  |  |  | }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | test('multiple output reports based on config', async ({ runInlineTest, mergeReports }) => { | 
					
						
							|  |  |  |  |   test.slow(); | 
					
						
							|  |  |  |  |   const reportDir = test.info().outputPath('blob-report'); | 
					
						
							|  |  |  |  |   const files = { | 
					
						
							|  |  |  |  |     'merged/playwright.config.ts': `
 | 
					
						
							|  |  |  |  |       module.exports = { | 
					
						
							| 
									
										
										
										
											2023-05-01 12:58:40 -07:00
										 |  |  |  |         reporter: [['blob', { outputDir: 'merged-blob' }], ['html', { outputFolder: 'html', open: 'never' }], ['line']] | 
					
						
							| 
									
										
										
										
											2023-04-27 15:16:18 -07:00
										 |  |  |  |       }; | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'playwright.config.ts': `
 | 
					
						
							|  |  |  |  |       module.exports = { | 
					
						
							|  |  |  |  |         retries: 1, | 
					
						
							|  |  |  |  |         reporter: [['blob', { outputDir: '${reportDir.replace(/\\/g, '/')}' }]] | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'a.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       import fs from 'fs'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       test('first', async ({}) => { | 
					
						
							|  |  |  |  |         const attachmentPath = test.info().outputPath('foo.txt'); | 
					
						
							|  |  |  |  |         fs.writeFileSync(attachmentPath, 'hello!'); | 
					
						
							|  |  |  |  |         await test.info().attach('file-attachment', {path: attachmentPath}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         console.log('console info'); | 
					
						
							|  |  |  |  |         console.error('console error'); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test('failing 1', async ({}) => { | 
					
						
							|  |  |  |  |         await test.info().attach('text-attachment', { body: 'hi!' }); | 
					
						
							|  |  |  |  |         expect(1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 1', async ({}) => {}); | 
					
						
							|  |  |  |  |     `,
 | 
					
						
							|  |  |  |  |     'b.test.js': `
 | 
					
						
							|  |  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |  |       test('math 2', async ({}) => { }); | 
					
						
							|  |  |  |  |       test('failing 2', async ({}) => { | 
					
						
							|  |  |  |  |         expect(1).toBe(2); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       test.skip('skipped 2', async ({}) => {}); | 
					
						
							|  |  |  |  |     `
 | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  |   await runInlineTest(files, { shard: `1/2` }); | 
					
						
							|  |  |  |  |   await runInlineTest(files, { shard: `2/2` }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   const reportFiles = await fs.promises.readdir(reportDir); | 
					
						
							|  |  |  |  |   reportFiles.sort(); | 
					
						
							|  |  |  |  |   expect(reportFiles).toEqual(['report-1-of-2.zip', 'report-2-of-2.zip']); | 
					
						
							|  |  |  |  |   const { exitCode, output } = await mergeReports(reportDir, { 'PW_TEST_DEBUG_REPORTERS': '1' }, { additionalArgs: ['--config', test.info().outputPath('merged/playwright.config.ts')] }); | 
					
						
							|  |  |  |  |   expect(exitCode).toBe(0); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // Check that line reporter was called.
 | 
					
						
							|  |  |  |  |   const text = stripAnsi(output); | 
					
						
							|  |  |  |  |   expect(text).toContain('Running 6 tests using 2 workers'); | 
					
						
							|  |  |  |  |   expect(text).toContain('[1/6] a.test.js:5:11 › first'); | 
					
						
							|  |  |  |  |   expect(text).toContain('a.test.js:13:11 › failing 1 (retry #1)'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // Check html report presence.
 | 
					
						
							|  |  |  |  |   expect((await fs.promises.stat(test.info().outputPath('merged/html/index.html'))).isFile).toBeTruthy(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // Check report presence.
 | 
					
						
							|  |  |  |  |   expect((await fs.promises.stat(test.info().outputPath('merged/merged-blob/report.zip'))).isFile).toBeTruthy(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-01 12:58:40 -07:00
										 |  |  |  | }); |