From 1aec628957b20419913399045b0e5e9f246633e8 Mon Sep 17 00:00:00 2001 From: Alexander Polyankin <alexander.polyankin@metabase.com> Date: Tue, 1 Aug 2023 17:11:42 +0300 Subject: [PATCH] Add e2e tests for changing column list in the query in viz settings (#32791) --- .../table-column-settings.cy.spec.js | 682 ++++++++++++++++++ .../settings/ChartSettingOrderedColumns.jsx | 4 +- 2 files changed, 684 insertions(+), 2 deletions(-) create mode 100644 e2e/test/scenarios/visualizations/table-column-settings.cy.spec.js diff --git a/e2e/test/scenarios/visualizations/table-column-settings.cy.spec.js b/e2e/test/scenarios/visualizations/table-column-settings.cy.spec.js new file mode 100644 index 00000000000..543368eec90 --- /dev/null +++ b/e2e/test/scenarios/visualizations/table-column-settings.cy.spec.js @@ -0,0 +1,682 @@ +import { restore } from "e2e/support/helpers"; +import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; + +const { ORDERS_ID, ORDERS, PRODUCTS_ID, PRODUCTS } = SAMPLE_DATABASE; + +const tableQuestion = { + display: "table", + query: { + "source-table": ORDERS_ID, + }, +}; + +const tableQuestionWithJoin = { + display: "table", + query: { + "source-table": ORDERS_ID, + joins: [ + { + fields: "all", + "source-table": PRODUCTS_ID, + condition: [ + "=", + ["field", ORDERS.PRODUCT_ID, null], + ["field", PRODUCTS.ID, { "join-alias": "Products" }], + ], + alias: "Products", + }, + ], + }, +}; + +const tableQuestionWithJoinOnQuestion = card => ({ + display: "table", + query: { + "source-table": ORDERS_ID, + joins: [ + { + fields: "all", + "source-table": `card__${card.id}`, + condition: [ + "=", + ["field", ORDERS.ID, null], + ["field", ORDERS.ID, { "join-alias": `Question ${card.id}` }], + ], + alias: `Question ${card.id}`, + }, + ], + }, +}); + +const tableQuestionWithJoinAndFields = { + display: "table", + query: { + "source-table": ORDERS_ID, + joins: [ + { + "source-table": PRODUCTS_ID, + fields: [["field", PRODUCTS.CATEGORY, { "join-alias": "Products" }]], + condition: [ + "=", + ["field", ORDERS.PRODUCT_ID, null], + ["field", PRODUCTS.ID, { "join-alias": "Products" }], + ], + alias: "Products", + }, + ], + }, +}; + +const tableQuestionWithExpression = { + display: "table", + query: { + "source-table": ORDERS_ID, + expressions: { + Math: ["+", 1, 1], + }, + }, +}; + +const tableQuestionWithExpressionAndFields = { + display: "table", + query: { + "source-table": ORDERS_ID, + expressions: { + Math: ["+", 1, 1], + }, + fields: [ + ["field", ORDERS.ID, { "base-type": "type/BigInteger" }], + ["expression", "Math", { "base-type": "type/Integer" }], + ], + }, +}; + +const tableWithAggregations = { + display: "table", + query: { + "source-table": ORDERS_ID, + aggregation: [ + ["count"], + ["sum", ["field", ORDERS.QUANTITY, { "base-type": "type/Integer" }]], + ], + }, +}; + +const nativeQuestion = { + display: "table", + native: { + query: "SELECT * FROM ORDERS", + }, +}; + +const nestedQuestion = card => ({ + display: "table", + query: { + "source-table": `card__${card.id}`, + }, +}); + +const nestedQuestionWithJoinOnQuestion = card => ({ + display: "table", + query: { + "source-table": `card__${card.id}`, + joins: [ + { + fields: "all", + "source-table": `card__${card.id}`, + condition: [ + "=", + ["field", ORDERS.ID, null], + ["field", ORDERS.ID, { "join-alias": `Question ${card.id}` }], + ], + alias: `Question ${card.id}`, + }, + ], + }, +}); + +describe("scenarios > visualizations > table column settings", () => { + beforeEach(() => { + restore(); + cy.signInAsNormalUser(); + cy.intercept("POST", "/api/dataset").as("dataset"); + }); + + describe("tables", () => { + it("should be able to show and hide table fields", () => { + cy.createQuestion(tableQuestion, { visitQuestion: true }); + openSettings(); + + cy.log("hide a column"); + visibleColumns().within(() => hideColumn("Tax")); + visibleColumns().findByText("Tax").should("not.exist"); + visibleColumns().findByText("ID").should("exist"); + disabledColumns().findByText("Tax").should("exist"); + additionalColumns().findByText("Tax").should("not.exist"); + visualization().findByText("Tax").should("not.exist"); + + cy.log("re-run the query"); + runQuery(); + cy.wait("@dataset"); + visualization().findByText("Tax").should("not.exist"); + + cy.log("show a column"); + additionalColumns().within(() => showColumn("Tax")); + cy.wait("@dataset"); + visibleColumns().findByText("Tax").should("exist"); + visibleColumns().findByText("ID").should("exist"); + disabledColumns().findByText("Tax").should("not.exist"); + additionalColumns().findByText("Tax").should("not.exist"); + scrollVisualization(); + visualization().findByText("Tax").should("exist"); + }); + + it("should be able to show and hide table fields with in a join", () => { + cy.createQuestion(tableQuestionWithJoin, { visitQuestion: true }); + openSettings(); + + cy.log("hide a column"); + visibleColumns().within(() => hideColumn("Products → Category")); + visibleColumns().findByText("Products → Category").should("not.exist"); + visibleColumns().findByText("Products → Ean").should("exist"); + disabledColumns().findByText("Products → Category").should("exist"); + additionalColumns().findByText("Category").should("not.exist"); + visualization().findByText("Products → Category").should("not.exist"); + + cy.log("re-run the query"); + runQuery(); + cy.wait("@dataset"); + visualization().findByText("Products → Category").should("not.exist"); + + cy.log("show a column"); + additionalColumns().within(() => showColumn("Category")); + cy.wait("@dataset"); + visibleColumns().findByText("Products → Category").should("exist"); + visibleColumns().findByText("Products → Ean").should("exist"); + disabledColumns().findByText("Category").should("not.exist"); + additionalColumns().findByText("Category").should("not.exist"); + scrollVisualization(); + visualization().findByText("Products → Category").should("exist"); + }); + + it("should be able to show and hide table fields with in a join with fields", () => { + cy.createQuestion(tableQuestionWithJoinAndFields, { + visitQuestion: true, + }); + openSettings(); + + cy.log("hide an existing column"); + visibleColumns().within(() => hideColumn("Products → Category")); + visibleColumns().findByText("Products → Category").should("not.exist"); + visibleColumns().findByText("Products → Ean").should("not.exist"); + disabledColumns().findByText("Products → Category").should("exist"); + additionalColumns().findByText("Category").should("not.exist"); + visualization().findByText("Products → Category").should("not.exist"); + + cy.log("re-run the query"); + runQuery(); + cy.wait("@dataset"); + visualization().findByText("Products → Category").should("not.exist"); + + cy.log("show an existing column"); + additionalColumns().within(() => showColumn("Ean")); + cy.wait("@dataset"); + visibleColumns().findByText("Products → Ean").should("exist"); + visibleColumns().findByText("Products → Category").should("not.exist"); + disabledColumns().findByText("Products → Ean").should("not.exist"); + additionalColumns().findByText("Ean").should("not.exist"); + additionalColumns().findByText("Category").should("exist"); + scrollVisualization(); + visualization().findByText("Products → Ean").should("exist"); + + cy.log("show a new column"); + additionalColumns().within(() => showColumn("Category")); + cy.wait("@dataset"); + visibleColumns().findByText("Products → Ean").should("exist"); + visibleColumns().findByText("Products → Category").should("exist"); + additionalColumns().findByText("Ean").should("not.exist"); + additionalColumns().findByText("Category").should("not.exist"); + additionalColumns().findByText("Rating").should("exist"); + scrollVisualization(); + visualization().findByText("Products → Category").should("exist"); + }); + + it("should be able to show and hide implicitly joinable fields for a table", () => { + cy.createQuestion(tableQuestion, { visitQuestion: true }); + openSettings(); + + cy.log("show a column"); + additionalColumns().within(() => showColumn("Category")); + cy.wait("@dataset"); + visibleColumns().findByText("Product → Category").should("exist"); + additionalColumns().findByText("Category").should("not.exist"); + scrollVisualization(); + visualization().findByText("Product → Category").should("exist"); + + cy.log("hide a column"); + visibleColumns().within(() => hideColumn("Product → Category")); + visibleColumns().findByText("Product → Category").should("not.exist"); + disabledColumns().findByText("Product → Category").should("exist"); + additionalColumns().findByText("Category").should("not.exist"); + scrollVisualization(); + visualization().findByText("Product → Category").should("not.exist"); + + cy.log("re-run the query"); + runQuery(); + cy.wait("@dataset"); + visibleColumns().findByText("Product → Category").should("not.exist"); + disabledColumns().findByText("Product → Category").should("not.exist"); + additionalColumns().findByText("Category").should("exist"); + scrollVisualization(); + visualization().findByText("Product → Category").should("not.exist"); + }); + + it("should be able to show and hide custom expressions for a table", () => { + cy.createQuestion(tableQuestionWithExpression, { + visitQuestion: true, + }); + openSettings(); + + cy.log("hide a column"); + visibleColumns().within(() => hideColumn("Math")); + visibleColumns().findByText("Math").should("not.exist"); + disabledColumns().findByText("Math").should("exist"); + scrollVisualization(); + visualization().findByText("Math").should("not.exist"); + + cy.log("re-run the query"); + runQuery(); + cy.wait("@dataset"); + scrollVisualization(); + visualization().findByText("Math").should("not.exist"); + + cy.log("show a column"); + additionalColumns().within(() => showColumn("Math")); + cy.wait("@dataset"); + visibleColumns().findByText("Math").should("exist"); + additionalColumns().findByText("Math").should("not.exist"); + scrollVisualization(); + visualization().findByText("Math").should("exist"); + }); + + it("should be able to show and hide custom expressions for a table with selected fields", () => { + cy.createQuestion(tableQuestionWithExpressionAndFields, { + visitQuestion: true, + }); + openSettings(); + + cy.log("hide a column"); + visibleColumns().within(() => hideColumn("Math")); + visibleColumns().findByText("Math").should("not.exist"); + disabledColumns().findByText("Math").should("exist"); + visualization().findByText("Math").should("not.exist"); + + cy.log("re-run the query"); + runQuery(); + cy.wait("@dataset"); + visualization().findByText("Math").should("not.exist"); + + cy.log("show a column"); + additionalColumns().within(() => showColumn("Math")); + cy.wait("@dataset"); + visibleColumns().findByText("Math").should("exist"); + additionalColumns().findByText("Math").should("not.exist"); + visualization().findByText("Math").should("exist"); + }); + + it("should be able to show and hide columns from aggregations", () => { + cy.createQuestion(tableWithAggregations, { visitQuestion: true }); + openSettings(); + + cy.log("hide a column"); + visibleColumns().within(() => hideColumn("Count")); + visibleColumns().findByText("Count").should("not.exist"); + visibleColumns().findByText("Sum of Quantity").should("exist"); + disabledColumns().findByText("Count").should("exist"); + visualization().findByText("Count").should("not.exist"); + + cy.log("show a column"); + disabledColumns().within(() => showColumn("Count")); + visibleColumns().findByText("Count").should("exist"); + visibleColumns().findByText("Sum of Quantity").should("exist"); + visualization().findByText("Count").should("exist"); + + cy.log("hide a column with an inner field"); + visibleColumns().within(() => hideColumn("Sum of Quantity")); + visibleColumns().findByText("Sum of Quantity").should("not.exist"); + visibleColumns().findByText("Count").should("exist"); + disabledColumns().findByText("Sum of Quantity").should("exist"); + visualization().findByText("Sum of Quantity").should("not.exist"); + + cy.log("show a column with an inner field"); + disabledColumns().within(() => showColumn("Sum of Quantity")); + visibleColumns().findByText("Sum of Quantity").should("exist"); + visibleColumns().findByText("Count").should("exist"); + visualization().findByText("Sum of Quantity").should("exist"); + }); + }); + + describe("nested structured questions", () => { + it("should be able to show and hide fields from a nested query", () => { + cy.createQuestion(tableQuestion).then(({ body: card }) => { + cy.createQuestion(nestedQuestion(card), { visitQuestion: true }); + }); + openSettings(); + + cy.log("hide a column"); + visibleColumns().within(() => hideColumn("Tax")); + visibleColumns().findByText("Tax").should("not.exist"); + disabledColumns().findByText("Tax").should("exist"); + scrollVisualization(); + visualization().findByText("Tax").should("not.exist"); + + cy.log("re-run the query"); + runQuery(); + cy.wait("@dataset"); + scrollVisualization(); + visualization().findByText("Tax").should("not.exist"); + + cy.log("show a column"); + additionalColumns().within(() => showColumn("Tax")); + cy.wait("@dataset"); + visibleColumns().findByText("Tax").should("exist"); + additionalColumns().findByText("Tax").should("not.exist"); + scrollVisualization(); + visualization().findByText("Tax").should("exist"); + }); + + it.skip("should be able to show and hide fields from a nested query with joins (metabase#32373)", () => { + cy.createQuestion(tableQuestionWithJoin).then(({ body: card }) => { + cy.createQuestion(nestedQuestion(card), { visitQuestion: true }); + }); + openSettings(); + + cy.log("hide a column"); + visibleColumns().within(() => hideColumn("Products → Ean")); + visibleColumns().findByText("Products → Ean").should("not.exist"); + disabledColumns().findByText("Products → Ean").should("exist"); + scrollVisualization(); + visualization().findByText("Products → Ean").should("not.exist"); + + cy.log("re-run the query"); + runQuery(); + cy.wait("@dataset"); + scrollVisualization(); + visualization().findByText("Products → Ean").should("not.exist"); + + cy.log("show a column"); + additionalColumns().within(() => showColumn("Products → Ean")); + cy.wait("@dataset"); + visibleColumns().findByText("Products → Ean").should("exist"); + additionalColumns().findByText("Products → Ean").should("not.exist"); + scrollVisualization(); + visualization().findByText("Products → Ean").should("exist"); + }); + + it.skip("should be able to show and hide fields from a nested query with joins and fields (metabase#32373)", () => { + cy.createQuestion(tableQuestionWithJoinAndFields).then( + ({ body: card }) => { + cy.createQuestion(nestedQuestion(card), { visitQuestion: true }); + }, + ); + openSettings(); + + cy.log("hide an existing column"); + visibleColumns().within(() => hideColumn("Products → Category")); + visibleColumns().findByText("Products → Category").should("not.exist"); + disabledColumns().findByText("Products → Category").should("exist"); + scrollVisualization(); + visualization().findByText("Products → Category").should("not.exist"); + + cy.log("re-run the query"); + runQuery(); + cy.wait("@dataset"); + scrollVisualization(); + visualization().findByText("Products → Category").should("not.exist"); + + cy.log("show a new column"); + additionalColumns().within(() => showColumn("Ean")); + cy.wait("@dataset"); + visibleColumns().findByText("Product → Ean").should("exist"); + additionalColumns().findByText("Ean").should("not.exist"); + scrollVisualization(); + visualization().findByText("Products → Ean").should("exist"); + + cy.log("show an existing column"); + additionalColumns().within(() => showColumn("Products → Category")); + cy.wait("@dataset"); + visibleColumns().findByText("Products → Category").should("exist"); + visibleColumns().findByText("Products → Ean").should("exist"); + additionalColumns().findByText("Products → Category").should("not.exist"); + scrollVisualization(); + visualization().findByText("Products → Category").should("exist"); + visualization().findByText("Products → Ean").should("exist"); + }); + + it("should be able to show and hide implicitly joinable fields for a nested query", () => { + cy.createQuestion(tableQuestion).then(({ body: card }) => { + cy.createQuestion(nestedQuestion(card), { visitQuestion: true }); + }); + openSettings(); + + cy.log("show a column"); + additionalColumns().within(() => showColumn("Category")); + cy.wait("@dataset"); + visibleColumns().findByText("Product → Category").should("exist"); + additionalColumns().findByText("Category").should("not.exist"); + scrollVisualization(); + visualization().findByText("Product → Category").should("exist"); + + cy.log("hide a column"); + visibleColumns().within(() => hideColumn("Product → Category")); + visibleColumns().findByText("Product → Category").should("not.exist"); + disabledColumns().findByText("Product → Category").should("exist"); + visualization().findByText("Product → Category").should("not.exist"); + + cy.log("re-run the query"); + runQuery(); + cy.wait("@dataset"); + visibleColumns().findByText("Product → Category").should("not.exist"); + additionalColumns().findByText("Category").should("exist"); + scrollVisualization(); + visualization().findByText("Product → Category").should("not.exist"); + }); + + it("should be able to show and hide custom expressions from a nested query", () => { + cy.createQuestion(tableQuestionWithExpression).then(({ body: card }) => { + cy.createQuestion(nestedQuestion(card), { visitQuestion: true }); + }); + openSettings(); + + cy.log("hide a column"); + visibleColumns().within(() => hideColumn("Math")); + visibleColumns().findByText("Math").should("not.exist"); + disabledColumns().findByText("Math").should("exist"); + scrollVisualization(); + visualization().findByText("Math").should("not.exist"); + + cy.log("re-run the query"); + runQuery(); + cy.wait("@dataset"); + scrollVisualization(); + visualization().findByText("Math").should("not.exist"); + + cy.log("show a column"); + additionalColumns().within(() => showColumn("Math")); + cy.wait("@dataset"); + visibleColumns().findByText("Math").should("exist"); + additionalColumns().findByText("Math").should("not.exist"); + scrollVisualization(); + visualization().findByText("Math").should("exist"); + }); + + it("should be able to show and hide columns from aggregations from a nested query", () => { + cy.createQuestion(tableWithAggregations).then(({ body: card }) => { + cy.createQuestion(nestedQuestion(card), { visitQuestion: true }); + }); + openSettings(); + + cy.log("hide a column"); + visibleColumns().within(() => hideColumn("Count")); + visibleColumns().findByText("Count").should("not.exist"); + visibleColumns().findByText("Sum of Quantity").should("exist"); + disabledColumns().findByText("Count").should("exist"); + visualization().findByText("Count").should("not.exist"); + runQuery(); + cy.wait("@dataset"); + + cy.log("show a column"); + additionalColumns().within(() => showColumn("Count")); + cy.wait("@dataset"); + visibleColumns().findByText("Count").should("exist"); + visibleColumns().findByText("Sum of Quantity").should("exist"); + visualization().findByText("Count").should("exist"); + + cy.log("hide a column with an inner field"); + visibleColumns().within(() => hideColumn("Sum of Quantity")); + visibleColumns().findByText("Sum of Quantity").should("not.exist"); + visibleColumns().findByText("Count").should("exist"); + disabledColumns().findByText("Sum of Quantity").should("exist"); + visualization().findByText("Sum of Quantity").should("not.exist"); + runQuery(); + cy.wait("@dataset"); + + cy.log("show a column with an inner field"); + additionalColumns().within(() => showColumn("Sum of Quantity")); + cy.wait("@dataset"); + visibleColumns().findByText("Sum of Quantity").should("exist"); + visibleColumns().findByText("Count").should("exist"); + visualization().findByText("Sum of Quantity").should("exist"); + }); + + it("should be able to show and hide questions from a nested query with a self join", () => { + cy.createQuestion(tableQuestion).then(({ body: card }) => { + const columnName = "Tax"; + const columnLongName = `Question ${card.id} → ${columnName}`; + + cy.createQuestion(nestedQuestionWithJoinOnQuestion(card), { + visitQuestion: true, + }); + openSettings(); + + cy.log("hide a column"); + visibleColumns().within(() => hideColumn(columnLongName)); + visibleColumns().findByText(columnLongName).should("not.exist"); + disabledColumns().findByText(columnLongName).should("exist"); + scrollVisualization(); + visualization().findByText(columnLongName).should("not.exist"); + + cy.log("re-run the query"); + runQuery(); + cy.wait("@dataset"); + scrollVisualization(); + visualization().findByText(columnLongName).should("not.exist"); + + cy.log("show a column"); + additionalColumns().within(() => showColumn(columnName)); + cy.wait("@dataset"); + visibleColumns().findByText(columnLongName).should("exist"); + scrollVisualization(); + visualization().findByText(columnLongName).should("exist"); + }); + }); + + it("should be able to show and hide custom expressions from a joined question", () => { + cy.createQuestion(tableQuestionWithExpression).then(({ body: card }) => { + cy.createQuestion(tableQuestionWithJoinOnQuestion(card), { + visitQuestion: true, + }); + const columnName = "Math"; + const columnLongName = `Question ${card.id} → ${columnName}`; + + openSettings(); + + cy.log("hide a column"); + visibleColumns().within(() => hideColumn(columnLongName)); + visibleColumns().findByText(columnLongName).should("not.exist"); + disabledColumns().findByText(columnLongName).should("exist"); + scrollVisualization(); + visualization().findByText(columnLongName).should("not.exist"); + + cy.log("re-run the query"); + runQuery(); + cy.wait("@dataset"); + scrollVisualization(); + visualization().findByText(columnLongName).should("not.exist"); + + cy.log("show a column"); + additionalColumns().within(() => showColumn(columnName)); + cy.wait("@dataset"); + visibleColumns().findByText(columnLongName).should("exist"); + additionalColumns().findByText(columnName).should("not.exist"); + scrollVisualization(); + visualization().findByText(columnLongName).should("exist"); + }); + }); + }); + + describe("nested native questions", () => { + it("should be able to show and hide fields from a nested native query", () => { + cy.createNativeQuestion(nativeQuestion).then(({ body: card }) => { + cy.createQuestion(nestedQuestion(card), { visitQuestion: true }); + }); + openSettings(); + + cy.log("hide a column"); + visibleColumns().within(() => hideColumn("TAX")); + visibleColumns().findByText("TAX").should("not.exist"); + disabledColumns().findByText("TAX").should("exist"); + scrollVisualization(); + visualization().findByText("TAX").should("not.exist"); + + cy.log("re-run the query"); + runQuery(); + cy.wait("@dataset"); + scrollVisualization(); + visualization().findByText("TAX").should("not.exist"); + + cy.log("show a column"); + additionalColumns().within(() => showColumn("TAX")); + cy.wait("@dataset"); + visibleColumns().findByText("TAX").should("exist"); + scrollVisualization(); + visualization().findByText("TAX").should("exist"); + }); + }); +}); + +const runQuery = () => { + cy.findByTestId("query-builder-main").icon("play").click(); +}; + +const showColumn = column => { + cy.findByTestId(`${column}-add-button`).click(); +}; + +const hideColumn = column => { + cy.findByTestId(`${column}-hide-button`).click(); +}; + +const openSettings = () => { + cy.findByTestId("viz-settings-button").click(); +}; + +const visualization = () => { + return cy.findByTestId("TableInteractive-root"); +}; + +const scrollVisualization = (position = "right") => { + cy.get("#main-data-grid").scrollTo(position); +}; + +const visibleColumns = () => { + return cy.findByTestId("visible-columns"); +}; + +const disabledColumns = () => { + return cy.findByTestId("disabled-columns"); +}; + +const additionalColumns = () => { + return cy.findByTestId("additional-columns"); +}; diff --git a/frontend/src/metabase/visualizations/components/settings/ChartSettingOrderedColumns.jsx b/frontend/src/metabase/visualizations/components/settings/ChartSettingOrderedColumns.jsx index f0ec7227cf7..da3292557aa 100644 --- a/frontend/src/metabase/visualizations/components/settings/ChartSettingOrderedColumns.jsx +++ b/frontend/src/metabase/visualizations/components/settings/ChartSettingOrderedColumns.jsx @@ -123,7 +123,7 @@ export const ChartSettingOrderedColumns = ({ return ( <div className="list" role="list"> {enabledColumns.length > 0 ? ( - <div role="group" title="visible-columns"> + <div role="group" data-testid="visible-columns"> <ChartSettingOrderedItems items={enabledColumns} getItemName={getColumnName} @@ -153,7 +153,7 @@ export const ChartSettingOrderedColumns = ({ ))} </div> {additionalFieldOptions.count > 0 && ( - <div> + <div data-testid="additional-columns"> {additionalFieldOptions.dimensions.map((dimension, index) => ( <ColumnItem key={index} -- GitLab