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

[E2E] Optimize `dashboard-filters/parameters` spec (#24449)

* Remove duplicated test

We're already testing `between` number dashboard filter in:
- frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-number.cy.spec.js
- frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-sql-number.cy.spec.js

* Speed test up using API where possible

* Remove another duplicate

This flow is already covered in:
- frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-sql-required-field-filter.cy.spec.js
- frontend/test/metabase/scenarios/dashboard-filters/dashboard-filters-sql-required-simple-filter.cy.spec.js

* Merge three logical units into one test

Reference issue #17933 and explicitly mark it as reproduced.

* Merge repros for 9299 and 16181 together

* Refactor permissions test

* Rename and put TODO note for 18113
parent 8e1f7653
No related branches found
No related tags found
No related merge requests found
import {
sidebar,
popover,
restore,
openNativeEditor,
visitDashboard,
filterWidget,
} from "__support__/e2e/helpers";
import { SAMPLE_DATABASE } from "__support__/e2e/cypress_sample_database";
// NOTE: some overlap with parameters-embedded.cy.spec.js
import { SAMPLE_DB_ID } from "__support__/e2e/cypress_data";
const { ORDERS_ID, ORDERS, PRODUCTS } = SAMPLE_DATABASE;
......@@ -21,44 +18,21 @@ describe("scenarios > dashboard > parameters", () => {
cy.intercept("GET", "/api/collection/**").as("collection");
});
it("should be visible if previously added", () => {
visitDashboard(1);
cy.findByTextEnsureVisible("Created At");
cy.findByText("Baker").should("not.exist");
// Add a filter
addCityFilterWithDefault();
cy.log(
"**Filter should be set and applied after we leave and back to the dashboard**",
);
cy.visit("/");
cy.wait("@collection");
cy.findByText("Our analytics").click();
cy.wait("@collection");
cy.findByText("Orders in a dashboard").click();
cy.wait("@collection");
cy.findByTextEnsureVisible("Product ID");
cy.findByTextEnsureVisible("Baker");
});
it("one filter should search across multiple fields", () => {
cy.createDashboard({ name: "my dash" }).then(({ body: { id } }) => {
// add the same question twice
cy.request("POST", `/api/dashboard/${id}/cards`, {
cardId: 2, // Orders, count
});
it("should search across multiple fields", () => {
cy.createDashboard({ name: "my dash" });
cy.request("POST", `/api/dashboard/${id}/cards`, {
cardId: 2,
});
cy.visit("/collection/root");
cy.wait("@collection");
cy.findByText("my dash").click();
cy.wait("@collection");
visitDashboard(id);
});
// add the same question twice
cy.icon("pencil").click();
cy.get(".QueryBuilder-section .Icon-add").click();
cy.wait("@collection");
addQuestion("Orders, Count");
addQuestion("Orders, Count");
// add a category filter
cy.icon("filter").click();
......@@ -78,7 +52,7 @@ describe("scenarios > dashboard > parameters", () => {
cy.contains("You're editing this dashboard.").should("not.exist");
// confirm that typing searches both fields
cy.contains("Text").click();
filterWidget().contains("Text").click();
// After typing "Ga", you should see this name
popover().find("input").type("Ga");
......@@ -88,101 +62,19 @@ describe("scenarios > dashboard > parameters", () => {
// Continue typing a "d" and you see "Gadget"
popover().find("input").type("d");
cy.wait("@dashboard");
popover().contains("Gadget").click();
popover().contains("Add filter").click();
});
it("should query with a 2 argument parameter", () => {
cy.createDashboard({ name: "my dash" });
cy.visit("/collection/root");
cy.wait("@collection");
cy.findByText("my dash").click();
cy.wait("@collection");
// add a question
cy.icon("pencil").click();
cy.get(".QueryBuilder-section .Icon-add").click();
cy.wait("@collection");
addQuestion("Orders, Count");
// add a Number - Between filter
cy.icon("filter").click();
cy.contains("Number").click();
cy.findByText("Between").click();
// map the parameter to the Rating field
selectFilter(cy.get(".DashCard"), "Rating");
// finish editing filter and save dashboard
cy.contains("Save").click();
// wait for saving to finish
cy.wait("@dashboard");
cy.contains("You're editing this dashboard.").should("not.exist");
// populate the filter inputs
cy.contains("Between").click();
popover().find("input").first().type("3");
popover().find("input").last().type("4");
popover().contains("Add filter").click();
cy.wait("@dashboard");
// There should be 8849 orders with a rating >= 3 && <= 4
cy.get(".DashCard").contains("8,849");
cy.url().should("include", "between=3&between=4");
});
it("should not search field for results non-exact parameter string operators", () => {
visitDashboard(1);
cy.findByTextEnsureVisible("Created At");
// Add a filter tied to a field that triggers a search for field values
cy.icon("pencil").click();
cy.icon("filter").click();
cy.findByText("Text or Category").click();
cy.findByText("Starts with").click();
// Link that filter to the card
cy.findByText("Select…").click();
popover().within(() => {
cy.findByText("Name").click();
cy.findByText("Gadget").click();
cy.button("Add filter").click();
});
// Add a filter with few enough values that it does not search
cy.icon("filter").click();
cy.findByText("Text or Category").click();
cy.findByText("Ends with").click();
// Link that filter to the card
cy.findByText("Select…").click();
popover().within(() => {
cy.findByText("Category").click();
});
cy.findByText("Save").click();
cy.wait("@dashboard");
cy.findByText("You're editing this dashboard.").should("not.exist");
cy.contains("Text starts with").click();
cy.findByPlaceholderText("Enter some text").click().type("Corbin");
cy.findByText("Corbin Mertz").should("not.exist");
cy.findByText("Add filter").click();
cy.contains("Text ends with").click();
cy.findByPlaceholderText("Enter some text").click().type("dget");
cy.findByText("Widget").should("not.exist");
cy.findByText("Add filter").click();
cy.location("search").should("eq", "?text=Gadget");
cy.get(".DashCard").first().should("contain", "0");
cy.get(".DashCard").last().should("contain", "4,939");
});
it("should remove parameter from URL after its name has been removed (metabase#10829)", () => {
it("should remove parameter name or the whole parameter (metabase#10829, metabase#17933)", () => {
// Mirrored issue in metabase-enterprise#275
cy.intercept("POST", "/api/dashboard/*/dashcard/*/card/*/query").as(
"dashcardQuery",
);
const questionDetails = {
query: {
......@@ -191,7 +83,15 @@ describe("scenarios > dashboard > parameters", () => {
},
};
const filter = {
const startsWith = {
name: "Text starts with",
slug: "text_starts_with",
id: "1b9cd9f1",
type: "string/starts-with",
sectionId: "string",
};
const endsWith = {
name: "Text ends with",
slug: "text_ends_with",
id: "88a1257c",
......@@ -200,7 +100,7 @@ describe("scenarios > dashboard > parameters", () => {
};
const dashboardDetails = {
parameters: [filter],
parameters: [startsWith, endsWith],
};
cy.createQuestionAndDashboard({ questionDetails, dashboardDetails }).then(
......@@ -218,7 +118,21 @@ describe("scenarios > dashboard > parameters", () => {
visualization_settings: {},
parameter_mappings: [
{
parameter_id: filter.id,
parameter_id: startsWith.id,
card_id,
target: [
"dimension",
[
"field",
PRODUCTS.CATEGORY,
{
"source-field": ORDERS.PRODUCT_ID,
},
],
],
},
{
parameter_id: endsWith.id,
card_id,
target: [
"dimension",
......@@ -241,133 +155,60 @@ describe("scenarios > dashboard > parameters", () => {
},
);
// populate the filter input
filterWidget().click();
cy.findByPlaceholderText("Enter some text").type("zmo{enter}");
cy.findByText(startsWith.name).click();
cy.findByPlaceholderText("Enter some text").type("G");
// Make sure the dropdown list with values is not populated,
// because it makes no sense for non-exact parameter string operators.
// See: https://github.com/metabase/metabase/pull/15477
cy.findByText("Gizmo").should("not.exist");
cy.findByText("Gadget").should("not.exist");
cy.button("Add filter").click();
cy.log(
"**URL is updated correctly with the given parameter at this point**",
);
cy.location("search").should("eq", "?text_ends_with=zmo");
const startsWithSlug = `${startsWith.slug}=G`;
cy.location("search").should("eq", `?${startsWithSlug}`);
cy.findByText("37.65").should("not.exist");
cy.findByText(endsWith.name).click();
cy.findByPlaceholderText("Enter some text").type("zmo");
// Make sure the dropdown list with values is not populated,
// because it makes no sense for non-exact parameter string operators.
// See: https://github.com/metabase/metabase/pull/15477
cy.findByText("Gizmo").should("not.exist");
// Remove filter name
cy.button("Add filter").click();
const endsWithSlug = `${endsWith.slug}=zmo`;
cy.location("search").should("eq", `?${startsWithSlug}&${endsWithSlug}`);
cy.findByText("52.72").should("not.exist");
// Remove filter (metabase#17933)
cy.icon("pencil").click();
cy.get(".Dashboard").find(".Icon-gear").click();
cy.findByText(startsWith.name).find(".Icon-gear").click();
cy.findByDisplayValue("Text ends with").clear().blur();
cy.findByText("Remove").click();
cy.location("search").should("eq", `?${endsWithSlug}`);
cy.findByDisplayValue("unnamed");
// Remove filter name (metabase#10829)
cy.findByText(endsWith.name).find(".Icon-gear").click();
cy.findByDisplayValue(endsWith.name).clear().blur();
cy.location("search").should("eq", "?unnamed=zmo");
cy.findByDisplayValue("unnamed");
cy.button("Save").click();
cy.wait("@dashcardQuery");
cy.log("Filter name should be 'unnamed' and the value cleared");
filterWidget().contains(/unnamed/i);
cy.log("URL should reset");
cy.location("search").should("eq", "");
});
it("should allow linked question to be changed without breaking (metabase#9299)", () => {
openNativeEditor().type("SELECT * FROM ORDERS WHERE {{filter}}", {
parseSpecialCharSequences: false,
});
cy.wait("@collection");
// make {{filter}} a "Field Filter" connected to `Orders > Created At`
cy.findAllByTestId("select-button").contains("Text").click();
cy.findByText("Field Filter").click();
popover().within(() => {
cy.findByText("Sample Database");
cy.findByText("Orders").click();
cy.findByText("Created At").click();
});
cy.findByText("Save").click();
cy.findByPlaceholderText("What is the name of your card?")
.click()
.type("DashQ");
cy.get(".Modal").within(() => {
cy.findByText("Save").click();
});
// add question to existing dashboard, rather than creating a new one
cy.findByText("Yes please!").click();
cy.findByText("Orders in a dashboard").click();
cy.wait("@dashboard");
// it automatically switches to that dashboard and enters the editing mode
cy.findByTextEnsureVisible("You're editing this dashboard.");
cy.findByTextEnsureVisible("Created At");
cy.findByTextEnsureVisible("DashQ");
cy.wait("@cardQuery");
cy.icon("filter").click();
cy.findByText("Time").click();
cy.findByText("All Options").click();
// update the filter with the default option "Previous 30 days"
// it will automatically be selected - just press "Update filter"
cy.findByText("No default").click();
cy.findByText("Relative dates...").click();
cy.findByText("Past").click();
cy.findByText("Update filter").click();
// connect that filter to the second card/question (dashboard already had one question previously)
cy.get(".DashCard").last().contains("Select").click();
popover().contains("Filter").click();
// save the dashboard
cy.findByText("Save").click();
cy.wait("@dashboard");
cy.findByTextEnsureVisible("Product ID");
cy.findByText("You're editing this dashboard.").should("not.exist");
cy.visit("/");
cy.wait("@collection");
// find and edit the question
cy.findByText("Our analytics").click();
cy.wait("@collection");
cy.findByText("DashQ").click();
cy.wait("@collection");
cy.findByText("Open Editor").click();
cy.wait("@cardQuery");
cy.findByTextEnsureVisible("PRODUCT_ID");
// remove the connected filter from the question...
cy.get("@editor")
.click()
.type("{selectall}{backspace}") // cannot use `clear()` on a custom (unsupported) element
.type("{selectall}{backspace}") // repeat because sometimes Cypress fails to clear everything
.type("SELECT * from ORDERS");
cy.findByText("Save").click();
// ... and save it (override the current one is selected by default - just press "Save")
cy.get(".Modal").within(() => {
cy.findByText("Save").click();
});
cy.findByText("New question").should("not.exist");
cy.log("Bug was breaking the dashboard at this point");
visitDashboard(1);
// error was always ending in "is undefined" when dashboard broke in the past
cy.contains(/is undefined$/).should("not.exist");
cy.findByText("Orders in a dashboard");
cy.wait("@collection");
cy.findByText("DashQ");
cy.findByText("37.65");
});
it("should not having any mapping options if the native question field filter and parameter type differ (metabase#16181)", () => {
const filter = {
name: "Text contains",
slug: "text_contains",
id: "98289b9b",
type: "string/contains",
sectionId: "string",
};
cy.createNativeQuestion({
it("should handle mismatch between filter types (metabase#9299, metabase#16181)", () => {
const questionDetails = {
name: "16181",
native: {
query: "select count(*) from products where {{filter}}",
......@@ -375,56 +216,99 @@ describe("scenarios > dashboard > parameters", () => {
filter: {
id: "0b004110-d64a-a413-5aa2-5a5314fc8fec",
name: "filter",
"display-name": "Filter",
"display-name": "Native Filter",
type: "dimension",
dimension: ["field", PRODUCTS.TITLE, null],
dimension: ["field", PRODUCTS.CATEGORY, null],
"widget-type": "string/=",
default: null,
},
},
},
display: "scalar",
}).then(({ body: { id: card_id } }) => {
cy.createDashboard().then(({ body: { id: dashboard_id } }) => {
// Add previously created question to the dashboard
cy.request("POST", `/api/dashboard/${dashboard_id}/cards`, {
cardId: card_id,
}).then(({ body: { id } }) => {
cy.addFilterToDashboard({ filter, dashboard_id });
cy.request("PUT", `/api/dashboard/${dashboard_id}/cards`, {
cards: [
};
const matchingFilterType = {
name: "Text",
slug: "text",
id: "d245671f",
type: "string/=",
sectionId: "string",
default: "Gadget",
};
const dashboardDetails = {
parameters: [matchingFilterType],
};
cy.createNativeQuestionAndDashboard({
questionDetails,
dashboardDetails,
}).then(({ body: { id, card_id, dashboard_id } }) => {
cy.request("PUT", `/api/dashboard/${dashboard_id}/cards`, {
cards: [
{
id,
card_id,
row: 0,
col: 0,
sizeX: 8,
sizeY: 6,
parameter_mappings: [
{
id,
parameter_id: matchingFilterType.id,
card_id,
row: 0,
col: 0,
sizeX: 8,
sizeY: 6,
parameter_mappings: [
{
parameter_id: filter.id,
card_id,
target: ["dimension", ["template-tag", "filter"]],
},
],
target: ["dimension", ["template-tag", "filter"]],
},
],
});
});
},
],
});
visitDashboard(dashboard_id);
cy.wait("@collection");
visitDashboard(dashboard_id);
cy.get(".ScalarValue").invoke("text").should("eq", "53");
// Confirm you can't map wrong parameter type the native question's field filter (metabase#16181)
cy.icon("pencil").click();
cy.icon("filter").click();
cy.findByText("ID").click();
cy.findByText("No valid fields");
// Confirm that the correct parameter type is connected to the native question's field filter
cy.findByText(matchingFilterType.name).find(".Icon-gear").click();
cy.findByText("Column to filter on").parent().contains("Native Filter");
// Update the underlying question's query
cy.request("PUT", `/api/card/${card_id}`, {
dataset_query: {
type: "native",
native: {
query: "select 1",
"template-tags": {},
},
database: SAMPLE_DB_ID,
},
});
});
// confirm you can't map the parameter on the dashboard to the native question's field filter
cy.icon("pencil").click();
cy.findByText("Text contains").click();
cy.findByText("No valid fields");
// Upon visiting the dashboard again the filter preserves its value
visitDashboard(dashboard_id);
cy.location("search").should("eq", "?text=Gadget");
filterWidget().contains("Gadget");
// But the question should display the new value and is not affected by the filter
cy.get(".ScalarValue").invoke("text").should("eq", "1");
// Confirm that it is not possible to connect filter to the updated question anymore (metabase#9299)
cy.icon("pencil").click();
cy.findByText(matchingFilterType.name).find(".Icon-gear").click();
cy.findByText("No valid fields");
});
});
it("should render other categories filter that allows selecting multiple values (metabase#18113)", () => {
// TODO: Completely rewrite, and put together with other nested question reproductions
// - This repro is using the old params API
// - It's tightly connected to metabase#12985 so put them together if possible
it("should allow applying multiple values to filter connected to nested question (metabase#18113)", () => {
const filter = {
id: "c2967a17",
name: "Category",
......@@ -493,39 +377,29 @@ describe("scenarios > dashboard > parameters", () => {
cy.get("tbody > tr").should("have.length", 2);
});
it("should be removable from dashboard", () => {
visitDashboard(1);
cy.findByTextEnsureVisible("Created At");
// Add a filter
addCityFilterWithDefault();
// Remove the filter from the dashboard
cy.icon("pencil").click();
cy.findByText("Location").click();
cy.findByText("Remove").click();
cy.findByText("Save").click();
cy.wait("@dashboard");
cy.findByText("You're editing this dashboard.").should("not.exist");
cy.findByText("Baker").should("not.exist");
});
describe("when the user does not have self service data permissions", () => {
describe("when the user does not have self-service data permissions", () => {
beforeEach(() => {
visitDashboard(1);
cy.wait("@collection");
cy.findByTextEnsureVisible("Created At");
addCityFilterWithDefault();
cy.icon("pencil").click();
cy.icon("filter").click();
popover().findByText("ID").click();
selectFilter(cy.get(".DashCard"), "User ID");
cy.findByText("Save").click();
cy.findByText("You're editing this dashboard.").should("not.exist");
cy.signIn("nodata");
cy.reload();
cy.wait("@collection");
visitDashboard(1);
});
it("should not see mapping options", () => {
cy.icon("pencil").click();
cy.findByText("Location").click({ force: true });
cy.findByTestId("edit-dashboard-parameters-widget-container")
.find(".Icon-gear")
.click();
cy.icon("key");
});
......@@ -536,33 +410,3 @@ function selectFilter(selection, filterName) {
selection.contains("Select…").click();
popover().contains(filterName).click({ force: true });
}
function addQuestion(name) {
sidebar().contains(name).click();
cy.wait("@cardQuery");
}
function addCityFilterWithDefault() {
cy.icon("pencil").click();
cy.icon("filter").click();
cy.findByText("Location").click();
cy.findByText("Dropdown").click();
// Link that filter to the card
cy.findByText("Select…").click();
popover().within(() => {
cy.findByText("City").click();
});
// Create a default value and save filter
cy.findByText("No default").click();
cy.findByPlaceholderText("Search by City").click().type("B");
cy.findByText("Baker").click();
cy.findByText("Add filter").click();
cy.get(".Button--primary").contains("Done").click();
cy.findByText("Save").click();
cy.wait("@dashboard");
cy.findByText("You're editing this dashboard.").should("not.exist");
cy.findByTextEnsureVisible("Baker");
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment