Skip to content

Commit

Permalink
feat(expect-puppeteer): enhance toMatchElement / toClick text option
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Text is now trimmed and no longer evaluated as a RegExp. If you want this behaviour, use a true RegExp.

Closes #51
Closes #50
  • Loading branch information
gregberge committed May 3, 2018
1 parent edbd646 commit cee8f46
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 11 deletions.
5 changes: 3 additions & 2 deletions packages/expect-puppeteer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Expect an element to be in the page or element, then click on it.
* `raf` - to constantly execute `pageFunction` in `requestAnimationFrame` callback. This is the tightest polling mode which is suitable to observe styling changes.
* `mutation` - to execute `pageFunction` on every DOM mutation.
* `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `500`.
* `text` <[string]> A text or a RegExp to match in element `textContent`.
* `text` <[string]|[RegExp]> A text or a RegExp to match in element `textContent`.

```js
await expect(page).toClick('button', { text: 'Home' })
Expand Down Expand Up @@ -172,7 +172,7 @@ Expect an element be present in the page or element.
* `raf` - to constantly execute `pageFunction` in `requestAnimationFrame` callback. This is the tightest polling mode which is suitable to observe styling changes.
* `mutation` - to execute `pageFunction` on every DOM mutation.
* `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `500`.
* `text` <[string]> A text or a RegExp to match in element `textContent`.
* `text` <[string]|[RegExp]> A text or a RegExp to match in element `textContent`.

```js
// Select a row containing a text
Expand Down Expand Up @@ -246,6 +246,7 @@ MIT
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type 'Number'
[object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object 'Object'
[promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise 'Promise'
[regexp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp 'RegExp'
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type 'String'
[error]: https://nodejs.org/api/errors.html#errors_class_error 'Error'
[element]: https://developer.mozilla.org/en-US/docs/Web/API/element 'Element'
Expand Down
56 changes: 49 additions & 7 deletions packages/expect-puppeteer/src/matchers/toMatchElement.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,73 @@
import { getContext, enhanceError } from '../utils'
import { defaultOptions } from '../options'

async function toMatchElement(instance, selector, { text, ...options } = {}) {
const isRegExp = input =>
Object.prototype.toString.call(input) === '[object RegExp]'

const expandSearchExpr = expr => {
if (isRegExp(expr)) return { text: null, regexp: expr.toString() }
if (typeof expr === 'string') return { text: expr, regexp: null }
return { text: null, regexp: null }
}

async function toMatchElement(
instance,
selector,
{ text: searchExpr, ...options } = {},
) {
options = defaultOptions(options)

const { page, handle } = await getContext(instance, () => document)

const getElement = (handle, selector, text) => {
const { text, regexp } = expandSearchExpr(searchExpr)

const getElement = (handle, selector, text, regexp) => {
const elements = handle.querySelectorAll(selector)
if (text !== undefined) {
return [...elements].find(({ textContent }) => textContent.match(text))
if (regexp !== null) {
const [, pattern, flags] = regexp.match(/\/(.*)\/(.*)?/)
return [...elements].find(({ textContent }) =>
textContent
.replace(/\s+/g, ' ')
.trim()
.match(new RegExp(pattern, flags)),
)
}
if (text !== null) {
return [...elements].find(({ textContent }) =>
textContent
.replace(/\s+/g, ' ')
.trim()
.includes(text),
)
}
return elements[0]
}

try {
await page.waitForFunction(getElement, options, handle, selector, text)
await page.waitForFunction(
getElement,
options,
handle,
selector,
text,
regexp,
)
} catch (error) {
throw enhanceError(
error,
`Element ${selector}${
text !== undefined ? ` (text: "${text}") ` : ' '
text !== null || regexp !== null ? ` (text: "${text || regexp}") ` : ' '
}not found`,
)
}

const jsHandle = await page.evaluateHandle(getElement, handle, selector, text)
const jsHandle = await page.evaluateHandle(
getElement,
handle,
selector,
text,
regexp,
)
return jsHandle.asElement()
}

Expand Down
23 changes: 21 additions & 2 deletions packages/expect-puppeteer/src/matchers/toMatchElement.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,22 @@ describe('toMatchElement', () => {
expect(textContent).toBe('Page 2')
})

it('should match using text', async () => {
it('should match using text (string)', async () => {
const element = await expect(page).toMatchElement('a', { text: 'Page 2' })
const textContentProperty = await element.getProperty('textContent')
const textContent = await textContentProperty.jsonValue()
expect(textContent).toBe('Page 2')
})

it('should match using text (RegExp)', async () => {
const element = await expect(page).toMatchElement('a', {
text: /Page\s2/,
})
const textContentProperty = await element.getProperty('textContent')
const textContent = await textContentProperty.jsonValue()
expect(textContent).toBe('Page 2')
})

it('should return an error if element is not found', async () => {
expect.assertions(3)

Expand All @@ -39,7 +48,7 @@ describe('toMatchElement', () => {
expect(textContent).toMatch('A div in the main')
})

it('should match using text', async () => {
it('should match using text (string)', async () => {
const main = await page.$('main')
const element = await expect(main).toMatchElement('*', {
text: 'in the main',
Expand All @@ -49,6 +58,16 @@ describe('toMatchElement', () => {
expect(textContent).toMatch('A div in the main')
})

it('should match using text (RegExp)', async () => {
const main = await page.$('main')
const element = await expect(main).toMatchElement('*', {
text: /in.the\smain/g,
})
const textContentProperty = await element.getProperty('textContent')
const textContent = await textContentProperty.jsonValue()
expect(textContent).toMatch('A div in the main')
})

it('should return an error if element is not found', async () => {
const main = await page.$('main')
expect.assertions(3)
Expand Down

0 comments on commit cee8f46

Please sign in to comment.