diff --git a/frontend/test/__support__/e2e/cypress.js b/frontend/test/__support__/e2e/cypress.js index df1c9667c4278f09671d763159cc14b1ed272f1e..73598860caacdeae600384253e894f50b1afee2e 100644 --- a/frontend/test/__support__/e2e/cypress.js +++ b/frontend/test/__support__/e2e/cypress.js @@ -11,7 +11,6 @@ export const version = require("../../../../version.json"); export * from "./helpers/e2e-setup-helpers"; export * from "./helpers/e2e-ui-elements-helpers"; export * from "./helpers/e2e-dashboard-helpers"; -export * from "./helpers/e2e-open-table-helpers"; export * from "./helpers/e2e-database-metadata-helpers"; export * from "./helpers/e2e-qa-databases-helpers"; export * from "./helpers/e2e-ad-hoc-question-helpers"; diff --git a/frontend/test/__support__/e2e/helpers/e2e-ad-hoc-question-helpers.js b/frontend/test/__support__/e2e/helpers/e2e-ad-hoc-question-helpers.js index 1f15b5c61e37098f60cac2c64d53ceff413ac167..f0c7687a1791e8a3fa9cbd54f234568a26b73d7d 100644 --- a/frontend/test/__support__/e2e/helpers/e2e-ad-hoc-question-helpers.js +++ b/frontend/test/__support__/e2e/helpers/e2e-ad-hoc-question-helpers.js @@ -6,19 +6,77 @@ export function adhocQuestionHash(question) { return btoa(unescape(encodeURIComponent(JSON.stringify(question)))); } -export function visitQuestionAdhoc(question, { callback } = {}) { - const [url, alias] = getInterceptDetails(question); +/** + * Visit any valid query in an ad-hoc manner. + * + * @param {object} question + * @param {{callback: function, mode: (undefined|"notebook")}} config + */ +export function visitQuestionAdhoc(question, { callback, mode } = {}) { + const questionMode = mode === "notebook" ? "/notebook" : ""; + + const [url, alias] = getInterceptDetails(question, mode); cy.intercept(url).as(alias); - cy.visit("/question#" + adhocQuestionHash(question)); + cy.visit(`/question${questionMode}#` + adhocQuestionHash(question)); cy.wait("@" + alias).then(xhr => { callback && callback(xhr); }); } -function getInterceptDetails(question) { +/** + * Open a table as an ad-hoc query in a simple or a notebook mode, and optionally limit the number of results. + * + * @param {{database:number, table: number, mode: (undefined|"notebook"), limit: number, callback: function}} config + */ +export function openTable({ + database = 1, + table, + mode = null, + limit, + callback, +} = {}) { + visitQuestionAdhoc( + { + dataset_query: { + database, + query: { + "source-table": table, + limit, + }, + type: "query", + }, + }, + { mode, callback }, + ); +} + +export function openProductsTable({ mode, limit, callback } = {}) { + return openTable({ table: 1, mode, limit, callback }); +} + +export function openOrdersTable({ mode, limit, callback } = {}) { + return openTable({ table: 2, mode, limit, callback }); +} + +export function openPeopleTable({ mode, limit, callback } = {}) { + return openTable({ table: 3, mode, limit, callback }); +} + +export function openReviewsTable({ mode, limit, callback } = {}) { + return openTable({ table: 4, mode, limit, callback }); +} + +function getInterceptDetails(question, mode) { + // When visiting notebook mode directly, we don't render any results to the page. + // Therefore, there is no `dataset` to wait for. + // But we need to make sure the schema for our database is loaded before we can proceed. + if (mode === "notebook") { + return ["/api/database/1/schema/PUBLIC", "publicSchema"]; + } + const { display, dataset_query: { type }, diff --git a/frontend/test/__support__/e2e/helpers/e2e-open-table-helpers.js b/frontend/test/__support__/e2e/helpers/e2e-open-table-helpers.js deleted file mode 100644 index 77567b9311e63d4dcee674f6e8deb0328596fff3..0000000000000000000000000000000000000000 --- a/frontend/test/__support__/e2e/helpers/e2e-open-table-helpers.js +++ /dev/null @@ -1,26 +0,0 @@ -export function openTable({ database = 1, table, mode = null } = {}) { - const url = "/question/new?"; - const params = new URLSearchParams({ database, table }); - - if (mode === "notebook") { - params.append("mode", mode); - } - - cy.visit(url + params.toString()); -} - -export function openProductsTable({ mode } = {}) { - return openTable({ table: 1, mode }); -} - -export function openOrdersTable({ mode } = {}) { - return openTable({ table: 2, mode }); -} - -export function openPeopleTable({ mode } = {}) { - return openTable({ table: 3, mode }); -} - -export function openReviewsTable({ mode } = {}) { - return openTable({ table: 4, mode }); -} diff --git a/frontend/test/metabase-visual/visualizations/table.cy.spec.js b/frontend/test/metabase-visual/visualizations/table.cy.spec.js index a0941fed90ccfac6cf7fab9bb3d858d36d798744..628e7e4c909a43ed2f7489351d0f59e7ec735a74 100644 --- a/frontend/test/metabase-visual/visualizations/table.cy.spec.js +++ b/frontend/test/metabase-visual/visualizations/table.cy.spec.js @@ -2,12 +2,11 @@ import { restore, openOrdersTable, modal } from "__support__/e2e/cypress"; describe("visual tests > visualizations > table", () => { beforeEach(() => { - cy.intercept("POST", "/api/dataset").as("dataset"); restore(); cy.signInAsNormalUser(); openOrdersTable(); - cy.wait("@dataset"); + cy.findByTestId("loading-spinner").should("not.exist"); }); diff --git a/frontend/test/metabase/scenarios/binning/binning-options.cy.spec.js b/frontend/test/metabase/scenarios/binning/binning-options.cy.spec.js index 0d86735027e489d8078e7d47378d70ee41ba20b4..00edb1991b2c520a3a2b5fb5302473e37722304f 100644 --- a/frontend/test/metabase/scenarios/binning/binning-options.cy.spec.js +++ b/frontend/test/metabase/scenarios/binning/binning-options.cy.spec.js @@ -183,8 +183,6 @@ describe("scenarios > binning > binning options", () => { it("should render time series binning options correctly", () => { openTable({ table: ORDERS_ID }); - cy.wait("@dataset"); - cy.findByText("Created At").click(); cy.findByText("Distribution").click(); diff --git a/frontend/test/metabase/scenarios/permissions/sandboxes.cy.spec.js b/frontend/test/metabase/scenarios/permissions/sandboxes.cy.spec.js index fef501115e365791203b7feced6644611c53dd54..4bd782773e7634c3fceec4f8472bbc1a871c1c41 100644 --- a/frontend/test/metabase/scenarios/permissions/sandboxes.cy.spec.js +++ b/frontend/test/metabase/scenarios/permissions/sandboxes.cy.spec.js @@ -482,10 +482,8 @@ describeWithToken("formatting > sandboxes", () => { cy.signOut(); cy.signInAsSandboxedUser(); - openOrdersTable(); - - cy.wait("@dataset").then(xhr => { - expect(xhr.response.body.error).not.to.exist; + openOrdersTable({ + callback: xhr => expect(xhr.response.body.error).not.to.exist, }); cy.get(".cellData") @@ -640,11 +638,10 @@ describeWithToken("formatting > sandboxes", () => { cy.signOut(); cy.signInAsSandboxedUser(); - openOrdersTable(); - - cy.wait("@dataset").then(xhr => { - expect(xhr.response.body.error).not.to.exist; + openOrdersTable({ + callback: xhr => expect(xhr.response.body.error).not.to.exist, }); + // Title of the first order for User ID = 1 cy.findByText("Awesome Concrete Shoes"); }); @@ -969,10 +966,9 @@ describeWithToken("formatting > sandboxes", () => { cy.signOut(); cy.signInAsSandboxedUser(); - openOrdersTable(); - cy.wait("@dataset").then(xhr => { - expect(xhr.response.body.error).not.to.exist; + openOrdersTable({ + callback: xhr => expect(xhr.response.body.error).not.to.exist, }); cy.contains("37.65"); @@ -1026,10 +1022,9 @@ describeWithToken("formatting > sandboxes", () => { }); cy.signOut(); cy.signInAsSandboxedUser(); - openReviewsTable(); - cy.wait("@dataset").then(xhr => { - expect(xhr.response.body.error).not.to.exist; + openReviewsTable({ + callback: xhr => expect(xhr.response.body.error).not.to.exist, }); // Add positive assertion once this issue is fixed diff --git a/frontend/test/metabase/scenarios/question/new.cy.spec.js b/frontend/test/metabase/scenarios/question/new.cy.spec.js index 981efd0e3c97ab51570939c41ff497e48b83dab2..2b9dce56f892ec20e4f8fc2c2199273b1243cb1b 100644 --- a/frontend/test/metabase/scenarios/question/new.cy.spec.js +++ b/frontend/test/metabase/scenarios/question/new.cy.spec.js @@ -287,10 +287,8 @@ describe("scenarios > question > new", () => { }); it("should remove `/notebook` from URL when converting question to SQL/Native (metabase#12651)", () => { - cy.server(); - cy.route("POST", "/api/dataset").as("dataset"); openOrdersTable(); - cy.wait("@dataset"); + cy.url().should("include", "question#"); // Isolate icons within "QueryBuilder" scope because there is also `.Icon-sql` in top navigation cy.get(".QueryBuilder .Icon-notebook").click(); diff --git a/frontend/test/metabase/scenarios/question/nulls.cy.spec.js b/frontend/test/metabase/scenarios/question/nulls.cy.spec.js index 04b577b40d814bbebbfa5d36299b6edfd4510416..5ab267e4fc158be9f62ffe34fe75c261683d1b60 100644 --- a/frontend/test/metabase/scenarios/question/nulls.cy.spec.js +++ b/frontend/test/metabase/scenarios/question/nulls.cy.spec.js @@ -181,14 +181,9 @@ describe("scenarios > question > null", () => { }); describe("aggregations with null values", () => { - beforeEach(() => { - cy.server(); - cy.route("POST", "/api/dataset").as("dataset"); - }); - it("summarize with null values (metabase#12585)", () => { openOrdersTable(); - cy.wait("@dataset"); + cy.contains("Summarize").click(); // remove pre-selected "Count" cy.icon("close").click(); @@ -200,10 +195,6 @@ describe("scenarios > question > null", () => { // Group by cy.contains("Created At").click(); cy.contains("Cumulative sum of Discount by Created At: Month"); - cy.wait(["@dataset", "@dataset"]).then(xhrs => { - expect(xhrs[0].status).to.equal(202); - expect(xhrs[1].status).to.equal(202); - }); cy.findByText("There was a problem with your question").should( "not.exist", diff --git a/frontend/test/metabase/scenarios/question/saved.cy.spec.js b/frontend/test/metabase/scenarios/question/saved.cy.spec.js index bd72b53c0a7ed336b0de95bdeb0fab44b5460725..f21f5dbc41761b7d70cbfa5a03945bdace22c9e1 100644 --- a/frontend/test/metabase/scenarios/question/saved.cy.spec.js +++ b/frontend/test/metabase/scenarios/question/saved.cy.spec.js @@ -12,11 +12,14 @@ describe("scenarios > question > saved", () => { }); it("should should correctly display 'Save' modal (metabase#13817)", () => { - openOrdersTable({ mode: "notebook" }); + openOrdersTable(); + cy.icon("notebook").click(); cy.findByText("Summarize").click(); cy.findByText("Count of rows").click(); cy.findByText("Pick a column to group by").click(); - cy.findByText("Total").click(); + popover() + .findByText("Total") + .click(); // Save the question cy.findByText("Save").click(); modal().within(() => { @@ -27,7 +30,9 @@ describe("scenarios > question > saved", () => { // Add a filter in order to be able to save question again cy.findByText("Filter").click(); - cy.findByText(/^Total$/).click(); + popover() + .findByText(/^Total$/) + .click(); cy.findByText("Equal to").click(); cy.findByText("Greater than").click(); cy.findByPlaceholderText("Enter a number").type("60"); diff --git a/frontend/test/metabase/scenarios/question/view.cy.spec.js b/frontend/test/metabase/scenarios/question/view.cy.spec.js index 1741a869364161d134d4267f6fb31a59659b46d2..ac0ca344bf394370c69f95a186a8e52bac86dcf1 100644 --- a/frontend/test/metabase/scenarios/question/view.cy.spec.js +++ b/frontend/test/metabase/scenarios/question/view.cy.spec.js @@ -16,10 +16,8 @@ describe("scenarios > question > view", () => { describe("summarize sidebar", () => { it("should summarize by category and show a bar chart", () => { - cy.server(); - cy.route("POST", "/api/dataset").as("dataset"); openOrdersTable(); - cy.wait("@dataset"); + cy.contains("Summarize").click(); cy.contains("Category").click(); cy.contains("Done").click(); diff --git a/frontend/test/metabase/scenarios/visualizations/table.cy.spec.js b/frontend/test/metabase/scenarios/visualizations/table.cy.spec.js index c05cdadbfc3e55534c439da0b06176655955b101..2ddef848e510ed98d118bad254881d8554bc35fb 100644 --- a/frontend/test/metabase/scenarios/visualizations/table.cy.spec.js +++ b/frontend/test/metabase/scenarios/visualizations/table.cy.spec.js @@ -12,12 +12,10 @@ describe("scenarios > visualizations > table", () => { beforeEach(() => { restore(); cy.signInAsNormalUser(); - cy.intercept("POST", "/api/dataset").as("dataset"); }); it("should allow to display any column as link with extrapolated url and text", () => { openPeopleTable(); - cy.wait("@dataset"); cy.findByText("City").click(); @@ -51,7 +49,6 @@ describe("scenarios > visualizations > table", () => { it("should show field metadata in a popover when hovering over a table column header", () => { openPeopleTable(); - cy.wait("@dataset"); cy.icon("notebook").click(); cy.findByTestId("fields-picker").click(); @@ -173,7 +170,6 @@ describe("scenarios > visualizations > table", () => { it("should show the field metadata popover for a foreign key field (metabase#19577)", () => { openOrdersTable(); - cy.wait("@dataset"); cy.findByText("Product ID").trigger("mouseenter"); @@ -198,7 +194,6 @@ describe("scenarios > visualizations > table", () => { it.skip("should close the colum popover on subsequent click (metabase#16789)", () => { openPeopleTable(); - cy.wait("@dataset"); cy.findByText("City").click(); popover().within(() => { diff --git a/frontend/test/metabase/scenarios/visualizations/trendline.cy.spec.js b/frontend/test/metabase/scenarios/visualizations/trendline.cy.spec.js index 462b40ac5bd0bd3cd3541cb95d10450cb9dc6797..857283cae1687fffda61056ba9f0283ca2242f1b 100644 --- a/frontend/test/metabase/scenarios/visualizations/trendline.cy.spec.js +++ b/frontend/test/metabase/scenarios/visualizations/trendline.cy.spec.js @@ -1,41 +1,30 @@ -import { - restore, - openOrdersTable, - sidebar, - visualize, -} from "__support__/e2e/cypress"; +import { restore, sidebar } from "__support__/e2e/cypress"; +import { SAMPLE_DATABASE } from "__support__/e2e/cypress_sample_database"; + +const { ORDERS_ID, ORDERS } = SAMPLE_DATABASE; + +const questionDetails = { + name: "12781", + query: { + "source-table": ORDERS_ID, + aggregation: [ + ["avg", ["field", ORDERS.SUBTOTAL, null]], + ["sum", ["field", ORDERS.TOTAL, null]], + ], + breakout: [["field", ORDERS.CREATED_AT, { "temporal-unit": "year" }]], + }, + display: "line", +}; describe("scenarios > question > trendline", () => { beforeEach(() => { restore(); cy.signInAsNormalUser(); + + cy.createQuestion(questionDetails, { visitQuestion: true }); }); it("displays trendline when there are multiple numeric outputs (for simple question) (metabase#12781)", () => { - // Create question: orders summarized with "Average of Subtotal" and "Sum of Total" by CreatedAt:Year - openOrdersTable(); - cy.icon("notebook").click(); - cy.findByText("Summarize").click(); - cy.findByText("Average of ...").click(); - cy.findByText("Subtotal").click(); - - cy.icon("add") - .last() - .click(); - cy.findByText("Sum of ...").click(); - cy.findByText("Total").click(); - - cy.findByText("Pick a column to group by").click(); - cy.findByText("Created At").click(); - cy.findByText("Created At: Month").click(); - cy.findByText("by month").click(); - cy.findByText("Year").click(); - - visualize(); - - cy.findByText("Visualization"); - cy.get("rect"); - // Change settings to trendline cy.findByText("Visualization").click(); sidebar().within(() => {