UNPKG

8.39 kBJavaScriptView Raw
1import { ContentContainerComponentHarness, HarnessPredicate, ComponentHarness, parallel } from '@angular/cdk/testing';
2import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
3
4/**
5 * @license
6 * Copyright Google LLC All Rights Reserved.
7 *
8 * Use of this source code is governed by an MIT-style license that can be
9 * found in the LICENSE file at https://angular.io/license
10 */
11/** Harness for interacting with a standard Angular Material tree node. */
12class MatTreeNodeHarness extends ContentContainerComponentHarness {
13 constructor() {
14 super(...arguments);
15 this._toggle = this.locatorForOptional('[matTreeNodeToggle]');
16 }
17 /**
18 * Gets a `HarnessPredicate` that can be used to search for a tree node with specific attributes.
19 * @param options Options for narrowing the search
20 * @return a `HarnessPredicate` configured with the given options.
21 */
22 static with(options = {}) {
23 return getNodePredicate(MatTreeNodeHarness, options);
24 }
25 /** Whether the tree node is expanded. */
26 async isExpanded() {
27 return coerceBooleanProperty(await (await this.host()).getAttribute('aria-expanded'));
28 }
29 /** Whether the tree node is disabled. */
30 async isDisabled() {
31 return coerceBooleanProperty(await (await this.host()).getProperty('aria-disabled'));
32 }
33 /** Gets the level of the tree node. Note that this gets the aria-level and is 1 indexed. */
34 async getLevel() {
35 return coerceNumberProperty(await (await this.host()).getAttribute('aria-level'));
36 }
37 /** Gets the tree node's text. */
38 async getText() {
39 return (await this.host()).text({ exclude: '.mat-tree-node, .mat-nested-tree-node, button' });
40 }
41 /** Toggles node between expanded/collapsed. Only works when node is not disabled. */
42 async toggle() {
43 const toggle = await this._toggle();
44 if (toggle) {
45 return toggle.click();
46 }
47 }
48 /** Expands the node if it is collapsed. Only works when node is not disabled. */
49 async expand() {
50 if (!(await this.isExpanded())) {
51 await this.toggle();
52 }
53 }
54 /** Collapses the node if it is expanded. Only works when node is not disabled. */
55 async collapse() {
56 if (await this.isExpanded()) {
57 await this.toggle();
58 }
59 }
60}
61/** The selector of the host element of a `MatTreeNode` instance. */
62MatTreeNodeHarness.hostSelector = '.mat-tree-node, .mat-nested-tree-node';
63function getNodePredicate(type, options) {
64 return new HarnessPredicate(type, options)
65 .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))
66 .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled)
67 .addOption('expanded', options.expanded, async (harness, expanded) => (await harness.isExpanded()) === expanded)
68 .addOption('level', options.level, async (harness, level) => (await harness.getLevel()) === level);
69}
70
71/**
72 * @license
73 * Copyright Google LLC All Rights Reserved.
74 *
75 * Use of this source code is governed by an MIT-style license that can be
76 * found in the LICENSE file at https://angular.io/license
77 */
78/** Harness for interacting with a standard mat-tree in tests. */
79class MatTreeHarness extends ComponentHarness {
80 /**
81 * Gets a `HarnessPredicate` that can be used to search for a tree with specific attributes.
82 * @param options Options for narrowing the search
83 * @return a `HarnessPredicate` configured with the given options.
84 */
85 static with(options = {}) {
86 return new HarnessPredicate(MatTreeHarness, options);
87 }
88 /** Gets all of the nodes in the tree. */
89 async getNodes(filter = {}) {
90 return this.locatorForAll(MatTreeNodeHarness.with(filter))();
91 }
92 /**
93 * Gets an object representation for the visible tree structure
94 * If a node is under an unexpanded node it will not be included.
95 * Eg.
96 * Tree (all nodes expanded):
97 * `
98 * <mat-tree>
99 * <mat-tree-node>Node 1<mat-tree-node>
100 * <mat-nested-tree-node>
101 * Node 2
102 * <mat-nested-tree-node>
103 * Node 2.1
104 * <mat-tree-node>
105 * Node 2.1.1
106 * <mat-tree-node>
107 * <mat-nested-tree-node>
108 * <mat-tree-node>
109 * Node 2.2
110 * <mat-tree-node>
111 * <mat-nested-tree-node>
112 * </mat-tree>`
113 *
114 * Tree structure:
115 * {
116 * children: [
117 * {
118 * text: 'Node 1',
119 * children: [
120 * {
121 * text: 'Node 2',
122 * children: [
123 * {
124 * text: 'Node 2.1',
125 * children: [{text: 'Node 2.1.1'}]
126 * },
127 * {text: 'Node 2.2'}
128 * ]
129 * }
130 * ]
131 * }
132 * ]
133 * };
134 */
135 async getTreeStructure() {
136 const nodes = await this.getNodes();
137 const nodeInformation = await parallel(() => nodes.map(node => {
138 return parallel(() => [node.getLevel(), node.getText(), node.isExpanded()]);
139 }));
140 return this._getTreeStructure(nodeInformation, 1, true);
141 }
142 /**
143 * Recursively collect the structured text of the tree nodes.
144 * @param nodes A list of tree nodes
145 * @param level The level of nodes that are being accounted for during this iteration
146 * @param parentExpanded Whether the parent of the first node in param nodes is expanded
147 */
148 _getTreeStructure(nodes, level, parentExpanded) {
149 const result = {};
150 for (let i = 0; i < nodes.length; i++) {
151 const [nodeLevel, text, expanded] = nodes[i];
152 const nextNodeLevel = nodes[i + 1]?.[0] ?? -1;
153 // Return the accumulated value for the current level once we reach a shallower level node
154 if (nodeLevel < level) {
155 return result;
156 }
157 // Skip deeper level nodes during this iteration, they will be picked up in a later iteration
158 if (nodeLevel > level) {
159 continue;
160 }
161 // Only add to representation if it is visible (parent is expanded)
162 if (parentExpanded) {
163 // Collect the data under this node according to the following rules:
164 // 1. If the next node in the list is a sibling of the current node add it to the child list
165 // 2. If the next node is a child of the current node, get the sub-tree structure for the
166 // child and add it under this node
167 // 3. If the next node has a shallower level, we've reached the end of the child nodes for
168 // the current parent.
169 if (nextNodeLevel === level) {
170 this._addChildToNode(result, { text });
171 }
172 else if (nextNodeLevel > level) {
173 let children = this._getTreeStructure(nodes.slice(i + 1), nextNodeLevel, expanded)?.children;
174 let child = children ? { text, children } : { text };
175 this._addChildToNode(result, child);
176 }
177 else {
178 this._addChildToNode(result, { text });
179 return result;
180 }
181 }
182 }
183 return result;
184 }
185 _addChildToNode(result, child) {
186 result.children ? result.children.push(child) : (result.children = [child]);
187 }
188}
189/** The selector for the host element of a `MatTableHarness` instance. */
190MatTreeHarness.hostSelector = '.mat-tree';
191
192/**
193 * @license
194 * Copyright Google LLC All Rights Reserved.
195 *
196 * Use of this source code is governed by an MIT-style license that can be
197 * found in the LICENSE file at https://angular.io/license
198 */
199
200/**
201 * @license
202 * Copyright Google LLC All Rights Reserved.
203 *
204 * Use of this source code is governed by an MIT-style license that can be
205 * found in the LICENSE file at https://angular.io/license
206 */
207
208/**
209 * @license
210 * Copyright Google LLC All Rights Reserved.
211 *
212 * Use of this source code is governed by an MIT-style license that can be
213 * found in the LICENSE file at https://angular.io/license
214 */
215
216export { MatTreeHarness, MatTreeNodeHarness };
217//# sourceMappingURL=testing.mjs.map