| 
									
										
										
										
											2021-07-15 22:02:10 -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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-02 17:48:59 +01:00
										 |  |  | import type { Reporter, TestCase, TestResult, TestStep } from '../../packages/playwright-test/reporter'; | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  | import { test, expect } from './playwright-test-fixtures'; | 
					
						
							| 
									
										
										
										
											2021-07-15 22:02:10 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-20 15:03:01 -05:00
										 |  |  | const smallReporterJS = `
 | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  | const path = require('path'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function formatLocation(location) { | 
					
						
							|  |  |  |   if (!location) | 
					
						
							|  |  |  |     return ' @ <no location>'; | 
					
						
							|  |  |  |   return ' @ ' + path.basename(location.file) + ':' + location.line; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function formatTitle(test) { | 
					
						
							|  |  |  |   let titlePath = test.titlePath(); | 
					
						
							|  |  |  |   if (!titlePath[0]) | 
					
						
							|  |  |  |     [, ...titlePath] = titlePath; | 
					
						
							|  |  |  |   return titlePath.join(' > '); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const asciiRegex = new RegExp('[\\\\u001B\\\\u009B][[\\\\]()#;?]*(?:(?:(?:[a-zA-Z\\\\d]*(?:;[-a-zA-Z\\\\d\\\\/#&.:=?%@~_]*)*)?\\\\u0007)|(?:(?:\\\\d{1,4}(?:;\\\\d{0,4})*)?[\\\\dA-PR-TZcf-ntqry=><~]))', 'g'); | 
					
						
							|  |  |  | function stripAnsi(str) { | 
					
						
							|  |  |  |   return str.replace(asciiRegex, ''); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-20 15:03:01 -05:00
										 |  |  | class Reporter { | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |   constructor(options) { | 
					
						
							|  |  |  |     this.options = options; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   trimError(message) { | 
					
						
							|  |  |  |     if (this.options.skipErrorMessage) | 
					
						
							|  |  |  |       return '<error message>'; | 
					
						
							|  |  |  |     const lines = message.split('\\n'); | 
					
						
							|  |  |  |     return lines[0].trimEnd(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   printErrors(errors, prefix) { | 
					
						
							|  |  |  |     for (const error of errors) { | 
					
						
							|  |  |  |       const errorLocation = this.options.skipErrorLocation ? '' : formatLocation(error.location); | 
					
						
							|  |  |  |       console.log((prefix || '  error: ') + this.trimError(error.message) + errorLocation); | 
					
						
							|  |  |  |       if (this.options.printErrorSnippet && error.snippet) { | 
					
						
							|  |  |  |         const lines = ['======', ...error.snippet.split('\\n'), '======']; | 
					
						
							|  |  |  |         console.log(lines.map(line => stripAnsi('  ' + line).trimEnd()).join('\\n')); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   printsToStdio() { | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-20 15:03:01 -05:00
										 |  |  |   onBegin(config, suite) { | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |     console.log(); // for nicer expectations
 | 
					
						
							|  |  |  |     console.log('onBegin: ' + suite.allTests().length + ' tests total'); | 
					
						
							|  |  |  |     if (this.options.onBegin) | 
					
						
							|  |  |  |       console.log('  options.onBegin=' + this.options.onBegin); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   onTestBegin(test, result) { | 
					
						
							|  |  |  |     console.log('onTestBegin: ' + formatTitle(test) + '; retry #' + result.retry); | 
					
						
							| 
									
										
										
										
											2021-07-20 15:03:01 -05:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   onTestEnd(test, result) { | 
					
						
							| 
									
										
										
										
											2024-04-23 10:51:27 -07:00
										 |  |  |     const status = this.options.printTestStatus ? '[' + result.status + '] ' : ''; | 
					
						
							|  |  |  |     console.log('onTestEnd: ' + status + formatTitle(test) + '; retry #' + result.retry); | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |     this.printErrors(result.errors); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   onStepBegin(test, result, step) { | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   onStepEnd(test, result, step) { | 
					
						
							|  |  |  |     if (this.options.printSteps) { | 
					
						
							|  |  |  |       console.log('onStepEnd: ' + step.title); | 
					
						
							|  |  |  |       if (step.error) | 
					
						
							|  |  |  |         this.printErrors([step.error]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // For easier debugging.
 | 
					
						
							|  |  |  |   onStdOut(data) { | 
					
						
							|  |  |  |     process.stdout.write(data.toString()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // For easier debugging.
 | 
					
						
							|  |  |  |   onStdErr(data) { | 
					
						
							|  |  |  |     process.stderr.write(data.toString()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 06:47:41 -08:00
										 |  |  |   onError(error) { | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |     this.printErrors([error], 'onError: '); | 
					
						
							| 
									
										
										
										
											2021-11-12 06:47:41 -08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   async onEnd() { | 
					
						
							|  |  |  |     await new Promise(f => setTimeout(f, 500)); | 
					
						
							|  |  |  |     console.log('onEnd'); | 
					
						
							|  |  |  |     if (this.options.onEnd) | 
					
						
							|  |  |  |       console.log('  options.onEnd=' + this.options.onEnd); | 
					
						
							| 
									
										
										
										
											2021-07-20 15:03:01 -05:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   async onExit() { | 
					
						
							|  |  |  |     console.log('onExit'); | 
					
						
							| 
									
										
										
										
											2023-04-04 10:50:40 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-07-20 15:03:01 -05:00
										 |  |  | } | 
					
						
							|  |  |  | module.exports = Reporter; | 
					
						
							|  |  |  | `;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  | for (const useIntermediateMergeReport of [false, true] as const) { | 
					
						
							|  |  |  |   test.describe(`${useIntermediateMergeReport ? 'merged' : 'created'}`, () => { | 
					
						
							|  |  |  |     test.use({ useIntermediateMergeReport }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test('should work with custom reporter', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |       const result = await runInlineTest({ | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |         'reporter.ts': smallReporterJS, | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  |         'playwright.config.ts': `
 | 
					
						
							|  |  |  |           module.exports = { | 
					
						
							|  |  |  |             reporter: [ | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |               [ './reporter.ts', { onBegin: 'begin-data', onEnd: 'end-data' } ] | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  |             ], | 
					
						
							|  |  |  |             projects: [ | 
					
						
							|  |  |  |               { name: 'foo', repeatEach: 2 }, | 
					
						
							|  |  |  |               { name: 'bar' }, | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'a.test.ts': `
 | 
					
						
							|  |  |  |           import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |           test('not run', async ({}) => { | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |           test.only('is run', async ({}) => { | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       }, { reporter: '', workers: 1 }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(result.exitCode).toBe(0); | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |       expect(result.output).toBe(`
 | 
					
						
							|  |  |  | onBegin: 3 tests total | 
					
						
							|  |  |  |   options.onBegin=begin-data | 
					
						
							|  |  |  | onTestBegin: foo > a.test.ts > is run; retry #0 | 
					
						
							|  |  |  | onTestEnd: foo > a.test.ts > is run; retry #0 | 
					
						
							|  |  |  | onTestBegin: foo > a.test.ts > is run; retry #0 | 
					
						
							|  |  |  | onTestEnd: foo > a.test.ts > is run; retry #0 | 
					
						
							|  |  |  | onTestBegin: bar > a.test.ts > is run; retry #0 | 
					
						
							|  |  |  | onTestEnd: bar > a.test.ts > is run; retry #0 | 
					
						
							|  |  |  | onEnd | 
					
						
							|  |  |  |   options.onEnd=end-data | 
					
						
							|  |  |  | onExit | 
					
						
							|  |  |  | `);
 | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test('should work without a file extension', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |       const result = await runInlineTest({ | 
					
						
							|  |  |  |         'reporter.ts': smallReporterJS, | 
					
						
							|  |  |  |         'playwright.config.ts': `
 | 
					
						
							|  |  |  |           module.exports = { | 
					
						
							|  |  |  |             reporter: './reporter', | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'a.test.ts': `
 | 
					
						
							|  |  |  |           import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |           test('pass', async ({}) => { | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       }, { reporter: '', workers: 1 }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(result.exitCode).toBe(0); | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |       expect(result.output).toBe(`
 | 
					
						
							|  |  |  | onBegin: 1 tests total | 
					
						
							|  |  |  | onTestBegin:  > a.test.ts > pass; retry #0 | 
					
						
							|  |  |  | onTestEnd:  > a.test.ts > pass; retry #0 | 
					
						
							|  |  |  | onEnd | 
					
						
							|  |  |  | onExit | 
					
						
							|  |  |  | `);
 | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test('should report onEnd after global teardown', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |       test.skip(useIntermediateMergeReport); | 
					
						
							|  |  |  |       const result = await runInlineTest({ | 
					
						
							|  |  |  |         'reporter.ts': smallReporterJS, | 
					
						
							|  |  |  |         'globalSetup.ts': `
 | 
					
						
							|  |  |  |           module.exports = () => { | 
					
						
							|  |  |  |             return () => console.log('\\n%%global teardown'); | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'playwright.config.ts': `
 | 
					
						
							|  |  |  |           module.exports = { | 
					
						
							|  |  |  |             reporter: './reporter', | 
					
						
							|  |  |  |             globalSetup: './globalSetup', | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'a.test.ts': `
 | 
					
						
							|  |  |  |           import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |           test('pass', async ({}) => { | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       }, { reporter: '', workers: 1 }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(result.exitCode).toBe(0); | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |       expect(result.output).toBe(`
 | 
					
						
							|  |  |  | onBegin: 1 tests total | 
					
						
							|  |  |  | onTestBegin:  > a.test.ts > pass; retry #0 | 
					
						
							|  |  |  | onTestEnd:  > a.test.ts > pass; retry #0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | %%global teardown | 
					
						
							|  |  |  | onEnd | 
					
						
							|  |  |  | onExit | 
					
						
							|  |  |  | `);
 | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test('should load reporter from node_modules', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |       const result = await runInlineTest({ | 
					
						
							|  |  |  |         'node_modules/my-reporter/index.js': smallReporterJS, | 
					
						
							|  |  |  |         'playwright.config.ts': `
 | 
					
						
							|  |  |  |           module.exports = { | 
					
						
							|  |  |  |             reporter: 'my-reporter', | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'a.test.ts': `
 | 
					
						
							|  |  |  |           import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |           test('pass', async ({}) => { | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       }, { reporter: '', workers: 1 }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(result.exitCode).toBe(0); | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |       expect(result.output).toBe(`
 | 
					
						
							|  |  |  | onBegin: 1 tests total | 
					
						
							|  |  |  | onTestBegin:  > a.test.ts > pass; retry #0 | 
					
						
							|  |  |  | onTestEnd:  > a.test.ts > pass; retry #0 | 
					
						
							|  |  |  | onEnd | 
					
						
							|  |  |  | onExit | 
					
						
							|  |  |  | `);
 | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test('should not have internal error when steps are finished after timeout', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |       const result = await runInlineTest({ | 
					
						
							|  |  |  |         'a.test.ts': `
 | 
					
						
							|  |  |  |           import { test as base, expect } from '@playwright/test'; | 
					
						
							|  |  |  |           const test = base.extend({ | 
					
						
							|  |  |  |             page: async ({ page }, use) => { | 
					
						
							|  |  |  |               await use(page); | 
					
						
							|  |  |  |               // Timeout in fixture teardown that will resolve on browser.close.
 | 
					
						
							|  |  |  |               await page.waitForNavigation(); | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |           test('pass', async ({ page }) => { | 
					
						
							|  |  |  |             // Timeout in the test.
 | 
					
						
							|  |  |  |             await page.click('foo'); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       }, { workers: 1, timeout: 1000, reporter: 'dot', retries: 1 }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(result.exitCode).toBe(1); | 
					
						
							|  |  |  |       expect(result.failed).toBe(1); | 
					
						
							|  |  |  |       expect(result.output).not.toContain('Internal error'); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test('should report forbid-only error to reporter', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |       const result = await runInlineTest({ | 
					
						
							|  |  |  |         'reporter.ts': smallReporterJS, | 
					
						
							|  |  |  |         'playwright.config.ts': `
 | 
					
						
							|  |  |  |           module.exports = { | 
					
						
							|  |  |  |             reporter: './reporter', | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'a.test.ts': `
 | 
					
						
							|  |  |  |           import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |           test.only('pass', () => {}); | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       }, { 'reporter': '', 'forbid-only': true }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(result.exitCode).toBe(1); | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |       expect(result.output).toBe(`
 | 
					
						
							|  |  |  | onBegin: 0 tests total | 
					
						
							|  |  |  | onError: Error: item focused with '.only' is not allowed due to the '--forbid-only' CLI flag: "a.test.ts pass" @ a.test.ts:3 | 
					
						
							|  |  |  | onEnd | 
					
						
							|  |  |  | onExit | 
					
						
							|  |  |  | `);
 | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test('should report no-tests error to reporter', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |       const result = await runInlineTest({ | 
					
						
							|  |  |  |         'reporter.ts': smallReporterJS, | 
					
						
							|  |  |  |         'playwright.config.ts': `
 | 
					
						
							|  |  |  |           module.exports = { | 
					
						
							|  |  |  |             reporter: './reporter', | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |       }, { 'reporter': '' }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(result.exitCode).toBe(1); | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |       expect(result.output).toBe(`
 | 
					
						
							|  |  |  | onBegin: 0 tests total | 
					
						
							|  |  |  | onError: Error: No tests found @ <no location> | 
					
						
							|  |  |  | onEnd | 
					
						
							|  |  |  | onExit | 
					
						
							|  |  |  | `);
 | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test('should report require error to reporter', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |       const result = await runInlineTest({ | 
					
						
							|  |  |  |         'reporter.ts': smallReporterJS, | 
					
						
							|  |  |  |         'playwright.config.ts': `
 | 
					
						
							|  |  |  |           module.exports = { | 
					
						
							|  |  |  |             reporter: './reporter', | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'a.spec.js': `
 | 
					
						
							|  |  |  |           throw new Error('Oh my!'); | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |       }, { 'reporter': '' }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(result.exitCode).toBe(1); | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |       expect(result.output).toBe(`
 | 
					
						
							|  |  |  | onBegin: 0 tests total | 
					
						
							|  |  |  | onError: Error: Oh my! @ a.spec.js:2 | 
					
						
							|  |  |  | onError: Error: No tests found @ <no location> | 
					
						
							|  |  |  | onEnd | 
					
						
							|  |  |  | onExit | 
					
						
							|  |  |  | `);
 | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test('should report global setup error to reporter', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |       const result = await runInlineTest({ | 
					
						
							|  |  |  |         'reporter.ts': smallReporterJS, | 
					
						
							|  |  |  |         'playwright.config.ts': `
 | 
					
						
							|  |  |  |           module.exports = { | 
					
						
							|  |  |  |             reporter: './reporter', | 
					
						
							|  |  |  |             globalSetup: './globalSetup', | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'globalSetup.ts': `
 | 
					
						
							|  |  |  |           module.exports = () => { | 
					
						
							|  |  |  |             throw new Error('Oh my!'); | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'a.spec.js': `
 | 
					
						
							|  |  |  |           const { test, expect } = require('@playwright/test'); | 
					
						
							|  |  |  |           test('test', () => {}); | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |       }, { 'reporter': '' }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(result.exitCode).toBe(1); | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |       expect(result.output).toBe(`
 | 
					
						
							|  |  |  | onBegin: 0 tests total | 
					
						
							|  |  |  | onError: Error: Oh my! @ globalSetup.ts:3 | 
					
						
							|  |  |  | onEnd | 
					
						
							|  |  |  | onExit | 
					
						
							|  |  |  | `);
 | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test('should report correct tests/suites when using grep', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |       const result = await runInlineTest({ | 
					
						
							|  |  |  |         'a.spec.js': `
 | 
					
						
							|  |  |  |           import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           test.describe('@foo', () => { | 
					
						
							|  |  |  |             test('test1', async ({ }) => { | 
					
						
							|  |  |  |               console.log('%%test1'); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |             test('test2', async ({ }) => { | 
					
						
							|  |  |  |               console.log('%%test2'); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           test('test3', async ({ }) => { | 
					
						
							|  |  |  |             console.log('%%test3'); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |       }, { 'grep': '@foo' }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(result.exitCode).toBe(0); | 
					
						
							|  |  |  |       expect(result.output).toContain('%%test1'); | 
					
						
							|  |  |  |       expect(result.output).toContain('%%test2'); | 
					
						
							|  |  |  |       expect(result.output).not.toContain('%%test3'); | 
					
						
							|  |  |  |       const fileSuite = result.report.suites[0]; | 
					
						
							|  |  |  |       expect(fileSuite.suites!.length).toBe(1); | 
					
						
							|  |  |  |       expect(fileSuite.suites![0].specs.length).toBe(2); | 
					
						
							|  |  |  |       expect(fileSuite.specs.length).toBe(0); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test('should use sourceMap-based file suite names', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |       test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/11028' }); | 
					
						
							|  |  |  |       const result = await runInlineTest({ | 
					
						
							|  |  |  |         'reporter.js': `
 | 
					
						
							|  |  |  |           class Reporter { | 
					
						
							|  |  |  |             onBegin(config, suite) { | 
					
						
							|  |  |  |               console.log(suite.suites[0].suites[0].location.file); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           module.exports = Reporter; | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'playwright.config.ts': `
 | 
					
						
							|  |  |  |           module.exports = { | 
					
						
							|  |  |  |             reporter: './reporter', | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'a.spec.js': | 
					
						
							| 
									
										
										
										
											2023-02-14 19:20:56 -08:00
										 |  |  | `var __create = Object.create;
 | 
					
						
							| 
									
										
										
										
											2022-02-01 13:04:54 -08:00
										 |  |  | var __defProp = Object.defineProperty; | 
					
						
							|  |  |  | var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | 
					
						
							|  |  |  | var __getOwnPropNames = Object.getOwnPropertyNames; | 
					
						
							|  |  |  | var __getProtoOf = Object.getPrototypeOf; | 
					
						
							|  |  |  | var __hasOwnProp = Object.prototype.hasOwnProperty; | 
					
						
							|  |  |  | var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); | 
					
						
							|  |  |  | var __reExport = (target, module2, desc) => { | 
					
						
							|  |  |  |   if (module2 && typeof module2 === "object" || typeof module2 === "function") { | 
					
						
							|  |  |  |     for (let key of __getOwnPropNames(module2)) | 
					
						
							|  |  |  |       if (!__hasOwnProp.call(target, key) && key !== "default") | 
					
						
							|  |  |  |         __defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return target; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | var __toModule = (module2) => { | 
					
						
							|  |  |  |   return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | var import_test = __toModule(require("@playwright/test")); | 
					
						
							|  |  |  | (0, import_test.test)("pass", async () => { | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL2Euc3BlYy50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiaW1wb3J0IHsgdGVzdCB9IGZyb20gXCJAcGxheXdyaWdodC90ZXN0XCI7XG5cbnRlc3QoJ3Bhc3MnLCBhc3luYyAoKSA9PiB7fSk7Il0sCiAgIm1hcHBpbmdzIjogIjs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsa0JBQXFCO0FBRXJCLHNCQUFLLFFBQVEsWUFBWTtBQUFBOyIsCiAgIm5hbWVzIjogW10KfQo=`,
 | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  |       }, { 'reporter': '' }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(result.exitCode).toBe(0); | 
					
						
							|  |  |  |       expect(result.output).toContain('a.spec.ts'); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test('parallelIndex is presented in onTestEnd', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |       const result = await runInlineTest({ | 
					
						
							|  |  |  |         'reporter.ts': `
 | 
					
						
							|  |  |  |         class Reporter { | 
					
						
							|  |  |  |           onTestEnd(test, result) { | 
					
						
							|  |  |  |             console.log('parallelIndex: ' + result.parallelIndex) | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         module.exports = Reporter;`,
 | 
					
						
							|  |  |  |         'playwright.config.ts': `
 | 
					
						
							|  |  |  |           module.exports = { | 
					
						
							|  |  |  |             reporter: './reporter', | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         'a.spec.js': `
 | 
					
						
							|  |  |  |           const { test, expect } = require('@playwright/test'); | 
					
						
							|  |  |  |           test('test', () => {}); | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |       }, { 'reporter': '', 'workers': 1 }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(result.output).toContain('parallelIndex: 0'); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test('test and step error should have code snippet', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |       const result = await runInlineTest({ | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |         'reporter.ts': smallReporterJS, | 
					
						
							|  |  |  |         'playwright.config.ts': `module.exports = { reporter: [['./reporter', { printSteps: true, printErrorSnippet: true }]] };`, | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  |         'a.spec.js': `
 | 
					
						
							|  |  |  |           const { test, expect } = require('@playwright/test'); | 
					
						
							|  |  |  |           test('test', async () => { | 
					
						
							|  |  |  |             await test.step('step', async () => { | 
					
						
							|  |  |  |               expect(1).toBe(2); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |       }, { 'reporter': '', 'workers': 1 }); | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |       expect(result.output).toBe(`
 | 
					
						
							|  |  |  | onBegin: 1 tests total | 
					
						
							|  |  |  | onTestBegin:  > a.spec.js > test; retry #0 | 
					
						
							|  |  |  | onStepEnd: Before Hooks | 
					
						
							|  |  |  | onStepEnd: expect.toBe | 
					
						
							|  |  |  |   error: Error: expect(received).toBe(expected) // Object.is equality @ a.spec.js:5
 | 
					
						
							|  |  |  |   ====== | 
					
						
							|  |  |  |     3 |           test('test', async () => { | 
					
						
							|  |  |  |     4 |             await test.step('step', async () => { | 
					
						
							|  |  |  |   > 5 |               expect(1).toBe(2); | 
					
						
							|  |  |  |       |                         ^ | 
					
						
							|  |  |  |     6 |             }); | 
					
						
							|  |  |  |     7 |           }); | 
					
						
							|  |  |  |     8 | | 
					
						
							|  |  |  |   ====== | 
					
						
							|  |  |  | onStepEnd: step | 
					
						
							|  |  |  |   error: Error: expect(received).toBe(expected) // Object.is equality @ a.spec.js:5
 | 
					
						
							|  |  |  |   ====== | 
					
						
							|  |  |  |     3 |           test('test', async () => { | 
					
						
							|  |  |  |     4 |             await test.step('step', async () => { | 
					
						
							|  |  |  |   > 5 |               expect(1).toBe(2); | 
					
						
							|  |  |  |       |                         ^ | 
					
						
							|  |  |  |     6 |             }); | 
					
						
							|  |  |  |     7 |           }); | 
					
						
							|  |  |  |     8 | | 
					
						
							|  |  |  |   ====== | 
					
						
							|  |  |  | onStepEnd: After Hooks | 
					
						
							|  |  |  | onStepEnd: Worker Cleanup | 
					
						
							|  |  |  | onTestEnd:  > a.spec.js > test; retry #0 | 
					
						
							|  |  |  |   error: Error: expect(received).toBe(expected) // Object.is equality @ a.spec.js:5
 | 
					
						
							|  |  |  |   ====== | 
					
						
							|  |  |  |     3 |           test('test', async () => { | 
					
						
							|  |  |  |     4 |             await test.step('step', async () => { | 
					
						
							|  |  |  |   > 5 |               expect(1).toBe(2); | 
					
						
							|  |  |  |       |                         ^ | 
					
						
							|  |  |  |     6 |             }); | 
					
						
							|  |  |  |     7 |           }); | 
					
						
							|  |  |  |     8 | | 
					
						
							|  |  |  |   ====== | 
					
						
							|  |  |  | onEnd | 
					
						
							|  |  |  | onExit | 
					
						
							|  |  |  | `);
 | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test('onError should have code snippet', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |       const result = await runInlineTest({ | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |         'reporter.ts': smallReporterJS, | 
					
						
							|  |  |  |         'playwright.config.ts': `module.exports = { reporter: [['./reporter', { printSteps: true, printErrorSnippet: true }]] };`, | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  |         'a.spec.js': `
 | 
					
						
							|  |  |  |           const { test, expect } = require('@playwright/test'); | 
					
						
							|  |  |  |           throw new Error('test'); | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |       }, { 'reporter': '', 'workers': 1 }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-22 18:29:26 -07:00
										 |  |  |       expect(result.output).toBe(`
 | 
					
						
							|  |  |  | onBegin: 0 tests total | 
					
						
							|  |  |  | onError: Error: test @ a.spec.js:3 | 
					
						
							|  |  |  |   ====== | 
					
						
							|  |  |  |      at a.spec.js:3 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     1 | | 
					
						
							|  |  |  |     2 |           const { test, expect } = require('@playwright/test'); | 
					
						
							|  |  |  |   > 3 |           throw new Error('test'); | 
					
						
							|  |  |  |       |                 ^ | 
					
						
							|  |  |  |     4 | | 
					
						
							|  |  |  |   ====== | 
					
						
							|  |  |  | onError: Error: No tests found @ <no location> | 
					
						
							|  |  |  | onEnd | 
					
						
							|  |  |  | onExit | 
					
						
							|  |  |  | `);
 | 
					
						
							| 
									
										
										
										
											2023-06-16 21:30:55 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2024-01-16 13:32:47 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | test('should report a stable test.id', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |   const result = await runInlineTest({ | 
					
						
							|  |  |  |     'reporter.ts': `
 | 
					
						
							|  |  |  |       class Reporter { | 
					
						
							|  |  |  |         onTestBegin(test) { | 
					
						
							|  |  |  |           console.log('\\n%%testbegin-' + test.id); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       export default Reporter; | 
					
						
							|  |  |  |     `,
 | 
					
						
							|  |  |  |     'playwright.config.ts': `
 | 
					
						
							|  |  |  |       module.exports = { reporter: [[ './reporter.ts' ]] }; | 
					
						
							|  |  |  |     `,
 | 
					
						
							|  |  |  |     'a.test.ts': `
 | 
					
						
							|  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |       test('example test', async ({}) => { | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   }, { reporter: '', workers: 1 }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   expect(result.exitCode).toBe(0); | 
					
						
							|  |  |  |   expect(result.outputLines).toEqual([ | 
					
						
							|  |  |  |     'testbegin-20289bcdad95a5e18c38-8b63c3695b9c8bd62d98', | 
					
						
							|  |  |  |   ]); | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
											
												feat(test runner): tags/annotations (#29248)
API changes:
- `test(title, details, body)` where details contain `tag` and
`annotation`.
- similar `details` property added to `test.skip`, `test.fail`,
`test.fixme`, `test.only`, `test.describe` and other `test.describe.*`
variations.
- `TestProject.tagFilter`/`TestConfig.tagFilter` that supports logical
tag expressions with `(`, `)`, `and`, `or` and `not`.
- `--tag` CLI option to filter by tags.
- New annotations are available in `TestInfo.annotations` and
`TestCase.annotations`.
- New tags are available in `TestCase.tags`.
    
Reporter changes:
- `json` reporter includes new tags in addition to old `@smoke`-style
tags. **Breaking**: tags are now listed with the leading `@` symbol.
- `html` reporter filters by old and new tags with the same `@smoke`
token.
Fixes #29229, fixes #23180.
											
										 
											2024-02-07 16:31:25 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | test('should report annotations from test declaration', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |   const result = await runInlineTest({ | 
					
						
							|  |  |  |     'reporter.ts': `
 | 
					
						
							|  |  |  |       export default class Reporter { | 
					
						
							|  |  |  |         onBegin(config, suite) { | 
					
						
							|  |  |  |           const visit = suite => { | 
					
						
							|  |  |  |             for (const test of suite.tests || []) { | 
					
						
							|  |  |  |               const annotations = test.annotations.map(a => { | 
					
						
							|  |  |  |                 return a.description ? a.type + '=' + a.description : a.type; | 
					
						
							|  |  |  |               }); | 
					
						
							|  |  |  |               console.log('\\n%%title=' + test.title + ', annotations=' + annotations.join(',')); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             for (const child of suite.suites || []) | 
					
						
							|  |  |  |               visit(child); | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |           visit(suite); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         onError(error) { | 
					
						
							|  |  |  |           console.log(error); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `,
 | 
					
						
							|  |  |  |     'playwright.config.ts': `
 | 
					
						
							|  |  |  |       module.exports = { | 
					
						
							|  |  |  |         reporter: './reporter', | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     `,
 | 
					
						
							|  |  |  |     'stdio.spec.js': `
 | 
					
						
							|  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |       test('none', () => { | 
					
						
							|  |  |  |         expect(test.info().annotations).toEqual([]); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       test('foo', { annotation: { type: 'foo' } }, () => { | 
					
						
							|  |  |  |         expect(test.info().annotations).toEqual([{ type: 'foo' }]); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       test('foo-bar', { | 
					
						
							|  |  |  |         annotation: [ | 
					
						
							|  |  |  |           { type: 'foo', description: 'desc' }, | 
					
						
							|  |  |  |           { type: 'bar' }, | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |       }, () => { | 
					
						
							|  |  |  |         expect(test.info().annotations).toEqual([ | 
					
						
							|  |  |  |           { type: 'foo', description: 'desc' }, | 
					
						
							|  |  |  |           { type: 'bar' }, | 
					
						
							|  |  |  |         ]); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       test.skip('skip-foo', { annotation: { type: 'foo' } }, () => { | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       test.fixme('fixme-bar', { annotation: { type: 'bar' } }, () => { | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       test.fail('fail-foo-bar', { | 
					
						
							|  |  |  |         annotation: [ | 
					
						
							|  |  |  |           { type: 'foo' }, | 
					
						
							|  |  |  |           { type: 'bar', description: 'desc' }, | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |       }, () => { | 
					
						
							|  |  |  |         expect(1).toBe(2); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       test.describe('suite', { annotation: { type: 'foo' } }, () => { | 
					
						
							|  |  |  |         test('foo-suite', () => { | 
					
						
							|  |  |  |           expect(test.info().annotations).toEqual([{ type: 'foo' }]); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         test.describe('inner', { annotation: { type: 'bar' } }, () => { | 
					
						
							|  |  |  |           test('foo-bar-suite', () => { | 
					
						
							|  |  |  |             expect(test.info().annotations).toEqual([{ type: 'foo' }, { type: 'bar' }]); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       test.describe.skip('skip-foo-suite', { annotation: { type: 'foo' } }, () => { | 
					
						
							|  |  |  |         test('skip-foo-suite', () => { | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       test.describe.fixme('fixme-bar-suite', { annotation: { type: 'bar' } }, () => { | 
					
						
							|  |  |  |         test('fixme-bar-suite', () => { | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   expect(result.exitCode).toBe(0); | 
					
						
							|  |  |  |   expect(result.outputLines).toEqual([ | 
					
						
							|  |  |  |     `title=none, annotations=`, | 
					
						
							|  |  |  |     `title=foo, annotations=foo`, | 
					
						
							|  |  |  |     `title=foo-bar, annotations=foo=desc,bar`, | 
					
						
							|  |  |  |     `title=skip-foo, annotations=foo,skip`, | 
					
						
							|  |  |  |     `title=fixme-bar, annotations=bar,fixme`, | 
					
						
							|  |  |  |     `title=fail-foo-bar, annotations=foo,bar=desc,fail`, | 
					
						
							|  |  |  |     `title=foo-suite, annotations=foo`, | 
					
						
							|  |  |  |     `title=foo-bar-suite, annotations=foo,bar`, | 
					
						
							|  |  |  |     `title=skip-foo-suite, annotations=foo,skip`, | 
					
						
							|  |  |  |     `title=fixme-bar-suite, annotations=bar,fixme`, | 
					
						
							|  |  |  |   ]); | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2024-04-23 10:51:27 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | test('tests skipped in serial mode receive onTestBegin/onTestEnd', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |   test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/28321' }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const result = await runInlineTest({ | 
					
						
							|  |  |  |     'reporter.ts': smallReporterJS, | 
					
						
							|  |  |  |     'playwright.config.ts': `module.exports = { reporter: [['./reporter', { printTestStatus: true }]] };`, | 
					
						
							|  |  |  |     'a.spec.ts': `
 | 
					
						
							|  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       test.describe.configure({ mode: 'serial', retries: 1 }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       test('test1', () => { | 
					
						
							|  |  |  |         expect(test.info().retry).toBe(1); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       test('test2', () => { | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     `,
 | 
					
						
							|  |  |  |   }, { 'reporter': '', 'workers': 1 }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   expect(result.output).toBe(`
 | 
					
						
							|  |  |  | onBegin: 2 tests total | 
					
						
							|  |  |  | onTestBegin:  > a.spec.ts > test1; retry #0 | 
					
						
							|  |  |  | onTestEnd: [failed]  > a.spec.ts > test1; retry #0 | 
					
						
							|  |  |  |   error: Error: expect(received).toBe(expected) // Object.is equality @ a.spec.ts:7
 | 
					
						
							|  |  |  | onTestBegin:  > a.spec.ts > test2; retry #0 | 
					
						
							|  |  |  | onTestEnd: [skipped]  > a.spec.ts > test2; retry #0 | 
					
						
							|  |  |  | onTestBegin:  > a.spec.ts > test1; retry #1 | 
					
						
							|  |  |  | onTestEnd: [passed]  > a.spec.ts > test1; retry #1 | 
					
						
							|  |  |  | onTestBegin:  > a.spec.ts > test2; retry #1 | 
					
						
							|  |  |  | onTestEnd: [passed]  > a.spec.ts > test2; retry #1 | 
					
						
							|  |  |  | onEnd | 
					
						
							|  |  |  | onExit | 
					
						
							|  |  |  | `);
 | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2025-01-02 17:48:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | test('step attachments are referentially equal to result attachments', async ({ runInlineTest }) => { | 
					
						
							|  |  |  |   class TestReporter implements Reporter { | 
					
						
							|  |  |  |     onStepEnd(test: TestCase, result: TestResult, step: TestStep) { | 
					
						
							|  |  |  |       console.log('%%%', JSON.stringify({ | 
					
						
							|  |  |  |         title: step.title, | 
					
						
							|  |  |  |         attachments: step.attachments.map(a => result.attachments.indexOf(a)), | 
					
						
							|  |  |  |       })); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   const result = await runInlineTest({ | 
					
						
							|  |  |  |     'reporter.ts': `module.exports = ${TestReporter.toString()}`, | 
					
						
							|  |  |  |     'playwright.config.ts': `module.exports = { reporter: './reporter' };`, | 
					
						
							|  |  |  |     'a.spec.ts': `
 | 
					
						
							|  |  |  |       import { test, expect } from '@playwright/test'; | 
					
						
							|  |  |  |       test('test', async ({}, testInfo) => { | 
					
						
							|  |  |  |         await test.step('step', async () => { | 
					
						
							|  |  |  |           testInfo.attachments.push({ name: 'attachment', body: Buffer.from('content') }); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     `,
 | 
					
						
							|  |  |  |   }, { 'reporter': '', 'workers': 1 }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const steps = result.outputLines.map(line => JSON.parse(line)); | 
					
						
							|  |  |  |   expect(steps).toEqual([ | 
					
						
							|  |  |  |     { title: 'Before Hooks', attachments: [] }, | 
					
						
							|  |  |  |     { title: 'step', attachments: [0] }, | 
					
						
							|  |  |  |     { title: 'After Hooks', attachments: [] }, | 
					
						
							|  |  |  |   ]); | 
					
						
							|  |  |  | }); |