From d53b0a7a80568e12d3d139a736f79b92b223386b Mon Sep 17 00:00:00 2001 From: Ryan Laurie <30528226+iethree@users.noreply.github.com> Date: Tue, 4 Jun 2024 08:06:49 -0600 Subject: [PATCH] Consolidate sharing e2e specs (#43514) --- .../sharing/approved-domains.cy.spec.js | 74 -- .../10803-timestamp-formatting.cy.spec.js | 65 -- ...19-temporal-units-not-formatted.cy.spec.js | 62 -- ...-syntax-missing-renamed-columns.cy.spec.js | 106 -- ...-remapped-display-value-dropped.cy.spec.js | 67 -- ...573-remapped-fields-not-renamed.cy.spec.js | 54 - .../18729-date-formatting-x-of-y.cy.spec.js | 56 -- ...ative-query-export-column-order.cy.spec.js | 91 -- .../28834-modified-native-question.cy.spec.js | 47 - .../sharing-download-reproductions.cy.spec.js | 512 ++++++++++ .../sharing/public-sharing.cy.spec.js | 71 ++ .../sharing/reproductions.cy.spec.js | 279 ------ ...tes-subscription-receives-error.cy.spec.js | 45 - ...on-shows-original-question-name.cy.spec.js | 61 -- ...2-subscription-int64-value-card.cy.spec.js | 59 -- ...8669-test-email-with-parameters.cy.spec.js | 85 -- ...ard-nested-card-with-parameters.cy.spec.js | 67 -- ...subscription-bar-sent-as-scalar.cy.spec.js | 79 -- ...dates-after-changing-parameters.cy.spec.js | 68 -- ...ilter-defaults-on-subscriptions.cy.spec.js | 79 -- ...d-text-filter-asking-for-number.cy.spec.js | 116 --- ...6988-embedding-dynamic-settings.cy.spec.js | 67 -- ...314-new-subscription-form-state.cy.spec.js | 38 - .../sharing/sharing-reproductions.cy.spec.js | 946 ++++++++++++++++++ 24 files changed, 1529 insertions(+), 1665 deletions(-) delete mode 100644 e2e/test/scenarios/sharing/approved-domains.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/downloads/reproductions/10803-timestamp-formatting.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/downloads/reproductions/18219-temporal-units-not-formatted.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/downloads/reproductions/18382-old-syntax-missing-renamed-columns.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/downloads/reproductions/18440-remapped-display-value-dropped.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/downloads/reproductions/18573-remapped-fields-not-renamed.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/downloads/reproductions/18729-date-formatting-x-of-y.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/downloads/reproductions/19889-native-query-export-column-order.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/downloads/reproductions/28834-modified-native-question.cy.spec.js create mode 100644 e2e/test/scenarios/sharing/downloads/sharing-download-reproductions.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/reproductions.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/reproductions/18009-nodata-creates-subscription-receives-error.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/reproductions/18344-subscription-shows-original-question-name.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/reproductions/18352-subscription-int64-value-card.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/reproductions/18669-test-email-with-parameters.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/reproductions/20393-public-dashboard-nested-card-with-parameters.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/reproductions/21559-subscription-bar-sent-as-scalar.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/reproductions/22524-public-dashboard-updates-after-changing-parameters.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/reproductions/24223-remove-filter-defaults-on-subscriptions.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/reproductions/25473-dashboard-text-filter-asking-for-number.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/reproductions/26988-embedding-dynamic-settings.cy.spec.js delete mode 100644 e2e/test/scenarios/sharing/reproductions/30314-new-subscription-form-state.cy.spec.js create mode 100644 e2e/test/scenarios/sharing/sharing-reproductions.cy.spec.js diff --git a/e2e/test/scenarios/sharing/approved-domains.cy.spec.js b/e2e/test/scenarios/sharing/approved-domains.cy.spec.js deleted file mode 100644 index b3a07f6afc4..00000000000 --- a/e2e/test/scenarios/sharing/approved-domains.cy.spec.js +++ /dev/null @@ -1,74 +0,0 @@ -import { - ORDERS_QUESTION_ID, - ORDERS_DASHBOARD_ID, -} from "e2e/support/cypress_sample_instance_data"; -import { - describeEE, - restore, - setupSMTP, - sidebar, - visitQuestion, - visitDashboard, - setTokenFeatures, -} from "e2e/support/helpers"; - -const allowedDomain = "metabase.test"; -const deniedDomain = "metabase.example"; -const deniedEmail = `mailer@${deniedDomain}`; -const subscriptionError = `You're only allowed to email subscriptions to addresses ending in ${allowedDomain}`; -const alertError = `You're only allowed to email alerts to addresses ending in ${allowedDomain}`; - -describeEE( - "scenarios > sharing > approved domains (EE)", - { tags: "@external" }, - () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - setTokenFeatures("all"); - setupSMTP(); - setAllowedDomains(); - }); - - it("should validate approved email domains for a question alert", () => { - visitQuestion(ORDERS_QUESTION_ID); - - cy.icon("bell").click(); - cy.button("Set up an alert").click(); - - cy.findByRole("heading", { name: "Email" }) - .closest("li") - .within(() => { - addEmailRecipient(deniedEmail); - cy.findByText(alertError); - }); - cy.button("Done").should("be.disabled"); - }); - - it("should validate approved email domains for a dashboard subscription (metabase#17977)", () => { - visitDashboard(ORDERS_DASHBOARD_ID); - cy.icon("subscription").click(); - - cy.findByRole("heading", { name: "Email it" }).click(); - - sidebar().within(() => { - addEmailRecipient(deniedEmail); - - // Reproduces metabase#17977 - cy.button("Send email now").should("be.disabled"); - cy.button("Done").should("be.disabled"); - cy.findByText(subscriptionError); - }); - }); - }, -); - -function addEmailRecipient(email) { - cy.findByRole("textbox").click().type(`${email}`).blur(); -} - -function setAllowedDomains() { - cy.request("PUT", "/api/setting/subscription-allowed-domains", { - value: allowedDomain, - }); -} diff --git a/e2e/test/scenarios/sharing/downloads/reproductions/10803-timestamp-formatting.cy.spec.js b/e2e/test/scenarios/sharing/downloads/reproductions/10803-timestamp-formatting.cy.spec.js deleted file mode 100644 index 703fed775be..00000000000 --- a/e2e/test/scenarios/sharing/downloads/reproductions/10803-timestamp-formatting.cy.spec.js +++ /dev/null @@ -1,65 +0,0 @@ -import { - restore, - downloadAndAssert, - runNativeQuery, -} from "e2e/support/helpers"; - -const testCases = ["csv", "xlsx"]; - -describe("issue 10803", () => { - beforeEach(() => { - cy.intercept("POST", "/api/dataset").as("dataset"); - - restore(); - cy.signInAsAdmin(); - - cy.createNativeQuestion( - { - name: "10803", - native: { - query: - "SELECT cast(parsedatetime('2026-06-03', 'yyyy-MM-dd') AS timestamp) AS \"birth_date\", cast(parsedatetime('2026-06-03 23:41:23', 'yyyy-MM-dd HH:mm:ss') AS timestamp) AS \"created_at\"", - }, - }, - { visitQuestion: true, wrapId: true }, - ); - }); - - testCases.forEach(fileType => { - it(`should format the date properly for ${fileType} in saved questions (metabase#10803)`, () => { - cy.get("@questionId").then(questionId => { - downloadAndAssert( - { fileType, questionId, logResults: true, raw: true }, - testWorkbookDatetimes, - ); - }); - }); - - it(`should format the date properly for ${fileType} in unsaved questions`, () => { - // Add a space at the end of the query to make it "dirty" - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.contains(/open editor/i).click(); - cy.get(".ace_editor").type("{movetoend} "); - - runNativeQuery(); - downloadAndAssert({ fileType, raw: true }, testWorkbookDatetimes); - }); - - function testWorkbookDatetimes(sheet) { - expect(sheet["A1"].v).to.eq("birth_date"); - expect(sheet["B1"].v).to.eq("created_at"); - - // Excel and CSV will have different formats - if (fileType === "csv") { - expect(sheet["A2"].v).to.eq("June 3, 2026, 12:00 AM"); - expect(sheet["B2"].v).to.eq("June 3, 2026, 11:41 PM"); - } else if (fileType === "xlsx") { - // We tell the xlsx library to read raw and not parse dates - // So for the _date_ format we expect an integer - // And for timestamp, we expect a float - expect(sheet["A2"].v).to.eq(46176); - expect(sheet["B2"].v).to.eq(46176.98707175926); - } - } - }); -}); diff --git a/e2e/test/scenarios/sharing/downloads/reproductions/18219-temporal-units-not-formatted.cy.spec.js b/e2e/test/scenarios/sharing/downloads/reproductions/18219-temporal-units-not-formatted.cy.spec.js deleted file mode 100644 index 6ed820662c5..00000000000 --- a/e2e/test/scenarios/sharing/downloads/reproductions/18219-temporal-units-not-formatted.cy.spec.js +++ /dev/null @@ -1,62 +0,0 @@ -import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; -import { restore, downloadAndAssert, visitQuestion } from "e2e/support/helpers"; - -const { ORDERS, ORDERS_ID } = SAMPLE_DATABASE; - -const questionDetails = { - name: "18219", - query: { - "source-table": ORDERS_ID, - aggregation: [["count"]], - breakout: [["field", ORDERS.CREATED_AT, { "temporal-unit": "year" }]], - }, -}; - -const testCases = ["csv", "xlsx"]; - -describe.skip("issue 18219", () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - }); - - testCases.forEach(fileType => { - it("should format temporal units on export (metabase#18219)", () => { - cy.createQuestion(questionDetails).then( - ({ body: { id: questionId } }) => { - visitQuestion(questionId); - - cy.findByText("Created At: Year"); - cy.findByText("2022"); - cy.findByText("744"); - - downloadAndAssert({ fileType, questionId, raw: true }, assertion); - }, - ); - }); - - function assertion(sheet) { - expect(sheet["A1"].v).to.eq("Created At: Year"); - - if (fileType === "csv") { - expect(sheet["A2"].v).to.eq("2022"); - } - - if (fileType === "xlsx") { - /** - * Depending on how we end up solving this issue, - * the following assertion on the cell type might not be correct. - * It's very likely we'll format temporal breakouts as strings. - * I.e. we have to take into account Q1, Q2, etc. - */ - // expect(A2.t).to.eq("n"); - - /** - * Because of the excel date format, we cannot assert on the raw value `v`. - * Rather, we have to do it on the parsed value `w`. - */ - expect(sheet["A2"].w).to.eq("2022"); - } - } - }); -}); diff --git a/e2e/test/scenarios/sharing/downloads/reproductions/18382-old-syntax-missing-renamed-columns.cy.spec.js b/e2e/test/scenarios/sharing/downloads/reproductions/18382-old-syntax-missing-renamed-columns.cy.spec.js deleted file mode 100644 index 41769ac3923..00000000000 --- a/e2e/test/scenarios/sharing/downloads/reproductions/18382-old-syntax-missing-renamed-columns.cy.spec.js +++ /dev/null @@ -1,106 +0,0 @@ -import { SAMPLE_DB_ID } from "e2e/support/cypress_data"; -import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; -import { - restore, - visitQuestionAdhoc, - downloadAndAssert, -} from "e2e/support/helpers"; - -const { REVIEWS, REVIEWS_ID, PRODUCTS, PRODUCTS_ID } = SAMPLE_DATABASE; - -/** - * This question might seem a bit overwhelming at the first sight. - * The whole point of this repro was to try to cover as much of the old syntax as possible. - * We want to make sure it still works when loaded into a new(er) Metabase version. - */ - -const questionDetails = { - dataset_query: { - database: SAMPLE_DB_ID, - type: "query", - query: { - "source-table": REVIEWS_ID, - joins: [ - { - fields: [["joined-field", "Products", ["field-id", PRODUCTS.TITLE]]], - "source-table": PRODUCTS_ID, - condition: [ - "=", - ["field-id", REVIEWS.PRODUCT_ID], - ["joined-field", "Products", ["field-id", PRODUCTS.ID]], - ], - alias: "Products", - }, - ], - filter: ["and", ["=", ["field-id", REVIEWS.RATING], 4]], - "order-by": [ - ["asc", ["joined-field", "Products", ["field-id", PRODUCTS.TITLE]]], - ], - fields: [ - ["field-id", REVIEWS.ID], - ["field-id", REVIEWS.REVIEWER], - ], - limit: 5, - }, - }, - display: "table", - visualization_settings: { - column_settings: { - [`["ref",["field",${REVIEWS.ID},null]]`]: { - column_title: "MOD:ID", - }, - [`["ref",["field",${REVIEWS.REVIEWER},null]]`]: { - column_title: "MOD:Reviewer", - }, - [`["ref",["field",${PRODUCTS.TITLE},null]]`]: { - column_title: "MOD:Title", - }, - }, - // Reorder columns - "table.columns": [ - { - name: "TITLE", - fieldRef: ["joined-field", "Products", ["field-id", PRODUCTS.TITLE]], - enabled: true, - }, - { - name: "ID", - fieldRef: ["field-id", REVIEWS.ID], - enabled: true, - }, - { - name: "REVIEWER", - fieldRef: ["field-id", REVIEWS.REVIEWER], - enabled: true, - }, - ], - }, -}; - -const testCases = ["csv", "xlsx"]; - -testCases.forEach(fileType => { - describe("issue 18382", () => { - beforeEach(() => { - // TODO: Please remove this line when issue gets fixed - cy.skipOn(fileType === "csv"); - - restore(); - cy.signInAsAdmin(); - - visitQuestionAdhoc(questionDetails); - }); - - it(`should handle the old syntax in downloads for ${fileType} (metabase#18382)`, () => { - downloadAndAssert({ fileType }, assertion); - }); - }); -}); - -function assertion(sheet) { - expect(sheet["A1"].v).to.eq("MOD:Title"); - expect(sheet["B1"].v).to.eq("MOD:ID"); - expect(sheet["C1"].v).to.eq("MOD:Reviewer"); - - expect(sheet["A2"].v).to.eq("Aerodynamic Concrete Bench"); -} diff --git a/e2e/test/scenarios/sharing/downloads/reproductions/18440-remapped-display-value-dropped.cy.spec.js b/e2e/test/scenarios/sharing/downloads/reproductions/18440-remapped-display-value-dropped.cy.spec.js deleted file mode 100644 index 02d91d85162..00000000000 --- a/e2e/test/scenarios/sharing/downloads/reproductions/18440-remapped-display-value-dropped.cy.spec.js +++ /dev/null @@ -1,67 +0,0 @@ -import { SAMPLE_DB_ID } from "e2e/support/cypress_data"; -import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; -import { - restore, - visitQuestionAdhoc, - downloadAndAssert, - visitQuestion, -} from "e2e/support/helpers"; - -const { ORDERS, ORDERS_ID, PRODUCTS } = SAMPLE_DATABASE; - -const query = { "source-table": ORDERS_ID, limit: 5 }; - -const questionDetails = { - dataset_query: { - type: "query", - query, - database: SAMPLE_DB_ID, - }, -}; - -const testCases = ["csv", "xlsx"]; - -describe("issue 18440", () => { - beforeEach(() => { - cy.intercept("POST", "/api/card").as("saveQuestion"); - - restore(); - cy.signInAsAdmin(); - - // Remap Product ID -> Product Title - cy.request("POST", `/api/field/${ORDERS.PRODUCT_ID}/dimension`, { - name: "Product ID", - type: "external", - human_readable_field_id: PRODUCTS.TITLE, - }); - }); - - testCases.forEach(fileType => { - it(`export should include a column with remapped values for ${fileType} (metabase#18440-1)`, () => { - visitQuestionAdhoc(questionDetails); - - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Product ID"); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Awesome Concrete Shoes"); - - downloadAndAssert({ fileType }, assertion); - }); - - it(`export should include a column with remapped values for ${fileType} for a saved question (metabase#18440-2)`, () => { - cy.createQuestion({ query }).then(({ body: { id } }) => { - visitQuestion(id); - - cy.findByText("Product ID"); - cy.findByText("Awesome Concrete Shoes"); - - downloadAndAssert({ fileType, questionId: id }, assertion); - }); - }); - }); -}); - -function assertion(sheet) { - expect(sheet["C1"].v).to.eq("Product ID"); - expect(sheet["C2"].v).to.eq("Awesome Concrete Shoes"); -} diff --git a/e2e/test/scenarios/sharing/downloads/reproductions/18573-remapped-fields-not-renamed.cy.spec.js b/e2e/test/scenarios/sharing/downloads/reproductions/18573-remapped-fields-not-renamed.cy.spec.js deleted file mode 100644 index 2cd9c99e2bf..00000000000 --- a/e2e/test/scenarios/sharing/downloads/reproductions/18573-remapped-fields-not-renamed.cy.spec.js +++ /dev/null @@ -1,54 +0,0 @@ -import { SAMPLE_DB_ID } from "e2e/support/cypress_data"; -import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; -import { - restore, - visitQuestionAdhoc, - downloadAndAssert, -} from "e2e/support/helpers"; - -const { ORDERS, ORDERS_ID, PRODUCTS } = SAMPLE_DATABASE; - -const questionDetails = { - dataset_query: { - type: "query", - query: { "source-table": ORDERS_ID, limit: 2 }, - database: SAMPLE_DB_ID, - }, - visualization_settings: { - column_settings: { - [`["ref",["field",${ORDERS.PRODUCT_ID},null]]`]: { - column_title: "Foo", - }, - }, - }, -}; - -describe("issue 18573", () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - - // Remap Product ID -> Product Title - cy.request("POST", `/api/field/${ORDERS.PRODUCT_ID}/dimension`, { - name: "Product ID", - type: "external", - human_readable_field_id: PRODUCTS.TITLE, - }); - }); - - it("for the remapped columns, it should preserve renamed column name in exports for xlsx (metabase#18573)", () => { - visitQuestionAdhoc(questionDetails); - - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Foo"); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Awesome Concrete Shoes"); - - downloadAndAssert({ fileType: "xlsx" }, assertion); - }); -}); - -function assertion(sheet) { - expect(sheet["C1"].v).to.eq("Foo"); - expect(sheet["C2"].v).to.eq("Awesome Concrete Shoes"); -} diff --git a/e2e/test/scenarios/sharing/downloads/reproductions/18729-date-formatting-x-of-y.cy.spec.js b/e2e/test/scenarios/sharing/downloads/reproductions/18729-date-formatting-x-of-y.cy.spec.js deleted file mode 100644 index c59b8051609..00000000000 --- a/e2e/test/scenarios/sharing/downloads/reproductions/18729-date-formatting-x-of-y.cy.spec.js +++ /dev/null @@ -1,56 +0,0 @@ -import { SAMPLE_DB_ID } from "e2e/support/cypress_data"; -import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; -import { - restore, - downloadAndAssert, - visitQuestionAdhoc, -} from "e2e/support/helpers"; - -const { ORDERS, ORDERS_ID, PRODUCTS } = SAMPLE_DATABASE; - -const questionDetails = { - dataset_query: { - database: SAMPLE_DB_ID, - query: { - "source-table": ORDERS_ID, - aggregation: [["count"]], - breakout: [ - ["field", ORDERS.CREATED_AT, { "temporal-unit": "month-of-year" }], - ["field", PRODUCTS.CATEGORY, { "source-field": ORDERS.PRODUCT_ID }], - ], - limit: 2, - }, - type: "query", - }, - display: "line", -}; - -describe("issue 18729", () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - }); - - ["csv", "xlsx"].forEach(fileType => { - it(`should properly format the 'X of Y'dates in ${fileType} exports (metabase#18729)`, () => { - visitQuestionAdhoc(questionDetails); - - downloadAndAssert({ fileType }, assertion); - }); - }); -}); - -function assertion(sheet) { - // It currently says only "Created At", but that is already covered in an issue #18219. - - // TODO: When 18219 gets fixed, uncomment the following assertion and delete the `contain` one. - // expect(sheet["A1"].v).to.eq("Created At: Month of year"); - expect(sheet["A1"].v).to.contain("Created At"); - - // Based on how this issue gets resolved, the following assertions might need to change! - - expect(sheet["A2"].v).to.eq(1); - expect(sheet["A2"].t).to.eq("n"); - // Parsed values are always in the form of a string - expect(sheet["A2"].w).to.eq("1"); -} diff --git a/e2e/test/scenarios/sharing/downloads/reproductions/19889-native-query-export-column-order.cy.spec.js b/e2e/test/scenarios/sharing/downloads/reproductions/19889-native-query-export-column-order.cy.spec.js deleted file mode 100644 index d391f4191c8..00000000000 --- a/e2e/test/scenarios/sharing/downloads/reproductions/19889-native-query-export-column-order.cy.spec.js +++ /dev/null @@ -1,91 +0,0 @@ -import { restore, downloadAndAssert, visitQuestion } from "e2e/support/helpers"; - -const questionDetails = { - name: "19889", - native: { - query: 'select 1 "column a", 2 "column b", 3 "column c"', - }, -}; - -const testCases = ["csv", "xlsx"]; - -describe("issue 19889", () => { - beforeEach(() => { - cy.intercept("POST", "/api/dataset").as("dataset"); - - restore(); - cy.signInAsAdmin(); - - cy.createNativeQuestion(questionDetails, { - loadMetadata: true, - wrapId: true, - }); - - // Reorder columns a and b - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("column a").trigger("mousedown", 0, 0).wait(100); //Don't force the first interaction. This ensures things are actually visible to start moving - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("column a") - .trigger("mousemove", 10, 10, { force: true }) - .wait(100); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("column a") - .trigger("mousemove", 100, 0, { force: true }) - .wait(100); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("column a") - .trigger("mouseup", 100, 0, { force: true }) - .wait(100); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Started from").click(); // Give DOM some time to update - }); - - testCases.forEach(fileType => { - it("should order columns correctly in unsaved native query exports", () => { - downloadAndAssert({ fileType, raw: true }, sheet => { - expect(sheet["A1"].v).to.equal("column b"); - expect(sheet["B1"].v).to.equal("column a"); - expect(sheet["C1"].v).to.equal("column c"); - }); - }); - - it("should order columns correctly in saved native query exports", () => { - saveAndOverwrite(); - - cy.get("@questionId").then(questionId => { - downloadAndAssert({ fileType, questionId, raw: true }, sheet => { - expect(sheet["A1"].v).to.equal("column b"); - expect(sheet["B1"].v).to.equal("column a"); - expect(sheet["C1"].v).to.equal("column c"); - }); - }); - }); - - it("should order columns correctly in saved native query exports when the query was modified but not re-run before save (#19889)", () => { - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.contains(/open editor/i).click(); - cy.get(".ace_editor").type( - '{selectall}select 1 "column x", 2 "column y", 3 "column c"', - ); - - saveAndOverwrite(); - - cy.get("@questionId").then(questionId => { - visitQuestion(questionId); - - downloadAndAssert({ fileType, questionId, raw: true }, sheet => { - expect(sheet["A1"].v).to.equal("column x"); - expect(sheet["B1"].v).to.equal("column y"); - expect(sheet["C1"].v).to.equal("column c"); - }); - }); - }); - }); -}); - -function saveAndOverwrite() { - cy.findByText("Save").click(); - cy.findByTestId("save-question-modal").within(modal => { - cy.findByText("Save").click(); - }); -} diff --git a/e2e/test/scenarios/sharing/downloads/reproductions/28834-modified-native-question.cy.spec.js b/e2e/test/scenarios/sharing/downloads/reproductions/28834-modified-native-question.cy.spec.js deleted file mode 100644 index e29322e92cd..00000000000 --- a/e2e/test/scenarios/sharing/downloads/reproductions/28834-modified-native-question.cy.spec.js +++ /dev/null @@ -1,47 +0,0 @@ -import { restore, downloadAndAssert } from "e2e/support/helpers"; - -const questionDetails = { - name: "28834", - native: { - query: 'select 1 "column a"', - }, -}; - -describe("metabase#28834", () => { - // I have a test for saved native questions in `QueryBuilder.unit.spec.tsx`. - // Initially, this test was planned as a unit test, but with some technical - // difficulties, I've decided to test with Cypress instead. - - beforeEach(() => { - cy.intercept("POST", "/api/dataset").as("dataset"); - - restore(); - cy.signInAsAdmin(); - - cy.createNativeQuestion(questionDetails, { - loadMetadata: true, - wrapId: true, - }); - - cy.findByTestId("query-builder-main").findByText("Open Editor").click(); - cy.get(".ace_editor").should("be.visible").type(', select 2 "column b"'); - }); - - it("should be able to export unsaved native query results as CSV even after the query has changed", () => { - const fileType = "csv"; - downloadAndAssert({ fileType, raw: true }, sheet => { - expect(sheet["A1"].v).to.equal("column a"); - expect(sheet["A2"].v).to.equal("1"); - expect(sheet["A3"]).to.be.undefined; - }); - }); - - it("should be able to export unsaved native query results as XLSX even after the query has changed", () => { - const fileType = "xlsx"; - downloadAndAssert({ fileType, raw: true }, sheet => { - expect(sheet["A1"].v).to.equal("column a"); - expect(sheet["A2"].v).to.equal(1); - expect(sheet["A3"]).to.be.undefined; - }); - }); -}); diff --git a/e2e/test/scenarios/sharing/downloads/sharing-download-reproductions.cy.spec.js b/e2e/test/scenarios/sharing/downloads/sharing-download-reproductions.cy.spec.js new file mode 100644 index 00000000000..f2e4082caf7 --- /dev/null +++ b/e2e/test/scenarios/sharing/downloads/sharing-download-reproductions.cy.spec.js @@ -0,0 +1,512 @@ +import { SAMPLE_DB_ID } from "e2e/support/cypress_data"; +import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; +import { + restore, + downloadAndAssert, + runNativeQuery, + visitQuestion, + visitQuestionAdhoc, +} from "e2e/support/helpers"; + +const { ORDERS, ORDERS_ID, REVIEWS, REVIEWS_ID, PRODUCTS, PRODUCTS_ID } = + SAMPLE_DATABASE; + +describe("issue 10803", () => { + const testCases = ["csv", "xlsx"]; + + beforeEach(() => { + cy.intercept("POST", "/api/dataset").as("dataset"); + + restore(); + cy.signInAsAdmin(); + + cy.createNativeQuestion( + { + name: "10803", + native: { + query: + "SELECT cast(parsedatetime('2026-06-03', 'yyyy-MM-dd') AS timestamp) AS \"birth_date\", cast(parsedatetime('2026-06-03 23:41:23', 'yyyy-MM-dd HH:mm:ss') AS timestamp) AS \"created_at\"", + }, + }, + { visitQuestion: true, wrapId: true }, + ); + }); + + testCases.forEach(fileType => { + it(`should format the date properly for ${fileType} in saved questions (metabase#10803)`, () => { + cy.get("@questionId").then(questionId => { + downloadAndAssert( + { fileType, questionId, logResults: true, raw: true }, + testWorkbookDatetimes, + ); + }); + }); + + it(`should format the date properly for ${fileType} in unsaved questions`, () => { + // Add a space at the end of the query to make it "dirty" + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.contains(/open editor/i).click(); + cy.get(".ace_editor").type("{movetoend} "); + + runNativeQuery(); + downloadAndAssert({ fileType, raw: true }, testWorkbookDatetimes); + }); + + function testWorkbookDatetimes(sheet) { + expect(sheet["A1"].v).to.eq("birth_date"); + expect(sheet["B1"].v).to.eq("created_at"); + + // Excel and CSV will have different formats + if (fileType === "csv") { + expect(sheet["A2"].v).to.eq("June 3, 2026, 12:00 AM"); + expect(sheet["B2"].v).to.eq("June 3, 2026, 11:41 PM"); + } else if (fileType === "xlsx") { + // We tell the xlsx library to read raw and not parse dates + // So for the _date_ format we expect an integer + // And for timestamp, we expect a float + expect(sheet["A2"].v).to.eq(46176); + expect(sheet["B2"].v).to.eq(46176.98707175926); + } + } + }); +}); + +describe.skip("issue 18219", () => { + const questionDetails = { + name: "18219", + query: { + "source-table": ORDERS_ID, + aggregation: [["count"]], + breakout: [["field", ORDERS.CREATED_AT, { "temporal-unit": "year" }]], + }, + }; + + const testCases = ["csv", "xlsx"]; + + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + }); + + testCases.forEach(fileType => { + it("should format temporal units on export (metabase#18219)", () => { + cy.createQuestion(questionDetails).then( + ({ body: { id: questionId } }) => { + visitQuestion(questionId); + + cy.findByText("Created At: Year"); + cy.findByText("2022"); + cy.findByText("744"); + + downloadAndAssert({ fileType, questionId, raw: true }, assertion); + }, + ); + }); + + function assertion(sheet) { + expect(sheet["A1"].v).to.eq("Created At: Year"); + + if (fileType === "csv") { + expect(sheet["A2"].v).to.eq("2022"); + } + + if (fileType === "xlsx") { + /** + * Depending on how we end up solving this issue, + * the following assertion on the cell type might not be correct. + * It's very likely we'll format temporal breakouts as strings. + * I.e. we have to take into account Q1, Q2, etc. + */ + // expect(A2.t).to.eq("n"); + + /** + * Because of the excel date format, we cannot assert on the raw value `v`. + * Rather, we have to do it on the parsed value `w`. + */ + expect(sheet["A2"].w).to.eq("2022"); + } + } + }); +}); + +describe("issue 18382", () => { + /** + * This question might seem a bit overwhelming at the first sight. + * The whole point of this repro was to try to cover as much of the old syntax as possible. + * We want to make sure it still works when loaded into a new(er) Metabase version. + */ + + function assertion(sheet) { + expect(sheet["A1"].v).to.eq("MOD:Title"); + expect(sheet["B1"].v).to.eq("MOD:ID"); + expect(sheet["C1"].v).to.eq("MOD:Reviewer"); + + expect(sheet["A2"].v).to.eq("Aerodynamic Concrete Bench"); + } + + const questionDetails = { + dataset_query: { + database: SAMPLE_DB_ID, + type: "query", + query: { + "source-table": REVIEWS_ID, + joins: [ + { + fields: [ + ["joined-field", "Products", ["field-id", PRODUCTS.TITLE]], + ], + "source-table": PRODUCTS_ID, + condition: [ + "=", + ["field-id", REVIEWS.PRODUCT_ID], + ["joined-field", "Products", ["field-id", PRODUCTS.ID]], + ], + alias: "Products", + }, + ], + filter: ["and", ["=", ["field-id", REVIEWS.RATING], 4]], + "order-by": [ + ["asc", ["joined-field", "Products", ["field-id", PRODUCTS.TITLE]]], + ], + fields: [ + ["field-id", REVIEWS.ID], + ["field-id", REVIEWS.REVIEWER], + ], + limit: 5, + }, + }, + display: "table", + visualization_settings: { + column_settings: { + [`["ref",["field",${REVIEWS.ID},null]]`]: { + column_title: "MOD:ID", + }, + [`["ref",["field",${REVIEWS.REVIEWER},null]]`]: { + column_title: "MOD:Reviewer", + }, + [`["ref",["field",${PRODUCTS.TITLE},null]]`]: { + column_title: "MOD:Title", + }, + }, + // Reorder columns + "table.columns": [ + { + name: "TITLE", + fieldRef: ["joined-field", "Products", ["field-id", PRODUCTS.TITLE]], + enabled: true, + }, + { + name: "ID", + fieldRef: ["field-id", REVIEWS.ID], + enabled: true, + }, + { + name: "REVIEWER", + fieldRef: ["field-id", REVIEWS.REVIEWER], + enabled: true, + }, + ], + }, + }; + + const testCases = ["csv", "xlsx"]; + + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + visitQuestionAdhoc(questionDetails); + }); + + testCases.forEach(fileType => { + it(`should handle the old syntax in downloads for ${fileType} (metabase#18382)`, () => { + // TODO: Please remove this line when issue gets fixed + cy.skipOn(fileType === "csv"); + + downloadAndAssert({ fileType }, assertion); + }); + }); +}); + +describe("issue 18440", () => { + const query = { "source-table": ORDERS_ID, limit: 5 }; + + const questionDetails = { + dataset_query: { + type: "query", + query, + database: SAMPLE_DB_ID, + }, + }; + + const testCases = ["csv", "xlsx"]; + + function assertion(sheet) { + expect(sheet["C1"].v).to.eq("Product ID"); + expect(sheet["C2"].v).to.eq("Awesome Concrete Shoes"); + } + + beforeEach(() => { + cy.intercept("POST", "/api/card").as("saveQuestion"); + + restore(); + cy.signInAsAdmin(); + + // Remap Product ID -> Product Title + cy.request("POST", `/api/field/${ORDERS.PRODUCT_ID}/dimension`, { + name: "Product ID", + type: "external", + human_readable_field_id: PRODUCTS.TITLE, + }); + }); + + testCases.forEach(fileType => { + it(`export should include a column with remapped values for ${fileType} (metabase#18440-1)`, () => { + visitQuestionAdhoc(questionDetails); + + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Product ID"); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Awesome Concrete Shoes"); + + downloadAndAssert({ fileType }, assertion); + }); + + it(`export should include a column with remapped values for ${fileType} for a saved question (metabase#18440-2)`, () => { + cy.createQuestion({ query }).then(({ body: { id } }) => { + visitQuestion(id); + + cy.findByText("Product ID"); + cy.findByText("Awesome Concrete Shoes"); + + downloadAndAssert({ fileType, questionId: id }, assertion); + }); + }); + }); +}); + +describe("issue 18573", () => { + const questionDetails = { + dataset_query: { + type: "query", + query: { "source-table": ORDERS_ID, limit: 2 }, + database: SAMPLE_DB_ID, + }, + visualization_settings: { + column_settings: { + [`["ref",["field",${ORDERS.PRODUCT_ID},null]]`]: { + column_title: "Foo", + }, + }, + }, + }; + function assertion(sheet) { + expect(sheet["C1"].v).to.eq("Foo"); + expect(sheet["C2"].v).to.eq("Awesome Concrete Shoes"); + } + + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + + // Remap Product ID -> Product Title + cy.request("POST", `/api/field/${ORDERS.PRODUCT_ID}/dimension`, { + name: "Product ID", + type: "external", + human_readable_field_id: PRODUCTS.TITLE, + }); + }); + + it("for the remapped columns, it should preserve renamed column name in exports for xlsx (metabase#18573)", () => { + visitQuestionAdhoc(questionDetails); + + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Foo"); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Awesome Concrete Shoes"); + + downloadAndAssert({ fileType: "xlsx" }, assertion); + }); +}); + +describe("issue 18729", () => { + const questionDetails = { + dataset_query: { + database: SAMPLE_DB_ID, + query: { + "source-table": ORDERS_ID, + aggregation: [["count"]], + breakout: [ + ["field", ORDERS.CREATED_AT, { "temporal-unit": "month-of-year" }], + ["field", PRODUCTS.CATEGORY, { "source-field": ORDERS.PRODUCT_ID }], + ], + limit: 2, + }, + type: "query", + }, + display: "line", + }; + + function assertion(sheet) { + // It currently says only "Created At", but that is already covered in an issue #18219. + + // TODO: When 18219 gets fixed, uncomment the following assertion and delete the `contain` one. + // expect(sheet["A1"].v).to.eq("Created At: Month of year"); + expect(sheet["A1"].v).to.contain("Created At"); + + // Based on how this issue gets resolved, the following assertions might need to change! + + expect(sheet["A2"].v).to.eq(1); + expect(sheet["A2"].t).to.eq("n"); + // Parsed values are always in the form of a string + expect(sheet["A2"].w).to.eq("1"); + } + + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + }); + + ["csv", "xlsx"].forEach(fileType => { + it(`should properly format the 'X of Y'dates in ${fileType} exports (metabase#18729)`, () => { + visitQuestionAdhoc(questionDetails); + + downloadAndAssert({ fileType }, assertion); + }); + }); +}); + +describe("issue 19889", () => { + const questionDetails = { + name: "19889", + native: { + query: 'select 1 "column a", 2 "column b", 3 "column c"', + }, + }; + + const testCases = ["csv", "xlsx"]; + + function saveAndOverwrite() { + cy.findByText("Save").click(); + cy.findByTestId("save-question-modal").within(modal => { + cy.findByText("Save").click(); + }); + } + + beforeEach(() => { + cy.intercept("POST", "/api/dataset").as("dataset"); + + restore(); + cy.signInAsAdmin(); + + cy.createNativeQuestion(questionDetails, { + loadMetadata: true, + wrapId: true, + }); + + // Reorder columns a and b + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("column a").trigger("mousedown", 0, 0).wait(100); //Don't force the first interaction. This ensures things are actually visible to start moving + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("column a") + .trigger("mousemove", 10, 10, { force: true }) + .wait(100); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("column a") + .trigger("mousemove", 100, 0, { force: true }) + .wait(100); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("column a") + .trigger("mouseup", 100, 0, { force: true }) + .wait(100); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Started from").click(); // Give DOM some time to update + }); + + testCases.forEach(fileType => { + it("should order columns correctly in unsaved native query exports", () => { + downloadAndAssert({ fileType, raw: true }, sheet => { + expect(sheet["A1"].v).to.equal("column b"); + expect(sheet["B1"].v).to.equal("column a"); + expect(sheet["C1"].v).to.equal("column c"); + }); + }); + + it("should order columns correctly in saved native query exports", () => { + saveAndOverwrite(); + + cy.get("@questionId").then(questionId => { + downloadAndAssert({ fileType, questionId, raw: true }, sheet => { + expect(sheet["A1"].v).to.equal("column b"); + expect(sheet["B1"].v).to.equal("column a"); + expect(sheet["C1"].v).to.equal("column c"); + }); + }); + }); + + it("should order columns correctly in saved native query exports when the query was modified but not re-run before save (#19889)", () => { + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.contains(/open editor/i).click(); + cy.get(".ace_editor").type( + '{selectall}select 1 "column x", 2 "column y", 3 "column c"', + ); + + saveAndOverwrite(); + + cy.get("@questionId").then(questionId => { + visitQuestion(questionId); + + downloadAndAssert({ fileType, questionId, raw: true }, sheet => { + expect(sheet["A1"].v).to.equal("column x"); + expect(sheet["B1"].v).to.equal("column y"); + expect(sheet["C1"].v).to.equal("column c"); + }); + }); + }); + }); +}); + +describe("metabase#28834", () => { + const questionDetails = { + name: "28834", + native: { + query: 'select 1 "column a"', + }, + }; + + // I have a test for saved native questions in `QueryBuilder.unit.spec.tsx`. + // Initially, this test was planned as a unit test, but with some technical + // difficulties, I've decided to test with Cypress instead. + + beforeEach(() => { + cy.intercept("POST", "/api/dataset").as("dataset"); + + restore(); + cy.signInAsAdmin(); + + cy.createNativeQuestion(questionDetails, { + loadMetadata: true, + wrapId: true, + }); + + cy.findByTestId("query-builder-main").findByText("Open Editor").click(); + cy.get(".ace_editor").should("be.visible").type(', select 2 "column b"'); + }); + + it("should be able to export unsaved native query results as CSV even after the query has changed", () => { + const fileType = "csv"; + downloadAndAssert({ fileType, raw: true }, sheet => { + expect(sheet["A1"].v).to.equal("column a"); + expect(sheet["A2"].v).to.equal("1"); + expect(sheet["A3"]).to.be.undefined; + }); + }); + + it("should be able to export unsaved native query results as XLSX even after the query has changed", () => { + const fileType = "xlsx"; + downloadAndAssert({ fileType, raw: true }, sheet => { + expect(sheet["A1"].v).to.equal("column a"); + expect(sheet["A2"].v).to.equal(1); + expect(sheet["A3"]).to.be.undefined; + }); + }); +}); diff --git a/e2e/test/scenarios/sharing/public-sharing.cy.spec.js b/e2e/test/scenarios/sharing/public-sharing.cy.spec.js index c79e1947ae4..47c5d0077b3 100644 --- a/e2e/test/scenarios/sharing/public-sharing.cy.spec.js +++ b/e2e/test/scenarios/sharing/public-sharing.cy.spec.js @@ -1,11 +1,21 @@ import { SAMPLE_DB_ID } from "e2e/support/cypress_data"; import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; +import { + ORDERS_QUESTION_ID, + ORDERS_DASHBOARD_ID, +} from "e2e/support/cypress_sample_instance_data"; import { restore, modal, setActionsEnabledForDB, createAction, visitDashboardAndCreateTab, + describeEE, + setupSMTP, + sidebar, + visitQuestion, + visitDashboard, + setTokenFeatures, } from "e2e/support/helpers"; const { ORDERS_ID } = SAMPLE_DATABASE; @@ -267,3 +277,64 @@ describe("scenarios > admin > settings > public sharing", () => { ); }); }); + +describeEE( + "scenarios > sharing > approved domains (EE)", + { tags: "@external" }, + () => { + const allowedDomain = "metabase.test"; + const deniedDomain = "metabase.example"; + const deniedEmail = `mailer@${deniedDomain}`; + const subscriptionError = `You're only allowed to email subscriptions to addresses ending in ${allowedDomain}`; + const alertError = `You're only allowed to email alerts to addresses ending in ${allowedDomain}`; + + function addEmailRecipient(email) { + cy.findByRole("textbox").click().type(`${email}`).blur(); + } + + function setAllowedDomains() { + cy.request("PUT", "/api/setting/subscription-allowed-domains", { + value: allowedDomain, + }); + } + + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + setTokenFeatures("all"); + setupSMTP(); + setAllowedDomains(); + }); + + it("should validate approved email domains for a question alert", () => { + visitQuestion(ORDERS_QUESTION_ID); + + cy.icon("bell").click(); + cy.button("Set up an alert").click(); + + cy.findByRole("heading", { name: "Email" }) + .closest("li") + .within(() => { + addEmailRecipient(deniedEmail); + cy.findByText(alertError); + }); + cy.button("Done").should("be.disabled"); + }); + + it("should validate approved email domains for a dashboard subscription (metabase#17977)", () => { + visitDashboard(ORDERS_DASHBOARD_ID); + cy.icon("subscription").click(); + + cy.findByRole("heading", { name: "Email it" }).click(); + + sidebar().within(() => { + addEmailRecipient(deniedEmail); + + // Reproduces metabase#17977 + cy.button("Send email now").should("be.disabled"); + cy.button("Done").should("be.disabled"); + cy.findByText(subscriptionError); + }); + }); + }, +); diff --git a/e2e/test/scenarios/sharing/reproductions.cy.spec.js b/e2e/test/scenarios/sharing/reproductions.cy.spec.js deleted file mode 100644 index 8cce0f99b53..00000000000 --- a/e2e/test/scenarios/sharing/reproductions.cy.spec.js +++ /dev/null @@ -1,279 +0,0 @@ -import { USERS } from "e2e/support/cypress_data"; -import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; -import { - ADMIN_USER_ID, - ORDERS_DASHBOARD_DASHCARD_ID, - ORDERS_DASHBOARD_ID, - ORDERS_QUESTION_ID, -} from "e2e/support/cypress_sample_instance_data"; -import { - restore, - setupSMTP, - visitDashboard, - getFullName, - sidebar, - popover, - visitQuestion, -} from "e2e/support/helpers"; - -const { ORDERS, ORDERS_ID, PEOPLE } = SAMPLE_DATABASE; -const { admin } = USERS; -const { first_name, last_name } = admin; - -describe("issue 17657", () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - - createSubscriptionWithoutRecipients(); - }); - - it("frontend should gracefully handle the case of a subscription without a recipient (metabase#17657)", () => { - visitDashboard(ORDERS_DASHBOARD_ID); - - cy.icon("subscription").click(); - - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText(/^Emailed monthly/).click(); - - sidebar().within(() => { - cy.button("Done").should("be.disabled"); - }); - - // Open the popover with all users - cy.findByPlaceholderText("Enter user names or email addresses").click(); - // Pick admin as a recipient - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText(`${first_name} ${last_name}`).click(); - - sidebar().within(() => { - cy.button("Done").should("not.be.disabled"); - }); - }); -}); - -function createSubscriptionWithoutRecipients() { - cy.request("POST", "/api/pulse", { - name: "Orders in a dashboard", - cards: [ - { - id: ORDERS_QUESTION_ID, - collection_id: null, - description: null, - display: "table", - name: "Orders", - include_csv: false, - include_xls: false, - dashboard_card_id: 1, - dashboard_id: ORDERS_DASHBOARD_ID, - parameter_mappings: [], - }, - ], - channels: [ - { - channel_type: "email", - enabled: true, - // Since the fix (https://github.com/metabase/metabase/pull/17668), this is not even possible to do in the UI anymore. - // Backend still doesn't do this validation so we're making sure the FE handles the case of missing recipients gracefully. - recipients: [], - details: {}, - schedule_type: "monthly", - schedule_day: "mon", - schedule_hour: 8, - schedule_frame: "first", - }, - ], - skip_if_empty: false, - collection_id: null, - parameters: [], - dashboard_id: ORDERS_DASHBOARD_ID, - }); -} - -describe("issue 17658", { tags: "@external" }, () => { - beforeEach(() => { - cy.intercept("PUT", "/api/pulse/*").as("deletePulse"); - restore(); - cy.signInAsAdmin(); - - setupSMTP(); - - moveDashboardToCollection("First collection"); - }); - - it("should delete dashboard subscription from any collection (metabase#17658)", () => { - visitDashboard(ORDERS_DASHBOARD_ID); - - cy.icon("subscription").click(); - - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText(/^Emailed monthly/).click(); - - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Delete this subscription").click(); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText(/^This dashboard will no longer be emailed to/).click(); - - cy.button("Delete").click(); - - cy.wait("@deletePulse").then(({ response }) => { - expect(response.body.cause).not.to.exist; - expect(response.statusCode).not.to.eq(500); - }); - - cy.button("Delete").should("not.exist"); - }); -}); - -function moveDashboardToCollection(collectionName) { - const { first_name, last_name, email } = admin; - - cy.request("GET", "/api/collection/tree?tree=true").then( - ({ body: collections }) => { - const { id } = collections.find( - collection => collection.name === collectionName, - ); - - // Move dashboard - cy.request("PUT", `/api/dashboard/${ORDERS_DASHBOARD_ID}`, { - collection_id: id, - }); - - // Create subscription - cy.request("POST", "/api/pulse", { - name: "Orders in a dashboard", - cards: [ - { - id: ORDERS_QUESTION_ID, - collection_id: null, - description: null, - display: "table", - name: "Orders", - include_csv: false, - include_xls: false, - dashboard_card_id: ORDERS_DASHBOARD_DASHCARD_ID, - dashboard_id: ORDERS_DASHBOARD_ID, - parameter_mappings: [], - }, - ], - channels: [ - { - channel_type: "email", - enabled: true, - recipients: [ - { - id: ADMIN_USER_ID, - email, - first_name, - last_name, - common_name: getFullName(admin), - }, - ], - details: {}, - schedule_type: "monthly", - schedule_day: "mon", - schedule_hour: 8, - schedule_frame: "first", - }, - ], - skip_if_empty: false, - collection_id: id, - parameters: [], - dashboard_id: ORDERS_DASHBOARD_ID, - }); - }, - ); -} - -describe("issue 17547", () => { - const questionDetails = { - query: { - "source-table": ORDERS_ID, - breakout: [ - ["field", ORDERS.CREATED_AT, { "temporal-unit": "month" }], - ["field", PEOPLE.SOURCE, { "source-field": ORDERS.USER_ID }], - ], - aggregation: [["count"]], - }, - display: "area", - }; - - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - - cy.createQuestion(questionDetails).then(({ body: { id: questionId } }) => { - setUpAlert(questionId); - - visitQuestion(questionId); - }); - }); - - it("editing an alert should not delete it (metabase#17547)", () => { - cy.icon("bell").click(); - popover().within(() => { - cy.findByText("Daily, 12:00 PM"); - cy.findByText("Edit").click(); - }); - - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("AM").click(); - cy.button("Save changes").click(); - - cy.wait("@alertQuery"); - - cy.icon("bell").click(); - popover().within(() => { - cy.findByText("Daily, 12:00 AM"); - }); - }); -}); - -function setUpAlert(questionId) { - cy.request("POST", "/api/alert", { - channels: [ - { - schedule_type: "daily", - schedule_hour: 12, - channel_type: "slack", - schedule_frame: null, - recipients: [], - details: { channel: "#work" }, - pulse_id: 1, - id: 1, - schedule_day: null, - enabled: true, - }, - ], - alert_condition: "rows", - name: null, - creator_id: ADMIN_USER_ID, - card: { id: questionId, include_csv: true, include_xls: false }, - alert_first_only: false, - skip_if_empty: true, - parameters: [], - dashboard_id: null, - }).then(({ body: { id: alertId } }) => { - cy.intercept("PUT", `/api/alert/${alertId}`).as("alertQuery"); - }); -} - -describe("issue 16108", () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - }); - - it("should display a tooltip for CTA icons on an individual question (metabase#16108)", () => { - visitQuestion(ORDERS_QUESTION_ID); - cy.icon("download").realHover(); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Download full results"); - cy.icon("bell").realHover(); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Get alerts"); - cy.icon("share").realHover(); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Sharing"); - }); -}); diff --git a/e2e/test/scenarios/sharing/reproductions/18009-nodata-creates-subscription-receives-error.cy.spec.js b/e2e/test/scenarios/sharing/reproductions/18009-nodata-creates-subscription-receives-error.cy.spec.js deleted file mode 100644 index f9be7fc5928..00000000000 --- a/e2e/test/scenarios/sharing/reproductions/18009-nodata-creates-subscription-receives-error.cy.spec.js +++ /dev/null @@ -1,45 +0,0 @@ -import { ORDERS_DASHBOARD_ID } from "e2e/support/cypress_sample_instance_data"; -import { - restore, - popover, - setupSMTP, - visitDashboard, - sendEmailAndAssert, - sidebar, -} from "e2e/support/helpers"; - -describe("issue 18009", { tags: "@external" }, () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - - setupSMTP(); - - cy.signIn("nodata"); - }); - - it("nodata user should be able to create and receive an email subscription without errors (metabase#18009)", () => { - visitDashboard(ORDERS_DASHBOARD_ID); - - cy.findByLabelText("subscriptions").click(); - - sidebar() - .findByPlaceholderText("Enter user names or email addresses") - .click(); - popover() - .contains(/^No Data/) - .click(); - - // Click anywhere to close the popover that covers the "Send email now" button - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("To:").click(); - - sendEmailAndAssert(email => { - expect(email.html).not.to.include( - "An error occurred while displaying this card.", - ); - - expect(email.html).to.include("37.65"); - }); - }); -}); diff --git a/e2e/test/scenarios/sharing/reproductions/18344-subscription-shows-original-question-name.cy.spec.js b/e2e/test/scenarios/sharing/reproductions/18344-subscription-shows-original-question-name.cy.spec.js deleted file mode 100644 index de56a4a07ed..00000000000 --- a/e2e/test/scenarios/sharing/reproductions/18344-subscription-shows-original-question-name.cy.spec.js +++ /dev/null @@ -1,61 +0,0 @@ -import { USERS } from "e2e/support/cypress_data"; -import { ORDERS_DASHBOARD_ID } from "e2e/support/cypress_sample_instance_data"; -import { - restore, - editDashboard, - saveDashboard, - setupSMTP, - visitDashboard, - sendEmailAndAssert, - modal, -} from "e2e/support/helpers"; - -const { - admin: { first_name, last_name }, -} = USERS; - -describe("issue 18344", { tags: "@external" }, () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - - setupSMTP(); - - // Rename the question - visitDashboard(ORDERS_DASHBOARD_ID); - - editDashboard(); - - // Open visualization options - cy.findByTestId("dashcard").realHover(); - cy.icon("palette").click(); - - modal().within(() => { - cy.findByDisplayValue("Orders").type("Foo").blur(); - - cy.button("Done").click(); - }); - - saveDashboard(); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("OrdersFoo"); - }); - - it("subscription should not include original question name when it's been renamed in the dashboard (metabase#18344)", () => { - // Send a test email subscription - cy.icon("subscription").click(); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Email it").click(); - - cy.findByPlaceholderText("Enter user names or email addresses").click(); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText(`${first_name} ${last_name}`).click(); - // Click this just to close the popover that is blocking the "Send email now" button - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("To:").click(); - - sendEmailAndAssert(email => { - expect(email.html).to.include("OrdersFoo"); - }); - }); -}); diff --git a/e2e/test/scenarios/sharing/reproductions/18352-subscription-int64-value-card.cy.spec.js b/e2e/test/scenarios/sharing/reproductions/18352-subscription-int64-value-card.cy.spec.js deleted file mode 100644 index ed8536a60cb..00000000000 --- a/e2e/test/scenarios/sharing/reproductions/18352-subscription-int64-value-card.cy.spec.js +++ /dev/null @@ -1,59 +0,0 @@ -import { USERS } from "e2e/support/cypress_data"; -import { - restore, - setupSMTP, - visitQuestion, - visitDashboard, - sendEmailAndAssert, -} from "e2e/support/helpers"; - -const { - admin: { first_name, last_name }, -} = USERS; - -const questionDetails = { - name: "18352", - native: { - query: "SELECT 'foo', 1 UNION ALL SELECT 'bar', 2", - }, -}; - -describe("issue 18352", { tags: "@external" }, () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - - setupSMTP(); - - cy.createNativeQuestionAndDashboard({ questionDetails }).then( - ({ body: { card_id, dashboard_id } }) => { - visitQuestion(card_id); - - visitDashboard(dashboard_id); - }, - ); - }); - - it("should send the card with the INT64 values (metabase#18352)", () => { - cy.icon("subscription").click(); - - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Email it").click(); - - cy.findByPlaceholderText("Enter user names or email addresses").click(); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText(`${first_name} ${last_name}`).click(); - // Click this just to close the popover that is blocking the "Send email now" button - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("To:").click(); - - sendEmailAndAssert(({ html }) => { - expect(html).not.to.include( - "An error occurred while displaying this card.", - ); - - expect(html).to.include("foo"); - expect(html).to.include("bar"); - }); - }); -}); diff --git a/e2e/test/scenarios/sharing/reproductions/18669-test-email-with-parameters.cy.spec.js b/e2e/test/scenarios/sharing/reproductions/18669-test-email-with-parameters.cy.spec.js deleted file mode 100644 index 2953f8f1f02..00000000000 --- a/e2e/test/scenarios/sharing/reproductions/18669-test-email-with-parameters.cy.spec.js +++ /dev/null @@ -1,85 +0,0 @@ -import { USERS, SAMPLE_DB_ID } from "e2e/support/cypress_data"; -import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; -import { - describeEE, - popover, - restore, - setupSMTP, - sidebar, - visitDashboard, - clickSend, - setTokenFeatures, -} from "e2e/support/helpers"; - -const { admin } = USERS; -const { PRODUCTS_ID, PRODUCTS } = SAMPLE_DATABASE; - -describeEE("issue 18669", { tags: "@external" }, () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - setTokenFeatures("all"); - setupSMTP(); - - cy.createQuestionAndDashboard({ questionDetails, dashboardDetails }).then( - ({ body: card }) => { - cy.editDashboardCard(card, getFilterMapping(card)); - visitDashboard(card.dashboard_id); - }, - ); - }); - - it("should send a test email with non-default parameters (metabase#18669)", () => { - cy.icon("subscription").click(); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Email it").click(); - - cy.findByPlaceholderText("Enter user names or email addresses") - .click() - .type(`${admin.first_name} ${admin.last_name}{enter}`) - .blur(); - - sidebar().within(() => { - cy.findByText("Doohickey").click(); - }); - - popover().within(() => { - cy.findByText("Gizmo").click(); - cy.button("Update filter").click(); - }); - - clickSend(); - }); -}); - -const questionDetails = { - name: "Product count", - database: SAMPLE_DB_ID, - type: "query", - query: { - "source-table": PRODUCTS_ID, - aggregation: [["count"]], - }, -}; - -const filterDetails = { - name: "Category", - slug: "category", - id: "c32a49e1", - type: "category", - default: ["Doohickey"], -}; - -const dashboardDetails = { - parameters: [filterDetails], -}; - -const getFilterMapping = card => ({ - parameter_mappings: [ - { - parameter_id: filterDetails.id, - card_id: card.card_id, - target: ["dimension", ["field", PRODUCTS.CATEGORY, null]], - }, - ], -}); diff --git a/e2e/test/scenarios/sharing/reproductions/20393-public-dashboard-nested-card-with-parameters.cy.spec.js b/e2e/test/scenarios/sharing/reproductions/20393-public-dashboard-nested-card-with-parameters.cy.spec.js deleted file mode 100644 index c74c040b3ff..00000000000 --- a/e2e/test/scenarios/sharing/reproductions/20393-public-dashboard-nested-card-with-parameters.cy.spec.js +++ /dev/null @@ -1,67 +0,0 @@ -import { - restore, - popover, - visitDashboard, - editDashboard, - setFilter, - openNewPublicLinkDropdown, -} from "e2e/support/helpers"; - -describe("issue 20393", () => { - beforeEach(() => { - cy.intercept("POST", "/api/dashboard/*/public_link").as("publicLink"); - - restore(); - cy.signInAsAdmin(); - }); - - it("should show public dashboards with nested cards mapped to parameters (metabase#20393)", () => { - createDashboardWithNestedCard(); - - editDashboard(); - - setFilter("Time", "All Options"); - - // map the date parameter to the card - cy.findByTestId("dashcard-container").contains("Select").click(); - popover().contains("CREATED_AT").click(); - - // save the dashboard - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Save").click(); - - // open the sharing modal and enable sharing - openNewPublicLinkDropdown("dashboard"); - - // navigate to the public dashboard link - cy.wait("@publicLink").then(({ response: { body } }) => { - const { uuid } = body; - - cy.signOut(); - cy.visit(`/public/dashboard/${uuid}`); - }); - - // verify that the card is visible on the page - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Q2"); - }); -}); - -function createDashboardWithNestedCard() { - cy.createNativeQuestion({ - name: "Q1", - native: { query: 'SELECT * FROM "ORDERS"', "template-tags": {} }, - }).then(({ body }) => - cy - .createQuestionAndDashboard({ - questionDetails: { - name: "Q2", - query: { "source-table": `card__${body.id}` }, - }, - dashboardDetails: { - name: "Q2 in a dashboard", - }, - }) - .then(({ body: { dashboard_id } }) => visitDashboard(dashboard_id)), - ); -} diff --git a/e2e/test/scenarios/sharing/reproductions/21559-subscription-bar-sent-as-scalar.cy.spec.js b/e2e/test/scenarios/sharing/reproductions/21559-subscription-bar-sent-as-scalar.cy.spec.js deleted file mode 100644 index 7a6553cef84..00000000000 --- a/e2e/test/scenarios/sharing/reproductions/21559-subscription-bar-sent-as-scalar.cy.spec.js +++ /dev/null @@ -1,79 +0,0 @@ -import { USERS } from "e2e/support/cypress_data"; -import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; -import { - restore, - visitDashboard, - editDashboard, - saveDashboard, - setupSMTP, - sendEmailAndAssert, - chartPathWithFillColor, -} from "e2e/support/helpers"; - -const { admin } = USERS; - -const { ORDERS, ORDERS_ID, PRODUCTS, PRODUCTS_ID } = SAMPLE_DATABASE; - -const q1Details = { - name: "21559-1", - query: { - "source-table": ORDERS_ID, - aggregation: [["avg", ["field", ORDERS.TOTAL, null]]], - }, - display: "scalar", -}; - -const q2Details = { - name: "21559-2", - query: { - "source-table": PRODUCTS_ID, - aggregation: [["avg", ["field", PRODUCTS.PRICE, null]]], - }, - display: "scalar", -}; - -describe("issue 21559", { tags: "@external" }, () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - - setupSMTP(); - - cy.createQuestionAndDashboard({ - questionDetails: q1Details, - }).then(({ body: { dashboard_id } }) => { - cy.createQuestion(q2Details); - - visitDashboard(dashboard_id); - editDashboard(); - }); - }); - - it("should respect dashboard card visualization (metabase#21559)", () => { - cy.findByTestId("add-series-button").click({ force: true }); - - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText(q2Details.name).click(); - cy.findByTestId("add-series-modal").button("Done").click(); - - // Make sure visualization changed to bars - chartPathWithFillColor("#A989C5").should("have.length", 1); - chartPathWithFillColor("#88BF4D").should("have.length", 1); - - saveDashboard(); - - cy.icon("subscription").click(); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Email it").click(); - - cy.findByPlaceholderText("Enter user names or email addresses") - .click() - .type(`${admin.first_name} ${admin.last_name}{enter}`) - .blur(); // blur is needed to close the popover - - sendEmailAndAssert(email => { - expect(email.html).to.include("img"); // Bar chart is sent as img (inline attachment) - expect(email.html).not.to.include("80.52"); // Scalar displays its value in HTML - }); - }); -}); diff --git a/e2e/test/scenarios/sharing/reproductions/22524-public-dashboard-updates-after-changing-parameters.cy.spec.js b/e2e/test/scenarios/sharing/reproductions/22524-public-dashboard-updates-after-changing-parameters.cy.spec.js deleted file mode 100644 index eef5dbb233e..00000000000 --- a/e2e/test/scenarios/sharing/reproductions/22524-public-dashboard-updates-after-changing-parameters.cy.spec.js +++ /dev/null @@ -1,68 +0,0 @@ -import { - restore, - popover, - visitDashboard, - saveDashboard, - editDashboard, - setFilter, - openNewPublicLinkDropdown, -} from "e2e/support/helpers"; - -const questionDetails = { - name: "22524 question", - native: { - query: "select * from people where city = {{city}}", - "template-tags": { - city: { - id: "6d077d39-a420-fd14-0b0b-a5eb611ce1e0", - name: "city", - "display-name": "City", - type: "text", - }, - }, - }, -}; - -describe("issue 22524", () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - }); - - it("update dashboard cards when changing parameters on publicly shared dashboards (metabase#22524)", () => { - cy.createNativeQuestionAndDashboard({ questionDetails }).then( - ({ body: { dashboard_id } }) => { - cy.intercept("POST", `/api/dashboard/${dashboard_id}/public_link`).as( - "publicLink", - ); - visitDashboard(dashboard_id); - }, - ); - - editDashboard(); - setFilter("Text or Category", "Is"); - - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Select…").click(); - popover().contains("City").click(); - - saveDashboard(); - - // Share dashboard - openNewPublicLinkDropdown("dashboard"); - - cy.wait("@publicLink").then(({ response: { body } }) => { - const { uuid } = body; - - cy.signOut(); - cy.visit(`/public/dashboard/${uuid}`); - }); - - // Set parameter value - cy.findByPlaceholderText("Text").clear().type("Rye{enter}"); - - // Check results - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("2-7900 Cuerno Verde Road"); - }); -}); diff --git a/e2e/test/scenarios/sharing/reproductions/24223-remove-filter-defaults-on-subscriptions.cy.spec.js b/e2e/test/scenarios/sharing/reproductions/24223-remove-filter-defaults-on-subscriptions.cy.spec.js deleted file mode 100644 index b24949b6309..00000000000 --- a/e2e/test/scenarios/sharing/reproductions/24223-remove-filter-defaults-on-subscriptions.cy.spec.js +++ /dev/null @@ -1,79 +0,0 @@ -import { USERS } from "e2e/support/cypress_data"; -import { ORDERS_DASHBOARD_ID } from "e2e/support/cypress_sample_instance_data"; -import { - describeEE, - editDashboard, - popover, - restore, - saveDashboard, - sendEmailAndVisitIt, - setTokenFeatures, - setupSMTP, - setFilter, -} from "e2e/support/helpers"; - -const { admin } = USERS; - -describeEE("issue 24223", () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - setTokenFeatures("all"); - setupSMTP(); - }); - - it("should clear default filter", () => { - cy.visit(`/dashboard/${ORDERS_DASHBOARD_ID}`); - addParametersToDashboard(); - cy.findByLabelText("subscriptions").click(); - // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage - cy.findByText("Email it").click(); - cy.findByPlaceholderText("Enter user names or email addresses") - .click() - .type(`${admin.first_name} ${admin.last_name}{enter}`) - .blur(); // blur is needed to close the popover - - cy.wait(500); // we need to wait here for some reason for CI to pass - - cy.findAllByText("Doohickey") - .last() - .closest("fieldset") - .icon("close") - .click(); - cy.button("Done").click(); - - cy.get("[aria-label='Pulse Card']") - .findByText("Text contains is Awesome") - .click(); - - sendEmailAndVisitIt(); - cy.get("table.header").within(() => { - cy.findByText("Text").should("not.exist"); - cy.findByText("Awesome").parent().findByText("Text contains"); - }); - }); -}); - -function addParametersToDashboard() { - editDashboard(); - - setFilter("Text or Category", "Is"); - - cy.findByText("Select…").click(); - popover().findByText("Category").click(); - cy.findByText("No default").click(); - popover().within(() => { - cy.findByText("Doohickey").click(); - cy.button("Add filter").click(); - }); - - setFilter("Text or Category", "Contains", "Text contains"); - - cy.findByText("Select…").click(); - popover().findByText("Title").click(); - cy.findByText("No default").click(); - popover().find("input").type("Awesome"); - popover().button("Add filter").click(); - - saveDashboard(); -} diff --git a/e2e/test/scenarios/sharing/reproductions/25473-dashboard-text-filter-asking-for-number.cy.spec.js b/e2e/test/scenarios/sharing/reproductions/25473-dashboard-text-filter-asking-for-number.cy.spec.js deleted file mode 100644 index 8c3e4ec786f..00000000000 --- a/e2e/test/scenarios/sharing/reproductions/25473-dashboard-text-filter-asking-for-number.cy.spec.js +++ /dev/null @@ -1,116 +0,0 @@ -import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; -import { - restore, - visitEmbeddedPage, - filterWidget, - visitPublicDashboard, -} from "e2e/support/helpers"; - -const { REVIEWS, REVIEWS_ID } = SAMPLE_DATABASE; - -const ccName = "CC Reviewer"; - -const dashboardFilter = { - name: "Text ends with", - slug: "text_ends_with", - id: "3a8ecdbd", - type: "string/ends-with", - sectionId: "string", -}; - -const questionDetails = { - name: "25473", - query: { - "source-table": REVIEWS_ID, - expressions: { [ccName]: ["field", REVIEWS.REVIEWER, null] }, - limit: 10, - // Let's show only a few columns to make it easier to focus on the UI - fields: [ - ["field", REVIEWS.REVIEWER, null], - ["field", REVIEWS.RATING, null], - ["field", REVIEWS.CREATED_AT, null], - ["expression", ccName, null], - ], - }, -}; - -const dashboardDetails = { - name: "25473D", - parameters: [dashboardFilter], -}; - -describe("issue 25473", () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - - cy.createQuestionAndDashboard({ questionDetails, dashboardDetails }).then( - ({ body: { id, card_id, dashboard_id } }) => { - cy.request("PUT", `/api/dashboard/${dashboard_id}`, { - dashcards: [ - { - id, - card_id, - row: 0, - col: 0, - size_x: 16, - size_y: 8, - series: [], - visualization_settings: {}, - parameter_mappings: [ - { - parameter_id: dashboardFilter.id, - card_id, - target: ["dimension", ["expression", ccName, null]], - }, - ], - }, - ], - }); - - cy.wrap(dashboard_id).as("dashboardId"); - }, - ); - }); - - it("public sharing: dashboard text filter on a custom column should accept text input (metabase#25473-1)", () => { - cy.get("@dashboardId").then(id => { - visitPublicDashboard(id); - }); - - assertOnResults(); - }); - - it("signed embedding: dashboard text filter on a custom column should accept text input (metabase#25473-2)", () => { - cy.get("@dashboardId").then(id => { - cy.request("PUT", `/api/dashboard/${id}`, { - embedding_params: { - [dashboardFilter.slug]: "enabled", - }, - enable_embedding: true, - }); - - const payload = { - resource: { dashboard: id }, - params: {}, - }; - - visitEmbeddedPage(payload); - }); - - assertOnResults(); - }); -}); - -function assertOnResults() { - cy.findAllByTestId("column-header").last().should("have.text", ccName); - cy.findAllByText("xavier").should("have.length", 2); - - filterWidget().click(); - cy.findByPlaceholderText("Enter some text").type("e").blur(); - cy.button("Add filter").click(); - - cy.location("search").should("eq", `?${dashboardFilter.slug}=e`); - cy.findAllByText("xavier").should("not.exist"); - cy.findAllByText("cameron.nitzsche").should("have.length", 2); -} diff --git a/e2e/test/scenarios/sharing/reproductions/26988-embedding-dynamic-settings.cy.spec.js b/e2e/test/scenarios/sharing/reproductions/26988-embedding-dynamic-settings.cy.spec.js deleted file mode 100644 index 44b4652d46e..00000000000 --- a/e2e/test/scenarios/sharing/reproductions/26988-embedding-dynamic-settings.cy.spec.js +++ /dev/null @@ -1,67 +0,0 @@ -import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; -import { - describeEE, - getIframeBody, - openStaticEmbeddingModal, - popover, - restore, - setTokenFeatures, - visitDashboard, -} from "e2e/support/helpers"; - -const { ORDERS_ID } = SAMPLE_DATABASE; - -describeEE("issue 26988", () => { - beforeEach(() => { - restore(); - cy.intercept("GET", "/api/preview_embed/dashboard/*").as( - "previewDashboard", - ); - - cy.signInAsAdmin(); - setTokenFeatures("all"); - }); - - it("should apply embedding settings passed in URL on load", () => { - cy.createQuestionAndDashboard({ - questionDetails: { - name: "Q1", - query: { - "source-table": ORDERS_ID, - limit: 3, - }, - }, - dashboardDetails: { - enable_embedding: true, - }, - }).then(({ body: card }) => { - visitDashboard(card.dashboard_id); - }); - - openStaticEmbeddingModal({ - activeTab: "appearance", - previewMode: "preview", - acceptTerms: false, - }); - - cy.wait("@previewDashboard"); - getIframeBody().should("have.css", "font-family", "Lato, sans-serif"); - - cy.findByLabelText("Playing with appearance options") - .findByLabelText("Font") - .as("font-control") - .click(); - popover().findByText("Oswald").click(); - - getIframeBody().should("have.css", "font-family", "Oswald, sans-serif"); - - cy.get("@font-control").click(); - popover().findByText("Slabo 27px").click(); - - getIframeBody().should( - "have.css", - "font-family", - '"Slabo 27px", sans-serif', - ); - }); -}); diff --git a/e2e/test/scenarios/sharing/reproductions/30314-new-subscription-form-state.cy.spec.js b/e2e/test/scenarios/sharing/reproductions/30314-new-subscription-form-state.cy.spec.js deleted file mode 100644 index 40e0f3ad991..00000000000 --- a/e2e/test/scenarios/sharing/reproductions/30314-new-subscription-form-state.cy.spec.js +++ /dev/null @@ -1,38 +0,0 @@ -import { ORDERS_DASHBOARD_ID } from "e2e/support/cypress_sample_instance_data"; -import { - visitDashboard, - restore, - setupSMTP, - dashboardHeader, - sidebar, -} from "e2e/support/helpers"; - -describe("issue 30314", () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - setupSMTP(); - }); - - it("should clean the new subscription form on cancel (metabase#30314)", () => { - visitDashboard(ORDERS_DASHBOARD_ID); - - dashboardHeader().findByLabelText("subscriptions").click(); - sidebar().within(() => { - cy.findByText("Email it").click(); - - cy.findByLabelText("Attach results").should("not.be.checked").click(); - cy.findByLabelText("Questions to attach") - .should("not.be.checked") - .click(); - - cy.button("Cancel").click(); - cy.findByText("Email it").click(); - - cy.findByLabelText("Attach results").should("not.be.checked"); - cy.findByText("Questions to attach").should("not.exist"); - cy.findByText(".xlsx").should("not.exist"); - cy.findByText(".csv").should("not.exist"); - }); - }); -}); diff --git a/e2e/test/scenarios/sharing/sharing-reproductions.cy.spec.js b/e2e/test/scenarios/sharing/sharing-reproductions.cy.spec.js new file mode 100644 index 00000000000..62a057268a9 --- /dev/null +++ b/e2e/test/scenarios/sharing/sharing-reproductions.cy.spec.js @@ -0,0 +1,946 @@ +import { USERS, SAMPLE_DB_ID } from "e2e/support/cypress_data"; +import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; +import { + ORDERS_DASHBOARD_ID, + ADMIN_USER_ID, + ORDERS_DASHBOARD_DASHCARD_ID, + ORDERS_QUESTION_ID, +} from "e2e/support/cypress_sample_instance_data"; +import { + restore, + popover, + setupSMTP, + visitDashboard, + sendEmailAndAssert, + sidebar, + editDashboard, + saveDashboard, + modal, + visitQuestion, + setTokenFeatures, + clickSend, + openNewPublicLinkDropdown, + setFilter, + describeEE, + chartPathWithFillColor, + sendEmailAndVisitIt, + visitPublicDashboard, + visitEmbeddedPage, + getIframeBody, + openStaticEmbeddingModal, + dashboardHeader, + filterWidget, + getFullName, +} from "e2e/support/helpers"; + +const { admin } = USERS; +const { + ORDERS, + ORDERS_ID, + PRODUCTS, + PRODUCTS_ID, + REVIEWS, + REVIEWS_ID, + PEOPLE, +} = SAMPLE_DATABASE; + +describe("issue 18009", { tags: "@external" }, () => { + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + + setupSMTP(); + + cy.signIn("nodata"); + }); + + it("nodata user should be able to create and receive an email subscription without errors (metabase#18009)", () => { + visitDashboard(ORDERS_DASHBOARD_ID); + + cy.findByLabelText("subscriptions").click(); + + sidebar() + .findByPlaceholderText("Enter user names or email addresses") + .click(); + popover() + .contains(/^No Data/) + .click(); + + // Click anywhere to close the popover that covers the "Send email now" button + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("To:").click(); + + sendEmailAndAssert(email => { + expect(email.html).not.to.include( + "An error occurred while displaying this card.", + ); + + expect(email.html).to.include("37.65"); + }); + }); +}); + +describe("issue 18344", { tags: "@external" }, () => { + const { + admin: { first_name, last_name }, + } = USERS; + + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + + setupSMTP(); + + // Rename the question + visitDashboard(ORDERS_DASHBOARD_ID); + + editDashboard(); + + // Open visualization options + cy.findByTestId("dashcard").realHover(); + cy.icon("palette").click(); + + modal().within(() => { + cy.findByDisplayValue("Orders").type("Foo").blur(); + + cy.button("Done").click(); + }); + + saveDashboard(); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("OrdersFoo"); + }); + + it("subscription should not include original question name when it's been renamed in the dashboard (metabase#18344)", () => { + // Send a test email subscription + cy.icon("subscription").click(); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Email it").click(); + + cy.findByPlaceholderText("Enter user names or email addresses").click(); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText(`${first_name} ${last_name}`).click(); + // Click this just to close the popover that is blocking the "Send email now" button + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("To:").click(); + + sendEmailAndAssert(email => { + expect(email.html).to.include("OrdersFoo"); + }); + }); +}); + +describe("issue 18352", { tags: "@external" }, () => { + const { + admin: { first_name, last_name }, + } = USERS; + + const questionDetails = { + name: "18352", + native: { + query: "SELECT 'foo', 1 UNION ALL SELECT 'bar', 2", + }, + }; + + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + + setupSMTP(); + + cy.createNativeQuestionAndDashboard({ questionDetails }).then( + ({ body: { card_id, dashboard_id } }) => { + visitQuestion(card_id); + + visitDashboard(dashboard_id); + }, + ); + }); + + it("should send the card with the INT64 values (metabase#18352)", () => { + cy.icon("subscription").click(); + + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Email it").click(); + + cy.findByPlaceholderText("Enter user names or email addresses").click(); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText(`${first_name} ${last_name}`).click(); + // Click this just to close the popover that is blocking the "Send email now" button + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("To:").click(); + + sendEmailAndAssert(({ html }) => { + expect(html).not.to.include( + "An error occurred while displaying this card.", + ); + + expect(html).to.include("foo"); + expect(html).to.include("bar"); + }); + }); +}); + +describeEE("issue 18669", { tags: "@external" }, () => { + const questionDetails = { + name: "Product count", + database: SAMPLE_DB_ID, + type: "query", + query: { + "source-table": PRODUCTS_ID, + aggregation: [["count"]], + }, + }; + + const filterDetails = { + name: "Category", + slug: "category", + id: "c32a49e1", + type: "category", + default: ["Doohickey"], + }; + + const dashboardDetails = { + parameters: [filterDetails], + }; + + const getFilterMapping = card => ({ + parameter_mappings: [ + { + parameter_id: filterDetails.id, + card_id: card.card_id, + target: ["dimension", ["field", PRODUCTS.CATEGORY, null]], + }, + ], + }); + + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + setTokenFeatures("all"); + setupSMTP(); + + cy.createQuestionAndDashboard({ questionDetails, dashboardDetails }).then( + ({ body: card }) => { + cy.editDashboardCard(card, getFilterMapping(card)); + visitDashboard(card.dashboard_id); + }, + ); + }); + + it("should send a test email with non-default parameters (metabase#18669)", () => { + cy.icon("subscription").click(); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Email it").click(); + + cy.findByPlaceholderText("Enter user names or email addresses") + .click() + .type(`${admin.first_name} ${admin.last_name}{enter}`) + .blur(); + + sidebar().within(() => { + cy.findByText("Doohickey").click(); + }); + + popover().within(() => { + cy.findByText("Gizmo").click(); + cy.button("Update filter").click(); + }); + + clickSend(); + }); +}); + +describe("issue 20393", () => { + function createDashboardWithNestedCard() { + cy.createNativeQuestion({ + name: "Q1", + native: { query: 'SELECT * FROM "ORDERS"', "template-tags": {} }, + }).then(({ body }) => + cy + .createQuestionAndDashboard({ + questionDetails: { + name: "Q2", + query: { "source-table": `card__${body.id}` }, + }, + dashboardDetails: { + name: "Q2 in a dashboard", + }, + }) + .then(({ body: { dashboard_id } }) => visitDashboard(dashboard_id)), + ); + } + + beforeEach(() => { + cy.intercept("POST", "/api/dashboard/*/public_link").as("publicLink"); + + restore(); + cy.signInAsAdmin(); + }); + + it("should show public dashboards with nested cards mapped to parameters (metabase#20393)", () => { + createDashboardWithNestedCard(); + + editDashboard(); + + setFilter("Time", "All Options"); + + // map the date parameter to the card + cy.findByTestId("dashcard-container").contains("Select").click(); + popover().contains("CREATED_AT").click(); + + // save the dashboard + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Save").click(); + + // open the sharing modal and enable sharing + openNewPublicLinkDropdown("dashboard"); + + // navigate to the public dashboard link + cy.wait("@publicLink").then(({ response: { body } }) => { + const { uuid } = body; + + cy.signOut(); + cy.visit(`/public/dashboard/${uuid}`); + }); + + // verify that the card is visible on the page + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Q2"); + }); +}); + +describe("issue 21559", { tags: "@external" }, () => { + const q1Details = { + name: "21559-1", + query: { + "source-table": ORDERS_ID, + aggregation: [["avg", ["field", ORDERS.TOTAL, null]]], + }, + display: "scalar", + }; + + const q2Details = { + name: "21559-2", + query: { + "source-table": PRODUCTS_ID, + aggregation: [["avg", ["field", PRODUCTS.PRICE, null]]], + }, + display: "scalar", + }; + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + + setupSMTP(); + + cy.createQuestionAndDashboard({ + questionDetails: q1Details, + }).then(({ body: { dashboard_id } }) => { + cy.createQuestion(q2Details); + + visitDashboard(dashboard_id); + editDashboard(); + }); + }); + + it("should respect dashboard card visualization (metabase#21559)", () => { + cy.findByTestId("add-series-button").click({ force: true }); + + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText(q2Details.name).click(); + cy.findByTestId("add-series-modal").button("Done").click(); + + // Make sure visualization changed to bars + chartPathWithFillColor("#A989C5").should("have.length", 1); + chartPathWithFillColor("#88BF4D").should("have.length", 1); + + saveDashboard(); + + cy.icon("subscription").click(); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Email it").click(); + + cy.findByPlaceholderText("Enter user names or email addresses") + .click() + .type(`${admin.first_name} ${admin.last_name}{enter}`) + .blur(); // blur is needed to close the popover + + sendEmailAndAssert(email => { + expect(email.html).to.include("img"); // Bar chart is sent as img (inline attachment) + expect(email.html).not.to.include("80.52"); // Scalar displays its value in HTML + }); + }); +}); +describe("issue 22524", () => { + const questionDetails = { + name: "22524 question", + native: { + query: "select * from people where city = {{city}}", + "template-tags": { + city: { + id: "6d077d39-a420-fd14-0b0b-a5eb611ce1e0", + name: "city", + "display-name": "City", + type: "text", + }, + }, + }, + }; + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + }); + + it("update dashboard cards when changing parameters on publicly shared dashboards (metabase#22524)", () => { + cy.createNativeQuestionAndDashboard({ questionDetails }).then( + ({ body: { dashboard_id } }) => { + cy.intercept("POST", `/api/dashboard/${dashboard_id}/public_link`).as( + "publicLink", + ); + visitDashboard(dashboard_id); + }, + ); + + editDashboard(); + setFilter("Text or Category", "Is"); + + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Select…").click(); + popover().contains("City").click(); + + saveDashboard(); + + // Share dashboard + openNewPublicLinkDropdown("dashboard"); + + cy.wait("@publicLink").then(({ response: { body } }) => { + const { uuid } = body; + + cy.signOut(); + cy.visit(`/public/dashboard/${uuid}`); + }); + + // Set parameter value + cy.findByPlaceholderText("Text").clear().type("Rye{enter}"); + + // Check results + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("2-7900 Cuerno Verde Road"); + }); +}); + +describeEE("issue 24223", () => { + function addParametersToDashboard() { + editDashboard(); + + setFilter("Text or Category", "Is"); + + cy.findByText("Select…").click(); + popover().findByText("Category").click(); + cy.findByText("No default").click(); + popover().within(() => { + cy.findByText("Doohickey").click(); + cy.button("Add filter").click(); + }); + + setFilter("Text or Category", "Contains", "Text contains"); + + cy.findByText("Select…").click(); + popover().findByText("Title").click(); + cy.findByText("No default").click(); + popover().find("input").type("Awesome"); + popover().button("Add filter").click(); + + saveDashboard(); + } + + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + setTokenFeatures("all"); + setupSMTP(); + }); + + it("should clear default filter", () => { + cy.visit(`/dashboard/${ORDERS_DASHBOARD_ID}`); + addParametersToDashboard(); + cy.findByLabelText("subscriptions").click(); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Email it").click(); + cy.findByPlaceholderText("Enter user names or email addresses") + .click() + .type(`${admin.first_name} ${admin.last_name}{enter}`) + .blur(); // blur is needed to close the popover + + cy.wait(500); // we need to wait here for some reason for CI to pass + + cy.findAllByText("Doohickey") + .last() + .closest("fieldset") + .icon("close") + .click(); + cy.button("Done").click(); + + cy.get("[aria-label='Pulse Card']") + .findByText("Text contains is Awesome") + .click(); + + sendEmailAndVisitIt(); + cy.get("table.header").within(() => { + cy.findByText("Text").should("not.exist"); + cy.findByText("Awesome").parent().findByText("Text contains"); + }); + }); +}); + +describe("issue 25473", () => { + const ccName = "CC Reviewer"; + + const dashboardFilter = { + name: "Text ends with", + slug: "text_ends_with", + id: "3a8ecdbd", + type: "string/ends-with", + sectionId: "string", + }; + + const questionDetails = { + name: "25473", + query: { + "source-table": REVIEWS_ID, + expressions: { [ccName]: ["field", REVIEWS.REVIEWER, null] }, + limit: 10, + // Let's show only a few columns to make it easier to focus on the UI + fields: [ + ["field", REVIEWS.REVIEWER, null], + ["field", REVIEWS.RATING, null], + ["field", REVIEWS.CREATED_AT, null], + ["expression", ccName, null], + ], + }, + }; + + const dashboardDetails = { + name: "25473D", + parameters: [dashboardFilter], + }; + + function assertOnResults() { + cy.findAllByTestId("column-header").last().should("have.text", ccName); + cy.findAllByText("xavier").should("have.length", 2); + + filterWidget().click(); + cy.findByPlaceholderText("Enter some text").type("e").blur(); + cy.button("Add filter").click(); + + cy.location("search").should("eq", `?${dashboardFilter.slug}=e`); + cy.findAllByText("xavier").should("not.exist"); + cy.findAllByText("cameron.nitzsche").should("have.length", 2); + } + + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + + cy.createQuestionAndDashboard({ questionDetails, dashboardDetails }).then( + ({ body: { id, card_id, dashboard_id } }) => { + cy.request("PUT", `/api/dashboard/${dashboard_id}`, { + dashcards: [ + { + id, + card_id, + row: 0, + col: 0, + size_x: 16, + size_y: 8, + series: [], + visualization_settings: {}, + parameter_mappings: [ + { + parameter_id: dashboardFilter.id, + card_id, + target: ["dimension", ["expression", ccName, null]], + }, + ], + }, + ], + }); + + cy.wrap(dashboard_id).as("dashboardId"); + }, + ); + }); + + it("public sharing: dashboard text filter on a custom column should accept text input (metabase#25473-1)", () => { + cy.get("@dashboardId").then(id => { + visitPublicDashboard(id); + }); + + assertOnResults(); + }); + + it("signed embedding: dashboard text filter on a custom column should accept text input (metabase#25473-2)", () => { + cy.get("@dashboardId").then(id => { + cy.request("PUT", `/api/dashboard/${id}`, { + embedding_params: { + [dashboardFilter.slug]: "enabled", + }, + enable_embedding: true, + }); + + const payload = { + resource: { dashboard: id }, + params: {}, + }; + + visitEmbeddedPage(payload); + }); + + assertOnResults(); + }); +}); + +describeEE("issue 26988", () => { + beforeEach(() => { + restore(); + cy.intercept("GET", "/api/preview_embed/dashboard/*").as( + "previewDashboard", + ); + + cy.signInAsAdmin(); + setTokenFeatures("all"); + }); + + it("should apply embedding settings passed in URL on load", () => { + cy.createQuestionAndDashboard({ + questionDetails: { + name: "Q1", + query: { + "source-table": ORDERS_ID, + limit: 3, + }, + }, + dashboardDetails: { + enable_embedding: true, + }, + }).then(({ body: card }) => { + visitDashboard(card.dashboard_id); + }); + + openStaticEmbeddingModal({ + activeTab: "appearance", + previewMode: "preview", + acceptTerms: false, + }); + + cy.wait("@previewDashboard"); + getIframeBody().should("have.css", "font-family", "Lato, sans-serif"); + + cy.findByLabelText("Playing with appearance options") + .findByLabelText("Font") + .as("font-control") + .click(); + popover().findByText("Oswald").click(); + + getIframeBody().should("have.css", "font-family", "Oswald, sans-serif"); + + cy.get("@font-control").click(); + popover().findByText("Slabo 27px").click(); + + getIframeBody().should( + "have.css", + "font-family", + '"Slabo 27px", sans-serif', + ); + }); +}); + +describe("issue 30314", () => { + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + setupSMTP(); + }); + + it("should clean the new subscription form on cancel (metabase#30314)", () => { + visitDashboard(ORDERS_DASHBOARD_ID); + + dashboardHeader().findByLabelText("subscriptions").click(); + sidebar().within(() => { + cy.findByText("Email it").click(); + + cy.findByLabelText("Attach results").should("not.be.checked").click(); + cy.findByLabelText("Questions to attach") + .should("not.be.checked") + .click(); + + cy.button("Cancel").click(); + cy.findByText("Email it").click(); + + cy.findByLabelText("Attach results").should("not.be.checked"); + cy.findByText("Questions to attach").should("not.exist"); + cy.findByText(".xlsx").should("not.exist"); + cy.findByText(".csv").should("not.exist"); + }); + }); +}); + +describe("issue 17657", () => { + const { first_name, last_name } = admin; + + function createSubscriptionWithoutRecipients() { + cy.request("POST", "/api/pulse", { + name: "Orders in a dashboard", + cards: [ + { + id: ORDERS_QUESTION_ID, + collection_id: null, + description: null, + display: "table", + name: "Orders", + include_csv: false, + include_xls: false, + dashboard_card_id: 1, + dashboard_id: ORDERS_DASHBOARD_ID, + parameter_mappings: [], + }, + ], + channels: [ + { + channel_type: "email", + enabled: true, + // Since the fix (https://github.com/metabase/metabase/pull/17668), this is not even possible to do in the UI anymore. + // Backend still doesn't do this validation so we're making sure the FE handles the case of missing recipients gracefully. + recipients: [], + details: {}, + schedule_type: "monthly", + schedule_day: "mon", + schedule_hour: 8, + schedule_frame: "first", + }, + ], + skip_if_empty: false, + collection_id: null, + parameters: [], + dashboard_id: ORDERS_DASHBOARD_ID, + }); + } + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + + createSubscriptionWithoutRecipients(); + }); + + it("frontend should gracefully handle the case of a subscription without a recipient (metabase#17657)", () => { + visitDashboard(ORDERS_DASHBOARD_ID); + + cy.icon("subscription").click(); + + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText(/^Emailed monthly/).click(); + + sidebar().within(() => { + cy.button("Done").should("be.disabled"); + }); + + // Open the popover with all users + cy.findByPlaceholderText("Enter user names or email addresses").click(); + // Pick admin as a recipient + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText(`${first_name} ${last_name}`).click(); + + sidebar().within(() => { + cy.button("Done").should("not.be.disabled"); + }); + }); +}); + +describe("issue 17658", { tags: "@external" }, () => { + function moveDashboardToCollection(collectionName) { + const { first_name, last_name, email } = admin; + + cy.request("GET", "/api/collection/tree?tree=true").then( + ({ body: collections }) => { + const { id } = collections.find( + collection => collection.name === collectionName, + ); + + // Move dashboard + cy.request("PUT", `/api/dashboard/${ORDERS_DASHBOARD_ID}`, { + collection_id: id, + }); + + // Create subscription + cy.request("POST", "/api/pulse", { + name: "Orders in a dashboard", + cards: [ + { + id: ORDERS_QUESTION_ID, + collection_id: null, + description: null, + display: "table", + name: "Orders", + include_csv: false, + include_xls: false, + dashboard_card_id: ORDERS_DASHBOARD_DASHCARD_ID, + dashboard_id: ORDERS_DASHBOARD_ID, + parameter_mappings: [], + }, + ], + channels: [ + { + channel_type: "email", + enabled: true, + recipients: [ + { + id: ADMIN_USER_ID, + email, + first_name, + last_name, + common_name: getFullName(admin), + }, + ], + details: {}, + schedule_type: "monthly", + schedule_day: "mon", + schedule_hour: 8, + schedule_frame: "first", + }, + ], + skip_if_empty: false, + collection_id: id, + parameters: [], + dashboard_id: ORDERS_DASHBOARD_ID, + }); + }, + ); + } + + beforeEach(() => { + cy.intercept("PUT", "/api/pulse/*").as("deletePulse"); + restore(); + cy.signInAsAdmin(); + + setupSMTP(); + + moveDashboardToCollection("First collection"); + }); + + it("should delete dashboard subscription from any collection (metabase#17658)", () => { + visitDashboard(ORDERS_DASHBOARD_ID); + + cy.icon("subscription").click(); + + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText(/^Emailed monthly/).click(); + + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Delete this subscription").click(); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText(/^This dashboard will no longer be emailed to/).click(); + + cy.button("Delete").click(); + + cy.wait("@deletePulse").then(({ response }) => { + expect(response.body.cause).not.to.exist; + expect(response.statusCode).not.to.eq(500); + }); + + cy.button("Delete").should("not.exist"); + }); +}); + +describe("issue 17547", () => { + const questionDetails = { + query: { + "source-table": ORDERS_ID, + breakout: [ + ["field", ORDERS.CREATED_AT, { "temporal-unit": "month" }], + ["field", PEOPLE.SOURCE, { "source-field": ORDERS.USER_ID }], + ], + aggregation: [["count"]], + }, + display: "area", + }; + + function setUpAlert(questionId) { + cy.request("POST", "/api/alert", { + channels: [ + { + schedule_type: "daily", + schedule_hour: 12, + channel_type: "slack", + schedule_frame: null, + recipients: [], + details: { channel: "#work" }, + pulse_id: 1, + id: 1, + schedule_day: null, + enabled: true, + }, + ], + alert_condition: "rows", + name: null, + creator_id: ADMIN_USER_ID, + card: { id: questionId, include_csv: true, include_xls: false }, + alert_first_only: false, + skip_if_empty: true, + parameters: [], + dashboard_id: null, + }).then(({ body: { id: alertId } }) => { + cy.intercept("PUT", `/api/alert/${alertId}`).as("alertQuery"); + }); + } + + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + + cy.createQuestion(questionDetails).then(({ body: { id: questionId } }) => { + setUpAlert(questionId); + + visitQuestion(questionId); + }); + }); + + it("editing an alert should not delete it (metabase#17547)", () => { + cy.icon("bell").click(); + popover().within(() => { + cy.findByText("Daily, 12:00 PM"); + cy.findByText("Edit").click(); + }); + + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("AM").click(); + cy.button("Save changes").click(); + + cy.wait("@alertQuery"); + + cy.icon("bell").click(); + popover().within(() => { + cy.findByText("Daily, 12:00 AM"); + }); + }); +}); + +describe("issue 16108", () => { + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + }); + + it("should display a tooltip for CTA icons on an individual question (metabase#16108)", () => { + visitQuestion(ORDERS_QUESTION_ID); + cy.icon("download").realHover(); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Download full results"); + cy.icon("bell").realHover(); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Get alerts"); + cy.icon("share").realHover(); + // eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage + cy.findByText("Sharing"); + }); +}); -- GitLab