Skip to content

Commit

Permalink
fix: handle toHaveText when receiving an array of elements (#1571)
Browse files Browse the repository at this point in the history
* refine `executeCommand` function to improve type annotations and documentation

* add type assertions across matchers

* add support for element arrays in `toHaveText` matcher and update tests and documentation accordingly

* update `toHaveElementClass` type definition and add examples

* update example in API documentation to use correct selector

* add selector property to mock elements

* update return type in `enhanceError`

* enhance `toHaveText` matcher to handle multiple elements and add test for custom message

* alter usage examples for `toHaveElementClass` and `toHaveText`

* update JSDoc

* fix indentation in`toHaveText` method
  • Loading branch information
sangcnguyen committed Jun 13, 2024
1 parent 14dd6d5 commit bedf208
Show file tree
Hide file tree
Showing 19 changed files with 212 additions and 94 deletions.
15 changes: 8 additions & 7 deletions __mocks__/@wdio/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,16 @@ export function $$(selector) {
}
})
// Required to refetch
const parent: any = element;
parent._length = length;
els.parent = parent;
const parent: any = element
parent._length = length
els.parent = parent

els.foundWith = "$$";
els.foundWith = "$$"
// Required to check length prop
els.props = [];
els.props.length = length;
return els;
els.props = []
els.props.length = length
els.selector = selector
return els
}

async function waitUntil(condition, { timeout, interval }) {
Expand Down
16 changes: 16 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,22 @@ await expect(elem).toHaveText('Next-gen browser and mobile automation test frame
await expect(elem).toHaveText(['Next-gen browser and mobile automation test framework for Node.js', 'Get Started'])
```

In case there is a list of elements in the div below, you can assert them using an array.
```
<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ul>
```

##### Usage

```js
const elem = await $$('ul > li')
await expect(elem).toHaveText(['Coffee', 'Tea', 'Milk'])
```

### toHaveTextContaining

Checks if element contains a specific text. Can also be called with an array as parameter in the case where the element can have different texts.
Expand Down
14 changes: 9 additions & 5 deletions src/matchers/element/toHaveAttribute.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import {
waitUntil, enhanceError, compareText, executeCommand, wrapExpectedWithArray
} from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
import {
compareText,
enhanceError,
executeCommand,
waitUntil,
wrapExpectedWithArray
} from '../../utils.js'

async function conditionAttr(el: WebdriverIO.Element, attribute: string) {
const attr = await el.getAttribute(attribute)
Expand Down Expand Up @@ -30,7 +34,7 @@ export async function toHaveAttributeAndValue(received: WdioElementMaybePromise,
let attr
const pass = await waitUntil(async () => {
const result = await executeCommand.call(this, el, conditionAttrAndValue, options, [attribute, value, options])
el = result.el
el = result.el as WebdriverIO.Element
attr = result.values

return result.success
Expand All @@ -53,7 +57,7 @@ async function toHaveAttributeFn(received: WdioElementMaybePromise, attribute: s

const pass = await waitUntil(async () => {
const result = await executeCommand.call(this, el, conditionAttr, {}, [attribute])
el = result.el
el = result.el as WebdriverIO.Element

return result.success
}, isNot, {})
Expand Down
12 changes: 8 additions & 4 deletions src/matchers/element/toHaveChildren.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
import {
waitUntil, enhanceError, compareNumbers, numberError, executeCommand,
compareNumbers,
enhanceError,
executeCommand,
numberError,
waitUntil,
wrapExpectedWithArray
} from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'

async function condition(el: WebdriverIO.Element, options: ExpectWebdriverIO.NumberOptions) {
const children = await el.$$('./*')
Expand Down Expand Up @@ -48,7 +52,7 @@ export async function toHaveChildren(
let children
const pass = await waitUntil(async () => {
const result = await executeCommand.call(this, el, condition, numberOptions, [numberOptions])
el = result.el
el = result.el as WebdriverIO.Element
children = result.values

return result.success
Expand Down
6 changes: 3 additions & 3 deletions src/matchers/element/toHaveClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import type { WdioElementMaybePromise } from '../../types.js'
import { compareText, compareTextWithArray, enhanceError, executeCommand, isAsymmeyricMatcher, waitUntil, wrapExpectedWithArray } from '../../utils.js'
import { toHaveAttributeAndValue } from './toHaveAttribute.js'

async function condition(el: WdioElementMaybePromise, attribute: string, value: string | RegExp | Array<string | RegExp> | ExpectWebdriverIO.PartialMatcher, options: ExpectWebdriverIO.StringOptions) {
const actualClass = await (await el).getAttribute(attribute)
async function condition(el: WebdriverIO.Element, attribute: string, value: string | RegExp | Array<string | RegExp> | ExpectWebdriverIO.PartialMatcher, options: ExpectWebdriverIO.StringOptions) {
const actualClass = await el.getAttribute(attribute)
if (typeof actualClass !== 'string') {
return { result: false }
}
Expand Down Expand Up @@ -58,7 +58,7 @@ export async function toHaveElementClass(

const pass = await waitUntil(async () => {
const result = await executeCommand.call(this, el, condition, options, [attribute, expectedValue, options])
el = result.el
el = result.el as WebdriverIO.Element
attr = result.values

return result.success
Expand Down
10 changes: 5 additions & 5 deletions src/matchers/element/toHaveComputedLabel.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
import {
waitUntil,
enhanceError,
compareText,
compareTextWithArray,
enhanceError,
executeCommand,
waitUntil,
wrapExpectedWithArray
} from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'

async function condition(
el: WebdriverIO.Element,
Expand Down Expand Up @@ -41,7 +41,7 @@ export async function toHaveComputedLabel(
const pass = await waitUntil(
async () => {
const result = await executeCommand.call(this, el, condition, options, [expectedValue, options])
el = result.el
el = result.el as WebdriverIO.Element
actualLabel = result.values

return result.success
Expand Down
10 changes: 5 additions & 5 deletions src/matchers/element/toHaveComputedRole.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
import {
waitUntil,
enhanceError,
compareText,
compareTextWithArray,
enhanceError,
executeCommand,
waitUntil,
wrapExpectedWithArray
} from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'

async function condition(
el: WebdriverIO.Element,
Expand Down Expand Up @@ -41,7 +41,7 @@ export async function toHaveComputedRole(
const pass = await waitUntil(
async () => {
const result = await executeCommand.call(this, el, condition, options, [expectedValue, options])
el = result.el
el = result.el as WebdriverIO.Element
actualRole = result.values

return result.success
Expand Down
10 changes: 5 additions & 5 deletions src/matchers/element/toHaveElementProperty.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
import {
waitUntil,
enhanceError,
compareText,
enhanceError,
executeCommand,
waitUntil,
wrapExpectedWithArray
} from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'

async function condition(
el: WebdriverIO.Element,
Expand Down Expand Up @@ -54,7 +54,7 @@ export async function toHaveElementProperty(
const pass = await waitUntil(
async () => {
const result = await executeCommand.call(this, el, condition, options, [property, value])
el = result.el
el = result.el as WebdriverIO.Element
prop = result.values

return result.success
Expand Down
11 changes: 7 additions & 4 deletions src/matchers/element/toHaveHTML.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
import {
waitUntil, enhanceError, compareText, compareTextWithArray, executeCommand,
compareText, compareTextWithArray,
enhanceError,
executeCommand,
waitUntil,
wrapExpectedWithArray
} from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'

async function condition(el: WebdriverIO.Element, html: string | RegExp | ExpectWebdriverIO.PartialMatcher | Array<string | RegExp>, options: ExpectWebdriverIO.HTMLOptions) {
const actualHTML = await el.getHTML(options.includeSelectorTag)
Expand Down Expand Up @@ -32,7 +35,7 @@ export async function toHaveHTML(

const pass = await waitUntil(async () => {
const result = await executeCommand.call(this, el, condition, options, [expectedValue, options])
el = result.el
el = result.el as WebdriverIO.Element
actualHTML = result.values

return result.success
Expand Down
8 changes: 4 additions & 4 deletions src/matchers/element/toHaveHeight.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
import {
waitUntil,
enhanceError,
executeCommand,
waitUntil,
wrapExpectedWithArray
} from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'

async function condition(el: WebdriverIO.Element, height: number) {
const actualHeight = await el.getSize('height')
Expand Down Expand Up @@ -37,7 +37,7 @@ export async function toHaveHeight(
async () => {
const result = await executeCommand.call(this, el, condition, options, [expectedValue, options])

el = result.el
el = result.el as WebdriverIO.Element
actualHeight = result.values

return result.success
Expand Down
12 changes: 6 additions & 6 deletions src/matchers/element/toHaveSize.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { DEFAULT_OPTIONS } from '../../constants.js';
import type { WdioElementMaybePromise } from '../../types.js';
import {
waitUntil,
compareObject,
enhanceError,
executeCommand,
waitUntil,
wrapExpectedWithArray,
compareObject,
} from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
} from '../../utils.js';

async function condition(el: WebdriverIO.Element, size: { height: number; width: number }): Promise<any> {
const actualSize = await el.getSize()
Expand Down Expand Up @@ -35,7 +35,7 @@ export async function toHaveSize(
async () => {
const result = await executeCommand.call(this, el, condition, options, [expectedValue, options])

el = result.el
el = result.el as WebdriverIO.Element
actualSize = result.values

return result.success
Expand Down
14 changes: 9 additions & 5 deletions src/matchers/element/toHaveStyle.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { DEFAULT_OPTIONS } from '../../constants.js';
import type { WdioElementMaybePromise } from '../../types.js';
import {
waitUntil, enhanceError, compareStyle, executeCommand, wrapExpectedWithArray
} from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
compareStyle,
enhanceError,
executeCommand,
waitUntil,
wrapExpectedWithArray
} from '../../utils.js';

async function condition(el: WebdriverIO.Element, style: { [key: string]: string; }, options: ExpectWebdriverIO.StringOptions) {
return compareStyle(el, style, options)
Expand All @@ -27,7 +31,7 @@ export async function toHaveStyle(

const pass = await waitUntil(async () => {
const result = await executeCommand.call(this, el, condition, options, [expectedValue, options])
el = result.el
el = result.el as WebdriverIO.Element
actualStyle = result.values

return result.success
Expand Down
44 changes: 34 additions & 10 deletions src/matchers/element/toHaveText.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
import { DEFAULT_OPTIONS } from '../../constants.js';
import { WdioElementMaybePromise, WdioElementsMaybePromise } from '../../types.js';
import {
waitUntil, enhanceError, compareText, compareTextWithArray, executeCommand,
compareText, compareTextWithArray,
enhanceError,
executeCommand,
waitUntil,
wrapExpectedWithArray
} from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
} from '../../utils.js';

async function condition(el: WebdriverIO.Element, text: string | RegExp | ExpectWebdriverIO.PartialMatcher | Array<string | RegExp>, options: ExpectWebdriverIO.StringOptions) {
const actualText = await el.getText()
if (Array.isArray(text)) {
return compareTextWithArray(actualText, text, options)
async function condition(el: WebdriverIO.Element | WebdriverIO.ElementArray, text: string | RegExp | Array<string | RegExp> | ExpectWebdriverIO.PartialMatcher | Array<string | RegExp>, options: ExpectWebdriverIO.StringOptions) {
const actualTextArray = []
let actualText, checkAllValuesMatchCondition

if(Array.isArray(el)){
const checkPromises = el.map(async (value) => {
actualText = await value.getText()
actualTextArray.push(actualText)
return Array.isArray(text)
? compareTextWithArray(actualText, text, options).result
: compareText(actualText, text, options).result
})
const results = await Promise.all(checkPromises)
checkAllValuesMatchCondition = results.every(result => result);
}
else{
actualText = await (el as WebdriverIO.Element).getText()
actualTextArray.push(actualText);
checkAllValuesMatchCondition = Array.isArray(text)
? compareTextWithArray(actualText, text, options).result
: compareText(actualText, text, options).result
}

return {
value: actualTextArray.length === 1 ? actualTextArray[0] : actualTextArray,
result: checkAllValuesMatchCondition
}
return compareText(actualText, text, options)
}

export async function toHaveText(
received: WdioElementMaybePromise,
received: WdioElementMaybePromise | WdioElementsMaybePromise,
expectedValue: string | RegExp | ExpectWebdriverIO.PartialMatcher | Array<string | RegExp>,
options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS
) {
Expand Down
8 changes: 4 additions & 4 deletions src/matchers/element/toHaveWidth.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
import {
waitUntil,
enhanceError,
executeCommand,
waitUntil,
wrapExpectedWithArray
} from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'

async function condition(el: WebdriverIO.Element, width: number): Promise<any> {
const actualWidth = await el.getSize('width')
Expand Down Expand Up @@ -37,7 +37,7 @@ export async function toHaveWidth(
async () => {
const result = await executeCommand.call(this, el, condition, options, [expectedValue, options])

el = result.el
el = result.el as WebdriverIO.Element
actualWidth = result.values

return result.success
Expand Down

0 comments on commit bedf208

Please sign in to comment.