Skip to content

Commit aec39c2

Browse files
committed
Use supported type instead of unknown for toHaveElementProperty
1 parent c36590a commit aec39c2

5 files changed

Lines changed: 91 additions & 13 deletions

File tree

src/matchers/element/toHaveElementProperty.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
async function condition(
1212
el: WebdriverIO.Element,
1313
property: string,
14-
expected: unknown | RegExp | WdioAsymmetricMatcher<string>,
14+
expected: string | number | null | RegExp | WdioAsymmetricMatcher<string>,
1515
options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS
1616
) {
1717
const { asString = false } = options
@@ -33,13 +33,13 @@ async function condition(
3333
}
3434

3535
// To review the cast to be more type safe but for now let's keep the existing behavior to ensure no regression
36-
return compareText(prop.toString(), expected as string, options)
36+
return compareText(prop.toString(), expected as string | RegExp | WdioAsymmetricMatcher<string>, options)
3737
}
3838

3939
export async function toHaveElementProperty(
4040
received: WdioElementOrArrayMaybePromise,
4141
property: string,
42-
expectedValue: MaybeArray<unknown | RegExp | WdioAsymmetricMatcher<string>>,
42+
expectedValue: MaybeArray<string | number | null | RegExp | WdioAsymmetricMatcher<string>>,
4343
options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS
4444
) {
4545
const { expectation = 'property', verb = 'have', isNot, matcherName = 'toHaveElementProperty' } = this
@@ -58,7 +58,8 @@ export async function toHaveElementProperty(
5858
(elements) => defaultMultipleElementsIterationStrategy(
5959
elements,
6060
expectedValue,
61-
(element, expected) => condition(element, property, expected, options)
61+
(element, expected) => condition(element, property, expected, options),
62+
{ supportArrayForSingleElement: true }
6263
)
6364
)
6465
el = result.elementOrArray

src/util/executeCommand.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,15 @@ export async function executeCommand<T>(
8484
* @param elements The element or array of elements.
8585
* @param expectedValues The expected value or array of expected values.
8686
* @param condition The condition to execute on each element.
87+
* @param options Optional configuration options.
8788
*/
8889
export async function defaultMultipleElementsIterationStrategy<Expected, Value>(
8990
elements: WebdriverIO.Element | WdioElements,
9091
expectedValues: MaybeArray<Expected>,
9192
condition: (awaitedElement: WebdriverIO.Element, expectedValue: Expected) => Promise<
9293
{ result: boolean; value?: Value }
93-
>
94+
>,
95+
{ supportArrayForSingleElement = false } = {}
9496
): Promise<{ result: boolean; value?: Value | string }[]> {
9597
if (isElementArrayLike(elements)) {
9698
if (Array.isArray(expectedValues)) {
@@ -101,8 +103,13 @@ export async function defaultMultipleElementsIterationStrategy<Expected, Value>(
101103
}
102104
return await map(elements, (el: WebdriverIO.Element) => condition(el, expectedValues))
103105

104-
} else if (Array.isArray(expectedValues)) {
105-
return [{ result: false, value: 'Expected value cannot be an array' }]
106+
} else if ( Array.isArray(expectedValues)) {
107+
if (!supportArrayForSingleElement) {
108+
return [{ result: false, value: 'Expected value cannot be an array' }]
109+
}
110+
111+
// Case where a single element's value can be an array compared to an expected value array and not multiple expected values
112+
return [await condition(elements, expectedValues as Expected)]
106113
}
107114
return [await condition(elements, expectedValues)]
108115
}

test/matchers/element/toHaveElementProperty.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ describe(toHaveElementProperty, () => {
4646
expect(result.pass).toBe(true)
4747
})
4848

49-
// TODO With unknonwn as expect should array be be supported ? If so this is a bug!!!
49+
// TODO Need deep equality to support array and object properly
5050
test('success with when property value is an array, bug?', async () => {
5151
vi.mocked(el.getProperty).mockResolvedValue([5])
5252

@@ -57,14 +57,15 @@ describe(toHaveElementProperty, () => {
5757
Expect $(\`sel\`) to have property property
5858
5959
Expected: [5]
60-
Received: "Expected value cannot be an array"`
60+
Received: [5]`
6161
)
6262
})
6363

64-
// TODO With unknonwn as expect should array be be supported ? If so this is a bug!!!
64+
// TODO Need deep equality to support array and object properly
6565
test('success with when property value an object, bug?', async () => {
6666
vi.mocked(el.getProperty).mockResolvedValue( { foo: 'bar' } )
6767

68+
// @ts-expect-error -- object not working for now, to support later
6869
const result = await thisContext.toHaveElementProperty(el, 'property', { foo: 'bar' } )
6970

7071
expect(result.pass).toBe(false)

test/util/executeCommand.test.ts

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { describe, expect, test, vi } from 'vitest'
1+
import { describe, expect, test, vi, beforeEach } from 'vitest'
22
import { $, $$ } from '@wdio/globals'
3-
import { executeCommand } from '../../src/util/executeCommand'
3+
import { executeCommand, defaultMultipleElementsIterationStrategy } from '../../src/util/executeCommand'
44

55
vi.mock('@wdio/globals')
66

@@ -222,3 +222,71 @@ describe(executeCommand, () => {
222222
})
223223
})
224224
})
225+
226+
describe(defaultMultipleElementsIterationStrategy, () => {
227+
228+
describe('given single element', () => {
229+
let singleElement: WebdriverIO.Element
230+
let condition: any
231+
232+
beforeEach(async () => {
233+
singleElement = await $('single-mock-element').getElement()
234+
condition = vi.fn().mockImplementation(async (_el, expected) => ({ result: true, value: expected }))
235+
})
236+
237+
test('should handle single element and single expected value', async () => {
238+
const result = await defaultMultipleElementsIterationStrategy(singleElement, 'val', condition)
239+
240+
expect(result).toEqual([{ result: true, value: 'val' }])
241+
})
242+
243+
test('should fail if single element and expected value is array (default)', async () => {
244+
const result = await defaultMultipleElementsIterationStrategy(singleElement, ['val'], condition)
245+
246+
expect(result).toEqual([{ result: false, value: 'Expected value cannot be an array' }])
247+
})
248+
249+
test('should handle single element and expected value array if supportArrayForSingleElement is true', async () => {
250+
const result = await defaultMultipleElementsIterationStrategy(singleElement, ['val'], condition, { supportArrayForSingleElement: true })
251+
252+
expect(result).toEqual([{ result: true, value: ['val'] }])
253+
expect(condition).toHaveBeenCalledTimes(1)
254+
})
255+
})
256+
257+
describe('given multiple elements', () => {
258+
let elements: WebdriverIO.ElementArray
259+
let condition: () => Promise<{ result: boolean; value: string }>
260+
261+
beforeEach(async () => {
262+
elements = await $$('elements').getElements()
263+
expect(elements.length).toBe(2)
264+
condition = vi.fn()
265+
.mockResolvedValueOnce({ result: true, value: 'val1' })
266+
.mockResolvedValueOnce({ result: true, value: 'val2' })
267+
})
268+
269+
test('should iterate over array of elements and array of expected values', async () => {
270+
const result = await defaultMultipleElementsIterationStrategy(elements, ['val1', 'val2'], condition)
271+
272+
expect(result).toEqual([{ result: true, value: 'val1' }, { result: true, value: 'val2' }])
273+
expect(condition).toHaveBeenCalledTimes(2)
274+
})
275+
276+
test('should fail if array lengths mismatch', async () => {
277+
const result = await defaultMultipleElementsIterationStrategy([elements[0]] as any, ['val1', 'val2'], condition)
278+
279+
expect(result).toEqual([{ result: false, value: 'Received array length 1, expected 2' }])
280+
})
281+
282+
test('should iterate over array of elements and single expected value', async () => {
283+
condition = vi.fn()
284+
.mockResolvedValue({ result: true, value: 'val' })
285+
286+
const result = await defaultMultipleElementsIterationStrategy(elements, 'val', condition)
287+
288+
expect(result).toEqual([{ result: true, value: 'val' }, { result: true, value: 'val' }])
289+
expect(condition).toHaveBeenCalledTimes(2)
290+
})
291+
})
292+
})

types/expect-webdriverio.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,8 @@ interface WdioElementOrArrayMatchers<_R, ActualT = unknown> {
220220
*/
221221
toHaveElementProperty: FnWhenElementOrArrayLike<ActualT, (
222222
property: string,
223-
value: MaybeArray<unknown | RegExp | ExpectWebdriverIO.PartialMatcher<string> | null>,
223+
// Needs deep equality to support unknown property types (objects & arrays)
224+
value: MaybeArray<string | number | null | RegExp | ExpectWebdriverIO.PartialMatcher<string>>,
224225
options?: ExpectWebdriverIO.StringOptions
225226
) => Promise<void>>
226227

0 commit comments

Comments
 (0)