🏡 index : quine-global/webdriverio.git

import os from 'node:os'
import path from 'node:path'
import { describe, expect, it, vi, beforeEach } from 'vitest'
import { TestStats } from '@wdio/reporter'

import WDIOJunitReporter from '../src/index.js'
import type { SuiteStats } from '@wdio/reporter'

const mochaRunnerLog = (await vi.importActual('./__fixtures__/mocha-runner.json') as any).default
const mochaRunnerNestedArrayOfSuitesLog = (await vi.importActual('./__fixtures__/mocha-runner-nested-array-specs.json') as any).default
const cucumberRunnerLog = (await vi.importActual('./__fixtures__/cucumber-runner.json') as any).default
const cucumberRunnerBrowserstackIosLog = (await vi.importActual('./__fixtures__/cucumber-runner-browserstack-ios.json') as any).default
const cucumberRunnerBrowserstackAndroidLog = (await vi.importActual('./__fixtures__/cucumber-runner-browserstack-android.json') as any).default
const cucumberRunnerBrowserstackAndroidLogMissingOS = (await vi.importActual('./__fixtures__/cucumber-runner-browserstack-android-missing-os.json') as any).default
const suitesLog = (await vi.importActual('./__fixtures__/suites.json') as any).default
const suitesWithNoErrorObjectLog = (await vi.importActual('./__fixtures__/suites-with-no-error-object.json') as any).default
const featuresLog = (await vi.importActual('./__fixtures__/cucumber-features.json') as any).default
const featuresWithFailingThenSkipStepLog = (await vi.importActual('./__fixtures__/cucumber-features-with-failed-then-skipped-steps.json') as any).default
const featuresWithPendingStepLog = (await vi.importActual('./__fixtures__/cucumber-features-with-pending-step.json') as any).default
const featuresWithErrorStepAndNoErrorObjectLog = (await vi.importActual('./__fixtures__/cucumber-features-with-error-step-and-no-error-object.json') as any).default
const nestedSuites = (await vi.importActual('./__fixtures__/nested-suites.json') as any).default
const nestedArrayOfSuites = (await vi.importActual('./__fixtures__/nested-array-suites.json') as any).default
const unorderedFeatureAndScenarioWithError = (await vi.importActual('./__fixtures__/cucumber-features-with-error-step-and-no-error-object-unordered.json') as any).default
const suitesWithFailedBeforeEachHookLog = (await vi.importActual('./__fixtures__/suites-with-failed-before-each-hook.json') as any).default
const suitesWithFailedAfterEachHookLog = (await vi.importActual('./__fixtures__/suites-with-failed-after-each-hook.json') as any).default
const suitesHooksLog = (await vi.importActual('./__fixtures__/suites-hooks.json') as any).default
const suiteTestRetry = (await vi.importActual('./__fixtures__/suite-test-retry.json') as any).default
const suitesMultipleLog = (await vi.importActual('./__fixtures__/suites-multiple.json') as any).default
const suitesErrorLog = (await vi.importActual('./__fixtures__/suites-error.json') as any).default
const suiteEmpty = (await vi.importActual('./__fixtures__/suite-empty.json') as any).default

vi.mock('@wdio/reporter', () => import(path.join(process.cwd(), '__mocks__', '@wdio/reporter')))

if (os.platform() === 'win32') {
    cucumberRunnerLog.specs = ['file:///C:/features/sample_feature.feature']
    mochaRunnerLog.specs = ['file:///C:/path/to/project/test/specs/sync.spec.js']
    mochaRunnerNestedArrayOfSuitesLog.specs = ['file:///C:/path/to/project/test/specs/sync_0.spec.js', 'file:///C:/path/to/project/test/specs/sync_1.spec.js']
    cucumberRunnerBrowserstackAndroidLogMissingOS.specs = ['file:///C:/features/sample_feature.feature']
    cucumberRunnerBrowserstackAndroidLog.specs = ['file:///C:/features/sample_feature.feature']
    cucumberRunnerBrowserstackIosLog.specs = ['file:///C:/features/sample_feature.feature']
    for (const fixture of [featuresLog, featuresWithPendingStepLog, unorderedFeatureAndScenarioWithError, featuresWithErrorStepAndNoErrorObjectLog, featuresWithFailingThenSkipStepLog]) {
        for (const [, suite] of Object.entries(fixture) as any) {
            suite.file = 'C:\\features\\sample_feature.feature'
        }
    }

    for (const fixture of [suitesLog, suitesErrorLog, suitesHooksLog, suiteTestRetry, suitesMultipleLog, suitesWithFailedAfterEachHookLog, suitesWithFailedBeforeEachHookLog, suitesWithNoErrorObjectLog, nestedArrayOfSuites, nestedSuites, suiteEmpty]) {
        for (const [index, [, suite]] of Object.entries(Object.entries(fixture)) as any) {
            const specFileName = (fixture === nestedArrayOfSuites) ? `sync_${index}` : 'sync'
            suite.file = `C:\\path\\to\\project\\test\\specs\\${specFileName}.spec.js`
        }
    }
}

describe('wdio-junit-reporter', () => {
    let reporter: WDIOJunitReporter

    beforeEach(() => {
        reporter = new WDIOJunitReporter({ stdout: true })
    })

    it('should write to output stream on runnerEnd', () => {
        reporter['_buildJunitXml'] = vi.fn().mockReturnValue(undefined)
        reporter.write = vi.fn()
        reporter.onRunnerEnd({} as any)
        expect(vi.mocked(reporter.write).mock.calls[0][0]).toMatchSnapshot()
    })

    it('should prepare name', () => {
        expect(reporter['_prepareName']()).toMatchSnapshot()
        expect(reporter['_prepareName']('Chrome something_odd.foobar')).toMatchSnapshot()
        expect(reporter['_prepareName']('Chrome @something_odd.foobar')).toMatchSnapshot()
    })

    it('has a suiteNameFormat option for setting names manually', () => {
        reporter = new WDIOJunitReporter({ stdout: true, suiteNameFormat: /foobar/ })
        expect(reporter['_prepareName']('Skipped_foobar_test')).toMatchSnapshot()
    })

    it('can format output', () => {
        expect(reporter['_format'](1)).toMatchSnapshot()
        expect(reporter['_format']({ a: 'foo', b: 'bar' })).toMatchSnapshot()
        expect(reporter['_format']({ a: 'foobar'.repeat(100) })).toMatchSnapshot()
        expect(reporter['_format']({ a: '@'.repeat(10000) })).toMatchSnapshot()
        expect(reporter['_format']({ a: '@'.repeat(100).split('') })).toMatchSnapshot()
        expect(reporter['_format']([...Array(11)].map((item, i) => i).reduce((a, b) => {
            a['entry' + b] = 'foobar'
            return a
        }, {} as Record<string, string>))).toBe('{"entry0":"foobar","entry1":"foobar","entry2":"foobar","entry3":"foobar","entry4":"foobar","entry5":"foobar","entry6":"foobar","entry7":"foobar","entry8":"foobar","entry9":"foobar","_":"1 more keys: [\\"entry10\\"]"}')
    })

    it('can translate stdout', () => {
        const teststats: TestStats = {
            output: [{
                cid: '123',
                type: 'command',
                method: 'POST',
                sessionId: 'foobar',
                endpoint: '/sessionId/click',
                body: { elementId: 'foobar' },
                command: 'getText',
                params: {},
                result: { value: 'some text' }
            }, {
                cid: '123',
                method: 'POST',
                sessionId: 'foobar',
                endpoint: '/sessionId/click',
                command: 'getText',
                params: {},
                result: { value: 'some text' },
                type: 'result',
                body: { value: 'foobar' }
            }]
        } as unknown as TestStats
        expect(reporter['_getStandardOutput'](teststats)).toContain('COMMAND: POST /sessionId/click - {"elementId":"foobar"}')
        expect(reporter['_getStandardOutput'](teststats)).toContain('RESULT: {"value":"foobar"}')
    })

    it('test is marked as skipped when is retried', () => {
        const testStats = new TestStats({
            type: 'test:start',
            uid: 'test-00-0',
            cid: '0-0',
            title: 'test',
            fullTitle: 'suite test',
            retries: 0,
            parent: 'suite',
            pending: false,
            specs: []
        })
        reporter['onTestRetry'](testStats)

        expect(testStats.state).toContain('skipped')
    })

    it('generates xml output', () => {
        reporter.suites = suitesLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](mochaRunnerLog as any).replace(/\s/g, '').replace(/file:\/\//g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output (Cucumber-style)', () => {
        reporter.suites = featuresLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](cucumberRunnerLog as any).replace(/\s/g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output (Cucumber-style) (with packageName)', () => {
        reporter = new WDIOJunitReporter({ packageName: 'wdio-unit-tests', stdout: true })
        reporter.suites = featuresLog as any
        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](cucumberRunnerLog as any).replace(/\s/g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('scenario will be marked failed if a single scenario step fails (Cucumber-style)', () => {
        reporter.suites = featuresWithFailingThenSkipStepLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](cucumberRunnerLog as any).replace(/\s/g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('scenario will be marked failed if a single scenario step throws an error but there is no error object in JSON', () => {
        reporter.suites = suitesWithNoErrorObjectLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](mochaRunnerLog as any).replace(/\s/g, '').replace(/file:\/\//g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('scenario will be marked failed if a single scenario step throws an error but there is no error object in JSON (Cucumber-style)', () => {
        reporter.suites = featuresWithErrorStepAndNoErrorObjectLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](cucumberRunnerLog as any).replace(/\s/g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('should build the xml if feature and scenario are unordered in JSON (Cucumber-style)', () => {
        reporter.suites = unorderedFeatureAndScenarioWithError as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](cucumberRunnerLog as any).replace(/\s/g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('scenario will be marked skipped if a single scenario step is pending (Cucumber-style)', () => {
        reporter.suites = featuresWithPendingStepLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](cucumberRunnerLog as any).replace(/\s/g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output if before all hook failed', () => {
        reporter.suites = suitesHooksLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](mochaRunnerLog as any).replace(/\s/g, '').replace(/file:\/\//g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output if before each hook failed', () => {
        reporter.suites = suitesWithFailedBeforeEachHookLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](mochaRunnerLog as any).replace(/\s/g, '').replace(/file:\/\//g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output if after each hook failed', () => {
        reporter.suites = suitesWithFailedAfterEachHookLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](mochaRunnerLog as any).replace(/\s/g, '').replace(/file:\/\//g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output for multiple describe blocks', () => {
        reporter.suites = suitesMultipleLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](mochaRunnerLog as any).replace(/\s/g, '').replace(/file:\/\//g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output when test is marked as skipped', () => {
        reporter.suites = suiteTestRetry as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](mochaRunnerLog as any).replace(/\s/g, '').replace(/file:\/\//g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output correctly when error options are set', () => {
        const errorOptions = {
            error: 'message',
            failure: 'message',
            stacktrace: 'stack'
        }

        reporter = new WDIOJunitReporter({ stdout: true, errorOptions })
        reporter.suites = suitesLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](mochaRunnerLog as any).replace(/\s/g, '').replace(/file:\/\//g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output correctly when error options are set (Cucumber-style)', () => {
        const errorOptions = {
            error: 'message',
            failure: 'message',
            stacktrace: 'stack'
        }

        reporter = new WDIOJunitReporter({ stdout: true, errorOptions })
        reporter.suites = featuresLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](cucumberRunnerLog as any).replace(/\s/g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output with correct information when test is ran against Browserstack for iOS apps', () => {
        reporter = new WDIOJunitReporter({ stdout: true })
        reporter.suites = featuresLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](cucumberRunnerBrowserstackIosLog as any).replace(/\s/g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output with correct information when test is ran against Browserstack for Android apps', () => {
        reporter = new WDIOJunitReporter({ stdout: true })
        reporter.suites = featuresLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](cucumberRunnerBrowserstackAndroidLog as any).replace(/\s/g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('ensures that capabilities passed to buildJunitXml are not null/undefined', () => {
        reporter = new WDIOJunitReporter({})
        reporter.suites = featuresLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](cucumberRunnerBrowserstackAndroidLogMissingOS as any).replace(/\s/g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output correctly when the addFileAttribute option is set', () => {
        reporter = new WDIOJunitReporter({ stdout: true, addFileAttribute: true })
        reporter.suites = suitesLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](mochaRunnerLog as any).replace(/\s/g, '').replace(/file:\/\//g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output correctly when the addFileAttribute option is set (Cucumber-style)', () => {
        reporter = new WDIOJunitReporter({ stdout: true, addFileAttribute: true })
        reporter.suites = featuresLog as any

        // verifies the content of the report but omits format by stripping all whitespace and new lines
        expect(reporter['_buildJunitXml'](cucumberRunnerLog as any).replace(/\s/g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output without ansi', () => {
        reporter.suites = suitesErrorLog as any
        expect(reporter['_buildJunitXml'](mochaRunnerLog as any).replace(/\s/g, '').replace(/file:\/\//g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output correctly when having nested suites', () => {
        reporter.suites = nestedSuites as any
        expect(reporter['_buildJunitXml'](mochaRunnerLog as any).replace(/\s/g, '').replace(/file:\/\//g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output correctly when having nested array of suites', () => {
        reporter.suites = nestedArrayOfSuites as any
        expect(reporter['_buildJunitXml'](mochaRunnerNestedArrayOfSuitesLog as any).replace(/\s/g, '').replace(/file:\/\//g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output correctly when having classNameFormat override with mocha', () => {
        reporter = new WDIOJunitReporter({ stdout: true, classNameFormat: ({ packageName, suite }) => `foo-${packageName}-${suite!.fullTitle}` })
        reporter.suites = suitesErrorLog as any
        expect(reporter['_buildJunitXml'](mochaRunnerLog as any).replace(/\s/g, '').replace(/file:\/\//g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('generates xml output correctly when having classNameFormat override with cucumber', () => {
        reporter = new WDIOJunitReporter({ stdout: true, classNameFormat: ({ packageName, activeFeatureName }) => `foo-${packageName}-${activeFeatureName}` })
        reporter.suites = featuresLog as any

        expect(reporter['_buildJunitXml'](cucumberRunnerLog as any).replace(/\s/g, '').replace('C:/', '')).toMatchSnapshot()
    })

    it('generates xml output correctly when having testSuiteNameFormat override with mocha', () => {
        reporter = new WDIOJunitReporter({ stdout: true, suiteNameFormat: ({ name, suite }) => `foo ${name} ${suite.title}` })
        reporter.suites = suitesErrorLog as any
        expect(reporter['_buildJunitXml'](mochaRunnerLog as any).replace(/\s/g, '').replace('file://', '').replace('C:/', '')).toMatchSnapshot()
    })

    it('generates xml output correctly when having testSuiteNameFormat override with cucumber', () => {
        reporter = new WDIOJunitReporter({ stdout: true, suiteNameFormat: ({ name, suite }) => `foo-${name}-${suite.title}` })
        reporter.suites = featuresLog as any

        expect(reporter['_buildJunitXml'](cucumberRunnerLog as any).replace(/\s/g, '').replace('C:/', '')).toMatchSnapshot()
    })

    it('generates xml output correctly with empty suite', () => {
        reporter.suites = suiteEmpty as any
        expect(reporter['_buildJunitXml'](mochaRunnerLog as any).replace(/\s/g, '').replace(/file:\/\//g, '').replace(/C:\//g, '')).toMatchSnapshot()
    })

    it('_buildOrderedReport', () => {
        reporter = new WDIOJunitReporter({ stdout: true, suiteNameFormat: ({ name, suite }) => `foo-${name}-${suite.title}` })
        reporter['_addCucumberFeatureToBuilder'] = () => '_addCucumberFeatureToBuilder'
        reporter['_addSuiteToBuilder'] = () => '_addSuiteToBuilder'
        const specFileName = os.platform() === 'win32' ? 'file:///C:/foo/bar' : 'file:///foo/bar'
        const file = os.platform() === 'win32' ? 'C:\\foo\\bar' : '/foo/bar'
        reporter.suites = {
            foo: { type: 'feature' } as any,
            bar: { type: 'feature', file } as any
        }
        expect(reporter['_buildOrderedReport'](null, null as any, specFileName, 'feature', true)).toBe('_addCucumberFeatureToBuilder')
    })

    it('_sameFileName - case independent on win32', function (context) {
        if (os.platform() !== 'win32') {
            context.skip()
        }
        expect(reporter['_sameFileName']('file:///C:/foo/bar', 'file:///C:/foo/bar')).toBeTruthy()
        expect(reporter['_sameFileName']('file:///c:/foo/bar', 'file:///C:/foo/bar')).toBeTruthy()
        expect(reporter['_sameFileName']('file:///c:/foo/bar', 'C:\\foo\\bar')).toBeTruthy()
        expect(reporter['_sameFileName']('file:///C:/foo/bar', 'c:\\foo\\bar')).toBeTruthy()
        expect(reporter['_sameFileName']('file:///C:/foo/bar', 'C:\\FOO\\bar')).toBeTruthy()
        expect(reporter['_sameFileName']('file:///c:/foo/bar', 'C:\\bar\\foo')).toBeFalsy()
    })

    it('_sameFileName - case sensitive on everything except win32', function (context) {
        if (os.platform() === 'win32') {
            context.skip()
        }
        expect(reporter['_sameFileName']('file:///foo/bar', 'file:///Foo/bar')).toBeFalsy()
        expect(reporter['_sameFileName']('file:///foo/bar', '/Foo/bar')).toBeFalsy()
    })

    it('_sameFileName', () => {
        reporter = new WDIOJunitReporter({ stdout: true })
        const file1URL = os.platform() === 'win32' ? 'file:///C:/foo/bar' : 'file:///foo/bar'
        const file1Path = os.platform() === 'win32' ? 'C:\\foo\\bar' : '/foo/bar'
        const file2URL = os.platform() === 'win32' ? 'file:///C:/bar/foo' : 'file:///bar/foo'
        const file2Path = os.platform() === 'win32' ? 'C:\\bar\\foo' : '/bar/foo'
        expect(reporter['_sameFileName'](file1URL, file1URL)).toBeTruthy()
        expect(reporter['_sameFileName'](file1URL, file1Path)).toBeTruthy()
        expect(reporter['_sameFileName'](file1Path, file1URL)).toBeTruthy()
        expect(reporter['_sameFileName'](file1Path, file1Path)).toBeTruthy()
        expect(reporter['_sameFileName'](file1URL, file2URL)).toBeFalsy()
        expect(reporter['_sameFileName'](file1URL, file2Path)).toBeFalsy()
        expect(reporter['_sameFileName'](file1Path, file2URL)).toBeFalsy()
        expect(reporter['_sameFileName'](file1Path, file2Path)).toBeFalsy()
        expect(reporter['_sameFileName'](undefined, file1URL)).toBeFalsy()
        expect(reporter['_sameFileName'](undefined, file1Path)).toBeFalsy()
        expect(reporter['_sameFileName'](file1URL, undefined)).toBeFalsy()
        expect(reporter['_sameFileName'](file1Path, undefined)).toBeFalsy()
        expect(reporter['_sameFileName'](undefined, undefined)).toBeTruthy()
    })

    const options = { stdout: true, addWorkerLogs: true }

    it('addWorkerLogs: should add worker console log to report for test if activated', () => {
        reporter = new WDIOJunitReporter(options)
        const suite = Object.values(suitesLog)[0] as SuiteStats
        reporter.onSuiteStart(suite)
        const test1 = suite.tests[0]
        const test2 = suite.tests[1]
        reporter.onTestStart(test1)
        reporter['_appendConsoleLog']('0 - line 0', 'utf-8', () => {})
        reporter['_appendConsoleLog']('0 - line 1', 'utf-8', () => {})
        reporter.onTestPass(test1)
        reporter.onTestStart(test2)
        reporter['_appendConsoleLog']('1 - line 0', 'utf-8', () => {})
        reporter['_appendConsoleLog']('1 - line 1', 'utf-8', () => {})
        reporter.onTestPass(suite.tests[0])
        expect(reporter['_getStandardOutput'](test1).toString()).toContain('0 - line 1')
        expect(reporter['_getStandardOutput'](test2).toString()).toContain('1 - line 1')

        expect(reporter['_getStandardOutput'](test1).toString()).not.toContain('1 - line 1')
        expect(reporter['_getStandardOutput'](test2).toString()).not.toContain('0 - line 1')
    })

    it('addProperty adds a property to currently running testcase', () => {
        reporter = new WDIOJunitReporter(options)
        reporter.suites = suitesLog as any
        const suite = Object.values(suitesLog)[0] as SuiteStats
        reporter.onSuiteStart(suite)
        const test1 = suite.tests[0]
        const test2 = suite.tests[1]
        reporter.onTestStart(test1)
        reporter['_addPropertyToCurrentTest']({ name: '0-prop1', value: '0-value' })
        reporter.onTestPass(test1)
        reporter.onTestStart(test2)
        reporter['_addPropertyToCurrentTest']({ name: '1-prop1', value: '1-value' })
        reporter.onTestPass(suite.tests[0])
        const output = reporter['_buildJunitXml'](mochaRunnerLog).toString()
        expect(output).toContain('<property name="0-prop1" value="0-value"/>')
    })
})