Skip to content

Commit 14694c8

Browse files
authored
fix(multiple): enable easy extension of harnesses (#24878)
Add the capability to extend a harness without having to duplicate the `with` method used to load it. This was necessary before because the returned predicate was of type the parent harness which resultet in loading a harness instance of the parent type instead of the derived class unless the method is overridden.
1 parent 9e1d77e commit 14694c8

File tree

40 files changed

+438
-257
lines changed

40 files changed

+438
-257
lines changed

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

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

9-
import {HarnessPredicate} from '@angular/cdk/testing';
9+
import {ComponentHarnessConstructor, HarnessPredicate} from '@angular/cdk/testing';
1010
import {
1111
MatOptgroupHarness,
1212
MatOptionHarness,
@@ -33,16 +33,17 @@ export class MatAutocompleteHarness extends _MatAutocompleteHarnessBase<
3333
static hostSelector = '.mat-mdc-autocomplete-trigger';
3434

3535
/**
36-
* Gets a `HarnessPredicate` that can be used to search for a `MatAutocompleteHarness` that meets
37-
* certain criteria.
36+
* Gets a `HarnessPredicate` that can be used to search for an autocomplete with specific
37+
* attributes.
3838
* @param options Options for filtering which autocomplete instances are considered a match.
3939
* @return a `HarnessPredicate` configured with the given options.
4040
*/
41-
static with(options: AutocompleteHarnessFilters = {}): HarnessPredicate<MatAutocompleteHarness> {
42-
return new HarnessPredicate(MatAutocompleteHarness, options).addOption(
43-
'value',
44-
options.value,
45-
(harness, value) => HarnessPredicate.stringMatches(harness.getValue(), value),
41+
static with<T extends MatAutocompleteHarness>(
42+
this: ComponentHarnessConstructor<T>,
43+
options: AutocompleteHarnessFilters = {},
44+
): HarnessPredicate<T> {
45+
return new HarnessPredicate(this, options).addOption('value', options.value, (harness, value) =>
46+
HarnessPredicate.stringMatches(harness.getValue(), value),
4647
);
4748
}
4849
}

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

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

9-
import {ContentContainerComponentHarness, HarnessPredicate} from '@angular/cdk/testing';
9+
import {
10+
ComponentHarnessConstructor,
11+
ContentContainerComponentHarness,
12+
HarnessPredicate,
13+
} from '@angular/cdk/testing';
1014
import {coerceBooleanProperty} from '@angular/cdk/coercion';
1115
import {ButtonHarnessFilters} from '@angular/material/button/testing';
1216

@@ -23,11 +27,12 @@ export class MatButtonHarness extends ContentContainerComponentHarness {
2327
* - `text` finds a button with specific text content.
2428
* @return a `HarnessPredicate` configured with the given options.
2529
*/
26-
static with(options: ButtonHarnessFilters = {}): HarnessPredicate<MatButtonHarness> {
27-
return new HarnessPredicate(MatButtonHarness, options).addOption(
28-
'text',
29-
options.text,
30-
(harness, text) => HarnessPredicate.stringMatches(harness.getText(), text),
30+
static with<T extends MatButtonHarness>(
31+
this: ComponentHarnessConstructor<T>,
32+
options: ButtonHarnessFilters = {},
33+
): HarnessPredicate<T> {
34+
return new HarnessPredicate(this, options).addOption('text', options.text, (harness, text) =>
35+
HarnessPredicate.stringMatches(harness.getText(), text),
3136
);
3237
}
3338

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

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

9-
import {HarnessPredicate, ContentContainerComponentHarness} from '@angular/cdk/testing';
9+
import {
10+
ComponentHarnessConstructor,
11+
ContentContainerComponentHarness,
12+
HarnessPredicate,
13+
} from '@angular/cdk/testing';
1014
import {CardHarnessFilters} from './card-harness-filters';
1115

1216
/** Selectors for different sections of the mat-card that can container user content. */
@@ -23,13 +27,15 @@ export class MatCardHarness extends ContentContainerComponentHarness<MatCardSect
2327
static hostSelector = '.mat-mdc-card';
2428

2529
/**
26-
* Gets a `HarnessPredicate` that can be used to search for a `MatCardHarness` that meets
27-
* certain criteria.
30+
* Gets a `HarnessPredicate` that can be used to search for a card with specific attributes.
2831
* @param options Options for filtering which card instances are considered a match.
2932
* @return a `HarnessPredicate` configured with the given options.
3033
*/
31-
static with(options: CardHarnessFilters = {}): HarnessPredicate<MatCardHarness> {
32-
return new HarnessPredicate(MatCardHarness, options)
34+
static with<T extends MatCardHarness>(
35+
this: ComponentHarnessConstructor<T>,
36+
options: CardHarnessFilters = {},
37+
): HarnessPredicate<T> {
38+
return new HarnessPredicate(this, options)
3339
.addOption('text', options.text, (harness, text) =>
3440
HarnessPredicate.stringMatches(harness.getText(), text),
3541
)

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

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

9-
import {HarnessPredicate} from '@angular/cdk/testing';
9+
import {ComponentHarnessConstructor, HarnessPredicate} from '@angular/cdk/testing';
1010
import {CheckboxHarnessFilters, _MatCheckboxHarnessBase} from '@angular/material/checkbox/testing';
1111

1212
/** Harness for interacting with a MDC-based mat-checkbox in tests. */
@@ -21,9 +21,12 @@ export class MatCheckboxHarness extends _MatCheckboxHarnessBase {
2121
* - `name` finds a checkbox with specific name.
2222
* @return a `HarnessPredicate` configured with the given options.
2323
*/
24-
static with(options: CheckboxHarnessFilters = {}): HarnessPredicate<MatCheckboxHarness> {
24+
static with<T extends MatCheckboxHarness>(
25+
this: ComponentHarnessConstructor<T>,
26+
options: CheckboxHarnessFilters = {},
27+
): HarnessPredicate<T> {
2528
return (
26-
new HarnessPredicate(MatCheckboxHarness, options)
29+
new HarnessPredicate(this, options)
2730
.addOption('label', options.label, (harness, label) =>
2831
HarnessPredicate.stringMatches(harness.getLabelText(), label),
2932
)

src/material-experimental/mdc-chips/testing/chip-avatar-harness.ts

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

9-
import {HarnessPredicate, ComponentHarness} from '@angular/cdk/testing';
9+
import {
10+
ComponentHarness,
11+
ComponentHarnessConstructor,
12+
HarnessPredicate,
13+
} from '@angular/cdk/testing';
1014
import {ChipAvatarHarnessFilters} from './chip-harness-filters';
1115

1216
/** Harness for interacting with a standard Material chip avatar in tests. */
1317
export class MatChipAvatarHarness extends ComponentHarness {
1418
static hostSelector = '.mat-mdc-chip-avatar';
1519

1620
/**
17-
* Gets a `HarnessPredicate` that can be used to search for a `MatChipAvatarHarness` that meets
18-
* certain criteria.
21+
* Gets a `HarnessPredicate` that can be used to search for a chip avatar with specific
22+
* attributes.
1923
* @param options Options for filtering which input instances are considered a match.
2024
* @return a `HarnessPredicate` configured with the given options.
2125
*/
22-
static with(options: ChipAvatarHarnessFilters = {}): HarnessPredicate<MatChipAvatarHarness> {
23-
return new HarnessPredicate(MatChipAvatarHarness, options);
26+
static with<T extends MatChipAvatarHarness>(
27+
this: ComponentHarnessConstructor<T>,
28+
options: ChipAvatarHarnessFilters = {},
29+
): HarnessPredicate<T> {
30+
return new HarnessPredicate(this, options);
2431
}
2532
}

src/material-experimental/mdc-chips/testing/chip-grid-harness.ts

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

9-
import {ComponentHarness, HarnessPredicate} from '@angular/cdk/testing';
9+
import {
10+
ComponentHarness,
11+
ComponentHarnessConstructor,
12+
HarnessPredicate,
13+
} from '@angular/cdk/testing';
1014
import {
1115
ChipGridHarnessFilters,
1216
ChipInputHarnessFilters,
@@ -21,9 +25,14 @@ export class MatChipGridHarness extends ComponentHarness {
2125

2226
/**
2327
* Gets a `HarnessPredicate` that can be used to search for a chip grid with specific attributes.
28+
* @param options Options for filtering which chip grid instances are considered a match.
29+
* @return a `HarnessPredicate` configured with the given options.
2430
*/
25-
static with(options: ChipGridHarnessFilters = {}): HarnessPredicate<MatChipGridHarness> {
26-
return new HarnessPredicate(MatChipGridHarness, options);
31+
static with<T extends MatChipGridHarness>(
32+
this: ComponentHarnessConstructor<T>,
33+
options: ChipGridHarnessFilters = {},
34+
): HarnessPredicate<T> {
35+
return new HarnessPredicate(this, options);
2736
}
2837

2938
/** Gets whether the chip grid is disabled. */

src/material-experimental/mdc-chips/testing/chip-harness.ts

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

9-
import {ContentContainerComponentHarness, HarnessPredicate, TestKey} from '@angular/cdk/testing';
9+
import {
10+
ComponentHarnessConstructor,
11+
ContentContainerComponentHarness,
12+
HarnessPredicate,
13+
TestKey,
14+
} from '@angular/cdk/testing';
1015
import {MatChipAvatarHarness} from './chip-avatar-harness';
1116
import {
1217
ChipAvatarHarnessFilters,
@@ -23,20 +28,16 @@ export class MatChipHarness extends ContentContainerComponentHarness {
2328

2429
/**
2530
* Gets a `HarnessPredicate` that can be used to search for a chip with specific attributes.
31+
* @param options Options for narrowing the search.
32+
* @return a `HarnessPredicate` configured with the given options.
2633
*/
27-
// Note(mmalerba): generics are used as a workaround for lack of polymorphic `this` in static
28-
// methods. See https://github.com/microsoft/TypeScript/issues/5863
29-
static with<T extends typeof MatChipHarness>(
30-
this: T,
34+
static with<T extends MatChipHarness>(
35+
this: ComponentHarnessConstructor<T>,
3136
options: ChipHarnessFilters = {},
32-
): HarnessPredicate<InstanceType<T>> {
33-
return new HarnessPredicate(MatChipHarness, options).addOption(
34-
'text',
35-
options.text,
36-
(harness, label) => {
37-
return HarnessPredicate.stringMatches(harness.getText(), label);
38-
},
39-
) as unknown as HarnessPredicate<InstanceType<T>>;
37+
): HarnessPredicate<T> {
38+
return new HarnessPredicate(this, options).addOption('text', options.text, (harness, label) => {
39+
return HarnessPredicate.stringMatches(harness.getText(), label);
40+
});
4041
}
4142

4243
/** Gets a promise for the text content the option. */

src/material-experimental/mdc-chips/testing/chip-input-harness.ts

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

9-
import {ComponentHarness, HarnessPredicate, TestKey} from '@angular/cdk/testing';
9+
import {
10+
ComponentHarness,
11+
ComponentHarnessConstructor,
12+
HarnessPredicate,
13+
TestKey,
14+
} from '@angular/cdk/testing';
1015
import {ChipInputHarnessFilters} from './chip-harness-filters';
1116

1217
/** Harness for interacting with a grid's chip input in tests. */
1318
export class MatChipInputHarness extends ComponentHarness {
1419
static hostSelector = '.mat-mdc-chip-input';
1520

1621
/**
17-
* Gets a `HarnessPredicate` that can be used to search for a `MatChipInputHarness` that meets
18-
* certain criteria.
22+
* Gets a `HarnessPredicate` that can be used to search for a chip input with specific
23+
* attributes.
1924
* @param options Options for filtering which input instances are considered a match.
2025
* @return a `HarnessPredicate` configured with the given options.
2126
*/
22-
static with(options: ChipInputHarnessFilters = {}): HarnessPredicate<MatChipInputHarness> {
23-
return new HarnessPredicate(MatChipInputHarness, options)
27+
static with<T extends MatChipInputHarness>(
28+
this: ComponentHarnessConstructor<T>,
29+
options: ChipInputHarnessFilters = {},
30+
): HarnessPredicate<T> {
31+
return new HarnessPredicate(this, options)
2432
.addOption('value', options.value, async (harness, value) => {
2533
return (await harness.getValue()) === value;
2634
})

src/material-experimental/mdc-chips/testing/chip-listbox-harness.ts

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

9-
import {ComponentHarness, HarnessPredicate, parallel} from '@angular/cdk/testing';
9+
import {
10+
ComponentHarness,
11+
ComponentHarnessConstructor,
12+
HarnessPredicate,
13+
parallel,
14+
} from '@angular/cdk/testing';
1015
import {ChipListboxHarnessFilters, ChipOptionHarnessFilters} from './chip-harness-filters';
1116
import {MatChipOptionHarness} from './chip-option-harness';
1217

@@ -17,9 +22,14 @@ export class MatChipListboxHarness extends ComponentHarness {
1722
/**
1823
* Gets a `HarnessPredicate` that can be used to search for a chip listbox with specific
1924
* attributes.
25+
* @param options Options for narrowing the search.
26+
* @return a `HarnessPredicate` configured with the given options.
2027
*/
21-
static with(options: ChipListboxHarnessFilters = {}): HarnessPredicate<MatChipListboxHarness> {
22-
return new HarnessPredicate(MatChipListboxHarness, options);
28+
static with<T extends MatChipListboxHarness>(
29+
this: ComponentHarnessConstructor<T>,
30+
options: ChipListboxHarnessFilters = {},
31+
): HarnessPredicate<T> {
32+
return new HarnessPredicate(this, options);
2333
}
2434

2535
/** Gets whether the chip listbox is disabled. */

src/material-experimental/mdc-chips/testing/chip-option-harness.ts

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

9-
import {HarnessPredicate} from '@angular/cdk/testing';
9+
import {ComponentHarnessConstructor, HarnessPredicate} from '@angular/cdk/testing';
1010
import {MatChipHarness} from './chip-harness';
1111
import {ChipOptionHarnessFilters} from './chip-harness-filters';
1212

@@ -17,13 +17,13 @@ export class MatChipOptionHarness extends MatChipHarness {
1717
/**
1818
* Gets a `HarnessPredicate` that can be used to search for a chip option with specific
1919
* attributes.
20+
* @param options Options for narrowing the search.
21+
* @return a `HarnessPredicate` configured with the given options.
2022
*/
21-
// Note(mmalerba): generics are used as a workaround for lack of polymorphic `this` in static
22-
// methods. See https://github.com/microsoft/TypeScript/issues/5863
23-
static override with<T extends typeof MatChipHarness>(
24-
this: T,
23+
static override with<T extends MatChipHarness>(
24+
this: ComponentHarnessConstructor<T>,
2525
options: ChipOptionHarnessFilters = {},
26-
): HarnessPredicate<InstanceType<T>> {
26+
): HarnessPredicate<T> {
2727
return new HarnessPredicate(MatChipOptionHarness, options)
2828
.addOption('text', options.text, (harness, label) =>
2929
HarnessPredicate.stringMatches(harness.getText(), label),
@@ -32,7 +32,7 @@ export class MatChipOptionHarness extends MatChipHarness {
3232
'selected',
3333
options.selected,
3434
async (harness, selected) => (await harness.isSelected()) === selected,
35-
) as unknown as HarnessPredicate<InstanceType<T>>;
35+
) as unknown as HarnessPredicate<T>;
3636
}
3737

3838
/** Whether the chip is selected. */

src/material-experimental/mdc-chips/testing/chip-remove-harness.ts

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

9-
import {HarnessPredicate, ComponentHarness} from '@angular/cdk/testing';
9+
import {
10+
ComponentHarness,
11+
ComponentHarnessConstructor,
12+
HarnessPredicate,
13+
} from '@angular/cdk/testing';
1014
import {ChipRemoveHarnessFilters} from './chip-harness-filters';
1115

1216
/** Harness for interacting with a standard Material chip remove button in tests. */
1317
export class MatChipRemoveHarness extends ComponentHarness {
1418
static hostSelector = '.mat-mdc-chip-remove';
1519

1620
/**
17-
* Gets a `HarnessPredicate` that can be used to search for a `MatChipRemoveHarness` that meets
18-
* certain criteria.
21+
* Gets a `HarnessPredicate` that can be used to search for a chip remove with specific
22+
* attributes.
1923
* @param options Options for filtering which input instances are considered a match.
2024
* @return a `HarnessPredicate` configured with the given options.
2125
*/
22-
static with(options: ChipRemoveHarnessFilters = {}): HarnessPredicate<MatChipRemoveHarness> {
23-
return new HarnessPredicate(MatChipRemoveHarness, options);
26+
static with<T extends MatChipRemoveHarness>(
27+
this: ComponentHarnessConstructor<T>,
28+
options: ChipRemoveHarnessFilters = {},
29+
): HarnessPredicate<T> {
30+
return new HarnessPredicate(this, options);
2431
}
2532

2633
/** Clicks the remove button. */

src/material-experimental/mdc-chips/testing/chip-row-harness.ts

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

9-
import {HarnessPredicate} from '@angular/cdk/testing';
10-
import {ChipRowHarnessFilters} from './chip-harness-filters';
119
import {MatChipHarness} from './chip-harness';
1210

1311
// TODO(crisbeto): add harness for the chip edit input inside the row.
@@ -16,20 +14,6 @@ import {MatChipHarness} from './chip-harness';
1614
export class MatChipRowHarness extends MatChipHarness {
1715
static override hostSelector = '.mat-mdc-chip-row';
1816

19-
/**
20-
* Gets a `HarnessPredicate` that can be used to search for a chip row with specific attributes.
21-
*/
22-
// Note(mmalerba): generics are used as a workaround for lack of polymorphic `this` in static
23-
// methods. See https://github.com/microsoft/TypeScript/issues/5863
24-
static override with<T extends typeof MatChipHarness>(
25-
this: T,
26-
options: ChipRowHarnessFilters = {},
27-
): HarnessPredicate<InstanceType<T>> {
28-
return new HarnessPredicate(MatChipRowHarness, options) as unknown as HarnessPredicate<
29-
InstanceType<T>
30-
>;
31-
}
32-
3317
/** Whether the chip is editable. */
3418
async isEditable(): Promise<boolean> {
3519
return (await this.host()).hasClass('mat-mdc-chip-editable');

0 commit comments

Comments
 (0)