Skip to content

Commit 63641c5

Browse files
authored
feat(material/sidenav): add test harnesses for container and content elements (#21115)
* Adds test harnesses for `MatDrawerContainer`, `MatDrawerContent`, `MatSidenavContainer` and `MatSidenavContent`. This came up during the original PR review (#16695), but we never followed up on it. * Fixes that `MatSidenavHarness.with` was returning a `MatDrawerHarness`. I had to move all the common code into a new class called `MatDrawerHarnessBase` so TS doesn't complain that the types don't match up.
1 parent 3bfad18 commit 63641c5

11 files changed

Lines changed: 299 additions & 34 deletions
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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.io/license
7+
*/
8+
9+
import {ContentContainerComponentHarness, HarnessPredicate} from '@angular/cdk/testing';
10+
import {DrawerContainerHarnessFilters, DrawerHarnessFilters} from './drawer-harness-filters';
11+
import {MatDrawerContentHarness} from './drawer-content-harness';
12+
import {MatDrawerHarness} from './drawer-harness';
13+
14+
/** Harness for interacting with a standard mat-drawer-container in tests. */
15+
export class MatDrawerContainerHarness extends ContentContainerComponentHarness<string> {
16+
/** The selector for the host element of a `MatDrawerContainer` instance. */
17+
static hostSelector = '.mat-drawer-container';
18+
19+
/**
20+
* Gets a `HarnessPredicate` that can be used to search for a `MatDrawerContainerHarness` that
21+
* meets certain criteria.
22+
* @param options Options for filtering which container instances are considered a match.
23+
* @return a `HarnessPredicate` configured with the given options.
24+
*/
25+
static with(options: DrawerContainerHarnessFilters = {}):
26+
HarnessPredicate<MatDrawerContainerHarness> {
27+
return new HarnessPredicate(MatDrawerContainerHarness, options);
28+
}
29+
30+
/**
31+
* Gets drawers that match particular criteria within the container.
32+
* @param filter Optionally filters which chips are included.
33+
*/
34+
async getDrawers(filter: DrawerHarnessFilters = {}): Promise<MatDrawerHarness[]> {
35+
return this.locatorForAll(MatDrawerHarness.with(filter))();
36+
}
37+
38+
/** Gets the element that has the container's content. */
39+
async getContent(): Promise<MatDrawerContentHarness> {
40+
return this.locatorFor(MatDrawerContentHarness)();
41+
}
42+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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.io/license
7+
*/
8+
9+
import {ContentContainerComponentHarness, HarnessPredicate} from '@angular/cdk/testing';
10+
import {DrawerContentHarnessFilters} from './drawer-harness-filters';
11+
12+
/** Harness for interacting with a standard mat-drawer-content in tests. */
13+
export class MatDrawerContentHarness extends ContentContainerComponentHarness<string> {
14+
/** The selector for the host element of a `MatDrawerContent` instance. */
15+
static hostSelector = '.mat-drawer-content';
16+
17+
/**
18+
* Gets a `HarnessPredicate` that can be used to search for a `MatDrawerContentHarness` that
19+
* meets certain criteria.
20+
* @param options Options for filtering which drawer content instances are considered a match.
21+
* @return a `HarnessPredicate` configured with the given options.
22+
*/
23+
static with(options: DrawerContentHarnessFilters = {}):
24+
HarnessPredicate<MatDrawerContentHarness> {
25+
return new HarnessPredicate(MatDrawerContentHarness, options);
26+
}
27+
}

src/material/sidenav/testing/drawer-harness-filters.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,9 @@ export interface DrawerHarnessFilters extends BaseHarnessFilters {
1313
/** Only find instances whose side is the given value. */
1414
position?: 'start' | 'end';
1515
}
16+
17+
/** A set of criteria that can be used to filter a list of `MatDrawerContainerHarness` instances. */
18+
export interface DrawerContainerHarnessFilters extends BaseHarnessFilters {}
19+
20+
/** A set of criteria that can be used to filter a list of `MatDrawerContentHarness` instances. */
21+
export interface DrawerContentHarnessFilters extends BaseHarnessFilters {}

src/material/sidenav/testing/drawer-harness.ts

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,11 @@
99
import {ContentContainerComponentHarness, HarnessPredicate} from '@angular/cdk/testing';
1010
import {DrawerHarnessFilters} from './drawer-harness-filters';
1111

12-
/** Harness for interacting with a standard mat-drawer in tests. */
13-
export class MatDrawerHarness extends ContentContainerComponentHarness<string> {
14-
/** The selector for the host element of a `MatDrawer` instance. */
15-
static hostSelector = '.mat-drawer';
16-
17-
/**
18-
* Gets a `HarnessPredicate` that can be used to search for a `MatDrawerHarness` that meets
19-
* certain criteria.
20-
* @param options Options for filtering which drawer instances are considered a match.
21-
* @return a `HarnessPredicate` configured with the given options.
22-
*/
23-
static with(options: DrawerHarnessFilters = {}): HarnessPredicate<MatDrawerHarness> {
24-
return new HarnessPredicate(MatDrawerHarness, options)
25-
.addOption('position', options.position,
26-
async (harness, position) => (await harness.getPosition()) === position);
27-
}
28-
12+
/**
13+
* Base class for the drawer harness functionality.
14+
* @docs-private
15+
*/
16+
export class MatDrawerHarnessBase extends ContentContainerComponentHarness<string> {
2917
/** Whether the drawer is open. */
3018
async isOpen(): Promise<boolean> {
3119
return (await this.host()).hasClass('mat-drawer-opened');
@@ -52,3 +40,21 @@ export class MatDrawerHarness extends ContentContainerComponentHarness<string> {
5240
return 'over';
5341
}
5442
}
43+
44+
/** Harness for interacting with a standard mat-drawer in tests. */
45+
export class MatDrawerHarness extends MatDrawerHarnessBase {
46+
/** The selector for the host element of a `MatDrawer` instance. */
47+
static hostSelector = '.mat-drawer';
48+
49+
/**
50+
* Gets a `HarnessPredicate` that can be used to search for a `MatDrawerHarness` that meets
51+
* certain criteria.
52+
* @param options Options for filtering which drawer instances are considered a match.
53+
* @return a `HarnessPredicate` configured with the given options.
54+
*/
55+
static with(options: DrawerHarnessFilters = {}): HarnessPredicate<MatDrawerHarness> {
56+
return new HarnessPredicate(MatDrawerHarness, options)
57+
.addOption('position', options.position,
58+
async (harness, position) => (await harness.getPosition()) === position);
59+
}
60+
}

src/material/sidenav/testing/public-api.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
export * from './drawer-harness';
9+
export {MatDrawerHarness} from './drawer-harness';
10+
export * from './drawer-container-harness';
11+
export * from './drawer-content-harness';
1012
export * from './drawer-harness-filters';
13+
export * from './sidenav-container-harness';
14+
export * from './sidenav-content-harness';
1115
export * from './sidenav-harness';

src/material/sidenav/testing/shared.spec.ts

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
1-
import {HarnessLoader} from '@angular/cdk/testing';
1+
import {HarnessLoader, parallel} from '@angular/cdk/testing';
22
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
33
import {Component} from '@angular/core';
44
import {ComponentFixture, TestBed} from '@angular/core/testing';
55
import {MatSidenavModule} from '@angular/material/sidenav';
6-
import {MatSidenavHarness} from '@angular/material/sidenav/testing/sidenav-harness';
76
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
7+
import {MatDrawerContainerHarness} from './drawer-container-harness';
8+
import {MatDrawerContentHarness} from './drawer-content-harness';
89
import {MatDrawerHarness} from './drawer-harness';
10+
import {MatSidenavContainerHarness} from './sidenav-container-harness';
11+
import {MatSidenavContentHarness} from './sidenav-content-harness';
12+
import {MatSidenavHarness} from './sidenav-harness';
913

1014
/** Shared tests to run on both the original and MDC-based drawer & sidenav. */
1115
export function runHarnessTests(sidenavModule: typeof MatSidenavModule,
1216
drawerHarness: typeof MatDrawerHarness,
13-
sidenavHarness: typeof MatSidenavHarness) {
14-
describe('MatDrawerHarness', () => {
17+
drawerContainerHarness: typeof MatDrawerContainerHarness,
18+
drawerContentHarness: typeof MatDrawerContentHarness,
19+
sidenavHarness: typeof MatSidenavHarness,
20+
sidenavContainerHarness: typeof MatSidenavContainerHarness,
21+
sidenavContentHarness: typeof MatSidenavContentHarness) {
22+
describe('drawer', () => {
1523
let fixture: ComponentFixture<DrawerHarnessTest>;
1624
let loader: HarnessLoader;
1725

@@ -67,9 +75,41 @@ export function runHarnessTests(sidenavModule: typeof MatSidenavModule,
6775
expect(await drawers[1].getMode()).toBe('side');
6876
expect(await drawers[2].getMode()).toBe('push');
6977
});
78+
79+
it('should load all drawer container harnesses', async () => {
80+
const containers = await loader.getAllHarnesses(drawerContainerHarness);
81+
expect(containers.length).toBe(2);
82+
});
83+
84+
it('should get the drawers within a container', async () => {
85+
const containers = await loader.getAllHarnesses(drawerContainerHarness);
86+
const [firstContainerDrawers, secondContainerDrawers] = await parallel(() => {
87+
return containers.map(container => container.getDrawers());
88+
});
89+
90+
expect(await parallel(() => {
91+
return firstContainerDrawers.map(async container => (await container.host()).text());
92+
})).toEqual(['One', 'Two']);
93+
94+
expect(await parallel(() => {
95+
return secondContainerDrawers.map(async container => (await container.host()).text());
96+
})).toEqual(['Three']);
97+
});
98+
99+
it('should get the content of a container', async () => {
100+
const container = await loader.getHarness(drawerContainerHarness);
101+
const content = await container.getContent();
102+
expect(await (await content.host()).text()).toBe('Content');
103+
});
104+
105+
it('should load all drawer content harnesses', async () => {
106+
const contentElements = await loader.getAllHarnesses(drawerContentHarness);
107+
expect(contentElements.length).toBe(2);
108+
});
109+
70110
});
71111

72-
describe('MatSidenavHarness', () => {
112+
describe('sidenav', () => {
73113
let fixture: ComponentFixture<SidenavHarnessTest>;
74114
let loader: HarnessLoader;
75115

@@ -91,6 +131,38 @@ export function runHarnessTests(sidenavModule: typeof MatSidenavModule,
91131
expect(await sidenavs[1].isFixedInViewport()).toBe(false);
92132
expect(await sidenavs[2].isFixedInViewport()).toBe(true);
93133
});
134+
135+
it('should load all sidenav container harnesses', async () => {
136+
const containers = await loader.getAllHarnesses(sidenavContainerHarness);
137+
expect(containers.length).toBe(2);
138+
});
139+
140+
it('should get the sidenavs within a container', async () => {
141+
const containers = await loader.getAllHarnesses(sidenavContainerHarness);
142+
const [firstContainerSidenavs, secondContainerSidenavs] = await parallel(() => {
143+
return containers.map(container => container.getSidenavs());
144+
});
145+
146+
expect(await parallel(() => {
147+
return firstContainerSidenavs.map(async container => (await container.host()).text());
148+
})).toEqual(['One', 'Two']);
149+
150+
expect(await parallel(() => {
151+
return secondContainerSidenavs.map(async container => (await container.host()).text());
152+
})).toEqual(['Three']);
153+
});
154+
155+
it('should get the content of a container', async () => {
156+
const container = await loader.getHarness(sidenavContainerHarness);
157+
const content = await container.getContent();
158+
expect(await (await content.host()).text()).toBe('Content');
159+
});
160+
161+
it('should load all sidenav content harnesses', async () => {
162+
const contentElements = await loader.getAllHarnesses(sidenavContentHarness);
163+
expect(contentElements.length).toBe(2);
164+
});
165+
94166
});
95167
}
96168

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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.io/license
7+
*/
8+
9+
import {ContentContainerComponentHarness, HarnessPredicate} from '@angular/cdk/testing';
10+
import {DrawerContainerHarnessFilters, DrawerHarnessFilters} from './drawer-harness-filters';
11+
import {MatSidenavContentHarness} from './sidenav-content-harness';
12+
import {MatSidenavHarness} from './sidenav-harness';
13+
14+
/** Harness for interacting with a standard mat-sidenav-container in tests. */
15+
export class MatSidenavContainerHarness extends ContentContainerComponentHarness<string> {
16+
/** The selector for the host element of a `MatSidenavContainer` instance. */
17+
static hostSelector = '.mat-sidenav-container';
18+
19+
/**
20+
* Gets a `HarnessPredicate` that can be used to search for a `MatSidenavContainerHarness` that
21+
* meets certain criteria.
22+
* @param options Options for filtering which container instances are considered a match.
23+
* @return a `HarnessPredicate` configured with the given options.
24+
*/
25+
static with(options: DrawerContainerHarnessFilters = {}):
26+
HarnessPredicate<MatSidenavContainerHarness> {
27+
return new HarnessPredicate(MatSidenavContainerHarness, options);
28+
}
29+
30+
/**
31+
* Gets sidenavs that match particular criteria within the container.
32+
* @param filter Optionally filters which chips are included.
33+
*/
34+
async getSidenavs(filter: DrawerHarnessFilters = {}): Promise<MatSidenavHarness[]> {
35+
return this.locatorForAll(MatSidenavHarness.with(filter))();
36+
}
37+
38+
/** Gets the element that has the container's content. */
39+
async getContent(): Promise<MatSidenavContentHarness> {
40+
return this.locatorFor(MatSidenavContentHarness)();
41+
}
42+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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.io/license
7+
*/
8+
9+
import {ContentContainerComponentHarness, HarnessPredicate} from '@angular/cdk/testing';
10+
import {DrawerContentHarnessFilters} from './drawer-harness-filters';
11+
12+
/** Harness for interacting with a standard mat-sidenav-content in tests. */
13+
export class MatSidenavContentHarness extends ContentContainerComponentHarness<string> {
14+
/** The selector for the host element of a `MatSidenavContent` instance. */
15+
static hostSelector = '.mat-sidenav-content';
16+
17+
/**
18+
* Gets a `HarnessPredicate` that can be used to search for a `MatSidenavContentHarness` that
19+
* meets certain criteria.
20+
* @param options Options for filtering which sidenav content instances are considered a match.
21+
* @return a `HarnessPredicate` configured with the given options.
22+
*/
23+
static with(options: DrawerContentHarnessFilters = {}):
24+
HarnessPredicate<MatSidenavContentHarness> {
25+
return new HarnessPredicate(MatSidenavContentHarness, options);
26+
}
27+
}
Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
11
import {MatSidenavModule} from '@angular/material/sidenav';
22
import {runHarnessTests} from '@angular/material/sidenav/testing/shared.spec';
3+
import {MatDrawerContainerHarness} from './drawer-container-harness';
4+
import {MatDrawerContentHarness} from './drawer-content-harness';
35
import {MatDrawerHarness} from './drawer-harness';
6+
import {MatSidenavContainerHarness} from './sidenav-container-harness';
7+
import {MatSidenavContentHarness} from './sidenav-content-harness';
48
import {MatSidenavHarness} from './sidenav-harness';
59

610
describe('Non-MDC-based', () => {
7-
runHarnessTests(MatSidenavModule, MatDrawerHarness, MatSidenavHarness);
11+
runHarnessTests(
12+
MatSidenavModule,
13+
MatDrawerHarness,
14+
MatDrawerContainerHarness,
15+
MatDrawerContentHarness,
16+
MatSidenavHarness,
17+
MatSidenavContainerHarness,
18+
MatSidenavContentHarness
19+
);
820
});

src/material/sidenav/testing/sidenav-harness.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
*/
88

99
import {HarnessPredicate} from '@angular/cdk/testing';
10-
import {MatDrawerHarness} from './drawer-harness';
10+
import {MatDrawerHarnessBase} from './drawer-harness';
1111
import {DrawerHarnessFilters} from './drawer-harness-filters';
1212

1313
/** Harness for interacting with a standard mat-sidenav in tests. */
14-
export class MatSidenavHarness extends MatDrawerHarness {
14+
export class MatSidenavHarness extends MatDrawerHarnessBase {
1515
/** The selector for the host element of a `MatSidenav` instance. */
1616
static hostSelector = '.mat-sidenav';
1717

@@ -21,8 +21,8 @@ export class MatSidenavHarness extends MatDrawerHarness {
2121
* @param options Options for filtering which sidenav instances are considered a match.
2222
* @return a `HarnessPredicate` configured with the given options.
2323
*/
24-
static with(options: DrawerHarnessFilters = {}): HarnessPredicate<MatDrawerHarness> {
25-
return new HarnessPredicate(MatDrawerHarness, options)
24+
static with(options: DrawerHarnessFilters = {}): HarnessPredicate<MatSidenavHarness> {
25+
return new HarnessPredicate(MatSidenavHarness, options)
2626
.addOption('position', options.position,
2727
async (harness, position) => (await harness.getPosition()) === position);
2828
}

0 commit comments

Comments
 (0)