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

E2E test coverage for "Highlight the selected data source in the notebook editor" project (#40199)

* Add the initial test for the empty app db

* Add reproduction for #25142

* Add test that covers only saved questions

* Cover scenarios with a table as a source

* Add tests that cover nested questions and models

* Add test for simple model's source table

* Remove intercepted schema routes

* Improve assertion

* Fix the test failing on enterprise instance

* Add E2E reproduction for #40223

* Remove superfluous code block

* Avoid casting
parent ee131b5f
No related merge requests found
......@@ -53,6 +53,9 @@ export const FIRST_COLLECTION_ID = _.findWhere(
{ name: "First collection" },
).id;
/**
* @type number
*/
export const SECOND_COLLECTION_ID = _.findWhere(
SAMPLE_INSTANCE_DATA.collections,
{ name: "Second collection" },
......
......@@ -275,7 +275,11 @@ export function waitForSyncToFinish({
});
}
export function resyncDatabase({ dbId = 2, tableName = "", tableAlias }) {
export function resyncDatabase({
dbId = 2,
tableName = "",
tableAlias = undefined, // TS was complaining that this was a required param
}) {
// must be signed in as admin to sync
cy.request("POST", `/api/database/${dbId}/sync_schema`);
cy.request("POST", `/api/database/${dbId}/rescan_values`);
......
// TypeScript doesn't recognize `onlyOn` on the `cy` object.
// Hence, we have to import it as a standalone helper.
import { onlyOn } from "@cypress/skip-test";
import { WRITABLE_DB_ID } from "e2e/support/cypress_data";
import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
import {
SECOND_COLLECTION_ID,
ORDERS_COUNT_QUESTION_ID,
ORDERS_MODEL_ID,
} from "e2e/support/cypress_sample_instance_data";
import {
popover,
restore,
startNewQuestion,
openReviewsTable,
openNotebook,
visitQuestion,
resetTestTable,
resyncDatabase,
visualize,
saveQuestion,
visitModel,
openQuestionActions,
isOSS,
isEE,
} from "e2e/support/helpers";
const { REVIEWS_ID } = SAMPLE_DATABASE;
describe("scenarios > notebook > data source", () => {
describe("empty app db", () => {
beforeEach(() => {
restore("setup");
cy.signInAsAdmin();
});
it(
"should display tables from the only existing database by default",
{ tags: "@OSS" },
() => {
onlyOn(isOSS);
cy.visit("/");
cy.findByTestId("app-bar").findByText("New").click();
popover().findByTextEnsureVisible("Question").click();
cy.findByTestId("data-step-cell").should(
"have.text",
"Pick your starting data",
);
popover().within(() => {
cy.findByTestId("source-database").should(
"have.text",
"Sample Database",
);
cy.findAllByRole("option")
.should("have.length", 8)
.each(table => {
cy.wrap(table).should("have.attr", "aria-selected", "false");
});
});
},
);
it.skip("should display tables from the only existing database by default on an enterprise instance without token activation (metabase#40223)", () => {
onlyOn(isEE);
cy.visit("/");
cy.findByTestId("app-bar").findByText("New").click();
popover().findByTextEnsureVisible("Question").click();
cy.findByTestId("data-step-cell").should(
"have.text",
"Pick your starting data",
);
popover().within(() => {
cy.findByTestId("source-database").should(
"have.text",
"Sample Database",
);
cy.findAllByRole("option")
.should("have.length", 8)
.each(table => {
cy.wrap(table).should("have.attr", "aria-selected", "false");
});
});
});
it("should not show saved questions if only models exist (metabase#25142)", () => {
cy.createQuestion({
name: "GUI Model",
query: { "source-table": REVIEWS_ID, limit: 1 },
display: "table",
type: "model",
});
startNewQuestion();
popover().within(() => {
cy.findByPlaceholderText("Search for some data…");
cy.findAllByTestId("data-bucket-list-item")
.as("sources")
.should("have.length", 2);
cy.get("@sources")
.first()
.should("contain", "Models")
.and("have.attr", "aria-selected", "false");
cy.get("@sources")
.last()
.should("contain", "Raw Data")
.and("have.attr", "aria-selected", "false");
});
});
// There is a huge discrepancy between how we render this popover vs the one for models
// That's the reason this test is a bit vague. Will be reported as a separate issue
// and covered in a separate reproduction.
it("should not show models if only saved questions exist", () => {
cy.createQuestion({
name: "GUI Question",
query: { "source-table": REVIEWS_ID, limit: 1 },
display: "table",
});
startNewQuestion();
popover().within(() => {
cy.get(".List-section-title")
.should("have.length", 2)
.and("contain", "Saved Questions")
.and("not.contain", "Models");
});
});
});
describe("table as a source", () => {
beforeEach(() => {
restore();
cy.signInAsAdmin();
});
it("should correctly display the source data for ad-hoc questions", () => {
openReviewsTable();
openNotebook();
cy.findByTestId("data-step-cell").should("have.text", "Reviews").click();
popover().within(() => {
cy.findByTestId("source-database").should(
"have.text",
"Sample Database",
);
cy.findByLabelText("Reviews").should(
"have.attr",
"aria-selected",
"true",
);
});
});
it("should correctly display the source data for a simple saved question", () => {
visitQuestion(ORDERS_COUNT_QUESTION_ID);
openNotebook();
cy.findByTestId("data-step-cell").should("have.text", "Orders").click();
popover().within(() => {
cy.findByTestId("source-database").should(
"have.text",
"Sample Database",
);
cy.findByLabelText("Orders").should(
"have.attr",
"aria-selected",
"true",
);
});
});
it(
"should correctly display a table from a multi-schema database (metabase#39807)",
{ tags: "@external" },
() => {
const dialect = "postgres";
const TEST_TABLE = "multi_schema";
const dbName = "Writable Postgres12";
const schemaName = "Wild";
const tableName = "Animals";
resetTestTable({ type: dialect, table: TEST_TABLE });
restore(`${dialect}-writable`);
cy.signInAsAdmin();
resyncDatabase({
dbId: WRITABLE_DB_ID,
});
startNewQuestion();
popover().within(() => {
cy.findByText("Raw Data").click();
cy.findByText(dbName).click();
cy.findByText(schemaName).click();
cy.findByText(tableName).click();
});
visualize();
saveQuestion("Beasts");
openNotebook();
cy.findByTestId("data-step-cell").should("contain", tableName).click();
popover().within(() => {
cy.findByTestId("source-database").should("have.text", dbName);
cy.findByTestId("source-schema").should("have.text", schemaName);
});
},
);
it("should correctly display a table as the model's source when editing simple model's query", () => {
cy.visit(`/model/${ORDERS_MODEL_ID}/query`);
cy.findByTestId("data-step-cell").should("have.text", "Orders").click();
popover().within(() => {
cy.findByTestId("source-database").should(
"have.text",
"Sample Database",
);
cy.findByLabelText("Orders").should(
"have.attr",
"aria-selected",
"true",
);
});
});
});
describe("saved entity as a source (aka the virtual table)", () => {
const modelDetails: Parameters<typeof cy.createQuestion>[0] = {
name: "GUI Model",
query: { "source-table": REVIEWS_ID, limit: 1 },
display: "table",
type: "model",
collection_id: SECOND_COLLECTION_ID,
};
beforeEach(() => {
restore();
cy.signInAsAdmin();
});
it("data selector should properly show a model as the source (metabase#39699)", () => {
cy.createQuestion(modelDetails, { visitQuestion: true });
openNotebook();
cy.findByTestId("data-step-cell")
.should("have.text", modelDetails.name)
.click();
cy.findByTestId("saved-entity-back-navigation").should(
"have.text",
"Models",
);
cy.findByTestId("saved-entity-collection-tree").within(() => {
cy.findByLabelText("Our analytics")
.should("have.attr", "aria-expanded", "false")
.and("have.attr", "aria-selected", "false");
cy.findByLabelText("First collection")
.should("have.attr", "aria-expanded", "true")
.and("have.attr", "aria-selected", "false");
cy.findByLabelText("Second collection")
.should("have.attr", "aria-expanded", "false")
.and("have.attr", "aria-selected", "true");
});
cy.findByTestId("select-list")
.findByLabelText(modelDetails.name)
.should("have.attr", "aria-selected", "true");
});
it("moving the model to another collection should immediately be reflected in the data selector (metabase#39812-1)", () => {
visitModel(ORDERS_MODEL_ID);
openNotebook();
openDataSelector();
assertSourceCollection("Our analytics");
assertDataSource("Orders Model");
moveToCollection("First collection");
openDataSelector();
assertSourceCollection("First collection");
assertDataSource("Orders Model");
});
it("moving the source question should immediately reflect in the data selector for the nested question that depends on it (metabase#39812-2)", () => {
const SOURCE_QUESTION_ID = ORDERS_COUNT_QUESTION_ID;
// Rename the source question to make assertions more explicit
const sourceQuestionName = "Source Question";
cy.request("PUT", `/api/card/${ORDERS_COUNT_QUESTION_ID}`, {
name: sourceQuestionName,
});
const nestedQuestionDetails = {
name: "Nested Question",
query: { "source-table": `card__${SOURCE_QUESTION_ID}` },
};
cy.createQuestion(nestedQuestionDetails, {
wrapId: true,
idAlias: "nestedQuestionId",
});
visitQuestion("@nestedQuestionId");
openNotebook();
openDataSelector();
assertSourceCollection("Our analytics");
assertDataSource(sourceQuestionName);
cy.log("Move the source question to another collection");
visitQuestion(SOURCE_QUESTION_ID);
openNotebook();
moveToCollection("First collection");
cy.log("Make sure the source change is reflected in a nested question");
visitQuestion("@nestedQuestionId");
openNotebook();
openDataSelector();
assertSourceCollection("First collection");
assertDataSource(sourceQuestionName);
});
});
});
function moveToCollection(collection: string) {
openQuestionActions();
popover().findByTextEnsureVisible("Move").click();
cy.findByRole("dialog").within(() => {
cy.intercept("GET", "/api/collection/tree**").as("updateCollectionTree");
cy.findAllByTestId("item-picker-item")
.filter(`:contains(${collection})`)
.click();
cy.button("Move").click();
cy.wait("@updateCollectionTree");
});
}
function openDataSelector() {
cy.findByTestId("data-step-cell").click();
}
function assertItemSelected(item: string) {
cy.findByLabelText(item).should("have.attr", "aria-selected", "true");
}
function assertSourceCollection(collection: string) {
return assertItemSelected(collection);
}
function assertDataSource(questionOrModel: string) {
return assertItemSelected(questionOrModel);
}
import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
import { SECOND_COLLECTION_ID } from "e2e/support/cypress_sample_instance_data";
import { restore, openNotebook } from "e2e/support/helpers";
const { REVIEWS_ID } = SAMPLE_DATABASE;
const modelDetails = {
name: "GUI Model",
query: { "source-table": REVIEWS_ID, limit: 1 },
display: "table",
type: "model",
collection_id: SECOND_COLLECTION_ID,
};
describe("issue 39699", () => {
beforeEach(() => {
restore();
cy.signInAsAdmin();
cy.createQuestion(modelDetails, { visitQuestion: true });
});
it("data selector should properly show a model as the source (metabase#39699)", () => {
openNotebook();
cy.findByTestId("data-step-cell")
.should("have.text", modelDetails.name)
.click();
cy.findByTestId("saved-entity-back-navigation").should(
"have.text",
"Models",
);
cy.findByTestId("saved-entity-collection-tree").within(() => {
cy.findByLabelText("Our analytics")
.should("have.attr", "aria-expanded", "false")
.and("have.attr", "aria-selected", "false");
cy.findByLabelText("First collection")
.should("have.attr", "aria-expanded", "true")
.and("have.attr", "aria-selected", "false");
cy.findByLabelText("Second collection")
.should("have.attr", "aria-expanded", "false")
.and("have.attr", "aria-selected", "true");
});
cy.findByTestId("select-list")
.findByLabelText(modelDetails.name)
.should("have.attr", "aria-selected", "true");
});
});
import { WRITABLE_DB_ID } from "e2e/support/cypress_data";
import {
resetTestTable,
restore,
resyncDatabase,
startNewQuestion,
popover,
saveQuestion,
visualize,
openNotebook,
} from "e2e/support/helpers";
const dialect = "postgres";
const TEST_TABLE = "multi_schema";
const dbName = "Writable Postgres12";
const schemaName = "Wild";
const tableName = "Animals";
describe("issue 39807", () => {
beforeEach(() => {
restore();
cy.signInAsAdmin();
});
it(
"should properly display a table from a multi-schema database (metabase#39807)",
{ tags: "@external" },
() => {
resetTestTable({ type: dialect, table: TEST_TABLE });
restore(`${dialect}-writable`);
cy.signInAsAdmin();
resyncDatabase({
dbId: WRITABLE_DB_ID,
});
startNewQuestion();
popover().within(() => {
cy.findByText("Raw Data").click();
cy.findByText(dbName).click();
cy.findByText(schemaName).click();
cy.findByText(tableName).click();
});
visualize();
saveQuestion("Beasts");
openNotebook();
cy.findByTestId("data-step-cell").should("contain", tableName).click();
popover().within(() => {
cy.findByTestId("source-database").should("have.text", dbName);
cy.findByTestId("source-schema").should("have.text", schemaName);
});
},
);
});
import {
ORDERS_MODEL_ID,
ORDERS_COUNT_QUESTION_ID,
} from "e2e/support/cypress_sample_instance_data";
import {
restore,
visitModel,
openNotebook,
openQuestionActions,
popover,
visitQuestion,
} from "e2e/support/helpers";
describe("issue 39812", () => {
beforeEach(() => {
restore();
cy.signInAsAdmin();
});
it("moving the model to another collection should immediately be reflected in the data selector (metabase#39812-1)", () => {
visitModel(ORDERS_MODEL_ID);
openNotebook();
openDataSelector();
assertSourceCollection("Our analytics");
assertDataSource("Orders Model");
moveToCollection("First collection");
openDataSelector();
assertSourceCollection("First collection");
assertDataSource("Orders Model");
});
it("moving the source question should immediately reflect in the data selector for the nested question that depends on it (metabase#39812-2)", () => {
const SOURCE_QUESTION_ID = ORDERS_COUNT_QUESTION_ID;
// Rename the source question to make assertions more explicit
const sourceQuestionName = "Source Question";
cy.request("PUT", `/api/card/${ORDERS_COUNT_QUESTION_ID}`, {
name: sourceQuestionName,
});
const nestedQuestionDetails = {
name: "Nested Question",
query: { "source-table": `card__${SOURCE_QUESTION_ID}` },
};
cy.createQuestion(nestedQuestionDetails, {
wrapId: true,
idAlias: "nestedQuestionId",
});
visitQuestion("@nestedQuestionId");
openNotebook();
openDataSelector();
assertSourceCollection("Our analytics");
assertDataSource(sourceQuestionName);
cy.log("Move the source question to another collection");
visitQuestion(SOURCE_QUESTION_ID);
openNotebook();
moveToCollection("First collection");
cy.log("Make sure the source change is reflected in a nested question");
visitQuestion("@nestedQuestionId");
openNotebook();
openDataSelector();
assertSourceCollection("First collection");
assertDataSource(sourceQuestionName);
});
});
function moveToCollection(collection: string) {
openQuestionActions();
popover().findByTextEnsureVisible("Move").click();
cy.findByRole("dialog").within(() => {
cy.intercept("GET", "/api/collection/tree**").as("updateCollectionTree");
cy.findAllByTestId("item-picker-item")
.filter(`:contains(${collection})`)
.click();
cy.button("Move").click();
cy.wait("@updateCollectionTree");
});
}
function openDataSelector() {
cy.findByTestId("data-step-cell").click();
}
function assertItemSelected(item: string) {
cy.findByLabelText(item).should("have.attr", "aria-selected", "true");
}
function assertSourceCollection(collection: string) {
return assertItemSelected(collection);
}
function assertDataSource(questionOrModel: string) {
return assertItemSelected(questionOrModel);
}
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