Skip to content

Commit 7b18496

Browse files
authored
fix: toBeDisplayed, align options and fix typing (#1981)
* fix: use only 1 options object and consider command options + fix typing - Merge toBeDisplayed options with command options as done for other kind of option (see NumberOptions or StringOptions) - Ensure default options are always applied when passing only 1 options - Fix typing not considering the `ToBeDisplayedOptions` * fix unwanted isNot in UT
1 parent 9f14972 commit 7b18496

File tree

5 files changed

+150
-40
lines changed

5 files changed

+150
-40
lines changed

__mocks__/@wdio/globals.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { vi } from 'vitest'
2+
13
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms))
24

35
function beFn() {
@@ -32,9 +34,7 @@ function getSizeFn(property?: 'height' | 'width') {
3234
return this._size ? this._size(property) : undefined
3335
}
3436

35-
const element = {
36-
$,
37-
$$,
37+
const elementMethods = {
3838
isDisplayed: beFn,
3939
isDisplayedInViewport: beFn,
4040
isExisting: beFn,
@@ -50,23 +50,36 @@ const element = {
5050
getSize: getSizeFn,
5151
}
5252

53+
const element = {
54+
$,
55+
$$,
56+
...elementMethods
57+
}
58+
5359
export function $(selector) {
5460
const el: any = {
5561
...element,
5662
selector
5763
}
64+
for (const [prop, method] of Object.entries(elementMethods)) {
65+
el[prop] = vi.fn(method)
66+
}
5867
el.getElement = async () => el
5968
return el
6069
}
6170

6271
export function $$(selector) {
6372
const length = this?._length || 2
6473
const els: any = Array(length).map((_, index) => {
65-
return {
74+
const el = {
6675
...element,
6776
selector,
6877
index
6978
}
79+
for (const [prop, method] of Object.entries(elementMethods)) {
80+
el[prop] = vi.fn(method)
81+
}
82+
return el
7083
})
7184
// Required to refetch
7285
const parent: any = element

src/matchers/element/toBeDisplayed.ts

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,17 @@ import { executeCommandBe } from '../../utils.js'
22
import { DEFAULT_OPTIONS } from '../../constants.js'
33
import type { WdioElementMaybePromise } from '../../types.js'
44

5-
interface ToBeDisplayedOptions {
6-
/**
7-
* `true` to check if the element is within the viewport. false by default.
8-
*/
9-
withinViewport?: boolean
10-
/**
11-
* `true` to check if the element content-visibility property has (or inherits) the value auto,
12-
* and it is currently skipping its rendering. `true` by default.
13-
* @default true
14-
*/
15-
contentVisibilityAuto?: boolean
16-
/**
17-
* `true` to check if the element opacity property has (or inherits) a value of 0. `true` by default.
18-
* @default true
19-
*/
20-
opacityProperty?: boolean
21-
/**
22-
* `true` to check if the element is invisible due to the value of its visibility property. `true` by default.
23-
* @default true
24-
*/
25-
visibilityProperty?: boolean
5+
const DEFAULT_OPTIONS_DISPLAYED: ExpectWebdriverIO.ToBeDisplayedOptions = {
6+
...DEFAULT_OPTIONS,
7+
withinViewport: false,
8+
contentVisibilityAuto: true,
9+
opacityProperty: true,
10+
visibilityProperty: true
2611
}
2712

2813
export async function toBeDisplayed(
2914
received: WdioElementMaybePromise,
30-
{ withinViewport = false, contentVisibilityAuto = true, opacityProperty = true, visibilityProperty = true }: ToBeDisplayedOptions = {},
31-
options: ExpectWebdriverIO.CommandOptions = DEFAULT_OPTIONS
15+
options: ExpectWebdriverIO.ToBeDisplayedOptions = DEFAULT_OPTIONS_DISPLAYED,
3216
) {
3317
this.expectation = this.expectation || 'displayed'
3418

@@ -37,7 +21,20 @@ export async function toBeDisplayed(
3721
options,
3822
})
3923

40-
const result = await executeCommandBe.call(this, received, el => el?.isDisplayed({ withinViewport, contentVisibilityAuto, opacityProperty, visibilityProperty }), options)
24+
const {
25+
withinViewport,
26+
contentVisibilityAuto,
27+
opacityProperty,
28+
visibilityProperty,
29+
...commandOptions
30+
} = { ...DEFAULT_OPTIONS_DISPLAYED, ...options }
31+
32+
const result = await executeCommandBe.call(this, received, el => el?.isDisplayed({
33+
withinViewport,
34+
contentVisibilityAuto,
35+
opacityProperty,
36+
visibilityProperty
37+
}), commandOptions)
4138

4239
await options.afterAssertion?.({
4340
matcherName: 'toBeDisplayed',

test/matchers/element/toBeDisplayed.test.ts

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,18 @@ import { $ } from '@wdio/globals'
33

44
import { getExpectMessage, getReceived } from '../../__fixtures__/utils.js'
55
import { toBeDisplayed } from '../../../src/matchers/element/toBeDisplayed.js'
6+
import { executeCommandBe } from '../../../src/utils.js'
7+
import { DEFAULT_OPTIONS } from '../../../src/constants.js'
68

79
vi.mock('@wdio/globals')
10+
vi.mock('../../../src/utils.js', async (importOriginal) => {
11+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
12+
const actual = await importOriginal<typeof import('../../../src/utils.js')>()
13+
return {
14+
...actual,
15+
executeCommandBe: vi.fn(actual.executeCommandBe)
16+
}
17+
})
818

919
describe('toBeDisplayed', () => {
1020
/**
@@ -24,7 +34,21 @@ describe('toBeDisplayed', () => {
2434

2535
const beforeAssertion = vi.fn()
2636
const afterAssertion = vi.fn()
27-
const result = await toBeDisplayed.call({}, el, {}, { beforeAssertion, afterAssertion })
37+
38+
const result = await toBeDisplayed.call({}, el, { beforeAssertion, afterAssertion })
39+
40+
expect(el.isDisplayed).toHaveBeenCalledWith(
41+
{
42+
withinViewport: false,
43+
contentVisibilityAuto: true,
44+
opacityProperty: true,
45+
visibilityProperty: true
46+
}
47+
)
48+
expect(executeCommandBe).toHaveBeenCalledWith(el, expect.anything(), expect.objectContaining({
49+
wait: DEFAULT_OPTIONS.wait,
50+
interval: DEFAULT_OPTIONS.interval
51+
}))
2852
expect(result.pass).toBe(true)
2953
expect(el._attempts).toBe(0)
3054
expect(beforeAssertion).toBeCalledWith({
@@ -38,6 +62,28 @@ describe('toBeDisplayed', () => {
3862
})
3963
})
4064

65+
test('success with ToBeDisplayed and command options', async () => {
66+
const el: any = await $('sel')
67+
el._value = function (): boolean {
68+
return true
69+
}
70+
const result = await toBeDisplayed.call({}, el, { wait: 1, withinViewport: true })
71+
72+
expect(el.isDisplayed).toHaveBeenCalledWith(
73+
{
74+
withinViewport: true,
75+
contentVisibilityAuto: true,
76+
opacityProperty: true,
77+
visibilityProperty: true
78+
}
79+
)
80+
expect(executeCommandBe).toHaveBeenCalledWith(el, expect.anything(), expect.objectContaining({
81+
wait: 1,
82+
interval: DEFAULT_OPTIONS.interval
83+
}))
84+
expect(result.pass).toBe(true)
85+
})
86+
4187
test('wait but failure', async () => {
4288
const el: any = await $('sel')
4389
el._value = function (): boolean {
@@ -69,7 +115,8 @@ describe('toBeDisplayed', () => {
69115
return false
70116
}
71117

72-
const result = await toBeDisplayed.call({}, el, {}, { wait: 0 })
118+
const result = await toBeDisplayed.call({}, el, { wait: 0 })
119+
73120
expect(result.pass).toBe(false)
74121
expect(el._attempts).toBe(1)
75122
})
@@ -82,7 +129,21 @@ describe('toBeDisplayed', () => {
82129
return true
83130
}
84131

85-
const result = await toBeDisplayed.call({}, el, {}, { wait: 0 })
132+
const result = await toBeDisplayed.call({}, el, { wait: 0 })
133+
134+
expect(el.isDisplayed).toHaveBeenCalledWith(
135+
{
136+
withinViewport: false,
137+
contentVisibilityAuto: true,
138+
opacityProperty: true,
139+
visibilityProperty: true
140+
}
141+
)
142+
expect(executeCommandBe).toHaveBeenCalledWith(el, expect.anything(), expect.objectContaining({
143+
wait: 0,
144+
interval: DEFAULT_OPTIONS.interval
145+
}))
146+
86147
expect(result.pass).toBe(true)
87148
expect(el._attempts).toBe(1)
88149
})
@@ -92,7 +153,7 @@ describe('toBeDisplayed', () => {
92153
el._value = function (): boolean {
93154
return true
94155
}
95-
const result = await toBeDisplayed.call({ isNot: true }, el, {}, { wait: 0 })
156+
const result = await toBeDisplayed.call({ isNot: true }, el, { wait: 0 })
96157
const received = getReceived(result.message())
97158

98159
expect(received).not.toContain('not')
@@ -104,7 +165,7 @@ describe('toBeDisplayed', () => {
104165
el._value = function (): boolean {
105166
return false
106167
}
107-
const result = await toBeDisplayed.call({ isNot: true }, el, {}, { wait: 0 })
168+
const result = await toBeDisplayed.call({ isNot: true }, el, { wait: 0 })
108169
const received = getReceived(result.message())
109170

110171
expect(received).toContain('not')
@@ -116,7 +177,7 @@ describe('toBeDisplayed', () => {
116177
el._value = function (): boolean {
117178
return true
118179
}
119-
const result = await toBeDisplayed.call({ isNot: true }, el, {}, { wait: 1 })
180+
const result = await toBeDisplayed.call({ isNot: true }, el, { wait: 1 })
120181
const received = getReceived(result.message())
121182

122183
expect(received).not.toContain('not')
@@ -128,9 +189,21 @@ describe('toBeDisplayed', () => {
128189
el._value = function (): boolean {
129190
return false
130191
}
131-
const result = await toBeDisplayed.call({ isNot: true }, el, {}, { wait: 1 })
192+
const result = await toBeDisplayed.call({ isNot: true }, el, { wait: 1 })
132193
const received = getReceived(result.message())
133194

195+
expect(el.isDisplayed).toHaveBeenCalledWith(
196+
{
197+
withinViewport: false,
198+
contentVisibilityAuto: true,
199+
opacityProperty: true,
200+
visibilityProperty: true
201+
}
202+
)
203+
expect(executeCommandBe).toHaveBeenCalledWith(el, expect.anything(), expect.objectContaining({
204+
wait: 1,
205+
interval: DEFAULT_OPTIONS.interval
206+
}))
134207
expect(received).toContain('not')
135208
expect(result.pass).toBe(false)
136209
})

types/expect-webdriverio.d.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ interface WdioElementOrArrayMatchers<_R, ActualT = unknown> {
122122
/**
123123
* `WebdriverIO.Element` -> `isDisplayed`
124124
*/
125-
toBeDisplayed: FnWhenElementOrArrayLike<ActualT, (options?: ExpectWebdriverIO.CommandOptions) => Promise<void>>
125+
toBeDisplayed: FnWhenElementOrArrayLike<ActualT, (options?: ExpectWebdriverIO.ToBeDisplayedOptions) => Promise<void>>
126126

127127
/**
128128
* `WebdriverIO.Element` -> `isExisting`
@@ -702,6 +702,32 @@ declare namespace ExpectWebdriverIO {
702702
gte?: number
703703
}
704704

705+
interface ToBeDisplayedOptions extends CommandOptions {
706+
/**
707+
* `true` to check if the element is within the viewport. false by default.
708+
*/
709+
withinViewport?: boolean
710+
711+
/**
712+
* `true` to check if the element content-visibility property has (or inherits) the value auto,
713+
* and it is currently skipping its rendering. `true` by default.
714+
* @default true
715+
*/
716+
contentVisibilityAuto?: boolean
717+
718+
/**
719+
* `true` to check if the element opacity property has (or inherits) a value of 0. `true` by default.
720+
* @default true
721+
*/
722+
opacityProperty?: boolean
723+
724+
/**
725+
* `true` to check if the element is invisible due to the value of its visibility property. `true` by default.
726+
* @default true
727+
*/
728+
visibilityProperty?: boolean
729+
}
730+
705731
type RequestedWith = {
706732
url?: string | ExpectWebdriverIO.PartialMatcher<string>| ((url: string) => boolean)
707733
method?: string | Array<string>

vitest.config.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export default defineConfig({
1717
exclude: [
1818
'**/build/**',
1919
'**/__fixtures__/**',
20+
'**/__mocks__/**',
2021
'**/*.test.ts',
2122
'lib',
2223
'test-types',
@@ -29,10 +30,10 @@ export default defineConfig({
2930
'types-checks-filter-out-node_modules.js',
3031
],
3132
thresholds: {
32-
lines: 87.3,
33-
functions: 85.8,
34-
statements: 87,
35-
branches: 78.6,
33+
lines: 88.4,
34+
functions: 86.9,
35+
statements: 88.3,
36+
branches: 79.4,
3637
}
3738
}
3839
}

0 commit comments

Comments
 (0)