fix(@wdio/reporter): propagate failing session creation to report (#14223)
* fix(@wdio/reporter): propagate failing session creation to report * add unit tests
This commit is contained in:
parent
06aab2b51f
commit
10fe0271bd
13 changed files with 62 additions and 26 deletions
11
@types/junit-report-builder.d.ts
vendored
11
@types/junit-report-builder.d.ts
vendored
|
|
@ -1,11 +0,0 @@
|
|||
declare module "junit-report-builder" {
|
||||
interface Builder {
|
||||
build: () => void
|
||||
}
|
||||
|
||||
const cssValue: {
|
||||
newBuilder: () => Builder
|
||||
}
|
||||
|
||||
export = cssValue
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import os from 'node:os'
|
|||
|
||||
import type { RunnerStats, SuiteStats, TestStats } from '@wdio/reporter'
|
||||
import WDIOReporter from '@wdio/reporter'
|
||||
import junit from 'junit-report-builder'
|
||||
import junitReportBuilder, { type Builder as JUnitReportBuilder } from 'junit-report-builder'
|
||||
|
||||
import type { JUnitReporterOptions } from './types.js'
|
||||
import { limit } from './utils.js'
|
||||
|
|
@ -276,7 +276,7 @@ class JunitReporter extends WDIOReporter {
|
|||
}
|
||||
|
||||
private _buildJunitXml(runner: RunnerStats) {
|
||||
const builder = junit.newBuilder()
|
||||
const builder = (junitReportBuilder as unknown as JUnitReportBuilder).newBuilder()
|
||||
if (runner.config.hostname !== undefined && runner.config.hostname.indexOf('browserstack') > -1) {
|
||||
// NOTE: deviceUUID is used to build sanitizedCapabilities resulting in a ever-changing package name in runner.sanitizedCapabilities when running Android tests under Browserstack. (i.e. ht79v1a03938.android.9)
|
||||
// NOTE: platformVersion is used to build sanitizedCapabilities which can be incorrect and includes a minor version for iOS which is not guaranteed to be the same under Browserstack.
|
||||
|
|
@ -317,7 +317,14 @@ class JunitReporter extends WDIOReporter {
|
|||
return builder.build() as unknown as string
|
||||
}
|
||||
|
||||
private _buildOrderedReport(builder: any, runner: RunnerStats, specFileName: string, type: string, isCucumberFrameworkRunner: boolean) {
|
||||
private _buildOrderedReport(builder: JUnitReportBuilder, runner: RunnerStats, specFileName: string, type: string, isCucumberFrameworkRunner: boolean) {
|
||||
const suiteKeys = Object.keys(this.suites)
|
||||
|
||||
if (suiteKeys.length === 0) {
|
||||
const error = this.runnerStat?.error ?? 'No tests found'
|
||||
return builder.testCase().failure(error)
|
||||
}
|
||||
|
||||
for (const suiteKey of Object.keys(this.suites)) {
|
||||
/**
|
||||
* ignore root before all
|
||||
|
|
|
|||
|
|
@ -209,6 +209,7 @@ export default class WDIOReporter extends EventEmitter {
|
|||
if (this.runnerStat) {
|
||||
this.runnerStat.failures = runner.failures
|
||||
this.runnerStat.retries = runner.retries
|
||||
this.runnerStat.error = runner.error
|
||||
this.runnerStat.complete()
|
||||
this.onRunnerEnd(this.runnerStat)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export default class RunnerStats extends RunnableStats {
|
|||
retry?: number
|
||||
failures?: number
|
||||
retries?: number
|
||||
error?: string
|
||||
|
||||
constructor (runner: Options.RunnerStart) {
|
||||
super('runner')
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ export default class Runner extends EventEmitter {
|
|||
private _cid?: string
|
||||
private _specs?: string[]
|
||||
private _caps?: Capabilities.RequestedStandaloneCapabilities | Capabilities.RequestedMultiremoteCapabilities
|
||||
private _sessionInitError?: Error
|
||||
|
||||
/**
|
||||
* run test suite
|
||||
|
|
@ -272,7 +273,9 @@ export default class Runner extends EventEmitter {
|
|||
const browser = await this._startSession(config, caps) as WebdriverIO.Browser
|
||||
|
||||
// return null if session couldn't get established
|
||||
if (!browser) { return }
|
||||
if (!browser) {
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* register global helper method to fetch elements
|
||||
|
|
@ -366,8 +369,9 @@ export default class Runner extends EventEmitter {
|
|||
if (this._isMultiremote) {
|
||||
_setGlobal('multiremotebrowser', this._browser, config.injectGlobals)
|
||||
}
|
||||
} catch (err: any) {
|
||||
log.error(err)
|
||||
} catch (error: any) {
|
||||
log.error(error)
|
||||
this._sessionInitError = error
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -393,7 +397,7 @@ export default class Runner extends EventEmitter {
|
|||
config: this._config,
|
||||
isMultiremote: this._isMultiremote,
|
||||
instanceOptions: {},
|
||||
capabilities: { ...this._configParser!.getCapabilities() },
|
||||
capabilities: this._caps,
|
||||
retry: this._specFileRetryAttempts
|
||||
})
|
||||
}
|
||||
|
|
@ -401,7 +405,8 @@ export default class Runner extends EventEmitter {
|
|||
this._reporter!.emit('runner:end', {
|
||||
failures,
|
||||
cid: this._cid,
|
||||
retries
|
||||
retries,
|
||||
...(this._sessionInitError instanceof Error ? { error: this._sessionInitError.message } : {})
|
||||
} as Options.RunnerEnd)
|
||||
try {
|
||||
await this._reporter!.waitForSync()
|
||||
|
|
|
|||
|
|
@ -49,13 +49,13 @@ export default class BaseReporter {
|
|||
uid?: string
|
||||
file?: string
|
||||
title?: string
|
||||
error?: Error
|
||||
error?: string
|
||||
sessionId?: string
|
||||
config?: unknown
|
||||
isMultiremote?: boolean
|
||||
instanceOptions?: Options.Testrunner
|
||||
capabilities?: unknown
|
||||
retry?: number
|
||||
retry?: number,
|
||||
}) {
|
||||
payload.cid = this._cid
|
||||
|
||||
|
|
|
|||
|
|
@ -476,6 +476,7 @@ describe('wdio-runner', () => {
|
|||
const runner = new WDIORunner()
|
||||
runner['_configParser'] = { getCapabilities: vi.fn().mockReturnValue([{ browserName: 'safari' }]) } as any
|
||||
runner['_browser'] = {} as unknown as BrowserObject
|
||||
runner['_caps'] = { 'browserName': 'safari' }
|
||||
runner['_reporter'] = {
|
||||
waitForSync: vi.fn().mockReturnValue(Promise.resolve()),
|
||||
emit: vi.fn()
|
||||
|
|
@ -485,9 +486,7 @@ describe('wdio-runner', () => {
|
|||
|
||||
const args = [
|
||||
['runner:start', {
|
||||
'capabilities': {
|
||||
'0': { 'browserName': 'safari' }
|
||||
},
|
||||
'capabilities': { 'browserName': 'safari' },
|
||||
'cid': undefined,
|
||||
'config': undefined,
|
||||
'instanceOptions': {},
|
||||
|
|
|
|||
|
|
@ -213,6 +213,14 @@ export default class SpecReporter extends WDIOReporter {
|
|||
// Get the results
|
||||
const results = this.getResultDisplay(preface)
|
||||
|
||||
if (results.length === 0 && runner.error) {
|
||||
results.push(
|
||||
this.setMessageColor(`${this.getSymbol(State.FAILED)} Failed to create a session:`, State.FAILED),
|
||||
runner.error,
|
||||
''
|
||||
)
|
||||
}
|
||||
|
||||
// If there are no test results then return nothing
|
||||
if (results.length === 0) {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -273,6 +273,21 @@ exports[`SpecReporter > onlyFailures > true > 1 failure 1`] = `
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`SpecReporter > printReport > should print a report even if session could not be created 1`] = `
|
||||
[
|
||||
[
|
||||
"------------------------------------------------------------------
|
||||
[loremipsum 50 Windows 10 #0-0] Running: loremipsum (v50) on Windows 10
|
||||
[loremipsum 50 Windows 10 #0-0] Session ID: foobar
|
||||
[loremipsum 50 Windows 10 #0-0]
|
||||
[loremipsum 50 Windows 10 #0-0] red ✖ Failed to create a session:
|
||||
[loremipsum 50 Windows 10 #0-0] No tests found
|
||||
[loremipsum 50 Windows 10 #0-0]
|
||||
",
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`SpecReporter > printReport > with disabled sharable Sauce report links > should print the default Sauce Labs job details page link 1`] = `[]`;
|
||||
|
||||
exports[`SpecReporter > printReport > with normal setup > should print jobs of all instance when run with multiremote 1`] = `
|
||||
|
|
|
|||
|
|
@ -341,6 +341,16 @@ describe('SpecReporter', () => {
|
|||
|
||||
expect(printReporter.write.mock.calls.length).toBe(0)
|
||||
})
|
||||
|
||||
it('should print a report even if session could not be created', () => {
|
||||
printReporter['_suiteUids'] = []
|
||||
printReporter.suites = {}
|
||||
const runner = getRunnerConfig()
|
||||
runner.error = 'No tests found'
|
||||
|
||||
printReporter.printReport(runner)
|
||||
expect(printReporter.write.mock.calls).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
describe('getHeaderDisplay', () => {
|
||||
|
|
|
|||
|
|
@ -358,4 +358,5 @@ export interface RunnerEnd {
|
|||
failures: number
|
||||
cid: string
|
||||
retries: number
|
||||
error?: string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ export async function startWebDriverSession (params: RemoteConfig): Promise<{ se
|
|||
} catch (err) {
|
||||
log.error(err)
|
||||
const message = getSessionError(err as Error, params)
|
||||
throw new Error('Failed to create session.\n' + message)
|
||||
throw new Error(message)
|
||||
}
|
||||
const sessionId = response.value.sessionId || response.sessionId
|
||||
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ describe('utils', () => {
|
|||
logLevel: 'warn',
|
||||
capabilities: {}
|
||||
}).catch((err) => err)
|
||||
expect(error.message).toContain('Failed to create session')
|
||||
expect(error.message).toContain('Invalid URL')
|
||||
})
|
||||
|
||||
it('should break if JSONWire and WebDriver caps are mixed together', async () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue