Skip to content

Commit 632fd46

Browse files
authored
refactor(material-experimental/mdc-checkbox): de-duplicate test harness logic (#21506)
Refactors the MDC checkbox harness so that it extends the non-MDC one directly, because most of the logic is identical.
1 parent 9d92927 commit 632fd46

File tree

5 files changed

+65
-151
lines changed

5 files changed

+65
-151
lines changed

scripts/check-mdc-exports-config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ export const config = {
7171
// Private symbols that are only exported for MDC.
7272
'_MatTableDataSource',
7373
'_MAT_TEXT_COLUMN_TEMPLATE'
74+
],
75+
'mdc-checkbox/testing': [
76+
// Private symbols that are only exported for MDC.
77+
'_MatCheckboxHarnessBase'
7478
]
7579
}
7680
};

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ ts_library(
1010
),
1111
module_name = "@angular/material-experimental/mdc-checkbox/testing",
1212
deps = [
13-
"//src/cdk/coercion",
1413
"//src/cdk/testing",
1514
"//src/material/checkbox/testing",
1615
],

src/material-experimental/mdc-checkbox/testing/checkbox-harness.ts

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

9-
import {ComponentHarness, HarnessPredicate} from '@angular/cdk/testing';
10-
import {coerceBooleanProperty} from '@angular/cdk/coercion';
11-
import {CheckboxHarnessFilters} from '@angular/material/checkbox/testing';
9+
import {HarnessPredicate} from '@angular/cdk/testing';
10+
import {CheckboxHarnessFilters, _MatCheckboxHarnessBase} from '@angular/material/checkbox/testing';
1211

1312
/** Harness for interacting with a MDC-based mat-checkbox in tests. */
14-
export class MatCheckboxHarness extends ComponentHarness {
13+
export class MatCheckboxHarness extends _MatCheckboxHarnessBase {
1514
static hostSelector = '.mat-mdc-checkbox';
1615

1716
/**
@@ -33,120 +32,12 @@ export class MatCheckboxHarness extends ComponentHarness {
3332
.addOption('name', options.name, async (harness, name) => await harness.getName() === name);
3433
}
3534

36-
private _label = this.locatorFor('label');
37-
private _input = this.locatorFor('input');
35+
protected _input = this.locatorFor('input');
36+
protected _label = this.locatorFor('label');
3837
private _inputContainer = this.locatorFor('.mdc-checkbox');
3938

40-
/** Gets a boolean promise indicating if the checkbox is checked. */
41-
async isChecked(): Promise<boolean> {
42-
const checked = (await this._input()).getProperty('checked');
43-
return coerceBooleanProperty(await checked);
44-
}
45-
46-
/** Gets a boolean promise indicating if the checkbox is in an indeterminate state. */
47-
async isIndeterminate(): Promise<boolean> {
48-
const indeterminate = (await this._input()).getProperty('indeterminate');
49-
return coerceBooleanProperty(await indeterminate);
50-
}
51-
52-
/** Gets a boolean promise indicating if the checkbox is disabled. */
53-
async isDisabled(): Promise<boolean> {
54-
const disabled = (await this._input()).getAttribute('disabled');
55-
return coerceBooleanProperty(await disabled);
56-
}
57-
58-
/** Gets a boolean promise indicating if the checkbox is required. */
59-
async isRequired(): Promise<boolean> {
60-
const required = (await this._input()).getAttribute('required');
61-
return coerceBooleanProperty(await required);
62-
}
63-
64-
/** Gets a boolean promise indicating if the checkbox is valid. */
65-
async isValid(): Promise<boolean> {
66-
const invalid = (await this.host()).hasClass('ng-invalid');
67-
return !(await invalid);
68-
}
69-
70-
/** Gets a promise for the checkbox's name. */
71-
async getName(): Promise<string|null> {
72-
return (await this._input()).getAttribute('name');
73-
}
74-
75-
/** Gets a promise for the checkbox's value. */
76-
async getValue(): Promise<string|null> {
77-
return (await this._input()).getProperty('value');
78-
}
79-
80-
/** Gets a promise for the checkbox's aria-label. */
81-
async getAriaLabel(): Promise<string|null> {
82-
return (await this._input()).getAttribute('aria-label');
83-
}
84-
85-
/** Gets a promise for the checkbox's aria-labelledby. */
86-
async getAriaLabelledby(): Promise<string|null> {
87-
return (await this._input()).getAttribute('aria-labelledby');
88-
}
89-
90-
/** Gets a promise for the checkbox's label text. */
91-
async getLabelText(): Promise<string> {
92-
return (await this._label()).text();
93-
}
94-
95-
/** Focuses the checkbox and returns a void promise that indicates when the action is complete. */
96-
async focus(): Promise<void> {
97-
return (await this._input()).focus();
98-
}
99-
100-
/** Blurs the checkbox and returns a void promise that indicates when the action is complete. */
101-
async blur(): Promise<void> {
102-
return (await this._input()).blur();
103-
}
104-
105-
/** Whether the checkbox is focused. */
106-
async isFocused(): Promise<boolean> {
107-
return (await this._input()).isFocused();
108-
}
109-
110-
/**
111-
* Toggle the checked state of the checkbox and returns a void promise that indicates when the
112-
* action is complete.
113-
*
114-
* Note: This attempts to toggle the checkbox as a user would, by clicking it. Therefore if you
115-
* are using `MAT_CHECKBOX_DEFAULT_OPTIONS` to change the behavior on click, calling this method
116-
* might not have the expected result.
117-
*/
11839
async toggle(): Promise<void> {
11940
const elToClick = await this.isDisabled() ? this._inputContainer() : this._input();
12041
return (await elToClick).click();
12142
}
122-
123-
/**
124-
* Puts the checkbox in a checked state by toggling it if it is currently unchecked, or doing
125-
* nothing if it is already checked. Returns a void promise that indicates when the action is
126-
* complete.
127-
*
128-
* Note: This attempts to check the checkbox as a user would, by clicking it. Therefore if you
129-
* are using `MAT_CHECKBOX_DEFAULT_OPTIONS` to change the behavior on click, calling this method
130-
* might not have the expected result.
131-
*/
132-
async check(): Promise<void> {
133-
if (!(await this.isChecked())) {
134-
await this.toggle();
135-
}
136-
}
137-
138-
/**
139-
* Puts the checkbox in an unchecked state by toggling it if it is currently checked, or doing
140-
* nothing if it is already unchecked. Returns a void promise that indicates when the action is
141-
* complete.
142-
*
143-
* Note: This attempts to uncheck the checkbox as a user would, by clicking it. Therefore if you
144-
* are using `MAT_CHECKBOX_DEFAULT_OPTIONS` to change the behavior on click, calling this method
145-
* might not have the expected result.
146-
*/
147-
async uncheck(): Promise<void> {
148-
if (await this.isChecked()) {
149-
await this.toggle();
150-
}
151-
}
15243
}

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

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,17 @@
77
*/
88

99
import {coerceBooleanProperty} from '@angular/cdk/coercion';
10-
import {ComponentHarness, HarnessPredicate} from '@angular/cdk/testing';
10+
import {
11+
AsyncFactoryFn,
12+
ComponentHarness,
13+
HarnessPredicate,
14+
TestElement,
15+
} from '@angular/cdk/testing';
1116
import {CheckboxHarnessFilters} from './checkbox-harness-filters';
1217

13-
/** Harness for interacting with a standard mat-checkbox in tests. */
14-
export class MatCheckboxHarness extends ComponentHarness {
15-
/** The selector for the host element of a `MatCheckbox` instance. */
16-
static hostSelector = '.mat-checkbox';
17-
18-
/**
19-
* Gets a `HarnessPredicate` that can be used to search for a `MatCheckboxHarness` that meets
20-
* certain criteria.
21-
* @param options Options for filtering which checkbox instances are considered a match.
22-
* @return a `HarnessPredicate` configured with the given options.
23-
*/
24-
static with(options: CheckboxHarnessFilters = {}): HarnessPredicate<MatCheckboxHarness> {
25-
return new HarnessPredicate(MatCheckboxHarness, options)
26-
.addOption(
27-
'label', options.label,
28-
(harness, label) => HarnessPredicate.stringMatches(harness.getLabelText(), label))
29-
// We want to provide a filter option for "name" because the name of the checkbox is
30-
// only set on the underlying input. This means that it's not possible for developers
31-
// to retrieve the harness of a specific checkbox with name through a CSS selector.
32-
.addOption('name', options.name, async (harness, name) => await harness.getName() === name);
33-
}
34-
35-
private _label = this.locatorFor('.mat-checkbox-label');
36-
private _input = this.locatorFor('input');
37-
private _inputContainer = this.locatorFor('.mat-checkbox-inner-container');
18+
export abstract class _MatCheckboxHarnessBase extends ComponentHarness {
19+
protected abstract _input: AsyncFactoryFn<TestElement>;
20+
protected abstract _label: AsyncFactoryFn<TestElement>;
3821

3922
/** Whether the checkbox is checked. */
4023
async isChecked(): Promise<boolean> {
@@ -113,9 +96,7 @@ export class MatCheckboxHarness extends ComponentHarness {
11396
* are using `MAT_CHECKBOX_DEFAULT_OPTIONS` to change the behavior on click, calling this method
11497
* might not have the expected result.
11598
*/
116-
async toggle(): Promise<void> {
117-
return (await this._inputContainer()).click();
118-
}
99+
abstract toggle(): Promise<void>;
119100

120101
/**
121102
* Puts the checkbox in a checked state by toggling it if it is currently unchecked, or doing
@@ -145,3 +126,34 @@ export class MatCheckboxHarness extends ComponentHarness {
145126
}
146127
}
147128
}
129+
130+
/** Harness for interacting with a standard mat-checkbox in tests. */
131+
export class MatCheckboxHarness extends _MatCheckboxHarnessBase {
132+
/** The selector for the host element of a `MatCheckbox` instance. */
133+
static hostSelector = '.mat-checkbox';
134+
135+
/**
136+
* Gets a `HarnessPredicate` that can be used to search for a `MatCheckboxHarness` that meets
137+
* certain criteria.
138+
* @param options Options for filtering which checkbox instances are considered a match.
139+
* @return a `HarnessPredicate` configured with the given options.
140+
*/
141+
static with(options: CheckboxHarnessFilters = {}): HarnessPredicate<MatCheckboxHarness> {
142+
return new HarnessPredicate(MatCheckboxHarness, options)
143+
.addOption(
144+
'label', options.label,
145+
(harness, label) => HarnessPredicate.stringMatches(harness.getLabelText(), label))
146+
// We want to provide a filter option for "name" because the name of the checkbox is
147+
// only set on the underlying input. This means that it's not possible for developers
148+
// to retrieve the harness of a specific checkbox with name through a CSS selector.
149+
.addOption('name', options.name, async (harness, name) => await harness.getName() === name);
150+
}
151+
152+
protected _input = this.locatorFor('input');
153+
protected _label = this.locatorFor('.mat-checkbox-label');
154+
private _inputContainer = this.locatorFor('.mat-checkbox-inner-container');
155+
156+
async toggle(): Promise<void> {
157+
return (await this._inputContainer()).click();
158+
}
159+
}
Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
export interface CheckboxHarnessFilters extends BaseHarnessFilters {
2-
label?: string | RegExp;
3-
name?: string;
4-
}
5-
6-
export declare class MatCheckboxHarness extends ComponentHarness {
1+
export declare abstract class _MatCheckboxHarnessBase extends ComponentHarness {
2+
protected abstract _input: AsyncFactoryFn<TestElement>;
3+
protected abstract _label: AsyncFactoryFn<TestElement>;
74
blur(): Promise<void>;
85
check(): Promise<void>;
96
focus(): Promise<void>;
@@ -18,8 +15,19 @@ export declare class MatCheckboxHarness extends ComponentHarness {
1815
isIndeterminate(): Promise<boolean>;
1916
isRequired(): Promise<boolean>;
2017
isValid(): Promise<boolean>;
21-
toggle(): Promise<void>;
18+
abstract toggle(): Promise<void>;
2219
uncheck(): Promise<void>;
20+
}
21+
22+
export interface CheckboxHarnessFilters extends BaseHarnessFilters {
23+
label?: string | RegExp;
24+
name?: string;
25+
}
26+
27+
export declare class MatCheckboxHarness extends _MatCheckboxHarnessBase {
28+
protected _input: AsyncFactoryFn<TestElement>;
29+
protected _label: AsyncFactoryFn<TestElement>;
30+
toggle(): Promise<void>;
2331
static hostSelector: string;
2432
static with(options?: CheckboxHarnessFilters): HarnessPredicate<MatCheckboxHarness>;
2533
}

0 commit comments

Comments
 (0)