Files
metabase/e2e/test/scenarios/sharing/public-sharing-embed-button-behavior.cy.spec.js
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

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;
};
}