🏑 index : quine-global/webdriverio.git

import path from 'node:path'
import { describe, it, expect, beforeEach, vi, beforeAll, afterAll } from 'vitest'
import type { HookStats } from '@wdio/types'
import type { Parameter, Label, Link } from 'allure-js-commons'
import { Stage, Status, LabelName, LinkType, ContentType } from 'allure-js-commons'
import AllureReporter from '../src/reporter.js'
import { linkPlaceholder } from '../src/constants.js'
import { TYPE } from '../src/types.js'
import { getResults, clean } from './helpers/wdio-allure-helper.js'

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

import { temporaryDirectory } from 'tempy'
import { runnerEnd, runnerStart } from './__fixtures__/runner.js'
import { testStart } from './__fixtures__/testState.js'
import { suiteStart, suiteEnd } from './__fixtures__/suite.js'

let processOn: any

beforeAll(() => {
    processOn = process.on.bind(process)
    process.on = vi.fn()
})

afterAll(() => {
    process.on = processOn
})

beforeEach(() => {
    vi.clearAllMocks()
})

describe('reporter runtime implementation', () => {
    const outputDir = temporaryDirectory()

    beforeEach(() => {
        clean(outputDir)
    })

    it('should correct add custom label', () => {
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addLabel({ name: 'customLabel', value: 'Label' })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].labels.find((label: Label) => label.name === 'customLabel').value).toEqual('Label')
    })

    it('should correct add story label', () => {
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addStory({ storyName: 'foo' })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].labels.find((label: Label) => label.name === LabelName.STORY).value).toEqual('foo')
    })

    it('should correct add feature label', () => {
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addFeature({ featureName: 'foo' })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].labels.find((label: Label) => label.name === LabelName.FEATURE).value).toEqual('foo')
    })

    it('should correct add severity label', () => {
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addSeverity({ severity: 'foo' })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].labels.find((label: Label) => label.name === LabelName.SEVERITY).value).toEqual('foo')
    })

    it('should correct add suite label', () => {
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addSuite({ suiteName: 'foo' })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].labels.find((label: Label) => label.name === LabelName.SUITE).value).toEqual('foo')
    })

    it('should correct add subSuite label', () => {
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addSubSuite({ suiteName: 'foo' })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].labels.find((label: Label) => label.name === LabelName.SUB_SUITE).value).toEqual('foo')
    })

    it('should correct add parentSuite label', () => {
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addParentSuite({ suiteName: 'foo' })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].labels.find((label: Label) => label.name === LabelName.PARENT_SUITE).value).toEqual('foo')
    })

    it('should correct add tag label', () => {
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addTag({ tag: 'foo' })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].labels.find((label: Label) => label.name === LabelName.TAG).value).toEqual('foo')
    })

    it('should correct add epic label', () => {
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addEpic({ epicName: 'foo' })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].labels.find((label: Label) => label.name === LabelName.EPIC).value).toEqual('foo')
    })

    it('should correct add allure id label', () => {
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addAllureId({ id: 'foo' })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].labels.find((label: Label) => label.name === LabelName.AS_ID).value).toEqual('foo')
    })

    it('should correctly add issue label with link', () => {
        const reporter = new AllureReporter({ outputDir, issueLinkTemplate: `http://example.com/${linkPlaceholder}` })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addIssue({ issue: '1' })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].links.find((link: Link) => link.type === LinkType.ISSUE).url).toEqual('http://example.com/1')
    })

    it('should correctly add test id label with link', () => {
        const reporter = new AllureReporter({ outputDir, tmsLinkTemplate: `https://webdriver.io/${linkPlaceholder}` })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addTestId({ testId: '2' })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].links.find((link: Link) => link.type === LinkType.TMS).url).toEqual('https://webdriver.io/2')
    })

    it('should correct add description', () => {
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addDescription({
            description: 'foo',
            descriptionType: TYPE.MARKDOWN
        })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].description).toEqual('foo')
    })

    it('should correct add html description', () => {
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addDescription({
            description: 'foo',
            descriptionType: TYPE.HTML
        })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].descriptionHtml).toEqual('foo')
    })

    it('should correct add attachment', () => {
        const reporter = new AllureReporter({ outputDir })
        const attachFileSpy = vi.spyOn(reporter, 'attachFile')

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addAttachment({ name: 'foo', content: 'bar', type: ContentType.TEXT })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].attachments).toHaveLength(1)
        expect(results[0].attachments[0]).toEqual({
            name: 'foo',
            type: ContentType.TEXT,
            source: expect.any(String),
        })
        expect(attachFileSpy).toHaveBeenCalledWith('foo', Buffer.from('bar'), ContentType.TEXT)
    })

    it('should correct add "application/json" attachment', () => {
        const reporter = new AllureReporter({ outputDir })
        const attachJSONSpy = vi.spyOn(reporter, 'attachJSON')

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addAttachment({ name: 'foo', content: { bar: 'bar' }, type: ContentType.JSON })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].attachments).toHaveLength(1)
        expect(results[0].attachments[0]).toEqual({
            name: 'foo',
            type: ContentType.JSON,
            source: expect.any(String),
        })
        expect(attachJSONSpy).toHaveBeenCalledWith('foo', { bar: 'bar' })
    })

    it('should allow to start end step', () => {
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.startStep('bar')
        reporter.endStep(Status.FAILED)
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].steps).toHaveLength(1)
        expect(results[0].steps[0].status).toEqual(Status.FAILED)
        expect(results[0].steps[0].stage).toEqual(Stage.FINISHED)
    })

    it('should correct add step with attachment', () => {
        const step = {
            attachment: { content: 'baz', name: 'attachment' },
            status: Status.PASSED,
            title: 'foo'
        }
        const reporter = new AllureReporter({ outputDir })
        const attachFileSpy = vi.spyOn(reporter, 'attachFile')

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addStep({ step })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].steps).toHaveLength(1)
        expect(results[0].steps[0].name).toEqual(step.title)
        expect(results[0].steps[0].status).toEqual(step.status)
        expect(results[0].steps[0].stage).toEqual(Stage.FINISHED)
        expect(results[0].steps[0].attachments).toHaveLength(1)
        expect(results[0].steps[0].attachments[0]).toEqual({
            name: step.attachment.name,
            type: ContentType.TEXT,
            source: expect.any(String),
        })
        expect(attachFileSpy).toHaveBeenCalledWith(step.attachment.name, step.attachment.content, ContentType.TEXT)
    })

    it('should correct add step without attachment', () => {
        const step = {
            status: Status.PASSED,
            title: 'foo'
        }
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addStep({ step })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].steps).toHaveLength(1)
        expect(results[0].steps[0].name).toEqual(step.title)
        expect(results[0].steps[0].status).toEqual(step.status)
        expect(results[0].steps[0].stage).toEqual(Stage.FINISHED)
    })

    it('should correctly add parameter', () => {
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.addArgument({ name: 'os', value: 'osx' })
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)
        const osParameter = results[0].parameters.find((parameter: Parameter) => parameter.name === 'os')

        expect(results).toHaveLength(1)
        expect(results[0].historyId).toEqual('3cffc3d1d78d183463efee4c042f156c')
        expect(osParameter.value).toEqual('osx')
    })

    it('should do nothing if no tests run', () => {
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart(runnerStart())
        reporter.addLabel({ name: 'foo', value: 'bar' })
        reporter.addStory({ storyName: 'foobar' })
        reporter.addFeature({ featureName: 'foobar' })
        reporter.addSeverity({ severity: 'foobar' })
        reporter.addIssue({ issue: 'foobar' })
        reporter.addTestId({ testId: '123' })
        reporter.addDescription({ description: 'foobar' })
        reporter.addAttachment({ name: '', content: '', type: '' })
        reporter.startStep('test')
        reporter.endStep(Status.PASSED)
        reporter.addStep({})
        reporter.addArgument({})
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(0)
    })

    describe('add argument', () => {
        it('should correctly add argument for selenium', () => {
            const reporter = new AllureReporter({ outputDir })

            reporter.onRunnerStart({
                ...runnerStart(),
                capabilities: { browserName: 'firefox', version: '1.2.3' },
            })
            reporter.onTestStart(testStart())
            reporter.onTestPass()
            reporter.onRunnerEnd(runnerEnd())

            const { results } = getResults(outputDir)

            expect(results).toHaveLength(1)
            expect(results[0].parameters).toHaveLength(1)
            expect(results[0].parameters[0]).toEqual({ name: 'browser', value: 'firefox-1.2.3' })
        })

        it('should correctly set proper browser version for chrome headless in devtools', () => {
            const reporter = new AllureReporter({ outputDir })

            reporter.onRunnerStart({
                ...runnerStart(),
                capabilities: { browserName: 'Chrome Headless', browserVersion: '85.0.4183.84' },
            })
            reporter.onTestStart(testStart())
            reporter.onTestPass()
            reporter.onRunnerEnd(runnerEnd())

            const { results } = getResults(outputDir)

            expect(results).toHaveLength(1)
            expect(results[0].parameters).toHaveLength(1)
            expect(results[0].parameters[0]).toEqual({ name: 'browser', value: 'Chrome Headless-85.0.4183.84' })
        })

        it('should correctly add argument for appium', () => {
            const reporter = new AllureReporter({ outputDir })

            reporter.onRunnerStart({
                ...runnerStart(),
                capabilities: {
                    deviceName: 'Android Emulator',
                    'appium:platformVersion': '8.0'
                },
            })
            reporter.onTestStart(testStart())
            reporter.onTestPass()
            reporter.onRunnerEnd(runnerEnd())

            const { results } = getResults(outputDir)

            expect(results).toHaveLength(1)
            expect(results[0].parameters).toHaveLength(1)
            expect(results[0].parameters[0]).toEqual({ name: 'device', value: 'Android Emulator-8.0' })
        })

        it('should correctly add device name when run on BrowserStack', () => {
            const reporter = new AllureReporter({ outputDir })

            reporter.onRunnerStart({
                ...runnerStart(),
                capabilities: { device: 'Google Pixel 3', 'appium:platformVersion': '9.0' },
            })
            reporter.onTestStart(testStart())
            reporter.onTestPass()
            reporter.onRunnerEnd(runnerEnd())

            const { results } = getResults(outputDir)

            expect(results).toHaveLength(1)
            expect(results[0].parameters).toHaveLength(1)
            expect(results[0].parameters[0]).toEqual({ name: 'device', value: 'Google Pixel 3-9.0' })
        })

        it('should correctly add argument for multiremote', () => {
            const reporter = new AllureReporter({ outputDir })

            reporter.onRunnerStart({
                ...runnerStart(),
                isMultiremote: true,
                capabilities: { myBrowser: { browserName: 'chrome' } },
            })
            reporter.onTestStart(testStart())
            reporter.onTestPass()
            reporter.onRunnerEnd(runnerEnd())

            const { results } = getResults(outputDir)

            expect(results).toHaveLength(1)
            expect(results[0].parameters).toHaveLength(1)
            expect(results[0].parameters[0]).toEqual({ name: 'isMultiremote', value: 'true' })
        })
    })

    describe('add allure step', () => {
        it('should add labels from custom steps', () => {
            const reporter = new AllureReporter({ outputDir })

            reporter.onRunnerStart({
                ...runnerStart(),
                isMultiremote: true,
                capabilities: { myBrowser: { browserName: 'chrome' } },
            })
            reporter.onTestStart(testStart())
            reporter.addAllureStep({
                labels: [{ name: 'foo', value: 'bar' }],
            })
            reporter.onTestPass()
            reporter.onRunnerEnd(runnerEnd())

            const { results } = getResults(outputDir)
            const childStepLabel = results[0].labels.find((label: Label) => label.name === 'foo')

            expect(results).toHaveLength(1)
            expect(childStepLabel.value).toEqual('bar')
        })

        it('should add parameters from custom steps', () => {
            const reporter = new AllureReporter({ outputDir })

            reporter.onRunnerStart({
                ...runnerStart(),
                isMultiremote: true,
                capabilities: { myBrowser: { browserName: 'chrome' } },
            })
            reporter.onTestStart(testStart())
            reporter.addAllureStep({
                parameter: [{ name: 'foo', value: 'bar' }],
            })
            reporter.onTestPass()
            reporter.onRunnerEnd(runnerEnd())

            const { results } = getResults(outputDir)
            const childStepParameter = results[0].parameters.find((param: Parameter) => param.name === 'foo')

            expect(results).toHaveLength(1)
            expect(childStepParameter.value).toEqual('bar')
        })

        it('should add links from custom steps', () => {
            const reporter = new AllureReporter({ outputDir })

            reporter.onRunnerStart({
                ...runnerStart(),
                isMultiremote: true,
                capabilities: { myBrowser: { browserName: 'chrome' } },
            })
            reporter.onTestStart(testStart())
            reporter.addAllureStep({
                links: [{ name: 'foo', url: 'http://example.org', type: 'type' }],
            })
            reporter.onTestPass()
            reporter.onRunnerEnd(runnerEnd())

            const { results } = getResults(outputDir)
            const childStepLink = results[0].links.find((link: Link) => link.name === 'foo')

            expect(results).toHaveLength(1)
            expect(childStepLink.url).toEqual('http://example.org')
            expect(childStepLink.type).toEqual('type')
        })

        it('should add description from custom steps', () => {
            const reporter = new AllureReporter({ outputDir })

            reporter.onRunnerStart({
                ...runnerStart(),
                isMultiremote: true,
                capabilities: { myBrowser: { browserName: 'chrome' } },
            })
            reporter.onTestStart(testStart())
            reporter.addAllureStep({
                description: 'foo'
            })
            reporter.onTestPass()
            reporter.onRunnerEnd(runnerEnd())

            const { results } = getResults(outputDir)

            expect(results).toHaveLength(1)
            expect(results[0].description).toEqual('foo')
        })

        it('should add html description from custom steps', () => {
            const reporter = new AllureReporter({ outputDir })

            reporter.onRunnerStart({
                ...runnerStart(),
                isMultiremote: true,
                capabilities: { myBrowser: { browserName: 'chrome' } },
            })
            reporter.onTestStart(testStart())
            reporter.addAllureStep({
                descriptionHtml: 'foo'
            })
            reporter.onTestPass()
            reporter.onRunnerEnd(runnerEnd())

            const { results } = getResults(outputDir)

            expect(results).toHaveLength(1)
            expect(results[0].descriptionHtml).toEqual('foo')
        })

        it('should add custom steps as children to current test', () => {
            const reporter = new AllureReporter({ outputDir })

            reporter.onRunnerStart({
                ...runnerStart(),
                isMultiremote: true,
                capabilities: { myBrowser: { browserName: 'chrome' } },
            })
            reporter.onTestStart(testStart())
            reporter.addAllureStep({
                steps: [
                    {
                        name: 'custom step',
                        attachments: [],
                        parameters: [],
                        steps: [],
                        status: Status.PASSED,
                        statusDetails: {
                            message: undefined,
                            trace: undefined,
                        },
                        stage: Stage.FINISHED,
                    }
                ]
            })
            reporter.onTestPass()
            reporter.onRunnerEnd(runnerEnd())

            const { results } = getResults(outputDir)

            expect(results).toHaveLength(1)
            expect(results[0].steps).toHaveLength(1)
        })
    })
})

describe('auxiliary methods', () => {
    const outputDir = temporaryDirectory()

    beforeEach(() => {
        clean(outputDir)
    })

    it('should populate the correct deviceName', () => {
        const capabilities = {
            deviceName: 'emulator',
            desired: {
                platformName: 'Android',
                'appium:automationName': 'UiAutomator2',
                'appium:deviceName': 'Android GoogleAPI Emulator',
                'appium:platformVersion': '6.0',
                'appium:noReset': true,
            }
        }
        const reporter = new AllureReporter({ outputDir })

        reporter.onRunnerStart({
            ...runnerStart(),
            capabilities,
        })
        reporter.onTestStart(testStart())
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].parameters).toHaveLength(1)
        expect(results[0].parameters[0]).toEqual({ name: 'device', value: 'Android GoogleAPI Emulator 6.0' })
    })
})

// TODO: check twice the checks here, seems some of them doesn't look so obvious and good
describe('hooks handling disabled Mocha Hooks', () => {
    let reporter: any
    const outputDir = temporaryDirectory()

    beforeEach(() => {
        clean(outputDir)
        reporter = new AllureReporter({ outputDir, disableMochaHooks: true })
    })

    it('should not add test on custom hook', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart(suiteStart())
        reporter.onHookStart({ cid: '0-0', title: 'foo', parent: 'bar' } as HookStats)
        reporter.onHookEnd({ cid: '0-0', title: 'foo', parent: 'bar' } as HookStats)
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(0)
    })

    it('should not add test if no suite', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onHookStart({ cid: '0-0', title: 'foo', parent: 'bar' } as HookStats)
        reporter.onHookEnd({ cid: '0-0', title: 'foo', parent: 'bar' } as HookStats)
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(0)
    })

    it('should ignore global mocha hooks', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onHookStart({ title: '"after all" hook', parent: '' } as HookStats)
        reporter.onHookEnd({ title: '"after all" hook', parent: '' } as HookStats)
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(0)
    })

    it('should not capture mocha/jasmine each hooks', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart(suiteStart())
        reporter.onTestStart(testStart())
        reporter.onHookStart({ cid: '0-0', title: '"before each" hook', parent: 'foo' } as HookStats)
        reporter.onHookEnd({ cid: '0-0', title: '"before each" hook', parent: 'foo' } as HookStats)
        reporter.onTestPass()
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results, containers } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].status).toEqual(Status.PASSED)
        expect(results[0].stage).toEqual(Stage.FINISHED)
        expect(containers[0].befores).toHaveLength(0)
    })

    it('should ignore passed mocha/jasmine each hooks if no test', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart(suiteStart())
        reporter.onHookStart({ cid: '0-0', title: '"after each" hook', parent: 'foo' } as HookStats)
        reporter.onHookEnd({ cid: '0-0', title: '"after each" hook', parent: 'foo' } as HookStats)
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results, containers } = getResults(outputDir)

        expect(containers[0].afters).toHaveLength(0)
        expect(containers[0].children).toHaveLength(0)
        expect(containers[0].befores).toHaveLength(0)
        expect(results).toHaveLength(0)
    })

    it('should not start test onHookStart if no suite', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onTestStart(testStart())
        reporter.onHookStart({ cid: '0-0', title: 'foo', parent: 'foo' } as HookStats)
        reporter.onHookEnd({ cid: '0-0', title: 'foo', parent: 'foo' } as HookStats)
        reporter.onTestPass()
        reporter.onRunnerEnd(runnerEnd())

        const { results, containers } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].name).toEqual('should can do something')
        expect(containers).toHaveLength(0)
    })

    it('should ignore custom onHookEnd if no test', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart(testStart())
        reporter.onHookEnd({ cid: '0-0', title: 'foo', parent: 'foo' } as HookStats)
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results, containers } = getResults(outputDir)

        expect(results).toHaveLength(0)
        expect(containers[0].befores).toHaveLength(0)
        expect(containers[0].afters).toHaveLength(0)
    })

    it('should ignore global mocha/jasmine end hooks', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart(testStart())
        reporter.onHookEnd({ cid: '0-0', title: 'foo' } as HookStats)
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results, containers } = getResults(outputDir)

        expect(results).toHaveLength(0)
        expect(containers[0].befores).toHaveLength(0)
    })

    it('should not pop test case if no steps and before hook', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart(testStart())
        reporter.onTestStart(testStart())
        reporter.onHookEnd({ cid: '0-0', title: '"before all" hook', parent: 'foo' } as HookStats)
        reporter.onTestPass()
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].name).toEqual('should can do something')
        expect(results[0].steps).toHaveLength(0)
    })

    it('should pop test case if no steps and custom hook', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart(testStart())
        reporter.onTestStart(testStart())
        reporter.onHookEnd({ cid: '0-0', title: 'bar', parent: 'foo' } as HookStats)
        reporter.onTestPass()
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results, containers } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(results[0].name).toEqual('should can do something')
        expect(containers[0].children).toHaveLength(1)
        expect(containers[0].befores).toHaveLength(0)
        expect(containers[0].afters).toHaveLength(0)
        expect(results[0].steps).toHaveLength(0)
    })

    it('should keep passed custom hooks if there are some steps', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart(testStart())
        reporter.onTestStart(testStart())
        reporter.onBeforeCommand({ command: 'SomeCommandStep' })
        reporter.onHookStart({ cid: '0-0', title: 'foo', parent: 'bar' } as HookStats)
        reporter.onHookEnd({ cid: '0-0', title: 'foo', parent: 'bar' } as HookStats)
        reporter.onAfterCommand({ command: 'SomeCommandStep' })
        reporter.onTestPass()
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)
        const testResult = results.find(
            (result) => result.name === 'should can do something',
        )
        const hookResult = results.find((result) =>
            result.name.endsWith('foo'),
        )

        expect(results).toHaveLength(1)
        expect(testResult).not.toBeUndefined()
        expect(testResult.steps).toHaveLength(1)
        expect(testResult.steps[0].name).toEqual('SomeCommandStep')
        expect(hookResult).toBeUndefined()
    })

    it('should keep failed hooks if there no some steps', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart(testStart())
        reporter.onTestStart(testStart())
        reporter.onHookStart({ cid: '0-0', title: '"after all" hook', parent: 'bar' } as HookStats)
        reporter.onHookEnd({ cid: '0-0', title: '"after all" hook', parent: 'bar', error: { message: '', stack: '' } } as HookStats)
        reporter.onTestPass()
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results, containers } = getResults(outputDir)
        const testResult = results.find(
            (result) => result.name === 'should can do something',
        )

        expect(results).toHaveLength(1)
        expect(testResult).not.toBeUndefined()
        expect(containers).toHaveLength(1)
        expect(containers[0].afters).toHaveLength(1)
        expect(containers[0].afters[0].status).toEqual(Status.BROKEN)
    })

    it('should keep failed hooks if there are some steps', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart(testStart())
        reporter.onTestStart(testStart())
        reporter.onBeforeCommand({ command: 'SomeCommandStep' })
        reporter.onHookStart({ cid: '0-0', title: '"after all" hook', parent: 'bar' } as HookStats)
        reporter.onHookEnd({ cid: '0-0', title: '"after all" hook', parent: 'bar', error: { message: '', stack: '' } } as HookStats)
        reporter.onAfterCommand({ command: 'SomeCommandStep' })
        reporter.onTestPass()
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results, containers } = getResults(outputDir)
        const testResult = results.find(
            (result) => result.name === 'should can do something',
        )

        expect(results).toHaveLength(1)
        expect(testResult).not.toBeUndefined()
        expect(testResult.steps).toHaveLength(1)
        expect(testResult.steps[0].name).toEqual('SomeCommandStep')
        expect(containers[0].afters).toHaveLength(1)
        expect(containers[0].afters[0].status).toEqual(Status.BROKEN)
    })

    it('should not capture mocha/jasmine each hooks end - passed', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart(testStart())
        reporter.onTestStart(testStart())
        reporter.onHookStart({ title: '"after each" hook', parent: 'foo' })
        reporter.onHookEnd({ title: '"after each" hook', parent: 'foo' })
        reporter.onTestPass()
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results, containers } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(containers[0].afters).toHaveLength(0)
    })

    it('should create a test and add mocha all hooks as fixtures if hook throws', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart({ cid: '0-0', title: 'SomeSuite' })
        reporter.onHookStart({ title: '"before all" hook', parent: 'foo' })
        reporter.onHookEnd({ title: '"before all" hook', parent: 'foo', error: { message: '', stack: '' } })
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results, containers } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(containers[0].befores[0].status).toEqual(Status.BROKEN)
        expect(containers[0].befores[0].stage).toEqual(Stage.FINISHED)
    })
})

describe('hooks handling default', () => {
    const outputDir = temporaryDirectory()
    let reporter: any

    beforeEach(() => {
        clean(outputDir)
        reporter = new AllureReporter({ outputDir, disableMochaHooks: false })
    })

    it('should capture mocha/jasmine each hooks ', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart({ cid: '0-0', title: 'SomeSuite' })
        reporter.onHookStart({ title: '"before each" hook', parent: 'foo' })
        reporter.onHookEnd({ title: '"before each" hook', parent: 'foo' })
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results, containers } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(containers[0].befores).toHaveLength(1)
        expect(containers[0].befores[0].steps[0].name).toEqual(
            '"before each" hook',
        )
    })

    it('should not ignore mocha/jasmine each hooks if no test', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart({ cid: '0-0', title: 'SomeSuite' })
        reporter.onHookStart({ title: '"after each" hook', parent: 'foo' })
        reporter.onHookEnd({ title: '"after each" hook', parent: 'foo' })
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results, containers } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(containers[0].afters[0].steps[0].name).toEqual(
            '"after each" hook',
        )
    })

    it('should keep passed hooks if there are no steps (before/after)', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart({ cid: '0-0', title: 'SomeSuite' })
        reporter.onHookStart({ title: '"before all" hook', parent: 'foo' })
        reporter.onHookEnd({ title: '"before all" hook', parent: 'foo' })
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results, containers } = getResults(outputDir)

        expect(results).toHaveLength(1)
        expect(containers[0].befores[0].steps[0].name).toEqual(
            '"before all" hook',
        )
    })

    it('should keep passed hooks if there are some steps', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart({ cid: '0-0', title: 'SomeSuite' })
        reporter.onTestStart({ cid: '0-0', title: 'SomeTest' })
        reporter.onBeforeCommand({ command: 'SomeCommandStep' })
        reporter.onHookStart({ title: 'foo', parent: 'bar' })
        reporter.onHookEnd({ title: 'foo', parent: 'bar' })
        reporter.onAfterCommand({ command: 'SomeCommandStep' })
        reporter.onTestPass()
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)

        expect(results).toHaveLength(2)
        expect(
            results.find((result) => result.name === 'hook:foo'),
        ).not.toBeUndefined()
    })
})

describe('nested suite naming', () => {
    const outputDir = temporaryDirectory()
    let reporter: any

    beforeEach(() => {
        clean(outputDir)
        reporter = new AllureReporter({ outputDir, disableMochaHooks: false })
    })

    it('should not end test if no hook ignored', () => {
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart({ cid: '0-0', title: 'foo' })
        reporter.onSuiteStart({ cid: '0-0', title: 'bar' })
        reporter.onSuiteEnd({ cid: '0-0', title: 'foo' })
        reporter.onSuiteEnd({ cid: '0-0', title: 'foo' })
        reporter.onRunnerEnd(runnerEnd())

        const { containers } = getResults(outputDir)
        const parentSuite = containers.find((suite) => suite.name === 'foo')
        const childSuite = containers.find(
            (suite) => suite.name === 'foo: bar',
        )

        expect(parentSuite).not.toBeUndefined()
        expect(childSuite).not.toBeUndefined()
    })
})

describe('test step naming', () => {
    const outputDir = temporaryDirectory()
    let reporter: any

    beforeEach(() => {
        clean(outputDir)
        reporter = new AllureReporter({ outputDir, disableMochaHooks: false })
    })

    it('should display command name when both command name and enpoint are available ', () => {
        const command = {
            command: 'SomeCommandStep',
            method: 'POST',
            endpoint: '/session/:sessionId/element',
        }
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart(testStart())
        reporter.onTestStart(testStart())
        reporter.onBeforeCommand(command)
        reporter.onAfterCommand(command)
        reporter.onTestPass()
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)
        const testResult = results.find(
            (result) => result.name === 'should can do something'
        )

        expect(results).toHaveLength(1)
        expect(testResult).not.toBeUndefined()
        expect(testResult.steps).toHaveLength(1)
        expect(testResult.steps[0].name).toEqual(
            'SomeCommandStep'
        )
    })

    it('should display the endpoint and method in the absence of command name', () => {
        const command = {
            method: 'POST',
            endpoint: '/session/:sessionId/element',
        }
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart(testStart())
        reporter.onTestStart(testStart())
        reporter.onBeforeCommand(command)
        reporter.onAfterCommand(command)
        reporter.onTestPass()
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        const { results } = getResults(outputDir)
        const testResult = results.find(
            (result) => result.name === 'should can do something'
        )

        expect(results).toHaveLength(1)
        expect(testResult).not.toBeUndefined()
        expect(testResult.steps).toHaveLength(1)
        expect(testResult.steps[0].name).toEqual(
            'POST /session/:sessionId/element'
        )
    })
})

describe('request fails', () => {
    const outputDir = temporaryDirectory()
    let reporter: any

    beforeEach(() => {
        clean(outputDir)
        reporter = new AllureReporter({ outputDir, disableMochaHooks: false })
    })

    it('should attachJSON with error name in onAfterCommand when the command fails', () => {
        const command = {
            method: 'POST',
            endpoint: '/session/:sessionId/element',
            result : {
                error: {
                    name: 'SomeError',
                }
            }
        }
        const attachJSONSpy = vi.spyOn(reporter, 'attachJSON')

        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart(testStart())
        reporter.onTestStart(testStart())
        reporter.onBeforeCommand(command)

        reporter.onAfterCommand(command)

        reporter.onTestPass()
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        expect(attachJSONSpy).toHaveBeenCalledWith('Response', 'SomeError')
    })

    it('should not attachJSON in onAfterCommand when the command result has nothing', () => {
        const command = {
            method: 'POST',
            endpoint: '/session/:sessionId/element',
            result : {}
        }
        const attachJSONSpy = vi.spyOn(reporter, 'attachJSON')
        reporter.onRunnerStart(runnerStart())
        reporter.onSuiteStart(testStart())
        reporter.onTestStart(testStart())
        reporter.onBeforeCommand(command)

        reporter.onAfterCommand(command)

        reporter.onTestPass()
        reporter.onSuiteEnd(suiteEnd())
        reporter.onRunnerEnd(runnerEnd())

        expect(attachJSONSpy).not.toHaveBeenCalled()
    })
})