diff --git a/frontend/test/metabase/scenarios/dashboard/dashboard.cy.spec.js b/frontend/test/metabase/scenarios/dashboard/dashboard.cy.spec.js index 6513ea596c666861cc44ad0842f3580b3030cbc3..01b2e3d572f10e481649d2c93d28442468e9f73c 100644 --- a/frontend/test/metabase/scenarios/dashboard/dashboard.cy.spec.js +++ b/frontend/test/metabase/scenarios/dashboard/dashboard.cy.spec.js @@ -53,6 +53,30 @@ describe("scenarios > dashboard", () => { cy.findByText("You're editing this dashboard."); }); + it("should update the name and description", () => { + cy.visit("/dashboard/1"); + + cy.icon("ellipsis").click(); + // update title + popover().within(() => cy.findByText("Edit dashboard details").click()); + + modal().within(() => { + cy.findByText("Edit dashboard details"); + cy.findByLabelText("Name").type("{selectall}Orders per year"); + cy.findByLabelText("Description").type( + "{selectall}How many orders were placed in each year?", + ); + cy.findByText("Update").click(); + }); + + // refresh page and check that title/desc were updated + cy.visit("/dashboard/1"); + cy.findByText("Orders per year") + .next() + .trigger("mouseenter"); + cy.findByText("How many orders were placed in each year?"); + }); + it("should add a filter", () => { cy.visit("/dashboard/1"); cy.icon("pencil").click(); diff --git a/frontend/test/metabase/scenarios/dashboard/text-box.cy.spec.js b/frontend/test/metabase/scenarios/dashboard/text-box.cy.spec.js index c2d48196dfe17890c3d5f890cbc1e62bbfd14ede..a2667550e6e66e6c02cc651d3f71db48873112c9 100644 --- a/frontend/test/metabase/scenarios/dashboard/text-box.cy.spec.js +++ b/frontend/test/metabase/scenarios/dashboard/text-box.cy.spec.js @@ -1,4 +1,8 @@ -import { restore, showDashboardCardActions } from "__support__/e2e/cypress"; +import { + restore, + showDashboardCardActions, + popover, +} from "__support__/e2e/cypress"; function addTextBox(string) { cy.icon("pencil").click(); @@ -109,4 +113,26 @@ describe("scenarios > dashboard > text-box", () => { .and("have.length", 2); }); }); + + it("should let you add a parameter to a dashboard with a text box (metabase#11927)", () => { + cy.visit("/dashboard/1"); + // click pencil icon to edit + cy.icon("pencil").click(); + // add text box with text + cy.icon("string").click(); + cy.get(".DashCard") + .last() + .find("textarea") + .type("text text text"); + cy.icon("filter").click(); + popover().within(() => { + cy.findByText("Text or Category").click(); + cy.findByText("Dropdown").click(); + }); + cy.findByText("Save").click(); + + // confirm text box and filter are still there + cy.findByText("text text text"); + cy.findByPlaceholderText("Text"); + }); }); diff --git a/frontend/test/metabase/scenarios/embedding/admin-embedding-settings.cy.spec.js b/frontend/test/metabase/scenarios/embedding/admin-embedding-settings.cy.spec.js deleted file mode 100644 index 070024b40bda3b3e3ba06520c8c6f3e13c999ded..0000000000000000000000000000000000000000 --- a/frontend/test/metabase/scenarios/embedding/admin-embedding-settings.cy.spec.js +++ /dev/null @@ -1,98 +0,0 @@ -import { restore } from "__support__/e2e/cypress"; - -// Skipping temporarily because tests need to be updated to work -describe.skip("admin > settings > embedding ", () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - }); - - describe(" > embedding settings", () => { - it("should validate a premium embedding token has a valid format", () => { - cy.server(); - cy.route("PUT", "/api/setting/premium-embedding-token").as( - "saveEmbeddingToken", - ); - - cy.visit("/admin/settings/embedding_in_other_applications"); - cy.contains("Premium embedding"); - cy.contains("Enter a token").click(); - - // Try an invalid token format - cy.contains("Enter the token") - .next() - .type("Hi") - .blur(); - cy.wait("@saveEmbeddingToken").then(({ response }) => { - expect(response.body).to.equal( - "Token format is invalid. Token should be 64 hexadecimal characters.", - ); - }); - cy.contains("Token format is invalid."); - }); - - it("should validate a premium embedding token exists", () => { - cy.server(); - cy.route("PUT", "/api/setting/premium-embedding-token").as( - "saveEmbeddingToken", - ); - - cy.visit("/admin/settings/embedding_in_other_applications"); - cy.contains("Premium embedding"); - cy.contains("Enter a token").click(); - - // Try a valid format, but an invalid token - cy.contains("Enter the token") - .next() - .type( - "11397b1e60cfb1372f2f33ac8af234a15faee492bbf5c04d0edbad76da3e614a", - ) - .blur(); - cy.wait("@saveEmbeddingToken").then(({ response }) => { - expect(response.body).to.equal( - "Unable to validate token: 404 not found.", - ); - }); - cy.contains("Unable to validate token: 404 not found."); - }); - - it("should be able to set a premium embedding token", () => { - // A random embedding token with valid format - const embeddingToken = - "11397b1e60cfb1372f2f33ac8af234a15faee492bbf5c04d0edbad76da3e614a"; - - cy.server(); - cy.route({ - method: "PUT", - url: "/api/setting/premium-embedding-token", - response: embeddingToken, - }).as("saveEmbeddingToken"); - - cy.visit("/admin/settings/embedding_in_other_applications"); - cy.contains("Premium embedding"); - cy.contains("Enter a token").click(); - - cy.route("GET", "/api/session/properties").as("getSessionProperties"); - cy.route({ - method: "GET", - url: "/api/setting", - response: [ - { key: "enable-embedding", value: true }, - { key: "embedding-secret-key", value: embeddingToken }, - { key: "premium-embedding-token", value: embeddingToken }, - ], - }).as("getSettings"); - - cy.contains("Enter the token") - .next() - .type(embeddingToken) - .blur(); - cy.wait("@saveEmbeddingToken").then(({ response }) => { - expect(response.body).to.equal(embeddingToken); - }); - cy.wait("@getSessionProperties"); - cy.wait("@getSettings"); - cy.contains("Premium embedding enabled"); - }); - }); -}); diff --git a/frontend/test/metabase/scenarios/embedding/embedding-dashboard.cy.spec.js b/frontend/test/metabase/scenarios/embedding/embedding-dashboard.cy.spec.js deleted file mode 100644 index d2161155ba834a025cde4ccf24ace2e232d44e2d..0000000000000000000000000000000000000000 --- a/frontend/test/metabase/scenarios/embedding/embedding-dashboard.cy.spec.js +++ /dev/null @@ -1,101 +0,0 @@ -import { restore, popover, modal } from "__support__/e2e/cypress"; - -describe("scenarios > dashboard > embed", () => { - beforeEach(() => { - restore(); - cy.signInAsAdmin(); - }); - - it.skip("should have the correct embed snippet", () => { - cy.visit("/dashboard/1"); - cy.icon("share").click(); - cy.contains(/Embed this .* in an application/).click(); - cy.contains("Code").click(); - - const JS_CODE = new RegExp( - `// you will need to install via 'npm install jsonwebtoken' or in your package.json - -var jwt = require("jsonwebtoken"); - -var METABASE_SITE_URL = "http://localhost:PORTPORTPORT"; -var METABASE_SECRET_KEY = "KEYKEYKEY"; -var payload = { - resource: { dashboard: 1 }, - params: {}, - exp: Math.round(Date.now() / 1000) + (10 * 60) // 10 minute expiration -}; -var token = jwt.sign(payload, METABASE_SECRET_KEY); - -var iframeUrl = METABASE_SITE_URL + "/embed/dashboard/" + token + "#bordered=true&titled=true";` - .split("\n") - .join("") - .replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") - .replace("KEYKEYKEY", ".*") - .replace("PORTPORTPORT", ".*"), - ); - - const IFRAME_CODE = `<iframe - src="{{iframeUrl}}" - frameborder="0" - width="800" - height="600" - allowtransparency -></iframe>` - .split("\n") - .join(""); - - cy.get(".ace_content") - .first() - .invoke("text") - .should("match", JS_CODE); - cy.get(".ace_content") - .last() - .should("have.text", IFRAME_CODE); - }); - - it("should update the name and description", () => { - cy.visit("/dashboard/1"); - // click pencil icon to edit - cy.icon("ellipsis").click(); - // update title - popover().within(() => cy.findByText("Edit dashboard details").click()); - - modal().within(() => { - cy.findByText("Edit dashboard details"); - cy.findByLabelText("Name").type("{selectall}Orders per year"); - cy.findByLabelText("Description").type( - "{selectall}How many orders were placed in each year?", - ); - cy.findByText("Update").click(); - }); - - // refresh page and check that title/desc were updated - cy.visit("/dashboard/1"); - cy.findByText("Orders per year") - .next() - .trigger("mouseenter"); - cy.findByText("How many orders were placed in each year?"); - }); - - it("should let you add a parameter to a dashboard with a text box", () => { - cy.visit("/dashboard/1"); - // click pencil icon to edit - cy.icon("pencil").click(); - // add text box with text - cy.icon("string").click(); - cy.get(".DashCard") - .last() - .find("textarea") - .type("text text text"); - cy.icon("filter").click(); - popover().within(() => { - cy.findByText("Text or Category").click(); - cy.findByText("Dropdown").click(); - }); - cy.findByText("Save").click(); - - // confirm text box and filter are still there - cy.findByText("text text text"); - cy.findByPlaceholderText("Text"); - }); -}); diff --git a/frontend/test/metabase/scenarios/embedding/embedding-premium-token.cy.spec.js b/frontend/test/metabase/scenarios/embedding/embedding-premium-token.cy.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..e31b4eb90f153cacc012f913a94ea88d7af6c83f --- /dev/null +++ b/frontend/test/metabase/scenarios/embedding/embedding-premium-token.cy.spec.js @@ -0,0 +1,126 @@ +import { restore, describeWithoutToken } from "__support__/e2e/cypress"; + +const embeddingPage = "/admin/settings/embedding_in_other_applications"; +const licensePage = "/admin/settings/premium-embedding-license"; + +// A random embedding token with valid format +const embeddingToken = + "11397b1e60cfb1372f2f33ac8af234a15faee492bbf5c04d0edbad76da3e614a"; + +const invalidTokenMessage = + "This token doesn't seem to be valid. Double-check it, then contact support if you think it should be working."; + +const discountedWarning = + "Our Premium Embedding product has been discontinued, but if you already have a license you can activate it here. You’ll continue to receive support for the duration of your license."; + +describeWithoutToken("scenarios > embedding > premium embedding token", () => { + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + }); + + it("should validate a premium embedding token", () => { + cy.intercept("PUT", "/api/setting/premium-embedding-token").as( + "saveEmbeddingToken", + ); + + cy.visit(embeddingPage); + + cy.contains("Have a Premium Embedding license?"); + cy.contains("Activate it here.").click(); + + cy.location("pathname").should("eq", licensePage); + + cy.findByRole("heading") + .invoke("text") + .should("eq", "Premium embedding"); + + cy.findByText(discountedWarning); + + cy.findByText("Enter the token you bought from the Metabase Store below."); + + cy.findByTestId("license-input") + .as("tokenInput") + .should("be.empty"); + + // 1. Try an invalid token format + cy.get("@tokenInput").type("Hi"); + cy.button("Activate").click(); + + cy.wait("@saveEmbeddingToken").then(({ response: { body } }) => { + expect(body.cause).to.eq("Token format is invalid."); + expect(body["error-details"]).to.eq( + "Token should be 64 hexadecimal characters.", + ); + }); + + cy.findByText(invalidTokenMessage); + + // 2. Try a valid format, but an invalid token + cy.get("@tokenInput") + .clear() + .type(embeddingToken); + cy.button("Activate").click(); + + cy.wait("@saveEmbeddingToken").then(({ response: { body } }) => { + expect(body.cause).to.eq("Unable to validate token"); + expect(body["error-details"]).to.eq("clj-http: status 404"); + }); + + cy.findByText(invalidTokenMessage); + + // 3. Try submitting an empty value + // Although this might sound counterintuitive, the goal is to provide a mechanism to reset a token. + cy.get("@tokenInput").clear(); + cy.button("Activate").click(); + + cy.wait("@saveEmbeddingToken").then(({ response: { body } }) => { + expect(body).to.eq(""); + }); + + cy.findByText(invalidTokenMessage).should("not.exist"); + }); + + it("should be able to set a premium embedding token", () => { + stubTokenResponses(); + + cy.visit(licensePage); + + cy.findByTestId("license-input").type(embeddingToken); + cy.button("Activate").click(); + + cy.wait("@saveEmbeddingToken").then(({ response }) => { + expect(response.body).to.eq(embeddingToken); + }); + + cy.wait("@getSettings"); + cy.findByText( + /Your Premium Embedding license is active until Dec 3(0|1), 2122\./, + ); + }); +}); + +function stubTokenResponses() { + cy.intercept("PUT", "/api/setting/premium-embedding-token", { + body: embeddingToken, + }).as("saveEmbeddingToken"); + + cy.request("GET", "/api/setting").then(({ body: stubbedBody }) => { + const tokenSetting = stubbedBody.find( + setting => setting.key === "premium-embedding-token", + ); + tokenSetting.value = embeddingToken; + + cy.intercept("GET", "api/setting", stubbedBody).as("getSettings"); + }); + + cy.intercept("GET", "/api/premium-features/token/status", { + body: { + valid: true, + status: "Token is valid.", + features: ["embedding"], + trial: false, + "valid-thru": "2122-12-30T23:00:00Z", + }, + }); +} diff --git a/frontend/test/metabase/scenarios/embedding/embedding-snippets.cy.spec.js b/frontend/test/metabase/scenarios/embedding/embedding-snippets.cy.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..875d3c535eee91dacf5b60015248abd0c2e2019a --- /dev/null +++ b/frontend/test/metabase/scenarios/embedding/embedding-snippets.cy.spec.js @@ -0,0 +1,84 @@ +import { restore, popover } from "__support__/e2e/cypress"; + +describe("scenarios > embedding > code snippets", () => { + beforeEach(() => { + restore(); + cy.signInAsAdmin(); + }); + + it("dashboard should have the correct embed snippet", () => { + cy.visit("/dashboard/1"); + cy.icon("share").click(); + cy.findByText("Sharing and embedding").click(); + cy.contains(/Embed this .* in an application/).click(); + cy.contains("Code").click(); + + cy.findByText("To embed this dashboard in your application:"); + cy.findByText( + "Insert this code snippet in your server code to generate the signed embedding URL", + ); + + const JS_CODE = new RegExp( + `// you will need to install via 'npm install jsonwebtoken' or in your package.json + +var jwt = require("jsonwebtoken"); + +var METABASE_SITE_URL = "http://localhost:PORTPORTPORT"; +var METABASE_SECRET_KEY = "KEYKEYKEY"; +var payload = { + resource: { dashboard: 1 }, + params: {}, + exp: Math.round(Date.now() / 1000) + (10 * 60) // 10 minute expiration +}; +var token = jwt.sign(payload, METABASE_SECRET_KEY); + +var iframeUrl = METABASE_SITE_URL + "/embed/dashboard/" + token + "#bordered=true&titled=true";` + .split("\n") + .join("") + .replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + .replace("KEYKEYKEY", ".*") + .replace("PORTPORTPORT", ".*"), + ); + + const IFRAME_CODE = `<iframe + src="{{iframeUrl}}" + frameborder="0" + width="800" + height="600" + allowtransparency +></iframe>` + .split("\n") + .join(""); + + cy.get(".ace_content") + .first() + .invoke("text") + .should("match", JS_CODE); + + cy.get(".ace_content") + .last() + .should("have.text", IFRAME_CODE); + + cy.findAllByTestId("select-button") + .first() + .should("contain", "Node.js") + .click(); + + popover() + .should("contain", "Node.js") + .and("contain", "Ruby") + .and("contain", "Python") + .and("contain", "Clojure"); + + cy.findAllByTestId("select-button") + .last() + .should("contain", "Mustache") + .click(); + + popover() + .should("contain", "Mustache") + .and("contain", "Pug / Jade") + .and("contain", "ERB") + .and("contain", "JSX"); + }); +});