import path from 'node:path' import type { Mock, VitestUtils } from 'vitest' import { beforeEach, expect, describe, it, afterEach, vi, beforeAll } from 'vitest' import logger from '@wdio/logger' import { MobileScrollDirection, remote } from '../../../src/index.js' vi.mock('fetch') const log = logger('test') vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger'))) let browser: WebdriverIO.Browser let elem: WebdriverIO.Element let rectSpy: any let logSpy: any const defaultAndroidSelector = '//android.widget.ScrollView' describe('swipe test', () => { beforeEach(async () => { browser = await remote({ baseUrl: 'http://foobar.com', capabilities: { browserName: 'foobar', platformName: 'Android', app: 'app', mobileMode: true, nativeAppMode: true, } as any }) // @ts-expect-error elem = await browser.$('#scrollableElement') rectSpy = vi.spyOn(browser, 'getElementRect').mockResolvedValue({ x: 100, y: 200, width: 300, height: 400 }) logSpy = vi.spyOn(log, 'warn') vi.spyOn(browser, 'pause').mockResolvedValue(0) vi.spyOn(browser, '$$') }) it('should log a warning if swipe is called with a "scrollableElement" and "from"', async () => { await browser.swipe({ scrollableElement: elem, from: { x: 10, y: 20 }, }) expect(logSpy).toHaveBeenCalledWith('`scrollableElement` is provided, so `from` and `to` will be ignored.') }) it('should log a warning if swipe is called with a "scrollableElement" and "to"', async () => { await browser.swipe({ scrollableElement: elem, to: { x: 10, y: 20 }, }) expect(logSpy).toHaveBeenCalledWith('`scrollableElement` is provided, so `from` and `to` will be ignored.') }) it('should find the default scroll element for Android and can swipe', async () => { (browser.$$ as Mock).mockResolvedValue([{ elementId: 'scrollable-android-element-id', }]) await browser.swipe() const [[selector]] = (browser.$$ as Mock).mock.calls expect(selector).toEqual(defaultAndroidSelector) }) it('does not find the default scroll element for Android and throws an error', async () => { (browser.$$ as Mock).mockResolvedValue([]) await expect(browser.swipe()).rejects.toThrowErrorMatchingSnapshot() }) it('should be able to swipe based on provided coordinates', async () => { await browser.swipe({ from: { x: 10, y: 20 }, to: { x: 30, y: 40 }, }) expect(rectSpy).not.toBeCalled() // @ts-expect-error mock implementation expect(vi.mocked(fetch).mock.calls[2][0].pathname) .toBe('/session/foobar-123/actions') expect(JSON.parse(vi.mocked(fetch).mock.calls[2][1]?.body as any).actions[0]) .toMatchSnapshot() }) it('should log a warning if swipe percentage is not a number', async () => { await browser.swipe({ direction: 'down', // @ts-expect-error invalid param percent: 'bar', scrollableElement: elem, }) expect(logSpy).toHaveBeenCalledWith('The percentage to swipe should be a number.') }) it('should log a warning if swipe percentage is lower than 0', async () => { browser.swipe({ direction: 'down', percent: -1, scrollableElement: elem, }) expect(logSpy).toHaveBeenCalledWith('The percentage to swipe should be a number between 0 and 1.') }) it('should log a warning if swipe percentage is bigger than 1', async () => { await browser.swipe({ direction: 'down', percent: 98, scrollableElement: elem, }) expect(logSpy).toHaveBeenCalledWith('The percentage to swipe should be a number between 0 and 1.') }) const percentages = [0.25, 0.45, 0.5, 0.75, 0.8] Object.values(MobileScrollDirection).forEach((direction, index) => { const percent = percentages[index % percentages.length] it(`should call a swipe ${direction} command with ${percent * 100}% for a scrollable element`, async () => { await browser.swipe({ direction, scrollableElement: elem, percent, }) expect(rectSpy).toHaveBeenCalledOnce() // @ts-expect-error mock implementation expect(vi.mocked(fetch).mock.calls[2][0].pathname) .toBe('/session/foobar-123/actions') expect(JSON.parse(vi.mocked(fetch).mock.calls[2][1]?.body as any).actions[0]) .toMatchSnapshot() }) }) it('should call a swipe down command with duration 5000 for a scrollable element', async () => { await browser.swipe({ duration: 5000, scrollableElement: elem, }) expect(rectSpy).toHaveBeenCalledOnce() // @ts-expect-error mock implementation expect(vi.mocked(fetch).mock.calls[2][0].pathname) .toBe('/session/foobar-123/actions') expect(JSON.parse(vi.mocked(fetch).mock.calls[2][1]?.body as any).actions[0]) .toMatchSnapshot() }) it('should throw an error a swipe command for direction foo', async () => { await expect( browser.swipe({ direction: 'foo' as MobileScrollDirection, scrollableElement: elem }) ).rejects.toThrowErrorMatchingSnapshot() }) it('should throw an error if the swipe command is called for a browser session', async () => { browser = await remote({ baseUrl: 'http://foobar.com', capabilities: { browserName: 'foobar', } as any }) await expect(browser.swipe()).rejects.toThrowErrorMatchingSnapshot() }) afterEach(() => { vi.mocked(fetch).mockClear() rectSpy.mockRestore() logSpy.mockRestore() }) })