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(() => {