Skip to content

Commit 771f728

Browse files
committed
docs: add testing guides to Angular Aria
1 parent 34090cb commit 771f728

12 files changed

Lines changed: 617 additions & 2 deletions

File tree

adev/src/content/guide/aria/accordion.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,53 @@ Use the `ngAccordionContent` directive on an `ng-template` to defer rendering co
158158
```
159159

160160
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.
161+
## Testing
162+
163+
Angular Aria provides component harnesses for testing accordion components.
164+
Here is an example of how to use the harnesses in a component test:
165+
166+
```typescript
167+
import {ComponentFixture, TestBed} from '@angular/core/testing';
168+
import {TestbedHarnessLoader} from '@angular/cdk/testing/testbed';
169+
import {AccordionGroupHarness} from '@angular/aria/accordion/testing';
170+
import {MyAccordionComponent} from './my-accordion'; // Your component
171+
172+
describe('MyAccordionComponent', () => {
173+
let fixture: ComponentFixture<MyAccordionComponent>;
174+
let loader: TestbedHarnessLoader;
175+
176+
beforeEach(async () => {
177+
await TestBed.configureTestingModule({
178+
imports: [MyAccordionComponent],
179+
}).compileComponents();
180+
181+
fixture = TestBed.createComponent(MyAccordionComponent);
182+
fixture.detectChanges();
183+
loader = TestbedHarnessLoader.create(fixture);
184+
});
185+
186+
it('should allow expanding panels', async () => {
187+
// Load the accordion group harness
188+
const group = await loader.getHarness(AccordionGroupHarness);
189+
190+
// Get all individual accordions (items) in the group
191+
const accordions = await group.getAccordions();
192+
expect(accordions.length).toBe(3);
193+
194+
// Verify initial state (first expanded, others collapsed)
195+
expect(await accordions[0].isExpanded()).toBe(true);
196+
expect(await accordions[1].isExpanded()).toBe(false);
197+
198+
// Expand the second panel
199+
await accordions[1].expand();
200+
201+
// Verify updated state
202+
expect(await accordions[1].isExpanded()).toBe(true);
203+
// If multiExpandable is false, the first one should now be collapsed
204+
expect(await accordions[0].isExpanded()).toBe(false);
205+
});
206+
});
207+
```
161208

162209
## APIs
163210

adev/src/content/guide/aria/autocomplete.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,56 @@ Highlight mode allows the user to navigate options with arrow keys without chang
148148
</docs-code-multifile>
149149
</docs-tab>
150150
</docs-tab-group>
151+
## Testing
152+
153+
The autocomplete pattern can be tested using a combination of `ComboboxHarness` and `ListboxHarness` from `@angular/aria/combobox/testing` and `@angular/aria/listbox/testing`.
154+
Here is an example of how to use the harnesses to test an autocomplete component:
155+
156+
```typescript
157+
import {ComponentFixture, TestBed} from '@angular/core/testing';
158+
import {TestbedHarnessLoader} from '@angular/cdk/testing/testbed';
159+
import {ComboboxHarness} from '@angular/aria/combobox/testing';
160+
import {ListboxHarness} from '@angular/aria/listbox/testing';
161+
import {MyAutocompleteComponent} from './my-autocomplete'; // Your component
162+
163+
describe('MyAutocompleteComponent', () => {
164+
let fixture: ComponentFixture<MyAutocompleteComponent>;
165+
let loader: TestbedHarnessLoader;
166+
167+
beforeEach(async () => {
168+
await TestBed.configureTestingModule({
169+
imports: [MyAutocompleteComponent],
170+
}).compileComponents();
171+
172+
fixture = TestBed.createComponent(MyAutocompleteComponent);
173+
fixture.detectChanges();
174+
loader = TestbedHarnessLoader.create(fixture);
175+
});
176+
177+
it('should filter options based on input', async () => {
178+
const combobox = await loader.getHarness(ComboboxHarness);
179+
180+
// Type in the input to trigger filtering
181+
await combobox.setValue('ap');
182+
expect(await combobox.isOpen()).toBe(true);
183+
184+
// Get the listbox harness from the popup
185+
const listbox = await combobox.getPopupWidget(ListboxHarness);
186+
const options = await listbox.getOptions();
187+
188+
// Verify options are filtered (e.g., 'Apple', 'Apricot')
189+
expect(options.length).toBe(2);
190+
expect(await options[0].getText()).toBe('Apple');
191+
192+
// Select the first option
193+
await options[0].click();
194+
195+
// Verify the input value is updated and popup is closed
196+
expect(await combobox.isOpen()).toBe(false);
197+
expect(await combobox.getValue()).toBe('Apple');
198+
});
199+
});
200+
```
151201

152202
## APIs
153203

adev/src/content/guide/aria/combobox.md

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ Combobox can coordinate with a two-dimensional grid to create accessible datepic
164164

165165
### Dialog popup
166166

167-
Popups sometimes need modal behavior with a backdrop and focus trap. The combobox dialog directive provides this pattern for specialized use cases.
167+
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.
168168

169169
<docs-tab-group>
170170
<docs-tab label="Basic">
@@ -192,7 +192,47 @@ Popups sometimes need modal behavior with a backdrop and focus trap. The combobo
192192
</docs-tab>
193193
</docs-tab-group>
194194

195-
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.
195+
## Testing
196+
197+
Angular Aria provides a `ComboboxHarness` for testing combobox components.
198+
Here is an example of how to use the harness in a component test:
199+
200+
```typescript
201+
import {ComponentFixture, TestBed} from '@angular/core/testing';
202+
import {TestbedHarnessLoader} from '@angular/cdk/testing/testbed';
203+
import {ComboboxHarness} from '@angular/aria/combobox/testing';
204+
import {MyComboboxComponent} from './my-combobox'; // Your component
205+
206+
describe('MyComboboxComponent', () => {
207+
let fixture: ComponentFixture<MyComboboxComponent>;
208+
let loader: TestbedHarnessLoader;
209+
210+
beforeEach(async () => {
211+
await TestBed.configureTestingModule({
212+
imports: [MyComboboxComponent],
213+
}).compileComponents();
214+
215+
fixture = TestBed.createComponent(MyComboboxComponent);
216+
fixture.detectChanges();
217+
loader = TestbedHarnessLoader.create(fixture);
218+
});
219+
220+
it('should allow opening and closing the popup', async () => {
221+
const combobox = await loader.getHarness(ComboboxHarness);
222+
223+
// Verify initial state
224+
expect(await combobox.isOpen()).toBe(false);
225+
226+
// Open the popup
227+
await combobox.open();
228+
expect(await combobox.isOpen()).toBe(true);
229+
230+
// Close the popup
231+
await combobox.close();
232+
expect(await combobox.isOpen()).toBe(false);
233+
});
234+
});
235+
```
196236

197237
## APIs
198238

adev/src/content/guide/aria/grid.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,56 @@ Enable selection with `[enableSelection]="true"` and configure how focus and sel
158158

159159
- `roving`: Focus moves to cells using `tabindex` (better for simple grids)
160160
- `activedescendant`: Focus stays on grid container, `aria-activedescendant` indicates active cell (better for virtual scrolling)
161+
## Testing
162+
163+
Angular Aria provides component harnesses for testing grid components.
164+
Here is an example of how to use the harnesses in a component test:
165+
166+
```typescript
167+
import {ComponentFixture, TestBed} from '@angular/core/testing';
168+
import {TestbedHarnessLoader} from '@angular/cdk/testing/testbed';
169+
import {GridHarness} from '@angular/aria/grid/testing';
170+
import {MyGridComponent} from './my-grid'; // Your component
171+
172+
describe('MyGridComponent', () => {
173+
let fixture: ComponentFixture<MyGridComponent>;
174+
let loader: TestbedHarnessLoader;
175+
176+
beforeEach(async () => {
177+
await TestBed.configureTestingModule({
178+
imports: [MyGridComponent],
179+
}).compileComponents();
180+
181+
fixture = TestBed.createComponent(MyGridComponent);
182+
fixture.detectChanges();
183+
loader = TestbedHarnessLoader.create(fixture);
184+
});
185+
186+
it('should read cell values and focus cells', async () => {
187+
const grid = await loader.getHarness(GridHarness);
188+
189+
// Get all cells text in a 2D array organized by rows
190+
const cellTexts = await grid.getCellTextByIndex();
191+
expect(cellTexts).toEqual([
192+
['Cell 1.1', 'Cell 1.2'],
193+
['Cell 2.1', 'Cell 2.2']
194+
]);
195+
196+
// Get a specific cell by text
197+
const cells = await grid.getCells({text: 'Cell 1.1'});
198+
expect(cells.length).toBe(1);
199+
const cell = cells[0];
200+
201+
// Verify cell state
202+
expect(await cell.isSelected()).toBe(true);
203+
expect(await cell.isActive()).toBe(true);
204+
205+
// Focus the cell
206+
await cell.focus();
207+
expect(await cell.isFocused()).toBe(true);
208+
});
209+
});
210+
```
161211

162212
## APIs
163213

adev/src/content/guide/aria/listbox.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,53 @@ The `'follow'` mode automatically selects the focused item, providing faster int
132132
| `'explicit'` | Requires Space or Enter to confirm selection, preventing accidental changes while navigating |
133133

134134
TIP: Dropdown patterns typically use `'follow'` mode for single selection.
135+
## Testing
136+
137+
Angular Aria provides component harnesses for testing listbox components.
138+
Here is an example of how to use the harnesses in a component test:
139+
140+
```typescript
141+
import {ComponentFixture, TestBed} from '@angular/core/testing';
142+
import {TestbedHarnessLoader} from '@angular/cdk/testing/testbed';
143+
import {ListboxHarness} from '@angular/aria/listbox/testing';
144+
import {MyListboxComponent} from './my-listbox'; // Your component
145+
146+
describe('MyListboxComponent', () => {
147+
let fixture: ComponentFixture<MyListboxComponent>;
148+
let loader: TestbedHarnessLoader;
149+
150+
beforeEach(async () => {
151+
await TestBed.configureTestingModule({
152+
imports: [MyListboxComponent],
153+
}).compileComponents();
154+
155+
fixture = TestBed.createComponent(MyListboxComponent);
156+
fixture.detectChanges();
157+
loader = TestbedHarnessLoader.create(fixture);
158+
});
159+
160+
it('should allow selecting options', async () => {
161+
const listbox = await loader.getHarness(ListboxHarness);
162+
163+
// Verify listbox properties
164+
expect(await listbox.isMulti()).toBe(true);
165+
166+
// Get all options
167+
const options = await listbox.getOptions();
168+
expect(options.length).toBe(2);
169+
170+
// Click an option
171+
await options[0].click();
172+
173+
// Verify option is selected
174+
expect(await options[0].isSelected()).toBe(true);
175+
176+
// Filter options by text
177+
const bananaOption = await listbox.getOptions({text: 'Banana'});
178+
expect(bananaOption.length).toBe(1);
179+
});
180+
});
181+
```
135182

136183
## APIs
137184

adev/src/content/guide/aria/menu.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,74 @@ Disable specific menu items using the `disabled` input. Control focus behavior w
173173
</docs-tab-group>
174174

175175
When `[softDisabled]="true"`, disabled items can receive focus but cannot be activated. When `[softDisabled]="false"`, disabled items are skipped during keyboard navigation.
176+
## Testing
177+
178+
Angular Aria provides component harnesses for testing menu components.
179+
Here is an example of how to use the harnesses in a component test:
180+
181+
```typescript
182+
import {ComponentFixture, TestBed} from '@angular/core/testing';
183+
import {TestbedHarnessLoader} from '@angular/cdk/testing/testbed';
184+
import {MenuHarness} from '@angular/aria/menu/testing';
185+
import {MyMenuComponent} from './my-menu'; // Your component
186+
187+
describe('MyMenuComponent', () => {
188+
let fixture: ComponentFixture<MyMenuComponent>;
189+
let loader: TestbedHarnessLoader;
190+
191+
beforeEach(async () => {
192+
await TestBed.configureTestingModule({
193+
imports: [MyMenuComponent],
194+
}).compileComponents();
195+
196+
fixture = TestBed.createComponent(MyMenuComponent);
197+
fixture.detectChanges();
198+
loader = TestbedHarnessLoader.create(fixture);
199+
});
200+
201+
it('should open menu and click item', async () => {
202+
// Load the menu harness by its trigger text
203+
const menu = await loader.getHarness(MenuHarness.with({triggerText: 'Open Menu'}));
204+
205+
// Verify initial state
206+
expect(await menu.isOpen()).toBe(false);
207+
208+
// Open the menu
209+
await menu.open();
210+
expect(await menu.isOpen()).toBe(true);
211+
212+
// Get items
213+
const items = await menu.getItems();
214+
expect(items.length).toBe(3);
215+
expect(await items[0].getText()).toBe('Item 1');
216+
217+
// Click first item
218+
await items[0].click();
219+
220+
// Menu should close after selection (depending on your implementation)
221+
expect(await menu.isOpen()).toBe(false);
222+
});
223+
224+
it('should interact with submenus', async () => {
225+
const menu = await loader.getHarness(MenuHarness.with({triggerText: 'Open Menu'}));
226+
await menu.open();
227+
228+
// Get the item that triggers a submenu
229+
const subItem = await loader.getHarness(MenuItemHarness.with({text: 'Submenu'}));
230+
expect(await subItem.hasSubmenu()).toBe(true);
231+
232+
// Open submenu
233+
await subItem.click();
234+
const submenu = await subItem.getSubmenu();
235+
expect(submenu).toBeTruthy();
236+
expect(await submenu!.isOpen()).toBe(true);
237+
238+
// Interact with submenu items
239+
const subItems = await submenu!.getItems();
240+
expect(subItems.length).toBe(1);
241+
});
242+
});
243+
```
176244

177245
## APIs
178246

0 commit comments

Comments
 (0)