Files
metabase/e2e/test/scenarios/sharing/public-resource-downloads.cy.spec.ts
Chris Vandoodewaard 519834f9ee Consolidate sharing e2e tests (#83657)
* 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>
2026-10-06 17:39:59 -04:00

308 lines
8.7 KiB
TypeScript

const { H } = cy;
import {
ORDERS_BY_YEAR_QUESTION_ID,
ORDERS_DASHBOARD_DASHCARD_ID,
ORDERS_DASHBOARD_ID,
} from "e2e/support/cypress_sample_instance_data";
/** These tests are about the `downloads` flag for public dashboards and questions.
* Unless the product changes, these should test the same things as `embed-resource-downloads.cy.spec.ts`
*/
describe("Public dashboards/questions downloads (results and pdf)", () => {
beforeEach(() => {
H.resetSnowplow();
cy.deleteDownloadsFolder();
});
describe("Public dashboards", () => {
let publicLink: string;
before(() => {
H.restore("default");
cy.signInAsAdmin();
H.activateToken("pro-self-hosted");
cy.visit(`/dashboard/${ORDERS_DASHBOARD_ID}`);
H.openSharingMenu("Create a public link");
H.popover()
.findByTestId("public-link-input")
.should("contain.value", "/public/")
.invoke("val")
.then((url) => {
// Unjustified type cast. FIXME
publicLink = url as string;
});
cy.signOut();
});
afterEach(() => {
H.expectNoBadSnowplowEvents();
});
it("#downloads=false should disable both PDF downloads and dashcard results downloads", () => {
cy.visit(`${publicLink}#downloads=false`);
waitLoading();
cy.findByRole("heading", { name: "Orders in a dashboard" }).should(
"be.visible",
);
H.getDashboardCard()
.findAllByTestId("cell-data")
.should("have.length.above", 0);
cy.findByRole("button", { name: "Download as PDF" }).should("not.exist");
// we should not have any dashcard action in a public/embed scenario, so the menu should not be there
H.getEmbeddedDashboardCardMenu().should("not.exist");
});
it("#downloads=pdf should enable only PDF downloads and allow downloading a public dashboard as PDF", () => {
cy.visit(`${publicLink}#downloads=pdf`);
waitLoading();
cy.get("header")
.findByRole("button", { name: "Download as PDF" })
.should("exist");
H.getDashboardCard()
.findAllByTestId("cell-data")
.should("have.length.above", 0);
H.getEmbeddedDashboardCardMenu().should("not.exist");
cy.get("header")
.findByRole("button", { name: "Download as PDF" })
.click();
cy.verifyDownload("Orders in a dashboard.pdf");
H.expectUnstructuredSnowplowEvent({
event: "dashboard_pdf_exported",
dashboard_id: 0,
dashboard_accessed_via: "public-link",
});
});
it("should enable both downloads by default, allow downloading a public dashcard as CSV, and #downloads=results should enable only dashcard results downloads", () => {
cy.visit(publicLink);
waitLoading();
cy.get("header")
.findByRole("button", { name: "Download as PDF" })
.should("exist");
const uuid = publicLink.split("/").at(-1);
H.downloadAndAssert({
publicUuid: uuid,
fileType: "csv",
questionId: ORDERS_BY_YEAR_QUESTION_ID,
isDashboard: true,
isEmbed: true,
dashcardId: ORDERS_DASHBOARD_DASHCARD_ID,
});
H.expectUnstructuredSnowplowEvent({
event: "download_results_clicked",
resource_type: "dashcard",
accessed_via: "public-link",
export_type: "csv",
});
cy.visit(`${publicLink}#downloads=results`);
cy.reload();
waitLoading();
H.getDashboardCard()
.findAllByTestId("cell-data")
.should("have.length.above", 0);
cy.get("header")
.findByRole("button", { name: "Download as PDF" })
.should("not.exist");
H.main().realHover();
H.getEmbeddedDashboardCardMenu().click();
cy.findByLabelText("Download results").should("be.visible");
});
it("#downloads=results, pdf should handle whitespace between parameters", () => {
cy.visit(`${publicLink}#downloads=results, pdf`);
waitLoading();
cy.get("header")
.findByRole("button", { name: "Download as PDF" })
.should("exist");
H.main().realHover();
H.getEmbeddedDashboardCardMenu().click();
cy.findByLabelText("Download results").should("be.visible");
});
});
describe("Public questions", () => {
let publicLink: string;
before(() => {
H.restore("default");
cy.signInAsAdmin();
H.activateToken("pro-self-hosted");
cy.visit(`/question/${ORDERS_BY_YEAR_QUESTION_ID}`);
H.openSharingMenu("Create a public link");
H.popover()
.findByTestId("public-link-input")
.should("contain.value", "/public/")
.invoke("val")
.then((url) => {
// Unjustified type cast. FIXME
publicLink = url as string;
});
cy.signOut();
});
it("#downloads=false should disable result downloads", () => {
cy.visit(`${publicLink}#downloads=false`);
waitLoading();
H.echartsContainer().should("be.visible");
H.main().realHover();
cy.findByRole("button", { name: "Download results" }).should("not.exist");
});
it("should be able to download the question as PNG", () => {
cy.visit(`${publicLink}`);
waitLoading();
cy.findByRole("button", { name: "Download results" }).click();
H.popover().within(() => {
cy.findByText(".png").click();
cy.findByTestId("download-results-button").click();
});
cy.verifyDownload(".png", { contains: true });
H.expectUnstructuredSnowplowEvent({
event: "download_results_clicked",
resource_type: "question",
accessed_via: "public-link",
export_type: "png",
});
});
it("#downloads=results should enable result downloads and allow downloading a public card as CSV", () => {
cy.visit(`${publicLink}#downloads=results`);
waitLoading();
H.main().realHover();
cy.findByLabelText("Download results").should("be.visible");
const uuid = publicLink.split("/").at(-1);
H.downloadAndAssert({
publicUuid: uuid,
fileType: "csv",
questionId: ORDERS_BY_YEAR_QUESTION_ID,
isDashboard: false,
isEmbed: true,
});
H.expectUnstructuredSnowplowEvent({
event: "download_results_clicked",
resource_type: "question",
accessed_via: "public-link",
export_type: "csv",
});
});
});
describe("Public questions with parameters", () => {
before(() => {
H.restore("default");
cy.signInAsAdmin();
H.activateToken("pro-self-hosted");
H.createNativeQuestion(
{
native: {
query: "SELECT * FROM orders WHERE TOTAL > {{ minimum }}",
"template-tags": {
minimum: {
id: "930e4001",
name: "minimum",
"display-name": "Minimum",
type: "number",
default: "10",
},
},
},
parameters: [
{
id: "930e4001",
slug: "minimum",
name: "minimum",
type: "number",
default: 10,
target: ["variable", ["template-tag", "minimum"]],
},
],
},
{
visitQuestion: true,
},
);
H.openSharingMenu("Create a public link");
H.popover()
.findByTestId("public-link-input")
.should("contain.value", "/public/")
.invoke("val")
.then((url) => {
if (typeof url === "string") {
cy.signOut();
cy.visit(url);
}
});
});
it("should not pass all the parameters to the public link", () => {
waitLoading();
H.main().realHover();
cy.findByLabelText("Download results").should("be.visible");
cy.location("pathname").then((pathname) => {
const uuid = pathname.split("/").at(-1);
H.downloadAndAssert({
publicUuid: uuid,
fileType: "csv",
isDashboard: false,
isEmbed: true,
});
cy.get<{ request: Request }>("@fileDownload").then(({ request }) => {
const url = new URL(request.url);
const parameters = JSON.parse(
url.searchParams.get("parameters") ?? "[]",
);
cy.wrap(parameters).should("have.length", 1);
cy.wrap(parameters[0]).should("have.property", "id");
cy.wrap(parameters[0]).should("have.property", "value");
cy.wrap(parameters[0]).should("not.have.property", "type");
cy.wrap(parameters[0]).should("not.have.property", "target");
});
});
});
});
});
const waitLoading = () => H.main().findByText("Loading...").should("not.exist");