|
| 1 | +/* eslint-disable no-console */ |
| 2 | +import type { |
| 3 | + FullConfig, TestResult, |
| 4 | + Reporter, Suite, TestCase, |
| 5 | +} from '@playwright/test/reporter' |
| 6 | + |
| 7 | +import { ansiRegex, ms } from 'e2e/test-utils' |
| 8 | + |
| 9 | +import fs from 'fs' |
| 10 | +import path from 'path' |
| 11 | + |
| 12 | +class StkReporter implements Reporter { |
| 13 | + outputFolder: string; |
| 14 | + failedTestErrors: Array<string>; |
| 15 | + totalTestCount: number; |
| 16 | + suite: Suite; |
| 17 | + |
| 18 | + constructor() { |
| 19 | + this.outputFolder = 'playwright-stk' |
| 20 | + this.cleanupFolder() |
| 21 | + |
| 22 | + this.totalTestCount = 0 |
| 23 | + this.failedTestErrors = [] |
| 24 | + } |
| 25 | + |
| 26 | + stripAnsiEscapes( str: string ): string { |
| 27 | + return str.replace( ansiRegex, '' ) |
| 28 | + } |
| 29 | + |
| 30 | + cleanupFolder() { |
| 31 | + const folderPath = path.resolve( this.outputFolder ) |
| 32 | + |
| 33 | + if ( fs.existsSync( folderPath ) ) { |
| 34 | + // Read the directory and delete files |
| 35 | + const files = fs.readdirSync( folderPath ) |
| 36 | + for ( const file of files ) { |
| 37 | + const filePath = path.join( folderPath, file ) |
| 38 | + if ( fs.lstatSync( filePath ).isFile() ) { |
| 39 | + fs.unlinkSync( filePath ) // Remove the file |
| 40 | + } |
| 41 | + } |
| 42 | + console.log( `All files removed from: ${ folderPath }` ) |
| 43 | + } else { |
| 44 | + // If folder doesn't exist, create it |
| 45 | + fs.mkdirSync( folderPath, { recursive: true } ) |
| 46 | + } |
| 47 | + } |
| 48 | + |
| 49 | + onBegin( config: FullConfig, suite: Suite ): void { |
| 50 | + this.totalTestCount = suite.allTests().length |
| 51 | + this.suite = suite |
| 52 | + console.log( `Running ${ this.totalTestCount } tests:` ) |
| 53 | + } |
| 54 | + |
| 55 | + onTestEnd( test: TestCase, result: TestResult ) { |
| 56 | + if ( test.outcome() === 'expected' ) { |
| 57 | + process.stdout.write( '·' ) |
| 58 | + } else { |
| 59 | + const titlePath = test.titlePath().filter( t => t !== '' ).join( ' › ' ) |
| 60 | + |
| 61 | + const out = test.results.length <= test.retries ? '×' : ( result.status === 'timedOut' ? 'T' : 'F' ) |
| 62 | + process.stdout.write( out ) |
| 63 | + |
| 64 | + let testResult = `${ titlePath } ${ test.results.length > 1 ? `(retry #${ test.results.length - 1 }) ` : '' }[${ ms( result.duration ) }]` + '\n\n' |
| 65 | + if ( result.errors.length >= 1 ) { |
| 66 | + result.errors.forEach( error => { |
| 67 | + if ( error.message ) { |
| 68 | + testResult += `${ error.message }` + '\n\n' |
| 69 | + } |
| 70 | + |
| 71 | + if ( error.snippet ) { |
| 72 | + testResult += `${ error.snippet }` + `\n\n` |
| 73 | + } |
| 74 | + } ) |
| 75 | + } |
| 76 | + |
| 77 | + this.failedTestErrors.push( testResult ) |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + getSummary() { |
| 82 | + let skipped = 0 |
| 83 | + let expected = 0 |
| 84 | + let unexpected = 0 |
| 85 | + let flaky = 0 |
| 86 | + |
| 87 | + const unexpectedTestTitles : Array<string> = [] |
| 88 | + |
| 89 | + this.suite.allTests().forEach( test => { |
| 90 | + switch ( test.outcome() ) { |
| 91 | + case 'skipped': skipped++; break |
| 92 | + case 'expected': expected++; break |
| 93 | + case 'unexpected': |
| 94 | + unexpected++ |
| 95 | + unexpectedTestTitles.push( '- ' + test.titlePath().filter( t => t !== '' ).join( ' › ' ) ) |
| 96 | + break |
| 97 | + case 'flaky': flaky++; break |
| 98 | + } |
| 99 | + } ) |
| 100 | + |
| 101 | + if ( unexpected ) { |
| 102 | + console.log( '\nSummary:' ) |
| 103 | + console.log( `${ expected } passed` ) |
| 104 | + console.log( `${ flaky } flaky` ) |
| 105 | + console.log( `${ skipped } skipped` ) |
| 106 | + console.log( `${ unexpected } failed` ) |
| 107 | + console.log( `---\n\nFailed Tests:` ) |
| 108 | + console.log( this.failedTestErrors.join( '' ) ) |
| 109 | + |
| 110 | + const md = ` |
| 111 | +| PASSED | FLAKY | SKIPPED | FAILED | |
| 112 | +| ------ | ----- | ------- | ------ | |
| 113 | +| ${ expected } | ${ flaky } | ${ skipped } | ${ unexpected } | |
| 114 | +
|
| 115 | +Failed Tests: |
| 116 | +${ unexpectedTestTitles.join( '\n' ) } |
| 117 | +` |
| 118 | + |
| 119 | + const folderPath = path.resolve( this.outputFolder ) |
| 120 | + if ( ! fs.existsSync( folderPath ) ) { |
| 121 | + fs.mkdirSync( folderPath, { recursive: true } ) |
| 122 | + } |
| 123 | + |
| 124 | + // Write the collected results to a JSON file |
| 125 | + const reportPath = path.join( folderPath, 'errors.md' ) |
| 126 | + fs.writeFileSync( reportPath, md ) |
| 127 | + } |
| 128 | + } |
| 129 | + |
| 130 | + async onEnd() { |
| 131 | + process.stdout.write( '\n' ) |
| 132 | + this.getSummary() |
| 133 | + } |
| 134 | +} |
| 135 | + |
| 136 | +export default StkReporter |
0 commit comments