🏡 index : quine-global/webdriverio.git

import path from 'node:path'

import { expect, test, vi, beforeEach, afterEach } from 'vitest'
import logger from '@wdio/logger'
import type { Capabilities } from '@wdio/types'

import SauceService from '../src/index.js'
import { isRDC } from '../src/utils.js'

const log = logger('test')
const uri = '/some/uri'
const featureObject = {
    name: 'Create a feature'
}
const jasmineSuiteTitle = 'Jasmine__TopLevel__Suite'

vi.mock('saucelabs', () => ({
    default: {
        default: class SauceLabsMock {
            public uploadJobAssets = vi.fn()
            public updateJob = vi.fn()
            public createTestRunsV1 = vi.fn()
        }
    }
}))

vi.mock('fs/promises', () => ({
    default: {
        createReadStream: vi.fn(),
        stat: vi.fn().mockReturnValue(Promise.resolve({ size: 123 })),
        readdir: vi.fn().mockReturnValue(Promise.resolve([
            'wdio-0-0-browser.log',
            'wdio-0-0-driver.log',
            'wdio-0-0.log',
            'wdio-1-0-browser.log',
            'wdio-1-0-driver.log',
            'wdio-1-0.log',
            'wdio-1-1-browser.log',
            'wdio-1-1-driver.log',
            'wdio-1-1.log',
            'wdio-1-11-browser.log',
            'wdio-1-11-driver.log',
            'wdio-1-11.log',
            'wdio.log'
        ]))
    }
}))

vi.mock('form-data', () => vi.fn().mockReturnValue({
    append: vi.fn()
}))

vi.mock('../src/utils', async () => {
    return {
        isRDC: vi.fn().mockReturnValue(false),
        ansiRegex: (await vi.importActual('../src/utils') as any).ansiRegex
    }
})

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

let browser: WebdriverIO.MultiRemoteBrowser
beforeEach(() => {
    browser = {
        executeScript: vi.fn(),
        getInstance: vi.fn().mockImplementation((browserName: string) => {
            // @ts-expect-error
            return browser[browserName] as WebdriverIO.Browser
        }),
        chromeA: { sessionId: 'sessionChromeA', executeScript: vi.fn() },
        chromeB: { sessionId: 'sessionChromeB', executeScript: vi.fn() },
        chromeC: { sessionId: 'sessionChromeC', executeScript: vi.fn() },
        instances: ['chromeA', 'chromeB', 'chromeC'],
    } as unknown as WebdriverIO.MultiRemoteBrowser
    vi.mocked(log.info).mockClear()
    vi.mocked(log.error).mockClear()
})

test('before should call isRDC', () => {
    const service = new SauceService({}, {}, {} as any)
    service.before({}, [], browser)
    expect(isRDC).toBeCalledTimes(1)
})

test('beforeSuite', () => {
    const service = new SauceService({}, {}, {} as any)
    service['_browser'] = browser
    expect(service['_suiteTitle']).toBeUndefined()
    service.beforeSuite({ title: 'foobar' } as any)
    expect(service['_suiteTitle']).toBe('foobar')
})

test('beforeSession should set to unknown creds if no sauce user and key are found', () => {
    const config: WebdriverIO.Config = { capabilities: [] }
    const service = new SauceService({}, {}, config)
    service['_browser'] = browser
    // @ts-expect-error
    service.beforeSession({})
    expect(config.user).toBe('unknown_user')
    expect(config.key).toBe('unknown_key')
})

test('beforeSuite should send request to set the job name as suite name', () => {
    const service = new SauceService({}, {}, {} as any)
    service['_browser'] = browser
    service.setAnnotation = vi.fn()
    expect(service['_suiteTitle']).toBeUndefined()
    service.beforeSuite({ title: 'foobar' } as any)
    expect(service['_suiteTitle']).toBe('foobar')
    expect(service.setAnnotation).toBeCalledWith('sauce:job-name=foobar')
})

test('beforeSuite should not send request to set the job name as suite name for Jasmine tests', () => {
    const service = new SauceService({}, {}, {} as any)
    service['_browser'] = browser
    service.setAnnotation = vi.fn()
    expect(service['_suiteTitle']).toBeUndefined()
    service.beforeSuite({ title: jasmineSuiteTitle } as any)
    expect(service['_suiteTitle']).toBe(jasmineSuiteTitle)
    expect(service.setAnnotation).not.toBeCalled()
})

test('beforeSuite should set job-name via custom setJobName method', async () => {
    const service = new SauceService({
        setJobName: (config, caps, title) => {
            return `${config.region} - ${(caps as any).browserName} - ${title}`
        }
    }, {
        browserName: 'foobar'
    }, {
        user: 'foobar',
        key: '123',
        region: 'barfoo' as any
    } as any)
    service['_browser'] = browser
    service.setAnnotation = vi.fn()
    expect(service['_isJobNameSet']).toBe(false)
    await service.beforeSuite({ title: 'Suite Title' } as any)
    expect(service['_isJobNameSet']).toBe(true)
    expect(service.setAnnotation).toBeCalledWith('sauce:job-name=barfoo - foobar - Suite Title')
})

test('beforeTest should send the job-name as suite name by default', async () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123', capabilities: {} })
    service['_browser'] = browser
    service['_suiteTitle'] = 'Suite Title'
    service.setAnnotation = vi.fn()
    expect(service['_isJobNameSet']).toBe(false)
    await service.beforeSuite({ title: 'foobar suite' } as any)
    expect(service['_isJobNameSet']).toBe(true)
    await service.beforeTest({
        fullName: 'my test can do something',
        description: 'foobar'
    } as any)
    expect(service.setAnnotation).toBeCalledTimes(2)
    expect(service.setAnnotation).toBeCalledWith('sauce:job-name=foobar suite')
    expect(service.setAnnotation).toBeCalledWith('sauce:context=my test can do something')
})

test('beforeTest should mark job-name as set', async () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123', capabilities: {} })
    service['_browser'] = browser
    service['_suiteTitle'] = 'Suite Title'
    expect(service['_isJobNameSet']).toBe(false)
    await service.beforeTest({
        fullName: 'my test can do something',
        description: 'foobar'
    } as any)
    expect(service['_isJobNameSet']).toBe(true)
})

test('beforeTest should set job-name via custom setJobName method', async () => {
    const service = new SauceService({
        setJobName: (config, caps, title) => {
            return `${config.region} - ${(caps as any).browserName} - ${title}`
        }
    }, {
        browserName: 'foobar'
    }, {
        user: 'foobar',
        key: '123',
        region: 'barfoo' as any
    } as any)
    service['_browser'] = browser
    service['_suiteTitle'] = 'Suite Title'
    service.setAnnotation = vi.fn()
    expect(service['_isJobNameSet']).toBe(false)
    await service.beforeTest({
        fullName: 'my test can do something',
        description: 'foobar'
    } as any)
    expect(service['_isJobNameSet']).toBe(true)
    expect(service.setAnnotation).toBeCalledWith('sauce:job-name=barfoo - foobar - Suite Title')
})

test('beforeTest should not set job-name when it has already been set', async () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123', capabilities: {} })
    service['_browser'] = browser
    service['_suiteTitle'] = 'Suite Title'
    service['_isJobNameSet'] = true
    service.setAnnotation = vi.fn()
    expect(service['_isJobNameSet']).toBe(true)
    await service.beforeTest({
        fullName: 'my test can do something',
        description: 'foobar'
    } as any)
    expect(service['_isJobNameSet']).toBe(true)
    expect(service.setAnnotation).not.toBeCalledWith('sauce:job-name=Suite Title')
})

test('beforeTest should set context for jasmine test', async () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123' } as any)
    service['_browser'] = browser
    service.setAnnotation = vi.fn()
    // @ts-expect-error
    service.beforeSession({})
    await service.beforeTest({
        fullName: 'my test can do something',
        description: 'foobar'
    } as any)
    expect(service.setAnnotation).toBeCalledWith('sauce:context=my test can do something')
})

test('beforeTest should set context for mocha test', async () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123' } as any)
    service['_browser'] = browser
    service.setAnnotation = vi.fn()
    // @ts-expect-error
    service.beforeSession({})
    await service.beforeTest({
        parent: 'foo',
        title: 'bar'
    } as any)
    expect(service.setAnnotation).toBeCalledWith('sauce:context=foo - bar')
})

test('beforeTest should not set context if user does not use sauce', async () => {
    const service = new SauceService({}, {}, {} as any)
    service['_browser'] = browser
    service.setAnnotation = vi.fn()
    // @ts-expect-error
    service.beforeSession({})
    await service.beforeTest({
        fullTitle: 'my test can do something'
    } as any)
    expect(service.setAnnotation).not.toBeCalled()
})

test('afterSuite', () => {
    const service = new SauceService({}, {}, {} as any)
    service['_browser'] = browser
    // @ts-expect-error
    service.beforeSession({})

    expect(service['_failures']).toBe(0)

    service.afterSuite({} as any)
    expect(service['_failures']).toBe(0)

    service.afterSuite({ error: new Error('foobar') } as any)
    expect(service['_failures']).toBe(1)
})

test('afterTest', () => {
    const service = new SauceService({}, {}, {} as any)
    service['_browser'] = browser
    service.setAnnotation = vi.fn()
    // @ts-expect-error
    service.beforeSession({})

    expect(service['_failures']).toBe(0)

    service.afterTest({} as any, {}, { passed: true } as any)
    expect(service['_failures']).toBe(0)

    service.afterTest({} as any, {}, { passed: false } as any)
    expect(service['_failures']).toBe(1)

    service.afterTest({ _retriedTest: {} } as any, {}, { passed: true } as any)
    expect(service['_failures']).toBe(0)

    service.afterTest({} as any, {}, { passed: false } as any)
    expect(service['_failures']).toBe(1)
    service.afterTest({
        _retriedTest: {},
        _currentRetry: 1,
        _retries: 2
    } as any, {}, { passed: false } as any)
    expect(service['_failures']).toBe(1)
    service.afterTest({
        _retriedTest: {},
        _currentRetry: 2,
        _retries: 2
    } as any, {}, { passed: false } as any)
    expect(service['_failures']).toBe(2)
    const stack = 'Error: Expected true to equal false.\n' +
        '    at <Jasmine>\n' +
        '    at UserContext.<anonymous> (/Users/test/specs/example.spec.js:12:44)\n' +
        '    at UserContext.executeSync (/Users/node_modules/@wdio/sync/build/index.js:25:22)\n' +
        '    at /Users/node_modules/@wdio/sync/build/index.js:46:68'
    service['_isRDC'] = true
    service.afterTest({} as any, {}, {
        error: {
            matcherName: 'toEqual',
            message: 'Expected true to equal false.',
            stack,
            passed: false,
            expected: [false, 'LoginPage page was not shown'],
            actual: true
        }
    } as any)
    expect(service.setAnnotation).toBeCalledTimes(5)
    vi.mocked(service.setAnnotation).mockClear()
    service['_isRDC'] = false
    service.afterTest({} as any, {}, {
        error: {
            matcherName: 'toEqual',
            message: 'Expected true to equal false.',
            stack,
            passed: false,
            expected: [false, 'LoginPage page was not shown'],
            actual: true
        }
    } as any)
    expect(service.setAnnotation).toBeCalledTimes(5)
    stack.split(/\r?\n/).forEach((line:string) => expect(service.setAnnotation).toBeCalledWith(`sauce:context=${line}`))
    vi.mocked(service.setAnnotation).mockClear()
    const maxErrorStackLength = 3
    service['_maxErrorStackLength'] = maxErrorStackLength
    service.afterTest({} as any, {}, {
        error: {
            matcherName: 'toEqual',
            message: 'Expected true to equal false.',
            stack,
            passed: false,
            expected: [false, 'LoginPage page was not shown'],
            actual: true
        }
    } as any)
    expect(service.setAnnotation).toBeCalledTimes(maxErrorStackLength)
    stack.split(/\r?\n/)
        .slice(0, maxErrorStackLength)
        .forEach((line:string) => expect(service.setAnnotation).toBeCalledWith(`sauce:context=${line}`))
})

test('afterTest should not mark test as fail if pending was called in Jasmine', () => {
    const service = new SauceService({}, {}, {} as any)
    service['_reportErrorLog'] = vi.fn()
    expect(service['_failures']).toBe(0)
    service.afterTest({} as any, {}, {
        retries: { attempts: 0, limit: 0 },
        error: '=> marked Pendingfoobar',
        result: undefined,
        duration: 0,
        passed: false,
    } as any)
    expect(service['_failures']).toBe(0)
})

test('afterTest should collect testRun info', () => {
    const service = new SauceService({},
        { browserName: 'chrome', build: 'my build', tags: ['tag1'] } as any,
        {} as any)
    expect(service['_failures']).toBe(0)
    service.afterTest(
        { parent: 'foo', title: 'bar' } as any,
        {}, { passed: true, duration: 1234 } as any
    )
    expect(service['_testRuns'].length).toBe(1)
    let testRun = service['_testRuns'][0]
    expect(testRun.name).toBe('foo - bar')
    expect(testRun.duration).toBe(1234)
    expect(testRun.framework).toBe('webdriverio')
    expect(testRun.build_name).toBe('my build')
    expect(testRun.tags).toMatchObject(['tag1'])
    expect(testRun.status).toBe('passed')

    service.afterTest(
        { parent: 'foo', title: 'bar', file: '/path/to/testfile' } as any,
        {}, { passed: false, duration: 1234, error: { message: 'err' } } as any
    )
    expect(service['_testRuns'].length).toBe(2)
    testRun = service['_testRuns'][1]
    expect(testRun.name).toBe('foo - bar')
    expect(testRun.duration).toBe(1234)
    expect(testRun.framework).toBe('webdriverio')
    expect(testRun.build_name).toBe('my build')
    expect(testRun.tags).toMatchObject(['tag1'])
    expect(testRun.status).toBe('failed')
    expect(testRun.errors).toMatchObject([{ message: 'err', path: '/path/to/testfile' }])
})

test('beforeFeature should set job-name', async () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123' } as any)
    service['_browser'] = browser
    service.setAnnotation = vi.fn()
    // @ts-expect-error
    service.beforeSession({})
    await service.beforeFeature( uri, featureObject)
    expect(service.setAnnotation).toBeCalledWith('sauce:job-name=Create a feature')
})

test('beforeFeature should set context', async () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123' } as any)
    service['_browser'] = browser
    service.setAnnotation = vi.fn()
    // @ts-expect-error
    service.beforeSession({})
    await service.beforeFeature( uri, featureObject)
    expect(service.setAnnotation).toBeCalledWith('sauce:context=Feature: Create a feature')
})

test('beforeFeature should not set context if no sauce user was applied', async () => {
    const service = new SauceService({}, {}, {} as any)
    service['_browser'] = browser
    service.setAnnotation = vi.fn()
    // @ts-expect-error
    service.beforeSession({})
    await service.beforeFeature(uri, featureObject)
    expect(service.setAnnotation).not.toBeCalledWith('sauce:context=Feature: Create a feature')
})

test('afterScenario', () => {
    const service = new SauceService({}, {}, {} as any)
    service['_browser'] = browser
    // @ts-expect-error
    service.beforeSession({})

    expect(service['_failures']).toBe(0)

    service.afterScenario({} as any, { passed: true })
    expect(service['_failures']).toBe(0)

    service.afterScenario({} as any, { passed: false })
    expect(service['_failures']).toBe(1)

    service.afterScenario({} as any, { passed: true })
    expect(service['_failures']).toBe(1)

    service.afterScenario({} as any, { passed: false })
    expect(service['_failures']).toBe(2)
})

test('beforeScenario should set context', () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123' } as any)
    service['_browser'] = browser
    service.setAnnotation = vi.fn()
    // @ts-expect-error
    service.beforeSession({})
    service.beforeScenario({ pickle: { name: 'foobar' } })
    expect(service.setAnnotation).toBeCalledWith('sauce:context=-Scenario: foobar')
})

test('beforeScenario should set context when no pickle name is provided', () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123' } as any)
    service['_browser'] = browser
    service.setAnnotation = vi.fn()
    // @ts-expect-error
    service.beforeSession({})
    service.beforeScenario({ pickle: { } })
    expect(service.setAnnotation).toBeCalledWith('sauce:context=-Scenario: unknown scenario')
})

test('beforeScenario should not set context if no sauce user was applied', () => {
    const service = new SauceService({}, {}, {} as any)
    service['_browser'] = browser
    service.setAnnotation = vi.fn()
    // @ts-expect-error
    service.beforeSession({})
    service.beforeScenario({ pickle: { name: 'foobar' } })
    expect(service.setAnnotation).not.toBeCalledWith('sauce:context=-Scenario: foobar')
})

test('beforeStep should set context', async () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123' } as any)
    const step = {
        id: '5',
        text: 'I am a step',
        astNodeIds: ['0'],
        keyword: 'Given ',
    }
    service['_browser'] = browser
    service.setAnnotation = vi.fn()
    // @ts-expect-error
    service.beforeSession({})
    await service.beforeStep(step)
    expect(service.setAnnotation).toBeCalledWith('sauce:context=--Step: Given I am a step')
})

test('beforeStep should not set context if no sauce user was applied', async () => {
    const service = new SauceService({}, {}, {} as any)
    const step = {
        id: '5',
        text: 'I am a step',
        astNodeIds: ['0'],
        keyword: 'Given ',
    }
    service['_browser'] = browser
    service.setAnnotation = vi.fn()
    // @ts-expect-error
    service.beforeSession({})
    await service.beforeStep(step)
    expect(service.setAnnotation).not.toBeCalledWith(
        'sauce:context=--Step: Given I am a step'
    )
})

test('after', async () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123' } as any)
    service['_browser'] = browser
    // @ts-expect-error
    service.beforeSession({})
    service['_failures'] = 5
    service.updateJob = vi.fn()
    service['_uploadLogs'] = vi.fn()

    // @ts-expect-error
    browser.isMultiremote = false
    // @ts-expect-error
    browser.sessionId = 'foobar'
    await service.after(1)

    expect(service.updateJob).toBeCalledWith('foobar', 5)
    expect(service['_uploadLogs']).toBeCalledWith('foobar')
})

test('after for RDC', async () => {
    const service = new SauceService({}, {}, {} as any)
    service['_browser'] = browser
    browser.capabilities = {}
    service.setAnnotation = vi.fn()
    service['_isServiceEnabled'] = true
    service['_isRDC'] = true
    service['_failures'] = 5

    // @ts-expect-error
    browser.isMultiremote = false
    await service.after(1)

    expect(service.setAnnotation).toBeCalledWith('sauce:job-result=false')
})

test('after for RDC with multi remote', async () => {
    const caps: Capabilities.MultiRemoteCapabilities = {
        chromeA: { capabilities: {} },
        chromeB: { capabilities: {} },
        chromeC: { capabilities: {} }
    }
    const service = new SauceService(
        {},
        caps,
        { user: 'foobar', key: '123' } as any
    )
    service['_browser'] = browser
    // @ts-expect-error
    service.beforeSession({})
    browser.capabilities = {}
    service['_isServiceEnabled'] = true
    service['_isRDC'] = true
    service['_failures'] = 0
    service['_uploadLogs'] = vi.fn()
    service.setAnnotation = vi.fn()
    vi.mocked(isRDC).mockImplementation(() => true)

    browser.isMultiremote = true
    // @ts-expect-error
    browser.sessionId = 'foobar'
    await service.after(123)

    expect(service.setAnnotation).toBeCalledTimes(3)
    expect(service.setAnnotation).toBeCalledWith('sauce:job-result=true')
    expect(service.setAnnotation).toBeCalledWith('sauce:job-result=true')
    expect(service.setAnnotation).toBeCalledWith('sauce:job-result=true')
    expect(service['_uploadLogs']).toBeCalledTimes(3)
    vi.mocked(isRDC).mockImplementation(() => false)
})

test('_uploadLogs should not upload if option is not set in config', async () => {
    const service = new SauceService(
        { uploadLogs: false },
        {},
        { outputDir: '/foo/bar' } as any
    )
    await service['_uploadLogs']('123')
    expect(vi.mocked(service['_api'].uploadJobAssets).mock.calls)
        .toHaveLength(0)
})

test('_uploadLogs should upload', async () => {
    const service = new SauceService(
        {},
        {},
        { outputDir: '/foo/bar' } as any
    )
    const api = { uploadJobAssets: vi.fn().mockResolvedValue({}) }
    service['_api'] = api as any
    await service.beforeSession({} as any, null as never, null as never, '1-1')
    await service['_uploadLogs']('123')
    expect(api.uploadJobAssets).toBeCalledTimes(1)
    expect(api.uploadJobAssets.mock.calls[0][1].files).toHaveLength(3)
    expect(api.uploadJobAssets.mock.calls[0][1].files)
        .toContain(path.sep + path.join('foo', 'bar', 'wdio-1-1-browser.log'))
    expect(api.uploadJobAssets.mock.calls[0][1].files)
        .toContain(path.sep + path.join('foo', 'bar', 'wdio-1-1-driver.log'))
    expect(api.uploadJobAssets.mock.calls[0][1].files)
        .toContain(path.sep + path.join('foo', 'bar', 'wdio-1-1.log'))
})

test('_uploadLogs should not fail in case of a platform error', async () => {
    const service = new SauceService(
        {},
        {},
        { outputDir: '/foo/bar' } as any
    )
    vi.mocked(service['_api'].uploadJobAssets).mockRejectedValueOnce(new Error('upps'))
    expect(log.error).toHaveBeenCalledTimes(0)
    await service['_uploadLogs']('123')
    expect(log.error).toHaveBeenCalledTimes(1)
})

test('after with bail set', async () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123', mochaOpts: { bail: 1 } } as any)
    service['_browser'] = browser
    // @ts-expect-error
    service.beforeSession({})
    service['_failures'] = 5
    service.updateJob = vi.fn()

    // @ts-expect-error
    browser.isMultiremote = false
    // @ts-expect-error
    browser.sessionId = 'foobar'
    await service.after(1)

    expect(service.updateJob).toBeCalledWith('foobar', 1)
})

test('beforeScenario should not set context if no sauce user was applied', async () => {
    const service = new SauceService({}, {}, {} as any)
    service['_browser'] = browser
    // @ts-expect-error
    service.beforeSession({})
    service['_failures'] = 5
    service.updateJob = vi.fn()

    // @ts-expect-error
    browser.isMultiremote = false
    // @ts-expect-error
    browser.sessionId = 'foobar'
    await service.after(1)

    expect(service.updateJob).not.toBeCalled()
})

test('after in multiremote', async () => {
    const caps: Capabilities.MultiRemoteCapabilities = {
        chromeA: { capabilities: {} },
        chromeB: { capabilities: {} },
        chromeC: { capabilities: {} }
    }
    const service = new SauceService({}, caps, { user: 'foobar', key: '123' } as any)
    service['_browser'] = browser
    // @ts-expect-error
    service.beforeSession({})
    service['_failures'] = 5
    service.updateJob = vi.fn()

    browser.isMultiremote = true
    // @ts-expect-error
    browser.sessionId = 'foobar'
    await service.after(1)

    expect(service.updateJob).toBeCalledWith('sessionChromeA', 5, false, 'chromeA')
    expect(service.updateJob).toBeCalledWith('sessionChromeB', 5, false, 'chromeB')
    expect(service.updateJob).toBeCalledWith('sessionChromeC', 5, false, 'chromeC')
})

test('onReload', () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123' } as any)
    service['_browser'] = browser
    // @ts-expect-error
    service.beforeSession({})
    service['_failures'] = 5
    service.updateJob = vi.fn()

    // @ts-expect-error
    browser.isMultiremote = false
    // @ts-expect-error
    browser.sessionId = 'foobar'
    service.onReload('oldbar', 'newbar')

    expect(service.updateJob).toBeCalledWith('oldbar', 5, true)
    expect(log.info).toHaveBeenCalledTimes(1)
    expect(log.info).toHaveBeenCalledWith('Update (reloaded) job with sessionId oldbar, status: failing')
})

test('onReload without failures', () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123' } as any)
    service['_browser'] = browser
    // @ts-expect-error
    service.beforeSession({})
    service['_failures'] = 0
    service.updateJob = vi.fn()

    // @ts-expect-error
    browser.isMultiremote = false
    // @ts-expect-error
    browser.sessionId = 'foobar'
    service.onReload('oldbar', 'newbar')

    expect(service.updateJob).toBeCalledWith('oldbar', 0, true)
    expect(log.info).toHaveBeenCalledTimes(1)
    expect(log.info).toHaveBeenCalledWith('Update (reloaded) job with sessionId oldbar, status: passing')
})

test('onReload should not set context if no sauce user was applied', () => {
    const service = new SauceService({}, {}, {} as any)
    service['_browser'] = browser
    // @ts-expect-error
    service.beforeSession({})
    service['_failures'] = 5
    service.updateJob = vi.fn()

    // @ts-expect-error
    browser.isMultiremote = false
    // @ts-expect-error
    browser.sessionId = 'foobar'
    service.onReload('oldbar', 'newbar')

    expect(service.updateJob).not.toBeCalled()
})

test('after in multiremote', () => {
    const caps: Capabilities.MultiRemoteCapabilities = {
        chromeA: { capabilities: {} },
        chromeB: { capabilities: {} },
        chromeC: { capabilities: {} }
    }
    const service = new SauceService({}, caps, { user: 'foobar', key: '123' } as any)
    service['_browser'] = browser
    // @ts-expect-error
    service.beforeSession({})
    service['_failures'] = 5
    service.updateJob = vi.fn()

    browser.isMultiremote = true
    // @ts-expect-error
    browser.sessionId = 'foobar'
    browser.getInstance('chromeB').sessionId = 'newSessionChromeB'
    service.onReload('sessionChromeB', 'newSessionChromeB')

    expect(service.updateJob).toBeCalledWith('sessionChromeB', 5, true, 'chromeB')
})

test('updateJob for VMs', async () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123' } as any)
    service['_browser'] = browser
    // @ts-expect-error
    service.beforeSession({})
    service['_suiteTitle'] = 'my test'

    await service.updateJob('12345', 23, true)

    expect(vi.mocked(service['_api'].updateJob)).toBeCalledWith(
        'foobar',
        '12345',
        { name: 'my test (1)', passed: false }
    )
    expect(service['_failures']).toBe(0)
})

test('updateJob for VMs without calledOnReload', () => {
    const service = new SauceService({}, {}, { user: 'foobar', key: '123' } as any)
    service['_browser'] = browser
    // @ts-expect-error
    service.beforeSession({})
    service['_suiteTitle'] = 'my test'

    service.updateJob('12345', 23)

    expect(vi.mocked(service['_api'].updateJob)).toBeCalledWith(
        'foobar',
        '12345',
        { passed: false }
    )
    expect(service['_failures']).toBe(0)
})

test('getBody', () => {
    const service = new SauceService({}, {
        name: 'jobname',
        tags: ['jobTag'],
        public: true,
        build: 'foobuild',
        'custom-data': { some: 'data' }
    }, {} as any)
    service['_browser'] = browser
    service['_suiteTitle'] = 'jojo'
    // @ts-expect-error
    service.beforeSession({})

    expect(service.getBody(0)).toEqual({
        name: 'jobname',
        tags: ['jobTag'],
        public: true,
        build: 'foobuild',
        'custom-data': { some: 'data' },
        passed: true
    })

    service['_capabilities'] = {} as WebdriverIO.Capabilities
    expect(service.getBody(1)).toEqual({
        passed: false
    })

    expect(service.getBody(1, true)).toEqual({
        name: 'jojo (1)',
        passed: false
    })

    service.getBody(1, true)
    service.getBody(1, true)
    browser.isMultiremote = true
    expect(service.getBody(12, true)).toEqual({
        name: 'jojo (2)',
        passed: false
    })

    expect(service.getBody(12, true, 'chrome')).toEqual({
        name: 'chrome: jojo (2)',
        passed: false
    })
})

test('getBody', () => {
    const service = new SauceService({},  {
        name: 'jobname',
        tags: ['jobTag'],
        public: true,
        build: 'foobuild',
        'custom-data': { some: 'data' }
    }, {} as any)
    service['_browser'] = browser
    service['_suiteTitle'] = 'jojo'
    // @ts-expect-error
    service.beforeSession({})

    expect(service.getBody(0)).toEqual({
        name: 'jobname',
        tags: ['jobTag'],
        public: true,
        build: 'foobuild',
        'custom-data': { some: 'data' },
        passed: true
    })

    service['_capabilities'] = {}
    expect(service.getBody(1)).toEqual({
        passed: false
    })

    expect(service.getBody(1, true)).toEqual({
        name: 'jojo (1)',
        passed: false
    })

    service.getBody(1, true)
    service.getBody(1, true)
    browser.isMultiremote = true
    expect(service.getBody(12, true)).toEqual({
        name: 'jojo (2)',
        passed: false
    })

    expect(service.getBody(12, true, 'chrome')).toEqual({
        name: 'chrome: jojo (2)',
        passed: false
    })
})

test('getBody with name Capability (JSON WP)', () => {
    const service = new SauceService({}, {
        name: 'bizarre'
    }, {} as any)
    service['_browser'] = browser
    service['_suiteTitle'] = 'jojo'
    // @ts-expect-error
    service.beforeSession({})

    expect(service.getBody(1)).toEqual({
        name: 'bizarre',
        passed: false
    })

    expect(service.getBody(1, true)).toEqual({
        name: 'bizarre',
        passed: false
    })

    service.getBody(1, true)
    service.getBody(1, true)
    browser.isMultiremote = true
    expect(service.getBody(12, true)).toEqual({
        name: 'bizarre',
        passed: false
    })

    expect(service.getBody(12, true, 'chrome')).toEqual({
        name: 'bizarre',
        passed: false
    })
})

test('getBody with name Capability (W3C)', () => {
    const service = new SauceService({}, {
        'sauce:options': {
            name: 'bizarre'
        }
    }, {} as any)
    service['_browser'] = browser
    service['_suiteTitle'] = 'jojo'
    // @ts-expect-error
    service.beforeSession({})

    expect(service.getBody(1)).toEqual({
        name: 'bizarre',
        passed: false
    })

    expect(service.getBody(1, true)).toEqual({
        name: 'bizarre',
        passed: false
    })

    service.getBody(1, true)
    service.getBody(1, true)
    browser.isMultiremote = true
    expect(service.getBody(12, true)).toEqual({
        name: 'bizarre',
        passed: false
    })

    expect(service.getBody(12, true, 'chrome')).toEqual({
        name: 'bizarre',
        passed: false
    })
})

test('getBody with custom setJobName method', () => {
    const service = new SauceService({
        setJobName: () => 'foobarloo'
    }, {
        'sauce:options': {
            name: 'bizarre'
        }
    }, {} as any)
    service['_browser'] = browser
    service['_suiteTitle'] = 'jojo'
    // @ts-expect-error
    service.beforeSession({})

    expect(service.getBody(1)).toEqual({
        name: 'foobarloo',
        passed: false
    })
})

test('getBody without multiremote', () => {
    const service = new SauceService({}, {
        tags: ['jobTag'],
        public: true,
        build: 'foobuild',
        'custom-data': { some: 'data' }
    }, {} as any)
    service['_browser'] = browser
    service['_suiteTitle'] = 'jojo'
    // @ts-expect-error
    service.beforeSession({})
    service['_testCnt'] = 3

    // @ts-expect-error
    browser.isMultiremote = false
    expect(service.getBody(0, true)).toEqual({
        name: 'jojo (4)',
        tags: ['jobTag'],
        public: true,
        build: 'foobuild',
        'custom-data': { some: 'data' },
        passed: true
    })
})

test('afterHook', () => {
    const service = new SauceService({}, {}, {} as any)
    service['_reportErrorLog'] = vi.fn()
    expect(service['_failures']).toBe(0)
    expect(service['_reportErrorLog']).toHaveBeenCalledTimes(0)

    // @ts-expect-error
    service.afterHook(undefined, undefined, { passed: true })
    expect(service['_failures']).toBe(0)
    expect(service['_reportErrorLog']).toHaveBeenCalledTimes(0)

    // @ts-expect-error
    service.afterHook(undefined, undefined, {
        error: new Error('foo'),
        passed: false
    })
    expect(service['_failures']).toBe(1)
    expect(service['_reportErrorLog']).toHaveBeenCalledTimes(1)
})

test('strip ansi from _reportErrorLog', () => {
    const service = new SauceService({}, {}, {} as any)
    service['_browser'] = { execute: vi.fn() } as any
    service.setAnnotation = vi.fn()
    const error = new Error('Received: ""')
    service['_reportErrorLog'](error)
    expect(service.setAnnotation).toBeCalledWith('sauce:context=Error: Received: ""')
})

test('_reportErrorLog without error stack', () => {
    const service = new SauceService({}, {}, {} as any)
    service['_browser'] = { execute: vi.fn() } as any
    service.setAnnotation = vi.fn()
    const error = { name: 'name', message: 'message' }
    service['_reportErrorLog'](error)
    expect(service.setAnnotation).toBeCalledWith('sauce:context=')
})

test('setAnnotation without a browser', async () => {
    const service = new SauceService({}, {}, {} as any)
    await service.setAnnotation('foo')

    expect(browser.executeScript).toBeCalledTimes(0)
})

test('setAnnotation', async () => {
    const service = new SauceService({}, {}, {} as any)
    service['_browser'] = browser
    // @ts-expect-error
    browser.isMultiremote = false
    await service.setAnnotation('foo')

    expect(browser.executeScript).toBeCalledWith('foo', [])
})

test('setAnnotation for VDC and RDC with multi remote', async () => {
    const caps: Capabilities.MultiRemoteCapabilities = {
        chromeA: { capabilities: {} },
        chromeB: { capabilities: {} },
        chromeC: { capabilities: {} }
    }
    const service = new SauceService({}, caps, {} as any)
    service['_browser'] = browser
    vi.mocked(isRDC).mockReturnValueOnce(true)
    browser.isMultiremote = true
    // @ts-expect-error
    browser.sessionId = 'foobar'
    await service.setAnnotation('sauce:context=foo')

    const browserChromeA = browser.getInstance('chromeA')
    const browserChromeB = browser.getInstance('chromeB')
    const browserChromeC = browser.getInstance('chromeC')

    expect(browserChromeA.executeScript).toBeCalledWith('sauce:context=foo', [])
    expect(browserChromeB.executeScript).toBeCalledWith('sauce:context=foo', [])
    expect(browserChromeC.executeScript).toBeCalledWith('sauce:context=foo', [])
})

afterEach(() => {
    // @ts-ignore
    browser = undefined
})