import path from 'node:path' import { RUNNER, SUITES, SUITE_UIDS, SUITES_NO_TESTS, SUITES_WITH_TEST_RETRY, SUITES_WITH_DATA_TABLE, SUITES_MULTIPLE_ERRORS, SUITES_WITH_DOC_STRING, SUITES_NO_TESTS_WITH_HOOK_ERROR, SUITES_WITH_RETRIES } from './__fixtures__/testdata.js' import { State } from '../src/types.js' import SpecReporter from '../src/index.js' import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' import { runnerEnd } from '../../wdio-allure-reporter/tests/__fixtures__/runner.js' vi.mock('chalk') vi.mock('@wdio/reporter', () => import(path.join(process.cwd(), '__mocks__', '@wdio/reporter'))) const reporter = new SpecReporter({}) const defaultCaps = { browserName: 'loremipsum', version: 50, platform: 'Windows 10', sessionId: 'foobar' } const fakeSessionId = 'ba86cbcb70774ef8a0757c1702c3bdf9' const getRunnerConfig = (config: any = {}) => { return Object.assign(JSON.parse(JSON.stringify(RUNNER)), { capabilities: config.capabilities || defaultCaps, config, sessionId: fakeSessionId, isMultiremote: Boolean(config.isMultiremote) }) } describe('SpecReporter', () => { let tmpReporter: SpecReporter beforeEach(() => { tmpReporter = new SpecReporter({}) }) describe('on create', () => { it('should verify initial properties', () => { expect(reporter['_suiteUids'].size).toBe(0) expect(reporter['_indents']).toBe(0) expect(reporter['_suiteIndents']).toEqual({}) expect(reporter['_stateCounts']).toEqual({ passed: 0, failed: 0, skipped: 0, pending: 0, retried: 0 }) }) }) describe('onSuiteStart', () => { beforeAll(() => { reporter.onSuiteStart(Object.values(SUITES)[0] as any) }) it('should add to suiteUids', () => { expect(reporter['_suiteUids'].size).toBe(1) expect([...reporter['_suiteUids'].values()][0]).toBe('Foo test1') }) it('should increase suiteIndents', () => { expect(reporter['_suiteIndents']['Foo test1']).toBe(1) }) }) describe('onHookEnd', () => { it('should increase stateCount failures if hook failed', () => { expect(tmpReporter['_stateCounts'].failed).toBe(0) tmpReporter.onHookEnd({} as any) expect(tmpReporter['_stateCounts'].failed).toBe(0) tmpReporter.onHookEnd({ error: new Error('boom!') } as any) expect(tmpReporter['_stateCounts'].failed).toBe(1) }) }) describe('getEventsToReport', () => { it('should return all tests and hook errors to report', () => { expect(tmpReporter.getEventsToReport({ tests: [{ type: 'test', title: '1' }, { type: 'test', title: '2' }], hooks: [{}], hooksAndTests: [{}, { type: 'test', title: '11' }, {}, { type: 'test', title: '22' }, {}] } as any)).toEqual([{ type: 'test', title: '11' }, { type: 'test', title: '22' }]) expect(tmpReporter.getEventsToReport({ tests: [{ type: 'test', title: '1' }, { type: 'test', title: '2' }], hooks: [{ error: 1 }, {}, { error: 2 }], hooksAndTests: [{}, { error: 11 }, {}, { type: 'test', title: '33' }, {}, { error: 22 }, {}] } as any)).toEqual([{ error: 11 }, { type: 'test', title: '33' }, { error: 22 }]) }) }) describe('onTestPass', () => { beforeAll(() => { reporter.onTestPass({ title:'test1', state:State.PASSED } as any) }) it('should increase stateCounts.passed by 1', () => { expect(reporter['_stateCounts'].passed).toBe(1) }) }) describe('onTestFail', () => { beforeAll(() => { reporter.onTestFail({ title:'test1', state:State.FAILED } as any) }) it('should increase stateCounts.failed by 1', () => { expect(reporter['_stateCounts'].failed).toBe(1) }) }) describe('onTestSkip', () => { beforeAll(() => { reporter.onTestSkip({ title:'test1', state:State.SKIPPED, pendingReason: 'some random reason' } as any) }) it('should have a pending reason', () => { expect(reporter['_pendingReasons'][0]).toBe('some random reason') }) it('should increase stateCounts.skipped by 1', () => { expect(reporter['_stateCounts'].skipped).toBe(1) }) }) describe('onSuiteEnd', () => { beforeAll(() => { reporter.onSuiteEnd() }) it('should decrease indents', () => { expect(reporter['_indents']).toBe(0) }) }) describe('onRunnerEnd', () => { it('should call printReport method', () => { reporter.printReport = vi.fn() reporter.onRunnerEnd(RUNNER as any) expect(vi.mocked(reporter.printReport).mock.calls.length).toBe(1) expect(vi.mocked(reporter.printReport).mock.calls[0][0]).toEqual(RUNNER) }) }) describe('printReport', () => { let printReporter: any = null beforeEach(() => { printReporter = new SpecReporter({}) printReporter.write = vi.fn() printReporter.runnerStat = { instanceOptions: { [fakeSessionId]: {} } } as any }) describe('with normal setup', () => { beforeEach(() => { printReporter['_suiteUids'] = SUITE_UIDS printReporter.suites = SUITES printReporter['_stateCounts'] = { passed: 4, failed: 1, skipped: 1, } }) it('should print the report to the console', () => { const runner = getRunnerConfig({ hostname: 'localhost' }) printReporter.printReport(runner) expect(printReporter.write.mock.calls).toMatchSnapshot() }) it('should print link to Sauce Labs job details page for VDC', () => { const options = { hostname: 'ondemand.saucelabs.com', user: 'foobar', key: '123' } const runner = getRunnerConfig({}) printReporter.runnerStat.instanceOptions[fakeSessionId] = options printReporter.printReport(runner) expect(printReporter.write.mock.calls).toMatchSnapshot() }) it('should print link to Sauce Labs job details page for RDC', () => { const options = { hostname: 'ondemand.saucelabs.com', user: 'foobar', key: '123' } const runner = getRunnerConfig({ capabilities: { browserName: 'safari', ['appium:deviceName']: 'udid-serial-of-device', ['appium:platformVersion']: '14.3', ['appium:platformName']: 'iOS', testobject_test_report_url: ' https://app.eu-central-1.saucelabs.com/tests/c752c683e0874da4b1dad593ce6645b2' }, sessionId: 'c752c683e0874da4b1dad593ce6645b2', }) printReporter.runnerStat.instanceOptions[fakeSessionId] = options printReporter.printReport(runner) expect(printReporter.write.mock.calls).toMatchSnapshot() }) it('should print jobs of all instance when run with multiremote', () => { const options = { hostname: 'ondemand.saucelabs.com', user: 'foobar', key: '123' } const runner = getRunnerConfig({ capabilities: { browserA: { browserName: 'chrome' }, browserB: { browserName: 'firefox' } }, isMultiremote: true }) printReporter.runnerStat.instanceOptions['foobar'] = options printReporter.runnerStat.instanceOptions['barfoo'] = options printReporter.printReport(runner) expect(printReporter.write.mock.calls).toMatchSnapshot() }) it('should print link to Sauce Labs job details page if run with Sauce Connect (w3c)', () => { const runner = getRunnerConfig({ capabilities: { ...defaultCaps, 'sauce:options': 'foobar' }, sessionId: fakeSessionId }) printReporter.runnerStat.instanceOptions[fakeSessionId] = { hostname: 'ondemand.us-west-1.saucelabs.com', user: 'foobar', key: '123' } printReporter.printReport(runner) expect(printReporter.write.mock.calls).toMatchSnapshot() }) it('should print link to Sauce Labs job details page if run with Sauce Connect (jsonwp)', () => { const runner = getRunnerConfig({ capabilities: { tunnelName: 'foobar', ...defaultCaps }, sessionId: fakeSessionId }) printReporter.runnerStat.instanceOptions[fakeSessionId] = { hostname: 'ondemand.saucelabs.com', user: 'foobar', key: '123' } printReporter.printReport(runner) expect(printReporter.write.mock.calls).toMatchSnapshot() }) it('should print link to Sauce Labs for many regions', () => { printReporter.runnerStat.instanceOptions[fakeSessionId] = { hostname: 'ondemand.eu-central-1.saucelabs.com', user: 'foobar', key: '123' } printReporter.printReport(getRunnerConfig({})) expect(printReporter.write.mock.calls).toMatchSnapshot() printReporter.write.mockClear() printReporter.runnerStat.instanceOptions[fakeSessionId] = { hostname: 'ondemand.us-east-4.saucelabs.com', user: 'foobar', key: '123' } printReporter.printReport(getRunnerConfig({})) expect(printReporter.write.mock.calls).toMatchSnapshot() }) }) describe('with disabled sharable Sauce report links', () => { const options = { sauceLabsSharableLinks: false } beforeEach(() => { tmpReporter = new SpecReporter(options) // tmpReporter.suiteUids = SUITE_UIDS // tmpReporter.suites = SUITES // tmpReporter.stateCounts = { // passed: 4, // failed: 1, // skipped: 1, // } tmpReporter.write = vi.fn() }) it('should print the default Sauce Labs job details page link', () => { const runner = getRunnerConfig({ hostname: 'ondemand.saucelabs.com', user: 'foobar', key: '123', }) tmpReporter.printReport(runner) expect(vi.mocked(tmpReporter.write).mock.calls).toMatchSnapshot() }) }) it('should print report for suites with no tests but failed hooks', () => { printReporter['_suiteUids'] = SUITE_UIDS printReporter.suites = SUITES_NO_TESTS_WITH_HOOK_ERROR const runner = getRunnerConfig({ capabilities: {}, hostname: 'localhost' }) printReporter.printReport(runner) expect(printReporter.write.mock.calls.length).toBe(1) expect(printReporter.write.mock.calls[0][0]).toContain('a failed hook') }) it('should not print the report because there are no tests', () => { printReporter['_suiteUids'] = SUITE_UIDS printReporter.suites = SUITES_NO_TESTS printReporter.printReport(getRunnerConfig()) 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', () => { it('should validate header output', () => { const result = reporter.getHeaderDisplay(getRunnerConfig() as any) expect(result).toMatchSnapshot() }) it('should list multiple specs', () => { const config = getRunnerConfig() as any config.specs.push('/foo/bar/loo.js', '/bar/foo/baz.js') const result = reporter.getHeaderDisplay(config) expect(result).toMatchSnapshot() }) it('should validate header output in multiremote', () => { const result = tmpReporter.getHeaderDisplay( getRunnerConfig({ capabilities: { browserA: { browserName: 'chrome' }, browserB: { browserName: 'firefox' } }, isMultiremote: true, })) expect(result).toMatchSnapshot() }) }) describe('getResultDisplay', () => { it('should validate the result output with tests', () => { tmpReporter.getOrderedSuites = vi.fn(() => Object.values(SUITES)) as any const result = tmpReporter.getResultDisplay() expect(result).toMatchSnapshot() }) it('should validate the result output with no tests', () => { tmpReporter.getOrderedSuites = vi.fn(() => Object.values(SUITES_NO_TESTS)) as any const result = tmpReporter.getResultDisplay() expect(result.length).toBe(0) }) it('should print data tables', () => { tmpReporter.getOrderedSuites = vi.fn(() => Object.values(SUITES_WITH_DATA_TABLE)) as any const result = tmpReporter.getResultDisplay() expect(result).toMatchSnapshot() }) it('should not print if argument is a single line doc string', () => { tmpReporter.getOrderedSuites = vi.fn(() => { const suites = Object.values(JSON.parse(JSON.stringify(SUITES_WITH_DATA_TABLE))) as any[] suites[0].hooksAndTests[0].argument = 'some different format' return suites }) const result = tmpReporter.getResultDisplay() expect(result).toMatchSnapshot() }) it('should print multiple lines doc string', () => { tmpReporter.getOrderedSuites = vi.fn(() => { return Object.values(JSON.parse(JSON.stringify(SUITES_WITH_DOC_STRING))) as any[] }) const result = tmpReporter.getResultDisplay() expect(result).toMatchSnapshot() }) it('should print if the doc string is a blank string', () => { tmpReporter.getOrderedSuites = vi.fn(() => { const suites = Object.values(JSON.parse(JSON.stringify(SUITES_WITH_DOC_STRING))) as any[] suites[0].hooksAndTests[0].argument = '' suites[0].hooksAndTests[1].argument = '' return suites }) const result = tmpReporter.getResultDisplay() expect(result).toMatchSnapshot() }) it('should not print if the argument is undefined', () => { tmpReporter.getOrderedSuites = vi.fn(() => { const suites = Object.values(JSON.parse(JSON.stringify(SUITES_WITH_DATA_TABLE))) as any[] suites[0].hooksAndTests[0].argument = undefined return suites }) const result = tmpReporter.getResultDisplay() expect(result).toMatchSnapshot() }) it('should not print if data table is empty', () => { tmpReporter.getOrderedSuites = vi.fn(() => { const suites = Object.values(JSON.parse(JSON.stringify(SUITES_WITH_DATA_TABLE))) as any[] suites[0].hooksAndTests[0].argument.rows = [] return suites }) const result = tmpReporter.getResultDisplay() expect(result).toMatchSnapshot() }) }) describe('getCountDisplay', () => { it('should return only passing counts', () => { tmpReporter['_stateCounts'].passed = 2 const result = tmpReporter.getCountDisplay('5') expect(result.length).toBe(1) expect(result[0]).toBe('green 2 passing 5') }) it('should return passing and failing counts', () => { tmpReporter['_stateCounts'].passed = 2 tmpReporter['_stateCounts'].failed = 1 const result = tmpReporter.getCountDisplay('5') expect(result.length).toBe(2) expect(result[0]).toBe('green 2 passing 5') expect(result[1]).toBe('red 1 failing') }) it('should return failing and skipped counts', () => { tmpReporter['_stateCounts'].failed = 1 tmpReporter['_stateCounts'].skipped = 2 const result = tmpReporter.getCountDisplay('5') expect(result.length).toBe(2) expect(result[0]).toBe('red 1 failing 5') expect(result[1]).toBe('cyan 2 skipped') }) it('should only display skipped with duration', () => { tmpReporter['_stateCounts'].skipped = 2 const result = tmpReporter.getCountDisplay('5') expect(result.length).toBe(1) expect(result[0]).toBe('cyan 2 skipped 5') }) }) describe('getFailureDisplay', () => { it('should return failing results', () => { tmpReporter.getOrderedSuites = vi.fn(() => Object.values(SUITES)) as any const result = tmpReporter.getFailureDisplay() expect(result.length).toBe(7) expect(result[0]).toBe('') expect(result[1]).toBe('1) Bar test a failed test') expect(result[2]).toBe('red expected foo to equal bar') expect(result[3]).toBe('gray Failed test stack trace') }) it('should return no results', () => { tmpReporter.getOrderedSuites = vi.fn(() => Object.values(SUITES_NO_TESTS)) as any const result = tmpReporter.getFailureDisplay() expect(result.length).toBe(0) }) it('should return mutliple failing results if they exist', () => { tmpReporter.getOrderedSuites = vi.fn(() => Object.values(SUITES_MULTIPLE_ERRORS)) as any const result = tmpReporter.getFailureDisplay() expect(result.length).toBe(6) expect(result[0]).toBe('') expect(result[1]).toBe('1) Bar test a test with two failures') expect(result[2]).toBe('red expected the party on the first part to be the party on the first part') expect(result[3]).toBe('gray First failed stack trace') expect(result[4]).toBe('red expected the party on the second part to be the party on the second part') expect(result[5]).toBe('gray Second failed stack trace') }) }) describe('getOrderedSuites', () => { it('should return the suites in order based on uids', () => { tmpReporter['_suiteUids'] = new Set(['5', '3', '8']) tmpReporter.suites = { '3': { uid: 3 }, '5': { uid: 5 } } as any const result = tmpReporter.getOrderedSuites() expect(result.length).toBe(2) expect(result[0]).toEqual({ uid: 5 }) expect(result[1]).toEqual({ uid: 3 }) expect(tmpReporter['_orderedSuites'].length).toBe(2) expect(tmpReporter['_orderedSuites'][0]).toEqual({ uid: 5 }) expect(tmpReporter['_orderedSuites'][1]).toEqual({ uid: 3 }) }) it('should return the cached ordered suites', () => { tmpReporter['_orderedSuites'] = ['foo', 'bar'] as any const result = tmpReporter.getOrderedSuites() expect(result.length).toBe(2) expect(result[0]).toBe('foo') expect(result[1]).toBe('bar') }) it('should return no suites', () => { expect(tmpReporter.getOrderedSuites().length).toBe(0) }) it('should propagate root suite hook errors', () => { tmpReporter['_suiteUids'] = new Set(['5', '3', '8']) tmpReporter.suites = { '3': { uid: 3 }, '5': { uid: 5 } } as any tmpReporter.currentSuites = [{ hooks: [{ type: 'hook', title: '"before all" hook in "{root}"', state: State.FAILED }, { type: 'hook', title: '"after all" hook in "{root}"', state: undefined }, { type: 'hook', title: '"after all" hook in "{root}"', state: State.FAILED }] }] as any const result = tmpReporter.getOrderedSuites() expect(result.length).toBe(4) expect(result[0].hooks.length).toBe(1) expect(result[3].hooks.length).toBe(1) }) }) describe('indent', () => { const uid = '123' it('should not indent', () => { tmpReporter['_suiteIndents'][uid] = 0 const result = tmpReporter.indent(uid) expect(result).toBe('') }) it('should indent', () => { tmpReporter['_suiteIndents'][uid] = 3 const result = tmpReporter.indent(uid) expect(result).toBe(' ') }) }) describe('getSymbol', () => { it('should get the checkbox symbol', () => { expect(tmpReporter.getSymbol(State.PASSED)).toBe('✓') }) it('should get the x symbol', () => { expect(tmpReporter.getSymbol(State.FAILED)).toBe('✖') }) it('should get the - symbol', () => { expect(tmpReporter.getSymbol(State.SKIPPED)).toBe('-') }) it('should get the ? symbol', () => { expect(tmpReporter.getSymbol()).toBe('?') }) }) describe('custom getSymbol', () => { const options = { symbols: { passed: 'Y', failed: 'N' } } beforeEach(() => { tmpReporter = new SpecReporter(options) }) it('should get new passed symbol', () => { expect(tmpReporter.getSymbol(State.PASSED)).toBe(options.symbols.passed) }) it('should get new failed symbol', () => { expect(tmpReporter.getSymbol(State.FAILED)).toBe(options.symbols.failed) }) it('should get the skipped symbol that is not set', () => { expect(tmpReporter.getSymbol(State.SKIPPED)).toBe('-') }) }) describe('add console logs', () => { const options = { addConsoleLogs: true } it('should add console log to report for passing test', () => { tmpReporter = new SpecReporter(options) tmpReporter.onSuiteStart(Object.values(SUITES)[0] as any) tmpReporter.onTestStart() tmpReporter['_orderedSuites'] = Object.values(SUITES) as any tmpReporter['_consoleOutput']='Printing to console spec' tmpReporter.onTestPass({ title:'test1', state:State.PASSED } as any) expect(tmpReporter.getResultDisplay().toString()).toContain('Printing to console spec') tmpReporter.onSuiteEnd() tmpReporter.onRunnerEnd(runnerEnd()) }) it('should add console logs to report for failing test', () => { tmpReporter = new SpecReporter(options) tmpReporter.onSuiteStart(Object.values(SUITES)[0] as any) tmpReporter.onTestStart() tmpReporter['_orderedSuites'] = Object.values(SUITES) as any tmpReporter['_consoleOutput']='Printing to console spec' tmpReporter.onTestFail({ title:'test1', state:State.FAILED } as any) expect(tmpReporter.getResultDisplay().toString()).toContain('Printing to console spec') tmpReporter.onSuiteEnd() tmpReporter.onRunnerEnd(runnerEnd()) }) it('should add console logs to report for skipping test', () => { tmpReporter = new SpecReporter(options) tmpReporter.onSuiteStart(Object.values(SUITES)[0] as any) tmpReporter.onTestStart() tmpReporter['_orderedSuites'] = Object.values(SUITES) as any tmpReporter['_consoleOutput']='Printing to console spec' tmpReporter.onTestSkip({ title:'test1', state:State.SKIPPED } as any) expect(tmpReporter.getResultDisplay().toString()).toContain('Printing to console spec') tmpReporter.onSuiteEnd() tmpReporter.onRunnerEnd(runnerEnd()) }) it('should add pending reason to report for skipped tests', () => { tmpReporter = new SpecReporter(options) tmpReporter.onSuiteStart(Object.values(SUITES)[2] as any) tmpReporter.onTestStart() tmpReporter['_orderedSuites'] = Object.values(SUITES) as any tmpReporter.onTestSkip({ title:'test1', state:State.SKIPPED, pendingReason:'some random Reasons' } as any) expect(tmpReporter.getResultDisplay().toString()).toContain('Pending Reasons') tmpReporter.onSuiteEnd() tmpReporter.onRunnerEnd(runnerEnd()) }) it('should not add webdriver logs to report', () => { tmpReporter = new SpecReporter(options) tmpReporter.onSuiteStart(Object.values(SUITES)[0] as any) tmpReporter['_orderedSuites'] = Object.values(SUITES) as any tmpReporter['_consoleOutput']='mwebdriver test log' expect(tmpReporter.getResultDisplay().toString()).not.toContain('mwebdriver test log') tmpReporter.onSuiteEnd() tmpReporter.onRunnerEnd(runnerEnd()) }) }) describe('onlyFailures', () => { let printReporter: any = null const runner = getRunnerConfig({ hostname: 'localhost' }) describe('false', () => { beforeEach(() => { printReporter = new SpecReporter({ onlyFailures: false }) printReporter.write = vi.fn() printReporter['_suiteUids'] = SUITE_UIDS printReporter.suites = SUITES }) it('1 failure', () => { runner.failures = 1 printReporter.printReport(runner) expect(printReporter['_onlyFailures']).toBe(false) expect(printReporter.write.mock.calls).toMatchSnapshot() }) it('0 failures', () => { runner.failures = 0 printReporter.printReport(runner) expect(printReporter['_onlyFailures']).toBe(false) expect(printReporter.write.mock.calls).toMatchSnapshot() }) }) describe('true', () => { beforeEach(() => { printReporter = new SpecReporter({ onlyFailures: true }) printReporter.write = vi.fn() printReporter['_suiteUids'] = SUITE_UIDS printReporter.suites = SUITES }) it('1 failure', () => { runner.failures = 1 printReporter.printReport(runner) expect(printReporter['_onlyFailures']).toBe(true) expect(printReporter.write.mock.calls).toMatchSnapshot() }) it('0 failures', () => { runner.failures = 0 printReporter.printReport(runner) expect(printReporter['_onlyFailures']).toBe(true) expect(printReporter.write.mock.calls).toMatchSnapshot() }) }) }) describe('onRetry', () => { let printReporter: any = null const runner = getRunnerConfig({ hostname: 'localhost' }) beforeEach(() => { printReporter = new SpecReporter({}) printReporter.write = vi.fn() printReporter['_suiteUids'] = SUITE_UIDS printReporter.suites = SUITES_WITH_TEST_RETRY }) it('should group retried test cases', () => { runner.failures = 0 printReporter.printReport(runner) expect(printReporter.write.mock.calls).toMatchSnapshot() }) }) describe('suite retry', () => { let printReporter: any = null const runner = getRunnerConfig({ hostname: 'localhost' }) beforeEach(() => { printReporter = new SpecReporter({}) printReporter.write = vi.fn() printReporter['_suiteUids'] = SUITE_UIDS printReporter.suites = SUITES_WITH_RETRIES }) it('should group retried test suites', () => { runner.failures = 0 printReporter.onTestPass({}) printReporter.onTestPass({}) printReporter.onTestFail({}) printReporter.onSuiteRetry() printReporter.onTestPass({}) printReporter.onTestPass({}) printReporter.printReport(runner) expect(printReporter.write.mock.calls).toMatchSnapshot() }) }) describe('showPreface', () => { let printReporter: SpecReporter = null as any const runner = getRunnerConfig({ hostname: 'localhost' }) it('false', () => { printReporter = new SpecReporter({ showPreface: false }) printReporter.write = vi.fn() printReporter['_suiteUids'] = SUITE_UIDS printReporter.printReport(runner) expect(printReporter['_showPreface']).toBe(false) expect(vi.mocked(printReporter.write).mock.calls).toMatchSnapshot() }) it('true', () => { printReporter = new SpecReporter({ showPreface: true }) printReporter.write = vi.fn() printReporter['_suiteUids'] = SUITE_UIDS printReporter.printReport(runner) expect(printReporter['_showPreface']).toBe(true) expect(vi.mocked(printReporter.write).mock.calls).toMatchSnapshot() }) }) describe('getColor', () => { it('should get green', () => { expect(tmpReporter.getColor(State.PASSED)).toBe('green') }) it('should get red', () => { expect(tmpReporter.getColor(State.FAILED)).toBe('red') }) it('should get cyan', () => { expect(tmpReporter.getColor(State.SKIPPED)).toBe('cyan') expect(tmpReporter.getColor(State.PENDING)).toBe('cyan') }) it('should get null', () => { expect(tmpReporter.getColor()).toBe('gray') }) }) describe('colors in terminal', () => { it('should give colors', () => { const tmpReporter = new SpecReporter({}) expect(tmpReporter.setMessageColor('test', State.PASSED)).toEqual('green test') expect(tmpReporter.setMessageColor('test', State.FAILED)).toEqual('red test') expect(tmpReporter.setMessageColor('test', State.SKIPPED)).toEqual('cyan test') }) it('should not give any color', () => { const tmpReporter = new SpecReporter({ color: false }) expect(tmpReporter.setMessageColor('test', State.PASSED)).toEqual('test') expect(tmpReporter.setMessageColor('test', State.FAILED)).toEqual('test') expect(tmpReporter.setMessageColor('test', State.SKIPPED)).toEqual('test') }) }) describe('getEnviromentCombo', () => { it('should return Multibrowser as capability if multiremote is used', () => { expect(tmpReporter.getEnviromentCombo({ myBrowser: { browserName: 'chrome', platform: 'Windows 8.1' } } as any, true, true)).toBe('MultiremoteBrowser on chrome') }) it('should not throw if mutliremote name is "app"', () => { expect(tmpReporter.getEnviromentCombo({ app: { browserName: 'chrome', platform: 'Windows 8.1' } } as any, true, true)).toBe('MultiremoteBrowser on chrome') }) it('should return verbose desktop combo', () => { expect(tmpReporter.getEnviromentCombo({ browserName: 'chrome', version: 50, platform: 'Windows 8.1' } as any)).toBe('chrome (v50) on Windows 8.1') }) it('should return preface desktop combo', () => { expect(tmpReporter.getEnviromentCombo({ browserName: 'chrome', version: 50, platform: 'Windows 8.1' } as any, false)).toBe('chrome 50 Windows 8.1') }) it('should return verbose mobile combo', () => { expect(tmpReporter.getEnviromentCombo({ ['appium:deviceName']: 'iPhone 6 Plus', ['appium:platformVersion']: '9.2', ['appium:platformName']: 'iOS' })).toBe('iPhone 6 Plus on iOS 9.2') }) it('should return verbose mobile combo', () => { expect(tmpReporter.getEnviromentCombo({ ['appium:deviceName']: 'iPhone 6 Plus', ['appium:platformVersion']: '9.2', platformName: 'iOS' })).toBe('iPhone 6 Plus on iOS 9.2') }) it('should return preface mobile combo', () => { expect(tmpReporter.getEnviromentCombo({ ['appium:deviceName']: 'iPhone 6 Plus', ['appium:platformVersion']: '9.2', ['appium:platformName']: 'iOS' }, false)).toBe('iPhone 6 Plus iOS 9.2') }) it('should return verbose mobile combo executing an app', () => { expect(tmpReporter.getEnviromentCombo({ ['appium:deviceName']: 'iPhone 6 Plus', ['appium:platformVersion']: '9.2', ['appium:platformName']: 'iOS', ['appium:app']: 'sauce-storage:myApp.app' })).toBe('iPhone 6 Plus on iOS 9.2 executing myApp.app') }) it('should return preface mobile combo executing an app', () => { expect(tmpReporter.getEnviromentCombo({ ['appium:deviceName']: 'iPhone 6 Plus', ['appium:platformVersion']: '9.2', ['appium:platformName']: 'iOS', ['appium:app']: 'sauce-storage:myApp.app' }, true)).toBe('iPhone 6 Plus on iOS 9.2 executing myApp.app') }) it('should return verbose mobile combo executing a browser', () => { expect(tmpReporter.getEnviromentCombo({ ['appium:deviceName']: 'iPhone 6 Plus', ['appium:platformVersion']: '9.2', ['appium:platformName']: 'iOS', browserName: 'Safari' })).toBe('iPhone 6 Plus on iOS 9.2 executing Safari') }) it('should return preface mobile combo executing a browser', () => { expect(tmpReporter.getEnviromentCombo({ ['appium:deviceName']: 'iPhone 6 Plus', ['appium:platformVersion']: '9.2', ['appium:platformName']: 'iOS', browserName: 'Safari' }, false)).toBe('iPhone 6 Plus iOS 9.2') }) it('should return verbose desktop combo when using BrowserStack capabilities', () => { expect(tmpReporter.getEnviromentCombo({ browser: 'Chrome', browser_version: 50, os: 'Windows', os_version: '10' } as any)).toBe('Chrome (v50) on Windows 10') }) it('should return preface desktop combo when using BrowserStack capabilities', () => { expect(tmpReporter.getEnviromentCombo({ browser: 'Chrome', browser_version: 50, os: 'Windows', os_version: '10' } as any, false)).toBe('Chrome 50 Windows 10') }) it('should return verbose desktop combo when using BrowserStack capabilities without os', () => { expect(tmpReporter.getEnviromentCombo({ browser: 'Chrome', browser_version: 50, } as any)).toBe('Chrome (v50) on (unknown)') }) it('should return preface desktop combo when using BrowserStack capabilities without os', () => { expect(tmpReporter.getEnviromentCombo({ browser: 'Chrome', browser_version: 50, } as any, false)).toBe('Chrome 50 (unknown)') }) it('should return verbose desktop combo when using BrowserStack capabilities without os_version', () => { expect(tmpReporter.getEnviromentCombo({ browser: 'Chrome', browser_version: 50, os: 'Windows', } as any)).toBe('Chrome (v50) on Windows') }) it('should return preface desktop combo when using BrowserStack capabilities without os_version', () => { expect(tmpReporter.getEnviromentCombo({ browser: 'Chrome', browser_version: 50, os: 'Windows', } as any, false)).toBe('Chrome 50 Windows') }) it('should return preface desktop combo when using W3C capabilities', () => { expect(tmpReporter.getEnviromentCombo({ browser: 'Chrome', browserVersion: 50, platformName: 'Windows', } as any, false)).toBe('Chrome 50 Windows') }) it('should return verbose desktop combo without platform', () => { expect(tmpReporter.getEnviromentCombo({ browserName: 'chrome', version: 50, } as any)).toBe('chrome (v50) on (unknown)') }) it('should return preface desktop combo without platform', () => { expect(tmpReporter.getEnviromentCombo({ browserName: 'chrome', version: 50, } as any, false)).toBe('chrome 50 (unknown)') }) it('should recognise bundleIds', () => { expect(tmpReporter.getEnviromentCombo({ platformName: 'Mac', automationName: 'Mac2', 'appium:bundleId': 'com.apple.calculator', sessionId: '53d1c8fd-23d9-4e81-a94b-011d2e694b9a' } as any, false)).toBe('com.apple.calculator Mac') }) it('should recognise appPackage', () => { expect(tmpReporter.getEnviromentCombo({ platformName: 'Android', 'appium:automationName': 'uiautomator2', 'appium:appPackage': 'com.example.android', 'appium:appActivity': '.MainActivity' }, false)).toBe('com.example.android Android') }) it('should prefer bundleId over app path', () => { expect(tmpReporter.getEnviromentCombo({ platformName: 'Android', 'appium:automationName': 'uiautomator2', 'appium:bundleId': 'com.example.android', 'appium:appActivity': '.MainActivity', 'appium:app': '/foo/bar/loo.apk' }, false)).toBe('com.example.android Android') }) it('prefers app activity over app path', () => { expect(tmpReporter.getEnviromentCombo({ platformName: 'Android', 'appium:automationName': 'uiautomator2', 'appium:appActivity': '.MainActivity', 'appium:app': '/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/loo.apk' }, false)).toBe('.MainActivity Android') }) it('uses file name as app path instead of long path', () => { expect(tmpReporter.getEnviromentCombo({ platformName: 'Android', 'appium:automationName': 'uiautomator2', 'appium:app': '/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/loo.apk' }, false)).toBe('loo.apk Android') }) }) describe('add real time report', () => { const options = { realtimeReporting: true } it('should call printCurrentStats for passing test', () => { tmpReporter = new SpecReporter(options) vi.spyOn(tmpReporter, 'printCurrentStats') tmpReporter.onSuiteStart(Object.values(SUITES)[0] as any) tmpReporter.onTestStart() tmpReporter['_orderedSuites'] = Object.values(SUITES) as any tmpReporter['_consoleOutput']='Printing to console spec' tmpReporter.onTestPass({ title:'test1', state:State.PASSED } as any) expect(tmpReporter.printCurrentStats).toBeCalledWith({ title:'test1', state:State.PASSED }) tmpReporter.onSuiteEnd() tmpReporter.onRunnerEnd(runnerEnd()) }) it('should call printCurrentStats for falling test', () => { tmpReporter = new SpecReporter(options) vi.spyOn(tmpReporter, 'printCurrentStats') tmpReporter.onSuiteStart(Object.values(SUITES)[0] as any) tmpReporter.onTestStart() tmpReporter['_orderedSuites'] = Object.values(SUITES) as any tmpReporter['_consoleOutput']='Printing to console spec' tmpReporter.onTestPass({ title:'test1', state:State.FAILED } as any) expect(tmpReporter.printCurrentStats).toBeCalledWith({ title:'test1', state:State.FAILED }) tmpReporter.onSuiteEnd() tmpReporter.onRunnerEnd(runnerEnd()) }) it('should call printCurrentStats skipped test', () => { tmpReporter = new SpecReporter(options) vi.spyOn(tmpReporter, 'printCurrentStats') tmpReporter.onSuiteStart(Object.values(SUITES)[0] as any) tmpReporter.onTestStart() tmpReporter['_orderedSuites'] = Object.values(SUITES) as any tmpReporter['_consoleOutput']='Printing to console spec' tmpReporter.onTestPass({ title:'test1', state:State.SKIPPED } as any) expect(tmpReporter.printCurrentStats).toBeCalledWith({ title:'test1', state:State.SKIPPED }) tmpReporter.onSuiteEnd() tmpReporter.onRunnerEnd(runnerEnd()) }) }) it('should call printCurrentStats on Hook complete', () => { tmpReporter = new SpecReporter({ realtimeReporting : false }) vi.spyOn(tmpReporter, 'printCurrentStats') tmpReporter.onSuiteStart(Object.values(SUITES)[0] as any) tmpReporter.onTestStart() tmpReporter['_orderedSuites'] = Object.values(SUITES) as any tmpReporter['_consoleOutput']='Printing to console spec' tmpReporter.onHookEnd({ title:'test1', state:State.FAILED } as any) expect(tmpReporter.printCurrentStats).toBeCalledWith({ title:'test1', state:State.FAILED }) tmpReporter.onSuiteEnd() tmpReporter.onRunnerEnd(runnerEnd()) }) })