451 lines
14 KiB
JavaScript
451 lines
14 KiB
JavaScript
const { H } = cy;
|
|
|
|
import * as SQLFilter from "../native-filters/helpers/e2e-sql-filter-helpers";
|
|
|
|
import {
|
|
questionDetails as questionDetails2,
|
|
questionDetailsWithDefaults,
|
|
} from "./shared/embedding-dashboard";
|
|
import { questionDetails } from "./shared/embedding-native";
|
|
|
|
describe("scenarios > embedding > native questions", () => {
|
|
beforeEach(() => {
|
|
H.restore();
|
|
cy.signInAsAdmin();
|
|
});
|
|
|
|
context("UI", () => {
|
|
function createAndVisitQuestion({ requiredTagName, defaultValue } = {}) {
|
|
const details = structuredClone(questionDetails);
|
|
|
|
if (requiredTagName) {
|
|
details.native["template-tags"][requiredTagName].default = defaultValue;
|
|
details.native["template-tags"][requiredTagName].required = true;
|
|
}
|
|
|
|
H.createNativeQuestion(details, {
|
|
wrapId: true,
|
|
visitQuestion: true,
|
|
});
|
|
|
|
cy.get("@questionId").then((questionId) => {
|
|
H.openLegacyStaticEmbeddingModal({
|
|
resource: "question",
|
|
resourceId: questionId,
|
|
activeTab: "parameters",
|
|
});
|
|
});
|
|
}
|
|
|
|
it("should not display disabled parameters", () => {
|
|
createAndVisitQuestion();
|
|
|
|
H.publishChanges("card", ({ request }) => {
|
|
assert.deepEqual(request.body.embedding_params, {
|
|
id: "disabled",
|
|
state: "disabled",
|
|
created_at: "disabled",
|
|
total: "disabled",
|
|
source: "disabled",
|
|
product_id: "disabled",
|
|
});
|
|
});
|
|
|
|
H.visitIframe();
|
|
|
|
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
|
|
cy.contains("Lora Cronin");
|
|
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
|
|
cy.contains("Organic");
|
|
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
|
|
cy.contains("39.58");
|
|
|
|
H.filterWidget().should("not.exist");
|
|
});
|
|
|
|
it("should display and work with enabled parameters while hiding the locked one", () => {
|
|
createAndVisitQuestion();
|
|
|
|
H.setEmbeddingParameter("Order ID", "Editable");
|
|
H.setEmbeddingParameter("Created At", "Editable");
|
|
H.setEmbeddingParameter("Total", "Locked");
|
|
H.setEmbeddingParameter("State", "Editable");
|
|
H.setEmbeddingParameter("Product ID", "Editable");
|
|
|
|
H.publishChanges("card", ({ request }) => {
|
|
const actual = request.body.embedding_params;
|
|
|
|
const expected = {
|
|
id: "enabled",
|
|
created_at: "enabled",
|
|
total: "locked",
|
|
state: "enabled",
|
|
product_id: "enabled",
|
|
source: "disabled",
|
|
};
|
|
|
|
assert.deepEqual(actual, expected);
|
|
});
|
|
|
|
cy.get("@questionId").then((questionId) => {
|
|
const payload = {
|
|
resource: { question: questionId },
|
|
params: { total: [] },
|
|
};
|
|
|
|
H.visitEmbeddedPage(payload);
|
|
});
|
|
|
|
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
|
|
cy.contains("Organic");
|
|
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
|
|
cy.contains("Twitter").should("not.exist");
|
|
|
|
// Created At: Q2 2023
|
|
H.filterWidget().contains("Created At").click();
|
|
H.popover().within(() => {
|
|
cy.findByText(/20\d+/).click();
|
|
cy.contains("2023").click();
|
|
});
|
|
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
|
|
cy.findByText("Q2").click();
|
|
|
|
// State: is not KS
|
|
H.filterWidget().contains("State").click();
|
|
cy.findByPlaceholderText("Search the list").type("KS{enter}");
|
|
cy.findAllByTestId(/-filter-value$/).should("have.length", 1);
|
|
cy.findByLabelText("KS").should("be.visible").click();
|
|
cy.button("Add filter").click();
|
|
|
|
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
|
|
cy.findByText("Logan Weber").should("not.exist");
|
|
|
|
// Product ID is 10
|
|
cy.findByPlaceholderText("Product ID").type("10{enter}");
|
|
|
|
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
|
|
cy.contains("Affiliate").should("not.exist");
|
|
|
|
// Let's try to remove one filter
|
|
H.filterWidget({ name: "Q2 2023" }).icon("close").click();
|
|
|
|
// Order ID is 926 - there should be only one result after this
|
|
H.filterWidget().contains("Order ID").click();
|
|
cy.findByPlaceholderText("Enter an ID").type("926");
|
|
cy.button("Add filter").click();
|
|
|
|
cy.findAllByRole("row").should("have.length", 1);
|
|
|
|
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
|
|
cy.findByText("December 29, 2024, 4:54 AM");
|
|
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
|
|
cy.findByText("CO");
|
|
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
|
|
cy.findByText("Sid Mills").should("not.exist");
|
|
|
|
cy.location("search")
|
|
.should("include", "product_id=10")
|
|
.and("include", "created_at=")
|
|
.and("include", "id=926")
|
|
.and("include", "state=KS");
|
|
});
|
|
|
|
it("should handle required parameters", () => {
|
|
createAndVisitQuestion({ requiredTagName: "total", defaultValue: [100] });
|
|
|
|
H.assertEmbeddingParameter("Total", "Editable");
|
|
|
|
H.publishChanges("card", ({ request }) => {
|
|
const actual = request.body.embedding_params;
|
|
|
|
// We only expect total to be "enabled" because the rest
|
|
// weren't touched and therefore aren't changed, whereas
|
|
// "enabled" must be set by default for required params.
|
|
const expected = {
|
|
id: "disabled",
|
|
state: "disabled",
|
|
created_at: "disabled",
|
|
total: "enabled",
|
|
source: "disabled",
|
|
product_id: "disabled",
|
|
};
|
|
|
|
assert.deepEqual(actual, expected);
|
|
});
|
|
|
|
H.visitIframe();
|
|
|
|
// Filter widget must be visible
|
|
H.filterWidget().contains("Total");
|
|
|
|
// And its default value must be in the URL
|
|
cy.location("search").should("eq", "?total=100");
|
|
});
|
|
|
|
it("should (dis)allow setting parameters as required for a published embedding", () => {
|
|
createAndVisitQuestion();
|
|
// Make one parameter editable and one locked
|
|
H.setEmbeddingParameter("Order ID", "Editable");
|
|
H.setEmbeddingParameter("Total", "Locked");
|
|
|
|
H.publishChanges("card");
|
|
H.closeStaticEmbeddingModal();
|
|
|
|
cy.findByTestId("native-query-editor-container")
|
|
.findByText("Open Editor")
|
|
.click();
|
|
|
|
// Open variable editor
|
|
cy.findByTestId("native-query-editor-action-buttons")
|
|
.icon("variable")
|
|
.click();
|
|
|
|
// Now check that all disabled parameters can't be required and the rest can
|
|
assertRequiredEnabledForName({ name: "id", enabled: true });
|
|
assertRequiredEnabledForName({ name: "total", enabled: true });
|
|
// disabled parameters
|
|
assertRequiredEnabledForName({ name: "created_at", enabled: false });
|
|
assertRequiredEnabledForName({ name: "source", enabled: false });
|
|
assertRequiredEnabledForName({ name: "state", enabled: false });
|
|
assertRequiredEnabledForName({ name: "product_id", enabled: false });
|
|
});
|
|
});
|
|
|
|
context("API", () => {
|
|
beforeEach(() => {
|
|
H.createNativeQuestion(questionDetails, {
|
|
wrapId: true,
|
|
});
|
|
});
|
|
|
|
it("should hide filters via url", () => {
|
|
cy.get("@questionId").then((questionId) => {
|
|
cy.request("PUT", `/api/card/${questionId}`, {
|
|
enable_embedding: true,
|
|
embedding_params: {
|
|
id: "enabled",
|
|
product_id: "enabled",
|
|
state: "enabled",
|
|
created_at: "enabled",
|
|
total: "enabled",
|
|
},
|
|
});
|
|
|
|
const payload = {
|
|
resource: { question: questionId },
|
|
params: {},
|
|
};
|
|
|
|
// It should be possible to both set the filter value and hide it at the same time.
|
|
// That's the synonymous to the locked filter.
|
|
H.visitEmbeddedPage(payload, {
|
|
setFilters: { id: 92 },
|
|
additionalHashOptions: {
|
|
hideFilters: ["id", "product_id", "state", "created_at", "total"],
|
|
},
|
|
});
|
|
|
|
cy.findAllByRole("row").should("have.length", 1);
|
|
cy.findByText("92");
|
|
|
|
H.filterWidget().should("not.exist");
|
|
});
|
|
});
|
|
|
|
it("should set multiple filter values via url", () => {
|
|
cy.get("@questionId").then((questionId) => {
|
|
cy.request("PUT", `/api/card/${questionId}`, {
|
|
enable_embedding: true,
|
|
embedding_params: {
|
|
created_at: "enabled",
|
|
source: "enabled",
|
|
state: "enabled",
|
|
total: "enabled",
|
|
},
|
|
});
|
|
|
|
const payload = {
|
|
resource: { question: questionId },
|
|
params: {},
|
|
};
|
|
|
|
H.visitEmbeddedPage(payload, {
|
|
setFilters: { created_at: "Q2-2025", source: "Organic", state: "OR" },
|
|
});
|
|
|
|
H.filterWidget()
|
|
.should("have.length", 4)
|
|
.and("contain", "OR")
|
|
.and("contain", "Q2 2025");
|
|
// Why do we use input field in one filter widget but a simple `span` in the other one?
|
|
cy.findByDisplayValue("Organic");
|
|
|
|
// Total's value should fall back to the default one (`0`) because we didn't set it explicitly
|
|
H.filterWidget({ name: "Total" }).should("contain", "0");
|
|
|
|
cy.contains("Emilie Goyette");
|
|
cy.contains("35.7");
|
|
|
|
// OTOH, we should also be able to override the default filter value by eplixitly setting it
|
|
H.visitEmbeddedPage(payload, {
|
|
setFilters: { total: 80 },
|
|
});
|
|
|
|
H.filterWidget({ name: "Total" }).should("contain", "80");
|
|
|
|
cy.contains("35.7").should("not.exist");
|
|
});
|
|
});
|
|
|
|
it("should lock all parameters", () => {
|
|
cy.get("@questionId").then((questionId) => {
|
|
cy.request("PUT", `/api/card/${questionId}`, {
|
|
enable_embedding: true,
|
|
embedding_params: {
|
|
id: "locked",
|
|
product_id: "locked",
|
|
state: "locked",
|
|
created_at: "locked",
|
|
total: "locked",
|
|
source: "locked",
|
|
},
|
|
});
|
|
|
|
const payload = {
|
|
resource: { question: questionId },
|
|
params: {
|
|
id: [92, 96, 102, 104],
|
|
product_id: [140],
|
|
state: ["AK", "TX"],
|
|
created_at: "Q3-2024",
|
|
total: [10],
|
|
source: ["Organic"],
|
|
},
|
|
};
|
|
|
|
H.visitEmbeddedPage(payload);
|
|
|
|
cy.findAllByRole("row").should("have.length", 1);
|
|
cy.findByText("66.8");
|
|
|
|
H.filterWidget().should("not.exist");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("locked parameters", () => {
|
|
beforeEach(() => {
|
|
const nameParameter = questionDetails2.native["template-tags"]["name"];
|
|
const sourceParameter =
|
|
questionDetails2.native["template-tags"]["source"];
|
|
|
|
H.createNativeQuestion(questionDetails2, {
|
|
wrapId: true,
|
|
});
|
|
|
|
cy.get("@questionId").then((questionId) => {
|
|
cy.request("PUT", `/api/card/${questionId}`, {
|
|
enable_embedding: true,
|
|
embedding_params: {
|
|
[nameParameter.name]: "enabled",
|
|
[sourceParameter.name]: "locked",
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
it("locked parameters require a value to be specified in the JWT", () => {
|
|
cy.get("@questionId").then((questionId) => {
|
|
const payload = {
|
|
resource: { question: questionId },
|
|
params: { source: null },
|
|
};
|
|
|
|
H.visitEmbeddedPage(payload);
|
|
});
|
|
|
|
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
|
|
cy.findByText("You must specify a value for :source in the JWT.").should(
|
|
"be.visible",
|
|
);
|
|
});
|
|
|
|
it("locked parameters should still render results in the preview by default (metabase#47570)", () => {
|
|
H.visitQuestion("@questionId").then((id) => {
|
|
H.openLegacyStaticEmbeddingModal({
|
|
resource: "question",
|
|
resourceId: id,
|
|
activeTab: "parameters",
|
|
unpublishBeforeOpen: false,
|
|
});
|
|
});
|
|
|
|
H.visitIframe();
|
|
|
|
cy.log("should show card results by default");
|
|
cy.findByTestId("visualization-root")
|
|
.findByText("2,500")
|
|
.should("be.visible");
|
|
cy.findByRole("heading", { name: "test question" }).should("be.visible");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("scenarios > embedding > native questions with default parameters", () => {
|
|
beforeEach(() => {
|
|
H.restore();
|
|
cy.signInAsAdmin();
|
|
|
|
H.createNativeQuestion(questionDetailsWithDefaults, {
|
|
visitQuestion: true,
|
|
wrapId: true,
|
|
});
|
|
|
|
cy.get("@questionId").then((questionId) => {
|
|
H.openLegacyStaticEmbeddingModal({
|
|
resource: "question",
|
|
resourceId: questionId,
|
|
activeTab: "parameters",
|
|
});
|
|
});
|
|
|
|
// Note: ID is disabled
|
|
H.setEmbeddingParameter("Source", "Locked");
|
|
H.setEmbeddingParameter("Name", "Editable");
|
|
H.publishChanges("card", ({ request }) => {
|
|
assert.deepEqual(request.body.embedding_params, {
|
|
id: "disabled",
|
|
source: "locked",
|
|
name: "enabled",
|
|
user_id: "disabled",
|
|
});
|
|
});
|
|
});
|
|
|
|
it("card parameter defaults should apply for disabled parameters, but not for editable or locked parameters", () => {
|
|
cy.get("@questionId").then((questionId) => {
|
|
const payload = {
|
|
resource: { question: questionId },
|
|
params: { source: [] },
|
|
};
|
|
|
|
H.visitEmbeddedPage(payload);
|
|
});
|
|
// Remove default filter value
|
|
H.clearFilterWidget();
|
|
// The ID default (1, 2) should apply, because it is disabled.
|
|
// The Name default ('Lina Heaney') should not apply, because the Name param is editable and empty
|
|
// The Source default ('Facebook') should not apply because the param is locked but the value is unset
|
|
// If either the Name or Source default applied the result would be 0.
|
|
cy.findByTestId("scalar-value").invoke("text").should("eq", "2");
|
|
});
|
|
});
|
|
|
|
function assertRequiredEnabledForName({ name, enabled }) {
|
|
cy.findByTestId(`tag-editor-variable-${name}`).within(() => {
|
|
SQLFilter.getRequiredInput().should(
|
|
enabled ? "be.enabled" : "not.be.enabled",
|
|
);
|
|
});
|
|
}
|