import { describe, expect, it, vi, beforeAll, beforeEach, afterEach, } from 'vitest' import { EventEmitter } from 'node:events' import type { TestStepFinished, TestStepStarted } from '@cucumber/messages' import { TestStepResultStatus } from '@cucumber/messages' import path from 'node:path' import CucumberFormatter from '../src/cucumberFormatter.js' import { gherkinDocument, pickle, testRunStarted, testCase, testCaseStarted, testStepStarted, testStepFinished, testCaseFinished, testRunFinished, } from './fixtures/envelopes.js' vi.mock('@cucumber/messages', () => ({ IdGenerator: { incrementing: vi.fn() }, TestStepResultStatus: { UNKNOWN: 'UNKNOWN', PASSED: 'PASSED', SKIPPED: 'SKIPPED', PENDING: 'PENDING', UNDEFINED: 'UNDEFINED', AMBIGUOUS: 'AMBIGUOUS', FAILED: 'FAILED', }, })) const wdioReporter = { write: vi.fn(), emit: vi.fn(), on: vi.fn(), } const _eventEmitter = { write: vi.fn(), emit: vi.fn(), on: vi.fn(), } const cid = '0-1' const specs = ['/foobar.js'] const gherkinDocEvent = gherkinDocument const gherkinDocEventNoLine = gherkinDocument const relativeGherkinDocPath = `.${gherkinDocument.uri}` // @ts-expect-error delete gherkinDocEventNoLine.feature?.location?.line const loadGherkin = (eventBroadcaster: EventEmitter) => eventBroadcaster.emit('envelope', { gherkinDocument }) const loadRelativeGherkinPath = (eventBroadcaster: EventEmitter) => eventBroadcaster.emit('envelope', { gherkinDocument: { ...gherkinDocument, uri: relativeGherkinDocPath } }) const acceptPickle = (eventBroadcaster: EventEmitter) => eventBroadcaster.emit('envelope', { pickle }) const loadGherkinNoLine = (eventBroadcaster: EventEmitter) => eventBroadcaster.emit('envelope', { gherkinDocument: gherkinDocEventNoLine, }) const prepareSuite = (eventBroadcaster: EventEmitter) => eventBroadcaster.emit('envelope', { testCase }) const startSuite = (eventBroadcaster: EventEmitter) => eventBroadcaster.emit('envelope', { testCaseStarted }) describe('CucumberFormatter', () => { describe('emits messages for certain cucumber events', () => { let cucumberFormatter: CucumberFormatter let eventBroadcaster: EventEmitter beforeEach(() => { wdioReporter.emit.mockClear() _eventEmitter.emit.mockClear() eventBroadcaster = new EventEmitter() cucumberFormatter = new CucumberFormatter({ eventBroadcaster: eventBroadcaster, parsedArgvOptions: { _reporter: wdioReporter, _cid: cid, _specs: specs, _eventEmitter: _eventEmitter, _scenarioLevelReporter: false, _tagsInTitle: false, _ignoreUndefinedDefinitions: false, _failAmbiguousDefinitions: true, }, } as any) }) it('should not send any data on `gherkin-document` event', () => { loadGherkin(eventBroadcaster) // @ts-ignore accessing private property expect(cucumberFormatter._gherkinDocEvents).toEqual([ gherkinDocument, ]) expect(wdioReporter.emit).not.toHaveBeenCalled() }) it('should return absolute uri on `gherkin-document` event', () => { loadRelativeGherkinPath(eventBroadcaster) // @ts-ignore accessing private property expect(cucumberFormatter._gherkinDocEvents[0].uri).toEqual( path.resolve(relativeGherkinDocPath) ) expect(wdioReporter.emit).not.toHaveBeenCalled() }) it('should send proper data on `test-run-started` event', () => { loadGherkin(eventBroadcaster) wdioReporter.emit.mockClear() eventBroadcaster.emit('envelope', { testRunStarted: {} }) expect(wdioReporter.emit.mock.calls).toMatchSnapshot() }) it('should not send any data on `pickle-accepted` event', () => { loadGherkin(eventBroadcaster) wdioReporter.emit.mockClear() acceptPickle(eventBroadcaster) expect(wdioReporter.emit).not.toHaveBeenCalled() }) it('should not be ok if line is missing', () => { loadGherkinNoLine(eventBroadcaster) wdioReporter.emit.mockClear() acceptPickle(eventBroadcaster) expect(wdioReporter.emit).not.toHaveBeenCalled() }) it("should send accepted pickle's data on `test-case-started` event", () => { loadGherkin(eventBroadcaster) acceptPickle(eventBroadcaster) prepareSuite(eventBroadcaster) wdioReporter.emit.mockClear() startSuite(eventBroadcaster) expect(wdioReporter.emit.mock.calls).toMatchSnapshot() }) describe('step finished events', () => { beforeEach(() => { loadGherkin(eventBroadcaster) acceptPickle(eventBroadcaster) prepareSuite(eventBroadcaster) startSuite(eventBroadcaster) wdioReporter.emit.mockClear() }) it('should send proper data on `test-step-started` event', () => { eventBroadcaster.emit('envelope', { testStepStarted }) expect(wdioReporter.emit.mock.calls).toMatchSnapshot() }) it('passed step', () => { const passingStep: TestStepFinished = JSON.parse( JSON.stringify(testStepFinished) ) eventBroadcaster.emit('envelope', { testStepFinished: passingStep, }) delete wdioReporter.emit.mock.calls[0][1].duration expect(wdioReporter.emit.mock.calls).toMatchSnapshot() }) it('failed step', () => { const failedStep: TestStepFinished = JSON.parse( JSON.stringify(testStepFinished) ) failedStep.testStepResult = { ...failedStep.testStepResult, status: TestStepResultStatus.FAILED, } eventBroadcaster.emit('envelope', { testStepFinished: failedStep, }) delete wdioReporter.emit.mock.calls[0][1].duration expect(wdioReporter.emit.mock.calls).toMatchSnapshot() }) }) it('should send proper data on onTestRunFinished', () => { const passingStep: TestStepFinished = JSON.parse( JSON.stringify(testStepFinished) ) loadGherkin(eventBroadcaster) acceptPickle(eventBroadcaster) prepareSuite(eventBroadcaster) startSuite(eventBroadcaster) eventBroadcaster.emit('envelope', { testStepStarted }) eventBroadcaster.emit('envelope', { testStepFinished: passingStep, }) eventBroadcaster.emit('envelope', { testCaseFinished }) wdioReporter.emit.mockClear() eventBroadcaster.emit('envelope', { testRunFinished }) expect(wdioReporter.emit.mock.calls).toMatchSnapshot() }) it('should proper data when executing a hook', () => { const hookStarted: TestStepStarted = JSON.parse( JSON.stringify(testStepStarted) ) hookStarted.testStepId = '24' const hookFinished: TestStepFinished = JSON.parse( JSON.stringify(testStepFinished) ) hookFinished.testStepId = '24' loadGherkin(eventBroadcaster) acceptPickle(eventBroadcaster) prepareSuite(eventBroadcaster) startSuite(eventBroadcaster) eventBroadcaster.emit('envelope', { testStepStarted: hookStarted }) wdioReporter.emit.mockClear() eventBroadcaster.emit('envelope', { testStepFinished: hookFinished, }) delete wdioReporter.emit.mock.calls[0][1].duration expect(wdioReporter.emit.mock.calls).toMatchSnapshot() }) it('should emit proper no of hookparams events', () => { loadGherkin(eventBroadcaster) prepareSuite(eventBroadcaster) acceptPickle(eventBroadcaster) startSuite(eventBroadcaster) eventBroadcaster.emit('envelope', { testStepStarted }) eventBroadcaster.emit('envelope', { testCaseFinished }) expect(_eventEmitter.emit).toBeCalledTimes(4) Array(4).fill(0).forEach((_, i) => expect(_eventEmitter.emit).toHaveBeenNthCalledWith(i+1, 'getHookParams', expect.any(Object))) }) }) describe('emits messages for certain cucumber events when executed in scenarioLeverReporter', () => { let cucumberFormatter: CucumberFormatter let eventBroadcaster: EventEmitter beforeEach(() => { wdioReporter.emit.mockClear() eventBroadcaster = new EventEmitter() cucumberFormatter = new CucumberFormatter({ eventBroadcaster: eventBroadcaster, parsedArgvOptions: { _reporter: wdioReporter, _cid: cid, _specs: specs, _eventEmitter: new EventEmitter(), _scenarioLevelReporter: true, _tagsInTitle: false, _ignoreUndefinedDefinitions: false, _failAmbiguousDefinitions: true, }, } as any) }) it('should not send any data on `gherkin-document` event', () => { loadGherkin(eventBroadcaster) // @ts-ignore accessing private property expect(cucumberFormatter._gherkinDocEvents).toEqual([ gherkinDocEvent, ]) expect(wdioReporter.emit).not.toHaveBeenCalled() }) it('should send proper data on `test-run-started` event', () => { loadGherkin(eventBroadcaster) wdioReporter.emit.mockClear() eventBroadcaster.emit('envelope', { testRunStarted }) expect(wdioReporter.emit.mock.calls).toMatchSnapshot() }) it('should not send any data on `pickle-accepted` event', () => { loadGherkin(eventBroadcaster) wdioReporter.emit.mockClear() acceptPickle(eventBroadcaster) expect(wdioReporter.emit).not.toHaveBeenCalled() }) it('should not be ok if line is missing', () => { loadGherkinNoLine(eventBroadcaster) wdioReporter.emit.mockClear() acceptPickle(eventBroadcaster) expect(wdioReporter.emit).not.toHaveBeenCalled() }) it("should send accepted pickle's data on `test-case-started` event", () => { loadGherkin(eventBroadcaster) acceptPickle(eventBroadcaster) prepareSuite(eventBroadcaster) wdioReporter.emit.mockClear() startSuite(eventBroadcaster) expect(wdioReporter.emit.mock.calls).toMatchSnapshot() }) it('should send proper data on `test-case-finished` event', () => { const passingStep: TestStepFinished = JSON.parse( JSON.stringify(testStepFinished) ) loadGherkin(eventBroadcaster) acceptPickle(eventBroadcaster) prepareSuite(eventBroadcaster) startSuite(eventBroadcaster) eventBroadcaster.emit('envelope', { testStepStarted }) eventBroadcaster.emit('envelope', { testStepFinished: passingStep, }) wdioReporter.emit.mockClear() eventBroadcaster.emit('envelope', { testCaseFinished }) delete wdioReporter.emit.mock.calls[0][1].duration expect(wdioReporter.emit.mock.calls).toMatchSnapshot() }) it('should send proper data on onTestRunFinished', () => { const passingStep: TestStepFinished = JSON.parse( JSON.stringify(testStepFinished) ) loadGherkin(eventBroadcaster) acceptPickle(eventBroadcaster) prepareSuite(eventBroadcaster) startSuite(eventBroadcaster) eventBroadcaster.emit('envelope', { testStepStarted }) eventBroadcaster.emit('envelope', { testStepFinished: passingStep, }) eventBroadcaster.emit('envelope', { testCaseFinished }) wdioReporter.emit.mockClear() eventBroadcaster.emit('envelope', { testRunFinished }) expect(wdioReporter.emit.mock.calls).toMatchSnapshot() }) }) describe('provides a fail counter', () => { let cucumberFormatter: CucumberFormatter let eventBroadcaster: EventEmitter beforeEach(() => { wdioReporter.emit.mockClear() eventBroadcaster = new EventEmitter() cucumberFormatter = new CucumberFormatter({ eventBroadcaster: eventBroadcaster, parsedArgvOptions: { _reporter: wdioReporter, _cid: cid, _specs: specs, _eventEmitter: new EventEmitter(), _scenarioLevelReporter: false, _tagsInTitle: false, _ignoreUndefinedDefinitions: false, _failAmbiguousDefinitions: true, }, } as any) loadGherkin(eventBroadcaster) acceptPickle(eventBroadcaster) prepareSuite(eventBroadcaster) startSuite(eventBroadcaster) }) it('should increment failed counter on `failed` status', () => { const failedStep: TestStepFinished = JSON.parse( JSON.stringify(testStepFinished) ) failedStep.testStepResult = { ...failedStep.testStepResult, status: TestStepResultStatus.FAILED, } eventBroadcaster.emit('envelope', { testStepFinished: failedStep }) // @ts-ignore accessing private property expect(cucumberFormatter.failedCount).toBe(1) }) it('should increment failed counter on `ambiguous` status', () => { const ambiguousStep: TestStepFinished = JSON.parse( JSON.stringify(testStepFinished) ) ambiguousStep.testStepResult = { ...ambiguousStep.testStepResult, status: TestStepResultStatus.AMBIGUOUS, } eventBroadcaster.emit('envelope', { testStepFinished: ambiguousStep, }) // @ts-ignore accessing private property expect(cucumberFormatter.failedCount).toBe(1) }) it('should increment failed counter on `undefined` status', () => { const undefinedStep: TestStepFinished = JSON.parse( JSON.stringify(testStepFinished) ) undefinedStep.testStepResult = { ...undefinedStep.testStepResult, status: TestStepResultStatus.UNDEFINED, } eventBroadcaster.emit('envelope', { testStepFinished: undefinedStep, }) // @ts-ignore accessing private property expect(cucumberFormatter.failedCount).toBe(1) }) it('should not increment failed counter on `undefined` status if ignoreUndefinedDefinitions set to true', () => { // @ts-ignore accessing private property cucumberFormatter.ignoreUndefinedDefinitions = true const undefinedStep: TestStepFinished = JSON.parse( JSON.stringify(testStepFinished) ) undefinedStep.testStepResult = { ...undefinedStep.testStepResult, status: TestStepResultStatus.UNDEFINED, } eventBroadcaster.emit('envelope', { testStepFinished: undefinedStep, }) // @ts-ignore accessing private property expect(cucumberFormatter.failedCount).toBe(0) }) }) describe('tags in title', () => { let eventBroadcaster: EventEmitter beforeAll(() => { eventBroadcaster = new EventEmitter() new CucumberFormatter({ eventBroadcaster: eventBroadcaster, parsedArgvOptions: { _reporter: wdioReporter, _cid: cid, _specs: specs, _eventEmitter: new EventEmitter(), _scenarioLevelReporter: false, _tagsInTitle: true, _ignoreUndefinedDefinitions: false, _failAmbiguousDefinitions: false, }, } as any) }) it('should add tags on handleBeforeFeatureEvent', () => { eventBroadcaster.emit('envelope', { gherkinDocument: gherkinDocEvent, }) expect(wdioReporter.emit).not.toBeCalled() eventBroadcaster.emit('envelope', { testRunStarted }) expect(wdioReporter.emit.mock.calls).toMatchSnapshot() }) }) afterEach(() => { wdioReporter.on.mockClear() wdioReporter.write.mockClear() wdioReporter.emit.mockClear() }) })