import _, { forEach, has, noop } from 'lodash';
import React from 'react';
import assert from 'assert';
import sinon from 'sinon';
import { mount, shallow } from 'enzyme';
import { common } from '../../util/generic-tests';
import { ToolTipDumb as ToolTip } from './ToolTip';
import ContextMenu from '../ContextMenu/ContextMenu';
import CloseIcon from '../Icon/CloseIcon/CloseIcon';
import { MOSTLY_STABLE_DELAY } from '../../util/constants';
const { Target, Title, Body } = ToolTip;
describe('ToolTip', () => {
common(ToolTip);
describe('render', () => {
it('should render a ContextMenu', () => {
const wrapper = shallow(
ToolTip TargetTitle
Body
);
assert.equal(wrapper.find(ContextMenu).length, 1);
});
});
describe('props', () => {
describe('children', () => {
it('should not render any direct child elements which are not ToolTip-specific', () => {
const wrapper = shallow(
ToolTip TargetTitle
Body
header
);
assert.equal(
wrapper.find('button').length,
0,
'must not render button'
);
assert.equal(wrapper.find('h1').length, 0, 'must not render h1');
});
});
describe('className', () => {
describe('FlyOut', () => {
let wrapper: any;
afterEach(() => {
if (wrapper) {
wrapper.unmount();
}
});
it('should pass the className prop thru to the FlyOut (portal) element', () => {
wrapper = mount(
Target
Body
);
const flyOutClassName = (document as any).querySelector(
'.lucid-ToolTip-FlyOut.lucid-ContextMenu-FlyOut'
).className;
assert(
_.includes(flyOutClassName, 'MyToolTip'),
'must include `MyToolTip`'
);
});
});
});
describe('isCloseable', () => {
describe('true', () => {
it('should render a `CloseIcon`', () => {
const wrapper = shallow(
Target
Body
);
assert.equal(
wrapper.find(CloseIcon).length,
1,
'must include a CloseIcon'
);
});
});
describe('false', () => {
it('should not render a `CloseIcon`', () => {
const wrapper = shallow(
Target
Body
);
assert.equal(
wrapper.find(CloseIcon).length,
0,
'must not include a CloseIcon'
);
});
});
});
describe('flyOutStyle', () => {
it('should pass flyOutStyle to the underlying ContextMenu FlyOut with a default maxWidth', () => {
const wrapper = shallow(
Target
Body
);
const flyOutStyle = wrapper.find(ContextMenu.FlyOut).prop('style');
assert.deepEqual(
flyOutStyle,
{ flex: 2, maxWidth: 200 },
'must have flex:2 and maxWidth: 200'
);
});
});
describe('flyOutMaxWidth', () => {
it('should pass maxWidth to the underlying ContextMenu FlyOut style', () => {
const wrapper = shallow(
Target
Body
);
const flyOutStyle = wrapper.find(ContextMenu.FlyOut).prop('style');
assert.deepEqual(
flyOutStyle,
{ flex: 2, maxWidth: 100 },
'must have flex:2 and maxWidth: 400'
);
});
});
describe('direction', () => {
it('should pass direction to the underlying ContextMenu', () => {
const wrapper = shallow(
Target
Body
);
assert.equal(
wrapper.find(ContextMenu).prop('direction'),
'right',
'must be "right"'
);
});
});
describe('alignment', () => {
it('should pass alignment center to the underlying ContextMenu', () => {
const wrapper = shallow(
Target
Body
);
assert.equal(
wrapper.find(ContextMenu).prop('alignment'),
'center',
'must be "center"'
);
});
describe('center', () => {
it('should pass getAlignmentOffset with correct closed over values', () => {
const wrapper = shallow(
Target
Body
);
const getAlignmentOffset = wrapper
.find(ContextMenu)
.prop('getAlignmentOffset');
assert.equal(getAlignmentOffset(400), 0, 'must be 0');
});
});
describe('start', () => {
it('should pass getAlignmentOffset with correct closed over values', () => {
const wrapper = shallow(
Target
Body
);
const getAlignmentOffset = wrapper
.find(ContextMenu)
.prop('getAlignmentOffset');
assert.equal(getAlignmentOffset(400), 177.5, 'must be 177.5');
});
});
describe('end', () => {
it('should pass getAlignmentOffset with correct closed over values', () => {
const wrapper = shallow(
Target
Body
);
const getAlignmentOffset = wrapper
.find(ContextMenu)
.prop('getAlignmentOffset');
assert.equal(getAlignmentOffset(400), -177.5, 'must be -177.5');
});
});
});
describe('isExpanded', () => {
it('should pass isExpanded=true to the underlying ContextMenu component thru props', () => {
const wrapper = shallow(
Target
Body
);
assert(
wrapper.find(ContextMenu).prop('isExpanded'),
'isExpanded must be true'
);
});
it('should be false by default', () => {
const wrapper = shallow(
Target
Body
);
assert(
!wrapper.find(ContextMenu).prop('isExpanded'),
'isExpanded must be false'
);
});
});
describe('onMouseOver', () => {
it('should call onMouseOver on target mouseover', () => {
const spy = sinon.spy();
const wrapper = shallow(
Target
Body
);
wrapper.find(ContextMenu).shallow().find('span').simulate('mouseOver');
assert(spy.calledOnce, 'onMouseOver must be called once');
});
});
describe('onMouseOut', () => {
it('should call onMouseOut when cursor leaves target', (done) => {
const spy = sinon.spy();
const wrapper = shallow(
Target
Body
);
const root = wrapper.find('ContextMenu').shallow().find('span');
root.simulate('mouseOver');
root.simulate('mouseOut');
// wait for timeout
_.delay(() => {
assert(spy.calledOnce, 'onMouseOut must be called once');
done();
}, MOSTLY_STABLE_DELAY * 2);
});
it('should not call onMouseOut if cursor enters FlyOut', (done) => {
const spy = sinon.spy();
const wrapper: any = shallow(
Target
Body
);
const root = wrapper.find(ContextMenu).shallow().find('span');
root.simulate('mouseOver');
// simulate click hover over FlyOut Portal
wrapper.find(ContextMenu.FlyOut).prop('onMouseOver')();
root.simulate('mouseOut');
// wait for timeout
_.delay(() => {
assert(!spy.called, 'onMouseOut must not be called');
done();
}, MOSTLY_STABLE_DELAY * 2);
});
});
describe('portalId', () => {
it('should pass portalId to underlying ContextMenu', () => {
const wrapper = shallow(
Target
Body
);
assert.equal(
wrapper.find(ContextMenu).prop('portalId'),
'foo-portal-id',
'must equal "foo-portal-id"'
);
});
});
describe('pass throughs', () => {
let wrapper: any;
const defaultProps = ToolTip.defaultProps;
beforeEach(() => {
const props = {
...defaultProps,
className: 'wut',
isCloseable: true,
isLight: true,
onClose: noop,
flyOutStyle: { marginLeft: 10 },
flyOutMaxWidth: 1000,
direction: 'right' as any,
alignment: 'end' as any,
isExpanded: true,
onMouseOver: noop,
onMouseOut: noop,
portalId: '11',
Title: 'Test Title',
Body:
ToolTip Test Body.,
Target:
Example Test Target
,
style: { marginRight: 10 },
initialState: { test: true },
callbackId: 1,
'data-testid': 10,
};
wrapper = shallow();
});
afterEach(() => {
wrapper.unmount();
});
it('passes through - to the root element - props not defined in `propTypes`', () => {
const rootProps = wrapper.find('.lucid-ToolTip').props();
expect(wrapper.first().prop(['className'])).toContain('wut');
expect(wrapper.first().prop(['style'])).toMatchObject({
marginRight: 10,
});
expect(wrapper.first().prop(['data-testid'])).toBe(10);
expect(wrapper.first().prop(['callbackId'])).toBe(1);
// 'className' and 'style' are plucked from the pass through object
// but still appear becuase they are also directly passed to the root element as a prop
// callbackId is passed through because the ContextMenu root element is not a DOM element
forEach(
[
'className',
'alignment',
'direction',
'directonOffset',
'getAlignmentOffset',
'isExpanded',
'style',
'portalId',
'data-testid',
'onMouseOver',
'onMouseOut',
'children',
'minWidthOffset',
'onClickOut',
'callbackId',
],
(prop) => {
expect(has(rootProps, prop)).toBe(true);
}
);
});
it('omits - from the root element `initialState` and the props defined in `propTypes`', () => {
const rootProps = wrapper.find('.lucid-ToolTip').props();
forEach(
[
'isCloseable',
'isLight',
'onClose',
'flyOutStyle',
'flyOutMaxWidth',
'Title',
'Body',
'Target',
'initialState',
],
(prop) => {
expect(has(rootProps, prop)).toBe(false);
}
);
});
});
});
});