Skip to content

Commit 5a8c187

Browse files
committed
Adds tests
1 parent 2cc7e92 commit 5a8c187

File tree

4 files changed

+317
-30
lines changed

4 files changed

+317
-30
lines changed

src/components/TreeViewNode.vue

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@
55
-->
66
<li :id="nodeId" class="tree-view-node">
77
<div class="tree-view-node-self"
8-
@click="onClick"
9-
@dblclick="onDblclick">
8+
@click="$_treeViewNode_onClick"
9+
@dblclick="$_treeViewNode_onDblclick">
1010

1111
<!-- Expander -->
12-
<button type="button"
12+
<button :id="expanderId"
13+
type="button"
1314
v-if="model.children.length > 0 && model.expandable"
1415
class="tree-view-node-self-expander"
1516
:class="{ 'tree-view-node-self-expanded': model.state.expanded }"
16-
@click="onExpandedChange">
17+
@click="$_treeViewNode_onExpandedChange">
1718
<i class="tree-view-node-self-expanded-indicator"></i></button>
1819
<span v-else class="tree-view-node-self-spacer"></span>
1920

@@ -25,7 +26,7 @@
2526
class="tree-view-node-self-checkbox"
2627
type="checkbox"
2728
v-model="model.state.checked"
28-
@change="onCheckedChange" />
29+
@change="$_treeViewNode_onCheckedChange" />
2930
{{ model.label }}
3031
</label>
3132

@@ -34,8 +35,8 @@
3435
</div>
3536

3637
<!-- Children -->
37-
<ul v-show="model.state.expanded"
38-
v-if="model.children.length > 0 && model.expandable"
38+
<ul v-show="model.state.expanded"
39+
v-if="model.children.length > 0 && model.expandable"
3940
class="tree-view-node-children">
4041
<TreeViewNode v-for="nodeModel in model.children"
4142
:key="nodeModel.id"
@@ -63,7 +64,7 @@
6364
if (typeof value.id !== 'number' && typeof value.id !== 'string') {
6465
console.error("model.id is required and must be a number or string.");
6566
return false;
66-
}
67+
}
6768
else if(typeof value.label !== 'string') {
6869
console.error("model.label is required and must be a string.");
6970
return false;
@@ -93,6 +94,9 @@
9394
},
9495
checkboxId() {
9596
return this.nodeId ? `${this.nodeId}-cbx` : null;
97+
},
98+
expanderId() {
99+
return this.nodeId ? `${this.nodeId}-exp` : null;
96100
}
97101
},
98102
methods: {
@@ -105,48 +109,46 @@
105109
if (!Array.isArray(this.model.children)) {
106110
this.model.children = [];
107111
}
108-
if (typeof this.model.expandable !== 'boolean') {
109-
this.model.expandable = true;
110-
}
111112
if (typeof this.model.checkable !== 'boolean') {
112113
this.model.checkable = false;
113114
}
115+
if (typeof this.model.expandable !== 'boolean') {
116+
this.model.expandable = true;
117+
}
114118
if (typeof this.model.selectable !== 'boolean') {
115119
this.model.selectable = false;
116120
}
117121
if (this.model.state === null || typeof this.model.state !== 'object') {
118122
this.model.state = {};
119123
}
120-
if (typeof this.model.state.expanded !== 'boolean') {
121-
this.model.state.expanded = false;
122-
}
123124
if (typeof this.model.state.checked !== 'boolean') {
124125
this.model.state.checked = false;
125126
}
127+
if (typeof this.model.state.expanded !== 'boolean') {
128+
this.model.state.expanded = false;
129+
}
126130
if (typeof this.model.state.selected !== 'boolean') {
127131
this.model.state.selected = false;
128132
}
129133
},
130-
onExpandedChange(event) {
131-
if (this.model.children.length > 0) {
132-
this.model.state.expanded = !this.model.state.expanded;
133-
this.$emit('treeViewNodeExpandedChange', this.model, event);
134-
}
134+
$_treeViewNode_onCheckedChange(event) {
135+
this.$emit('treeViewNodeCheckedChange', this.model, event);
136+
},
137+
$_treeViewNode_onExpandedChange(event) {
138+
this.model.state.expanded = !this.model.state.expanded;
139+
this.$emit('treeViewNodeExpandedChange', this.model, event);
135140
},
136-
onClick(event) {
141+
$_treeViewNode_onClick(event) {
137142
// Don't fire this if the target is the checkbox or expander, which have their own events
138143
if (!event.target.matches(".tree-view-node-self-checkbox, .tree-view-node-self-expander")) {
139144
this.$emit('treeViewNodeClick', this.model, event);
140145
}
141146
},
142-
onDblclick(event) {
147+
$_treeViewNode_onDblclick(event) {
143148
// Don't fire this if the target is the checkbox or expander, which have their own events
144149
if (!event.target.matches(".tree-view-node-self-checkbox, .tree-view-node-self-expander")) {
145150
this.$emit('treeViewNodeDblclick', this.model, event);
146151
}
147-
},
148-
onCheckedChange(event) {
149-
this.$emit('treeViewNodeCheckedChange', this.model, event);
150152
}
151153
},
152154
};
@@ -174,7 +176,7 @@
174176
175177
i.tree-view-node-self-expanded-indicator {
176178
font-style: normal;
177-
179+
178180
&::before {
179181
content: '+';
180182
}

tests/data/node-generator.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* Generates nodes, one per array element that is not an array.
3+
* Array elements that are arrays recursively generate child nodes
4+
* of the last created node.
5+
*
6+
* The node spec's node string should be in the format:
7+
* [eE]?[cC]?[sS]?
8+
* The presence of e, c or s indicate the node is expandable, checkable, or selectable
9+
* respectively. If it is capitalized, then the related state should be True.
10+
*
11+
* @param {Array<String, Array>} nodeSpec The node specification array.
12+
* @param {String} baseId The base string used in the node IDs.
13+
*/
14+
export function generateNodes(nodeSpec, baseId = "") {
15+
let nodes = [];
16+
let prevNode = null;
17+
18+
nodeSpec.forEach(function (item, index) {
19+
if (Array.isArray(item)) {
20+
if (prevNode === null) {
21+
return;
22+
}
23+
prevNode.children = generateNodes(item, prevNode.id);
24+
}
25+
else {
26+
prevNode = {
27+
id: baseId + 'n' + index,
28+
label: 'Node ' + index,
29+
checkable: item.toLowerCase().indexOf('c') > -1,
30+
expandable: item.toLowerCase().indexOf('e') > -1,
31+
selectable: item.toLowerCase().indexOf('s') > -1,
32+
state: {
33+
checked: item.indexOf('C') > -1,
34+
expanded: item.indexOf('E') > -1,
35+
selected: item.indexOf('S') > -1
36+
},
37+
children: []
38+
};
39+
40+
nodes.push(prevNode);
41+
}
42+
});
43+
44+
return nodes;
45+
}

tests/unit/TreeView.spec.js

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,60 @@
11
import { expect } from 'chai';
2-
import { shallowMount } from '@vue/test-utils';
2+
import { createLocalVue, shallowMount } from '@vue/test-utils';
33
import TreeView from '../../src/components/TreeView.vue';
4+
import { generateNodes } from '../data/node-generator.js';
5+
6+
const localVue = createLocalVue();
7+
8+
const defaultPropsData = { model: [] };
9+
10+
function createWrapper(customPropsData, customAttrs) {
11+
return shallowMount(TreeView, {
12+
propsData: customPropsData || defaultPropsData,
13+
localVue,
14+
attrs: customAttrs
15+
});
16+
}
417

518
describe('TreeView.vue', () => {
6-
it('renders props.msg when passed', () => {
7-
const wrapper = shallowMount(TreeView, {
8-
propsData: { model: [] },
19+
20+
let wrapper = null;
21+
22+
afterEach(() => {
23+
wrapper.vm.$destroy();
24+
wrapper = null;
25+
});
26+
27+
describe('when on an element with an ID', () => {
28+
29+
beforeEach(() => {
30+
wrapper = createWrapper(null, { id: 'my-id' });
31+
});
32+
33+
it('has a uniqueId of the root element ID', () => {
34+
expect(wrapper.vm.uniqueId).to.equal(wrapper.attributes('id'));
35+
});
36+
});
37+
38+
describe('when on an element without an ID', () => {
39+
40+
beforeEach(() => {
41+
wrapper = createWrapper();
42+
});
43+
44+
it('has a null uniqueId', () => {
45+
expect(wrapper.vm.uniqueId).to.be.null;
46+
});
47+
});
48+
49+
describe('when getChecked() is called', () => {
50+
51+
beforeEach(() => {
52+
wrapper = createWrapper({ model: generateNodes(['ecs', 'eCs', ['eCs', 'ecs']]) });
53+
});
54+
55+
it('should return checked nodes', () => {
56+
let nodes = wrapper.vm.getChecked();
57+
expect(nodes.length).to.equal(2);
958
});
10-
expect(wrapper.text()).to.include('');
1159
});
1260
});

0 commit comments

Comments
 (0)