react-d3-dag
Version:
React component to create interactive D3 directed acyclic graphs (DAGs)
214 lines (183 loc) • 7.28 kB
JavaScript
import React from 'react';
import { shallow, mount } from 'enzyme';
import Node from './index.tsx';
describe('<Node />', () => {
const data = {
name: 'mockNode',
__rd3dag: {
id: 'abc123',
},
};
const dagNode = {
data,
depth: 1,
height: 0,
parent: null,
x: -200,
y: 200,
};
const mockProps = {
data,
dagNode,
nodeSize: {
x: 123,
y: 321,
},
position: {
x: 111,
y: 222,
},
depth: 3,
nodeElement: {
tag: 'circle',
baseProps: {
r: 10,
},
},
attributes: {
testkeyA: 'testvalA',
testKeyB: 'testvalB',
},
orientation: 'horizontal',
parent: {
x: 999,
y: 888,
},
transitionDuration: 500,
onClick: () => {},
onMouseOver: () => {},
onMouseOut: () => {},
centerNode: () => {},
subscriptions: {},
allowForeignObjects: false,
};
jest.spyOn(Node.prototype, 'applyTransform');
// Clear method spies on prototype after each test
afterEach(() => jest.clearAllMocks());
it('has the correct `id` attribute value', () => {
const renderedComponent = shallow(<Node {...mockProps} />);
expect(renderedComponent.find('g').first().prop('id')).toBe(data.__rd3dag.id);
});
it('applies correct base className if `data.children` is defined and not empty', () => {
const leafNodeComponent = shallow(<Node {...mockProps} />);
const leafNodeComponentWithEmptyChildren = shallow(
<Node {...mockProps} data={{ ...data, children: [] }} />
);
const nodeComponent = shallow(
<Node {...mockProps} data={{ ...data, children: [{ name: 'leaf' }] }} />
);
expect(leafNodeComponent.find('g').first().prop('className')).toBe('rd3dag-leaf-node');
expect(leafNodeComponentWithEmptyChildren.find('g').first().prop('className')).toBe(
'rd3dag-leaf-node'
);
expect(nodeComponent.find('g').first().prop('className')).toBe('rd3dag-node');
});
it('applies `nodeClassName` if defined', () => {
const fixture = 'additionalNodeClass';
const leafNodeComponent = shallow(<Node {...mockProps} nodeClassName={fixture} />);
expect(leafNodeComponent.find('g').first().prop('className')).toBe(
['rd3dag-leaf-node', fixture].join(' ')
);
});
it.skip('applies correct `transform` prop based on its `orientation`', () => {
const horizontalTransform = `translate(${mockProps.parent.y},${mockProps.parent.x})`;
const verticalTransform = `translate(${mockProps.parent.x},${mockProps.parent.y})`;
const horizontalComponent = shallow(<Node {...mockProps} />);
const verticalComponent = shallow(<Node {...mockProps} orientation="vertical" />);
expect(horizontalComponent.find('g').first().prop('transform')).toBe(horizontalTransform);
expect(verticalComponent.find('g').first().prop('transform')).toBe(verticalTransform);
});
describe('Events', () => {
it('handles onNodeToggle and passes its nodeId to handler', () => {
const onNodeToggleSpy = jest.fn();
const mockEvt = { mock: 'event' };
const renderedComponent = shallow(
<Node {...mockProps} onNodeToggle={onNodeToggleSpy} onNodeClick={() => {}} />
);
renderedComponent.find('circle').simulate('click', mockEvt);
expect(onNodeToggleSpy).toHaveBeenCalledTimes(1);
expect(onNodeToggleSpy).toHaveBeenCalledWith(data.__rd3dag.id);
});
it('handles onNodeClick events and passes its `dagNode` representation & event object to handler', () => {
const onClickSpy = jest.fn();
const mockEvt = { mock: 'event' };
const renderedComponent = shallow(
<Node {...mockProps} onNodeToggle={() => {}} onNodeClick={onClickSpy} />
);
renderedComponent.find('circle').simulate('click', mockEvt);
expect(onClickSpy).toHaveBeenCalledTimes(1);
expect(onClickSpy).toHaveBeenCalledWith(mockProps.dagNode, expect.objectContaining(mockEvt));
});
it('handles onNodeMouseOver events and passes its `dagNode` representation & event object to handler', () => {
const onMouseOverSpy = jest.fn();
const mockEvt = { mock: 'event' };
const renderedComponent = shallow(<Node {...mockProps} onNodeMouseOver={onMouseOverSpy} />);
renderedComponent.find('circle').simulate('mouseover', mockEvt);
expect(onMouseOverSpy).toHaveBeenCalledTimes(1);
expect(onMouseOverSpy).toHaveBeenCalledWith(
mockProps.dagNode,
expect.objectContaining(mockEvt)
);
});
it('handles onNodeMouseOut events and passes its `dagNode` representation & event object to handler', () => {
const onMouseOutSpy = jest.fn();
const mockEvt = { mock: 'event' };
const renderedComponent = shallow(<Node {...mockProps} onNodeMouseOut={onMouseOutSpy} />);
renderedComponent.find('circle').simulate('mouseout', mockEvt);
expect(onMouseOutSpy).toHaveBeenCalledTimes(1);
expect(onMouseOutSpy).toHaveBeenCalledWith(
mockProps.dagNode,
expect.objectContaining(mockEvt)
);
});
});
it('applies its own x/y coords on `transform` once mounted', () => {
const fixture = `translate(${mockProps.position.y},${mockProps.position.x})`;
const renderedComponent = mount(<Node {...mockProps} />);
expect(renderedComponent.instance().applyTransform).toHaveBeenCalledWith(
fixture,
mockProps.transitionDuration
);
});
describe('Update Positioning', () => {
it('updates its position if `data.x` or `data.y` changes', () => {
const updatedProps = {
...mockProps,
x: 1,
y: 2,
data: {
...mockProps.data,
},
};
const initialTransform = `translate(${mockProps.position.y},${mockProps.position.x})`;
const updatedTransform = `translate(${updatedProps.position.y},${updatedProps.position.x})`;
const renderedComponent = mount(<Node {...mockProps} />);
expect(renderedComponent.instance().applyTransform).toHaveBeenCalledWith(
initialTransform,
mockProps.transitionDuration
);
renderedComponent.setProps(updatedProps);
expect(renderedComponent.instance().applyTransform).toHaveBeenCalledWith(
updatedTransform,
mockProps.transitionDuration
);
});
it('updates its position if `orientation` changes', () => {
const thisProps = { ...mockProps, shouldTranslateToOrigin: true, orientation: 'horizontal' };
delete thisProps.parent;
const renderedComponent = mount(<Node {...thisProps} />);
const nextProps = { ...thisProps, orientation: 'vertical' };
expect(
renderedComponent.instance().shouldNodeTransform(renderedComponent.props(), nextProps)
).toBe(true);
});
it('updates its position if any subscribed top-level props change', () => {
const subscriptions = { x: 12, y: 10, initialDepth: undefined };
const renderedComponent = mount(<Node {...mockProps} subscriptions={subscriptions} />);
const nextProps = { ...mockProps, subscriptions: { ...subscriptions, initialDepth: 1 } };
expect(
renderedComponent.instance().shouldNodeTransform(renderedComponent.props(), nextProps)
).toBe(true);
});
});
});