diff --git a/test/benchmarks/material/menu/BUILD.bazel b/test/benchmarks/material/menu/BUILD.bazel
new file mode 100644
index 000000000000..57d50dc86b3b
--- /dev/null
+++ b/test/benchmarks/material/menu/BUILD.bazel
@@ -0,0 +1,23 @@
+load("@npm_angular_dev_infra_private//benchmark/component_benchmark:component_benchmark.bzl", "component_benchmark")
+
+# TODO(wagnermaciel): Update this target to provide indigo-pink in a way that doesn't require having to import it with
+# stylesUrls inside the components once `component_benchmark` supports asset injection.
+
+component_benchmark(
+ name = "benchmark",
+ driver = ":menu.perf-spec.ts",
+ driver_deps = [
+ "@npm//@angular/dev-infra-private",
+ "@npm//protractor",
+ "@npm//@types/jasmine",
+ ],
+ ng_assets = [":menu.html"],
+ ng_deps = [
+ "@npm//@angular/core",
+ "@npm//@angular/platform-browser",
+ "//src/material/menu",
+ ],
+ ng_srcs = [":app.module.ts"],
+ prefix = "",
+ styles = ["//src/material/prebuilt-themes:indigo-pink"],
+)
diff --git a/test/benchmarks/material/menu/app.module.ts b/test/benchmarks/material/menu/app.module.ts
new file mode 100644
index 000000000000..d4dc09f02ba1
--- /dev/null
+++ b/test/benchmarks/material/menu/app.module.ts
@@ -0,0 +1,33 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {Component, NgModule, ViewEncapsulation} from '@angular/core';
+import {BrowserModule} from '@angular/platform-browser';
+import {MatMenuModule} from '@angular/material/menu';
+
+/** component: mat-menu */
+
+@Component({
+ selector: 'app-root',
+ templateUrl: './menu.html',
+ encapsulation: ViewEncapsulation.None,
+ styleUrls: ['//src/material/core/theming/prebuilt/indigo-pink.css'],
+})
+export class MenuBenchmarkApp {
+}
+
+
+@NgModule({
+ declarations: [MenuBenchmarkApp],
+ imports: [
+ BrowserModule,
+ MatMenuModule,
+ ],
+ bootstrap: [MenuBenchmarkApp]
+})
+export class AppModule {}
diff --git a/test/benchmarks/material/menu/menu.html b/test/benchmarks/material/menu/menu.html
new file mode 100644
index 000000000000..eddeaaa4f19d
--- /dev/null
+++ b/test/benchmarks/material/menu/menu.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/benchmarks/material/menu/menu.perf-spec.ts b/test/benchmarks/material/menu/menu.perf-spec.ts
new file mode 100755
index 000000000000..51dcc406d1a3
--- /dev/null
+++ b/test/benchmarks/material/menu/menu.perf-spec.ts
@@ -0,0 +1,69 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {$, by, element, ElementFinder, Key} from 'protractor';
+import {runBenchmark} from '@angular/dev-infra-private/benchmark/driver-utilities';
+
+// Clicking to close a menu is problematic. This is a solution that uses `.sendKeys()` avoids
+// issues with `.click()`.
+
+async function closeMenu(trigger: ElementFinder) {
+ const backdropId = await trigger.getAttribute('aria-controls');
+ if (await $(`#${backdropId}`).isPresent()) {
+ await $(`#${backdropId}`).sendKeys(Key.ESCAPE);
+ }
+}
+
+describe('menu performance benchmarks', () => {
+ it('opens a basic menu', async () => {
+ let trigger: ElementFinder;
+ await runBenchmark({
+ id: 'basic-menu-open',
+ url: '',
+ ignoreBrowserSynchronization: true,
+ params: [],
+ setup: async () => trigger = element(by.buttonText('Basic Menu')),
+ work: async () => {
+ await trigger.click();
+ await closeMenu(trigger);
+ }
+ });
+ });
+
+ it('opens the root menu of a set of nested menus', async () => {
+ let trigger: ElementFinder;
+ await runBenchmark({
+ id: 'nested-menu-open-shallow',
+ url: '',
+ ignoreBrowserSynchronization: true,
+ params: [],
+ setup: async () => trigger = element(by.buttonText('Nested Menu')),
+ work: async () => {
+ await trigger.click();
+ await closeMenu(trigger);
+ },
+ });
+ });
+
+ it('fully opens a menu with nested menus', async () => {
+ let trigger: ElementFinder;
+ await runBenchmark({
+ id: 'menu-open-deep',
+ url: '',
+ ignoreBrowserSynchronization: true,
+ params: [],
+ setup: async () => trigger = element(by.buttonText('Nested Menu')),
+ work: async () => {
+ await trigger.click();
+ await element(by.buttonText('Sub Menu 1')).click();
+ await element(by.buttonText('Sub Menu 2')).click();
+ await closeMenu(trigger);
+ },
+ });
+ });
+});