Skip to content

Commit ca464bd

Browse files
crisbetowagnermaciel
authored andcommitted
feat(input/testing): add test harnesses for native select and option
Adds test harnesses for native `select` and `option` elements that are placed inside a `mat-form-field`.
1 parent 6de8ca3 commit ca464bd

File tree

14 files changed

+477
-12
lines changed

14 files changed

+477
-12
lines changed

src/material-experimental/mdc-input/testing/BUILD.bazel

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,7 @@ filegroup(
2222

2323
ng_test_library(
2424
name = "unit_tests_lib",
25-
srcs = glob(
26-
["**/*.spec.ts"],
27-
exclude = ["shared.spec.ts"],
28-
),
25+
srcs = glob(["**/*.spec.ts"]),
2926
deps = [
3027
":testing",
3128
"//src/material-experimental/mdc-input",
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {MatInputModule} from '@angular/material-experimental/mdc-input';
2-
import {runHarnessTests} from '@angular/material/input/testing/shared.spec';
2+
import {runInputHarnessTests} from '@angular/material/input/testing/shared-input.spec';
33
import {MatInputHarness} from './index';
44

55
describe('MDC-based MatInputHarness', () => {
6-
runHarnessTests(MatInputModule, MatInputHarness);
6+
runInputHarnessTests(MatInputModule, MatInputHarness);
77
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import {MatInputModule} from '@angular/material-experimental/mdc-input';
2+
import {
3+
runNativeSelectHarnessTests
4+
} from '@angular/material/input/testing/shared-native-select.spec';
5+
import {MatNativeSelectHarness} from './index';
6+
7+
describe('MDC-based MatNativeSelectHarness', () => {
8+
runNativeSelectHarnessTests(MatInputModule, MatNativeSelectHarness);
9+
});

src/material-experimental/mdc-input/testing/public-api.ts

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

9-
export {InputHarnessFilters, MatInputHarness} from '@angular/material/input/testing';
9+
export {
10+
InputHarnessFilters,
11+
MatInputHarness,
12+
MatNativeSelectHarness,
13+
MatNativeOptionHarness,
14+
NativeOptionHarnessFilters,
15+
NativeSelectHarnessFilters,
16+
} from '@angular/material/input/testing';

src/material/input/testing/BUILD.bazel

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ filegroup(
2323

2424
ng_test_library(
2525
name = "harness_tests_lib",
26-
srcs = ["shared.spec.ts"],
26+
srcs = [
27+
"shared-input.spec.ts",
28+
"shared-native-select.spec.ts",
29+
],
2730
deps = [
2831
":testing",
2932
"//src/cdk/platform",
@@ -39,7 +42,10 @@ ng_test_library(
3942
name = "unit_tests_lib",
4043
srcs = glob(
4144
["**/*.spec.ts"],
42-
exclude = ["shared.spec.ts"],
45+
exclude = [
46+
"shared-input.spec.ts",
47+
"shared-native-select.spec.ts",
48+
],
4349
),
4450
deps = [
4551
":harness_tests_lib",
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {MatInputModule} from '@angular/material/input';
22
import {MatInputHarness} from './input-harness';
3-
import {runHarnessTests} from './shared.spec';
3+
import {runInputHarnessTests} from './shared-input.spec';
44

55
describe('Non-MDC-based MatInputHarness', () => {
6-
runHarnessTests(MatInputModule, MatInputHarness);
6+
runInputHarnessTests(MatInputModule, MatInputHarness);
77
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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 {NativeOptionHarnessFilters} from './native-select-harness-filters';
11+
12+
/** Harness for interacting with a native `option` in tests. */
13+
export class MatNativeOptionHarness extends ComponentHarness {
14+
/** Selector used to locate option instances. */
15+
static hostSelector = 'select[matNativeControl] option';
16+
17+
/**
18+
* Gets a `HarnessPredicate` that can be used to search for a `MatNativeOptionHarness` that meets
19+
* certain criteria.
20+
* @param options Options for filtering which option instances are considered a match.
21+
* @return a `HarnessPredicate` configured with the given options.
22+
*/
23+
static with(options: NativeOptionHarnessFilters = {}) {
24+
return new HarnessPredicate(MatNativeOptionHarness, options)
25+
.addOption('text', options.text,
26+
async (harness, title) =>
27+
HarnessPredicate.stringMatches(await harness.getText(), title))
28+
.addOption('index', options.index,
29+
async (harness, index) => await harness.getIndex() === index)
30+
.addOption('isSelected', options.isSelected,
31+
async (harness, isSelected) => await harness.isSelected() === isSelected);
32+
33+
}
34+
35+
/** Gets the option's label text. */
36+
async getText(): Promise<string> {
37+
return (await this.host()).getProperty('label');
38+
}
39+
40+
/** Index of the option within the native `select` element. */
41+
async getIndex(): Promise<number> {
42+
return (await this.host()).getProperty('index');
43+
}
44+
45+
/** Gets whether the option is disabled. */
46+
async isDisabled(): Promise<boolean> {
47+
return (await this.host()).getProperty('disabled');
48+
}
49+
50+
/** Gets whether the option is selected. */
51+
async isSelected(): Promise<boolean> {
52+
return (await this.host()).getProperty('selected');
53+
}
54+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
/** A set of criteria that can be used to filter a list of `MatNativeSelectHarness` instances. */
12+
export interface NativeSelectHarnessFilters extends BaseHarnessFilters {}
13+
14+
/** A set of criteria that can be used to filter a list of `MatNativeOptionHarness` instances. */
15+
export interface NativeOptionHarnessFilters extends BaseHarnessFilters {
16+
text?: string | RegExp;
17+
index?: number;
18+
isSelected?: boolean;
19+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {MatInputModule} from '@angular/material/input';
2+
import {MatNativeSelectHarness} from './native-select-harness';
3+
import {runNativeSelectHarnessTests} from './shared-native-select.spec';
4+
5+
describe('Non-MDC-based MatNativeSelectHarness', () => {
6+
runNativeSelectHarnessTests(MatInputModule, MatNativeSelectHarness);
7+
});
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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 {HarnessPredicate} from '@angular/cdk/testing';
10+
import {MatFormFieldControlHarness} from '@angular/material/form-field/testing/control';
11+
import {MatNativeOptionHarness} from './native-option-harness';
12+
import {
13+
NativeOptionHarnessFilters,
14+
NativeSelectHarnessFilters,
15+
} from './native-select-harness-filters';
16+
17+
18+
/** Harness for interacting with a native `select` in tests. */
19+
export class MatNativeSelectHarness extends MatFormFieldControlHarness {
20+
static hostSelector = 'select[matNativeControl]';
21+
22+
/**
23+
* Gets a `HarnessPredicate` that can be used to search for a `MatNativeSelectHarness` that meets
24+
* certain criteria.
25+
* @param options Options for filtering which select instances are considered a match.
26+
* @return a `HarnessPredicate` configured with the given options.
27+
*/
28+
static with(options: NativeSelectHarnessFilters = {}): HarnessPredicate<MatNativeSelectHarness> {
29+
return new HarnessPredicate(MatNativeSelectHarness, options);
30+
}
31+
32+
/** Gets a boolean promise indicating if the select is disabled. */
33+
async isDisabled(): Promise<boolean> {
34+
return (await this.host()).getProperty('disabled');
35+
}
36+
37+
/** Gets a boolean promise indicating if the select is required. */
38+
async isRequired(): Promise<boolean> {
39+
return (await this.host()).getProperty('required');
40+
}
41+
42+
/** Gets a boolean promise indicating if the select is in multi-selection mode. */
43+
async isMultiple(): Promise<boolean> {
44+
return (await this.host()).getProperty('multiple');
45+
}
46+
47+
/** Gets the name of the select. */
48+
async getName(): Promise<string> {
49+
// The "name" property of the native select is never undefined.
50+
return (await (await this.host()).getProperty('name'))!;
51+
}
52+
53+
/** Gets the id of the select. */
54+
async getId(): Promise<string> {
55+
// We're guaranteed to have an id, because the `matNativeControl` always assigns one.
56+
return (await (await this.host()).getProperty('id'))!;
57+
}
58+
59+
/** Focuses the select and returns a void promise that indicates when the action is complete. */
60+
async focus(): Promise<void> {
61+
return (await this.host()).focus();
62+
}
63+
64+
/** Blurs the select and returns a void promise that indicates when the action is complete. */
65+
async blur(): Promise<void> {
66+
return (await this.host()).blur();
67+
}
68+
69+
/** Whether the select is focused. */
70+
async isFocused(): Promise<boolean> {
71+
return (await this.host()).isFocused();
72+
}
73+
74+
/** Gets the options inside the select panel. */
75+
async getOptions(filter: NativeOptionHarnessFilters = {}):
76+
Promise<MatNativeOptionHarness[]> {
77+
return this.locatorForAll(MatNativeOptionHarness.with(filter))();
78+
}
79+
80+
/**
81+
* Selects the options that match the passed-in filter. If the select is in multi-selection
82+
* mode all options will be clicked, otherwise the harness will pick the first matching option.
83+
*/
84+
async selectOptions(filter: NativeOptionHarnessFilters = {}): Promise<void> {
85+
const [isMultiple, options] = await Promise.all([this.isMultiple(), this.getOptions(filter)]);
86+
87+
if (options.length === 0) {
88+
throw Error('Select does not have options matching the specified filter');
89+
}
90+
91+
const [host, optionIndexes] = await Promise.all([
92+
this.host(),
93+
Promise.all(options.slice(0, isMultiple ? undefined : 1).map(option => option.getIndex()))
94+
]);
95+
96+
// @breaking-change 12.0.0 Error can be removed once `selectOptions` is a required method.
97+
if (!host.selectOptions) {
98+
throw Error('TestElement implementation does not support the selectOptions ' +
99+
'method which is required for this function.');
100+
}
101+
102+
await host.selectOptions(...optionIndexes);
103+
}
104+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@
88

99
export * from './input-harness';
1010
export * from './input-harness-filters';
11+
export * from './native-select-harness';
12+
export * from './native-select-harness-filters';
13+
export * from './native-option-harness';

src/material/input/testing/shared.spec.ts renamed to src/material/input/testing/shared-input.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations';
99
import {MatInputHarness} from './input-harness';
1010

1111
/** Shared tests to run on both the original and MDC-based input's. */
12-
export function runHarnessTests(
12+
export function runInputHarnessTests(
1313
inputModule: typeof MatInputModule, inputHarness: typeof MatInputHarness) {
1414
let fixture: ComponentFixture<InputHarnessTest>;
1515
let loader: HarnessLoader;

0 commit comments

Comments
 (0)