* 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>
919 lines
28 KiB
JavaScript
919 lines
28 KiB
JavaScript
import {
|
|
embedModalContent,
|
|
legacyStaticEmbeddingButton,
|
|
openSharingMenu,
|
|
} from "e2e/support/helpers";
|
|
|
|
const { H } = cy;
|
|
|
|
["dashboard", "question"].forEach((resource) => {
|
|
describe(`embed modal behavior for ${resource}s`, () => {
|
|
beforeEach(() => {
|
|
H.restore();
|
|
cy.signInAsAdmin();
|
|
|
|
createResource(resource).then(({ body: { id } }) => {
|
|
cy.wrap(id).as("resourceId");
|
|
});
|
|
});
|
|
|
|
describe("when embedding and public sharing are disabled", () => {
|
|
beforeEach(() => {
|
|
H.updateSetting("enable-public-sharing", false);
|
|
H.updateSetting("enable-embedding-static", false);
|
|
});
|
|
|
|
it(`should hide the public link option for ${resource} and still let an admin open the embed modal`, () => {
|
|
cy.get("@resourceId").then((id) => {
|
|
visitResource(resource, id);
|
|
});
|
|
|
|
H.openSharingMenu();
|
|
|
|
H.sharingMenu().within(() => {
|
|
cy.findByRole("button", { name: "Embed" })
|
|
.should("be.visible")
|
|
.and("be.enabled");
|
|
cy.findByText(/public link/i).should("not.exist");
|
|
cy.findByText("Enable").should("not.exist");
|
|
});
|
|
|
|
H.sharingMenu().findByRole("button", { name: "Embed" }).click();
|
|
|
|
H.embedModalContent().should("be.visible");
|
|
|
|
cy.signInAsNormalUser();
|
|
cy.get("@resourceId").then((id) => {
|
|
visitResource(resource, id);
|
|
});
|
|
|
|
assertNonAdminCannotCreatePublicLink(resource);
|
|
});
|
|
});
|
|
|
|
describe("when embedding and public sharing are enabled", () => {
|
|
beforeEach(() => {
|
|
H.updateSetting("enable-public-sharing", true);
|
|
H.updateSetting("enable-embedding-static", true);
|
|
});
|
|
|
|
describe("when user is non-admin", () => {
|
|
it(`should let a non-admin copy the existing public link for a ${resource}`, () => {
|
|
cy.get("@resourceId").then((id) => {
|
|
createPublicResourceLink(resource, id);
|
|
});
|
|
|
|
cy.signInAsNormalUser();
|
|
|
|
cy.get("@resourceId").then((id) => {
|
|
visitResource(resource, id);
|
|
});
|
|
|
|
cy.window().then((win) => {
|
|
cy.stub(win.navigator.clipboard, "writeText")
|
|
.as("copyLink")
|
|
.resolves();
|
|
});
|
|
|
|
H.openSharingMenu("Copy public link");
|
|
H.tooltip().findByText("Public link copied to clipboard");
|
|
|
|
cy.get("@copyLink").should((stub) => {
|
|
expect(stub.firstCall.args[0]).to.match(
|
|
new RegExp(`/public/${resource}/`),
|
|
);
|
|
});
|
|
cy.findByTestId("public-link-popover-content").should("not.exist");
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Embed JS modal display", () => {
|
|
beforeEach(() => {
|
|
H.restore();
|
|
cy.signInAsAdmin();
|
|
|
|
createResource("dashboard").then(({ body: { id } }) => {
|
|
cy.wrap(id).as("dashboardId");
|
|
});
|
|
});
|
|
|
|
describe("when the user has a paid instance", () => {
|
|
it("should open Embed JS modal with the `enable simple embedding` card", () => {
|
|
H.activateToken("pro-self-hosted");
|
|
H.visitDashboard("@dashboardId");
|
|
|
|
H.openSharingMenu("Embed");
|
|
|
|
cy.findByLabelText("Metabase account (SSO)").click();
|
|
|
|
H.embedModalEnableEmbeddingCard().within(() => {
|
|
cy.findByText(/modular embedding/).should("be.visible");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("when the user has an OSS instance", () => {
|
|
it(
|
|
"should display a link to the product page for embedded analytics",
|
|
{ tags: "@OSS" },
|
|
() => {
|
|
H.visitDashboard("@dashboardId");
|
|
H.openSharingMenu("Embed");
|
|
|
|
H.embedModalContent().should("be.visible");
|
|
H.embedModalContent()
|
|
.findByTestId("upsell-card")
|
|
.scrollIntoView()
|
|
.within(() => {
|
|
cy.findByText("Get more powerful embedding").should("be.visible");
|
|
cy.findByText("Upgrade to Metabase Pro").should("be.visible");
|
|
});
|
|
},
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("#39152 sharing an unsaved question", () => {
|
|
beforeEach(() => {
|
|
H.restore();
|
|
cy.signInAsAdmin();
|
|
H.updateSetting("enable-public-sharing", true);
|
|
});
|
|
|
|
it("should ask the user to save the question before creating a public link", () => {
|
|
H.startNewQuestion();
|
|
H.miniPickerBrowseAll().click();
|
|
H.pickEntity({ path: ["Databases", "Sample Database", "People"] });
|
|
H.visualize();
|
|
|
|
H.openSharingMenu();
|
|
|
|
H.modal().within(() => {
|
|
cy.findByText("First, save your question").should("be.visible");
|
|
cy.findByText("Save").click();
|
|
});
|
|
|
|
H.openSharingMenu("Create a public link");
|
|
|
|
assertValidPublicLink({ resource: "question", shouldHaveRemoveLink: true });
|
|
});
|
|
});
|
|
|
|
[
|
|
{
|
|
resource: "dashboard",
|
|
apiPath: "dashboard",
|
|
},
|
|
{
|
|
resource: "question",
|
|
apiPath: "card",
|
|
},
|
|
].forEach(({ resource, apiPath }) => {
|
|
describe(`legacy static modal behavior for ${resource}`, () => {
|
|
beforeEach(() => {
|
|
H.restore();
|
|
cy.signInAsAdmin();
|
|
|
|
createResource(resource).then(({ body }) => {
|
|
cy.wrap(body.id).as("resourceId");
|
|
});
|
|
});
|
|
|
|
it("should show the legacy alert for a null embedding type but not for guest-embed", () => {
|
|
cy.get("@resourceId").then((id) => {
|
|
visitResource(resource, id);
|
|
|
|
cy.request("PUT", `/api/${apiPath}/${id}`, {
|
|
enable_embedding: true,
|
|
embedding_type: null,
|
|
});
|
|
|
|
openSharingMenu("Embed");
|
|
|
|
embedModalContent().should("exist");
|
|
legacyStaticEmbeddingButton().should("exist");
|
|
|
|
cy.intercept({
|
|
method: "GET",
|
|
pathname: `/api/${apiPath}/${id}`,
|
|
}).as("getResource");
|
|
|
|
embedModalContent().findByLabelText("Close").click();
|
|
embedModalContent().should("not.exist");
|
|
|
|
cy.request("PUT", `/api/${apiPath}/${id}`, {
|
|
enable_embedding: true,
|
|
embedding_type: "guest-embed",
|
|
});
|
|
|
|
openSharingMenu("Embed");
|
|
cy.wait("@getResource");
|
|
|
|
embedModalContent().should("exist");
|
|
legacyStaticEmbeddingButton().should("not.exist");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe(`public ${resource} sharing snowplow events`, () => {
|
|
beforeEach(() => {
|
|
H.restore();
|
|
H.resetSnowplow();
|
|
cy.signInAsAdmin();
|
|
H.enableTracking();
|
|
|
|
createResource(resource).then(({ body }) => {
|
|
cy.wrap(body.id).as("resourceId");
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
H.expectNoBadSnowplowEvents();
|
|
});
|
|
|
|
describe(`when embedding ${resource}`, () => {
|
|
describe("when interacting with public link popover", () => {
|
|
it("should send `public_link_copied` and `public_link_removed` events and show an existing public link", () => {
|
|
cy.get("@resourceId").then((id) => {
|
|
visitResource(resource, id);
|
|
});
|
|
|
|
H.openSharingMenu(/public link/i);
|
|
cy.findByTestId("copy-button").realClick();
|
|
if (resource === "dashboard") {
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "public_link_copied",
|
|
artifact: "dashboard",
|
|
format: null,
|
|
});
|
|
}
|
|
|
|
if (resource === "question") {
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "public_link_copied",
|
|
artifact: "question",
|
|
format: "html",
|
|
});
|
|
|
|
H.popover().findByText("csv").click();
|
|
cy.findByTestId("copy-button").realClick();
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "public_link_copied",
|
|
artifact: "question",
|
|
format: "csv",
|
|
});
|
|
|
|
H.popover().findByText("xlsx").click();
|
|
cy.findByTestId("copy-button").realClick();
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "public_link_copied",
|
|
artifact: "question",
|
|
format: "xlsx",
|
|
});
|
|
|
|
H.popover().findByText("json").click();
|
|
cy.findByTestId("copy-button").realClick();
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "public_link_copied",
|
|
artifact: "question",
|
|
format: "json",
|
|
});
|
|
}
|
|
|
|
H.popover().button("Remove public link").click();
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "public_link_removed",
|
|
artifact: resource,
|
|
source: "public-share",
|
|
});
|
|
|
|
cy.get("@resourceId").then((id) => {
|
|
createPublicResourceLink(resource, id);
|
|
visitResource(resource, id);
|
|
});
|
|
|
|
H.openSharingMenu(/public link/i);
|
|
|
|
assertValidPublicLink({ resource, shouldHaveRemoveLink: true });
|
|
});
|
|
});
|
|
|
|
describe("when interacting with static embedding", () => {
|
|
it("should send `static_embed_code_copied` when copying the static embed code", () => {
|
|
cy.get("@resourceId").then((id) => {
|
|
visitResource(resource, id);
|
|
|
|
H.openLegacyStaticEmbeddingModal({ resource, resourceId: id });
|
|
});
|
|
|
|
cy.log("Assert copying codes in Overview tab");
|
|
cy.findByTestId("embed-backend")
|
|
.findByTestId("copy-button")
|
|
.realClick();
|
|
|
|
// TODO: fix this test, it's flaky on CI
|
|
/*H.expectUnstructuredSnowplowEvent({
|
|
event: "static_embed_code_copied",
|
|
artifact: resource,
|
|
language: "node",
|
|
location: "code_overview",
|
|
code: "backend",
|
|
appearance: {
|
|
background: true,
|
|
bordered: true,
|
|
titled: true,
|
|
font: "instance",
|
|
theme: "light",
|
|
downloads: null,
|
|
},
|
|
});*/
|
|
|
|
cy.findByTestId("embed-frontend")
|
|
.findByTestId("copy-button")
|
|
.realClick();
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "static_embed_code_copied",
|
|
artifact: resource,
|
|
language: "pug",
|
|
location: "code_overview",
|
|
code: "view",
|
|
appearance: {
|
|
background: true,
|
|
bordered: true,
|
|
titled: true,
|
|
font: "instance",
|
|
theme: "light",
|
|
downloads: null,
|
|
},
|
|
});
|
|
|
|
cy.log("Assert copying code in Parameters tab");
|
|
H.modal().within(() => {
|
|
cy.findByRole("tab", { name: "Parameters" }).click();
|
|
|
|
cy.findByDisplayValue("Node.js").click();
|
|
});
|
|
H.selectDropdown().findByText("Ruby").click();
|
|
cy.findByTestId("embed-backend")
|
|
.findByTestId("copy-button")
|
|
.realClick();
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "static_embed_code_copied",
|
|
artifact: resource,
|
|
language: "ruby",
|
|
location: "code_params",
|
|
code: "backend",
|
|
appearance: {
|
|
background: true,
|
|
bordered: true,
|
|
titled: true,
|
|
font: "instance",
|
|
theme: "light",
|
|
downloads: null,
|
|
},
|
|
});
|
|
|
|
cy.log("Assert copying code in Appearance tab");
|
|
H.modal().within(() => {
|
|
cy.findByRole("tab", { name: "Look and Feel" }).click();
|
|
|
|
cy.findByDisplayValue("Ruby").click();
|
|
});
|
|
|
|
H.selectDropdown().findByText("Python").click();
|
|
|
|
H.modal().within(() => {
|
|
cy.findByLabelText("Dark").click({ force: true });
|
|
if (resource === "dashboard") {
|
|
cy.findByLabelText("Dashboard title").click({ force: true });
|
|
cy.findByLabelText("Dashboard border").click({ force: true });
|
|
}
|
|
if (resource === "question") {
|
|
cy.findByLabelText("Question title").click({ force: true });
|
|
cy.findByLabelText("Question border").click({ force: true });
|
|
}
|
|
});
|
|
|
|
cy.findByTestId("embed-backend")
|
|
.findByTestId("copy-button")
|
|
.realClick();
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "static_embed_code_copied",
|
|
artifact: resource,
|
|
language: "python",
|
|
location: "code_appearance",
|
|
code: "backend",
|
|
appearance: {
|
|
background: true,
|
|
bordered: false,
|
|
titled: false,
|
|
font: "instance",
|
|
theme: "night",
|
|
downloads: null,
|
|
},
|
|
});
|
|
|
|
// Question don't have an option to disable background (metabase#43838)
|
|
if (resource === "dashboard") {
|
|
cy.findByLabelText("Dashboard background").click({ force: true });
|
|
|
|
cy.findByTestId("embed-backend")
|
|
.findByTestId("copy-button")
|
|
.realClick();
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "static_embed_code_copied",
|
|
artifact: resource,
|
|
language: "python",
|
|
location: "code_appearance",
|
|
code: "backend",
|
|
appearance: {
|
|
background: false,
|
|
bordered: false,
|
|
titled: false,
|
|
font: "instance",
|
|
theme: "night",
|
|
downloads: null,
|
|
},
|
|
});
|
|
}
|
|
});
|
|
|
|
describe("Pro/EE instances", () => {
|
|
beforeEach(() => {
|
|
H.activateToken("pro-self-hosted");
|
|
});
|
|
|
|
it("should send `static_embed_code_copied` when copying the static embed code", () => {
|
|
cy.get("@resourceId").then((id) => {
|
|
visitResource(resource, id);
|
|
|
|
H.openLegacyStaticEmbeddingModal({
|
|
resource,
|
|
resourceId: id,
|
|
});
|
|
});
|
|
|
|
// Individual download options are only supported for dashboards
|
|
if (resource === "dashboard") {
|
|
H.modal().within(() => {
|
|
cy.findByRole("tab", { name: "Look and Feel" }).click();
|
|
});
|
|
|
|
cy.log(
|
|
"Assert that it sends `enabled_download_types: { pdf: false, results: true }` when only results download is enabled",
|
|
);
|
|
|
|
// Disable PDF exports
|
|
cy.findByLabelText("Export to PDF").click({ force: true });
|
|
|
|
cy.findByTestId("embed-backend")
|
|
.findByTestId("copy-button")
|
|
.realClick();
|
|
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "static_embed_code_copied",
|
|
artifact: resource,
|
|
language: "node",
|
|
location: "code_appearance",
|
|
code: "backend",
|
|
appearance: {
|
|
background: true,
|
|
bordered: true,
|
|
titled: true,
|
|
font: "instance",
|
|
theme: "light",
|
|
enabled_download_types: { pdf: false, results: true },
|
|
},
|
|
});
|
|
|
|
cy.log(
|
|
"Assert that it sends `enabled_download_types: { pdf: true, results: false }` when only PDF is enabled",
|
|
);
|
|
|
|
// Enable PDF exports again
|
|
cy.findByLabelText("Export to PDF").click({ force: true });
|
|
|
|
// Disable results download
|
|
cy.findByLabelText("Results (csv, xlsx, json, png)").click({
|
|
force: true,
|
|
});
|
|
|
|
cy.findByTestId("embed-backend")
|
|
.findByTestId("copy-button")
|
|
.realClick();
|
|
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "static_embed_code_copied",
|
|
artifact: resource,
|
|
language: "node",
|
|
location: "code_appearance",
|
|
code: "backend",
|
|
appearance: {
|
|
background: true,
|
|
bordered: true,
|
|
titled: true,
|
|
font: "instance",
|
|
theme: "light",
|
|
enabled_download_types: { pdf: true, results: false },
|
|
},
|
|
});
|
|
|
|
cy.log("Restore the default download options");
|
|
cy.findByLabelText("Results (csv, xlsx, json, png)").click({
|
|
force: true,
|
|
});
|
|
H.modal().findByRole("tab", { name: "Overview" }).click();
|
|
}
|
|
|
|
cy.log("Assert copying codes in Overview tab");
|
|
cy.findByTestId("embed-backend")
|
|
.findByTestId("copy-button")
|
|
.realClick();
|
|
|
|
// TODO: fix this test, it's flaky on CI
|
|
/*H.expectUnstructuredSnowplowEvent({
|
|
event: "static_embed_code_copied",
|
|
artifact: resource,
|
|
language: "node",
|
|
location: "code_overview",
|
|
code: "backend",
|
|
appearance: {
|
|
background: true,
|
|
bordered: true,
|
|
titled: true,
|
|
font: "instance",
|
|
theme: "light",
|
|
enabled_download_types: { pdf: true, results: true },
|
|
},
|
|
});*/
|
|
|
|
cy.findByTestId("embed-frontend")
|
|
.findByTestId("copy-button")
|
|
.realClick();
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "static_embed_code_copied",
|
|
artifact: resource,
|
|
language: "pug",
|
|
location: "code_overview",
|
|
code: "view",
|
|
appearance: {
|
|
background: true,
|
|
bordered: true,
|
|
titled: true,
|
|
font: "instance",
|
|
theme: "light",
|
|
enabled_download_types: { pdf: true, results: true },
|
|
},
|
|
});
|
|
|
|
cy.log("Assert copying code in Parameters tab");
|
|
H.modal().within(() => {
|
|
cy.findByRole("tab", { name: "Parameters" }).click();
|
|
|
|
cy.findByDisplayValue("Node.js").click();
|
|
});
|
|
H.selectDropdown().findByText("Ruby").click();
|
|
cy.findByTestId("embed-backend")
|
|
.findByTestId("copy-button")
|
|
.realClick();
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "static_embed_code_copied",
|
|
artifact: resource,
|
|
language: "ruby",
|
|
location: "code_params",
|
|
code: "backend",
|
|
appearance: {
|
|
background: true,
|
|
bordered: true,
|
|
titled: true,
|
|
font: "instance",
|
|
theme: "light",
|
|
enabled_download_types: { pdf: true, results: true },
|
|
},
|
|
});
|
|
|
|
cy.log("Assert copying code in Appearance tab");
|
|
H.modal().within(() => {
|
|
cy.findByRole("tab", { name: "Look and Feel" }).click();
|
|
|
|
cy.findByDisplayValue("Ruby").click();
|
|
});
|
|
|
|
H.selectDropdown().findByText("Python").click();
|
|
|
|
H.modal().within(() => {
|
|
cy.findByLabelText("Dark").click({ force: true });
|
|
if (resource === "dashboard") {
|
|
cy.findByLabelText("Dashboard title").click({ force: true });
|
|
cy.findByLabelText("Dashboard border").click({ force: true });
|
|
}
|
|
if (resource === "question") {
|
|
cy.findByLabelText("Question title").click({ force: true });
|
|
cy.findByLabelText("Question border").click({ force: true });
|
|
}
|
|
cy.findByLabelText("Font").click();
|
|
});
|
|
|
|
H.popover().findByText("Oswald").click();
|
|
|
|
cy.log(
|
|
"Assert that it sends `enabled_download_types: { pdf: false, results: false }` when both are disabled",
|
|
);
|
|
H.modal()
|
|
.findByLabelText(
|
|
resource === "dashboard"
|
|
? "Results (csv, xlsx, json, png)"
|
|
: "Download (csv, xlsx, json, png)",
|
|
)
|
|
.click({ force: true });
|
|
|
|
// We have to also disable PDF exports for both to be disabled
|
|
if (resource === "dashboard") {
|
|
cy.findByLabelText("Export to PDF").click({ force: true });
|
|
}
|
|
|
|
cy.findByTestId("embed-backend")
|
|
.findByTestId("copy-button")
|
|
.realClick();
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "static_embed_code_copied",
|
|
artifact: resource,
|
|
language: "python",
|
|
location: "code_appearance",
|
|
code: "backend",
|
|
appearance: {
|
|
background: true,
|
|
bordered: false,
|
|
titled: false,
|
|
font: "custom",
|
|
theme: "night",
|
|
enabled_download_types: { pdf: false, results: false },
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should set a proper embedding_type and send `static_embed_discarded` and `static_embed_unpublished` in the static embed modal", () => {
|
|
cy.get("@resourceId").then((id) => {
|
|
visitResource(resource, id);
|
|
|
|
H.openLegacyStaticEmbeddingModal({
|
|
resource,
|
|
resourceId: id,
|
|
activeTab: "parameters",
|
|
});
|
|
});
|
|
|
|
H.publishChanges(apiPath, ({ request, response }) => {
|
|
assert.deepEqual(request.body.embedding_type, "static-legacy");
|
|
assert.deepEqual(response.body.embedding_type, "static-legacy");
|
|
});
|
|
|
|
cy.log("changing parameters, so we could discard changes");
|
|
H.modal().findByLabelText("Price").click();
|
|
H.selectDropdown().findByText("Editable").click();
|
|
|
|
cy.findByTestId("embed-modal-content-status-bar").within(() => {
|
|
cy.findByText("Discard changes").click();
|
|
});
|
|
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "static_embed_discarded",
|
|
artifact: resource,
|
|
});
|
|
|
|
H.modal().findByLabelText("Price").click();
|
|
H.selectDropdown().findByText("Editable").click();
|
|
|
|
H.publishChanges(apiPath, ({ request, response }) => {
|
|
assert.deepEqual(request.body.embedding_type, "static-legacy");
|
|
assert.deepEqual(response.body.embedding_type, "static-legacy");
|
|
});
|
|
|
|
const HOUR = 60 * 60 * 1000;
|
|
cy.clock(new Date(Date.now() + HOUR));
|
|
|
|
H.unpublishChanges(apiPath, ({ request, response }) => {
|
|
assert.deepEqual(request.body.embedding_type, null);
|
|
assert.deepEqual(response.body.embedding_type, null);
|
|
});
|
|
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "static_embed_unpublished",
|
|
artifact: resource,
|
|
time_since_creation: closeTo(toSecond(HOUR), 15),
|
|
time_since_initial_publication: closeTo(toSecond(HOUR), 15),
|
|
});
|
|
});
|
|
|
|
it("should send `static_embed_published` when publishing changes in the static embed modal", () => {
|
|
cy.then(function () {
|
|
this.timeAfterResourceCreation = Date.now();
|
|
});
|
|
cy.get("@resourceId").then((id) => {
|
|
visitResource(resource, id);
|
|
|
|
H.openLegacyStaticEmbeddingModal({ resource, resourceId: id });
|
|
});
|
|
|
|
cy.findByTestId("embed-modal-content-status-bar")
|
|
.button("Publish")
|
|
.click();
|
|
|
|
cy.then(function () {
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "static_embed_published",
|
|
artifact: resource,
|
|
new_embed: false,
|
|
time_since_creation: closeTo(
|
|
toSecond(Date.now() - this.timeAfterResourceCreation),
|
|
15,
|
|
),
|
|
time_since_initial_publication: closeTo(
|
|
toSecond(Date.now() - this.timeAfterResourceCreation),
|
|
15,
|
|
),
|
|
params: {
|
|
disabled: 3,
|
|
locked: 0,
|
|
enabled: 0,
|
|
},
|
|
});
|
|
});
|
|
|
|
cy.log("Assert `time_since_initial_publication` and `params`");
|
|
cy.findByTestId("embed-modal-content-status-bar")
|
|
.button("Unpublish")
|
|
.click();
|
|
|
|
H.modal().findByRole("tab", { name: "Parameters" }).click();
|
|
H.modal().findByLabelText("Price").click();
|
|
H.selectDropdown().findByText("Editable").click();
|
|
|
|
H.modal().findByLabelText("Category").click();
|
|
H.selectDropdown().findByText("Locked").click();
|
|
|
|
cy.then(function () {
|
|
const HOUR = 60 * 60 * 1000;
|
|
const timeAfterPublication = Date.now() + HOUR;
|
|
cy.log("Mocks the clock to 1 hour later");
|
|
cy.clock(new Date(timeAfterPublication));
|
|
cy.findByTestId("embed-modal-content-status-bar")
|
|
.button("Publish")
|
|
.click();
|
|
|
|
H.expectUnstructuredSnowplowEvent({
|
|
event: "static_embed_published",
|
|
artifact: resource,
|
|
new_embed: false,
|
|
time_since_creation: closeTo(toSecond(HOUR), 15),
|
|
time_since_initial_publication: closeTo(toSecond(HOUR), 15),
|
|
params: {
|
|
disabled: 1,
|
|
locked: 1,
|
|
enabled: 1,
|
|
},
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
function toSecond(milliseconds) {
|
|
return Math.round(milliseconds / 1000);
|
|
}
|
|
|
|
function createResource(resource) {
|
|
if (resource === "question") {
|
|
return H.createNativeQuestion({
|
|
name: "Question",
|
|
native: {
|
|
query: `
|
|
SELECT *
|
|
FROM PRODUCTS
|
|
WHERE true
|
|
[[AND created_at > {{created_at}}]]
|
|
[[AND price > {{price}}]]
|
|
[[AND category = {{category}}]]`,
|
|
"template-tags": {
|
|
date: {
|
|
type: "date",
|
|
name: "created_at",
|
|
id: "b2517f32-d2e2-4f42-ab79-c91e07e820a0",
|
|
"display-name": "Created At",
|
|
},
|
|
price: {
|
|
type: "number",
|
|
name: "price",
|
|
id: "879d1597-e673-414c-a96f-ff5887359834",
|
|
"display-name": "Price",
|
|
},
|
|
category: {
|
|
type: "text",
|
|
name: "category",
|
|
id: "1f741a9a-a95e-4ac6-b584-5101e7cf77e1",
|
|
"display-name": "Category",
|
|
},
|
|
},
|
|
},
|
|
limit: 10,
|
|
});
|
|
}
|
|
|
|
if (resource === "dashboard") {
|
|
const dateFilter = {
|
|
id: "1",
|
|
name: "Created At",
|
|
slug: "created_at",
|
|
type: "date/month-year",
|
|
};
|
|
|
|
const numberFilter = {
|
|
id: "2",
|
|
name: "Price",
|
|
slug: "price",
|
|
type: "number/=",
|
|
};
|
|
|
|
const textFilter = {
|
|
id: "3",
|
|
name: "Category",
|
|
slug: "category",
|
|
type: "string/contains",
|
|
};
|
|
|
|
return H.createDashboard({
|
|
name: "Dashboard",
|
|
parameters: [dateFilter, numberFilter, textFilter],
|
|
});
|
|
}
|
|
}
|
|
|
|
function createPublicResourceLink(resource, id) {
|
|
if (resource === "question") {
|
|
return H.createPublicQuestionLink(id);
|
|
}
|
|
if (resource === "dashboard") {
|
|
return H.createPublicDashboardLink(id);
|
|
}
|
|
}
|
|
|
|
function visitResource(resource, id) {
|
|
if (resource === "question") {
|
|
H.visitQuestion(id);
|
|
}
|
|
|
|
if (resource === "dashboard") {
|
|
H.visitDashboard(id);
|
|
}
|
|
}
|
|
|
|
function assertNonAdminCannotCreatePublicLink(resource) {
|
|
if (resource === "question") {
|
|
// No public link: the share button copies the app link directly, no menu.
|
|
H.sharingMenuButton().should("have.attr", "aria-label", "Copy link");
|
|
}
|
|
|
|
if (resource === "dashboard") {
|
|
// No public link: dashboards keep the app link copy
|
|
H.openSharingMenu();
|
|
H.sharingMenu().within(() => {
|
|
cy.findByText("Copy link").should("be.visible");
|
|
cy.findByText(/embed/i).should("not.exist");
|
|
cy.findByText(/public link/i).should("not.exist");
|
|
});
|
|
}
|
|
}
|
|
|
|
function assertValidPublicLink({ resource, shouldHaveRemoveLink }) {
|
|
const regex = new RegExp(
|
|
`https?:\\/\\/[^\\/]+\\/public\\/${resource}\\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(\\.csv|\\.json|\\.xlsx)?`,
|
|
);
|
|
|
|
cy.findByTestId("public-link-popover-content").within(() => {
|
|
cy.findByText("Public link").should("be.visible");
|
|
|
|
cy.findByTestId("public-link-input")
|
|
.should("be.visible")
|
|
.invoke("val")
|
|
.should((value) => {
|
|
expect(value).to.match(regex);
|
|
});
|
|
|
|
cy.findByText("Remove public link").should(
|
|
shouldHaveRemoveLink ? "be.visible" : "not.exist",
|
|
);
|
|
});
|
|
}
|
|
|
|
function closeTo(value, offset) {
|
|
return (comparedValue) => {
|
|
return Math.abs(comparedValue - value) <= offset;
|
|
};
|
|
}
|