Skip to content
Snippets Groups Projects
Unverified Commit 34dec3c0 authored by Nemanja Glumac's avatar Nemanja Glumac Committed by GitHub
Browse files

[E2E] Refactor `parameters-embedded` spec into `embedding-dashboard` (#21292)

* Remove unneeded requests

* Remove tests not related to embedding

* Remove embedded question test

Covered in `embedding-native.cy.spec.js`

* Extract question details

* Extract dashboard details

* Fix question details

* Refactor an anti-pattern

Do not assign return values to previously defained variables.

* Do not use hard coded JWT signed token

* Simplify and merge dashboard tests into one

* Divide context into UI and API

* Extract common code into `beforeEeach` for UI context

* Merge UI tests into one

* Rename spec to `embedding-dashboard`

* Remove unnecessary `beforeEach` block

* Extract data and functions into a separate file

* Make sure we can disable previously set parameters
parent 1c9b7af7
Branches
Tags
No related merge requests found
......@@ -78,3 +78,16 @@ function getHiddenFilters(filters) {
function getEmbeddableObject(payload) {
return Object.keys(payload.resource)[0];
}
/**
* Grab iframe `src` via UI and open it,
* but make sure user is signed out.
*/
export function visitIframe() {
cy.document().then(doc => {
const iframe = doc.querySelector("iframe");
cy.signOut();
cy.visit(iframe.src);
});
}
import {
restore,
popover,
visitDashboard,
visitEmbeddedPage,
filterWidget,
visitIframe,
} from "__support__/e2e/cypress";
import {
questionDetails,
dashboardDetails,
mapParameters,
} from "./embedding-dashboard";
import { SAMPLE_DATABASE } from "__support__/e2e/cypress_sample_database";
const { ORDERS, PEOPLE } = SAMPLE_DATABASE;
describe("scenarios > embedding > dashboard parameters", () => {
beforeEach(() => {
restore();
cy.signInAsAdmin();
cy.request("POST", `/api/field/${ORDERS.USER_ID}/dimension`, {
type: "external",
name: "User ID",
human_readable_field_id: PEOPLE.NAME,
});
[ORDERS.USER_ID, PEOPLE.NAME, PEOPLE.ID].forEach(id =>
cy.request("PUT", `/api/field/${id}`, { has_field_values: "search" }),
);
cy.createNativeQuestionAndDashboard({
questionDetails,
dashboardDetails,
}).then(({ body: { id, card_id, dashboard_id } }) => {
cy.wrap(dashboard_id).as("dashboardId");
mapParameters({ id, card_id, dashboard_id });
});
});
context("UI", () => {
it("should be disabled by default but able to be set to editable and/or locked (metabase#20357)", () => {
cy.get("@dashboardId").then(dashboardId => {
visitDashboard(dashboardId);
});
cy.icon("share").click();
cy.findByText("Sharing and embedding").click();
cy.findByText("Embed this dashboard in an application").click();
cy.findByRole("heading", { name: "Parameters" })
.parent()
.as("allParameters")
.within(() => {
// verify that all the parameters on the dashboard are defaulted to disabled
cy.findAllByText("Disabled").should("have.length", 4);
// select the dropdown next to the Name parameter so that we can set it to editable
cy.findByText("Name")
.parent()
.within(() => {
cy.findByText("Disabled").click();
});
});
cy.findByText("Editable").click();
cy.get("@allParameters").within(() => {
cy.findByText("Id")
.parent()
.within(() => {
cy.findByText("Disabled").click();
});
});
cy.findByText("Locked").click();
// set the locked parameter's value
cy.findByText("Preview Locked Parameters")
.parent()
.within(() => {
cy.findByText("Id").click();
});
cy.findByPlaceholderText("Search by Name or enter an ID").type(
"1{enter}3{enter}",
);
cy.button("Add filter").click();
// publish the embedded dashboard so that we can directly navigate to its url
publishChanges(({ request }) => {
const actual = request.body.embedding_params;
const expected = {
id: "locked",
name: "enabled",
};
assert.deepEqual(actual, expected);
});
// directly navigate to the embedded dashboard
visitIframe();
// verify that the Id parameter doesn't show up but that its value is reflected in the dashcard
filterWidget()
.contains("Id")
.should("not.exist");
cy.get(".ScalarValue")
.invoke("text")
.should("eq", "2");
// verify that disabled filters don't show up
cy.findByText("Source").should("not.exist");
cy.findByText("User").should("not.exist");
// only Name parameter should be visible
openFilterOptions("Name");
cy.findByPlaceholderText("Search by Name").type("L");
cy.findByText("Lina Heaney").click();
cy.button("Add filter").click();
cy.get(".ScalarValue")
.invoke("text")
.should("eq", "1");
cy.log(
"Sanity check: lets make sure we can disable all previously set parameters",
);
cy.signInAsAdmin();
cy.get("@dashboardId").then(dashboardId => {
visitDashboard(dashboardId);
});
cy.icon("share").click();
cy.findByText("Sharing and embedding").click();
cy.findByText("Embed this dashboard in an application").click();
cy.findByText("Locked").click();
popover()
.contains("Disabled")
.click();
cy.findByText("Editable").click();
popover()
.contains("Disabled")
.click();
publishChanges(({ request }) => {
const actual = request.body.embedding_params;
const expected = { name: "disabled", id: "disabled" };
assert.deepEqual(actual, expected);
});
visitIframe();
filterWidget().should("not.exist");
cy.get(".ScalarValue")
.invoke("text")
.should("eq", "2,500");
});
});
context("API", () => {
beforeEach(() => {
cy.get("@dashboardId").then(dashboardId => {
cy.request("PUT", `/api/dashboard/${dashboardId}`, {
embedding_params: {
id: "enabled",
name: "enabled",
source: "enabled",
user_id: "enabled",
},
enable_embedding: true,
});
const payload = {
resource: { dashboard: dashboardId },
params: {},
};
visitEmbeddedPage(payload);
// wait for the results to load
cy.contains("Test Dashboard");
cy.contains("2,500");
});
});
it("should work for all filters", () => {
cy.log("should allow searching PEOPLE.ID by PEOPLE.NAME");
openFilterOptions("Id");
popover().within(() => {
cy.findByPlaceholderText("Search by Name or enter an ID").type("Aly");
cy.contains("Alycia McCullough - 2016");
});
cy.log("should allow searching PEOPLE.NAME by PEOPLE.NAME");
openFilterOptions("Name");
popover().within(() => {
cy.findByPlaceholderText("Search by Name").type("Aly");
cy.contains("Alycia McCullough");
});
cy.log("should show values for PEOPLE.SOURCE");
openFilterOptions("Source");
popover().contains("Affiliate");
cy.log("should allow searching ORDER.USER_ID by PEOPLE.NAME");
openFilterOptions("User");
popover().within(() => {
cy.findByPlaceholderText("Search by Name or enter an ID").type("Aly");
cy.contains("Alycia McCullough - 2016");
});
cy.log("should accept url parameters");
cy.url().then(url => cy.visit(url + "?id=1&id=3"));
cy.contains(".ScalarValue", "2");
});
});
});
function openFilterOptions(name) {
filterWidget()
.contains(name)
.click();
}
function publishChanges(callback) {
cy.intercept("PUT", "/api/dashboard/*").as("publishChanges");
cy.button("Publish").click();
cy.wait(["@publishChanges", "@publishChanges"]).then(xhrs => {
// Unfortunately, the order of requests is not always the same.
// Therefore, we must first get the one that has the `embedding_params` and then assert on it.
const targetXhr = xhrs.find(({ request }) =>
Object.keys(request.body).includes("embedding_params"),
);
callback && callback(targetXhr);
});
}
import { SAMPLE_DATABASE } from "__support__/e2e/cypress_sample_database";
const { ORDERS, PEOPLE } = SAMPLE_DATABASE;
export const questionDetails = {
native: {
query:
"SELECT COUNT(*) FROM people WHERE {{id}} AND {{name}} AND {{source}} /* AND {{user_id}} */",
"template-tags": {
id: {
id: "3fce42dd-fac7-c87d-e738-d8b3fc9d6d56",
name: "id",
display_name: "Id",
type: "dimension",
dimension: ["field", PEOPLE.ID, null],
"widget-type": "id",
default: null,
},
name: {
id: "1fe12d96-8cf7-49e4-05a3-6ed1aea24490",
name: "name",
display_name: "Name",
type: "dimension",
dimension: ["field", PEOPLE.NAME, null],
"widget-type": "category",
default: null,
},
source: {
id: "aed3c67a-820a-966b-d07b-ddf54a7f2e5e",
name: "source",
display_name: "Source",
type: "dimension",
dimension: ["field", PEOPLE.SOURCE, null],
"widget-type": "category",
default: null,
},
user_id: {
id: "cd4bb37d-8404-488e-f66a-6545a261bbe0",
name: "user_id",
display_name: "User",
type: "dimension",
dimension: ["field", ORDERS.USER_ID, null],
"widget-type": "id",
default: null,
},
},
},
display: "scalar",
};
// Define dashboard filters
const idFilter = { name: "Id", slug: "id", id: "1", type: "id" };
const nameFilter = { name: "Name", slug: "name", id: "2", type: "category" };
const sourceFilter = {
name: "Source",
slug: "source",
id: "3",
type: "category",
};
const userFilter = { name: "User", slug: "user_id", id: "4", type: "id" };
const parameters = [idFilter, nameFilter, sourceFilter, userFilter];
export const dashboardDetails = {
parameters,
};
function getParameterMappings(parameters, card_id) {
const parameter_mappings = [];
parameters.map(({ id, slug }) => {
parameter_mappings.push({
parameter_id: id,
card_id,
target: ["dimension", ["template-tag", slug]],
});
});
return parameter_mappings;
}
export function mapParameters({ id, card_id, dashboard_id } = {}) {
return cy.request("PUT", `/api/dashboard/${dashboard_id}/cards`, {
cards: [
{
id,
card_id,
row: 0,
col: 0,
sizeX: 18,
sizeY: 6,
series: [],
visualization_settings: {},
parameter_mappings: getParameterMappings(parameters, card_id),
},
],
});
}
import {
restore,
popover,
visitQuestion,
visitDashboard,
} from "__support__/e2e/cypress";
import { SAMPLE_DB_ID } from "__support__/e2e/cypress_data";
import { SAMPLE_DATABASE } from "__support__/e2e/cypress_sample_database";
const { ORDERS, PEOPLE } = SAMPLE_DATABASE;
const METABASE_SECRET_KEY =
"24134bd93e081773fb178e8e1abb4e8a973822f7e19c872bd92c8d5a122ef63f";
// Calling jwt.sign was failing in cypress (in browser issue maybe?). These
// tokens just hard code dashboardId=2 and questionId=3
const QUESTION_JWT_TOKEN =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZXNvdXJjZSI6eyJxdWVzdGlvbiI6M30sInBhcmFtcyI6e30sImlhdCI6MTU3OTU1OTg3NH0.alV205oYgfyWuwLNQSLVgfHop1tpevX4C26Xal-bia8";
const DASHBOARD_JWT_TOKEN =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyZXNvdXJjZSI6eyJkYXNoYm9hcmQiOjJ9LCJwYXJhbXMiOnt9LCJpYXQiOjE1Nzk1NjAxMTF9.LjOiTp4p2lV3b2VpSjcg0GuSaE2O0xhHwc59JDYcBJI";
// NOTE: some overlap with parameters.cy.spec.js
describe("scenarios > dashboard > parameters-embedded", () => {
let dashboardId, questionId, dashcardId;
beforeEach(() => {
restore();
cy.signInAsAdmin();
cy.request("POST", `/api/field/${ORDERS.USER_ID}/dimension`, {
type: "external",
name: "User ID",
human_readable_field_id: PEOPLE.NAME,
});
[ORDERS.USER_ID, PEOPLE.NAME, PEOPLE.ID].forEach(id =>
cy.request("PUT", `/api/field/${id}`, { has_field_values: "search" }),
);
createQuestion().then(res => {
questionId = res.body.id;
createDashboard().then(res => {
dashboardId = res.body.id;
addCardToDashboard({ dashboardId, questionId }).then(res => {
dashcardId = res.body.id;
mapParameters({ dashboardId, questionId, dashcardId });
});
});
});
cy.request("PUT", `/api/setting/embedding-secret-key`, {
value: METABASE_SECRET_KEY,
});
cy.request("PUT", `/api/setting/enable-embedding`, { value: true });
cy.request("PUT", `/api/setting/enable-public-sharing`, { value: true });
});
describe("embedded parameters", () => {
it("should be disabled by default but able to be set to editable", () => {
visitDashboard(2);
cy.icon("share").click();
cy.findByText("Sharing and embedding").click();
cy.findByText("Embed this dashboard in an application").click();
cy.get(".Modal--full").within(() => {
// verify that all the parameters on the dashboard are defaulted to disabled
cy.findAllByText("Disabled").should("have.length", 4);
// select the dropdown next to the Id parameter so that we can set it to editable
cy.findByText("Id")
.parent()
.within(() => {
cy.findByText("Disabled").click();
});
});
cy.findByText("Editable").click();
// publish the embedded dashboard so that we can directly navigate to its url
cy.findByText("Publish").click();
// directly navigate to the embedded dashboard
cy.document().then(doc => {
const iframe = doc.querySelector("iframe");
cy.visit(iframe.src);
});
// verify that only the Id parameter shows up and is editable
cy.findByText("Name").should("not.exist");
cy.findByText("Source").should("not.exist");
cy.findByText("User").should("not.exist");
cy.findByText("Id").click();
popover().within(() => {
cy.get("input").type("1{enter}3{enter}");
cy.findByText("Add filter").click();
});
// verify that the dashcard shows the correct, filtered value
cy.get(".Card").within(() => {
cy.contains("2");
});
});
it("should let parameters be locked to a specific value (metabase#20357)", () => {
visitDashboard(2);
cy.icon("share").click();
cy.findByText("Sharing and embedding").click();
cy.findByText("Embed this dashboard in an application").click();
cy.findByText("Parameters");
cy.get(".Modal--full").within(() => {
cy.findAllByText("Disabled").should("have.length", 4);
// select the dropdown next to the Id parameter so that we can set it to locked
cy.findByText("Id")
.parent()
.within(() => {
cy.findByText("Disabled").click();
});
});
cy.findByText("Locked").click();
// set the locked parameter's value
cy.findByText("Preview Locked Parameters")
.parent()
.within(() => {
cy.findByText("Id").click();
});
popover().within(() => {
cy.get("input").type("1{enter}3{enter}");
});
cy.findByText("Add filter").click();
// publish the embedded dashboard so that we can directly navigate to its url
cy.findByText("Publish").click();
// directly navigate to the embedded dashboard
cy.document().then(doc => {
const iframe = doc.querySelector("iframe");
cy.visit(iframe.src);
});
// verify that the Id parameter doesn't show up but that its value is reflected in the dashcard
cy.findByText("Id").should("not.exist");
cy.get(".Card").within(() => {
cy.contains("2");
});
});
});
describe("private question", () => {
beforeEach(cy.signInAsAdmin);
sharedParametersTests(() => {
visitQuestion(questionId);
// wait for question to load/run
cy.contains("Test Question");
cy.contains("2,500");
});
});
describe("public question", () => {
let uuid;
beforeEach(() => {
cy.request("POST", `/api/card/${questionId}/public_link`).then(
res => (uuid = res.body.uuid),
);
cy.signOut();
});
sharedParametersTests(() => {
cy.visit(`/public/question/${uuid}`);
// wait for question to load/run
cy.contains("Test Question");
cy.contains("2,500");
});
});
describe("embedded question", () => {
beforeEach(() => {
cy.request("PUT", `/api/card/${questionId}`, {
embedding_params: {
id: "enabled",
name: "enabled",
source: "enabled",
user_id: "enabled",
},
enable_embedding: true,
});
cy.signOut();
});
sharedParametersTests(() => {
cy.visit(`/embed/question/${QUESTION_JWT_TOKEN}`);
// wait for question to load/run
cy.contains("Test Question");
cy.contains("2,500");
});
});
describe("private dashboard", () => {
beforeEach(cy.signInAsAdmin);
sharedParametersTests(() => {
visitDashboard(dashboardId);
// wait for question to load/run
cy.contains("Test Dashboard");
cy.contains("2,500");
});
});
describe("public dashboard", () => {
let uuid;
beforeEach(() => {
cy.request("POST", `/api/dashboard/${dashboardId}/public_link`).then(
res => (uuid = res.body.uuid),
);
cy.signOut();
});
sharedParametersTests(() => {
cy.visit(`/public/dashboard/${uuid}`);
// wait for question to load/run
cy.contains("Test Dashboard");
cy.contains("2,500");
});
});
describe("embedded dashboard", () => {
beforeEach(() => {
cy.request("PUT", `/api/dashboard/${dashboardId}`, {
embedding_params: {
id: "enabled",
name: "enabled",
source: "enabled",
user_id: "enabled",
},
enable_embedding: true,
});
cy.signOut();
});
sharedParametersTests(() => {
cy.visit(`/embed/dashboard/${DASHBOARD_JWT_TOKEN}`);
// wait for question to load/run
cy.contains("Test Dashboard");
cy.contains("2,500");
});
});
});
function sharedParametersTests(visitUrl) {
it("should allow searching PEOPLE.ID by PEOPLE.NAME", () => {
visitUrl();
cy.contains("Id").click();
popover()
.find('[placeholder="Search by Name or enter an ID"]')
.type("Aly");
popover().contains("Alycia McCullough - 2016");
});
it("should allow searching PEOPLE.NAME by PEOPLE.NAME", () => {
visitUrl();
cy.contains("Name").click();
popover()
.find('[placeholder="Search by Name"]')
.type("Aly");
popover().contains("Alycia McCullough");
});
it("should show values for PEOPLE.SOURCE", () => {
visitUrl();
cy.contains("Source").click();
popover().contains("Affiliate");
});
it("should allow searching ORDER.USER_ID by PEOPLE.NAME", () => {
visitUrl();
cy.contains("User").click();
popover()
.find('[placeholder="Search by Name or enter an ID"]')
.type("Aly");
popover().contains("Alycia McCullough - 2016");
});
it("should accept url parameters", () => {
visitUrl();
cy.url().then(url => cy.visit(url + "?id=1&id=3"));
cy.contains(".ScalarValue", "2");
});
}
const createQuestion = () =>
cy.request("PUT", "/api/card/3", {
name: "Test Question",
dataset_query: {
type: "native",
native: {
query:
"SELECT COUNT(*) FROM people WHERE {{id}} AND {{name}} AND {{source}} /* AND {{user_id}} */",
"template-tags": {
id: {
id: "3fce42dd-fac7-c87d-e738-d8b3fc9d6d56",
name: "id",
display_name: "Id",
type: "dimension",
dimension: ["field", PEOPLE.ID, null],
"widget-type": "id",
default: null,
},
name: {
id: "1fe12d96-8cf7-49e4-05a3-6ed1aea24490",
name: "name",
display_name: "Name",
type: "dimension",
dimension: ["field", PEOPLE.NAME, null],
"widget-type": "category",
default: null,
},
source: {
id: "aed3c67a-820a-966b-d07b-ddf54a7f2e5e",
name: "source",
display_name: "Source",
type: "dimension",
dimension: ["field", PEOPLE.SOURCE, null],
"widget-type": "category",
default: null,
},
user_id: {
id: "cd4bb37d-8404-488e-f66a-6545a261bbe0",
name: "user_id",
display_name: "User",
type: "dimension",
dimension: ["field", ORDERS.USER_ID, null],
"widget-type": "id",
default: null,
},
},
},
database: SAMPLE_DB_ID,
},
display: "scalar",
description: null,
visualization_settings: {},
collection_id: null,
result_metadata: null,
});
const createDashboard = () =>
cy.request("POST", "/api/dashboard", {
name: "Test Dashboard",
collection_id: null,
parameters: [
{ name: "Id", slug: "id", id: "1", type: "id" },
{ name: "Name", slug: "name", id: "2", type: "category" },
{ name: "Source", slug: "source", id: "3", type: "category" },
{ name: "User", slug: "user_id", id: "4", type: "id" },
],
});
const addCardToDashboard = ({ dashboardId, questionId }) =>
cy.request("POST", `/api/dashboard/${dashboardId}/cards`, {
cardId: questionId,
});
const mapParameters = ({ dashboardId, dashcardId, questionId }) =>
cy.request("PUT", `/api/dashboard/${dashboardId}/cards`, {
cards: [
{
id: dashcardId,
card_id: questionId,
row: 0,
col: 0,
sizeX: 18,
sizeY: 6,
series: [],
visualization_settings: {},
parameter_mappings: [
{
parameter_id: "1",
card_id: questionId,
target: ["dimension", ["template-tag", "id"]],
},
{
parameter_id: "2",
card_id: questionId,
target: ["dimension", ["template-tag", "name"]],
},
{
parameter_id: "3",
card_id: questionId,
target: ["dimension", ["template-tag", "source"]],
},
{
parameter_id: "4",
card_id: questionId,
target: ["dimension", ["template-tag", "user_id"]],
},
],
},
],
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment