* Remove duplicate public sharing e2e tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Merge sharing reproductions e2e tests that share setup Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Merge downloads e2e tests that share setup Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Merge public sharing e2e tests that share setup Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Repair downloads e2e tests that cannot fail Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Repair sharing reproductions e2e tests that cannot fail Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Drop unused setup in sharing reproductions e2e tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Remove duplicate subscriptions e2e tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Repair public sharing e2e tests that cannot fail Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Drop unused setup in downloads e2e tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Drop unused setup in public sharing e2e tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Remove duplicate alert e2e tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Merge alert e2e tests that share setup Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Repair alert e2e tests that cannot fail Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Drop unused setup in alert e2e tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Remove duplicate public sharing e2e tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Merge subscriptions e2e tests that share setup Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Repair subscriptions e2e tests that cannot fail Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Drop unused setup in subscriptions e2e tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Fold public dashboard link creation e2e test into 20393 repro Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fold 30314 subscription cancel e2e test into 14117 test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Fold 16108 tooltip e2e test into download format test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Merge public sharing e2e tests that share setup Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Place merged 18352 card below the Orders card Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Type a name before Escape in the 24629 subscriptions e2e test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Repair public sharing e2e tests that cannot fail Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Tag sharing e2e tests that need maildev as external Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Drop unused setup in public sharing e2e tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Fix popover check after Escape in dashboard downloads e2e test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Keep the webhook channel in the 12349 alert e2e test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Tighten subscriptions e2e checks found in parity review Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Tighten sharing reproductions e2e checks found in parity review Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Restore admin and UI-created link coverage in public sharing e2e tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Jc2P2sKm39MghfKPxCSiv * Fix 7 sharing e2e failures from CI * Restore 48402 coverage and merge more sharing e2e tests - Restore the metabase#48402 delete-alert step in the 12349 test and check the delete modal - Make 18669 check the email filter value and 21559 check the card image - Merge public dashboard download flag tests with the real downloads - Merge static-viz, branding, and default-filter subscription tests - Merge dashboard PDF and PNG download tests and check download parameters - Merge the two background=false public dashboard tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Address review on sharing e2e consolidation Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix sharing e2e CI failures Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
247 lines
7.9 KiB
JavaScript
247 lines
7.9 KiB
JavaScript
const { H } = cy;
|
|
import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
|
|
import {
|
|
ORDERS_DASHBOARD_ID,
|
|
ORDERS_MODEL_ID,
|
|
ORDERS_QUESTION_ID,
|
|
} from "e2e/support/cypress_sample_instance_data";
|
|
|
|
describe("scenarios > alert", () => {
|
|
beforeEach(() => {
|
|
H.restore();
|
|
cy.signInAsAdmin();
|
|
});
|
|
|
|
describe("with nothing set", () => {
|
|
it("should prompt admins to add email/slack credentials and tell non-admins to ask an admin", () => {
|
|
H.visitQuestion(ORDERS_QUESTION_ID);
|
|
|
|
cy.findByLabelText("Move, trash, and more…").click();
|
|
H.popover().findByText("Create an alert").click();
|
|
|
|
H.modal().within(() => {
|
|
cy.findByText(
|
|
"To get notified when something happens, or to send this chart on a schedule, first set up email, Slack, or a webhook.",
|
|
);
|
|
|
|
cy.findByText("Set up email")
|
|
.should("be.visible")
|
|
.closest("a")
|
|
.should("have.attr", "href", "/admin/settings/email");
|
|
cy.findByText("Set up Slack")
|
|
.should("be.visible")
|
|
.closest("a")
|
|
.should("have.attr", "href", "/admin/settings/slack");
|
|
cy.findByText("Add a webhook")
|
|
.should("be.visible")
|
|
.closest("a")
|
|
.should("have.attr", "href", "/admin/settings/webhooks");
|
|
});
|
|
|
|
cy.signInAsNormalUser();
|
|
|
|
H.visitQuestion(ORDERS_QUESTION_ID);
|
|
|
|
cy.findByLabelText("Move, trash, and more…").click();
|
|
H.popover().findByText("Create an alert").click();
|
|
|
|
H.modal().within(() => {
|
|
cy.findByText(
|
|
"To get notified when something happens, or to send this chart on a schedule, ask your admin to set up email, Slack, or a webhook.",
|
|
).should("be.visible");
|
|
|
|
cy.findByText("Set up email").should("not.exist");
|
|
cy.findByText("Set up Slack").should("not.exist");
|
|
cy.findByText("Add a webhook").should("not.exist");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("with a webhook", { tags: ["@external"] }, () => {
|
|
beforeEach(() => {
|
|
H.setupNotificationChannel({
|
|
name: "Foo Hook",
|
|
description: "This is a hook",
|
|
});
|
|
H.setupNotificationChannel({
|
|
name: "Bar Hook",
|
|
description: "This is another hook",
|
|
});
|
|
});
|
|
|
|
it("should be able to create and delete alerts with webhooks enabled", () => {
|
|
H.visitQuestion(ORDERS_QUESTION_ID);
|
|
|
|
cy.findByLabelText("Move, trash, and more…").click();
|
|
H.popover().findByText("Create an alert").click();
|
|
|
|
H.addNotificationHandlerChannel("Bar Hook");
|
|
|
|
H.selectScheduleTime();
|
|
cy.findByRole("button", { name: "Done" }).click();
|
|
|
|
H.notificationList().findByText("Your alert is all set up.");
|
|
|
|
cy.findByLabelText("Move, trash, and more…").click();
|
|
H.popover().findByText("Edit alerts").click();
|
|
|
|
H.modal().within(() => {
|
|
cy.findByText("Edit alerts").should("be.visible");
|
|
|
|
cy.icon("webhook").should("be.visible");
|
|
|
|
cy.findByText(/Created by you/)
|
|
.should("be.visible")
|
|
.realHover();
|
|
|
|
cy.icon("trash").click();
|
|
});
|
|
|
|
cy.findByRole("button", { name: "Delete it" }).click();
|
|
H.notificationList().findByText("The alert was successfully deleted.");
|
|
|
|
// delete modal should close
|
|
H.modal().should("not.exist");
|
|
});
|
|
});
|
|
|
|
it("should not be offered for models (metabase#37893)", () => {
|
|
H.visitModel(ORDERS_MODEL_ID);
|
|
cy.findByTestId("view-footer").within(() => {
|
|
cy.findByTestId("question-row-count")
|
|
.should("have.text", "Showing first 2,000 rows")
|
|
.and("be.visible");
|
|
cy.icon("download").should("exist");
|
|
});
|
|
|
|
H.sharingMenuButton().should("not.exist");
|
|
|
|
cy.findByLabelText("Move, trash, and more…").click();
|
|
H.popover().within(() => {
|
|
cy.findByText("Turn back to saved question").should("be.visible");
|
|
cy.findByText("Create an alert").should("not.exist");
|
|
cy.findByText("Edit alerts").should("not.exist");
|
|
});
|
|
});
|
|
|
|
it("can set up an alert for a question saved in a dashboard", () => {
|
|
H.setupSMTP();
|
|
|
|
H.createQuestion(
|
|
{
|
|
name: "Total Orders",
|
|
database_id: SAMPLE_DATABASE.id,
|
|
dashboard_id: ORDERS_DASHBOARD_ID,
|
|
query: {
|
|
"source-table": SAMPLE_DATABASE.ORDERS_ID,
|
|
aggregation: [["count"]],
|
|
},
|
|
display: "scalar",
|
|
},
|
|
{ visitQuestion: true },
|
|
);
|
|
|
|
cy.findByLabelText("Move, duplicate, and more…").click();
|
|
H.popover().findByText("Create an alert").click();
|
|
H.selectScheduleTime();
|
|
H.modal().button("Done").click();
|
|
|
|
cy.findByLabelText("Move, duplicate, and more…").click();
|
|
H.popover().findByText("Edit alerts").click();
|
|
H.modal().within(() => {
|
|
cy.findByText("Edit alerts").should("be.visible");
|
|
cy.findByText(/Created by you/).should("be.visible");
|
|
});
|
|
});
|
|
|
|
describe("approved domains (EE)", { tags: "@external" }, () => {
|
|
const allowedDomain = "metabase.test";
|
|
const deniedDomain = "metabase.example";
|
|
const deniedEmail = `mailer@${deniedDomain}`;
|
|
// We're not exposing allowed domains to normal users.
|
|
const normalUserAlertError = `Failed save alert. The following email addresses are not allowed: ${deniedEmail}`;
|
|
const normalUserSubscriptionError = `Cannot create subscription. The following email addresses are not allowed: ${deniedEmail} Please contact your administrator.`;
|
|
const adminAlertError = `You're only allowed to email alerts to addresses ending in ${allowedDomain}`;
|
|
const adminSubscriptionError = `You're only allowed to email subscriptions to addresses ending in ${allowedDomain}`;
|
|
|
|
function addEmailRecipient(email) {
|
|
cy.findByTestId("token-field")
|
|
.findByRole("combobox")
|
|
.click()
|
|
.type(`${email}`)
|
|
.blur();
|
|
}
|
|
|
|
function setAllowedDomains() {
|
|
H.updateSetting("subscription-allowed-domains", allowedDomain);
|
|
}
|
|
|
|
beforeEach(() => {
|
|
H.activateToken("pro-self-hosted");
|
|
H.setupSMTP();
|
|
setAllowedDomains();
|
|
});
|
|
|
|
it("should validate approved email domains for a question alert and a dashboard subscription (metabase#17977)", () => {
|
|
H.visitQuestion(ORDERS_QUESTION_ID);
|
|
|
|
cy.findByLabelText("Move, trash, and more…").click();
|
|
H.popover().findByText("Create an alert").click();
|
|
|
|
H.selectScheduleTime();
|
|
H.modal().within(() => {
|
|
cy.findByText("New alert").should("be.visible");
|
|
|
|
addEmailRecipient(deniedEmail);
|
|
|
|
cy.findByText(adminAlertError);
|
|
cy.button("Done").should("be.disabled");
|
|
});
|
|
|
|
H.visitDashboard(ORDERS_DASHBOARD_ID);
|
|
H.toggleDashboardSubscriptionsSidebar();
|
|
|
|
H.sidebar().within(() => {
|
|
cy.findByText("Email it").click();
|
|
addEmailRecipient(deniedEmail);
|
|
|
|
// Reproduces metabase#17977
|
|
cy.button("Send email now").should("be.disabled");
|
|
cy.button("Done").should("be.disabled");
|
|
cy.findByText(adminSubscriptionError);
|
|
});
|
|
});
|
|
|
|
it("should not display the list of approved domains for non-admins (metabase#57138)", () => {
|
|
cy.signInAsNormalUser();
|
|
H.visitQuestion(ORDERS_QUESTION_ID);
|
|
|
|
cy.findByLabelText("Move, trash, and more…").click();
|
|
H.popover().findByText("Create an alert").click();
|
|
H.selectScheduleTime();
|
|
H.modal().within(() => {
|
|
cy.findByText("New alert").should("be.visible");
|
|
|
|
addEmailRecipient(deniedEmail);
|
|
|
|
cy.button("Done").click();
|
|
});
|
|
cy.findByTestId("toast-undo").within(() => {
|
|
cy.root().should("have.text", normalUserAlertError);
|
|
});
|
|
|
|
H.visitDashboard(ORDERS_DASHBOARD_ID);
|
|
H.toggleDashboardSubscriptionsSidebar();
|
|
|
|
H.sidebar().within(() => {
|
|
addEmailRecipient(deniedEmail);
|
|
|
|
cy.button("Done").click();
|
|
});
|
|
cy.findByTestId("toast-undo").within(() => {
|
|
cy.root().should("have.text", normalUserSubscriptionError);
|
|
});
|
|
});
|
|
});
|
|
});
|