Skip to content

Commit

Permalink
Add support multiple strings for toHaveText APIs (#275)
Browse files Browse the repository at this point in the history
* Add support mutliple strings for toHaveText APIs

* feat(CompareText): added new tests and improved lopp

* removed tests with expect.not

* test(toHaveText): add tests for toHaveText API and update type to string[]

* remove comment
  • Loading branch information
lacell75 authored Dec 15, 2020
1 parent 3b87b64 commit a899a43
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 13 deletions.
6 changes: 4 additions & 2 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -385,26 +385,28 @@ expect(elem).toHaveId('elem')

### toHaveText

Checks if element has a specific text.
Checks if element has a specific text. Can also be called with an array as parameter in the case where the element can have different texts.

##### Usage

```js
browser.url('https://webdriver.io/')
const elem = $('.tagline')
expect(elem).toHaveText('Next-gen browser and mobile automation test framework for Node.js')
expect(elem).toHaveText(['Next-gen browser and mobile automation test framework for Node.js', 'Adding helper functions'])
```

### toHaveTextContaining

Checks if element contains a specific text.
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.

##### Usage

```js
browser.url('https://webdriver.io/')
const elem = $('.tagline')
expect(elem).toHaveTextContaining('browser and mobile automation test framework')
expect(elem).toHaveTextContaining(['browser and mobile automation test framework', 'helper functions'])
```

### toBeDisplayedInViewport
Expand Down
9 changes: 6 additions & 3 deletions src/matchers/element/toHaveText.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { waitUntil, enhanceError, compareText, executeCommand, wrapExpectedWithArray, updateElementsArray } from '../../utils'
import { waitUntil, enhanceError, compareText, compareTextWithArray, executeCommand, wrapExpectedWithArray, updateElementsArray } from '../../utils'
import { runExpect } from '../../util/expectAdapter'

async function condition(el: WebdriverIO.Element, text: string, options: ExpectWebdriverIO.StringOptions): Promise<any> {
async function condition(el: WebdriverIO.Element, text: string | Array<string>, options: ExpectWebdriverIO.StringOptions): Promise<any> {
const actualText = await el.getText()
if (Array.isArray(text)) {
return compareTextWithArray(actualText, text, options)
}
return compareText(actualText, text, options)
}

export function toHaveTextFn(received: WebdriverIO.Element | WebdriverIO.ElementArray, text: string, options: ExpectWebdriverIO.StringOptions = {}): any {
export function toHaveTextFn(received: WebdriverIO.Element | WebdriverIO.ElementArray, text: string | Array<string>, options: ExpectWebdriverIO.StringOptions = {}): any {
const isNot = this.isNot
const { expectation = 'text', verb = 'have' } = this

Expand Down
2 changes: 1 addition & 1 deletion src/matchers/element/toHaveTextContaining.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { runExpect } from '../../util/expectAdapter'
import { toHaveTextFn } from './toHaveText'

function toHaveTextContainingFn(el: WebdriverIO.Element, text: string, options: ExpectWebdriverIO.StringOptions = {}): any {
function toHaveTextContainingFn(el: WebdriverIO.Element, text: string | Array<string>, options: ExpectWebdriverIO.StringOptions = {}): any {
return toHaveTextFn.call(this, el, text, {
...options,
containing: true
Expand Down
1 change: 1 addition & 0 deletions src/types/some-expect.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type WdioElementMaybePromise =
declare namespace WebdriverIO {
interface Element {
_value: () => boolean | string;
_text: () => string;
_attempts: number;
}
interface Browser {
Expand Down
28 changes: 28 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,34 @@ export const compareText = (actual: string, expected: string, { ignoreCase = fal
}
}

export const compareTextWithArray = (actual: string, expectedArray: Array<string>, { ignoreCase = false, trim = false, containing = false}) => {
if (typeof actual !== 'string') {
return {
value: actual,
result: false
}
}

if (trim) {
actual = actual.trim()
}
if (ignoreCase) {
actual = actual.toLowerCase()
expectedArray = expectedArray.map(item => item.toLowerCase())
}
if (containing) {
const textInArray = expectedArray.some((t)=> actual.includes(t))
return {
value: actual,
result: textInArray
}
}
return {
value: actual,
result: expectedArray.includes(actual)
}
}

function aliasFn(
fn: (...args: any) => void,
{ verb, expectation }: {
Expand Down
13 changes: 9 additions & 4 deletions test/__fixtures__/wdio.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ function getPropertyFn(propName) {
return this._value ? this._value(propName) : undefined
}

function getTextFn(propName) {
return this._text ? this._text(propName) : undefined
}

const element = {
$,
$$,
Expand All @@ -22,7 +26,8 @@ const element = {
isClickable: beFn,
isFocused: beFn,
isEnabled: beFn,
getProperty: getPropertyFn
getProperty: getPropertyFn,
getText: getTextFn
}

function $(selector, ...args) {
Expand Down Expand Up @@ -54,8 +59,8 @@ async function waitUntil(condition, { timeout, m, interval }) {
if (result) {
return true
}
} catch { }
attemptsLeft --
} catch {}
attemptsLeft--
await sleep(interval)
}
throw new Error('waitUntil: timeout after ' + timeout)
Expand All @@ -72,4 +77,4 @@ const browser = {

global.browser = browser
global.$ = $
global.$$ = $$
global.$$ = $$
149 changes: 149 additions & 0 deletions test/matchers/element/toHaveText.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { getExpectMessage, getReceived } from '../../__fixtures__/utils';
import { toHaveText } from '../../../src/matchers/element/toHaveText'

describe('toHaveText', () => {
test('wait for success', async () => {
const el = await $('sel')
el._attempts = 2
el._text= function (): string {
if (this._attempts > 0) {
this._attempts--
return ''
}
return 'webdriverio'
}

const result = await toHaveText(el, 'WebdriverIO', { ignoreCase: true })
expect(result.pass).toBe(true)
expect(el._attempts).toBe(0)
})

test('wait but failure', async () => {
const el = await $('sel')
el._text = function (): string {
throw new Error('some error')
}

const result = await toHaveText(el, 'WebdriverIO', { ignoreCase: true })
expect(result.pass).toBe(false)
})

test('success on the first attempt', async () => {
const el = await $('sel')
el._attempts = 0
el._text = function (): string {
this._attempts++
return 'WebdriverIO'
}

const result = await toHaveText(el, 'WebdriverIO', { ignoreCase: true })
expect(result.pass).toBe(true)
expect(el._attempts).toBe(1)
})

test('no wait - failure', async () => {
const el = await $('sel')
el._attempts = 0
el._text = function ():string {
this._attempts++
return 'webdriverio'
}

const result = await toHaveText(el, 'WebdriverIO', { wait: 0 })
expect(result.pass).toBe(false)
expect(el._attempts).toBe(1)
})

test('no wait - success', async () => {
const el = await $('sel')
el._attempts = 0
el._text = function (): string {
this._attempts++
return 'WebdriverIO'
}

const result = await toHaveText(el, 'WebdriverIO', { wait: 0 })
expect(result.pass).toBe(true)
expect(el._attempts).toBe(1)
})

test('not - failure', async () => {
const el = await $('sel')
el._text = function (): string {
return 'WebdriverIO'
}
const result = await toHaveText.call({ isNot: true }, el, 'WebdriverIO', { wait: 0 })
const received = getReceived(result.message())

expect(received).not.toContain('not')
expect(result.pass).toBe(true)
})

test('should return false if texts dont match', async () => {
const el = await $('sel')
el._text = function (): string {
return 'WebdriverIO'
}

const result = await toHaveText.bind({ isNot: true })(el, 'foobar', { wait: 1 })
expect(result.pass).toBe(false)
})

test('should return true if texts match', async () => {
const el = await $('sel')
el._text = function (): string {
return 'WebdriverIO'
}

const result = await toHaveText.bind({ isNot: true })(el, 'WebdriverIO', { wait: 1 })
expect(result.pass).toBe(true)
})

test('message', async () => {
const el = await $('sel')
el._text = function (): string {
return ''
}
const result = await toHaveText(el, 'WebdriverIO')
expect(getExpectMessage(result.message())).toContain('to have text')
})

test('success if array matches with text and ignoreCase', async () => {
const el = await $('sel')
el._attempts = 0
el._text = function (): string {
this._attempts++
return 'WebdriverIO'
}

const result = await toHaveText(el, ['WDIO', 'Webdriverio'], { ignoreCase: true })
expect(result.pass).toBe(true)
expect(el._attempts).toBe(1)
})

test('success if array matches with text and trim', async () => {
const el = await $('sel')
el._attempts = 0
el._text = function (): string {
this._attempts++
return ' WebdriverIO '
}

const result = await toHaveText(el, ['WDIO', 'WebdriverIO', 'toto'], { trim: true })
expect(result.pass).toBe(true)
expect(el._attempts).toBe(1)
})

test('failure if array does not match with text', async () => {
const el = await $('sel')
el._attempts = 0
el._text = function (): string {
this._attempts++
return 'WebdriverIO'
}

const result = await toHaveText(el, ['WDIO', 'Webdriverio'], { wait: 1 })
expect(result.pass).toBe(false)
expect(el._attempts).toBe(1)
})
})
23 changes: 22 additions & 1 deletion test/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { compareText } from '../src/utils'
import { compareText,compareTextWithArray } from '../src/utils'

describe('utils', () => {
describe('compareText', () => {
Expand All @@ -22,4 +22,25 @@ describe('utils', () => {
expect(compareText('qwe_AsD_zxc', 'asd', { ignoreCase: true, containing: true }).result).toBe(true)
})
})
describe('compareTextWithArray', () => {
test('string match in array', () => {
expect(compareTextWithArray('foo', ['foo', 'bar'], {}).result).toBe(true)
})

test('string does not match in array', () => {
expect(compareTextWithArray('foo', ['foot', 'bar'], {}).result).toBe(false)
})

test('trim', () => {
expect(compareTextWithArray(' foo ', ['foo', 'bar'], { trim: true }).result).toBe(true)
})

test('ignoreCase', () => {
expect(compareTextWithArray(' FOO ', ['foO', 'bar'], { trim: true, ignoreCase: true }).result).toBe(true)
})

test('containing', () => {
expect(compareTextWithArray('qwe_AsD_zxc', ['foo', 'zxc'], { ignoreCase: true, containing: true }).result).toBe(true)
})
})
})
4 changes: 2 additions & 2 deletions types/expect-webdriverio.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,12 +233,12 @@ declare namespace ExpectWebdriverIO {
/**
* `WebdriverIO.Element` -> `getText`
*/
toHaveText(text: string, options?: ExpectWebdriverIO.StringOptions): R
toHaveText(text: string | string[], options?: ExpectWebdriverIO.StringOptions): R
/**
* `WebdriverIO.Element` -> `getText`
* Element's text includes the text provided
*/
toHaveTextContaining(text: string, options?: ExpectWebdriverIO.StringOptions): R
toHaveTextContaining(text: string | string[], options?: ExpectWebdriverIO.StringOptions): R

// ===== browser only =====
/**
Expand Down

0 comments on commit a899a43

Please sign in to comment.