Skip to content

Commit

Permalink
test(project): add DOM test helpers (#4860)
Browse files Browse the repository at this point in the history
  • Loading branch information
joshblack committed Dec 11, 2019
1 parent 2897bc9 commit 4a4622c
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@
* LICENSE file in the root directory of this source tree.
*/

import { getByText } from '@carbon/test-utils/dom';
import { render, cleanup } from '@carbon/test-utils/react';
import React from 'react';
import { Simulate } from 'react-dom/test-utils';
import AccordionItem from '../AccordionItem';
import { mount } from 'enzyme';
import { settings } from 'carbon-components';

const { prefix } = settings;

describe('AccordionItem', () => {
afterEach(cleanup);

it('should render', () => {
const wrapper = mount(
<AccordionItem title="A heading" className="extra-class">
Expand Down Expand Up @@ -46,14 +51,15 @@ describe('AccordionItem', () => {
});

it('should call `onClick` when the accordion list item is clicked', () => {
const title = 'test title';
const onClick = jest.fn();
const wrapper = mount(
<AccordionItem title="A heading" open onClick={onClick}>
const { container } = render(
<AccordionItem title={title} open onClick={onClick}>
Lorem ipsum.
</AccordionItem>
);
// Event bubbles up from the interactive <button> element
wrapper.find('button').simulate('click');

Simulate.click(getByText(container, title));
expect(onClick).toHaveBeenCalledTimes(1);
});

Expand Down
86 changes: 86 additions & 0 deletions packages/test-utils/__tests__/dom-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* Copyright IBM Corp. 2016, 2018
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import { getByText, isElementVisible } from '../dom';

describe('DOM test helpers', () => {
let container;

beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});

afterEach(() => {
document.body.removeChild(container);
});

describe('getByText', () => {
it('should get the matching node for the given text input', () => {
const nodes = [
'<div>Text A</div>',
'<button>Text B</button>',
'<button>Text C <svg></svg></button>',
];
container.innerHTML = nodes.join('');

expect(getByText(container, 'Text A')).toEqual(container.childNodes[0]);
expect(getByText(container, 'Text B')).toEqual(container.childNodes[1]);
expect(getByText(container, 'Text C')).toEqual(container.childNodes[2]);
});

it('should return null if no matches are found', () => {
expect(getByText(container, 'Not found')).toEqual(null);
});
});

describe('isElementVisible', () => {
it('should detect if an element is visible', () => {
expect(isElementVisible(container)).toBe(true);
});

it('should detect if an element is not visible', () => {
const hidden = Array.from({ length: 6 }).map(() =>
document.createElement('div')
);

// <div hidden></div>
hidden[0].setAttribute('hidden', '');

// <div style="display: none;"></div>
hidden[1].style.display = 'none';

// <div style="visibility: hidden;"></div>
hidden[2].style.visibility = 'hidden';

// <div style="visibility: collapse;"></div>
hidden[3].style.visibility = 'collapse';

// <div style="opacity: 0;"></div>
hidden[4].style.opacity = '0';

// <div style="opacity: 0;"></div>
hidden[5].style.opacity = 0;

for (const node of hidden) {
container.appendChild(node);
expect(isElementVisible(node)).toBe(false);
}
});

it('should detect if an element has a parent that is not visible', () => {
const hiddenParent = document.createElement('div');
hiddenParent.style.display = 'none';

const visibleChild = document.createElement('div');
hiddenParent.appendChild(visibleChild);

expect(isElementVisible(hiddenParent)).toBe(false);
expect(isElementVisible(visibleChild)).toBe(false);
});
});
});
71 changes: 71 additions & 0 deletions packages/test-utils/dom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* Copyright IBM Corp. 2016, 2018
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import prettier from 'prettier';

/**
* Find the HTMLElement that includes the given `text`
*
* @param {HTMLElement} node
* @param {string} text
* @returns {?HTMLElement}
*/
export function getByText(node, text) {
if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() === text) {
return node.parentNode;
}

for (const child of node.childNodes) {
const match = getByText(child, text);
if (match) {
return match;
}
}

return null;
}

/**
* Check if an element is currently visible to an end-user.
*
* @param {HTMLElement} element
* @returns {boolean}
*/
export function isElementVisible(element) {
const { getComputedStyle } = element.ownerDocument.defaultView;
const { display, visibility, opacity } = getComputedStyle(element);

if (
element.hasAttribute('hidden') ||
display === 'none' ||
visibility === 'hidden' ||
visibility === 'collapse' ||
opacity === '0' ||
opacity === 0
) {
return false;
}

if (element.parentElement) {
return isElementVisible(element.parentElement);
}

return true;
}

/**
* Pretty-print the outerHTML of the given element. Uses prettier under the
* hood.
*
* @param {HTMLElement} element
* @returns {string}
*/
export function debug(element) {
return prettier.format(element.outerHTML, {
parser: 'html',
});
}
1 change: 1 addition & 0 deletions packages/test-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"react-dom": "^16.9.0"
},
"dependencies": {
"prettier": "^1.17.0",
"resolve": "^1.12.0",
"tabbable": "^4.0.0"
},
Expand Down

0 comments on commit 4a4622c

Please sign in to comment.