🏡 index : quine-global/webdriverio.git

import fs from 'node:fs/promises'
import * as cp from 'node:child_process'
import { vi, test, expect, beforeEach, afterEach, describe, it } from 'vitest'
import readDir from 'recursive-readdir'
import ejs from 'ejs'
import { $ } from 'execa'
import { runProgram, getPackageVersion,

    convertPackageHashToObject,
    generateTestFiles,
    getPathForFileGeneration,
    getDefaultFiles,
    getProjectRoot,
    getAnswers,
    getProjectProps,
    createPackageJSON,
    npmInstall,
    setupTypeScript,
    createWDIOConfig,
    createWDIOScript,
    runAppiumInstaller,
    detectCompiler,
    findInConfig,
    replaceConfig,
    formatConfigFilePaths,

} from '../src/utils.js'
import { parseAnswers } from '../src/cli/utils.js'
import path from 'node:path'
import { readPackageUp } from 'read-pkg-up'
import type { Questionnair } from '../src/types.js'
import inquirer from 'inquirer'
import { installPackages } from '../src/install.js'

const consoleLog = console.log.bind(console)
const processExit = process.exit.bind(process)

vi.mock('node:fs/promises', () => ({
    default: {
        access: vi.fn().mockRejectedValue(new Error('ENOENT')),
        mkdir: vi.fn(),
        writeFile: vi.fn().mockReturnValue(Promise.resolve())
    }
}))
vi.mock('child_process', () => {
    const m = {
        execSyncRes: 'APPIUM_MISSING',
        execSync: () => m.execSyncRes,
        exec: vi.fn(),
        spawn: vi.fn().mockReturnValue({ on: vi.fn().mockImplementation((ev, fn) => fn(0)) })
    }
    return m
})
vi.mock('inquirer')
vi.mock('read-pkg-up')
vi.mock('ejs')
vi.mock('execa', () => ({
    execa: vi.fn(()=>({ stdout:'', stderr:'', exitCode:0 })),
    $: vi.fn().mockReturnValue(async (sh: string) => sh)
}))
vi.mock('recursive-readdir', () => ({
    default: vi.fn().mockResolvedValue([
        '/foo/bar/loo/page.js.ejs',
        '/foo/bar/example.e2e.js'
    ] as any)
}))
vi.mock('../src/install.js', () => ({
    installPackages: vi.fn(),
    getInstallCommand: vi.fn().mockReturnValue('npm install foo bar --save-dev')
}))

beforeEach(() => {
    process.exit = vi.fn() as any
    console.log = vi.fn()
    vi.mocked(readPackageUp).mockResolvedValue({
        path: '/foo/package.json',
        packageJson: {
            name: 'cool-test-module',
            type: 'module'
        }
    })
})
afterEach(() => {
    process.exit = processExit
    console.log = consoleLog
})

test('runProgram', async () => {
    expect(await runProgram('echo', ['123'], {})).toBe(undefined)

    await runProgram('node', ['-e', 'throw new Error(\'ups\')'], {}).catch((e) => e)
    expect(vi.mocked(console.log).mock.calls[0][0]).toMatch(/Error calling: node -e throw new Error/)
    expect(process.exit).toBeCalledTimes(1)

    await runProgram('foobarloo', [], {}).catch((e) => e)

    expect(vi.mocked(console.log).mock.calls[1][0]).toMatch(/spawn foobarloo ENOENT/)
    expect(process.exit).toBeCalledTimes(2)
})

test('getPackageVersion', async () => {
    expect(await getPackageVersion()).toEqual(expect.any(String))
})

describe('convertPackageHashToObject', () => {
    it('works with default `$--$` hash', () => {
        expect(convertPackageHashToObject('test/package-name$--$package-name')).toMatchObject({
            package: 'test/package-name',
            short: 'package-name'
        })
    })

    it('works with custom hash', () => {
        expect(convertPackageHashToObject('test/package-name##-##package-name', '##-##')).toMatchObject({
            package: 'test/package-name',
            short: 'package-name'
        })
    })
})

describe('generateTestFiles', () => {
    it('Mocha with page objects', async () => {
        vi.mocked(readDir).mockResolvedValue([
            '/foo/bar/loo/page.js.ejs',
            '/foo/bar/example.e2e.js'
        ] as any)
        const answers = {
            runner: 'local',
            framework: 'mocha',
            usePageObjects: true,
            generateTestFiles: true,
            destPageObjectRootPath: '/tests/page/objects/model',
            destSpecRootPath: '/tests/specs'
        }

        await generateTestFiles(answers as any)

        expect(readDir).toBeCalledTimes(2)
        expect(vi.mocked(readDir).mock.calls[0][0]).toContain('mocha')
        expect(vi.mocked(readDir).mock.calls[1][0]).toContain('pageobjects')

        /**
         * test readDir callback
         */
        const readDirCb = vi.mocked(readDir).mock.calls[0][1][0] as Function
        const stats = { isDirectory: vi.fn().mockReturnValue(false) }
        expect(readDirCb('/foo/bar.lala', stats)).toBe(true)
        expect(readDirCb('/foo/bar.js.ejs', stats)).toBe(false)
        expect(readDirCb('/foo/bar.feature', stats)).toBe(false)
        stats.isDirectory.mockReturnValue(true)
        expect(readDirCb('/foo/bar.lala', stats)).toBe(false)
        expect(readDirCb('/foo/bar.js.ejs', stats)).toBe(false)
        expect(readDirCb('/foo/bar.feature', stats)).toBe(false)

        expect(ejs.renderFile).toBeCalledTimes(4)
        expect(ejs.renderFile).toBeCalledWith(
            '/foo/bar/loo/page.js.ejs',
            { answers },
            expect.any(Function)
        )
        expect(ejs.renderFile).toBeCalledWith(
            '/foo/bar/example.e2e.js',
            { answers },
            expect.any(Function)
        )
        expect(fs.mkdir).toBeCalledTimes(4)
        expect((vi.mocked(fs.writeFile).mock.calls[0][0] as string)
            .endsWith(`${path.sep}page${path.sep}objects${path.sep}model${path.sep}page.js`))
            .toBe(true)
        expect((vi.mocked(fs.writeFile).mock.calls[1][0] as string)
            .endsWith(`${path.sep}example.e2e.js`))
            .toBe(true)
    })

    it('jasmine with page objects', async () => {
        const answers = {
            runner: 'local',
            framework: 'jasmine',
            usePageObjects: true,
            generateTestFiles: true,
            destPageObjectRootPath: '/tests/page/objects/model',
            destSpecRootPath: '/tests/specs'
        }

        await generateTestFiles(answers as any)

        expect(readDir).toBeCalledTimes(2)
        expect(vi.mocked(readDir).mock.calls[0][0]).toContain('mochaJasmine')
        expect(vi.mocked(readDir).mock.calls[1][0]).toContain('pageobjects')

        /**
         * test readDir callback
         */
        const readDirCb = vi.mocked(readDir).mock.calls[0][1][0] as Function
        const stats = { isDirectory: vi.fn().mockReturnValue(false) }
        expect(readDirCb('/foo/bar.lala', stats)).toBe(true)
        expect(readDirCb('/foo/bar.js.ejs', stats)).toBe(false)
        expect(readDirCb('/foo/bar.feature', stats)).toBe(false)
        stats.isDirectory.mockReturnValue(true)
        expect(readDirCb('/foo/bar.lala', stats)).toBe(false)
        expect(readDirCb('/foo/bar.js.ejs', stats)).toBe(false)
        expect(readDirCb('/foo/bar.feature', stats)).toBe(false)

        expect(ejs.renderFile).toBeCalledTimes(4)
        expect(ejs.renderFile).toBeCalledWith(
            '/foo/bar/loo/page.js.ejs',
            { answers },
            expect.any(Function)
        )
        expect(ejs.renderFile).toBeCalledWith(
            '/foo/bar/example.e2e.js',
            { answers },
            expect.any(Function)
        )
        expect(fs.mkdir).toBeCalledTimes(4)
        expect((vi.mocked(fs.writeFile).mock.calls[0][0] as string)
            .endsWith(`${path.sep}page${path.sep}objects${path.sep}model${path.sep}page.js`))
            .toBe(true)
        expect((vi.mocked(fs.writeFile).mock.calls[1][0] as string)
            .endsWith(`${path.sep}example.e2e.js`))
            .toBe(true)
    })

    it('Jasmine with page generation and no pageObjects', async () => {
        vi.mocked(readDir).mockResolvedValue([] as any)
        const answers = {
            runner: 'local',
            specs: './tests/e2e/**/*.js',
            framework: 'jasmine',
            generateTestFiles: false,
            usePageObjects: false
        }

        await generateTestFiles(answers as any)

        expect(readDir).toBeCalledTimes(1)
        expect(ejs.renderFile).toBeCalledTimes(0)
    })

    it('Cucumber with page generation and no pageObjects', async () => {
        vi.mocked(readDir).mockResolvedValue([] as any)
        const answers = {
            runner: 'local',
            specs: './tests/e2e/**/*.js',
            framework: 'cucumber',
            generateTestFiles: false,
            usePageObjects: false,
        }

        await generateTestFiles(answers as any)

        expect(readDir).toBeCalledTimes(1)
        expect(ejs.renderFile).toBeCalledTimes(0)
    })

    it('Cucumber without page objects', async () => {
        vi.mocked(readDir).mockResolvedValue([
            '/foo/bar/loo/step_definition/example.step.js',
            '/foo/bar/example.feature'
        ] as any)
        const answers = {
            runner: 'local',
            specs: './tests/e2e/*.js',
            framework: 'cucumber',
            stepDefinitions: '/some/step/defs',
            usePageObjects: false,
            generateTestFiles: true,
            destSpecRootPath: '/tests/specs',
            destStepRootPath: '/tests/stepDefinitions'
        }
        await generateTestFiles(answers as any)

        expect(readDir).toBeCalledTimes(1)
        expect(vi.mocked(readDir).mock.calls[0][0]).toContain('cucumber')
        expect(ejs.renderFile).toBeCalledTimes(2)
        expect(ejs.renderFile).toBeCalledWith(
            '/foo/bar/loo/step_definition/example.step.js',
            { answers },
            expect.any(Function)
        )
        expect(ejs.renderFile).toBeCalledWith(
            '/foo/bar/example.feature',
            { answers },
            expect.any(Function)
        )
        expect(fs.mkdir).toBeCalledTimes(2)
    })

    it('Cucumber with page objects and TypeScript', async () => {
        vi.mocked(readDir).mockResolvedValue([
            '/foo/bar/loo/page.js.ejs',
            '/foo/bar/loo/step_definition/example.step.js',
            '/foo/bar/example.feature'
        ] as any)
        const answers = {
            runner: 'local',
            framework: 'cucumber',
            usePageObjects: true,
            isUsingTypeScript: true,
            destStepRootPath: '/tests/stepDefinitions',
            destSpecRootPath: '/tests/specs',
            destPageObjectRootPath: '/some/page/objects',
            relativePath: '../page/object'
        }
        await generateTestFiles(answers as any)

        expect(readDir).toBeCalledTimes(2)
        expect(vi.mocked(readDir).mock.calls[0][0]).toContain('cucumber')
        expect(ejs.renderFile).toBeCalledTimes(6)
        expect(ejs.renderFile).toBeCalledWith(
            '/foo/bar/loo/step_definition/example.step.js',
            { answers },
            expect.any(Function)
        )
        expect(ejs.renderFile).toBeCalledWith(
            '/foo/bar/example.feature',
            { answers },
            expect.any(Function)
        )
        expect(fs.mkdir).toBeCalledTimes(6)
        expect(
            (vi.mocked(fs.writeFile).mock.calls[0][0] as string)
                .endsWith(`${path.sep}some${path.sep}page${path.sep}objects${path.sep}page.ts`)
        ).toBe(true)
        expect(
            (vi.mocked(fs.writeFile).mock.calls[2][0] as string)
                .endsWith(`${path.sep}example.feature`)
        ).toBe(true)
    })

})

describe('findInConfig', () => {
    it('finds text for services', () => {
        const str = "services: ['foo', 'bar'],"

        expect(findInConfig(str, 'service')).toMatchObject([
            'services: [\'foo\', \'bar\']'
        ])
    })

    it('finds text for frameworks', () => {
        const str = "framework: 'mocha'"

        expect(findInConfig(str, 'framework')).toMatchObject([
            "framework: 'mocha'"
        ])
    })
})

describe('formatConfigFilePaths', () => {
    it('should format properly', async () => {

        expect(await formatConfigFilePaths('/path/to/foo.js')).toMatchObject({
            fullPath:'/path/to/foo.js',
            fullPathNoExtension:'/path/to/foo'
        })
    })
})

describe('replaceConfig', () => {
    it('correctly changes framework', () => {
        const fakeConfig = `exports.config = {
    runner: 'local',
    specs: [
        './test/specs/**/*.js'
    ],
    framework: 'mocha',
}`

        expect(replaceConfig(fakeConfig, 'framework', 'jasmine')).toBe(
            `exports.config = {
    runner: 'local',
    specs: [
        './test/specs/**/*.js'
    ],
    framework: 'jasmine',
}`
        )
    })

    it('correctly changes service', () => {
        const fakeConfig = `exports.config = {
    runner: 'local',
    specs: [
        './test/specs/**/*.js'
    ],
    services: [],
    framework: 'mocha',
}`
        expect(replaceConfig(fakeConfig, 'service', 'sauce')).toBe(
            `exports.config = {
    runner: 'local',
    specs: [
        './test/specs/**/*.js'
    ],
    services: ['sauce'],
    framework: 'mocha',
}`
        )
    })
})

describe('getPathForFileGeneration', () => {
    it('Cucumber with pageobjects default values', () => {
        const generatedPaths = getPathForFileGeneration({
            runner: 'local',
            stepDefinitions: './features/step-definitions/steps.js',
            pages: './features/pageobjects/**/*.js',
            generateTestFiles: true,
            usePageObjects: true,
            framework: '@wdio/cucumber-service$--$cucumber'
        } as any, '/foo/bar')
        expect(generatedPaths.relativePath).toEqual('../pageobjects')
    })

    it('Cucumber with pageobjects default different path', () => {
        const generatedPaths = getPathForFileGeneration({
            runner: 'local',
            stepDefinitions: './features/step-definitions/steps.js',
            pages: './features/page/objects/**/*.js',
            generateTestFiles: true,
            usePageObjects: true,
            framework: '@wdio/cucumber-service$--$cucumber'
        } as any, '/foo/bar')
        expect(generatedPaths.relativePath).toEqual('../page/objects')
    })
    it('Cucumber with pageobjects and steps different path', () => {
        const generatedPaths = getPathForFileGeneration({
            runner: 'local',
            stepDefinitions: 'cucumber/features/steps',
            pages: 'cucumber/features/pages',
            generateTestFiles: true,
            usePageObjects: true,
            framework: '@wdio/cucumber-service$--$cucumber'
        } as any, '/foo/bar')
        expect(generatedPaths.relativePath).toEqual('')
    })

    it('Cucumber with answer that is not a path', () => {
        const generatedPaths = getPathForFileGeneration({
            runner: 'local',
            stepDefinitions: 'y',
            pages: 'h',
            generateTestFiles: true,
            usePageObjects: true,
            framework: '@wdio/cucumber-service$--$cucumber'
        } as any, '/foo/bar')
        expect(generatedPaths.relativePath).toEqual('../h')
    })

    it('Mocha with pageobjects default values', () => {
        const generatedPaths = getPathForFileGeneration({
            runner: 'local',
            specs: './test/specs/**/*.js',
            pages: './test/pageobjects/**/*.js',
            generateTestFiles: true,
            usePageObjects: true,
            framework: '@wdio/cucumber-service$--$mocha'
        } as any, '/foo/bar')
        expect(generatedPaths.relativePath).toEqual('../pageobjects')
    })

    it('Mocha with pageobjects different path', () => {
        const generatedPaths = getPathForFileGeneration({
            runner: 'local',
            specs: './test/specs/files/**/*.js',
            pages: './test/pageobjects/**/*.js',
            generateTestFiles: true,
            usePageObjects: true,
            framework: '@wdio/cucumber-service$--$mocha'
        } as any, '/foo/bar')
        expect(generatedPaths.relativePath).toEqual('../../pageobjects')
    })

    it('Do not auto generate file', () => {
        const generatedPaths = getPathForFileGeneration({
            runner: 'local',
            specs: './test/specs/files/**/*.js',
            pages: './test/pageobjects/**/*.js',
            generateTestFiles: false,
            usePageObjects: true,
            framework: '@wdio/cucumber-service$--$mocha'
        } as any, '/foo/bar')
        expect(generatedPaths.relativePath).toEqual('')
    })

    it('Do not use PageObjects', () => {
        const generatedPaths = getPathForFileGeneration({
            runner: 'local',
            specs: './test/specs/files/**/*.js',
            pages: './test/pageobjects/**/*.js',
            generateTestFiles: true,
            usePageObjects: false,
            framework: '@wdio/cucumber-service$--$mocha'
        } as any, '/foo/bar')
        expect(generatedPaths.relativePath).toEqual('')
    })
})

test('getDefaultFiles', async () => {
    const files = '/foo/bar'
    expect(await getDefaultFiles({ projectRootCorrect: false, projectRoot: '/bar', isUsingTypeScript: true } as any, files))
        .toBe(path.join('/bar', 'foo', 'bar.ts'))
    expect(await getDefaultFiles({ projectRootCorrect: false, projectRoot: '/bar', isUsingTypeScript: true, preset: 'vite-plugin-solid$--$solid' } as any, files))
        .toBe(path.join('/bar', 'foo', 'bar.tsx'))
    expect(await getDefaultFiles({ projectRootCorrect: false, projectRoot: '/bar', isUsingTypeScript: false } as any, files))
        .toBe(path.join('/bar', 'foo', 'bar.js'))
})

test('original implementation of getDefaultFiles handles projectRoot with no package.json', async () => {
    const files = '/foo/bar'
    vi.mocked(readPackageUp).mockRestore()
    expect(await getDefaultFiles({ createPackageJSON: true, projectRoot: '/project-root-with-no-package.json', isUsingTypeScript: false } as any, files))
        .toBe(path.join('/project-root-with-no-package.json', 'foo', 'bar.js'))
})

test('getProjectRoot', async () => {
    expect(await getProjectRoot()).toBe('/foo')
    expect(await getProjectRoot({
        projectRootCorrect: true
    } as any)).toBe('/foo')
    expect(await getProjectRoot({
        projectRootCorrect: false,
        projectRoot: '/bar/foo'
    } as any)).toBe('/bar/foo')
})

test('detectCompiler', async () => {
    vi.mocked(readPackageUp).mockRestore()
    const answers = { createPackageJSON: true } as Questionnair
    expect(await detectCompiler(answers)).toBe(false)
})

test('getAnswers', async () => {
    let answers = await getAnswers(true)
    delete answers.pages // delete so it doesn't fail in Windows
    delete answers.specs // delete so it doesn't fail in Windows
    expect(answers).toMatchSnapshot()
    vi.mocked(inquirer.prompt).mockReturnValue('some value' as any)
    answers = await getAnswers(false)
    delete answers.pages // delete so it doesn't fail in Windows
    delete answers.specs // delete so it doesn't fail in Windows
    expect(answers).toBe('some value')
    expect(inquirer.prompt).toBeCalledTimes(2)
    // @ts-ignore
    expect(vi.mocked(inquirer.prompt).mock.calls[0][0][0].when).toBe(false)
    vi.mocked(readPackageUp).mockResolvedValue(undefined)
    vi.mocked(inquirer.prompt).mockClear()
    expect(await getAnswers(false)).toBe('some value')
    expect(inquirer.prompt).toBeCalledTimes(2)
    // @ts-ignore
    expect(vi.mocked(inquirer.prompt).mock.calls[0][0][0].when).toBe(true)
})

test('getProjectProps', async () => {
    vi.mocked(readPackageUp).mockResolvedValue(undefined)
    expect(await getProjectProps('/foo/bar')).toBe(undefined)
    expect(readPackageUp).toBeCalledWith({ cwd: '/foo/bar' })
    vi.mocked(readPackageUp).mockResolvedValue({
        path: '/foo/bar',
        packageJson: {
            name: 'cool-test-module2',
        }
    })
    expect(await getProjectProps('/foo/bar')).toEqual({
        esmSupported: false,
        packageJson: { name: 'cool-test-module2' },
        path: '/foo'
    })
})

test('createPackageJSON', async () => {
    await createPackageJSON({} as any)
    expect(fs.writeFile).toBeCalledTimes(0)
    await createPackageJSON({
        createPackageJSON: true
    } as any)
    expect(console.log).toBeCalledTimes(2)
})

test('npmInstall', async () => {
    const parsedAnswers = {
        rawAnswers: {
            services: ['foo$--$bar'],
            preset: 'barfoo$--$vue'
        },
        projectRootCorrect: false,
        projectRoot: '/foo/bar',
        isUsingTypeScript: true,
        framework: 'jasmine',
        installTestingLibrary: true,
        packagesToInstall: ['foo$--$bar', 'bar$--$foo'],
        npmInstall: true,
        includeVisualTesting: true
    } as any
    await npmInstall(parsedAnswers, 'next')
    expect(installPackages).toBeCalledTimes(1)
    expect(vi.mocked(installPackages).mock.calls).toMatchSnapshot()
})

test('not npmInstall', async () => {
    const parsedAnswers = {
        rawAnswers: {
            services: ['foo$--$bar'],
            preset: 'barfoo$--$vue'
        },
        installTestingLibrary: true,
        packagesToInstall: ['foo$--$bar', 'bar$--$foo'],
        npmInstall: false
    } as any
    await npmInstall(parsedAnswers, 'next')
    expect(installPackages).toBeCalledTimes(0)
    expect(vi.mocked(console.log).mock.calls[0][0]).toContain('To install dependencies, execute')
})

test('setupTypeScript', async () => {
    await setupTypeScript({} as any)
    expect(fs.writeFile).toBeCalledTimes(0)
    const parsedAnswers = {
        isUsingTypeScript: true,
        esmSupport: true,
        rawAnswers: {
            framework: 'foo',
            services: [
                'wdio-foobar-service$--$foobar',
                'wdio-electron-service$--$electron'
            ]
        },
        packagesToInstall: [],
        tsConfigFilePath: '/foobar/tsconfig.json'
    } as any
    await setupTypeScript(parsedAnswers)
    expect(vi.mocked(fs.writeFile).mock.calls[0][1]).toMatchSnapshot()
})

test('setupTypeScript does not create tsconfig.json if TypeScript was not selected', async () => {
    const parsedAnswers = {
        isUsingTypeScript: false,
        esmSupport: true,
        rawAnswers: {
            framework: 'foo',
            services: [
                'wdio-foobar-service$--$foobar',
                'wdio-electron-service$--$electron'
            ]
        },
        packagesToInstall: [],
        tsConfigFilePath: '/foobar/tsconfig.json'
    } as any
    await setupTypeScript(parsedAnswers)
    expect(fs.writeFile).not.toBeCalled()
    expect(parsedAnswers.packagesToInstall).toEqual([])
})

test('setupTypeScript does not create tsconfig.json if there is already one', async () => {
    const parsedAnswers = {
        isUsingTypeScript: true,
        esmSupport: true,
        rawAnswers: {
            framework: 'foo',
            services: [
                'wdio-foobar-service$--$foobar',
                'wdio-electron-service$--$electron'
            ]
        },
        packagesToInstall: [],
        tsConfigFilePath: '/foobar/tsconfig.json',
        hasRootTSConfig: true
    } as any
    await setupTypeScript(parsedAnswers)
    expect(fs.writeFile).not.toBeCalled()
})

describe.skip('createWDIOScript', () => {
    it('can run with success', async () => {
        const promise = createWDIOScript({ wdioConfigPath: '/foo/bar/wdio.conf.js' } as any)
        expect(await promise)
            .toBe(true)
        expect(cp.spawn).toBeCalledTimes(1)
    })

    it('does not fail the process if spawn errors out', async () => {
        vi.mocked(cp.spawn).mockReturnValue({ on: vi.fn().mockImplementation((ev, fn) => fn(1)) } as any)
        expect(await createWDIOScript({ wdioConfigPath: '/foo/bar/wdio.conf.js' } as any))
            .toBe(false)
        expect(cp.spawn).toBeCalledTimes(1)
    })
})

test('createWDIOConfig', async () => {
    const answers = await parseAnswers(true)
    answers.destSpecRootPath = '/tests/specs'
    answers.destPageObjectRootPath = '/tests/specs'
    answers.stepDefinitions = './foo/bar'
    answers.specs = '/foo/bar/**'
    await createWDIOConfig(answers as any)
    expect(fs.writeFile).toBeCalledTimes(7)
    expect(
        vi.mocked(fs.writeFile).mock.calls[0][0]
            .toString()
            .endsWith(path.resolve('wdio.conf.js'))
    ).toBe(true)
})

test('runAppiumInstaller', async () => {
    expect(await runAppiumInstaller({ e2eEnvironment: 'web' } as any))
        .toBe(undefined)
    expect(console.log).toBeCalledTimes(0)
    expect($).toBeCalledTimes(0)

    vi.mocked(inquirer.prompt).mockResolvedValue({
        continueWithAppiumSetup: false
    })

    expect(await runAppiumInstaller({ e2eEnvironment: 'mobile' } as any))
        .toBe(undefined)
    expect(console.log).toBeCalledTimes(1)
    expect($).toBeCalledTimes(0)

    vi.mocked(inquirer.prompt).mockResolvedValue({
        continueWithAppiumSetup: true
    })
    expect(await runAppiumInstaller({ e2eEnvironment: 'mobile' } as any))
        .toEqual(['npx appium-installer'])
    expect($).toBeCalledTimes(1)
})
afterEach(()=>{
    vi.mocked(inquirer.prompt).mockClear()
    vi.mocked(readDir).mockClear()
    vi.mocked(ejs.renderFile).mockClear()
    vi.mocked(fs.writeFile).mockClear()
    vi.mocked(fs.mkdir).mockClear()
    vi.mocked(cp.spawn).mockClear()
    vi.mocked(installPackages).mockClear()
})