diff --git a/README.md b/README.md index 6ad2e0d0..985ec8c5 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ to maintain. * [`toHaveAttribute`](#tohaveattribute) * [`toHaveClass`](#tohaveclass) * [`toHaveStyle`](#tohavestyle) + * [`toBeVisible`](#tobevisible) * [Inspiration](#inspiration) * [Other Solutions](#other-solutions) * [Guiding Principles](#guiding-principles) @@ -194,6 +195,35 @@ This also works with rules that are applied to the element via a class name for which some rules are defined in a stylesheet currently active in the document. The usual rules of css precedence apply. +### `toBeVisible` + +This allows you to check if an element is currently visible to the user. + +An element is visible if **all** the following conditions are met: + +* it does not have its css property `display` set to `none` +* it does not have its css property `visibility` set to either `hidden` or + `collapse` +* it does not have its css property `opacity` set to `0` +* its parent element is also visible (and so on up to the top of the DOM tree) + +```javascript +// add the custom expect matchers once +import 'jest-dom/extend-expect' + +// ... +//
+//

Page title

+//
+//
+//

Hello World +//

+expect(container.querySelector('header')).toBeVisible() +expect(container.querySelector('h1')).not.toBeVisible() +expect(container.querySelector('strong')).not.toBeVisible() +// ... +``` + ## Inspiration This whole library was extracted out of Kent C. Dodds' [dom-testing-library][], diff --git a/src/__tests__/index.js b/src/__tests__/index.js index 3b330653..dc3e42ce 100644 --- a/src/__tests__/index.js +++ b/src/__tests__/index.js @@ -167,3 +167,35 @@ test('.toHaveStyle', () => { document.body.removeChild(style) document.body.removeChild(container) }) + +test('.toBeVisible', () => { + const {container} = render(` +
+
+

Main title

+

Secondary title

+

Secondary title

+

Secondary title

+
Secondary title
+
+
+

Hello World

+
+
+ `) + + expect(container.querySelector('header')).toBeVisible() + expect(container.querySelector('h1')).not.toBeVisible() + expect(container.querySelector('h2')).not.toBeVisible() + expect(container.querySelector('h3')).not.toBeVisible() + expect(container.querySelector('h4')).not.toBeVisible() + expect(container.querySelector('h5')).toBeVisible() + expect(container.querySelector('strong')).not.toBeVisible() + + expect(() => + expect(container.querySelector('header')).not.toBeVisible(), + ).toThrowError() + expect(() => + expect(container.querySelector('p')).toBeVisible(), + ).toThrowError() +}) diff --git a/src/index.js b/src/index.js index 386c19e0..6750ac4d 100644 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,7 @@ import {toHaveTextContent} from './to-have-text-content' import {toHaveAttribute} from './to-have-attribute' import {toHaveClass} from './to-have-class' import {toHaveStyle} from './to-have-style' +import {toBeVisible} from './to-be-visible' export { toBeInTheDOM, @@ -10,4 +11,5 @@ export { toHaveAttribute, toHaveClass, toHaveStyle, + toBeVisible, } diff --git a/src/to-be-visible.js b/src/to-be-visible.js new file mode 100644 index 00000000..38b67321 --- /dev/null +++ b/src/to-be-visible.js @@ -0,0 +1,39 @@ +import {matcherHint} from 'jest-matcher-utils' +import {checkHtmlElement, getMessage} from './utils' + +function isStyleVisible(element) { + const {display, visibility, opacity} = getComputedStyle(element) + return ( + display !== 'none' && + visibility !== 'hidden' && + visibility !== 'collapse' && + opacity !== '0' && + opacity !== 0 + ) +} + +function isElementVisible(element) { + return ( + isStyleVisible(element) && + (!element.parentElement || isElementVisible(element.parentElement)) + ) +} + +export function toBeVisible(element) { + checkHtmlElement(element) + const isVisible = isElementVisible(element) + return { + pass: isVisible, + message: () => { + const to = this.isNot ? 'not to' : 'to' + const is = isVisible ? 'is' : 'is not' + return getMessage( + matcherHint(`${this.isNot ? '.not' : ''}.toBeVisible`, 'element', ''), + 'Expected', + `element ${to} be visible`, + 'Received', + `element ${is} visible`, + ) + }, + } +}