From 4d9c87c8bcd1d4506d4fdbee8946c221989941ac Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 28 Oct 2025 10:18:38 +0100 Subject: [PATCH] fix(material/menu): add filter for trigger icon name Adds the option to filter harnesses based on the icon in the menu trigger. Fixes #32181. --- goldens/material/menu/testing/index.api.md | 1 + src/material/menu/testing/BUILD.bazel | 2 + .../menu/testing/menu-harness-filters.ts | 3 + .../menu/testing/menu-harness.spec.ts | 58 +++++++++++-------- src/material/menu/testing/menu-harness.ts | 16 +++-- 5 files changed, 51 insertions(+), 29 deletions(-) diff --git a/goldens/material/menu/testing/index.api.md b/goldens/material/menu/testing/index.api.md index 8cc4d315be89..43e5a2601eea 100644 --- a/goldens/material/menu/testing/index.api.md +++ b/goldens/material/menu/testing/index.api.md @@ -62,6 +62,7 @@ export class MatMenuItemHarness extends ContentContainerComponentHarness // @public export interface MenuHarnessFilters extends BaseHarnessFilters { + triggerIconName?: string | RegExp; triggerText?: string | RegExp; } diff --git a/src/material/menu/testing/BUILD.bazel b/src/material/menu/testing/BUILD.bazel index 8db24891f557..bb8065b43cb3 100644 --- a/src/material/menu/testing/BUILD.bazel +++ b/src/material/menu/testing/BUILD.bazel @@ -11,6 +11,7 @@ ts_project( deps = [ "//src/cdk/coercion", "//src/cdk/testing", + "//src/material/icon/testing", ], ) @@ -31,6 +32,7 @@ ng_project( "//src/cdk/testing", "//src/cdk/testing/private", "//src/cdk/testing/testbed", + "//src/material/icon", "//src/material/menu", ], ) diff --git a/src/material/menu/testing/menu-harness-filters.ts b/src/material/menu/testing/menu-harness-filters.ts index b2bdccc45653..fc74bc6daa9a 100644 --- a/src/material/menu/testing/menu-harness-filters.ts +++ b/src/material/menu/testing/menu-harness-filters.ts @@ -12,6 +12,9 @@ import {BaseHarnessFilters} from '@angular/cdk/testing'; export interface MenuHarnessFilters extends BaseHarnessFilters { /** Only find instances whose trigger text matches the given value. */ triggerText?: string | RegExp; + + /** Only find instances where the trigger contains an icon whose name matches the given value. */ + triggerIconName?: string | RegExp; } /** A set of criteria that can be used to filter a list of `MatMenuItemHarness` instances. */ diff --git a/src/material/menu/testing/menu-harness.spec.ts b/src/material/menu/testing/menu-harness.spec.ts index 9db9280c9d47..84aa1721d16c 100644 --- a/src/material/menu/testing/menu-harness.spec.ts +++ b/src/material/menu/testing/menu-harness.spec.ts @@ -1,6 +1,7 @@ import {Component} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {HarnessLoader} from '@angular/cdk/testing'; +import {MatIconModule} from '@angular/material/icon'; import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; import {MatMenuModule} from '../menu-module'; import {MatMenuHarness} from './menu-harness'; @@ -18,7 +19,7 @@ describe('MatMenuHarness', () => { it('should load all menu harnesses', async () => { const menues = await loader.getAllHarnesses(MatMenuHarness); - expect(menues.length).toBe(2); + expect(menues.length).toBe(3); }); it('should load menu with exact text', async () => { @@ -33,6 +34,12 @@ describe('MatMenuHarness', () => { expect(await menus[0].getTriggerText()).toBe('Settings'); }); + it('should load menu by icon name', async () => { + const menus = await loader.getAllHarnesses(MatMenuHarness.with({triggerIconName: 'wrench'})); + expect(menus.length).toBe(1); + expect(await (await menus[0].host()).getAttribute('id')).toBe('with-icon'); + }); + it('should get disabled state', async () => { const [enabledMenu, disabledMenu] = await loader.getAllHarnesses(MatMenuHarness); expect(await enabledMenu.isDisabled()).toBe(false); @@ -146,39 +153,42 @@ describe('MatMenuHarness', () => { @Component({ template: ` - - - - - Profile - Account - + + + + + + Profile + Account + `, - imports: [MatMenuModule], + imports: [MatMenuModule, MatIconModule], }) class MenuHarnessTest {} @Component({ template: ` - + - - - - - + + + + + - - - + + + - - - + + + - - - + + + `, imports: [MatMenuModule], }) diff --git a/src/material/menu/testing/menu-harness.ts b/src/material/menu/testing/menu-harness.ts index 8bbe6215273f..55951213b65f 100644 --- a/src/material/menu/testing/menu-harness.ts +++ b/src/material/menu/testing/menu-harness.ts @@ -14,6 +14,7 @@ import { TestElement, } from '@angular/cdk/testing'; import {coerceBooleanProperty} from '@angular/cdk/coercion'; +import {MatIconHarness} from '@angular/material/icon/testing'; import {MenuHarnessFilters, MenuItemHarnessFilters} from './menu-harness-filters'; /** Harness for interacting with a mat-menu in tests. */ @@ -32,11 +33,16 @@ export class MatMenuHarness extends ContentContainerComponentHarness { this: ComponentHarnessConstructor, options: MenuHarnessFilters = {}, ): HarnessPredicate { - return new HarnessPredicate(this, options).addOption( - 'triggerText', - options.triggerText, - (harness, text) => HarnessPredicate.stringMatches(harness.getTriggerText(), text), - ); + return new HarnessPredicate(this, options) + .addOption('triggerText', options.triggerText, (harness, text) => + HarnessPredicate.stringMatches(harness.getTriggerText(), text), + ) + .addOption('triggerIconName', options.triggerIconName, async (harness, triggerIconName) => { + const result = await harness.locatorForOptional( + MatIconHarness.with({name: triggerIconName}), + )(); + return result !== null; + }); } /** Whether the menu is disabled. */