Skip to content

Commit 64628e1

Browse files
committedJan 2, 2022
feat(treemap): add unit tests
1 parent ba6071a commit 64628e1

File tree

3 files changed

+73
-343
lines changed

3 files changed

+73
-343
lines changed
 

‎packages/treemap/src/TreeMapHtmlNode.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const NonMemoizedTreeMapHtmlNode = <Datum extends object>({
2323

2424
return (
2525
<animated.div
26+
data-testid={`node.${node.id}`}
2627
id={node.path.replace(/[^\w]/gi, '-')}
2728
style={{
2829
boxSizing: 'border-box',
@@ -56,6 +57,7 @@ const NonMemoizedTreeMapHtmlNode = <Datum extends object>({
5657
/>
5758
{showLabel && (
5859
<animated.span
60+
data-testid={`label.${node.id}`}
5961
style={{
6062
...theme.labels.text,
6163
position: 'absolute',
@@ -83,6 +85,7 @@ const NonMemoizedTreeMapHtmlNode = <Datum extends object>({
8385
)}
8486
{showParentLabel && (
8587
<animated.span
88+
data-testid={`parentLabel.${node.id}`}
8689
style={{
8790
...theme.labels.text,
8891
position: 'absolute',

‎packages/treemap/src/TreeMapNode.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const NonMemoizedTreeMapNode = <Datum extends object>({
2424
return (
2525
<animated.g transform={svgNodeTransform(animatedProps.x, animatedProps.y)}>
2626
<animated.rect
27+
data-testid={`node.${node.id}`}
2728
width={to(animatedProps.width, v => Math.max(v, 0))}
2829
height={to(animatedProps.height, v => Math.max(v, 0))}
2930
fill={node.fill ? node.fill : animatedProps.color}
@@ -37,6 +38,7 @@ const NonMemoizedTreeMapNode = <Datum extends object>({
3738
/>
3839
{showLabel && (
3940
<animated.text
41+
data-testid={`label.${node.id}`}
4042
textAnchor="middle"
4143
dominantBaseline="central"
4244
style={{
@@ -56,6 +58,7 @@ const NonMemoizedTreeMapNode = <Datum extends object>({
5658
)}
5759
{showParentLabel && (
5860
<animated.text
61+
data-testid={`parentLabel.${node.id}`}
5962
dominantBaseline="central"
6063
style={{
6164
...theme.labels.text,

‎packages/treemap/tests/TreeMap.test.tsx

+67-343
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,26 @@ import {
44
TreeMap,
55
TreeMapSvgProps,
66
DefaultTreeMapDatum,
7+
NodeProps,
8+
svgDefaultProps,
9+
TooltipProps,
710
// @ts-ignore
811
} from '../src'
912

13+
const nodes = [
14+
{ id: 'root', sum: 100 },
15+
{ id: 'A', value: 50 },
16+
{ id: 'B', sum: 50 },
17+
{ id: 'B.0', value: 25 },
18+
{ id: 'B.1', value: 25 },
19+
]
1020
const sampleData: DefaultTreeMapDatum = {
11-
id: 'root',
21+
...nodes[0],
1222
children: [
23+
nodes[1],
1324
{
14-
id: 'A',
15-
value: 50,
16-
},
17-
{
18-
id: 'B',
19-
children: [
20-
{
21-
id: 'B.0',
22-
value: 25,
23-
},
24-
{
25-
id: 'B.1',
26-
value: 25,
27-
},
28-
],
25+
...nodes[2],
26+
children: [nodes[3], nodes[4]],
2927
},
3028
],
3129
}
@@ -45,308 +43,81 @@ afterAll(() => {
4543
Globals.assign({ skipAnimation: false })
4644
})
4745

48-
it('should render a basic treemap chart', () => {})
46+
it('should render a basic treemap chart', () => {
47+
const wrapper = mount(<TreeMap {...baseProps} />)
4948

50-
/*
51-
it('should render a basic network chart', () => {
52-
const wrapper = mount(<Network {...baseProps} />)
53-
54-
sampleData.nodes.forEach(node => {
55-
const nodeElement = wrapper.find(`circle[data-testid='node.${node.id}']`)
49+
nodes.forEach(node => {
50+
const nodeElement = wrapper.find(`rect[data-testid='node.${node.id}']`)
5651
expect(nodeElement.exists()).toBeTruthy()
57-
})
5852

59-
sampleData.links.forEach(link => {
60-
const linkElement = wrapper.find(`line[data-testid='link.${link.source}.${link.target}']`)
61-
expect(linkElement.exists()).toBeTruthy()
53+
if (node.value !== undefined) {
54+
const label = wrapper.find(`text[data-testid='label.${node.id}']`)
55+
expect(label.exists()).toBeTruthy()
56+
expect(label.text()).toEqual(`${node.value}`)
57+
} else {
58+
const parentLabel = wrapper.find(`text[data-testid='parentLabel.${node.id}']`)
59+
expect(parentLabel.exists()).toBeTruthy()
60+
expect(parentLabel.text()).toEqual(node.id)
61+
}
6262
})
6363
})
6464

6565
describe('nodes', () => {
66-
it('static node color', () => {
67-
const color = 'rgba(255, 0, 255, 1)'
68-
const wrapper = mount(<Network {...baseProps} nodeColor={color} />)
69-
70-
sampleData.nodes.forEach(node => {
71-
expect(wrapper.find(`circle[data-testid='node.${node.id}']`).prop('fill')).toEqual(
72-
color
73-
)
74-
})
75-
})
76-
77-
it('static node size', () => {
78-
const size = 32
79-
const wrapper = mount(<Network {...baseProps} nodeSize={size} />)
80-
81-
sampleData.nodes.forEach(node => {
82-
expect(wrapper.find(`circle[data-testid='node.${node.id}']`).prop('r')).toEqual(
83-
size / 2
84-
)
85-
})
86-
})
87-
88-
it('dynamic node size', () => {
89-
const computeSize = (node: { id: string; index: number }) => 10 + node.index * 2
90-
const nodesWithIndex = sampleData.nodes.map((node, index) => ({
91-
...node,
92-
index,
93-
}))
94-
const wrapper = mount(
95-
<Network<{ id: string; index: number }, InputLink>
96-
{...baseProps}
97-
data={{
98-
nodes: nodesWithIndex,
99-
links: sampleData.links,
100-
}}
101-
nodeSize={computeSize}
102-
/>
103-
)
104-
105-
nodesWithIndex.forEach(node => {
106-
expect(wrapper.find(`circle[data-testid='node.${node.id}']`).prop('r')).toEqual(
107-
computeSize(node) / 2
108-
)
109-
})
110-
})
111-
11266
it('custom node component', () => {
113-
const CustomNode = ({ node }: NodeProps<InputNode>) => (
67+
const CustomNode = ({ node }: NodeProps<DefaultTreeMapDatum>) => (
11468
<g data-testid={`node.${node.id}.custom`} />
11569
)
70+
const wrapper = mount(<TreeMap {...baseProps} nodeComponent={CustomNode} />)
11671

117-
const wrapper = mount(<Network {...baseProps} nodeComponent={CustomNode} />)
118-
119-
sampleData.nodes.forEach(node => {
72+
nodes.forEach(node => {
12073
expect(wrapper.find(`g[data-testid='node.${node.id}.custom']`).exists()).toBeTruthy()
12174
})
12275
})
12376
})
12477

125-
describe('active/inactive nodes', () => {
126-
it('styles depending on nodes status', () => {
127-
const wrapper = mount(
128-
<Network {...baseProps} nodeSize={10} activeNodeSize={20} inactiveNodeSize={0} />
129-
)
130-
131-
sampleData.nodes.forEach(activeNode => {
132-
wrapper.find(`circle[data-testid='node.${activeNode.id}']`).simulate('mouseenter')
133-
134-
expect(
135-
wrapper.find(`circle[data-testid='node.${activeNode.id}']`).parent().prop('r').get()
136-
).toEqual(10)
137-
138-
sampleData.nodes
139-
.filter(node => node.id !== activeNode.id)
140-
.forEach(otherNode => {
141-
expect(
142-
wrapper
143-
.find(`circle[data-testid='node.${otherNode.id}']`)
144-
.parent()
145-
.prop('r')
146-
.get()
147-
).toEqual(0)
148-
})
149-
})
150-
})
151-
152-
it('default active node ids', () => {
153-
const activeIds = ['A', 'C']
154-
const wrapper = mount(
155-
<Network
156-
{...baseProps}
157-
nodeSize={10}
158-
activeNodeSize={20}
159-
inactiveNodeSize={0}
160-
defaultActiveNodeIds={activeIds}
161-
/>
162-
)
163-
164-
sampleData.nodes.forEach(node => {
165-
const nodeElement = wrapper.find(`circle[data-testid='node.${node.id}']`)
166-
167-
if (activeIds.includes(node.id)) {
168-
expect(nodeElement.prop('r')).toEqual(10)
169-
} else {
170-
expect(nodeElement.prop('r')).toEqual(0)
171-
}
172-
})
173-
})
174-
175-
it('ignored if non interactive', () => {
176-
const wrapper = mount(
177-
<Network
178-
{...baseProps}
179-
nodeSize={10}
180-
activeNodeSize={20}
181-
inactiveNodeSize={0}
182-
defaultActiveNodeIds={['B', 'D']}
183-
isInteractive={false}
184-
/>
185-
)
186-
187-
sampleData.nodes.forEach(node => {
188-
expect(wrapper.find(`circle[data-testid='node.${node.id}']`).prop('r')).toEqual(5)
189-
})
190-
})
191-
})
192-
193-
describe('links', () => {
194-
it('static link thickness', () => {
195-
const wrapper = mount(<Network {...baseProps} linkThickness={3} />)
196-
197-
sampleData.links.forEach(link => {
198-
expect(
199-
wrapper
200-
.find(`line[data-testid='link.${link.source}.${link.target}']`)
201-
.prop('strokeWidth')
202-
).toEqual(3)
203-
})
204-
})
205-
206-
it('dynamic link thickness', () => {
207-
const wrapper = mount(<Network {...baseProps} linkThickness={link => 1 + link.index} />)
208-
209-
sampleData.links.forEach((link, index) => {
210-
expect(
211-
wrapper
212-
.find(`line[data-testid='link.${link.source}.${link.target}']`)
213-
.prop('strokeWidth')
214-
).toEqual(1 + index)
215-
})
216-
})
217-
218-
it('static link color', () => {
219-
const color = 'rgba(255, 0, 0, 1)'
220-
const wrapper = mount(<Network {...baseProps} linkColor={color} />)
221-
222-
sampleData.links.forEach(link => {
223-
expect(
224-
wrapper
225-
.find(`line[data-testid='link.${link.source}.${link.target}']`)
226-
.prop('stroke')
227-
).toEqual(color)
228-
})
229-
})
230-
231-
it('dynamic link color', () => {
232-
const wrapper = mount(
233-
<Network {...baseProps} linkColor={link => `rgba(${link.index * 10}, 0, 0, 1)`} />
234-
)
235-
236-
sampleData.links.forEach((link, index) => {
237-
expect(
238-
wrapper
239-
.find(`line[data-testid='link.${link.source}.${link.target}']`)
240-
.prop('stroke')
241-
).toEqual(`rgba(${index * 10}, 0, 0, 1)`)
242-
})
243-
})
244-
245-
it('link color from source node color', () => {
246-
const color = 'rgba(125, 255, 125, 1)'
247-
const wrapper = mount(
248-
<Network
249-
{...baseProps}
250-
nodeColor={color}
251-
linkColor={{
252-
from: 'source.color',
253-
}}
254-
/>
255-
)
256-
257-
sampleData.links.forEach(link => {
258-
expect(
259-
wrapper
260-
.find(`line[data-testid='link.${link.source}.${link.target}']`)
261-
.prop('stroke')
262-
).toEqual(color)
263-
})
264-
})
265-
266-
it('link color from target node color', () => {
267-
const color = 'rgba(125, 125, 255, 1)'
268-
const wrapper = mount(
269-
<Network
270-
{...baseProps}
271-
nodeColor={color}
272-
linkColor={{
273-
from: 'target.color',
274-
}}
275-
/>
276-
)
277-
278-
sampleData.links.forEach(link => {
279-
expect(
280-
wrapper
281-
.find(`line[data-testid='link.${link.source}.${link.target}']`)
282-
.prop('stroke')
283-
).toEqual(color)
284-
})
285-
})
286-
287-
it('link blend mode', () => {
288-
const wrapper = mount(<Network {...baseProps} linkBlendMode="multiply" />)
289-
290-
sampleData.links.forEach(link => {
291-
expect(
292-
wrapper.find(`line[data-testid='link.${link.source}.${link.target}']`).prop('style')
293-
).toEqual({ mixBlendMode: 'multiply' })
294-
})
295-
})
296-
297-
it('custom link component', () => {
298-
const CustomLink = ({ link }: LinkProps<InputNode, InputLink>) => (
299-
<g data-testid={`link.${link.source.id}.${link.target.id}.custom`} />
300-
)
301-
302-
const wrapper = mount(<Network {...baseProps} linkComponent={CustomLink} />)
303-
304-
sampleData.links.forEach(link => {
305-
expect(
306-
wrapper.find(`g[data-testid='link.${link.source}.${link.target}.custom']`).exists()
307-
).toBeTruthy()
308-
})
309-
})
310-
})
311-
31278
describe('tooltip', () => {
31379
it('default node tooltip', () => {
314-
const wrapper = mount(<Network {...baseProps} />)
80+
const wrapper = mount(<TreeMap {...baseProps} />)
31581

316-
sampleData.nodes.forEach(node => {
317-
const nodeElement = wrapper.find(`circle[data-testid='node.${node.id}']`)
82+
nodes.forEach(node => {
83+
const nodeElement = wrapper.find(`rect[data-testid='node.${node.id}']`)
31884

31985
nodeElement.simulate('mouseenter')
32086

321-
const tooltip = wrapper.find(svgDefaultProps.nodeTooltip).childAt(0).childAt(0)
87+
const tooltip = wrapper.find(svgDefaultProps.tooltip).childAt(0).childAt(0)
32288
expect(tooltip.exists()).toBeTruthy()
323-
expect(tooltip.text()).toEqual(node.id)
89+
90+
if (node.value !== undefined) {
91+
expect(tooltip.text()).toEqual(`${node.id}: ${node.value}`)
92+
} else {
93+
expect(tooltip.text()).toEqual(`${node.id}: ${node.sum}`)
94+
}
32495

32596
nodeElement.simulate('mouseleave')
326-
expect(wrapper.find(svgDefaultProps.nodeTooltip).children()).toHaveLength(0)
97+
expect(wrapper.find(svgDefaultProps.tooltip).children()).toHaveLength(0)
32798
})
32899
})
329100

330101
it('disabled if non interactive', () => {
331-
const wrapper = mount(<Network {...baseProps} isInteractive={false} />)
102+
const wrapper = mount(<TreeMap {...baseProps} isInteractive={false} />)
332103

333-
sampleData.nodes.forEach(node => {
334-
const nodeElement = wrapper.find(`circle[data-testid='node.${node.id}']`)
104+
nodes.forEach(node => {
105+
const nodeElement = wrapper.find(`rect[data-testid='node.${node.id}']`)
335106

336107
nodeElement.simulate('mouseenter')
337108

338-
expect(wrapper.find(svgDefaultProps.nodeTooltip).children()).toHaveLength(0)
109+
expect(wrapper.find(svgDefaultProps.tooltip).children()).toHaveLength(0)
339110
})
340111
})
341112

342-
it('custom node tooltip', () => {
343-
const CustomTooltip = ({ node }: { node: ComputedNode<InputNode> }) => (
113+
it('custom tooltip', () => {
114+
const CustomTooltip = ({ node }: TooltipProps<DefaultTreeMapDatum>) => (
344115
<div>Custom: {node.id}</div>
345116
)
346-
const wrapper = mount(<Network {...baseProps} nodeTooltip={CustomTooltip} />)
117+
const wrapper = mount(<TreeMap {...baseProps} tooltip={CustomTooltip} />)
347118

348-
sampleData.nodes.forEach(node => {
349-
const nodeElement = wrapper.find(`circle[data-testid='node.${node.id}']`)
119+
nodes.forEach(node => {
120+
const nodeElement = wrapper.find(`rect[data-testid='node.${node.id}']`)
350121

351122
nodeElement.simulate('mouseenter')
352123

@@ -363,10 +134,10 @@ describe('tooltip', () => {
363134
describe('interactivity', () => {
364135
it('onClick', () => {
365136
const onClick = jest.fn()
366-
const wrapper = mount(<Network {...baseProps} onClick={onClick} />)
137+
const wrapper = mount(<TreeMap {...baseProps} onClick={onClick} />)
367138

368-
sampleData.nodes.forEach(node => {
369-
wrapper.find(`circle[data-testid='node.${node.id}']`).simulate('click')
139+
nodes.forEach(node => {
140+
wrapper.find(`rect[data-testid='node.${node.id}']`).simulate('click')
370141

371142
expect(onClick).toHaveBeenCalledTimes(1)
372143
const [datum] = onClick.mock.calls[0]
@@ -378,10 +149,10 @@ describe('interactivity', () => {
378149

379150
it('onMouseEnter', () => {
380151
const onMouseEnter = jest.fn()
381-
const wrapper = mount(<Network {...baseProps} onMouseEnter={onMouseEnter} />)
152+
const wrapper = mount(<TreeMap {...baseProps} onMouseEnter={onMouseEnter} />)
382153

383-
sampleData.nodes.forEach(node => {
384-
wrapper.find(`circle[data-testid='node.${node.id}']`).simulate('mouseenter')
154+
nodes.forEach(node => {
155+
wrapper.find(`rect[data-testid='node.${node.id}']`).simulate('mouseenter')
385156

386157
expect(onMouseEnter).toHaveBeenCalledTimes(1)
387158
const [datum] = onMouseEnter.mock.calls[0]
@@ -393,10 +164,10 @@ describe('interactivity', () => {
393164

394165
it('onMouseMove handler', () => {
395166
const onMouseMove = jest.fn()
396-
const wrapper = mount(<Network {...baseProps} onMouseMove={onMouseMove} />)
167+
const wrapper = mount(<TreeMap {...baseProps} onMouseMove={onMouseMove} />)
397168

398-
sampleData.nodes.forEach(node => {
399-
wrapper.find(`circle[data-testid='node.${node.id}']`).simulate('mousemove')
169+
nodes.forEach(node => {
170+
wrapper.find(`rect[data-testid='node.${node.id}']`).simulate('mousemove')
400171

401172
expect(onMouseMove).toHaveBeenCalledTimes(1)
402173
const [datum] = onMouseMove.mock.calls[0]
@@ -408,10 +179,10 @@ describe('interactivity', () => {
408179

409180
it('onMouseLeave handler', () => {
410181
const onMouseLeave = jest.fn()
411-
const wrapper = mount(<Network {...baseProps} onMouseLeave={onMouseLeave} />)
182+
const wrapper = mount(<TreeMap {...baseProps} onMouseLeave={onMouseLeave} />)
412183

413-
sampleData.nodes.forEach(node => {
414-
wrapper.find(`circle[data-testid='node.${node.id}']`).simulate('mouseleave')
184+
nodes.forEach(node => {
185+
wrapper.find(`rect[data-testid='node.${node.id}']`).simulate('mouseleave')
415186

416187
expect(onMouseLeave).toHaveBeenCalledTimes(1)
417188
const [datum] = onMouseLeave.mock.calls[0]
@@ -428,7 +199,7 @@ describe('interactivity', () => {
428199
const onMouseLeave = jest.fn()
429200

430201
const wrapper = mount(
431-
<Network
202+
<TreeMap
432203
{...baseProps}
433204
onClick={onClick}
434205
onMouseEnter={onMouseEnter}
@@ -438,8 +209,8 @@ describe('interactivity', () => {
438209
/>
439210
)
440211

441-
sampleData.nodes.forEach(node => {
442-
const nodeElement = wrapper.find(`circle[data-testid='node.${node.id}']`)
212+
nodes.forEach(node => {
213+
const nodeElement = wrapper.find(`rect[data-testid='node.${node.id}']`)
443214

444215
nodeElement.simulate('mouseenter')
445216
expect(onMouseEnter).not.toHaveBeenCalled()
@@ -457,68 +228,22 @@ describe('interactivity', () => {
457228
})
458229

459230
describe('layers', () => {
460-
it('custom order', () => {
461-
const wrapper = mount(<Network {...baseProps} layers={['nodes', 'links']} />)
462-
463-
const layers = wrapper.find('svg > g').children()
464-
expect(layers.at(0).is('NetworkNodes')).toBeTruthy()
465-
expect(layers.at(1).is('NetworkLinks')).toBeTruthy()
466-
})
467-
468231
it('custom layer', () => {
469232
const CustomLayer = () => null
470-
const wrapper = mount(<Network {...baseProps} layers={[CustomLayer]} />)
233+
const wrapper = mount(<TreeMap {...baseProps} layers={[CustomLayer]} />)
471234

472235
const customLayer = wrapper.find(CustomLayer)
473236
expect(customLayer.exists()).toBeTruthy()
474237

475238
const customLayerProps = customLayer.props()
476239
expect(customLayerProps).toHaveProperty('nodes')
477-
expect(customLayerProps).toHaveProperty('links')
478-
expect(customLayerProps).toHaveProperty('activeNodeIds')
479-
expect(customLayerProps).toHaveProperty('setActiveNodeIds')
480-
})
481-
})
482-
483-
describe('annotations', () => {
484-
it('circle annotation using id', () => {
485-
const annotatedNodeId = 'C'
486-
const wrapper = mount(
487-
<Network
488-
{...baseProps}
489-
annotations={[
490-
{
491-
type: 'circle',
492-
match: {
493-
id: annotatedNodeId,
494-
},
495-
note: 'Note',
496-
noteX: 160,
497-
noteY: 36,
498-
},
499-
]}
500-
/>
501-
)
502-
503-
const annotation = wrapper.find(Annotation)
504-
expect(annotation.exists()).toBeTruthy()
505-
506-
const annotatedNode = wrapper.find(`circle[data-testid='node.${annotatedNodeId}']`)
507-
const [nodeX, nodeY] = Array.from(
508-
annotatedNode.prop('transform').match(/translate\(([0-9.]+),([0-9.]+)\)/)
509-
)
510-
.slice(1)
511-
.map(Number)
512-
513-
expect(annotation.find('circle').first().prop('cx')).toEqual(nodeX)
514-
expect(annotation.find('circle').first().prop('cy')).toEqual(nodeY)
515240
})
516241
})
517242

518243
describe('accessibility', () => {
519244
it('aria properties', () => {
520245
const wrapper = mount(
521-
<Network
246+
<TreeMap
522247
{...baseProps}
523248
ariaLabel="AriaLabel"
524249
ariaLabelledBy="AriaLabelledBy"
@@ -533,4 +258,3 @@ describe('accessibility', () => {
533258
expect(svg.prop('aria-describedby')).toBe('AriaDescribedBy')
534259
})
535260
})
536-
*/

0 commit comments

Comments
 (0)
Please sign in to comment.