From ee504455ed2f539cdb2e5681ee372b82c88c636c Mon Sep 17 00:00:00 2001 From: Zach Arend Date: Wed, 6 Dec 2023 09:22:00 -0800 Subject: [PATCH] refactor(material/tree): add unit tests for opt-out (#28237) Add unit tests to cover the use of LegacyTreeKeyManager, which opts out of updated focus management behavior. --- src/cdk/tree/BUILD.bazel | 1 + .../tree-using-legacy-key-manager.spec.ts | 92 +++++++++++++++++++ src/material/tree/BUILD.bazel | 2 + .../tree-using-legacy-key-manager.spec.ts | 38 ++++++-- 4 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 src/cdk/tree/tree-using-legacy-key-manager.spec.ts diff --git a/src/cdk/tree/BUILD.bazel b/src/cdk/tree/BUILD.bazel index 2da2df0bb980..1e28a24e66fb 100644 --- a/src/cdk/tree/BUILD.bazel +++ b/src/cdk/tree/BUILD.bazel @@ -33,6 +33,7 @@ ng_test_library( ), deps = [ ":tree", + "//src/cdk/a11y", "//src/cdk/bidi", "//src/cdk/collections", "//src/cdk/keycodes", diff --git a/src/cdk/tree/tree-using-legacy-key-manager.spec.ts b/src/cdk/tree/tree-using-legacy-key-manager.spec.ts new file mode 100644 index 000000000000..bf63a8929cec --- /dev/null +++ b/src/cdk/tree/tree-using-legacy-key-manager.spec.ts @@ -0,0 +1,92 @@ +import {Component, ElementRef, QueryList, ViewChild, ViewChildren} from '@angular/core'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {of} from 'rxjs'; +import {CdkTreeModule} from './tree-module'; +import {NOOP_TREE_KEY_MANAGER_FACTORY_PROVIDER} from '@angular/cdk/a11y'; + +describe('CdkTree when provided LegacyTreeKeyManager', () => { + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [CdkTreeModule], + declarations: [SimpleCdkTreeApp], + providers: [NOOP_TREE_KEY_MANAGER_FACTORY_PROVIDER], + }).compileComponents(); + + fixture = TestBed.createComponent(SimpleCdkTreeApp); + fixture.detectChanges(); + }); + + describe('with default node options', () => { + it('renders nodes with tabindex attribute of -1', () => { + const treeItems = fixture.componentInstance.treeNodes; + + expect(treeItems.map(x => `${x.nativeElement.getAttribute('tabindex')}`).join(', ')) + .withContext('tabindex of tree nodes') + .toEqual('-1, -1'); + }); + }); + + describe('when focusing the second node', () => { + beforeEach(() => { + const treeItems = fixture.componentInstance.treeNodes; + + treeItems.get(1)!.nativeElement.focus(); + fixture.detectChanges(); + }); + + it('does not change tabindex of nodes', () => { + const treeItems = fixture.componentInstance.treeNodes; + + expect(treeItems.map(x => `${x.nativeElement.getAttribute('tabindex')}`).join(', ')) + .withContext('tabindex of tree nodes') + .toEqual('-1, -1'); + }); + }); + + describe('when clicking the second node', () => { + beforeEach(() => { + const treeItems = fixture.componentInstance.treeNodes; + + treeItems.get(1)!.nativeElement.click(); + fixture.detectChanges(); + }); + + it('does not change active element', () => { + expect(document.activeElement).toEqual(document.body); + }); + + it('does not change tabindex of nodes', () => { + const treeItems = fixture.componentInstance.treeNodes; + + expect(treeItems.map(x => `${x.nativeElement.getAttribute('tabindex')}`).join(', ')) + .withContext('tabindex of tree nodes') + .toEqual('-1, -1'); + }); + }); +}); + +class MinimalTestData { + constructor(public name: string) {} + children: MinimalTestData[] = []; +} + +@Component({ + template: ` + + + {{node.name}} + + + `, +}) +class SimpleCdkTreeApp { + isExpandable = (node: MinimalTestData) => node.children.length > 0; + getChildren = (node: MinimalTestData) => node.children; + + dataSource = of([new MinimalTestData('apple'), new MinimalTestData('banana')]); + + @ViewChild('tree', {read: ElementRef}) tree: ElementRef; + @ViewChildren('node') treeNodes: QueryList>; +} diff --git a/src/material/tree/BUILD.bazel b/src/material/tree/BUILD.bazel index dfd5b373bd73..9833cfcf0bc8 100644 --- a/src/material/tree/BUILD.bazel +++ b/src/material/tree/BUILD.bazel @@ -51,6 +51,8 @@ ng_test_library( deps = [ ":tree", "//src/cdk/a11y", + "//src/cdk/keycodes", + "//src/cdk/testing/private", "//src/cdk/tree", "@npm//rxjs", ], diff --git a/src/material/tree/tree-using-legacy-key-manager.spec.ts b/src/material/tree/tree-using-legacy-key-manager.spec.ts index 91b772c18970..828087ae9111 100644 --- a/src/material/tree/tree-using-legacy-key-manager.spec.ts +++ b/src/material/tree/tree-using-legacy-key-manager.spec.ts @@ -1,9 +1,10 @@ import {Component, ElementRef, QueryList, ViewChild, ViewChildren} from '@angular/core'; import {of} from 'rxjs'; -import {MatTree} from './tree'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {MatTreeModule} from './tree-module'; import {NOOP_TREE_KEY_MANAGER_FACTORY_PROVIDER} from '@angular/cdk/a11y'; +import {DOWN_ARROW} from '@angular/cdk/keycodes'; +import {createKeyboardEvent} from '@angular/cdk/testing/private'; describe('MatTree when provided LegacyTreeKeyManager', () => { let fixture: ComponentFixture; @@ -19,7 +20,7 @@ describe('MatTree when provided LegacyTreeKeyManager', () => { fixture.detectChanges(); }); - describe('when nodes do not have a tabindex set', () => { + describe('when nodes have default options', () => { it('Should render tabindex attribute of 0', () => { const treeItems = fixture.componentInstance.treeNodes; @@ -30,10 +31,12 @@ describe('MatTree when provided LegacyTreeKeyManager', () => { }); describe('when nodes have TabIndex Input binding of 42', () => { - it('Should render tabindex attribute of 42.', () => { + beforeEach(() => { fixture.componentInstance.tabIndexInputBinding = 42; fixture.detectChanges(); + }); + it('Should render tabindex attribute of 42.', () => { const treeItems = fixture.componentInstance.treeNodes; expect(treeItems.map(x => `${x.nativeElement.getAttribute('tabindex')}`).join(', ')) @@ -43,10 +46,12 @@ describe('MatTree when provided LegacyTreeKeyManager', () => { }); describe('when nodes have tabindex attribute binding of 2', () => { - it('should render tabindex attribute of 2', () => { + beforeEach(() => { fixture.componentInstance.tabindexAttributeBinding = '2'; fixture.detectChanges(); + }); + it('should render tabindex attribute of 2', () => { const treeItems = fixture.componentInstance.treeNodes; expect(treeItems.map(x => `${x.nativeElement.getAttribute('tabindex')}`).join(', ')) @@ -54,6 +59,27 @@ describe('MatTree when provided LegacyTreeKeyManager', () => { .toEqual('2, 2, 2'); }); }); + + describe('when pressing down arrow key', () => { + beforeEach(() => { + const treeElement = fixture.componentInstance.tree.nativeElement; + + treeElement.dispatchEvent(createKeyboardEvent('keydown', DOWN_ARROW, 'down')); + fixture.detectChanges(); + }); + + it('should not change the active element', () => { + expect(document.activeElement).toEqual(document.body); + }); + + it('should not change the tabindex of tree nodes', () => { + const treeItems = fixture.componentInstance.treeNodes; + + expect(treeItems.map(x => `${x.nativeElement.getAttribute('tabindex')}`).join(', ')) + .withContext('tabindex of tree nodes') + .toEqual('0, 0, 0'); + }); + }); }); class MinimalTestData { @@ -63,7 +89,7 @@ class MinimalTestData { @Component({ template: ` - + {{node.name}} @@ -86,6 +112,6 @@ class SimpleMatTreeApp { /** Value passed to tabindex attribute binding of each tree node. Null by default. */ tabindexAttributeBinding: string | null = null; - @ViewChild(MatTree) tree: MatTree; + @ViewChild('tree', {read: ElementRef}) tree: ElementRef; @ViewChildren('node') treeNodes: QueryList>; }