🏡 index : quine-global/webdriverio.git

import path from 'node:path'
import { describe, expect, it, vi, beforeAll, afterEach } from 'vitest'
import { getValue, setValue, setPort, setResourcePool, getValueFromPool, addValueToPool } from '../src/client.js'

vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger')))
vi.spyOn(global, 'fetch').mockImplementation((URL: string | URL | globalThis.Request, options?: RequestInit) => {
    const url = URL as string
    const body = options?.body as string
    const key = url.split('/').pop() || JSON.parse(body).key

    if (key === 'fail') {
        return Promise.reject({
            message: 'Mock error'
        })
    }
    if (key === 'not-present') {
        return Promise.resolve(Response.json({ value: undefined }))
    }

    return Promise.resolve(Response.json({ value: 'store value' }))
})

const port = 3000
const baseUrl = `http://localhost:${port}`
const headers = {
    'Content-Type': 'application/json'
}

describe('client', () => {
    afterEach(() => {
        vi.mocked(fetch).mockClear()
    })

    describe('when used in launcher process', () => {
        it('Error should be thrown when attempting to retrieve a value before server initialization', () => {
            expect(getValue('*')).rejects.toThrowError('Attempting to use `getValue` before the server has been initialized.')
            expect(getValueFromPool('*')).rejects.toThrowError('Attempting to use `getValueFromPool` before the server has been initialized.')
            expect(addValueToPool('*', '')).rejects.toThrowError('Attempting to use `addValueToPool` before the server has been initialized.')
        })

        it('should not post before server has started', async () => {
            const result1 = await setValue('foo', 'bar')
            const result2 = await setValue('bar', 'foo')
            const result3 = await setResourcePool('foo', ['bar'])
            expect([result1, result2, result3]).toEqual([undefined, undefined, undefined])

            expect(fetch).toBeCalledTimes(0)
            setPort(port)
            await Promise.resolve()
            expect(vi.mocked(fetch)).toBeCalledTimes(3)
            for (let i = 1; i <= 3; i++) {
                expect(vi.mocked(fetch)).toHaveBeenNthCalledWith(
                    i,
                    expect.any(String),
                    expect.objectContaining({ method: 'post' })
                )
            }
            await setValue('another', 'item')
            expect(vi.mocked(fetch).mockName('post')).toBeCalledTimes(4)
            for (let i = 1; i <= 4; i++) {
                expect(vi.mocked(fetch)).toHaveBeenNthCalledWith(
                    i,
                    expect.any(String),
                    expect.objectContaining({ method: 'post' })
                )
            }
        })
    })

    describe('when used in worker process', () => {
        beforeAll(() => {
            setPort(port)
        })

        it('should set value', async () => {
            await setValue('foo', 'bar')
            expect(fetch).toBeCalledWith(`${baseUrl}/`, {
                method: 'post',
                body: JSON.stringify({ key: 'foo', value: 'bar' }),
                headers
            })
        })

        it('should get value', async () => {
            const result = await getValue('foo')
            expect(fetch).toBeCalledWith(`${baseUrl}/foo`, {
                method: 'get',
                headers
            })
            expect(result).toBe('store value')
        })

        it('should not fail if key is not in store', async () => {
            const result = await getValue('not-present')
            expect(fetch).toBeCalledWith(`${baseUrl}/not-present`, {
                method: 'get',
                headers
            })
            expect(result).toBeUndefined()
        })

        it('should throw an error on get error', async () => {
            await expect(() => getValue('fail')).rejects.toThrowError('Mock error')
        })

        it('should throw an error on set error', async () => {
            await expect(() => setValue('fail', 'fail')).rejects.toThrowError('Mock error')
        })
    })

    describe('when calling setResourcePool', () => {
        describe('and after setPort is called', () => {
            it("should call /pool/set and return it's response code", async () => {
                vi.mocked(fetch).mockResolvedValueOnce(Response.json({}, { status: 200 }))
                const result = await setResourcePool('foo', ['bar'])
                expect(fetch).toBeCalledWith(`${baseUrl}/pool`, {
                    method: 'post',
                    body: JSON.stringify({ key: 'foo', value: ['bar'] }),
                    headers
                })
                expect(result).toEqual(200)
            })
        })

        describe('and when /pool/set fails', () => {
            it('should throw the error', async () => {
                vi.mocked(fetch).mockRejectedValue({ message: 'postError' })
                await expect(() => setResourcePool('fail', ['bar'])).rejects.toThrowError('postError')
            })
        })
    })

    describe('when calling getValueFromPool', () => {
        describe('and after setPort is called', () => {
            it("should call /pool/get and return it's response", async () => {
                vi.mocked(fetch).mockResolvedValueOnce(Response.json({ value: 'getResult' }))
                const result = await getValueFromPool('foo', { timeout: 100 })
                expect(fetch).toBeCalledWith(`${baseUrl}/pool/foo?timeout=100`, {
                    method: 'get',
                    headers
                })
                expect(result).toEqual('getResult')
            })
        })

        describe('and when /pool/get fails', () => {
            it('should throw the error', async () => {
                vi.mocked(fetch).mockRejectedValue({ message: 'getError' })
                await expect(() => getValueFromPool('foo')).rejects.toThrowError('getError')
            })
        })
    })

    describe('when calling addValueToPool', () => {
        describe('and after setPort is called', () => {
            it("should call /pool/add and return it's response code", async () => {
                vi.mocked(fetch).mockResolvedValueOnce(Response.json({}, { status: 200 }))
                const result = await addValueToPool('foo', 'bar')
                expect(fetch).toBeCalledWith(`${baseUrl}/pool/foo`, {
                    method: 'post',
                    body: JSON.stringify({ value: 'bar' }),
                    headers })
                expect(result).toEqual(200)
            })
        })

        describe('and when /pool/add fails', () => {
            it('should throw the error', async () => {
                vi.mocked(fetch).mockRejectedValue({ message: 'postError' })
                await expect(() => addValueToPool('foo', {})).rejects.toThrowError('postError')
            })
        })
    })
})