Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
metrics.cy.spec.js 8.72 KiB
import {
  restore,
  popover,
  modal,
  openOrdersTable,
  visualize,
  summarize,
} from "__support__/e2e/cypress";
import { SAMPLE_DATABASE } from "__support__/e2e/cypress_sample_database";

const { ORDERS, ORDERS_ID } = SAMPLE_DATABASE;

describe("scenarios > admin > datamodel > metrics", () => {
  beforeEach(() => {
    restore();
    cy.signInAsAdmin();
    cy.viewport(1400, 860);
  });

  it("should be possible to sort by metric (metabase#8283)", () => {
    cy.request("POST", "/api/metric", {
      name: "Revenue",
      description: "Sum of orders subtotal",
      table_id: ORDERS_ID,
      definition: {
        "source-table": ORDERS_ID,
        aggregation: [["sum", ["field", ORDERS.SUBTOTAL, null]]],
      },
    });

    openOrdersTable({ mode: "notebook" });

    summarize({ mode: "notebook" });
    cy.findByText("Common Metrics").click();
    cy.findByText("Revenue").click();

    cy.findByText("Pick a column to group by").click();
    cy.findByText("Created At").click();

    cy.findByText("Sort").click();

    // Sorts ascending by default
    popover()
      .contains("Revenue")
      .click();

    // Let's make sure it's possible to sort descending as well
    cy.icon("arrow_up").click();

    cy.icon("arrow_down")
      .parent()
      .contains("Revenue");

    visualize();
    // Visualization will render line chart by default. Switch to the table.
    cy.icon("table2").click();

    cy.findAllByRole("grid").as("table");
    cy.get("@table")
      .first()
      .as("tableHeader")
      .within(() => {
        cy.get(".cellData")
          .eq(1)
          .invoke("text")
          .should("eq", "Revenue");
      });

    cy.get("@table")
      .last()
      .as("tableBody")
      .within(() => {
        cy.get(".cellData")
          .eq(1)
          .invoke("text")
          .should("eq", "50,072.98");
      });
  });

  describe("with no metrics", () => {
    it("should show no metrics in the list", () => {
      cy.visit("/admin/datamodel/metrics");
      cy.findByText(
        "Create metrics to add them to the Summarize dropdown in the query builder",
      );
    });

    it("should show how to create metrics", () => {
      cy.visit("/reference/metrics");
      cy.findByText(
        "Metrics are the official numbers that your team cares about",
      );
      cy.findByText("Learn how to create metrics");
    });

    it("custom expression aggregation should work in metrics (metabase#22700)", () => {
      cy.intercept("POST", "/api/dataset").as("dataset");

      const customExpression = "Count / Distinct([Product ID])";

      cy.visit("/admin/datamodel/metrics");

      cy.button("New metric").click();
      cy.findByText("Select a table").click();
      cy.findByText("Orders").click();
      // It sees that there is one dataset query for each of the fields:
      // `data`, `filtered by` and `view`
      cy.wait(["@dataset", "@dataset", "@dataset"]);

      cy.findByText("Count").click();
      popover()
        .contains("Custom Expression")
        .click();

      cy.get(".ace_text-input")
        .click()
        .type(`{selectall}{del}${customExpression}`)
        .blur();

      cy.findByPlaceholderText("Name (required)").type("Foo");

      cy.button("Done").click();
      cy.wait("@dataset");

      // The test should fail on this step first
      cy.findByText("Result: 93.8");

      // Let's make sure the custom expression is still preserved
      cy.findByText("Foo").click();
      cy.get(".ace_content").should("contain", customExpression);
    });
  });

  describe("with metrics", () => {
    beforeEach(() => {
      // CREATE METRIC
      cy.request("POST", "/api/metric", {
        definition: {
          aggregation: ["count"],
          filter: ["<", ["field", ORDERS.TOTAL, null], 100],
          "source-table": ORDERS_ID,
        },
        name: "orders < 100",
        description: "Count of orders with a total under $100.",
        table_id: ORDERS_ID,
      });
    });

    it("should show no questions based on a new metric", () => {
      cy.visit("/reference/metrics/1/questions");
      cy.findAllByText("Questions about orders < 100");
      cy.findByText("Loading...");
      cy.findByText("Loading...").should("not.exist");
      cy.findByText(
        "Questions about this metric will appear here as they're added",
      );
    });

    it("should see a newly asked question in its questions list", () => {
      // Ask a new qustion
      cy.visit("/reference/metrics/1/questions");
      cy.get(".full")
        .find(".Button")
        .click();
      cy.findByText("Filter").click();
      cy.findByText("Total").click();
      cy.findByText("Equal to").click();
      cy.findByText("Greater than").click();
      cy.findByPlaceholderText("Enter a number").type("50");
      cy.findByText("Add filter").click();
      cy.findByText("Save").click();
      cy.findAllByText("Save")
        .last()
        .click();
      cy.findByText("Not now").click();

      // Check the list
      cy.visit("/reference/metrics/1/questions");
      cy.findByText("Our analysis").should("not.exist");
      cy.findAllByText("Questions about orders < 100");
      cy.findByText("Orders, orders < 100, Filtered by Total");
    });

    it("should show the metric detail view for a specific id", () => {
      cy.visit("/admin/datamodel/metric/1");
      cy.findByText("Edit Your Metric");
      cy.findByText("Preview");
    });

    it("should update that metric", () => {
      cy.visit("/admin");
      cy.contains("Data Model").click();
      cy.contains("Metrics").click();

      cy.contains("orders < 100")
        .parent()
        .parent()
        .parent()
        .find(".Icon-ellipsis")
        .click();
      cy.contains("Edit Metric").click();

      // update the filter from "< 100" to "> 10"
      cy.url().should("match", /metric\/1$/);
      cy.contains("Edit Your Metric");
      cy.contains(/Total\s+is less than/).click();
      popover()
        .contains("Less than")
        .click();
      popover()
        .contains("Greater than")
        .click();
      popover()
        .find("input")
        .type("{SelectAll}10");
      popover()
        .contains("Update filter")
        .click();

      // confirm that the preview updated
      cy.contains("Result: 18758");

      // update name and description, set a revision note, and save the update
      cy.get('[name="name"]').type("{selectall}orders > 10");
      cy.get('[name="description"]').type(
        "{selectall}Count of orders with a total over $10.",
      );
      cy.get('[name="revision_message"]').type("time for a change");
      cy.contains("Save changes").click();

      // get redirected to previous page and see the new metric name
      cy.url().should("match", /datamodel\/metrics$/);
      cy.contains("orders > 10");

      // clean up
      cy.contains("orders > 10")
        .parent()
        .parent()
        .parent()
        .find(".Icon-ellipsis")
        .click();
      cy.contains("Retire Metric").click();
      modal()
        .find("textarea")
        .type("delete it");
      modal()
        .contains("button", "Retire")
        .click();
    });
  });

  describe("custom metrics", () => {
    it("should save the metric using custom expressions (metabase#13022)", () => {
      cy.request("POST", "/api/metric", {
        name: "13022_Metric",
        desription: "desc",
        table_id: ORDERS_ID,
        definition: {
          "source-table": ORDERS_ID,
          aggregation: [
            [
              "aggregation-options",
              [
                "sum",
                [
                  "*",
                  ["field", ORDERS.DISCOUNT, null],
                  ["field", ORDERS.QUANTITY, null],
                ],
              ],
              { "display-name": "CE" },
            ],
          ],
        },
      });

      cy.log(
        "**Navigate to the metrics page and assert the metric was indeed saved**",
      );
      cy.visit("/admin/datamodel/metrics");
      cy.findByText(
        'Unexpected input given to normalize. Expected type to be "object", found "string".',
      ).should("not.exist");

      cy.findByText("13022_Metric"); // Name
      cy.findByText("Orders, CE"); // Definition
    });

    it("should show CE that uses 'AND/OR' (metabase#13069, metabase#13070)", () => {
      cy.visit("/admin/datamodel/metrics");
      cy.findByText("New metric").click();
      cy.findByText("Select a table").click();
      cy.findByText("Orders").click();
      cy.findByText("Add filters to narrow your answer").click();
      cy.findByText("Custom Expression").click();
      cy.get(".ace_text-input")
        .clear()
        .type("[ID] > 0 OR [ID] < 9876543210");
      cy.button("Done").click();

      cy.log("**Assert that there is a filter text visible**");
      cy.findByText("ID > 0 OR ID < 9876543210");
    });
  });
});