From 5acd6831f7a24a0dbbf0d3cda6d61add3bb296be Mon Sep 17 00:00:00 2001
From: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Date: Wed, 12 May 2021 19:00:48 +0200
Subject: [PATCH] Cherry pick Cypress custom command `cy.button()` (#16011)

* Add Cypress custom command `button` (#16007)

* Apply new custom command to the missed occurrences on `master`
---
 frontend/test/__support__/e2e/commands.js     |  4 ++
 .../test/metabase-db/mongo/query.cy.spec.js   |  2 +-
 .../postgres/custom-column.cy.spec.js         |  4 +-
 .../metabase-db/postgres/native.cy.spec.js    |  2 +-
 .../test/metabase-smoketest/admin.cy.spec.js  |  2 +-
 .../metabase-smoketest/admin_setup.cy.spec.js |  2 +-
 .../test/metabase-smoketest/user.cy.spec.js   | 20 +++++-----
 .../scenarios/admin/databases/add.cy.spec.js  | 16 ++++----
 .../admin/permissions/sandboxes.cy.spec.js    | 14 +++----
 .../admin/settings/settings.cy.spec.js        |  2 +-
 .../collections/permissions.cy.spec.js        |  2 +-
 .../personal-collections.cy.spec.js           |  2 +-
 .../dashboard/dashboard-drill.cy.spec.js      |  4 +-
 .../scenarios/dashboard/dashboard.cy.spec.js  |  4 +-
 .../scenarios/native/native.cy.spec.js        |  4 +-
 .../onboarding/setup/user_settings.cy.spec.js |  2 +-
 .../question/custom_column.cy.spec.js         | 20 +++++-----
 .../scenarios/question/filter.cy.spec.js      | 20 +++++-----
 .../scenarios/question/joins.cy.spec.js       |  2 +-
 .../scenarios/question/nested.cy.spec.js      |  4 +-
 .../scenarios/question/new.cy.spec.js         |  4 +-
 .../scenarios/question/notebook.cy.spec.js    | 37 +++++++++--------
 .../scenarios/question/saved.cy.spec.js       |  2 +-
 .../scenarios/sharing/alert/alert.cy.spec.js  |  2 +-
 .../sharing/subscriptions.cy.spec.js          | 40 ++++++++++---------
 .../drillthroughs/chart_drill.cy.spec.js      |  2 +-
 .../visualizations/trendline.cy.spec.js       |  4 +-
 .../visualizations/waterfall.cy.spec.js       |  6 +--
 28 files changed, 117 insertions(+), 112 deletions(-)

diff --git a/frontend/test/__support__/e2e/commands.js b/frontend/test/__support__/e2e/commands.js
index f31417d7596..273c895f34e 100644
--- a/frontend/test/__support__/e2e/commands.js
+++ b/frontend/test/__support__/e2e/commands.js
@@ -39,6 +39,10 @@ Cypress.Commands.add("icon", icon_name => {
   cy.get(`.Icon-${icon_name}`);
 });
 
+Cypress.Commands.add("button", button_name => {
+  cy.findByRole("button", { name: button_name });
+});
+
 Cypress.Commands.add("createDashboard", name => {
   cy.log(`Create a dashboard: ${name}`);
   cy.request("POST", "/api/dashboard", { name });
diff --git a/frontend/test/metabase-db/mongo/query.cy.spec.js b/frontend/test/metabase-db/mongo/query.cy.spec.js
index aaa2bc1f535..8c15d2efe07 100644
--- a/frontend/test/metabase-db/mongo/query.cy.spec.js
+++ b/frontend/test/metabase-db/mongo/query.cy.spec.js
@@ -67,7 +67,7 @@ describe("mongodb > user > query", () => {
       cy.server();
       cy.route("POST", "/api/dataset").as("dataset");
 
-      cy.findByText("Visualize").click();
+      cy.button("Visualize").click();
       cy.wait("@dataset");
 
       cy.log("Reported failing on stats ~v0.36.3");
diff --git a/frontend/test/metabase-db/postgres/custom-column.cy.spec.js b/frontend/test/metabase-db/postgres/custom-column.cy.spec.js
index 9e59ea1c6e3..ba2249c3f40 100644
--- a/frontend/test/metabase-db/postgres/custom-column.cy.spec.js
+++ b/frontend/test/metabase-db/postgres/custom-column.cy.spec.js
@@ -48,7 +48,7 @@ describe("postgres > question > custom columns", () => {
         .click();
     });
 
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
     cy.findByText("Arnold Adams");
   });
 
@@ -90,7 +90,7 @@ describe("postgres > question > custom columns", () => {
       .click();
     cy.findByText("Function Percentile expects 1 argument").should("not.exist");
     cy.get("@description").type("A");
-    cy.findByRole("button", { name: "Done" })
+    cy.button("Done")
       .should("not.be.disabled")
       .click();
     // Todo: Add positive assertions once this is fixed
diff --git a/frontend/test/metabase-db/postgres/native.cy.spec.js b/frontend/test/metabase-db/postgres/native.cy.spec.js
index 887f902cd6e..ad6ff5dadf1 100644
--- a/frontend/test/metabase-db/postgres/native.cy.spec.js
+++ b/frontend/test/metabase-db/postgres/native.cy.spec.js
@@ -16,7 +16,7 @@ describe("postgres > question > native", () => {
     cy.get(".ace_content").type("select pg_sleep(60)");
     cy.findByText("Save").click();
     cy.findByLabelText("Name").type("14957");
-    cy.findByRole("button", { name: "Save" }).click();
+    cy.button("Save").click();
     modal().should("not.exist");
   });
 });
diff --git a/frontend/test/metabase-smoketest/admin.cy.spec.js b/frontend/test/metabase-smoketest/admin.cy.spec.js
index 41ef464270f..537e96268a9 100644
--- a/frontend/test/metabase-smoketest/admin.cy.spec.js
+++ b/frontend/test/metabase-smoketest/admin.cy.spec.js
@@ -168,7 +168,7 @@ describe("metabase-smoketest > admin", () => {
 
       cy.findByText("Join data").click();
       cy.findByText("People").click();
-      cy.findByText("Visualize").click();
+      cy.button("Visualize").click();
 
       // Summarize by State
       cy.findAllByText("Summarize")
diff --git a/frontend/test/metabase-smoketest/admin_setup.cy.spec.js b/frontend/test/metabase-smoketest/admin_setup.cy.spec.js
index 434c02998c8..9a7da9fb36a 100644
--- a/frontend/test/metabase-smoketest/admin_setup.cy.spec.js
+++ b/frontend/test/metabase-smoketest/admin_setup.cy.spec.js
@@ -478,7 +478,7 @@ describe("smoketest > admin_setup", () => {
         .last()
         .click();
       cy.findByText("Add filter").click();
-      cy.findByText("Visualize").click();
+      cy.button("Visualize").click();
 
       cy.findAllByText("Awesome Concrete Shoes");
       cy.findByText("Mediocre Wooden Bench").should("not.exist");
diff --git a/frontend/test/metabase-smoketest/user.cy.spec.js b/frontend/test/metabase-smoketest/user.cy.spec.js
index 4b93306c5d5..d134c001426 100644
--- a/frontend/test/metabase-smoketest/user.cy.spec.js
+++ b/frontend/test/metabase-smoketest/user.cy.spec.js
@@ -24,7 +24,7 @@ describe("smoketest > user", () => {
 
     cy.findByText("Average of Rating");
 
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
 
     cy.icon("bar");
     cy.findAllByText("Vendor is not empty");
@@ -67,7 +67,7 @@ describe("smoketest > user", () => {
     popover().within(() => {
       cy.findAllByText("Title").click();
     });
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
 
     cy.get("@firstTableCell").contains("Aerodynamic Bronze Hat");
 
@@ -106,9 +106,9 @@ describe("smoketest > user", () => {
     cy.findByText("Greater than or equal to").click();
     cy.get("input[placeholder='Enter a number']").type("5");
     cy.findByText("Add filter").click();
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
 
-    cy.findByText("Visualize").should("not.exist");
+    cy.button("Visualize").should("not.exist");
     cy.get("svg");
     cy.findByText("Average of Rating is greater than or equal to 5");
 
@@ -180,7 +180,7 @@ describe("smoketest > user", () => {
     cy.findByText("Count of rows").click();
     cy.findByText("Pick a column to group by").click();
     cy.icon("calendar").click();
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
 
     cy.get("svg");
     cy.findAllByText("Created At");
@@ -223,7 +223,7 @@ describe("smoketest > user", () => {
       "Demo Column",
     );
     cy.findByText("Done").click();
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
 
     cy.findByText("ID");
     cy.icon("table2");
@@ -238,7 +238,7 @@ describe("smoketest > user", () => {
 
     cy.icon("join_left_outer").click();
     cy.findByText("People").click(); // column selection happens automatcially
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
 
     cy.findByText("User → ID");
     cy.findByText("Created At");
@@ -250,7 +250,7 @@ describe("smoketest > user", () => {
 
     cy.findByText("Row limit").click();
     cy.get("input[type='number']").type("10");
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
 
     cy.get(".TableInteractive-cellWrapper--firstColumn").should(
       "have.length",
@@ -281,7 +281,7 @@ describe("smoketest > user", () => {
     // Distinctions
     // *** This test needs to be improved with variables that will change if the Sample data changes
 
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
 
     cy.findByText("Category").click();
     cy.findByText("Distincts").click();
@@ -375,7 +375,7 @@ describe("smoketest > user", () => {
       .last()
       .click();
     cy.findByText("People").click();
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
 
     cy.findByText("Longitude").click();
 
diff --git a/frontend/test/metabase/scenarios/admin/databases/add.cy.spec.js b/frontend/test/metabase/scenarios/admin/databases/add.cy.spec.js
index 8c79f80cc7e..56cc2428f73 100644
--- a/frontend/test/metabase/scenarios/admin/databases/add.cy.spec.js
+++ b/frontend/test/metabase/scenarios/admin/databases/add.cy.spec.js
@@ -44,7 +44,7 @@ describe("scenarios > admin > databases > add", () => {
     typeField("Database name", "test_postgres_db");
     typeField("Username", "uberadmin");
 
-    cy.findByRole("button", { name: "Save" })
+    cy.button("Save")
       .should("not.be.disabled")
       .click();
 
@@ -80,7 +80,7 @@ describe("scenarios > admin > databases > add", () => {
     typeField("Database name", "test_postgres_db");
     typeField("Username", "uberadmin");
 
-    cy.findByRole("button", { name: "Save" })
+    cy.button("Save")
       .should("not.be.disabled")
       .click();
 
@@ -88,7 +88,7 @@ describe("scenarios > admin > databases > add", () => {
 
     toggleFieldWithDisplayName("let me choose when Metabase syncs and scans");
 
-    cy.findByRole("button", { name: "Next" })
+    cy.button("Next")
       .should("not.be.disabled")
       .click();
 
@@ -107,17 +107,17 @@ describe("scenarios > admin > databases > add", () => {
     typeField("Database name", "test_postgres_db");
     typeField("Username", "uberadmin");
 
-    cy.findByRole("button", { name: "Save" }).should("not.be.disabled");
+    cy.button("Save").should("not.be.disabled");
 
     toggleFieldWithDisplayName("let me choose when Metabase syncs and scans");
 
-    cy.findByRole("button", { name: "Next" })
+    cy.button("Next")
       .should("not.be.disabled")
       .click();
 
     cy.findByText("Never, I'll do this manually if I need to").click();
 
-    cy.findByRole("button", { name: "Save" }).click();
+    cy.button("Save").click();
 
     cy.wait("@createDatabase").then(({ request }) => {
       expect(request.body.engine).to.equal("postgres");
@@ -144,7 +144,7 @@ describe("scenarios > admin > databases > add", () => {
     typeField("Database name", "test_postgres_db");
     typeField("Username", "uberadmin");
 
-    cy.findByRole("button", { name: "Save" }).click();
+    cy.button("Save").click();
 
     cy.wait("@createDatabase");
     cy.findByText("DATABASE CONNECTION ERROR").should("exist");
@@ -204,7 +204,7 @@ describe("scenarios > admin > databases > add", () => {
       }).as("createDatabase");
 
       // submit form and check that the file's body is included
-      cy.findByRole("button", { name: "Save" }).click();
+      cy.button("Save").click();
       cy.wait("@createDatabase").should(xhr => {
         expect(xhr.request.body.details["service-account-json"]).to.equal(
           '{"foo": 123}',
diff --git a/frontend/test/metabase/scenarios/admin/permissions/sandboxes.cy.spec.js b/frontend/test/metabase/scenarios/admin/permissions/sandboxes.cy.spec.js
index 075436782a4..7487ada6ab8 100644
--- a/frontend/test/metabase/scenarios/admin/permissions/sandboxes.cy.spec.js
+++ b/frontend/test/metabase/scenarios/admin/permissions/sandboxes.cy.spec.js
@@ -146,7 +146,7 @@ describeWithToken("formatting > sandboxes", () => {
         cy.findByText("Greater than").click();
         cy.findByPlaceholderText("Enter a number").type("100");
         cy.findByText("Add filter").click();
-        cy.findByText("Visualize").click();
+        cy.button("Visualize").click();
 
         cy.log("Make sure user is still sandboxed");
         cy.get(".TableInteractive-cellWrapper--firstColumn").should(
@@ -219,7 +219,7 @@ describeWithToken("formatting > sandboxes", () => {
           .click();
       });
 
-      cy.findByText("Visualize").click();
+      cy.button("Visualize").click();
       cy.findByText("Count by User → ID");
       cy.findByText("11"); // Sum of orders for user with ID #1
     });
@@ -605,7 +605,7 @@ describeWithToken("formatting > sandboxes", () => {
             // Save the question
             cy.findByText("Save").click();
             modal().within(() => {
-              cy.findAllByRole("button", { name: "Save" }).click();
+              cy.button("Save").click();
             });
             // Wait for an update so the other queries don't accidentally cancel it
             cy.wait("@questionUpdate");
@@ -749,12 +749,12 @@ describeWithToken("formatting > sandboxes", () => {
         .eq(1) // No better way of doing this, undfortunately (see table above)
         .click();
       cy.findByText("Grant sandboxed access").click();
-      cy.findAllByRole("button", { name: "Change" }).click();
+      cy.button("Change").click();
       cy.findByText(
         "Use a saved question to create a custom view for this table",
       ).click();
       cy.findByText(QUESTION_NAME).click();
-      cy.findAllByRole("button", { name: "Save" }).click();
+      cy.button("Save").click();
 
       cy.wait("@sandboxTable").then(xhr => {
         expect(xhr.status).to.eq(400);
@@ -778,7 +778,7 @@ describeWithToken("formatting > sandboxes", () => {
       cy.findByText("Count of rows").click();
       cy.findByText("Pick a column to group by").click();
       cy.findByText(/Products? → ID/).click();
-      cy.findByText("Visualize").click();
+      cy.button("Visualize").click();
 
       cy.wait("@dataset").then(xhr => {
         expect(xhr.response.body.error).not.to.exist;
@@ -822,7 +822,7 @@ describeWithToken("formatting > sandboxes", () => {
             .find(".Icon-close")
             .click();
         });
-      cy.findAllByRole("button", { name: "Done" }).click();
+      cy.button("Done").click();
       // Rerun the query
       cy.icon("play")
         .last()
diff --git a/frontend/test/metabase/scenarios/admin/settings/settings.cy.spec.js b/frontend/test/metabase/scenarios/admin/settings/settings.cy.spec.js
index 5450c6a6776..74e22fa6fdd 100644
--- a/frontend/test/metabase/scenarios/admin/settings/settings.cy.spec.js
+++ b/frontend/test/metabase/scenarios/admin/settings/settings.cy.spec.js
@@ -406,7 +406,7 @@ describe("scenarios > admin > settings", () => {
       cy.visit("/admin/settings/email");
       cy.findByText("Send test email").scrollIntoView();
       // Needed to scroll the page down first to be able to use findByRole() - it fails otherwise
-      cy.findByRole("button", { name: "Save changes" }).should("be.disabled");
+      cy.button("Save changes").should("be.disabled");
     });
   });
 
diff --git a/frontend/test/metabase/scenarios/collections/permissions.cy.spec.js b/frontend/test/metabase/scenarios/collections/permissions.cy.spec.js
index 96e6ae936f4..314d52d7f7d 100644
--- a/frontend/test/metabase/scenarios/collections/permissions.cy.spec.js
+++ b/frontend/test/metabase/scenarios/collections/permissions.cy.spec.js
@@ -475,7 +475,7 @@ describe("collection permissions", () => {
                   cy.findByText("Create a new dashboard").click();
                   cy.get(".AdminSelect").findByText(personalCollection);
                   cy.findByLabelText("Name").type("Foo");
-                  cy.findByRole("button", { name: "Create" }).click();
+                  cy.button("Create").click();
                 });
                 cy.url().should("match", /\/dashboard\/\d+$/);
                 saveDashboard();
diff --git a/frontend/test/metabase/scenarios/collections/personal-collections.cy.spec.js b/frontend/test/metabase/scenarios/collections/personal-collections.cy.spec.js
index 02343631500..73ac5f9976d 100644
--- a/frontend/test/metabase/scenarios/collections/personal-collections.cy.spec.js
+++ b/frontend/test/metabase/scenarios/collections/personal-collections.cy.spec.js
@@ -106,7 +106,7 @@ describe("personal collections", () => {
           popover()
             .findByText("My personal collection") /* [3] */
             .click();
-          cy.findByRole("button", { name: "Update" }).click();
+          cy.button("Update").click();
           // Clicking on "Foo" would've closed it and would hide its sub-collections (if there were any).
           // By doing this, we're making sure "Bar" lives at the same level as "Foo"
           cy.get("@sidebar")
diff --git a/frontend/test/metabase/scenarios/dashboard/dashboard-drill.cy.spec.js b/frontend/test/metabase/scenarios/dashboard/dashboard-drill.cy.spec.js
index 107f015342d..bb0cc4a5974 100644
--- a/frontend/test/metabase/scenarios/dashboard/dashboard-drill.cy.spec.js
+++ b/frontend/test/metabase/scenarios/dashboard/dashboard-drill.cy.spec.js
@@ -585,10 +585,10 @@ describe("scenarios > dashboard > dashboard drill", () => {
         cy.icon("palette").click();
         cy.get(".Modal").within(() => {
           cy.findByText("Reset to defaults").click();
-          cy.findByRole("button", { name: "Done" }).click();
+          cy.button("Done").click();
         });
         // Save the whole dashboard
-        cy.findByRole("button", { name: "Save" }).click();
+        cy.button("Save").click();
         cy.findByText("You're editing this dashboard.").should("not.exist");
         cy.log("Reported failing on v0.38.0 - link gets dropped");
         cy.get(".DashCard").findAllByText(LINK_NAME);
diff --git a/frontend/test/metabase/scenarios/dashboard/dashboard.cy.spec.js b/frontend/test/metabase/scenarios/dashboard/dashboard.cy.spec.js
index 4ce8f098c97..cb60e3e6ad4 100644
--- a/frontend/test/metabase/scenarios/dashboard/dashboard.cy.spec.js
+++ b/frontend/test/metabase/scenarios/dashboard/dashboard.cy.spec.js
@@ -588,7 +588,7 @@ describe("scenarios > dashboard", () => {
         cy.findByPlaceholderText("Enter some text")
           .click()
           .type("Gizmo", { delay: 10 });
-        cy.findByRole("button", { name: "Add filter" })
+        cy.button("Add filter")
           .should("not.be.disabled")
           .click();
         cy.contains("Rustic Paper Wallet");
@@ -728,7 +728,7 @@ describe("scenarios > dashboard", () => {
       popover()
         .findByText("Organic")
         .click();
-      cy.findByRole("button", { name: "Add filter" }).click();
+      cy.button("Add filter").click();
       // Check that the search works
       cy.get("fieldset")
         .contains("Search")
diff --git a/frontend/test/metabase/scenarios/native/native.cy.spec.js b/frontend/test/metabase/scenarios/native/native.cy.spec.js
index 68799db12b1..46fa2ba2965 100644
--- a/frontend/test/metabase/scenarios/native/native.cy.spec.js
+++ b/frontend/test/metabase/scenarios/native/native.cy.spec.js
@@ -560,7 +560,7 @@ describe("scenarios > question > native", () => {
     popover()
       .findByText("Doohickey")
       .click();
-    cy.findByRole("button", { name: "Add filter" }).click();
+    cy.button("Add filter").click();
     // Rerun the query
     cy.get(".NativeQueryEditor .Icon-play").click();
     cy.wait("@dataset").wait("@dataset");
@@ -597,7 +597,7 @@ describe("scenarios > question > native", () => {
     popover()
       .findByText("Doohickey")
       .click();
-    cy.findByRole("button", { name: "Add filter" }).click();
+    cy.button("Add filter").click();
     cy.get(".NativeQueryEditor .Icon-play").click();
     cy.wait("@dataset").then(xhr => {
       expect(xhr.response.body.error).not.to.exist;
diff --git a/frontend/test/metabase/scenarios/onboarding/setup/user_settings.cy.spec.js b/frontend/test/metabase/scenarios/onboarding/setup/user_settings.cy.spec.js
index a8f94890c6c..7a5afdacb13 100644
--- a/frontend/test/metabase/scenarios/onboarding/setup/user_settings.cy.spec.js
+++ b/frontend/test/metabase/scenarios/onboarding/setup/user_settings.cy.spec.js
@@ -37,7 +37,7 @@ describe("user > settings", () => {
     cy.findByDisplayValue(first_name);
     cy.findByDisplayValue(last_name);
     cy.findByDisplayValue(email);
-    cy.findByRole("button", { name: "Update" }).should("be.disabled");
+    cy.button("Update").should("be.disabled");
   });
 
   it("should update the user without fetching memberships", () => {
diff --git a/frontend/test/metabase/scenarios/question/custom_column.cy.spec.js b/frontend/test/metabase/scenarios/question/custom_column.cy.spec.js
index e1f128aff1d..df5773994e4 100644
--- a/frontend/test/metabase/scenarios/question/custom_column.cy.spec.js
+++ b/frontend/test/metabase/scenarios/question/custom_column.cy.spec.js
@@ -42,7 +42,7 @@ describe("scenarios > question > custom columns", () => {
     cy.server();
     cy.route("POST", "/api/dataset").as("dataset");
 
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
     cy.wait("@dataset");
     cy.findByText("There was a problem with your question").should("not.exist");
     cy.get(".Visualization").contains(columnName);
@@ -63,7 +63,7 @@ describe("scenarios > question > custom columns", () => {
       cy.server();
       cy.route("POST", "/api/dataset").as("dataset");
 
-      cy.findByText("Visualize").click();
+      cy.button("Visualize").click();
       cy.wait("@dataset");
       cy.get(".Visualization").contains(columnName);
     });
@@ -109,7 +109,7 @@ describe("scenarios > question > custom columns", () => {
     cy.server();
     cy.route("POST", "/api/dataset").as("dataset");
 
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
     cy.wait("@dataset");
     cy.findByText("There was a problem with your question").should("not.exist");
     // This is a pre-save state of the question but the column name should appear
@@ -151,7 +151,7 @@ describe("scenarios > question > custom columns", () => {
     cy.server();
     cy.route("POST", "/api/dataset").as("dataset");
 
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
     cy.wait("@dataset");
 
     cy.get(".Visualization").within(() => {
@@ -189,11 +189,11 @@ describe("scenarios > question > custom columns", () => {
       cy.findByText("Done").click();
     });
 
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
 
     // wait for results to load
     cy.get(".LoadingSpinner").should("not.exist");
-    cy.findByText("Visualize").should("not.exist");
+    cy.button("Visualize").should("not.exist");
 
     cy.log(
       "**Fails in 0.35.0, 0.35.1, 0.35.2, 0.35.4 and the latest master (2020-10-21)**",
@@ -408,7 +408,7 @@ describe("scenarios > question > custom columns", () => {
     cy.get("[class*=NotebookCellItem]")
       .contains(CE_NAME)
       .should("not.exist");
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
 
     cy.wait("@dataset").then(xhr => {
       expect(xhr.response.body.error).to.not.exist;
@@ -427,7 +427,7 @@ describe("scenarios > question > custom columns", () => {
         cy.findByPlaceholderText("Something nice and descriptive").type(
           "CategoryTitle",
         );
-        cy.findByRole("button", { name: "Done" }).click();
+        cy.button("Done").click();
       });
       cy.findByText("Filter").click();
       popover()
@@ -464,7 +464,7 @@ describe("scenarios > question > custom columns", () => {
         cy.findByPlaceholderText("Something nice and descriptive").type(
           "MiscDate",
         );
-        cy.findByRole("button", { name: "Done" }).click();
+        cy.button("Done").click();
       });
       cy.findByText("Filter").click();
       popover()
@@ -485,7 +485,7 @@ describe("scenarios > question > custom columns", () => {
         cy.findByPlaceholderText("Something nice and descriptive").type(
           "MiscDate",
         );
-        cy.findByRole("button", { name: "Done" }).click();
+        cy.button("Done").click();
       });
       cy.findByText("Filter").click();
       popover()
diff --git a/frontend/test/metabase/scenarios/question/filter.cy.spec.js b/frontend/test/metabase/scenarios/question/filter.cy.spec.js
index 4d18cf26b44..81a3a28c330 100644
--- a/frontend/test/metabase/scenarios/question/filter.cy.spec.js
+++ b/frontend/test/metabase/scenarios/question/filter.cy.spec.js
@@ -218,7 +218,7 @@ describe("scenarios > question > filter", () => {
     cy.findByText("Add filter").click();
     cy.contains("Category is not Gizmo");
 
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
     // wait for results to load
     cy.get(".LoadingSpinner").should("not.exist");
     cy.log("The point of failure in 0.37.0-rc3");
@@ -543,7 +543,7 @@ describe("scenarios > question > filter", () => {
       .click()
       .type("contains(");
     cy.findByText(/Checks to see if string1 contains string2 within it./i);
-    cy.findByRole("button", { name: "Done" }).should("not.be.disabled");
+    cy.button("Done").should("not.be.disabled");
     cy.get(".text-error").should("not.exist");
     cy.findAllByText(/Expected one of these possible Token sequences:/i).should(
       "not.exist",
@@ -701,8 +701,7 @@ describe("scenarios > question > filter", () => {
       .click()
       .clear()
       .type("NOT IsNull([Rating])", { delay: 50 });
-    cy.findAllByRole("button")
-      .contains("Done")
+    cy.button("Done")
       .should("not.be.disabled")
       .click();
 
@@ -713,13 +712,12 @@ describe("scenarios > question > filter", () => {
       .click()
       .clear()
       .type("NOT IsEmpty([Reviewer])", { delay: 50 });
-    cy.findAllByRole("button")
-      .contains("Done")
+    cy.button("Done")
       .should("not.be.disabled")
       .click();
 
     // check that filter is applied and rows displayed
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
     cy.contains("Showing 1,112 rows");
   });
 
@@ -848,7 +846,7 @@ describe("scenarios > question > filter", () => {
     cy.get("[contenteditable='true']").contains(
       'contains([Reviewer], "MULLER")',
     );
-    cy.findByRole("button", { name: "Done" }).click();
+    cy.button("Done").click();
     cy.wait("@dataset.2").then(xhr => {
       expect(xhr.response.body.data.rows).to.have.lengthOf(1);
     });
@@ -863,7 +861,7 @@ describe("scenarios > question > filter", () => {
     cy.get("[contenteditable='true']")
       .click()
       .type("3.14159");
-    cy.findAllByRole("button", { name: "Done" })
+    cy.button("Done")
       .should("not.be.disabled")
       .click();
     cy.findByText("Expecting boolean but found 3.14159");
@@ -877,7 +875,7 @@ describe("scenarios > question > filter", () => {
     cy.get("[contenteditable='true']")
       .click()
       .type('"TheAnswer"');
-    cy.findAllByRole("button", { name: "Done" })
+    cy.button("Done")
       .should("not.be.disabled")
       .click();
     cy.findByText('Expecting boolean but found "TheAnswer"');
@@ -902,7 +900,7 @@ describe("scenarios > question > filter", () => {
       .click();
     cy.findByText("Filter by this column").click();
     cy.findByPlaceholderText("Enter a number").type("42");
-    cy.findByRole("button", { name: "Update filter" })
+    cy.button("Update filter")
       .should("not.be.disabled")
       .click();
     cy.findByText("Doohickey");
diff --git a/frontend/test/metabase/scenarios/question/joins.cy.spec.js b/frontend/test/metabase/scenarios/question/joins.cy.spec.js
index 103bf53c921..fec04792aa9 100644
--- a/frontend/test/metabase/scenarios/question/joins.cy.spec.js
+++ b/frontend/test/metabase/scenarios/question/joins.cy.spec.js
@@ -35,7 +35,7 @@ describe("scenarios > question > joined questions", () => {
     popover()
       .findByText("Product ID") // Implicit assertion - test will fail for multiple strings
       .click();
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
     cy.wait("@dataset").then(xhr => {
       expect(xhr.response.body.error).not.to.exist;
     });
diff --git a/frontend/test/metabase/scenarios/question/nested.cy.spec.js b/frontend/test/metabase/scenarios/question/nested.cy.spec.js
index fc5fb453629..5b1aefae848 100644
--- a/frontend/test/metabase/scenarios/question/nested.cy.spec.js
+++ b/frontend/test/metabase/scenarios/question/nested.cy.spec.js
@@ -559,7 +559,7 @@ describe("scenarios > question > nested", () => {
       cy.findByText("Pick a column to group by").click();
       cy.findByText("CAT").click();
 
-      cy.findByText("Visualize").click();
+      cy.button("Visualize").click();
       cy.get("@consoleWarn").should(
         "not.be.calledWith",
         "Removing invalid MBQL clause",
@@ -571,7 +571,7 @@ describe("scenarios > question > nested", () => {
       cy.findByText("Pick a column to group by").click();
       cy.findByText("CAT").click();
 
-      cy.findByText("Visualize").click();
+      cy.button("Visualize").click();
       cy.wait("@dataset");
       cy.findAllByRole("button")
         .contains("Summarize")
diff --git a/frontend/test/metabase/scenarios/question/new.cy.spec.js b/frontend/test/metabase/scenarios/question/new.cy.spec.js
index c2581fd51ba..fe9437fbc02 100644
--- a/frontend/test/metabase/scenarios/question/new.cy.spec.js
+++ b/frontend/test/metabase/scenarios/question/new.cy.spec.js
@@ -71,7 +71,7 @@ describe("scenarios > question > new", () => {
         .click();
       cy.findByText("Rating").click();
     });
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
     cy.get(".Visualization .bar").should("have.length", 6);
   });
 
@@ -322,7 +322,7 @@ describe("scenarios > question > new", () => {
         cy.findByPlaceholderText("Name (required)").type("twice max total");
         cy.findByText("Done").click();
       });
-      cy.findByText("Visualize").click();
+      cy.button("Visualize").click();
       cy.findByText("318.7");
     });
 
diff --git a/frontend/test/metabase/scenarios/question/notebook.cy.spec.js b/frontend/test/metabase/scenarios/question/notebook.cy.spec.js
index f894df51107..1d58a606895 100644
--- a/frontend/test/metabase/scenarios/question/notebook.cy.spec.js
+++ b/frontend/test/metabase/scenarios/question/notebook.cy.spec.js
@@ -37,7 +37,7 @@ describe("scenarios > question > notebook", () => {
     cy.findByText("Not now").click();
     // enter "notebook" and visualize without changing anything
     cy.icon("notebook").click();
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
 
     // there were no changes to the question, so we shouldn't have the option to "Save"
     cy.findByText("Save").should("not.exist");
@@ -99,7 +99,7 @@ describe("scenarios > question > notebook", () => {
       .click()
       .clear()
       .type("contains([Category])", { delay: 50 });
-    cy.findAllByRole("button", { name: "Done" })
+    cy.button("Done")
       .should("not.be.disabled")
       .click();
     cy.contains(/^Function contains expects 2 arguments/i);
@@ -125,7 +125,7 @@ describe("scenarios > question > notebook", () => {
       .click()
       .clear()
       .type("[Price] > 1");
-    cy.findAllByRole("button", { name: "Done" }).click();
+    cy.button("Done").click();
 
     // change the corresponding custom expression
     cy.findByText("Price is greater than 1").click();
@@ -204,7 +204,7 @@ describe("scenarios > question > notebook", () => {
           }
         });
       cy.findByText("wolf.dina@yahoo.com").click();
-      cy.findByRole("button", { name: "Add filter" }).click();
+      cy.button("Add filter").click();
       cy.contains("Showing 1 row");
     });
 
@@ -231,7 +231,7 @@ describe("scenarios > question > notebook", () => {
       popover().within(() => cy.findByText("A_COLUMN").click());
       popover().within(() => cy.findByText("B_COLUMN").click());
 
-      cy.findByText("Visualize").click();
+      cy.button("Visualize").click();
       cy.queryByText("Visualize").then($el => cy.wrap($el).should("not.exist")); // wait for that screen to disappear to avoid "multiple elements" errors
 
       // check that query worked
@@ -265,13 +265,12 @@ describe("scenarios > question > notebook", () => {
           .click()
           .type("Sum Divide");
 
-        cy.findAllByRole("button")
-          .contains("Done")
+        cy.button("Done")
           .should("not.be.disabled")
           .click();
       });
       cy.route("POST", "/api/dataset").as("visualization");
-      cy.findByText("Visualize").click();
+      cy.button("Visualize").click();
 
       cy.wait("@visualization").then(xhr => {
         expect(xhr.response.body.error).not.to.exist;
@@ -390,7 +389,7 @@ describe("scenarios > question > notebook", () => {
       popover()
         .contains(/Products? → Category/)
         .click();
-      cy.findByText("Visualize").click();
+      cy.button("Visualize").click();
 
       cy.findByText("12928_Q1 + 12928_Q2");
       cy.log("Reported failing in v1.35.4.1 and `master` on July, 16 2020");
@@ -661,7 +660,7 @@ describe("scenarios > question > notebook", () => {
         .click();
       cy.findByText("Greater than").click();
       cy.findByPlaceholderText("Enter a number").type(0);
-      cy.findByRole("button", { name: "Add filter" })
+      cy.button("Add filter")
         .should("not.be.disabled")
         .click();
     });
@@ -736,7 +735,7 @@ describe("scenarios > question > notebook", () => {
         cy.findByText("Add filter").click();
       });
 
-      cy.findByText("Visualize").click();
+      cy.button("Visualize").click();
       cy.findByText("Gadget").should("exist");
       cy.findByText("Gizmo").should("not.exist");
 
@@ -773,11 +772,11 @@ describe("scenarios > question > notebook", () => {
         .click()
         .type("Example", { delay: 100 });
 
-      cy.findAllByRole("button", { name: "Done" })
+      cy.button("Done")
         .should("not.be.disabled")
         .click();
 
-      cy.findAllByRole("button", { name: "Visualize" }).click();
+      cy.button("Visualize").click();
       cy.contains("Example");
       cy.contains("Big");
       cy.contains("Small");
@@ -794,11 +793,11 @@ describe("scenarios > question > notebook", () => {
 
       cy.contains(/^redundant input/i).should("not.exist");
 
-      cy.findAllByRole("button", { name: "Done" })
+      cy.button("Done")
         .should("not.be.disabled")
         .click();
 
-      cy.findAllByRole("button", { name: "Visualize" }).click();
+      cy.button("Visualize").click();
       cy.contains("Showing 97 rows");
     });
 
@@ -825,11 +824,11 @@ describe("scenarios > question > notebook", () => {
         cy.contains(/^expected closing parenthesis/i).should("not.exist");
         cy.contains(/^redundant input/i).should("not.exist");
 
-        cy.findAllByRole("button", { name: "Done" })
+        cy.button("Done")
           .should("not.be.disabled")
           .click();
 
-        cy.findAllByRole("button", { name: "Visualize" }).click();
+        cy.button("Visualize").click();
         cy.contains(filter);
         cy.contains(result);
       });
@@ -881,7 +880,7 @@ describe("scenarios > question > notebook", () => {
         .click()
         .clear()
         .type('[Category] = "widget', { delay: 50 });
-      cy.findAllByRole("button", { name: "Done" })
+      cy.button("Done")
         .should("not.be.disabled")
         .click();
       cy.findByText("Missing closing quotes");
@@ -1038,7 +1037,7 @@ function joinTwoSavedQuestions(ALIAS = "Joined Question") {
         cy.log("Reported in v0.36.0");
         cy.icon("notebook").click();
         cy.url().should("contain", "/notebook");
-        cy.findByText("Visualize").should("exist");
+        cy.button("Visualize").should("exist");
       });
     });
   });
diff --git a/frontend/test/metabase/scenarios/question/saved.cy.spec.js b/frontend/test/metabase/scenarios/question/saved.cy.spec.js
index ef63c93b64a..a0fcfebe927 100644
--- a/frontend/test/metabase/scenarios/question/saved.cy.spec.js
+++ b/frontend/test/metabase/scenarios/question/saved.cy.spec.js
@@ -38,7 +38,7 @@ describe("scenarios > question > saved", () => {
 
     modal().within(() => {
       cy.findByText("Save question");
-      cy.findByRole("button", { name: /save/i }).as("saveButton");
+      cy.button("Save").as("saveButton");
       cy.get("@saveButton").should("not.be.disabled");
 
       cy.log(
diff --git a/frontend/test/metabase/scenarios/sharing/alert/alert.cy.spec.js b/frontend/test/metabase/scenarios/sharing/alert/alert.cy.spec.js
index dde401f5860..693b9aa4306 100644
--- a/frontend/test/metabase/scenarios/sharing/alert/alert.cy.spec.js
+++ b/frontend/test/metabase/scenarios/sharing/alert/alert.cy.spec.js
@@ -175,7 +175,7 @@ describe("scenarios > alert", () => {
         cy.findByPlaceholderText("Find...").type("Cr");
         cy.findByText("Created At").click();
       });
-      cy.findByText("Visualize").click();
+      cy.button("Visualize").click();
 
       // Set a goal
       setGoal("35");
diff --git a/frontend/test/metabase/scenarios/sharing/subscriptions.cy.spec.js b/frontend/test/metabase/scenarios/sharing/subscriptions.cy.spec.js
index b93de97b71d..4e884da0238 100644
--- a/frontend/test/metabase/scenarios/sharing/subscriptions.cy.spec.js
+++ b/frontend/test/metabase/scenarios/sharing/subscriptions.cy.spec.js
@@ -26,24 +26,6 @@ describe("scenarios > dashboard > subscriptions", () => {
       .should("have.class", "cursor-default");
   });
 
-  it.skip("should allow sharing if dashboard contains only text cards (metabase#15077)", () => {
-    cy.createDashboard("15077D").then(({ body: { id: DASHBOARD_ID } }) => {
-      cy.visit(`/dashboard/${DASHBOARD_ID}`);
-    });
-    cy.icon("pencil").click();
-    cy.icon("string").click();
-    cy.findByPlaceholderText("Write here, and use Markdown if you'd like")
-      .click()
-      .type("Foo");
-    cy.findByRole("button", { name: "Save" }).click();
-    cy.findByText("You're editing this dashboard.").should("not.exist");
-    cy.icon("share")
-      .closest("a")
-      .should("have.class", "cursor-pointer")
-      .click();
-    cy.findByText("Dashboard subscriptions").click();
-  });
-
   describe("with no channels set up", () => {
     it("should instruct user to connect email or slack", () => {
       openDashboardSubscriptions();
@@ -196,6 +178,28 @@ describe("scenarios > dashboard > subscriptions", () => {
         );
       });
     });
+
+    it.skip("should include text cards (metabase#15744)", () => {
+      const TEXT_CARD = "FooBar";
+
+      cy.visit("/dashboard/1");
+      cy.icon("pencil").click();
+      cy.icon("string").click();
+      cy.findByPlaceholderText(
+        "Write here, and use Markdown if you'd like",
+      ).type(TEXT_CARD);
+      cy.button("Save").click();
+      cy.findByText("You're editing this dashboard.").should("not.exist");
+      assignRecipient();
+      // Click outside popover to close it and at the same time check that the text card content is shown as expected
+      cy.findByText(TEXT_CARD).click();
+      cy.findByText("Send email now").click();
+      cy.findByText(/^Sending/);
+      cy.findByText("Email sent");
+      cy.request("GET", "http://localhost:80/email").then(({ body }) => {
+        expect(body[0].html).to.include(TEXT_CARD);
+      });
+    });
   });
 
   describe("with Slack set up", () => {
diff --git a/frontend/test/metabase/scenarios/visualizations/drillthroughs/chart_drill.cy.spec.js b/frontend/test/metabase/scenarios/visualizations/drillthroughs/chart_drill.cy.spec.js
index 2f3f688bf5b..91c9ac46283 100644
--- a/frontend/test/metabase/scenarios/visualizations/drillthroughs/chart_drill.cy.spec.js
+++ b/frontend/test/metabase/scenarios/visualizations/drillthroughs/chart_drill.cy.spec.js
@@ -240,7 +240,7 @@ describe("scenarios > visualizations > drillthroughs > chart drill", () => {
     cy.findByText("Add filter").click();
 
     // Visualize: line
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
     cy.findByText("Visualization").click();
     cy.icon("line").click();
     cy.findByText("Done").click();
diff --git a/frontend/test/metabase/scenarios/visualizations/trendline.cy.spec.js b/frontend/test/metabase/scenarios/visualizations/trendline.cy.spec.js
index 54a937a4f61..b8cdc14eeae 100644
--- a/frontend/test/metabase/scenarios/visualizations/trendline.cy.spec.js
+++ b/frontend/test/metabase/scenarios/visualizations/trendline.cy.spec.js
@@ -26,10 +26,10 @@ describe("scenarios > question > trendline", () => {
     cy.findByText("by month").click();
     cy.findByText("Year").click();
 
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
 
     // Check graph is there
-    cy.findByText("Visualize").should("not.exist");
+    cy.button("Visualize").should("not.exist");
     cy.findByText("Visualization");
     cy.get("rect");
 
diff --git a/frontend/test/metabase/scenarios/visualizations/waterfall.cy.spec.js b/frontend/test/metabase/scenarios/visualizations/waterfall.cy.spec.js
index 2bccfdbb33e..ef55ac856f5 100644
--- a/frontend/test/metabase/scenarios/visualizations/waterfall.cy.spec.js
+++ b/frontend/test/metabase/scenarios/visualizations/waterfall.cy.spec.js
@@ -84,9 +84,9 @@ describe("scenarios > visualizations > waterfall", () => {
     cy.get("[contenteditable=true]")
       .type("between([Created At], '2016-01-01', '2016-08-01')")
       .blur();
-    cy.findByRole("button", { name: "Done" }).click();
+    cy.button("Done").click();
 
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
     cy.contains("Visualization").click();
     cy.icon("waterfall").click();
 
@@ -100,7 +100,7 @@ describe("scenarios > visualizations > waterfall", () => {
     cy.findByText("Pick a column to group by").click();
     cy.findByText("Created At").click();
 
-    cy.findByText("Visualize").click();
+    cy.button("Visualize").click();
     cy.contains("Visualization").click();
     cy.icon("waterfall").click();
 
-- 
GitLab