diff --git a/adev/src/content/guide/aria/accordion.md b/adev/src/content/guide/aria/accordion.md index 2546ede7b62..d99a4a7ab8e 100644 --- a/adev/src/content/guide/aria/accordion.md +++ b/adev/src/content/guide/aria/accordion.md @@ -159,6 +159,55 @@ Use the `ngAccordionContent` directive on an `ng-template` to defer rendering co By default, content remains in the DOM after the panel collapses. Set `[preserveContent]="false"` to remove the content from the DOM when the panel closes. +## Testing + +Angular Aria provides component harnesses for testing accordion components. +Here is an example of how to use the harnesses in a component test: + +```typescript +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {HarnessLoader} from '@angular/cdk/testing'; +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {AccordionGroupHarness} from '@angular/aria/accordion/testing'; +import {MyAccordionComponent} from './my-accordion'; // Your component + +describe('MyAccordionComponent', () => { + let fixture: ComponentFixture; + let loader: HarnessLoader; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [MyAccordionComponent], + }); + + fixture = TestBed.createComponent(MyAccordionComponent); + await fixture.whenStable(); + loader = TestbedHarnessEnvironment.loader(fixture); + }); + + it('should allow expanding panels', async () => { + // Load the accordion group harness + const group = await loader.getHarness(AccordionGroupHarness); + + // Get all individual accordions (items) in the group + const accordions = await group.getAccordions(); + expect(accordions.length).toBe(3); + + // Verify initial state (first expanded, others collapsed) + expect(await accordions[0].isExpanded()).toBe(true); + expect(await accordions[1].isExpanded()).toBe(false); + + // Expand the second panel + await accordions[1].expand(); + + // Verify updated state + expect(await accordions[1].isExpanded()).toBe(true); + // If multiExpandable is false, the first one should now be collapsed + expect(await accordions[0].isExpanded()).toBe(false); + }); +}); +``` + ## APIs ### AccordionGroup diff --git a/adev/src/content/guide/aria/autocomplete.md b/adev/src/content/guide/aria/autocomplete.md index e7f629a124f..4c840ec32e7 100644 --- a/adev/src/content/guide/aria/autocomplete.md +++ b/adev/src/content/guide/aria/autocomplete.md @@ -148,6 +148,57 @@ Highlight mode allows the user to navigate options with arrow keys without chang +## Testing + +The autocomplete pattern can be tested using a combination of `ComboboxHarness` and `ListboxHarness` from `@angular/aria/combobox/testing` and `@angular/aria/listbox/testing`. +Here is an example of how to use the harnesses to test an autocomplete component: + +```typescript +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {HarnessLoader} from '@angular/cdk/testing'; +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {ComboboxHarness} from '@angular/aria/combobox/testing'; +import {ListboxHarness} from '@angular/aria/listbox/testing'; +import {MyAutocompleteComponent} from './my-autocomplete'; // Your component + +describe('MyAutocompleteComponent', () => { + let fixture: ComponentFixture; + let loader: HarnessLoader; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [MyAutocompleteComponent], + }); + + fixture = TestBed.createComponent(MyAutocompleteComponent); + await fixture.whenStable(); + loader = TestbedHarnessEnvironment.loader(fixture); + }); + + it('should filter options based on input', async () => { + const combobox = await loader.getHarness(ComboboxHarness); + + // Type in the input to trigger filtering + await combobox.setValue('ap'); + expect(await combobox.isOpen()).toBe(true); + + // Get the listbox harness from the popup + const listbox = await combobox.getPopupWidget(ListboxHarness); + const options = await listbox.getOptions(); + + // Verify options are filtered (e.g., 'Apple', 'Apricot') + expect(options.length).toBe(2); + expect(await options[0].getText()).toBe('Apple'); + + // Select the first option + await options[0].click(); + + // Verify the input value is updated and popup is closed + expect(await combobox.isOpen()).toBe(false); + expect(await combobox.getValue()).toBe('Apple'); + }); +}); +``` ## APIs diff --git a/adev/src/content/guide/aria/combobox.md b/adev/src/content/guide/aria/combobox.md index 6d3c6ea9422..e104eb8ba72 100644 --- a/adev/src/content/guide/aria/combobox.md +++ b/adev/src/content/guide/aria/combobox.md @@ -164,7 +164,7 @@ Combobox can coordinate with a two-dimensional grid to create accessible datepic ### Dialog popup -Popups sometimes need modal behavior with a backdrop and focus trap. The combobox dialog directive provides this pattern for specialized use cases. +Dialog popups combine the combobox trigger with standard dialog layouts and focus traps (such as CDK's `cdkTrapFocus`). Use dialog popups when the overlay requires modal behavior or backdrop interaction. @@ -192,7 +192,48 @@ Popups sometimes need modal behavior with a backdrop and focus trap. The combobo -Dialog popups combine the combobox trigger with standard dialog layouts and focus traps (such as CDK's `cdkTrapFocus`). Use dialog popups when the overlay requires modal behavior or backdrop interaction. +## Testing + +Angular Aria provides a `ComboboxHarness` for testing combobox components. +Here is an example of how to use the harness in a component test: + +```typescript +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {HarnessLoader} from '@angular/cdk/testing'; +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {ComboboxHarness} from '@angular/aria/combobox/testing'; +import {MyComboboxComponent} from './my-combobox'; // Your component + +describe('MyComboboxComponent', () => { + let fixture: ComponentFixture; + let loader: HarnessLoader; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [MyComboboxComponent], + }); + + fixture = TestBed.createComponent(MyComboboxComponent); + await fixture.whenStable(); + loader = TestbedHarnessEnvironment.loader(fixture); + }); + + it('should allow opening and closing the popup', async () => { + const combobox = await loader.getHarness(ComboboxHarness); + + // Verify initial state + expect(await combobox.isOpen()).toBe(false); + + // Open the popup + await combobox.open(); + expect(await combobox.isOpen()).toBe(true); + + // Close the popup + await combobox.close(); + expect(await combobox.isOpen()).toBe(false); + }); +}); +``` ## APIs diff --git a/adev/src/content/guide/aria/grid.md b/adev/src/content/guide/aria/grid.md index 90daffb71b6..753e73cebc8 100644 --- a/adev/src/content/guide/aria/grid.md +++ b/adev/src/content/guide/aria/grid.md @@ -159,6 +159,58 @@ Enable selection with `[enableSelection]="true"` and configure how focus and sel - `roving`: Focus moves to cells using `tabindex` (better for simple grids) - `activedescendant`: Focus stays on grid container, `aria-activedescendant` indicates active cell (better for virtual scrolling) +## Testing + +Angular Aria provides component harnesses for testing grid components. +Here is an example of how to use the harnesses in a component test: + +```typescript +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {HarnessLoader} from '@angular/cdk/testing'; +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {GridHarness} from '@angular/aria/grid/testing'; +import {MyGridComponent} from './my-grid'; // Your component + +describe('MyGridComponent', () => { + let fixture: ComponentFixture; + let loader: HarnessLoader; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [MyGridComponent], + }); + + fixture = TestBed.createComponent(MyGridComponent); + await fixture.whenStable(); + loader = TestbedHarnessEnvironment.loader(fixture); + }); + + it('should read cell values and focus cells', async () => { + const grid = await loader.getHarness(GridHarness); + + // Get all cells text in a 2D array organized by rows + const cellTexts = await grid.getCellTextByIndex(); + expect(cellTexts).toEqual([ + ['Cell 1.1', 'Cell 1.2'], + ['Cell 2.1', 'Cell 2.2'], + ]); + + // Get a specific cell by text + const cells = await grid.getCells({text: 'Cell 1.1'}); + expect(cells.length).toBe(1); + const cell = cells[0]; + + // Verify cell state + expect(await cell.isSelected()).toBe(true); + expect(await cell.isActive()).toBe(true); + + // Focus the cell + await cell.focus(); + expect(await cell.isFocused()).toBe(true); + }); +}); +``` + ## APIs ### Grid diff --git a/adev/src/content/guide/aria/listbox.md b/adev/src/content/guide/aria/listbox.md index 56864e9b9b4..5ca60d6af79 100644 --- a/adev/src/content/guide/aria/listbox.md +++ b/adev/src/content/guide/aria/listbox.md @@ -133,6 +133,55 @@ The `'follow'` mode automatically selects the focused item, providing faster int TIP: Dropdown patterns typically use `'follow'` mode for single selection. +## Testing + +Angular Aria provides component harnesses for testing listbox components. +Here is an example of how to use the harnesses in a component test: + +```typescript +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {HarnessLoader} from '@angular/cdk/testing'; +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {ListboxHarness} from '@angular/aria/listbox/testing'; +import {MyListboxComponent} from './my-listbox'; // Your component + +describe('MyListboxComponent', () => { + let fixture: ComponentFixture; + let loader: HarnessLoader; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [MyListboxComponent], + }); + + fixture = TestBed.createComponent(MyListboxComponent); + await fixture.whenStable(); + loader = TestbedHarnessEnvironment.loader(fixture); + }); + + it('should allow selecting options', async () => { + const listbox = await loader.getHarness(ListboxHarness); + + // Verify listbox properties + expect(await listbox.isMulti()).toBe(true); + + // Get all options + const options = await listbox.getOptions(); + expect(options.length).toBe(2); + + // Click an option + await options[0].click(); + + // Verify option is selected + expect(await options[0].isSelected()).toBe(true); + + // Filter options by text + const bananaOption = await listbox.getOptions({text: 'Banana'}); + expect(bananaOption.length).toBe(1); + }); +}); +``` + ## APIs ### Listbox Directive diff --git a/adev/src/content/guide/aria/menu.md b/adev/src/content/guide/aria/menu.md index 54c0a278b98..7c31c0ad5cb 100644 --- a/adev/src/content/guide/aria/menu.md +++ b/adev/src/content/guide/aria/menu.md @@ -174,6 +174,76 @@ Disable specific menu items using the `disabled` input. Control focus behavior w When `[softDisabled]="true"`, disabled items can receive focus but cannot be activated. When `[softDisabled]="false"`, disabled items are skipped during keyboard navigation. +## Testing + +Angular Aria provides component harnesses for testing menu components. +Here is an example of how to use the harnesses in a component test: + +```typescript +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {HarnessLoader} from '@angular/cdk/testing'; +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {MenuHarness} from '@angular/aria/menu/testing'; +import {MyMenuComponent} from './my-menu'; // Your component + +describe('MyMenuComponent', () => { + let fixture: ComponentFixture; + let loader: HarnessLoader; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [MyMenuComponent], + }); + + fixture = TestBed.createComponent(MyMenuComponent); + await fixture.whenStable(); + loader = TestbedHarnessEnvironment.loader(fixture); + }); + + it('should open menu and click item', async () => { + // Load the menu harness by its trigger text + const menu = await loader.getHarness(MenuHarness.with({triggerText: 'Open Menu'})); + + // Verify initial state + expect(await menu.isOpen()).toBe(false); + + // Open the menu + await menu.open(); + expect(await menu.isOpen()).toBe(true); + + // Get items + const items = await menu.getItems(); + expect(items.length).toBe(3); + expect(await items[0].getText()).toBe('Item 1'); + + // Click first item + await items[0].click(); + + // Menu should close after selection (depending on your implementation) + expect(await menu.isOpen()).toBe(false); + }); + + it('should interact with submenus', async () => { + const menu = await loader.getHarness(MenuHarness.with({triggerText: 'Open Menu'})); + await menu.open(); + + // Get the item that triggers a submenu + const subItem = await loader.getHarness(MenuItemHarness.with({text: 'Submenu'})); + expect(await subItem.hasSubmenu()).toBe(true); + + // Open submenu + await subItem.click(); + const submenu = await subItem.getSubmenu(); + expect(submenu).toBeTruthy(); + expect(await submenu!.isOpen()).toBe(true); + + // Interact with submenu items + const subItems = await submenu!.getItems(); + expect(subItems.length).toBe(1); + }); +}); +``` + ## APIs ### Menu diff --git a/adev/src/content/guide/aria/menubar.md b/adev/src/content/guide/aria/menubar.md index 57770adba72..36c25721b92 100644 --- a/adev/src/content/guide/aria/menubar.md +++ b/adev/src/content/guide/aria/menubar.md @@ -164,6 +164,56 @@ Menubars automatically adapt to right-to-left (RTL) languages. Arrow key navigat The `dir="rtl"` attribute enables RTL mode. Left arrow moves right, Right arrow moves left, maintaining natural navigation for RTL language users. +## Testing + +Angular Aria provides component harnesses for testing menubar components. +Here is an example of how to use the harnesses in a component test: + +```typescript +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {HarnessLoader} from '@angular/cdk/testing'; +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {MenuHarness} from '@angular/aria/menu/testing'; +import {MyMenubarComponent} from './my-menubar'; // Your component + +describe('MyMenubarComponent', () => { + let fixture: ComponentFixture; + let loader: HarnessLoader; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [MyMenubarComponent], + }); + + fixture = TestBed.createComponent(MyMenubarComponent); + await fixture.whenStable(); + loader = TestbedHarnessEnvironment.loader(fixture); + }); + + it('should interact with menubar items', async () => { + // Load the menubar harness (which is a MenuHarness with selector '[ngMenuBar]') + const menubar = await loader.getHarness(MenuHarness.with({selector: '[ngMenuBar]'})); + + // Menubars are persistent and always "open" + expect(await menubar.isOpen()).toBe(true); + expect(await menubar.isMenuBar()).toBe(true); + + // Get top-level items + const items = await menubar.getItems(); + expect(items.length).toBe(2); + expect(await items[0].getText()).toBe('File'); + expect(await items[1].getText()).toBe('Edit'); + + // Click an item to open its dropdown menu + await items[0].click(); + + const fileMenu = await items[0].getSubmenu(); + expect(fileMenu).toBeTruthy(); + expect(await fileMenu!.isOpen()).toBe(true); + }); +}); +``` + ## APIs The menubar pattern uses directives from Angular's Aria library. See the [Menu guide](guide/aria/menu) for complete API documentation. diff --git a/adev/src/content/guide/aria/multiselect.md b/adev/src/content/guide/aria/multiselect.md index c2719ff2b2f..8af5fb0d9fa 100644 --- a/adev/src/content/guide/aria/multiselect.md +++ b/adev/src/content/guide/aria/multiselect.md @@ -159,6 +159,62 @@ Forms sometimes need to limit the number of selections or validate user choices. This example limits selections to two items. When the limit is reached, unselected options are disabled to prevent further selections, and the combobox display updates to reflect the choices. +## Testing + +The multiselect pattern can be tested using a combination of `ComboboxHarness` and `ListboxHarness` from `@angular/aria/combobox/testing` and `@angular/aria/listbox/testing`. +Here is an example of how to use the harnesses to test a multiselect component: + +```typescript +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {HarnessLoader} from '@angular/cdk/testing'; +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {ComboboxHarness} from '@angular/aria/combobox/testing'; +import {ListboxHarness} from '@angular/aria/listbox/testing'; +import {MyMultiselectComponent} from './my-multiselect'; // Your component + +describe('MyMultiselectComponent', () => { + let fixture: ComponentFixture; + let loader: HarnessLoader; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [MyMultiselectComponent], + }); + + fixture = TestBed.createComponent(MyMultiselectComponent); + await fixture.whenStable(); + loader = TestbedHarnessEnvironment.loader(fixture); + }); + + it('should allow selecting multiple options', async () => { + const select = await loader.getHarness(ComboboxHarness); + + // Open the dropdown + await select.open(); + + // Get the listbox harness from the popup + const listbox = await select.getPopupWidget(ListboxHarness); + expect(await listbox.isMulti()).toBe(true); + + const options = await listbox.getOptions(); + + // Select first and second options + await options[0].click(); + await options[1].click(); + + // Verify both options are selected + expect(await options[0].isSelected()).toBe(true); + expect(await options[1].isSelected()).toBe(true); + + // Close the dropdown + await select.close(); + + // Verify value is updated (e.g., comma separated list or count) + expect(await (await select.host()).text()).toContain('Option 1, Option 2'); + }); +}); +``` + ## APIs The multiselect pattern uses the following directives from Angular's Aria library. See the full API documentation in the linked guides. diff --git a/adev/src/content/guide/aria/select.md b/adev/src/content/guide/aria/select.md index c00ec71280d..163cbc2798c 100644 --- a/adev/src/content/guide/aria/select.md +++ b/adev/src/content/guide/aria/select.md @@ -159,6 +159,59 @@ Selects can be disabled to prevent user interaction when certain form conditions When disabled, the select shows a disabled visual state and blocks all user interaction. Screen readers announce the disabled state to assistive technology users. +## Testing + +The select pattern can be tested using a combination of `ComboboxHarness` and `ListboxHarness` from `@angular/aria/combobox/testing` and `@angular/aria/listbox/testing`. +Here is an example of how to use the harnesses to test a select component: + +```typescript +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {HarnessLoader} from '@angular/cdk/testing'; +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {ComboboxHarness} from '@angular/aria/combobox/testing'; +import {ListboxHarness} from '@angular/aria/listbox/testing'; +import {MySelectComponent} from './my-select'; // Your component + +describe('MySelectComponent', () => { + let fixture: ComponentFixture; + let loader: HarnessLoader; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [MySelectComponent], + }); + + fixture = TestBed.createComponent(MySelectComponent); + await fixture.whenStable(); + loader = TestbedHarnessEnvironment.loader(fixture); + }); + + it('should allow selecting an option', async () => { + // Load the combobox harness (which acts as the select trigger) + const select = await loader.getHarness(ComboboxHarness); + + // Verify it is closed initially + expect(await select.isOpen()).toBe(false); + + // Open the dropdown + await select.open(); + expect(await select.isOpen()).toBe(true); + + // Get the listbox harness from the popup + const listbox = await select.getPopupWidget(ListboxHarness); + const options = await listbox.getOptions(); + expect(options.length).toBe(3); + + // Click the second option + await options[1].click(); + + // Verify the dropdown closed and the value updated + expect(await select.isOpen()).toBe(false); + expect(await (await select.host()).text()).toContain('Option 2'); + }); +}); +``` + ## APIs The select pattern uses the following directives from Angular's Aria library. See the full API documentation in the linked guides. diff --git a/adev/src/content/guide/aria/tabs.md b/adev/src/content/guide/aria/tabs.md index e96eaea3cdd..c15b98f4449 100644 --- a/adev/src/content/guide/aria/tabs.md +++ b/adev/src/content/guide/aria/tabs.md @@ -224,6 +224,66 @@ Disable specific tabs to prevent user interaction. Control whether disabled tabs When `[softDisabled]="true"` on the tab list, disabled tabs can receive focus but cannot be activated. When `[softDisabled]="false"`, disabled tabs are skipped during keyboard navigation. +## Testing + +Angular Aria provides component harnesses for testing tabs components. +Here is an example of how to use the harnesses in a component test: + +```typescript +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {ComponentHarness, HarnessLoader} from '@angular/cdk/testing'; +import {TabsHarness} from '@angular/aria/tabs/testing'; +import {MyTabsComponent} from './my-tabs'; // Your component + +// A simple harness to help query content inside the tab panel +class TestContentHarness extends ComponentHarness { + static hostSelector = '.test-content'; + async getText(): Promise { + return (await this.host()).text(); + } +} + +describe('MyTabsComponent', () => { + let fixture: ComponentFixture; + let loader: HarnessLoader; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [MyTabsComponent], + }); + + fixture = TestBed.createComponent(MyTabsComponent); + await fixture.whenStable(); + loader = TestbedHarnessEnvironment.loader(fixture); + }); + + it('should switch tabs and scope panel queries', async () => { + const tabs = await loader.getHarness(TabsHarness); + + // Get all tabs + const tabItems = await tabs.getTabs(); + expect(tabItems.length).toBe(3); + + // Verify initial selection + expect(await tabItems[0].isSelected()).toBe(true); + expect(await tabItems[1].isSelected()).toBe(false); + + // Query content inside the active tab panel + // TabHarness automatically scopes queries to its associated panel + const content = await tabItems[0].getHarness(TestContentHarness); + expect(await content.getText()).toBe('Content 1'); + + // Switch to the second tab + await tabItems[1].select(); + + // Verify selection updated + expect(await tabItems[0].isSelected()).toBe(false); + expect(await tabItems[1].isSelected()).toBe(true); + }); +}); +``` + ## APIs ### Tabs diff --git a/adev/src/content/guide/aria/toolbar.md b/adev/src/content/guide/aria/toolbar.md index c44774394a6..9b8f190b432 100644 --- a/adev/src/content/guide/aria/toolbar.md +++ b/adev/src/content/guide/aria/toolbar.md @@ -213,6 +213,48 @@ Toolbars automatically support right-to-left languages. Wrap the toolbar in a co +## Testing + +Angular Aria provides a set of component harnesses for testing. You can use these harnesses to interact with the toolbar components in your unit tests without relying on implementation details. +Here is an example of how to use the harnesses in a component test: + +```typescript +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {HarnessLoader} from '@angular/cdk/testing'; +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {ToolbarHarness} from '@angular/aria/toolbar/testing'; +import {MyToolbarComponent} from './my-toolbar'; // Your component + +describe('MyToolbarComponent', () => { + let fixture: ComponentFixture; + let loader: HarnessLoader; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [MyToolbarComponent], + }); + + fixture = TestBed.createComponent(MyToolbarComponent); + await fixture.whenStable(); + loader = TestbedHarnessEnvironment.loader(fixture); + }); + + it('should have widgets and allow selection', async () => { + // Load the toolbar harness + const toolbar = await loader.getHarness(ToolbarHarness); + + // Get all widgets + const widgets = await toolbar.getWidgets(); + expect(widgets.length).toBe(3); + + // Click the first widget + await widgets[0].click(); + + // Verify selection state + expect(await widgets[0].isSelected()).toBe(true); + }); +}); +``` ## APIs diff --git a/adev/src/content/guide/aria/tree.md b/adev/src/content/guide/aria/tree.md index 673ea420517..e97cd427270 100644 --- a/adev/src/content/guide/aria/tree.md +++ b/adev/src/content/guide/aria/tree.md @@ -157,6 +157,64 @@ Disable specific tree nodes to prevent interaction. Control whether disabled ite When `[softDisabled]="true"` on the tree, disabled items can receive focus but cannot be activated or selected. When `[softDisabled]="false"`, disabled items are skipped during keyboard navigation. +## Testing + +Angular Aria provides component harnesses for testing tree components. +Here is an example of how to use the harnesses in a component test: + +```typescript +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {HarnessLoader} from '@angular/cdk/testing'; +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {TreeHarness} from '@angular/aria/tree/testing'; +import {MyTreeComponent} from './my-tree'; // Your component + +describe('MyTreeComponent', () => { + let fixture: ComponentFixture; + let loader: HarnessLoader; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [MyTreeComponent], + }); + + fixture = TestBed.createComponent(MyTreeComponent); + await fixture.whenStable(); + loader = TestbedHarnessEnvironment.loader(fixture); + }); + + it('should navigate and expand tree items', async () => { + const tree = await loader.getHarness(TreeHarness); + + // Get top-level structure representation + expect(await tree.getTreeStructure()).toEqual({ + children: [{text: 'public'}, {text: 'src'}, {text: 'package.json'}], + }); + + // Get all items (currently visible) + const items = await tree.getItems(); + expect(items.length).toBe(3); + + // Expand the first item ('public') + expect(await items[0].isExpanded()).toBe(false); + await items[0].click(); + expect(await items[0].isExpanded()).toBe(true); + + // Verifying tree structure updates after expansion + expect(await tree.getTreeStructure()).toEqual({ + children: [ + { + text: 'public', + children: [{text: 'index.html'}, {text: 'styles.css'}], + }, + {text: 'src'}, + {text: 'package.json'}, + ], + }); + }); +}); +``` + ## APIs ### Tree