Skip to content

Commit db6aaa0

Browse files
authored
Merge branch 'master' into rename-to-main
2 parents c55cb48 + 6538bc8 commit db6aaa0

File tree

7 files changed

+172
-84
lines changed

7 files changed

+172
-84
lines changed

.eslintrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44
"./node_modules/kcd-scripts/eslint.js",
55
"plugin:import/typescript"
66
],
7+
"parserOptions": {
8+
"ecmaVersion": 2018,
9+
"sourceType": "module",
10+
"project": "*/**/tsconfig.json"
11+
},
712
"plugins": ["@typescript-eslint"],
13+
"ignorePatterns": "wdio.conf.js",
814
"rules": {
915
"babel/new-cap": "off",
1016
"func-names": "off",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"@wdio/spec-reporter": "^7.3.1",
4646
"@wdio/sync": "^7.3.1",
4747
"eslint": "^7.6.0",
48-
"kcd-scripts": "^5.0.0",
48+
"kcd-scripts": "^11.1.0",
4949
"npm-run-all": "^4.1.5",
5050
"semantic-release": "^17.0.2",
5151
"ts-node": "^9.1.1",

src/index.ts

Lines changed: 76 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
1-
/* eslint-disable babel/no-invalid-this, no-eval */
1+
/* eslint-disable no-eval, @babel/new-cap */
22

33
import path from 'path'
44
import fs from 'fs'
5-
import {queries as baseQueries} from '@testing-library/dom'
5+
import {
6+
Matcher,
7+
MatcherOptions,
8+
queries as baseQueries,
9+
waitForOptions as WaitForOptions,
10+
} from '@testing-library/dom'
611
import 'simmerjs'
712

13+
import {BrowserBase, ElementBase} from './wdio-types'
814
import {
9-
BrowserBase,
15+
QueryArg,
1016
Config,
11-
ElementBase,
1217
QueryName,
1318
WebdriverIOQueries,
19+
ObjectQueryArg,
20+
SerializedObject,
21+
SerializedArg,
1422
} from './types'
1523

1624
declare global {
@@ -45,9 +53,9 @@ async function injectDOMTestingLibrary(container: ElementBase) {
4553
})
4654

4755
if (shouldInject.domTestingLibrary) {
48-
await container.execute(function (library) {
56+
await container.execute(function (library: string) {
4957
// add DOM Testing Library to page as a script tag to support Firefox
50-
if (navigator.userAgent.indexOf('Firefox') !== -1) {
58+
if (navigator.userAgent.includes('Firefox')) {
5159
const script = document.createElement('script')
5260
script.innerHTML = library
5361
return document.head.append(script)
@@ -62,74 +70,83 @@ async function injectDOMTestingLibrary(container: ElementBase) {
6270
await container.execute(SIMMERJS)
6371
}
6472

65-
if (_config) {
66-
await container.execute(function (config: Config) {
67-
window.TestingLibraryDom.configure(config)
68-
}, _config)
69-
}
73+
await container.execute(function (config: Config) {
74+
window.TestingLibraryDom.configure(config)
75+
}, _config)
7076
}
7177

72-
function serializeObject(object: Object): Object {
78+
function serializeObject(object: ObjectQueryArg): SerializedObject {
7379
return Object.entries(object)
74-
.map(([key, value]) => [key, serializeArg(value)])
75-
.reduce((acc, [key, value]) => ({...acc, [key]: value}), {})
80+
.map<[string, SerializedArg]>(([key, value]: [string, QueryArg]) => [
81+
key,
82+
serializeArg(value),
83+
])
84+
.reduce((acc, [key, value]) => ({...acc, [key]: value}), {
85+
serialized: 'object',
86+
})
7687
}
7788

78-
function serializeArg(arg: any) {
89+
function serializeArg(arg: QueryArg): SerializedArg {
7990
if (arg instanceof RegExp) {
80-
return {RegExp: arg.toString()}
91+
return {serialized: 'RegExp', RegExp: arg.toString()}
8192
}
8293
if (typeof arg === 'undefined') {
83-
return {Undefined: true}
94+
return {serialized: 'Undefined', Undefined: true}
8495
}
8596
if (arg && typeof arg === 'object') {
8697
return serializeObject(arg)
8798
}
8899
return arg
89100
}
90101

102+
type SerializedQueryResult =
103+
| {selector: string | false; element: HTMLElement}[]
104+
| string
105+
| {selector: string | false; element: HTMLElement}
106+
| null
107+
91108
function executeQuery(
92109
query: QueryName,
93110
container: HTMLElement,
94-
...args: any[]
111+
...args: SerializedArg[]
95112
) {
96-
const done = args.pop() as (result: any) => void
113+
const done = args.pop() as unknown as (result: SerializedQueryResult) => void
97114

98-
function deserializeObject(object: object): object {
115+
function deserializeObject(object: SerializedObject) {
99116
return Object.entries(object)
100-
.map(([key, value]) => [key, deserializeArg(value)])
117+
.map<[string, QueryArg]>(([key, value]) => [key, deserializeArg(value)])
101118
.reduce((acc, [key, value]) => ({...acc, [key]: value}), {})
102119
}
103120

104-
function deserializeArg(arg: any) {
105-
if (arg && arg.RegExp) {
121+
function deserializeArg(arg: SerializedArg): QueryArg {
122+
if (typeof arg === 'object' && arg.serialized === 'RegExp') {
106123
return eval(arg.RegExp)
107124
}
108-
if (arg && arg.Undefined) {
125+
if (typeof arg === 'object' && arg.serialized === 'Undefined') {
109126
return undefined
110127
}
111-
if (arg && typeof arg === 'object') {
128+
if (typeof arg === 'object') {
112129
return deserializeObject(arg)
113130
}
114131
return arg
115132
}
116133

117134
const [matcher, options, waitForOptions] = args.map(deserializeArg)
118135

119-
;(async () => {
120-
let result: undefined | null | HTMLElement | HTMLElement[]
136+
void (async () => {
137+
let result: ReturnType<typeof window.TestingLibraryDom[typeof query]> = null
121138
try {
122139
// Override RegExp to fix 'matcher instanceof RegExp' check on Firefox
123140
window.RegExp = RegExp
124141

125142
result = await window.TestingLibraryDom[query](
126143
container,
127-
matcher,
128-
options,
129-
waitForOptions,
144+
matcher as Matcher,
145+
options as MatcherOptions,
146+
waitForOptions as WaitForOptions,
130147
)
131-
} catch (e) {
132-
done(e.message)
148+
} catch (e: unknown) {
149+
return done((e as Error).message)
133150
}
134151

135152
if (!result) {
@@ -160,10 +177,10 @@ Element. There are valid WebElement JSONs that exclude the key but can be turned
160177
into Elements, such as { ELEMENT: elementId }; this can happen in setups that
161178
aren't generated by @wdio/cli.
162179
*/
163-
function createElement(
180+
async function createElement(
164181
container: ElementBase,
165-
result: {selector: string | false; element: any},
166-
) {
182+
result: {selector: string | false; element: object},
183+
): Promise<WebdriverIO.Element> {
167184
// use selector if possible so that element can be refetched
168185
if (result.selector) {
169186
return container.$(result.selector)
@@ -176,11 +193,12 @@ function createElement(
176193
})
177194
}
178195

179-
function createQuery(container: ElementBase, queryName: string) {
180-
return async (...args: any[]) => {
196+
function createQuery(container: ElementBase, queryName: QueryName) {
197+
return async (...args: QueryArg[]) => {
181198
await injectDOMTestingLibrary(container)
182199

183-
const result = await container.executeAsync(
200+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
201+
const result: SerializedQueryResult = await container.executeAsync(
184202
executeQuery,
185203
queryName,
186204
container,
@@ -204,7 +222,7 @@ function createQuery(container: ElementBase, queryName: string) {
204222
}
205223

206224
function within(element: ElementBase) {
207-
return Object.keys(baseQueries).reduce(
225+
return (Object.keys(baseQueries) as QueryName[]).reduce(
208226
(queries, queryName) => ({
209227
...queries,
210228
[queryName]: createQuery(element, queryName),
@@ -213,22 +231,29 @@ function within(element: ElementBase) {
213231
) as WebdriverIOQueries
214232
}
215233

216-
function setupBrowser(browser: BrowserBase) {
217-
const queries: {[key: string]: any} = {}
234+
/*
235+
eslint-disable
236+
@typescript-eslint/no-explicit-any,
237+
@typescript-eslint/no-unsafe-argument
238+
*/
239+
function setupBrowser(browser: BrowserBase): WebdriverIOQueries {
240+
const queries: {[key: string]: WebdriverIOQueries[QueryName]} = {}
218241

219242
Object.keys(baseQueries).forEach((key) => {
220-
const queryName = key as keyof typeof baseQueries
243+
const queryName = key as QueryName
221244

222-
const query = async (...args: any[]) => {
245+
const query = async (
246+
...args: Parameters<WebdriverIOQueries[QueryName]>
247+
) => {
223248
const body = await browser.$('body')
224-
return within(body)[queryName](...args)
249+
return within(body)[queryName](...(args as any[]))
225250
}
226251

227252
// add query to response queries
228-
queries[queryName] = query
253+
queries[queryName] = query as WebdriverIOQueries[QueryName]
229254

230255
// add query to BrowserObject
231-
browser.addCommand(queryName, query)
256+
browser.addCommand(queryName, query as WebdriverIOQueries[QueryName])
232257

233258
// add query to Elements
234259
browser.addCommand(
@@ -242,6 +267,11 @@ function setupBrowser(browser: BrowserBase) {
242267

243268
return queries as WebdriverIOQueries
244269
}
270+
/*
271+
eslint-enable
272+
@typescript-eslint/no-explicit-any,
273+
@typescript-eslint/no-unsafe-argument
274+
*/
245275

246276
function configure(config: Partial<Config>) {
247277
_config = config

src/types.ts

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,17 @@ import {
22
Config as BaseConfig,
33
BoundFunction as BoundFunctionBase,
44
queries,
5+
waitForOptions,
6+
SelectorMatcherOptions,
7+
MatcherOptions,
58
} from '@testing-library/dom'
69

7-
declare global {
8-
namespace WebdriverIO {
9-
interface Element extends ElementBase {}
10-
}
11-
}
12-
13-
export type ElementBase = {
14-
$(
15-
selector: string | object,
16-
): WebdriverIO.Element | Promise<WebdriverIO.Element>
17-
18-
execute<T>(
19-
script: string | ((...args: any[]) => T),
20-
...args: any[]
21-
): Promise<T>
22-
23-
execute<T>(script: string | ((...args: any[]) => T), ...args: any[]): T
24-
25-
executeAsync(script: string | ((...args: any[]) => void), ...args: any[]): any
26-
}
27-
28-
export type BrowserBase = {
29-
$(
30-
selector: string | object,
31-
): WebdriverIO.Element | Promise<WebdriverIO.Element>
32-
33-
addCommand<T extends boolean>(
34-
queryName: string,
35-
commandFn: (
36-
this: T extends true ? ElementBase : BrowserBase,
37-
...args: any[]
38-
) => void,
39-
isElementCommand?: T,
40-
): any
41-
}
42-
4310
export type Config = Pick<
4411
BaseConfig,
45-
| 'testIdAttribute'
4612
| 'asyncUtilTimeout'
4713
| 'computedStyleSupportsPseudoElements'
4814
| 'defaultHidden'
15+
| 'testIdAttribute'
4916
| 'throwSuggestions'
5017
>
5118

@@ -83,3 +50,30 @@ export type WebdriverIOQueriesSync = WebdriverIOBoundFunctionsSync<
8350
>
8451

8552
export type QueryName = keyof typeof queries
53+
54+
export type ObjectQueryArg =
55+
| MatcherOptions
56+
| queries.ByRoleOptions
57+
| SelectorMatcherOptions
58+
| waitForOptions
59+
60+
export type QueryArg =
61+
| ObjectQueryArg
62+
| RegExp
63+
| number
64+
| string
65+
| undefined
66+
67+
export type SerializedObject = {
68+
serialized: 'object'
69+
[key: string]: SerializedArg
70+
}
71+
export type SerializedRegExp = {serialized: 'RegExp'; RegExp: string}
72+
export type SerializedUndefined = {serialized: 'Undefined'; Undefined: true}
73+
74+
export type SerializedArg =
75+
| SerializedObject
76+
| SerializedRegExp
77+
| SerializedUndefined
78+
| number
79+
| string

src/wdio-types.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
Types related to WebdriverIO are intentionally loose in order to support wdio
3+
version 6 and 7 at the same time. Disable eslint rules that prevent that.
4+
*/
5+
6+
/*
7+
eslint-disable @typescript-eslint/no-explicit-any,
8+
@typescript-eslint/no-namespace,
9+
@typescript-eslint/no-empty-interface
10+
*/
11+
12+
declare global {
13+
namespace WebdriverIO {
14+
interface Element extends ElementBase {}
15+
}
16+
}
17+
18+
export type ElementBase = {
19+
$(
20+
selector: object | string,
21+
): Promise<WebdriverIO.Element> | WebdriverIO.Element
22+
23+
execute<T>(
24+
script: string | ((...args: any[]) => T),
25+
...args: any[]
26+
): Promise<T>
27+
28+
execute<T>(script: string | ((...args: any[]) => T), ...args: any[]): T
29+
30+
executeAsync(script: string | ((...args: any[]) => void), ...args: any[]): any
31+
}
32+
33+
export type BrowserBase = {
34+
$(
35+
selector: object | string,
36+
): Promise<WebdriverIO.Element> | WebdriverIO.Element
37+
38+
addCommand<T extends boolean>(
39+
queryName: string,
40+
commandFn: (
41+
this: T extends true ? ElementBase : BrowserBase,
42+
...args: any[]
43+
) => void,
44+
isElementCommand?: T,
45+
): any
46+
}

0 commit comments

Comments
 (0)