Skip to content

Commit c1279bd

Browse files
committed
change to object representation
1 parent df01c9e commit c1279bd

File tree

3 files changed

+186
-39
lines changed

3 files changed

+186
-39
lines changed

src/material/tree/testing/shared.spec.ts

Lines changed: 99 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -100,32 +100,111 @@ export function runHarnessTests(
100100
it ('should correctly get tree structure', async () => {
101101
const trees = await loader.getAllHarnesses(treeHarness);
102102
const flatTree = trees[0];
103-
expect(await flatTree.getStructureText()).toEqual(
104-
`Flat Group 1
105-
Flat Group 2`);
103+
104+
expect(await flatTree.getTreeStructure()).toEqual({
105+
children: [
106+
{text: 'Flat Group 1'},
107+
{text: 'Flat Group 2'}
108+
]
109+
});
110+
111+
const firstGroup = (await flatTree.getNodes({text: /Flat Group 1/}))[0];
112+
await firstGroup.expand();
113+
114+
expect(await flatTree.getTreeStructure()).toEqual({
115+
children: [
116+
{
117+
text: 'Flat Group 1',
118+
children: [
119+
{text: 'Flat Leaf 1.1'},
120+
{text: 'Flat Leaf 1.2'},
121+
{text: 'Flat Leaf 1.3'}
122+
]
123+
},
124+
{text: 'Flat Group 2'}
125+
]
126+
});
127+
128+
const secondGroup = (await flatTree.getNodes({text: /Flat Group 2/}))[0];
129+
await secondGroup.expand();
130+
131+
expect(await flatTree.getTreeStructure()).toEqual({
132+
children: [
133+
{
134+
text: 'Flat Group 1',
135+
children: [
136+
{text: 'Flat Leaf 1.1'},
137+
{text: 'Flat Leaf 1.2'},
138+
{text: 'Flat Leaf 1.3'}
139+
]
140+
},
141+
{
142+
text: 'Flat Group 2',
143+
children: [
144+
{text: 'Flat Group 2.1'},
145+
]
146+
}
147+
]
148+
});
106149
});
107150

108151
it('should correctly get tree structure', async () => {
109152
const trees = await loader.getAllHarnesses(treeHarness);
110153
const nestedTree = trees[1];
111-
expect(await nestedTree.getStructureText()).toEqual(
112-
`Nested Group 1
113-
\tNested Leaf 1.1
114-
\tNested Leaf 1.2
115-
\tNested Leaf 1.3
116-
Nested Group 2
117-
\tNested Group 2.1
118-
\t\tNested Leaf 2.1.1
119-
\t\tNested Leaf 2.1.2`);
154+
expect(await nestedTree.getTreeStructure()).toEqual({
155+
children: [
156+
{text: 'Nested Group 1'},
157+
{text: 'Nested Group 2'}
158+
]
159+
});
160+
161+
const firstGroup = (await nestedTree.getNodes({text: /Nested Group 1/}))[0];
162+
await firstGroup.expand();
163+
expect(await nestedTree.getTreeStructure()).toEqual(
164+
{
165+
children: [
166+
{
167+
text: 'Nested Group 1',
168+
children: [
169+
{text: 'Nested Leaf 1.1'},
170+
{text: 'Nested Leaf 1.2'},
171+
{text: 'Nested Leaf 1.3'}
172+
]
173+
},
174+
{text: 'Nested Group 2'}
175+
]
176+
});
177+
178+
const secondGroup = (await nestedTree.getNodes({text: /Nested Group 2/}))[0];
179+
await secondGroup.expand();
180+
expect(await nestedTree.getTreeStructure()).toEqual(
181+
{
182+
children: [
183+
{
184+
text: 'Nested Group 1',
185+
children: [
186+
{text: 'Nested Leaf 1.1'},
187+
{text: 'Nested Leaf 1.2'},
188+
{text: 'Nested Leaf 1.3'}
189+
]
190+
},
191+
{
192+
text: 'Nested Group 2',
193+
children: [
194+
{text: 'Nested Group 2.1'},
195+
]
196+
}
197+
]
198+
});
120199
});
121200
}
122201

123-
interface FoodNode {
202+
interface Node {
124203
name: string;
125-
children?: FoodNode[];
204+
children?: Node[];
126205
}
127206

128-
const FLAT_TREE_DATA: FoodNode[] = [
207+
const FLAT_TREE_DATA: Node[] = [
129208
{
130209
name: 'Flat Group 1',
131210
children: [
@@ -148,7 +227,7 @@ const FLAT_TREE_DATA: FoodNode[] = [
148227
},
149228
];
150229

151-
const NESTED_TREE_DATA: FoodNode[] = [
230+
const NESTED_TREE_DATA: Node[] = [
152231
{
153232
name: 'Nested Group 1',
154233
children: [
@@ -210,7 +289,7 @@ interface ExampleFlatNode {
210289
`
211290
})
212291
class TreeHarnessTest {
213-
private _transformer = (node: FoodNode, level: number) => {
292+
private _transformer = (node: Node, level: number) => {
214293
return {
215294
expandable: !!node.children && node.children.length > 0,
216295
name: node.name,
@@ -223,8 +302,8 @@ class TreeHarnessTest {
223302
flatTreeControl = new FlatTreeControl<ExampleFlatNode>(
224303
node => node.level, node => node.expandable);
225304
flatTreeDataSource = new MatTreeFlatDataSource(this.flatTreeControl, this.treeFlattener);
226-
nestedTreeControl = new NestedTreeControl<FoodNode>(node => node.children);
227-
nestedTreeDataSource = new MatTreeNestedDataSource<FoodNode>();
305+
nestedTreeControl = new NestedTreeControl<Node>(node => node.children);
306+
nestedTreeDataSource = new MatTreeNestedDataSource<Node>();
228307

229308
constructor() {
230309
this.flatTreeDataSource.data = FLAT_TREE_DATA;
@@ -233,5 +312,5 @@ class TreeHarnessTest {
233312

234313
flatTreeHasChild = (_: number, node: ExampleFlatNode) => node.expandable;
235314

236-
nestedTreeHasChild = (_: number, node: FoodNode) => !!node.children && node.children.length > 0;
315+
nestedTreeHasChild = (_: number, node: Node) => !!node.children && node.children.length > 0;
237316
}

src/material/tree/testing/tree-harness.ts

Lines changed: 81 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {ComponentHarness, HarnessPredicate} from '@angular/cdk/testing';
9+
import {ComponentHarness, HarnessPredicate, parallel} from '@angular/cdk/testing';
1010
import {MatTreeNodeHarness} from './node-harness';
1111
import {TreeHarnessFilters, TreeNodeHarnessFilters} from './tree-harness-filters';
1212

13+
export type TextTree = {
14+
text?: string;
15+
children?: TextTree[];
16+
};
17+
1318
/** Harness for interacting with a standard mat-tree in tests. */
1419
export class MatTreeHarness extends ComponentHarness {
1520
/** The selector for the host element of a `MatTableHarness` instance. */
@@ -30,9 +35,10 @@ export class MatTreeHarness extends ComponentHarness {
3035
}
3136

3237
/**
33-
* String representation of the tree structure.
38+
* Gets an object representation for the visible tree structure
39+
* If a node is under an unexpanded node it will not be included.
3440
* Eg.
35-
* Tree:
41+
* Tree (all nodes expanded):
3642
* `
3743
* <mat-tree>
3844
* <mat-tree-node>Node 1<mat-tree-node>
@@ -50,25 +56,82 @@ export class MatTreeHarness extends ComponentHarness {
5056
* <mat-nested-tree-node>
5157
* </mat-tree>`
5258
*
53-
* Structured text:
54-
* Node 1
55-
* Node 2
56-
* Node 2.1
57-
* Node 2.1.1
58-
* Node 2.2
59+
* Tree structure:
60+
* {
61+
* children: [
62+
* {
63+
* text: 'Node 1',
64+
* children: [
65+
* {
66+
* text: 'Node 2',
67+
* children: [
68+
* {
69+
* text: 'Node 2.1',
70+
* children: [{text: 'Node 2.1.1'}]
71+
* },
72+
* {text: 'Node 2.2'}
73+
* ]
74+
* }
75+
* ]
76+
* }
77+
* ]
78+
* };
5979
*/
60-
async getStructureText(): Promise<string> {
61-
let treeString = '';
80+
async getTreeStructure(): Promise<TextTree> {
6281
const nodes = await this.getNodes();
63-
const levelsAndText = await Promise.all(nodes.map(node => {
64-
return Promise.all([node.getLevel(), node.getText()]);
82+
const nodeInformation = await parallel(() => nodes.map(node => {
83+
return Promise.all([node.getLevel(), node.getText(), node.isExpanded()]);
6584
}));
85+
return this._getTreeStructure(nodeInformation, 1, true);
86+
}
87+
88+
/**
89+
* Recursively collect the structured text of the tree nodes.
90+
* @param nodes A list of tree nodes
91+
* @param level The level of nodes that are being accounted for during this iteration
92+
* @param parentExpanded Whether the parent of the first node in param nodes is expanded
93+
*/
94+
private _getTreeStructure(nodes: [number, string, boolean][], level: number,
95+
parentExpanded: boolean): TextTree {
96+
const result: TextTree = {};
6697
for (let i = 0; i < nodes.length; i++) {
67-
const [level, text] = levelsAndText[i];
68-
treeString += i === 0 ? '' : '\n';
69-
treeString += '\t'.repeat(level - 1);
70-
treeString += text;
98+
const [nodeLevel, text, expanded] = nodes[i];
99+
const nextNodeLevel = nodes[i + 1]?.[0] ?? -1;
100+
101+
// Return the accumulated value for the current level once we reach a shallower level node
102+
if (nodeLevel < level) {
103+
return result;
104+
}
105+
// Skip deeper level nodes during this iteration, they will be picked up in a later iteration
106+
if (nodeLevel > level) {
107+
continue;
108+
}
109+
// Only add to representation if it is visible (parent is expanded)
110+
if (parentExpanded) {
111+
// Collect the data under this node according to the following rules:
112+
// 1. If the next node in the list is a sibling of the current node add it to the child list
113+
// 2. If the next node is a child of the current node, get the sub-tree structure for the
114+
// child and add it under this node
115+
// 3. If the next node has a shallower level, we've reached the end of the child nodes for
116+
// the current parent.
117+
if (nextNodeLevel === level) {
118+
this._addChildToNode(result, {text});
119+
} else if (nextNodeLevel > level) {
120+
let children = this._getTreeStructure(nodes.slice(i + 1),
121+
nextNodeLevel,
122+
expanded)?.children;
123+
let child = children ? {text, children} : {text};
124+
this._addChildToNode(result, child);
125+
} else {
126+
this._addChildToNode(result, {text});
127+
return result;
128+
}
129+
}
71130
}
72-
return treeString;
131+
return result;
132+
}
133+
134+
private _addChildToNode(result: TextTree, child: TextTree) {
135+
result.children ? result.children.push(child) : result.children = [child];
73136
}
74137
}

tools/public_api_guard/material/tree/testing.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export declare class MatTreeHarness extends ComponentHarness {
22
getNodes(filter?: TreeNodeHarnessFilters): Promise<MatTreeNodeHarness[]>;
3-
getTreeStructure(): Promise<string>;
3+
getTreeStructure(): Promise<TextTree>;
44
static hostSelector: string;
55
static with(options?: TreeHarnessFilters): HarnessPredicate<MatTreeHarness>;
66
}
@@ -18,6 +18,11 @@ export declare class MatTreeNodeHarness extends ComponentHarness {
1818
static with(options?: TreeNodeHarnessFilters): HarnessPredicate<MatTreeNodeHarness>;
1919
}
2020

21+
export declare type TextTree = {
22+
text?: string;
23+
children?: TextTree[];
24+
};
25+
2126
export interface TreeHarnessFilters extends BaseHarnessFilters {
2227
}
2328

0 commit comments

Comments
 (0)