77 */
88
99import {
10+ BaseHarnessFilters ,
11+ ComponentHarness ,
12+ ComponentHarnessConstructor ,
1013 ContentContainerComponentHarness ,
1114 HarnessLoader ,
1215 HarnessPredicate ,
@@ -16,27 +19,19 @@ import {
1619import { coerceBooleanProperty } from '@angular/cdk/coercion' ;
1720import { MenuHarnessFilters , MenuItemHarnessFilters } from './menu-harness-filters' ;
1821
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 > {
2430 private _documentRootLocator = this . documentRootLocatorFactory ( ) ;
31+ protected abstract _itemClass : ItemType ;
2532
2633 // TODO: potentially extend MatButtonHarness
2734
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-
4035 /** Whether the menu is disabled. */
4136 async isDisabled ( ) : Promise < boolean > {
4237 const disabled = ( await this . host ( ) ) . getAttribute ( 'disabled' ) ;
@@ -87,12 +82,13 @@ export class MatMenuHarness extends ContentContainerComponentHarness<string> {
8782 * Gets a list of `MatMenuItemHarness` representing the items in the menu.
8883 * @param filters Optionally filters which menu items are included.
8984 */
90- async getItems ( filters : Omit < MenuItemHarnessFilters , 'ancestor' > = { } ) :
91- Promise < MatMenuItemHarness [ ] > {
85+ async getItems ( filters ?: Omit < ItemFilters , 'ancestor' > ) : Promise < Item [ ] > {
9286 const panelId = await this . _getPanelId ( ) ;
9387 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 ) ) ( ) ;
9692 }
9793 return [ ] ;
9894 }
@@ -106,8 +102,8 @@ export class MatMenuHarness extends ContentContainerComponentHarness<string> {
106102 * `subItemFilters` will be clicked.
107103 */
108104 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 > {
111107 await this . open ( ) ;
112108 const items = await this . getItems ( itemFilter ) ;
113109 if ( ! items . length ) {
@@ -122,7 +118,7 @@ export class MatMenuHarness extends ContentContainerComponentHarness<string> {
122118 if ( ! menu ) {
123119 throw Error ( `Item matching ${ JSON . stringify ( itemFilter ) } does not have a submenu` ) ;
124120 }
125- return menu . clickItem ( ...subItemFilters as [ Omit < MenuItemHarnessFilters , 'ancestor' > ] ) ;
121+ return menu . clickItem ( ...subItemFilters as [ Omit < ItemFilters , 'ancestor' > ] ) ;
126122 }
127123
128124 protected async getRootHarnessLoader ( ) : Promise < HarnessLoader > {
@@ -143,25 +139,11 @@ export class MatMenuHarness extends ContentContainerComponentHarness<string> {
143139 }
144140}
145141
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 ;
165147
166148 /** Whether the menu is disabled. */
167149 async isDisabled ( ) : Promise < boolean > {
@@ -196,14 +178,57 @@ export class MatMenuItemHarness extends ContentContainerComponentHarness<string>
196178
197179 /** Whether this item has a submenu. */
198180 async hasSubmenu ( ) : Promise < boolean > {
199- return ( await this . host ( ) ) . matchesSelector ( MatMenuHarness . hostSelector ) ;
181+ return ( await this . host ( ) ) . matchesSelector ( this . _menuClass . hostSelector ) ;
200182 }
201183
202184 /** Gets the submenu associated with this menu item, or null if none. */
203- async getSubmenu ( ) : Promise < MatMenuHarness | null > {
185+ async getSubmenu ( ) : Promise < Menu | null > {
204186 if ( await this . hasSubmenu ( ) ) {
205- return new MatMenuHarness ( this . locatorFactory ) ;
187+ return new this . _menuClass ( this . locatorFactory ) ;
206188 }
207189 return null ;
208190 }
209191}
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