Skip to content

Commit bb97283

Browse files
committed
feat(core): add test harnesses for mat-option and mat-optgroup
Combines the option and option group harnesses from `select/testing` and `autocomplete/testing`. Also expands the harnesses to cover all of the supported uses of the components.
1 parent d3357b8 commit bb97283

23 files changed

+508
-176
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040

4141
# Angular Material core
4242
/src/material/core/* @jelbourn
43+
/src/material/core/testing/** @crisbeto
4344
/src/material/core/animation/** @jelbourn
4445
/src/material/core/common-behaviors/** @jelbourn @devversion
4546
/src/material/core/datetime/** @mmalerba

src/material/autocomplete/testing/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ ts_library(
1212
deps = [
1313
"//src/cdk/coercion",
1414
"//src/cdk/testing",
15+
"//src/material/core/testing",
1516
],
1617
)
1718

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

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88

99
import {coerceBooleanProperty} from '@angular/cdk/coercion';
1010
import {ComponentHarness, HarnessPredicate} from '@angular/cdk/testing';
11-
import {AutocompleteHarnessFilters} from './autocomplete-harness-filters';
1211
import {
13-
MatAutocompleteOptionGroupHarness,
14-
MatAutocompleteOptionHarness,
15-
OptionGroupHarnessFilters,
12+
MatOptgroupHarness,
13+
MatOptionHarness,
14+
OptgroupHarnessFilters,
1615
OptionHarnessFilters
17-
} from './option-harness';
16+
} from '@angular/material/core/testing';
17+
import {AutocompleteHarnessFilters} from './autocomplete-harness-filters';
1818

1919
/** Selector for the autocomplete panel. */
2020
const PANEL_SELECTOR = '.mat-autocomplete-panel';
@@ -66,15 +66,21 @@ export class MatAutocompleteHarness extends ComponentHarness {
6666
}
6767

6868
/** Gets the options inside the autocomplete panel. */
69-
async getOptions(filters: OptionHarnessFilters = {}): Promise<MatAutocompleteOptionHarness[]> {
70-
return this._documentRootLocator.locatorForAll(MatAutocompleteOptionHarness.with(filters))();
69+
async getOptions(filters: Omit<OptionHarnessFilters, 'ancestor'> = {}):
70+
Promise<MatOptionHarness[]> {
71+
return this._documentRootLocator.locatorForAll(MatOptionHarness.with({
72+
...filters,
73+
ancestor: PANEL_SELECTOR
74+
}))();
7175
}
7276

7377
/** Gets the option groups inside the autocomplete panel. */
74-
async getOptionGroups(filters: OptionGroupHarnessFilters = {}):
75-
Promise<MatAutocompleteOptionGroupHarness[]> {
76-
return this._documentRootLocator.locatorForAll(
77-
MatAutocompleteOptionGroupHarness.with(filters))();
78+
async getOptionGroups(filters: Omit<OptgroupHarnessFilters, 'ancestor'> = {}):
79+
Promise<MatOptgroupHarness[]> {
80+
return this._documentRootLocator.locatorForAll(MatOptgroupHarness.with({
81+
...filters,
82+
ancestor: PANEL_SELECTOR
83+
}))();
7884
}
7985

8086
/** Selects the first option matching the given filters. */
@@ -84,7 +90,7 @@ export class MatAutocompleteHarness extends ComponentHarness {
8490
if (!options.length) {
8591
throw Error(`Could not find a mat-option matching ${JSON.stringify(filters)}`);
8692
}
87-
await options[0].select();
93+
await options[0].click();
8894
}
8995

9096
/** Whether the autocomplete is open. */

src/material/autocomplete/testing/option-harness.ts

Lines changed: 0 additions & 82 deletions
This file was deleted.

src/material/config.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ entryPoints = [
1313
"checkbox/testing",
1414
"chips",
1515
"core",
16+
"core/testing",
1617
"datepicker",
1718
"dialog",
1819
"dialog/testing",

src/material/core/testing/BUILD.bazel

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package(default_visibility = ["//visibility:public"])
2+
3+
load("//tools:defaults.bzl", "ng_test_library", "ng_web_test_suite", "ts_library")
4+
5+
ts_library(
6+
name = "testing",
7+
srcs = glob(
8+
["**/*.ts"],
9+
exclude = ["**/*.spec.ts"],
10+
),
11+
module_name = "@angular/material/core/testing",
12+
deps = [
13+
"//src/cdk/testing",
14+
],
15+
)
16+
17+
filegroup(
18+
name = "source-files",
19+
srcs = glob(["**/*.ts"]),
20+
)
21+
22+
ng_test_library(
23+
name = "harness_tests_lib",
24+
srcs = [
25+
"optgroup-shared.spec.ts",
26+
"option-shared.spec.ts",
27+
],
28+
deps = [
29+
":testing",
30+
"//src/cdk/testing",
31+
"//src/cdk/testing/testbed",
32+
"//src/material/core",
33+
],
34+
)
35+
36+
ng_test_library(
37+
name = "unit_tests_lib",
38+
srcs = glob(
39+
["**/*.spec.ts"],
40+
exclude = [
41+
"option-shared.spec.ts",
42+
"optgroup-shared.spec.ts",
43+
],
44+
),
45+
deps = [
46+
":harness_tests_lib",
47+
":testing",
48+
"//src/material/core",
49+
],
50+
)
51+
52+
ng_web_test_suite(
53+
name = "unit_tests",
54+
deps = [":unit_tests_lib"],
55+
)

src/material/core/testing/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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+
export * from './public-api';
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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 {BaseHarnessFilters} from '@angular/cdk/testing';
10+
11+
export interface OptgroupHarnessFilters extends BaseHarnessFilters {
12+
labelText?: string | RegExp;
13+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {MatOptionModule} from '@angular/material/core';
2+
import {runHarnessTests} from './optgroup-shared.spec';
3+
import {MatOptgroupHarness} from './optgroup-harness';
4+
5+
describe('Non-MDC-based MatOptgroupHarness', () => {
6+
runHarnessTests(MatOptionModule, MatOptgroupHarness);
7+
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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 {ComponentHarness, HarnessPredicate} from '@angular/cdk/testing';
10+
import {OptgroupHarnessFilters} from './optgroup-harness-filters';
11+
import {MatOptionHarness} from './option-harness';
12+
import {OptionHarnessFilters} from './option-harness-filters';
13+
14+
/** Harness for interacting with a `mat-optgroup` in tests. */
15+
export class MatOptgroupHarness extends ComponentHarness {
16+
/** Selector used to locate option group instances. */
17+
static hostSelector = '.mat-optgroup';
18+
private _label = this.locatorFor('.mat-optgroup-label');
19+
20+
/**
21+
* Gets a `HarnessPredicate` that can be used to search for a `MatOptgroupHarness` that meets
22+
* certain criteria.
23+
* @param options Options for filtering which option instances are considered a match.
24+
* @return a `HarnessPredicate` configured with the given options.
25+
*/
26+
static with(options: OptgroupHarnessFilters = {}) {
27+
return new HarnessPredicate(MatOptgroupHarness, options)
28+
.addOption('labelText', options.labelText,
29+
async (harness, title) =>
30+
HarnessPredicate.stringMatches(await harness.getLabelText(), title));
31+
}
32+
33+
/** Gets the option group's label text. */
34+
async getLabelText(): Promise<string> {
35+
return (await this._label()).text();
36+
}
37+
38+
/** Gets whether the option group is disabled. */
39+
async isDisabled(): Promise<boolean> {
40+
return (await this.host()).hasClass('mat-optgroup-disabled');
41+
}
42+
43+
/**
44+
* Gets the options that are inside the group.
45+
* @param filter Optionally filters which options are included.
46+
*/
47+
async getOptions(filter: OptionHarnessFilters = {}): Promise<MatOptionHarness[]> {
48+
return this.locatorForAll(MatOptionHarness.with(filter))();
49+
}
50+
}
51+
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import {HarnessLoader} from '@angular/cdk/testing';
2+
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
3+
import {Component} from '@angular/core';
4+
import {ComponentFixture, TestBed} from '@angular/core/testing';
5+
import {MatOptionModule} from '@angular/material/core';
6+
import {MatOptgroupHarness} from './optgroup-harness';
7+
8+
/** Shared tests to run on both the original and MDC-based option groups. */
9+
export function runHarnessTests(
10+
optionModule: typeof MatOptionModule, optionGroupHarness: typeof MatOptgroupHarness) {
11+
let fixture: ComponentFixture<OptgroupHarnessTest>;
12+
let loader: HarnessLoader;
13+
14+
beforeEach(async () => {
15+
await TestBed.configureTestingModule({
16+
imports: [optionModule],
17+
declarations: [OptgroupHarnessTest],
18+
}).compileComponents();
19+
20+
fixture = TestBed.createComponent(OptgroupHarnessTest);
21+
fixture.detectChanges();
22+
loader = TestbedHarnessEnvironment.loader(fixture);
23+
});
24+
25+
it('should load all option group harnesses', async () => {
26+
const groups = await loader.getAllHarnesses(optionGroupHarness);
27+
expect(groups.length).toBe(2);
28+
});
29+
30+
it('should filter groups based on their text', async () => {
31+
const groups = await loader.getAllHarnesses(optionGroupHarness.with({
32+
labelText: 'Disabled group'
33+
}));
34+
35+
expect(groups.length).toBe(1);
36+
});
37+
38+
it('should filter group label text by a pattern', async () => {
39+
const groups = await loader.getAllHarnesses(optionGroupHarness.with({labelText: /group/}));
40+
expect(groups.length).toBe(2);
41+
});
42+
43+
it('should get the group label text', async () => {
44+
const groups = await loader.getAllHarnesses(optionGroupHarness);
45+
const texts = await Promise.all(groups.map(group => group.getLabelText()));
46+
expect(texts).toEqual(['Plain group', 'Disabled group']);
47+
});
48+
49+
it('should get the group disabled state', async () => {
50+
const groups = await loader.getAllHarnesses(optionGroupHarness);
51+
const disabledStates = await Promise.all(groups.map(group => group.isDisabled()));
52+
expect(disabledStates).toEqual([false, true]);
53+
});
54+
55+
it('should get the options inside the groups', async () => {
56+
const group = await loader.getHarness(optionGroupHarness);
57+
const optionTexts = await group.getOptions().then(async options => {
58+
return await Promise.all(options.map(option => option.getText()));
59+
});
60+
61+
expect(optionTexts).toEqual(['Option 1', 'Option 2']);
62+
});
63+
}
64+
65+
66+
@Component({
67+
template: `
68+
<mat-optgroup label="Plain group">
69+
<mat-option>Option 1</mat-option>
70+
<mat-option>Option 2</mat-option>
71+
</mat-optgroup>
72+
73+
<mat-optgroup label="Disabled group" disabled>
74+
<mat-option>Disabled option 1</mat-option>
75+
</mat-optgroup>
76+
`
77+
})
78+
class OptgroupHarnessTest {
79+
}
80+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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 {BaseHarnessFilters} from '@angular/cdk/testing';
10+
11+
export interface OptionHarnessFilters extends BaseHarnessFilters {
12+
text?: string | RegExp;
13+
isSelected?: boolean;
14+
}

0 commit comments

Comments
 (0)