7
7
*/
8
8
9
9
import {
10
+ BaseHarnessFilters ,
11
+ ComponentHarness ,
12
+ ComponentHarnessConstructor ,
10
13
ContentContainerComponentHarness ,
11
14
HarnessLoader ,
12
15
HarnessPredicate ,
@@ -16,27 +19,19 @@ import {
16
19
import { coerceBooleanProperty } from '@angular/cdk/coercion' ;
17
20
import { MenuHarnessFilters , MenuItemHarnessFilters } from './menu-harness-filters' ;
18
21
19
- /** Harness for interacting with a standard mat-menu in tests. */
20
- export class MatMenuHarness extends ContentContainerComponentHarness < string > {
21
- /** The selector for the host element of a `MatMenu` instance. */
22
- static hostSelector = '.mat-menu-trigger' ;
23
-
22
+ export abstract class _MatMenuHarnessBase <
23
+ ItemType extends ( ComponentHarnessConstructor < Item > & {
24
+ with : ( options ?: ItemFilters ) => HarnessPredicate < Item > } ) ,
25
+ Item extends ComponentHarness & {
26
+ click ( ) : Promise < void > ,
27
+ getSubmenu ( ) : Promise < _MatMenuHarnessBase < ItemType , Item , ItemFilters > | null > } ,
28
+ ItemFilters extends BaseHarnessFilters
29
+ > extends ContentContainerComponentHarness < string > {
24
30
private _documentRootLocator = this . documentRootLocatorFactory ( ) ;
31
+ protected abstract _itemClass : ItemType ;
25
32
26
33
// TODO: potentially extend MatButtonHarness
27
34
28
- /**
29
- * Gets a `HarnessPredicate` that can be used to search for a `MatMenuHarness` that meets certain
30
- * criteria.
31
- * @param options Options for filtering which menu instances are considered a match.
32
- * @return a `HarnessPredicate` configured with the given options.
33
- */
34
- static with ( options : MenuHarnessFilters = { } ) : HarnessPredicate < MatMenuHarness > {
35
- return new HarnessPredicate ( MatMenuHarness , options )
36
- . addOption ( 'triggerText' , options . triggerText ,
37
- ( harness , text ) => HarnessPredicate . stringMatches ( harness . getTriggerText ( ) , text ) ) ;
38
- }
39
-
40
35
/** Whether the menu is disabled. */
41
36
async isDisabled ( ) : Promise < boolean > {
42
37
const disabled = ( await this . host ( ) ) . getAttribute ( 'disabled' ) ;
@@ -87,12 +82,13 @@ export class MatMenuHarness extends ContentContainerComponentHarness<string> {
87
82
* Gets a list of `MatMenuItemHarness` representing the items in the menu.
88
83
* @param filters Optionally filters which menu items are included.
89
84
*/
90
- async getItems ( filters : Omit < MenuItemHarnessFilters , 'ancestor' > = { } ) :
91
- Promise < MatMenuItemHarness [ ] > {
85
+ async getItems ( filters ?: Omit < ItemFilters , 'ancestor' > ) : Promise < Item [ ] > {
92
86
const panelId = await this . _getPanelId ( ) ;
93
87
if ( panelId ) {
94
- return this . _documentRootLocator . locatorForAll (
95
- MatMenuItemHarness . with ( { ...filters , ancestor : `#${ panelId } ` } ) ) ( ) ;
88
+ return this . _documentRootLocator . locatorForAll ( this . _itemClass . with ( {
89
+ ...( filters || { } ) ,
90
+ ancestor : `#${ panelId } `
91
+ } as ItemFilters ) ) ( ) ;
96
92
}
97
93
return [ ] ;
98
94
}
@@ -106,8 +102,8 @@ export class MatMenuHarness extends ContentContainerComponentHarness<string> {
106
102
* `subItemFilters` will be clicked.
107
103
*/
108
104
async clickItem (
109
- itemFilter : Omit < MenuItemHarnessFilters , 'ancestor' > ,
110
- ...subItemFilters : Omit < MenuItemHarnessFilters , 'ancestor' > [ ] ) : Promise < void > {
105
+ itemFilter : Omit < ItemFilters , 'ancestor' > ,
106
+ ...subItemFilters : Omit < ItemFilters , 'ancestor' > [ ] ) : Promise < void > {
111
107
await this . open ( ) ;
112
108
const items = await this . getItems ( itemFilter ) ;
113
109
if ( ! items . length ) {
@@ -122,7 +118,7 @@ export class MatMenuHarness extends ContentContainerComponentHarness<string> {
122
118
if ( ! menu ) {
123
119
throw Error ( `Item matching ${ JSON . stringify ( itemFilter ) } does not have a submenu` ) ;
124
120
}
125
- return menu . clickItem ( ...subItemFilters as [ Omit < MenuItemHarnessFilters , 'ancestor' > ] ) ;
121
+ return menu . clickItem ( ...subItemFilters as [ Omit < ItemFilters , 'ancestor' > ] ) ;
126
122
}
127
123
128
124
protected async getRootHarnessLoader ( ) : Promise < HarnessLoader > {
@@ -143,25 +139,11 @@ export class MatMenuHarness extends ContentContainerComponentHarness<string> {
143
139
}
144
140
}
145
141
146
-
147
- /** Harness for interacting with a standard mat-menu-item in tests. */
148
- export class MatMenuItemHarness extends ContentContainerComponentHarness < string > {
149
- /** The selector for the host element of a `MatMenuItem` instance. */
150
- static hostSelector = '.mat-menu-item' ;
151
-
152
- /**
153
- * Gets a `HarnessPredicate` that can be used to search for a `MatMenuItemHarness` that meets
154
- * certain criteria.
155
- * @param options Options for filtering which menu item instances are considered a match.
156
- * @return a `HarnessPredicate` configured with the given options.
157
- */
158
- static with ( options : MenuItemHarnessFilters = { } ) : HarnessPredicate < MatMenuItemHarness > {
159
- return new HarnessPredicate ( MatMenuItemHarness , options )
160
- . addOption ( 'text' , options . text ,
161
- ( harness , text ) => HarnessPredicate . stringMatches ( harness . getText ( ) , text ) )
162
- . addOption ( 'hasSubmenu' , options . hasSubmenu ,
163
- async ( harness , hasSubmenu ) => ( await harness . hasSubmenu ( ) ) === hasSubmenu ) ;
164
- }
142
+ export abstract class _MatMenuItemHarnessBase <
143
+ MenuType extends ComponentHarnessConstructor < Menu > ,
144
+ Menu extends ComponentHarness ,
145
+ > extends ContentContainerComponentHarness < string > {
146
+ protected abstract _menuClass : MenuType ;
165
147
166
148
/** Whether the menu is disabled. */
167
149
async isDisabled ( ) : Promise < boolean > {
@@ -196,14 +178,57 @@ export class MatMenuItemHarness extends ContentContainerComponentHarness<string>
196
178
197
179
/** Whether this item has a submenu. */
198
180
async hasSubmenu ( ) : Promise < boolean > {
199
- return ( await this . host ( ) ) . matchesSelector ( MatMenuHarness . hostSelector ) ;
181
+ return ( await this . host ( ) ) . matchesSelector ( this . _menuClass . hostSelector ) ;
200
182
}
201
183
202
184
/** Gets the submenu associated with this menu item, or null if none. */
203
- async getSubmenu ( ) : Promise < MatMenuHarness | null > {
185
+ async getSubmenu ( ) : Promise < Menu | null > {
204
186
if ( await this . hasSubmenu ( ) ) {
205
- return new MatMenuHarness ( this . locatorFactory ) ;
187
+ return new this . _menuClass ( this . locatorFactory ) ;
206
188
}
207
189
return null ;
208
190
}
209
191
}
192
+
193
+
194
+ /** Harness for interacting with a standard mat-menu in tests. */
195
+ export class MatMenuHarness extends _MatMenuHarnessBase <
196
+ typeof MatMenuItemHarness , MatMenuItemHarness , MenuItemHarnessFilters > {
197
+ /** The selector for the host element of a `MatMenu` instance. */
198
+ static hostSelector = '.mat-menu-trigger' ;
199
+ protected _itemClass = MatMenuItemHarness ;
200
+
201
+ /**
202
+ * Gets a `HarnessPredicate` that can be used to search for a `MatMenuHarness` that meets certain
203
+ * criteria.
204
+ * @param options Options for filtering which menu instances are considered a match.
205
+ * @return a `HarnessPredicate` configured with the given options.
206
+ */
207
+ static with ( options : MenuHarnessFilters = { } ) : HarnessPredicate < MatMenuHarness > {
208
+ return new HarnessPredicate ( MatMenuHarness , options )
209
+ . addOption ( 'triggerText' , options . triggerText ,
210
+ ( harness , text ) => HarnessPredicate . stringMatches ( harness . getTriggerText ( ) , text ) ) ;
211
+ }
212
+ }
213
+
214
+ /** Harness for interacting with a standard mat-menu-item in tests. */
215
+ export class MatMenuItemHarness extends
216
+ _MatMenuItemHarnessBase < typeof MatMenuHarness , MatMenuHarness > {
217
+ /** The selector for the host element of a `MatMenuItem` instance. */
218
+ static hostSelector = '.mat-menu-item' ;
219
+ protected _menuClass = MatMenuHarness ;
220
+
221
+ /**
222
+ * Gets a `HarnessPredicate` that can be used to search for a `MatMenuItemHarness` that meets
223
+ * certain criteria.
224
+ * @param options Options for filtering which menu item instances are considered a match.
225
+ * @return a `HarnessPredicate` configured with the given options.
226
+ */
227
+ static with ( options : MenuItemHarnessFilters = { } ) : HarnessPredicate < MatMenuItemHarness > {
228
+ return new HarnessPredicate ( MatMenuItemHarness , options )
229
+ . addOption ( 'text' , options . text ,
230
+ ( harness , text ) => HarnessPredicate . stringMatches ( harness . getText ( ) , text ) )
231
+ . addOption ( 'hasSubmenu' , options . hasSubmenu ,
232
+ async ( harness , hasSubmenu ) => ( await harness . hasSubmenu ( ) ) === hasSubmenu ) ;
233
+ }
234
+ }
0 commit comments