Skip to content

Commit f8f6ba0

Browse files
committed
Code review & coverage
c
1 parent dabec4a commit f8f6ba0

8 files changed

Lines changed: 84 additions & 24 deletions

File tree

src/util/elementsUtil.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ export const isElementOrArrayLike = (obj: unknown): obj is WebdriverIO.ElementAr
4040
return !!obj && isElement(obj) || isElementArrayLike(obj)
4141
}
4242

43+
export const isElementOrNotEmptyElementArray = (obj: unknown): obj is WebdriverIO.Element | WdioElements => {
44+
return !!obj && isElement(obj) || (isElementArrayLike(obj) && obj.length > 0)
45+
}
4346
/**
4447
* Universaly await element(s) since depending on the type received, it can become complex.
4548
*

src/util/executeCommand.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export async function executeCommand<T>(
2727
const { elements, element, other } = await awaitElementOrArray(nonAwaitedElements)
2828
if (!elements && !element) {
2929
return {
30-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
30+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- one day move up the unknown type
3131
elementOrArray: other as any,
3232
success: false,
3333
valueOrArray: undefined,
@@ -45,6 +45,7 @@ export async function executeCommand<T>(
4545

4646
const elementOrArray = element ? element : elements ? elements : undefined
4747
if (!elementOrArray) {
48+
// Should be unreachable due to checks above
4849
throw new Error('No elements to process in executeCommand')
4950
}
5051

src/util/formatMessage.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { printDiffOrStringify, printExpected, printReceived } from 'jest-matcher-utils'
22
import { equals } from '../jasmineUtils.js'
33
import type { WdioElements } from '../types.js'
4-
import { isElement, isElementArrayLike, isStrictlyElementArray } from './elementsUtil.js'
4+
import { isElementOrNotEmptyElementArray, isStrictlyElementArray } from './elementsUtil.js'
55

66
export const getSelector = (el: WebdriverIO.Element | WebdriverIO.ElementArray) => {
77
let result = typeof el.selector === 'string' ? el.selector : '<fn>'
@@ -55,10 +55,7 @@ export const enhanceError = (
5555
} = {}): string => {
5656
const { isNot = false, useNotInLabel = true } = context
5757

58-
if (isElement(subject) || (isElementArrayLike(subject) && subject.length > 0)) {
59-
subject = getSelectors(subject)
60-
}
61-
subject = subject = typeof subject === 'string' ? subject : JSON.stringify(subject)
58+
subject = subject = isElementOrNotEmptyElementArray(subject) ? getSelectors(subject) : toJsonString(subject)
6259

6360
let contain = ''
6461
if (containing) {
@@ -125,5 +122,18 @@ export const numberError = (options: ExpectWebdriverIO.NumberOptions = {}): stri
125122
return `<= ${options.lte}`
126123
}
127124

128-
return `Incorrect number options provided. Received: ${JSON.stringify(options)}`
125+
return `Incorrect number options provided. Received: ${toJsonString(options)}`
126+
}
127+
128+
const isString = (value: unknown): value is string => typeof value === 'string'
129+
130+
const toJsonString = (value: unknown): string => {
131+
if (isString(value)) {
132+
return value
133+
}
134+
try {
135+
return JSON.stringify(value)
136+
} catch {
137+
return String(value)
138+
}
129139
}

src/util/waitUntil.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,36 +27,37 @@ export const waitUntil = async (
2727
try {
2828
result = await condition()
2929
error = undefined
30-
if (typeof result === 'boolean' ? result : result.success) {
30+
if (isBoolean(result) ? result : result.success) {
3131
break
3232
}
3333
} catch (err) {
3434
error = err
3535
}
3636

3737
// No need to sleep again if time is already over
38-
if (Date.now() - start < wait) {
38+
if (canWait(start, wait)) {
3939
await sleep(interval)
4040
}
41-
} while (Date.now() - start < wait)
41+
} while (canWait(start, wait))
4242

4343
if (error) { throw error }
4444

45-
if (typeof result === 'boolean') {
45+
if (isBoolean(result)) {
4646
return result
4747
}
4848

4949
const { results } = result
5050

5151
if (results.length === 0) {
52-
// To fails when using .not, we need to send true so that Jest inverts it to false
52+
// To fails with .not, we need pass=true, so it s inverted later by Jest's expect framework
5353
return isNot
5454
}
5555

56-
// TODO dprevost: Should we consider moving that into a strategy pattern, so that if a matching does not need the same pattern he can change it?
57-
const allTrue = () => results.every((result) => result)
58-
const allFalse = () => results.every((result) => !result)
59-
60-
// Needs to return true when all true, but with isNot, must be success=false when all false (failure=true when at least one true)
61-
return isNot ? !allFalse() : allTrue()
56+
// With isNot to succeed with need pass=false, so it s inverted later by Jest's expect framework
57+
return isNot ? !isAllFalse(results) : isAllTrue(results)
6258
}
59+
const isBoolean = (value: unknown): value is boolean => typeof value === 'boolean'
60+
61+
const isAllTrue = (results: boolean[]): boolean => results.every((res) => res === true)
62+
const isAllFalse = (results: boolean[]): boolean => results.every((res) => res === false)
63+
const canWait = (start: number, wait: number): boolean => (Date.now() - start) < wait

test/matchers.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ describe('Custom Wdio Matchers Integration Tests', async () => {
116116
})
117117

118118
// TODO to support one day?
119-
test('toHave works with arrayContaining asymmetric matcher', async () => {
119+
test.skip('toHave works with arrayContaining asymmetric matcher', async () => {
120120
await expectLib(el).toHaveText(
121121
expectLib.arrayContaining([
122122
expectLib.stringContaining('Valid'),

test/matchers/beMatchers.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,27 @@ Received: "not ${lastMatcherWords(matcherFn.name)}"`)
217217
})
218218
})
219219

220-
test('success with matcherFn and command options', async () => {
220+
test('success with matcherFn and custom command options', async () => {
221+
const result = await thisContext.matcherFn(elements, { wait: 4, interval: 99 })
222+
223+
for (const element of elements) {
224+
expect(element[elementFnName]).toHaveBeenCalledOnce()
225+
}
226+
expect(waitUntil).toHaveBeenCalledExactlyOnceWith(expect.any(Function), undefined, { wait: 4, interval: 99 })
227+
expect(result.pass).toBe(true)
228+
})
229+
230+
test('success with matcherFn and custom command options - only interval', async () => {
231+
const result = await thisContext.matcherFn(elements, { interval: 99 })
232+
233+
for (const element of elements) {
234+
expect(element[elementFnName]).toHaveBeenCalledOnce()
235+
}
236+
expect(waitUntil).toHaveBeenCalledExactlyOnceWith(expect.any(Function), undefined, { wait: 1, interval: 99 })
237+
expect(result.pass).toBe(true)
238+
})
239+
240+
test('success with matcherFn and default command options', async () => {
221241
const result = await thisContext.matcherFn(elements)
222242

223243
for (const element of elements) {

test/softAssertions.test.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@ describe('Soft Assertions', () => {
2828
expect(failures[0].error.message).toContain('text')
2929
})
3030

31-
// TODO dprevost: fix this, in soft results is undefined even thought the matcher records a failure and returns it
32-
it.skip('should support chained assertions with .not', async () => {
31+
it('should support chained assertions with .not', async () => {
32+
// Setup a test ID for this test
3333
const softService = SoftAssertService.getInstance()
3434
softService.setCurrentTest('test-2', 'test name', 'test file')
3535

36-
await expectWdio.soft(el).not.toHaveText('Actual Text')
36+
// This should not throw even though it fails
37+
await expectWdio.soft(el).not.toHaveText('Actual Text', { wait: 0 })
3738

39+
// Verify the failure was recorded
3840
const failures = expectWdio.getSoftFailures()
3941
expect(failures.length).toBe(1)
4042
expect(failures[0].matcherName).toBe('not.toHaveText')

test/util/elementsUtil.test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { vi, test, describe, expect, beforeEach } from 'vitest'
22
import { $, $$, } from '@wdio/globals'
33

4-
import { awaitElementOrArray, awaitElementArray, wrapExpectedWithArray, map, isStrictlyElementArray, isElement, isElementArrayLike, isElementOrArrayLike } from '../../src/util/elementsUtil.js'
4+
import { awaitElementOrArray, awaitElementArray, wrapExpectedWithArray, map, isStrictlyElementArray, isElement, isElementArrayLike, isElementOrArrayLike, isElementOrNotEmptyElementArray } from '../../src/util/elementsUtil.js'
55
import { chainableElementArrayFactory, elementArrayFactory, elementFactory } from '../__mocks__/@wdio/globals.js'
66

77
vi.mock('@wdio/globals')
@@ -478,4 +478,27 @@ describe('elementsUtil', () => {
478478
expect(result).toBe(false)
479479
})
480480
})
481+
482+
describe(isElementOrNotEmptyElementArray, async () => {
483+
test.for([
484+
await $('element'),
485+
await $$('elements'),
486+
[elementFactory('element1'), elementFactory('element2')],
487+
])('should return true for Element or non-empty ElementArray/Element[]: %s', async (element) => {
488+
const result = isElementOrNotEmptyElementArray(element)
489+
490+
expect(result).toBe(true)
491+
})
492+
test.for([
493+
[],
494+
$$('elements'),
495+
$('element'),
496+
'1',
497+
{},
498+
])('should return false for non-Element or empty array: %s', async (element) => {
499+
const result = isElementOrNotEmptyElementArray(element)
500+
501+
expect(result).toBe(false)
502+
})
503+
})
481504
})

0 commit comments

Comments
 (0)