Skip to content

Commit 22cd178

Browse files
authored
chore: Better leverage of vitest with typings (#1989)
* Better typed vitest mock - Leverage better vitest - Try to have better typed mock since Wdio object can be complexe and it will help with future changes - Remove weird _value mock and replace by vi mock * Fix remaining tests * Working refactor of test framework to better leverage vitest + types * Remove unnecessary fixture code * remove unused mocked code * now required config, not sure why though * code review and remove more any in test code * fix small missed * Fix pipeline
1 parent 7b18496 commit 22cd178

28 files changed

Lines changed: 832 additions & 1447 deletions

__mocks__/@wdio/globals.ts

Lines changed: 14 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,137 +1,21 @@
1-
import { vi } from 'vitest'
1+
/**
2+
* Global mocks on root only as vitest support
3+
* Re-exporting from test folder to benefit from typed mocks
4+
*/
5+
import { browser } from '../../test/__mocks__/@wdio/globals'
26

3-
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms))
4-
5-
function beFn() {
6-
return this._value ? this._value() : true
7-
}
8-
9-
function strFn() {
10-
return this._value ? this._value() : ' Valid Text '
11-
}
12-
13-
function getPropertyFn(propName) {
14-
return this._value ? this._value(propName) : undefined
15-
}
16-
17-
function getTextFn() {
18-
return this._text ? this._text() : undefined
7+
export function $(selector: string) {
8+
return browser.$(selector)
199
}
2010

21-
function getHTMLFn({ includeSelectorTag }) {
22-
return this._html ? this._html(includeSelectorTag) : undefined
11+
export function $$(selector: string) {
12+
return browser.$$(selector)
2313
}
2414

25-
function getComputedLabelFn() {
26-
return this._computed_label ? this._computed_label() : undefined
27-
}
28-
29-
function getComputedRoleFn() {
30-
return this._computed_role ? this._computed_role() : undefined
31-
}
32-
33-
function getSizeFn(property?: 'height' | 'width') {
34-
return this._size ? this._size(property) : undefined
35-
}
36-
37-
const elementMethods = {
38-
isDisplayed: beFn,
39-
isDisplayedInViewport: beFn,
40-
isExisting: beFn,
41-
isSelected: beFn,
42-
isClickable: beFn,
43-
isFocused: beFn,
44-
isEnabled: beFn,
45-
getProperty: getPropertyFn,
46-
getText: getTextFn,
47-
getHTML: getHTMLFn,
48-
getComputedLabel: getComputedLabelFn,
49-
getComputedRole: getComputedRoleFn,
50-
getSize: getSizeFn,
51-
}
52-
53-
const element = {
54-
$,
55-
$$,
56-
...elementMethods
57-
}
58-
59-
export function $(selector) {
60-
const el: any = {
61-
...element,
62-
selector
63-
}
64-
for (const [prop, method] of Object.entries(elementMethods)) {
65-
el[prop] = vi.fn(method)
66-
}
67-
el.getElement = async () => el
68-
return el
69-
}
70-
71-
export function $$(selector) {
72-
const length = this?._length || 2
73-
const els: any = Array(length).map((_, index) => {
74-
const el = {
75-
...element,
76-
selector,
77-
index
78-
}
79-
for (const [prop, method] of Object.entries(elementMethods)) {
80-
el[prop] = vi.fn(method)
81-
}
82-
return el
83-
})
84-
// Required to refetch
85-
const parent: any = element
86-
parent._length = length
87-
els.parent = parent
88-
89-
els.foundWith = '$$'
90-
// Required to check length prop
91-
els.props = []
92-
els.props.length = length
93-
els.selector = selector
94-
els.getElements = async () => els
95-
return els
96-
}
97-
98-
async function waitUntil(condition, { timeout, interval }) {
99-
if (!Number.isInteger(timeout) || timeout < 1) {
100-
throw new Error('wrong args passed to waitUntil fixture')
101-
}
102-
let attemptsLeft = timeout / interval
103-
while (attemptsLeft > 0) {
104-
try {
105-
const result = await condition()
106-
if (result) {
107-
return true
108-
}
109-
} catch {
110-
// don't do anything
111-
}
112-
attemptsLeft--
113-
await sleep(interval)
114-
}
115-
throw new Error('waitUntil: timeout after ' + timeout)
116-
}
117-
118-
async function execute(fn) {
119-
if (typeof fn === 'function' && fn.toString().includes('navigator.clipboard.readText')) {
120-
return 'some clipboard text'
121-
}
122-
}
123-
124-
async function setPermissions() {
125-
return true
126-
}
15+
export { browser }
12716

128-
export const browser = {
129-
$,
130-
$$,
131-
execute,
132-
waitUntil,
133-
setPermissions,
134-
getUrl: strFn,
135-
getTitle: strFn,
136-
call(fn) { return fn() },
17+
export default {
18+
browser,
19+
$: browser.$,
20+
$$: browser.$$,
13721
}

test/__fixtures__/beforeAll.js

Lines changed: 0 additions & 12 deletions
This file was deleted.

test/__fixtures__/index.js

Lines changed: 0 additions & 2 deletions
This file was deleted.

test/__fixtures__/wdio.js

Whitespace-only changes.

test/__mocks__/@wdio/globals.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* The real globals is mocked under the root folder.
3+
* This file exist for better typed mock implementation, so that we can follow wdio/globals API updates more easily.
4+
*/
5+
import { vi } from 'vitest'
6+
import type { ChainablePromiseArray, ChainablePromiseElement } from 'webdriverio'
7+
8+
import type { RectReturn } from '@wdio/protocols'
9+
export type Size = Pick<RectReturn, 'width' | 'height'>
10+
11+
const getElementMethods = () => ({
12+
isDisplayed: vi.spyOn({ isDisplayed: async () => true }, 'isDisplayed'),
13+
isExisting: vi.spyOn({ isExisting: async () => true }, 'isExisting'),
14+
isSelected: vi.spyOn({ isSelected: async () => true }, 'isSelected'),
15+
isClickable: vi.spyOn({ isClickable: async () => true }, 'isClickable'),
16+
isFocused: vi.spyOn({ isFocused: async () => true }, 'isFocused'),
17+
isEnabled: vi.spyOn({ isEnabled: async () => true }, 'isEnabled'),
18+
getProperty: vi.spyOn({ getProperty: async (_prop: string) => undefined }, 'getProperty'),
19+
getText: vi.spyOn({ getText: async () => ' Valid Text ' }, 'getText'),
20+
getHTML: vi.spyOn({ getHTML: async () => { return '<Html/>' } }, 'getHTML'),
21+
getComputedLabel: vi.spyOn({ getComputedLabel: async () => 'Computed Label' }, 'getComputedLabel'),
22+
getComputedRole: vi.spyOn({ getComputedRole: async () => 'Computed Role' }, 'getComputedRole'),
23+
getSize: vi.spyOn({ getSize: async (prop?: 'width' | 'height') => {
24+
if (prop === 'width') { return 100 }
25+
if (prop === 'height') { return 50 }
26+
return { width: 100, height: 50 } satisfies Size
27+
} }, 'getSize') as unknown as WebdriverIO.Element['getSize'],
28+
} satisfies Partial<WebdriverIO.Element>)
29+
30+
function $(_selector: string) {
31+
const element = {
32+
selector: _selector,
33+
...getElementMethods(),
34+
$,
35+
$$
36+
} satisfies Partial<WebdriverIO.Element> as unknown as WebdriverIO.Element
37+
element.getElement = async () => Promise.resolve(element)
38+
return element as unknown as ChainablePromiseElement
39+
}
40+
41+
function $$(selector: string) {
42+
const length = (this)?._length || 2
43+
const elements = Array(length).fill(null).map((_, index) => {
44+
const element = {
45+
selector,
46+
index,
47+
...getElementMethods(),
48+
$,
49+
$$
50+
} satisfies Partial<WebdriverIO.Element> as unknown as WebdriverIO.Element
51+
element.getElement = async () => Promise.resolve(element)
52+
return element
53+
}) satisfies WebdriverIO.Element[] as unknown as WebdriverIO.ElementArray
54+
55+
elements.foundWith = '$$'
56+
elements.props = []
57+
elements.props.length = length
58+
elements.selector = selector
59+
elements.getElements = async () => elements
60+
elements.length = length
61+
return elements as unknown as ChainablePromiseArray
62+
}
63+
64+
export const browser = {
65+
$,
66+
$$,
67+
execute: vi.fn(),
68+
setPermissions: vi.spyOn({ setPermissions: async () => {} }, 'setPermissions'),
69+
getUrl: vi.spyOn({ getUrl: async () => ' Valid text ' }, 'getUrl'),
70+
getTitle: vi.spyOn({ getTitle: async () => 'Example Domain' }, 'getTitle'),
71+
call(fn: Function) { return fn() },
72+
} satisfies Partial<WebdriverIO.Browser> as unknown as WebdriverIO.Browser
73+

test/matchers.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ test('matchers', () => {
5959
})
6060

6161
test('allows to add matcher', () => {
62-
const matcher: any = vi.fn((actual: any, expected: any) => ({ pass: actual === expected }))
62+
const matcher: any = vi.fn((actual, expected) => ({ pass: actual === expected }))
6363
expectLib.extend({ toBeCustom: matcher })
6464

6565
// @ts-expect-error not in types

0 commit comments

Comments
 (0)