* 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>
343 lines
8.7 KiB
JavaScript
343 lines
8.7 KiB
JavaScript
const { H } = cy;
|
|
import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
|
|
|
|
const { PRODUCTS, ORDERS_ID } = SAMPLE_DATABASE;
|
|
|
|
const questionDetails = {
|
|
name: "sql param",
|
|
native: {
|
|
query: "select count(*) from products where {{c}}",
|
|
"template-tags": {
|
|
c: {
|
|
id: "e126f242-fbaa-1feb-7331-21ac59f021cc",
|
|
name: "c",
|
|
"display-name": "Category",
|
|
type: "dimension",
|
|
dimension: ["field", PRODUCTS.CATEGORY, null],
|
|
default: null,
|
|
"widget-type": "category",
|
|
},
|
|
},
|
|
},
|
|
display: "scalar",
|
|
};
|
|
|
|
const textFilter = {
|
|
id: "1",
|
|
type: "string/=",
|
|
name: "Text",
|
|
slug: "text",
|
|
sectionId: "string",
|
|
};
|
|
|
|
const unusedFilter = {
|
|
id: "2",
|
|
type: "number/=",
|
|
name: "Number",
|
|
slug: "number",
|
|
sectionId: "number",
|
|
};
|
|
|
|
const tab1 = {
|
|
id: 1,
|
|
name: "Tab 1",
|
|
};
|
|
|
|
const tab2 = {
|
|
id: 2,
|
|
name: "Tab 2",
|
|
};
|
|
|
|
const dashboardDetails = {
|
|
parameters: [textFilter, unusedFilter],
|
|
tabs: [tab1, tab2],
|
|
};
|
|
|
|
const COUNT_ALL = "200";
|
|
const COUNT_DOOHICKEY = "42";
|
|
|
|
const USERS = {
|
|
"admin user": () => cy.signInAsAdmin(),
|
|
"user with no permissions": () => cy.signIn("none"),
|
|
"anonymous user": () => cy.signOut(),
|
|
};
|
|
|
|
const prepareDashboard = () => {
|
|
H.createNativeQuestionAndDashboard({
|
|
questionDetails,
|
|
dashboardDetails,
|
|
}).then(
|
|
({
|
|
body: { id, card_id, dashboard_id, dashboard_tab_id },
|
|
dashboardTabs,
|
|
}) => {
|
|
cy.wrap(dashboard_id).as("dashboardId");
|
|
// Connect filter to the card
|
|
cy.request("PUT", `/api/dashboard/${dashboard_id}`, {
|
|
tabs: dashboardTabs,
|
|
dashcards: [
|
|
{
|
|
id,
|
|
dashboard_tab_id,
|
|
card_id,
|
|
row: 0,
|
|
col: 0,
|
|
size_x: 8,
|
|
size_y: 6,
|
|
parameter_mappings: [
|
|
{
|
|
parameter_id: textFilter.id,
|
|
card_id,
|
|
target: ["dimension", ["template-tag", "c"]],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
},
|
|
);
|
|
};
|
|
|
|
describe("scenarios > public > dashboard", () => {
|
|
beforeEach(() => {
|
|
H.restore();
|
|
cy.signInAsAdmin();
|
|
|
|
prepareDashboard();
|
|
});
|
|
|
|
Object.entries(USERS).map(([userType, setUser]) =>
|
|
describe(`${userType}`, () => {
|
|
it("should be able to view public dashboards", () => {
|
|
cy.get("@dashboardId").then((id) => {
|
|
cy.request("POST", `/api/dashboard/${id}/public_link`).then(
|
|
({ body: { uuid } }) => {
|
|
setUser();
|
|
cy.visit(`/public/dashboard/${uuid}`);
|
|
},
|
|
);
|
|
});
|
|
|
|
cy.findByTestId("scalar-value").should("have.text", COUNT_ALL);
|
|
|
|
H.filterWidget().click();
|
|
H.popover().within(() => {
|
|
cy.findByText("Doohickey").click();
|
|
cy.button("Add filter").click();
|
|
});
|
|
|
|
cy.findByTestId("scalar-value").should("have.text", COUNT_DOOHICKEY);
|
|
});
|
|
}),
|
|
);
|
|
|
|
it("should respect 'disable auto-apply filters' in a public dashboard", () => {
|
|
cy.get("@dashboardId").then((id) => {
|
|
cy.request("PUT", `/api/dashboard/${id}`, {
|
|
auto_apply_filters: false,
|
|
});
|
|
|
|
H.visitPublicDashboard(id);
|
|
});
|
|
|
|
cy.findByTestId("scalar-value").should("have.text", COUNT_ALL);
|
|
H.applyFilterToast().should("not.exist");
|
|
|
|
H.filterWidget().click();
|
|
H.popover().within(() => {
|
|
cy.findByText("Doohickey").click();
|
|
cy.button("Add filter").click();
|
|
});
|
|
|
|
H.applyFilterButton().should("be.visible");
|
|
cy.findByTestId("scalar-value").should("have.text", COUNT_ALL);
|
|
|
|
H.applyFilterButton().click();
|
|
H.applyFilterToast().should("not.exist");
|
|
cy.findByTestId("scalar-value").should("have.text", COUNT_DOOHICKEY);
|
|
});
|
|
|
|
it("should only display filters mapped to cards on the selected tab", () => {
|
|
cy.get("@dashboardId").then((id) => {
|
|
H.visitPublicDashboard(id);
|
|
});
|
|
|
|
// new dashboards should default to 'fixed' width
|
|
H.assertDashboardFixedWidth();
|
|
|
|
H.dashboardParametersContainer().within(() => {
|
|
cy.findByText(textFilter.name).should("be.visible");
|
|
cy.findByText(unusedFilter.name).should("not.exist");
|
|
});
|
|
|
|
H.goToTab(tab2.name);
|
|
|
|
H.dashboardParametersContainer().should("not.exist");
|
|
cy.findByTestId("embed-frame").within(() => {
|
|
cy.findByText(textFilter.name).should("not.exist");
|
|
cy.findByText(unusedFilter.name).should("not.exist");
|
|
});
|
|
});
|
|
|
|
it("should render when a filter passed with value starting from '0', support #theme=dark, and respect full width (metabase#41483, metabase#65731)", () => {
|
|
cy.get("@dashboardId").then((id) => {
|
|
cy.request("PUT", `/api/dashboard/${id}`, {
|
|
width: "full",
|
|
});
|
|
H.visitPublicDashboard(id, {
|
|
params: { text: "002" },
|
|
hash: {
|
|
theme: "dark",
|
|
},
|
|
});
|
|
});
|
|
|
|
cy.url().should("include", "text=002");
|
|
|
|
H.filterWidget().findByText("002").should("be.visible");
|
|
|
|
cy.log("dark theme should have white text");
|
|
cy.findByRole("heading", {
|
|
name: "Test Dashboard",
|
|
}).should("have.css", "color", "rgba(255, 255, 255, 0.95)");
|
|
|
|
H.assertDashboardFullWidth();
|
|
});
|
|
|
|
it("should respect click behavior", () => {
|
|
H.createDashboardWithQuestions({
|
|
dashboardName: "test click behavior",
|
|
questions: [
|
|
{
|
|
name: "orders",
|
|
query: {
|
|
"source-table": ORDERS_ID,
|
|
limit: 5,
|
|
},
|
|
},
|
|
],
|
|
cards: [
|
|
{
|
|
visualization_settings: {
|
|
column_settings: {
|
|
'["name","TOTAL"]': {
|
|
click_behavior: {
|
|
type: "link",
|
|
linkType: "url",
|
|
linkTemplate: "https://metabase.com",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
],
|
|
}).then(({ dashboard }) => {
|
|
H.visitPublicDashboard(dashboard.id);
|
|
});
|
|
|
|
// This is a hacky way to intercept the link click we create an a element
|
|
// with href on fly and remove it afterwards in lib/dom.js
|
|
cy.window().then((win) => {
|
|
cy.spy(win.document.body, "appendChild").as("appendChild");
|
|
});
|
|
|
|
H.getDashboardCard().findByText("39.72").click();
|
|
|
|
cy.get("@appendChild").then((appendChild) => {
|
|
// last call is a link
|
|
const element = appendChild.lastCall.args[0];
|
|
|
|
expect(element.tagName).to.eq("A");
|
|
expect(element.href).to.eq("https://metabase.com/");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("scenarios [EE] > public > dashboard", () => {
|
|
beforeEach(() => {
|
|
H.restore();
|
|
cy.signInAsAdmin();
|
|
|
|
prepareDashboard();
|
|
|
|
H.activateToken("pro-self-hosted");
|
|
});
|
|
|
|
it("should set the window title to `{dashboard name} · {application name}`, keep the background via `#background=false` without an iframe, and disable it inside an iframe (metabase#62391)", () => {
|
|
H.updateSetting("application-name", "Custom Application Name");
|
|
|
|
cy.get("@dashboardId").then((id) => {
|
|
H.visitPublicDashboard(id, {
|
|
hash: { background: "false" },
|
|
});
|
|
|
|
cy.title().should("eq", "Test Dashboard · Custom Application Name");
|
|
});
|
|
|
|
cy.findByTestId("embed-frame").should("exist");
|
|
|
|
cy.get("body.mb-wrapper").should(
|
|
"not.have.css",
|
|
"background-color",
|
|
"rgba(0, 0, 0, 0)",
|
|
);
|
|
|
|
cy.signInAsAdmin();
|
|
cy.visit("/");
|
|
|
|
cy.get("@dashboardId").then((id) => {
|
|
H.visitPublicDashboard(id, {
|
|
hash: { background: "false" },
|
|
onBeforeLoad: (window) => {
|
|
window.overrideIsWithinIframe = true;
|
|
},
|
|
});
|
|
});
|
|
|
|
cy.findByTestId("embed-frame").should("exist");
|
|
|
|
cy.get("body.mb-wrapper").should(
|
|
"have.css",
|
|
"background-color",
|
|
"rgba(0, 0, 0, 0)",
|
|
);
|
|
|
|
cy.window().then((win) => {
|
|
delete win.overrideIsWithinIframe;
|
|
});
|
|
});
|
|
|
|
it("should allow to set locale from the `#locale` hash parameter (metabase#50182)", () => {
|
|
// We don't have a de-CH.json file, so it should fallback to de.json, see metabase#51039 for more details
|
|
cy.intercept("GET", "**/locale-de-json*.js").as("deLocale");
|
|
|
|
cy.get("@dashboardId").then((id) => {
|
|
H.visitPublicDashboard(id, {
|
|
hash: { locale: "de-CH" },
|
|
});
|
|
});
|
|
|
|
cy.wait("@deLocale");
|
|
|
|
cy.findByRole("button", {
|
|
name: "Automatische Aktualisierung",
|
|
}).should("exist");
|
|
|
|
cy.url().should("include", "locale=de");
|
|
});
|
|
|
|
it("should handle /api/session/properties incorrect response (metabase#62501)", () => {
|
|
cy.intercept("/api/session/properties", {
|
|
statusCode: 200,
|
|
headers: {},
|
|
body: "<html><body><h1>Those aren't the droids you're looking for</h1></body></html>",
|
|
});
|
|
|
|
cy.get("@dashboardId").then(H.visitPublicDashboard);
|
|
|
|
cy.findByTestId("embed-frame").within(() => {
|
|
cy.findByText("Test Dashboard").should("exist");
|
|
});
|
|
});
|
|
});
|