-
Notifications
You must be signed in to change notification settings - Fork 6.8k
feat(material/tree): Add test harness for MatTree
#20018
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
load("//tools:defaults.bzl", "ng_test_library", "ng_web_test_suite", "ts_library") | ||
|
||
package(default_visibility = ["//visibility:public"]) | ||
|
||
ts_library( | ||
name = "testing", | ||
srcs = glob( | ||
["**/*.ts"], | ||
exclude = ["**/*.spec.ts"], | ||
), | ||
module_name = "@angular/material/tree/testing", | ||
deps = [ | ||
"//src/cdk/coercion", | ||
"//src/cdk/testing", | ||
], | ||
) | ||
|
||
filegroup( | ||
name = "source-files", | ||
srcs = glob(["**/*.ts"]), | ||
) | ||
|
||
ng_test_library( | ||
name = "harness_tests_lib", | ||
srcs = ["shared.spec.ts"], | ||
deps = [ | ||
":testing", | ||
"//src/cdk/testing", | ||
"//src/cdk/testing/testbed", | ||
"//src/cdk/tree", | ||
"//src/material/tree", | ||
], | ||
) | ||
|
||
ng_test_library( | ||
name = "unit_tests_lib", | ||
srcs = glob( | ||
["**/*.spec.ts"], | ||
exclude = ["shared.spec.ts"], | ||
), | ||
deps = [ | ||
":harness_tests_lib", | ||
":testing", | ||
"//src/material/tree", | ||
], | ||
) | ||
|
||
ng_web_test_suite( | ||
name = "unit_tests", | ||
deps = [":unit_tests_lib"], | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
export * from './public-api'; |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,123 @@ | ||||||
/** | ||||||
* @license | ||||||
* Copyright Google LLC All Rights Reserved. | ||||||
* | ||||||
* Use of this source code is governed by an MIT-style license that can be | ||||||
* found in the LICENSE file at https://angular.io/license | ||||||
*/ | ||||||
|
||||||
import { | ||||||
ComponentHarness, | ||||||
ComponentHarnessConstructor, | ||||||
HarnessPredicate | ||||||
} from '@angular/cdk/testing'; | ||||||
import {TreeNodeHarnessFilters} from './tree-harness-filters'; | ||||||
import {coerceBooleanProperty, coerceNumberProperty} from '@angular/cdk/coercion'; | ||||||
|
||||||
/** Harness for interacting with a standard Angular Material tree node. */ | ||||||
export class MatTreeNodeHarness extends ComponentHarness { | ||||||
/** The selector of the host element of a `MatTreeNode` instance. */ | ||||||
static hostSelector = '.mat-tree-node'; | ||||||
|
||||||
_toggle = this.locatorForOptional('[matTreeNodeToggle]'); | ||||||
|
||||||
/** | ||||||
* Gets a `HarnessPredicate` that can be used to search for a tree node with specific attributes. | ||||||
* @param options Options for narrowing the search | ||||||
* @return a `HarnessPredicate` configured with the given options. | ||||||
*/ | ||||||
static with(options: TreeNodeHarnessFilters = {}): HarnessPredicate<MatTreeNodeHarness> { | ||||||
return getNodePredicate(MatTreeNodeHarness, options); | ||||||
} | ||||||
|
||||||
/** Whether the tree node is expanded. */ | ||||||
async isExpanded(): Promise<boolean> { | ||||||
return coerceBooleanProperty(await (await this.host()).getAttribute('aria-expanded')); | ||||||
} | ||||||
|
||||||
/** Whether the tree node is disabled. */ | ||||||
async isDisabled(): Promise<boolean> { | ||||||
return coerceBooleanProperty(await (await this.host()).getProperty('aria-disabled')); | ||||||
} | ||||||
|
||||||
/** Gets the level of the tree node.. */ | ||||||
async getLevel(): Promise<number> { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should be clear about whether this is zero-based or one-based (see comments on #17818) |
||||||
return coerceNumberProperty(await (await this.host()).getAttribute('aria-level')); | ||||||
} | ||||||
|
||||||
/** Whether the node is a leaf node. */ | ||||||
async isLeaf(): Promise<boolean> { | ||||||
const role = await (await this.host()).getAttribute('role'); | ||||||
if (role === 'group') { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI the way we use |
||||||
return false; | ||||||
} else if (role === 'treeitem') { | ||||||
return true | ||||||
} else { | ||||||
throw new Error('Invalid node role'); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
You don't actually need |
||||||
} | ||||||
} | ||||||
|
||||||
/** Gets the tree node's text. */ | ||||||
async getText(): Promise<string> { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think for the nested tree node this will get the text of the node and everything under it, you might want to have separate implementations of this method for the different subclasses |
||||||
return (await this.host()).text(); | ||||||
} | ||||||
|
||||||
/** Toggles node between expanded/collapsed. Only works when node is not disabled. */ | ||||||
async toggle(): Promise<void> { | ||||||
const toggle = await this._toggle(); | ||||||
if (toggle) { | ||||||
return toggle.click(); | ||||||
} | ||||||
} | ||||||
|
||||||
/** Expands the node if it is collapsed. Only works when node is not disabled. */ | ||||||
async expand(): Promise<void> { | ||||||
if (!(await this.isExpanded())) { | ||||||
await this.toggle(); | ||||||
} | ||||||
} | ||||||
|
||||||
/** Collapses the node if it is expanded. Only works when node is not disabled. */ | ||||||
async collapse(): Promise<void> { | ||||||
if (await this.isExpanded()) { | ||||||
await this.toggle(); | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
/** Harness for interacting with a standard Angular Material nested tree node. */ | ||||||
export class MatNestedTreeNodeHarness extends MatTreeNodeHarness { | ||||||
/** The selector for the host element of a `MatNestedTreeNode` instance. */ | ||||||
static hostSelector = '.mat-nested-tree-node'; | ||||||
|
||||||
/** | ||||||
* Gets a `HarnessPredicate` that can be used to search for | ||||||
* a nested tree node with specific attributes. | ||||||
* @param options Options for narrowing the search | ||||||
* @return a `HarnessPredicate` configured with the given options. | ||||||
*/ | ||||||
static with(options: TreeNodeHarnessFilters = {}): HarnessPredicate<MatNestedTreeNodeHarness> { | ||||||
return getNodePredicate(MatNestedTreeNodeHarness, options); | ||||||
} | ||||||
} | ||||||
|
||||||
|
||||||
function getNodePredicate<T extends MatTreeNodeHarness>( | ||||||
type: ComponentHarnessConstructor<T>, | ||||||
options: TreeNodeHarnessFilters): HarnessPredicate<T> { | ||||||
return new HarnessPredicate(type, options) | ||||||
.addOption('text', options.text, | ||||||
(harness, text) => HarnessPredicate.stringMatches(harness.getText(), text)) | ||||||
.addOption( | ||||||
'disabled', options.disabled, | ||||||
async (harness, disabled) => (await harness.isDisabled()) === disabled) | ||||||
.addOption( | ||||||
'expanded', options.expanded, | ||||||
async (harness, expanded) => (await harness.isExpanded()) === expanded) | ||||||
.addOption( | ||||||
'level', options.level, | ||||||
async (harness, level) => (await harness.getLevel()) === level) | ||||||
.addOption( | ||||||
'leaf', options.leaf, | ||||||
async (harness, leaf) => (await harness.isLeaf()) === leaf); | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
export * from './node-harness'; | ||
export * from './tree-harness'; | ||
export * from './tree-harness-filters'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW the intent is that this directive is just one way someone could add a toggle for a node. We may want to make the harness more flexible in terms of how expand/collapse works.