Skip to content

Commit d91f46b

Browse files
authored
feat(aria/accordion): introduce accordion harness (#33046)
* feat(aria/accordion): introduce accordion harness * refactor(aria/accordion): consolidate accordion harness to match material expansion harness * fixup! refactor(aria/accordion): consolidate accordion harness to match material expansion harness * fixup! refactor(aria/accordion): consolidate accordion harness to match material expansion harness
1 parent ad5fba3 commit d91f46b

9 files changed

Lines changed: 415 additions & 0 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
## API Report File for "@angular/aria_accordion_testing"
2+
3+
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
4+
5+
```ts
6+
7+
import * as _angular_cdk_testing from '@angular/cdk/testing';
8+
import { BaseHarnessFilters } from '@angular/cdk/testing';
9+
import { ComponentHarness } from '@angular/cdk/testing';
10+
import { ContentContainerComponentHarness } from '@angular/cdk/testing';
11+
import { HarnessPredicate } from '@angular/cdk/testing';
12+
13+
// @public
14+
export class AccordionGroupHarness extends ComponentHarness {
15+
getAccordions(filters?: AccordionHarnessFilters): Promise<AccordionHarness[]>;
16+
// (undocumented)
17+
static hostSelector: string;
18+
static with(options?: AccordionGroupHarnessFilters): HarnessPredicate<AccordionGroupHarness>;
19+
}
20+
21+
// @public
22+
export interface AccordionGroupHarnessFilters extends BaseHarnessFilters {
23+
}
24+
25+
// @public
26+
export class AccordionHarness extends ContentContainerComponentHarness<AccordionSection> {
27+
blur(): Promise<void>;
28+
collapse(): Promise<void>;
29+
expand(): Promise<void>;
30+
focus(): Promise<void>;
31+
protected getRootHarnessLoader(): Promise<_angular_cdk_testing.HarnessLoader>;
32+
getTitle(): Promise<string>;
33+
// (undocumented)
34+
static hostSelector: string;
35+
isDisabled(): Promise<boolean>;
36+
isExpanded(): Promise<boolean>;
37+
isFocused(): Promise<boolean>;
38+
toggle(): Promise<void>;
39+
static with(options?: AccordionHarnessFilters): HarnessPredicate<AccordionHarness>;
40+
}
41+
42+
// @public
43+
export interface AccordionHarnessFilters extends BaseHarnessFilters {
44+
disabled?: boolean;
45+
expanded?: boolean;
46+
title?: string | RegExp;
47+
}
48+
49+
// @public
50+
export enum AccordionSection {
51+
// (undocumented)
52+
PANEL = "[ngAccordionPanel]",
53+
// (undocumented)
54+
TRIGGER = "[ngAccordionTrigger]"
55+
}
56+
57+
// (No @packageDocumentation comment for this package)
58+
59+
```

src/aria/accordion/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ ng_project(
1313
"//src/aria/private",
1414
"//src/cdk/a11y",
1515
"//src/cdk/bidi",
16+
"//src/cdk/testing",
1617
],
1718
)
1819

@@ -26,7 +27,9 @@ ng_project(
2627
":accordion",
2728
"//:node_modules/@angular/core",
2829
"//:node_modules/@angular/platform-browser",
30+
"//src/cdk/testing",
2931
"//src/cdk/testing/private",
32+
"//src/cdk/testing/testbed",
3033
],
3134
)
3235

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
load("//tools:defaults.bzl", "ng_project", "ng_web_test_suite", "ts_project")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
ts_project(
6+
name = "testing",
7+
srcs = glob(
8+
["**/*.ts"],
9+
exclude = ["**/*.spec.ts"],
10+
),
11+
deps = [
12+
"//:node_modules/@angular/core",
13+
"//src/cdk/testing",
14+
],
15+
)
16+
17+
filegroup(
18+
name = "source-files",
19+
srcs = glob(["**/*.ts"]),
20+
)
21+
22+
ng_project(
23+
name = "unit_tests_lib",
24+
testonly = True,
25+
srcs = glob(["**/*.spec.ts"]),
26+
deps = [
27+
":testing",
28+
"//:node_modules/@angular/core",
29+
"//:node_modules/@angular/platform-browser",
30+
"//src/aria/accordion",
31+
"//src/cdk/testing",
32+
"//src/cdk/testing/private",
33+
"//src/cdk/testing/testbed",
34+
],
35+
)
36+
37+
ng_web_test_suite(
38+
name = "unit_tests",
39+
deps = [
40+
":unit_tests_lib",
41+
],
42+
)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import {BaseHarnessFilters} from '@angular/cdk/testing';
10+
11+
/** Filters for locating an `AccordionHarness`. */
12+
export interface AccordionHarnessFilters extends BaseHarnessFilters {
13+
/** Only find instances whose title text matches the given value. */
14+
title?: string | RegExp;
15+
/** Only find instances whose expanded state matches the given value. */
16+
expanded?: boolean;
17+
/** Only find instances whose disabled state matches the given value. */
18+
disabled?: boolean;
19+
}
20+
21+
/** Filters for locating an `AccordionGroupHarness`. */
22+
export interface AccordionGroupHarnessFilters extends BaseHarnessFilters {}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import {Component} from '@angular/core';
10+
import {TestBed} from '@angular/core/testing';
11+
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
12+
import {ComponentHarness} from '@angular/cdk/testing';
13+
import {AccordionHarness, AccordionGroupHarness} from './accordion-harness';
14+
import {AccordionGroup, AccordionPanel, AccordionTrigger} from '../index';
15+
16+
/** Lightweight test harness to test querying inside the accordion body panel. */
17+
class TestButtonHarness extends ComponentHarness {
18+
static hostSelector = 'button.test-button';
19+
20+
async getText(): Promise<string> {
21+
return (await this.host()).text();
22+
}
23+
}
24+
25+
describe('Accordion Harnesses', () => {
26+
let fixture: any;
27+
let loader: any;
28+
29+
@Component({
30+
imports: [AccordionGroup, AccordionPanel, AccordionTrigger],
31+
template: `
32+
<div ngAccordionGroup>
33+
<div #panel1="ngAccordionPanel" ngAccordionPanel>
34+
<button class="test-button">Inside Content 1</button>
35+
</div>
36+
<button ngAccordionTrigger [panel]="panel1">Section 1</button>
37+
38+
<div #panel2="ngAccordionPanel" ngAccordionPanel>Content 2</div>
39+
<button ngAccordionTrigger [panel]="panel2" disabled>Section 2</button>
40+
</div>
41+
`,
42+
})
43+
class AccordionHarnessTestComponent {}
44+
45+
beforeEach(() => {
46+
TestBed.configureTestingModule({
47+
imports: [AccordionHarnessTestComponent],
48+
});
49+
fixture = TestBed.createComponent(AccordionHarnessTestComponent);
50+
fixture.detectChanges();
51+
loader = TestbedHarnessEnvironment.loader(fixture);
52+
});
53+
54+
it('should find accordion group and list all scoped accordions using getAccordions', async () => {
55+
const group = await loader.getHarness(AccordionGroupHarness);
56+
const accordions = await group.getAccordions();
57+
58+
expect(accordions.length).toBe(2);
59+
expect(await accordions[0].getTitle()).toBe('Section 1');
60+
expect(await accordions[1].getTitle()).toBe('Section 2');
61+
});
62+
63+
it('should find all individual accordions via standard root loader', async () => {
64+
const accordions = await loader.getAllHarnesses(AccordionHarness);
65+
expect(accordions.length).toBe(2);
66+
});
67+
68+
it('should filter accordions by title', async () => {
69+
const accordions = await loader.getAllHarnesses(AccordionHarness.with({title: 'Section 1'}));
70+
expect(accordions.length).toBe(1);
71+
expect(await accordions[0].getTitle()).toBe('Section 1');
72+
});
73+
74+
it('should filter accordions by expanded state', async () => {
75+
const accordion = await loader.getHarness(AccordionHarness.with({title: 'Section 1'}));
76+
await accordion.expand();
77+
78+
const expandedAccordions = await loader.getAllHarnesses(
79+
AccordionHarness.with({expanded: true}),
80+
);
81+
expect(expandedAccordions.length).toBe(1);
82+
expect(await expandedAccordions[0].getTitle()).toBe('Section 1');
83+
});
84+
85+
it('should filter accordions by disabled state', async () => {
86+
const disabledAccordions = await loader.getAllHarnesses(
87+
AccordionHarness.with({disabled: true}),
88+
);
89+
expect(disabledAccordions.length).toBe(1);
90+
expect(await disabledAccordions[0].getTitle()).toBe('Section 2');
91+
});
92+
93+
it('should get the title of the accordion', async () => {
94+
const accordion = await loader.getHarness(AccordionHarness.with({title: 'Section 1'}));
95+
expect(await accordion.getTitle()).toBe('Section 1');
96+
});
97+
98+
it('should correctly report the expanded state of an accordion', async () => {
99+
const accordion = await loader.getHarness(AccordionHarness.with({title: 'Section 1'}));
100+
expect(await accordion.isExpanded()).toBeFalse();
101+
});
102+
103+
it('should correctly report the disabled state of an accordion', async () => {
104+
const activeAccordion = await loader.getHarness(AccordionHarness.with({title: 'Section 1'}));
105+
const disabledAccordion = await loader.getHarness(AccordionHarness.with({title: 'Section 2'}));
106+
107+
expect(await activeAccordion.isDisabled()).toBeFalse();
108+
expect(await disabledAccordion.isDisabled()).toBeTrue();
109+
});
110+
111+
it('expands a collapsed accordion using the expand method', async () => {
112+
const accordion = await loader.getHarness(AccordionHarness.with({title: 'Section 1'}));
113+
114+
await accordion.expand();
115+
116+
expect(await accordion.isExpanded()).toBeTrue();
117+
});
118+
119+
it('collapses an expanded accordion using the collapse method', async () => {
120+
const accordion = await loader.getHarness(AccordionHarness.with({title: 'Section 1'}));
121+
await accordion.expand();
122+
123+
await accordion.collapse();
124+
125+
expect(await accordion.isExpanded()).toBeFalse();
126+
});
127+
128+
it('toggles the expanded state of an accordion using the toggle method', async () => {
129+
const accordion = await loader.getHarness(AccordionHarness.with({title: 'Section 1'}));
130+
131+
await accordion.toggle();
132+
133+
expect(await accordion.isExpanded()).toBeTrue();
134+
});
135+
136+
it('should support focusing and blurring accordion triggers', async () => {
137+
const accordion = await loader.getHarness(AccordionHarness.with({title: 'Section 1'}));
138+
await accordion.focus();
139+
expect(await accordion.isFocused()).toBeTrue();
140+
141+
await accordion.blur();
142+
expect(await accordion.isFocused()).toBeFalse();
143+
});
144+
145+
it('should query components inside the accordion panel using ContentContainerComponentHarness', async () => {
146+
const accordion = await loader.getHarness(AccordionHarness.with({title: 'Section 1'}));
147+
const button = await accordion.getHarness(TestButtonHarness);
148+
expect(await button.getText()).toBe('Inside Content 1');
149+
});
150+
});

0 commit comments

Comments
 (0)