diff --git a/.gitignore b/.gitignore index 88edb628..05834bb2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ lib/ +yarn-error.log diff --git a/packages/expect-puppeteer/README.md b/packages/expect-puppeteer/README.md index 60ea3098..716ae5ad 100644 --- a/packages/expect-puppeteer/README.md +++ b/packages/expect-puppeteer/README.md @@ -165,6 +165,7 @@ Expect an element be present in the page or element. - `mutation` - to execute `pageFunction` on every DOM mutation. - `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method. - `text` <[string]|[RegExp]> A text or a RegExp to match in element `textContent`. + - `visible` <[boolean]> wait for element to be present in DOM and to be visible, i.e. to not have `display: none` or `visibility: hidden` CSS properties. Defaults to `false`. ```js // Select a row containing a text diff --git a/packages/expect-puppeteer/src/matchers/toMatchElement.js b/packages/expect-puppeteer/src/matchers/toMatchElement.js index 64055125..102126dc 100644 --- a/packages/expect-puppeteer/src/matchers/toMatchElement.js +++ b/packages/expect-puppeteer/src/matchers/toMatchElement.js @@ -4,19 +4,36 @@ import { defaultOptions } from '../options' async function toMatchElement( instance, selector, - { text: searchExpr, ...options } = {}, + { text: searchExpr, visible = false, ...options } = {}, ) { options = defaultOptions(options) - const { page, handle } = await getContext(instance, () => document) const { text, regexp } = expandSearchExpr(searchExpr) - const getElement = (handle, selector, text, regexp) => { - const elements = handle.querySelectorAll(selector) + const getElement = (handle, selector, text, regexp, visible) => { + function hasVisibleBoundingBox(element) { + const rect = element.getBoundingClientRect() + return !!(rect.top || rect.bottom || rect.width || rect.height) + } + + const isVisible = element => { + if (visible) { + const style = window.getComputedStyle(element) + return ( + style && + style.visibility !== 'hidden' && + hasVisibleBoundingBox(element) + ) + } + + return true + } + + const elements = [...handle.querySelectorAll(selector)].filter(isVisible) if (regexp !== null) { const [, pattern, flags] = regexp.match(/\/(.*)\/(.*)?/) - return [...elements].find(({ textContent }) => + return elements.find(({ textContent }) => textContent .replace(/\s+/g, ' ') .trim() @@ -24,7 +41,7 @@ async function toMatchElement( ) } if (text !== null) { - return [...elements].find(({ textContent }) => + return elements.find(({ textContent }) => textContent .replace(/\s+/g, ' ') .trim() @@ -42,6 +59,7 @@ async function toMatchElement( selector, text, regexp, + visible, ) } catch (error) { throw enhanceError( @@ -58,6 +76,7 @@ async function toMatchElement( selector, text, regexp, + visible, ) return jsHandle.asElement() } diff --git a/packages/expect-puppeteer/src/matchers/toMatchElement.test.js b/packages/expect-puppeteer/src/matchers/toMatchElement.test.js index 915c5671..f10374fd 100644 --- a/packages/expect-puppeteer/src/matchers/toMatchElement.test.js +++ b/packages/expect-puppeteer/src/matchers/toMatchElement.test.js @@ -37,6 +37,42 @@ describe('toMatchElement', () => { expect(error.message).toMatch('waiting for function failed') } }) + + it('should match using visible options', async () => { + expect.assertions(11) + + const normalElement = await expect(page).toMatchElement('.normal', { + visible: true, + }) + const textContentProperty = await normalElement.getProperty('textContent') + const textContent = await textContentProperty.jsonValue() + expect(textContent).toBe('normal element') + + try { + await expect(page).toMatchElement('.hidden', { visible: true }) + } catch (error) { + expect(error.message).toMatch('Element .hidden not found') + expect(error.message).toMatch('waiting for function failed') + } + + try { + await expect(page).toMatchElement('.displayed', { visible: true }) + } catch (error) { + expect(error.message).toMatch('Element .displayed not found') + expect(error.message).toMatch('waiting for function failed') + } + + try { + await expect(page).toMatchElement('.displayedWithClassname', { + visible: true, + }) + } catch (error) { + expect(error.message).toMatch( + 'Element .displayedWithClassname not found', + ) + expect(error.message).toMatch('waiting for function failed') + } + }) }) describe('ElementHandle', () => { diff --git a/server/public/index.html b/server/public/index.html index 9e792059..7cd6b65f 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -5,6 +5,11 @@