diff --git a/e2e/support/commands.js b/e2e/support/commands.js index d2f1cf9127e78e004dec83cebf7d211200db98f4..13fbfa233d737d8eaa350c2c303138fb562036e9 100644 --- a/e2e/support/commands.js +++ b/e2e/support/commands.js @@ -1,12 +1,7 @@ import "./commands/ui/button"; import "./commands/ui/icon"; -import "./commands/api/index"; -import "./commands/api/dashboardCard"; - -import "./commands/api/composite/createNativeQuestionAndDashboard"; -import "./commands/api/composite/createQuestionAndAddToDashboard"; -import "./commands/api/composite/createDashboardWithQuestions"; +import "./commands/api"; import "./commands/user/createUser"; import "./commands/user/authentication"; diff --git a/e2e/support/commands/api/index.ts b/e2e/support/commands/api.ts similarity index 60% rename from e2e/support/commands/api/index.ts rename to e2e/support/commands/api.ts index 2bf45fdf25b22b6a15a82bb7ddb7672302d3638a..6b7761f6904d8f9e3c211ec0a0e8a855f6cd2b44 100644 --- a/e2e/support/commands/api/index.ts +++ b/e2e/support/commands/api.ts @@ -3,9 +3,13 @@ import { archiveDashboard, createCollection, createDashboard, + createDashboardWithQuestions, createNativeQuestion, + createNativeQuestionAndDashboard, createQuestion, + createQuestionAndAddToDashboard, createQuestionAndDashboard, + editDashboardCard, } from "e2e/support/helpers"; declare global { @@ -43,6 +47,14 @@ declare global { */ createDashboard: typeof createDashboard; + /** + * @deprecated Use function helper instead, i.e. + * ``` + * import { createDashboardWithQuestions } from "e2e/support/helpers" + * ``` + */ + createDashboardWithQuestions: typeof createDashboardWithQuestions; + /** * @deprecated Use function helper instead, i.e. * ``` @@ -51,6 +63,14 @@ declare global { */ createNativeQuestion: typeof createNativeQuestion; + /** + * @deprecated Use function helper instead, i.e. + * ``` + * import { createNativeQuestionAndDashboard } from "e2e/support/helpers" + * ``` + */ + createNativeQuestionAndDashboard: typeof createNativeQuestionAndDashboard; + /** * @deprecated Use function helper instead, i.e. * ``` @@ -59,6 +79,14 @@ declare global { */ createQuestion: typeof createQuestion; + /** + * @deprecated Use function helper instead, i.e. + * ``` + * import { createQuestionAndAddToDashboard } from "e2e/support/helpers" + * ``` + */ + createQuestionAndAddToDashboard: typeof createQuestionAndAddToDashboard; + /** * @deprecated Use function helper instead, i.e. * ``` @@ -66,6 +94,14 @@ declare global { * ``` */ createQuestionAndDashboard: typeof createQuestionAndDashboard; + + /** + * @deprecated Use function helper instead, i.e. + * ``` + * import { editDashboardCard } from "e2e/support/helpers" + * ``` + */ + editDashboardCard: typeof editDashboardCard; } } } @@ -74,6 +110,19 @@ Cypress.Commands.add("archiveCollection", archiveCollection); Cypress.Commands.add("archiveDashboard", archiveDashboard); Cypress.Commands.add("createCollection", createCollection); Cypress.Commands.add("createDashboard", createDashboard); +Cypress.Commands.add( + "createDashboardWithQuestions", + createDashboardWithQuestions, +); Cypress.Commands.add("createNativeQuestion", createNativeQuestion); +Cypress.Commands.add( + "createNativeQuestionAndDashboard", + createNativeQuestionAndDashboard, +); Cypress.Commands.add("createQuestion", createQuestion); +Cypress.Commands.add( + "createQuestionAndAddToDashboard", + createQuestionAndAddToDashboard, +); Cypress.Commands.add("createQuestionAndDashboard", createQuestionAndDashboard); +Cypress.Commands.add("editDashboardCard", editDashboardCard); diff --git a/e2e/support/commands/api/composite/createDashboardWithQuestions.js b/e2e/support/commands/api/composite/createDashboardWithQuestions.js deleted file mode 100644 index 824112532fe137f155e4cc1126d0a9ca9f971d00..0000000000000000000000000000000000000000 --- a/e2e/support/commands/api/composite/createDashboardWithQuestions.js +++ /dev/null @@ -1,28 +0,0 @@ -import { cypressWaitAll } from "e2e/support/helpers"; - -Cypress.Commands.add( - "createDashboardWithQuestions", - ({ dashboardName, dashboardDetails, questions, cards }) => { - return cy - .createDashboard({ name: dashboardName, ...dashboardDetails }) - .then(({ body: dashboard }) => { - return cypressWaitAll( - questions.map((query, index) => - cy.createQuestionAndAddToDashboard( - query, - dashboard.id, - cards ? cards[index] : undefined, - ), - ), - ).then(dashcardResponses => { - const questions = dashcardResponses.map( - dashcardResponse => dashcardResponse.body.card, - ); - return { - questions, - dashboard, - }; - }); - }); - }, -); diff --git a/e2e/support/commands/api/composite/createNativeQuestionAndDashboard.js b/e2e/support/commands/api/composite/createNativeQuestionAndDashboard.js deleted file mode 100644 index daac98d5f1f473b4c9d03368216b38cdc3b5037b..0000000000000000000000000000000000000000 --- a/e2e/support/commands/api/composite/createNativeQuestionAndDashboard.js +++ /dev/null @@ -1,37 +0,0 @@ -Cypress.Commands.add( - "createNativeQuestionAndDashboard", - ({ questionDetails, dashboardDetails } = {}) => { - const tabs = dashboardDetails?.tabs ?? []; - const defaultTabId = tabs[0]?.id ?? null; - - cy.createNativeQuestion(questionDetails).then( - ({ body: { id: questionId } }) => { - cy.createDashboard(dashboardDetails).then( - ({ body: { id: dashboardId } }) => { - cy.request("PUT", `/api/dashboard/${dashboardId}`, { - tabs, - dashcards: [ - { - id: -1, - card_id: questionId, - dashboard_tab_id: defaultTabId, - // Add sane defaults for the dashboard card size and position - row: 0, - col: 0, - size_x: 11, - size_y: 6, - }, - ], - }).then(response => ({ - ...response, - dashboardId, - dashboardTabs: response.body.tabs, - body: response.body.dashcards[0], - questionId, - })); - }, - ); - }, - ); - }, -); diff --git a/e2e/support/commands/api/composite/createQuestionAndAddToDashboard.js b/e2e/support/commands/api/composite/createQuestionAndAddToDashboard.js deleted file mode 100644 index aafe7f5087aa23326f3a0161978cf9ce8b51b68c..0000000000000000000000000000000000000000 --- a/e2e/support/commands/api/composite/createQuestionAndAddToDashboard.js +++ /dev/null @@ -1,33 +0,0 @@ -Cypress.Commands.add( - "createQuestionAndAddToDashboard", - (query, dashboardId, card) => - (query.native - ? cy.createNativeQuestion(query) - : cy.createQuestion(query) - ).then(({ body: { id: card_id } }) => - cy - .request(`/api/dashboard/${dashboardId}`) - .then(({ body: { dashcards } }) => - cy - .request("PUT", `/api/dashboard/${dashboardId}`, { - dashcards: [ - ...dashcards, - { - id: -1, - card_id, - // Add sane defaults for the dashboard card size and position - row: 0, - col: 0, - size_x: 11, - size_y: 8, - ...card, - }, - ], - }) - .then(response => ({ - ...response, - body: response.body.dashcards.at(-1), - })), - ), - ), -); diff --git a/e2e/support/commands/api/dashboardCard.js b/e2e/support/commands/api/dashboardCard.js deleted file mode 100644 index 92107c742b7cee64c6ca18a3016f472803663324..0000000000000000000000000000000000000000 --- a/e2e/support/commands/api/dashboardCard.js +++ /dev/null @@ -1,28 +0,0 @@ -import _ from "underscore"; - -Cypress.Commands.add( - "editDashboardCard", - (dashboardCard, updatedProperties) => { - const { id, dashboard_id } = dashboardCard; - - const cleanCard = sanitizeCard(dashboardCard); - - const updatedCard = Object.assign({}, cleanCard, updatedProperties); - - cy.log(`Edit dashboard card ${id}`); - cy.request("PUT", `/api/dashboard/${dashboard_id}`, { - dashcards: [updatedCard], - }); - }, -); - -/** - * Remove `created_at` and `updated_at` fields from the dashboard card that was previously added to the dashboard. - * We don't want to hard code these fields in the next request that we'll pass the card object to. - * - * @param {Object} card - "Old", or the existing dashboard card. - * @returns {Object} - */ -function sanitizeCard(card) { - return _.omit(card, ["created_at", "updated_at"]); -} diff --git a/e2e/support/helpers/api/createDashboardWithQuestions.ts b/e2e/support/helpers/api/createDashboardWithQuestions.ts new file mode 100644 index 0000000000000000000000000000000000000000..d2a269cd292b3ed416b3e574037058282ff4d5f2 --- /dev/null +++ b/e2e/support/helpers/api/createDashboardWithQuestions.ts @@ -0,0 +1,49 @@ +import type { Card, Dashboard } from "metabase-types/api"; + +import { cypressWaitAll } from "../e2e-misc-helpers"; + +import { type DashboardDetails, createDashboard } from "./createDashboard"; +import type { NativeQuestionDetails } from "./createNativeQuestion"; +import type { StructuredQuestionDetails } from "./createQuestion"; +import { createQuestionAndAddToDashboard } from "./createQuestionAndAddToDashboard"; + +export const createDashboardWithQuestions = ({ + dashboardName, + dashboardDetails, + questions, + cards, +}: { + dashboardName?: string; + dashboardDetails?: DashboardDetails; + questions: (NativeQuestionDetails | StructuredQuestionDetails)[]; + cards?: Partial<Card>[]; +}): Cypress.Chainable< + Cypress.Response<{ + dashboard: Dashboard; + questions: Card; + }> +> => { + // @ts-expect-error - Cypress typings don't account for what happens in then() here + return createDashboard({ name: dashboardName, ...dashboardDetails }).then( + ({ body: dashboard }) => { + return cypressWaitAll( + questions.map((query, index) => + createQuestionAndAddToDashboard( + query, + dashboard.id, + cards ? cards[index] : undefined, + ), + ), + ).then(dashcardResponses => { + const questions = dashcardResponses.map( + dashcardResponse => dashcardResponse.body.card, + ); + + return { + questions, + dashboard, + }; + }); + }, + ); +}; diff --git a/e2e/support/helpers/api/createNativeQuestionAndDashboard.ts b/e2e/support/helpers/api/createNativeQuestionAndDashboard.ts index f25f828104b0d0f7799ac3e204a8008dc02d9c6c..4f43552a29bf2fa604c219f9e03823ca199e0eec 100644 --- a/e2e/support/helpers/api/createNativeQuestionAndDashboard.ts +++ b/e2e/support/helpers/api/createNativeQuestionAndDashboard.ts @@ -1,5 +1,10 @@ import { createNativeQuestion } from "e2e/support/helpers"; -import type { CardId, Dashboard, DashboardCard } from "metabase-types/api"; +import type { + CardId, + Dashboard, + DashboardCard, + DashboardId, +} from "metabase-types/api"; import { type DashboardDetails, createDashboard } from "./createDashboard"; import type { NativeQuestionDetails } from "./createQuestion"; @@ -13,32 +18,42 @@ export const createNativeQuestionAndDashboard = ({ dashboardDetails?: DashboardDetails; cardDetails?: Partial<DashboardCard>; }): Cypress.Chainable< - Cypress.Response<DashboardCard> & { questionId: CardId } + Cypress.Response<DashboardCard> & { + dashboardId: DashboardId; + dashboardTabs: Dashboard["tabs"]; + questionId: CardId; + } > => { + const tabs = dashboardDetails?.tabs ?? []; + const defaultTabId = tabs[0]?.id ?? null; + + // @ts-expect-error - Cypress typings don't account for what happens in then() here return createNativeQuestion(questionDetails).then( ({ body: { id: questionId } }) => { - return createDashboard(dashboardDetails).then( + createDashboard(dashboardDetails).then( ({ body: { id: dashboardId } }) => { - return cy - .request<Dashboard>("PUT", `/api/dashboard/${dashboardId}`, { - dashcards: [ - { - id: -1, - card_id: questionId, - // Add sane defaults for the dashboard card size - row: 0, - col: 0, - size_x: 11, - size_y: 6, - ...cardDetails, - }, - ], - }) - .then(response => ({ - ...response, - body: response.body.dashcards[0], - questionId, - })); + cy.request("PUT", `/api/dashboard/${dashboardId}`, { + tabs, + dashcards: [ + { + id: -1, + card_id: questionId, + dashboard_tab_id: defaultTabId, + // Add sane defaults for the dashboard card size and position + row: 0, + col: 0, + size_x: 11, + size_y: 6, + ...cardDetails, + }, + ], + }).then(response => ({ + ...response, + dashboardId, + dashboardTabs: response.body.tabs, + body: response.body.dashcards[0], + questionId, + })); }, ); }, diff --git a/e2e/support/helpers/api/createQuestionAndAddToDashboard.ts b/e2e/support/helpers/api/createQuestionAndAddToDashboard.ts new file mode 100644 index 0000000000000000000000000000000000000000..d974bde1b2fb75febd060305004fe19432186fbc --- /dev/null +++ b/e2e/support/helpers/api/createQuestionAndAddToDashboard.ts @@ -0,0 +1,49 @@ +import type { Card, DashboardCard, DashboardId } from "metabase-types/api"; + +import { + type NativeQuestionDetails, + createNativeQuestion, +} from "./createNativeQuestion"; +import { + type StructuredQuestionDetails, + createQuestion, +} from "./createQuestion"; + +export const createQuestionAndAddToDashboard = ( + query: NativeQuestionDetails | StructuredQuestionDetails, + dashboardId: DashboardId, + card?: Partial<Card>, +): Cypress.Chainable<Cypress.Response<DashboardCard>> => + (isNative(query) ? createNativeQuestion(query) : createQuestion(query)).then( + ({ body: { id: card_id } }) => + cy + .request(`/api/dashboard/${dashboardId}`) + .then(({ body: { dashcards } }) => + cy + .request("PUT", `/api/dashboard/${dashboardId}`, { + dashcards: [ + ...dashcards, + { + id: -1, + card_id, + // Add sane defaults for the dashboard card size and position + row: 0, + col: 0, + size_x: 11, + size_y: 8, + ...card, + }, + ], + }) + .then(response => ({ + ...response, + body: response.body.dashcards.at(-1), + })), + ), + ); + +const isNative = ( + query: NativeQuestionDetails | StructuredQuestionDetails, +): query is NativeQuestionDetails => { + return "native" in query; +}; diff --git a/e2e/support/helpers/api/editDashboardCard.ts b/e2e/support/helpers/api/editDashboardCard.ts new file mode 100644 index 0000000000000000000000000000000000000000..676f49edd18d07503a7cbe07228bcdaad35f748f --- /dev/null +++ b/e2e/support/helpers/api/editDashboardCard.ts @@ -0,0 +1,29 @@ +import _ from "underscore"; + +import type { Dashboard, DashboardCard } from "metabase-types/api"; + +export const editDashboardCard = ( + dashboardCard: DashboardCard, + updatedProperties: Partial<DashboardCard>, +): Cypress.Chainable<Cypress.Response<Dashboard>> => { + const { id, dashboard_id } = dashboardCard; + + const cleanCard = sanitizeCard(dashboardCard); + + const updatedCard = Object.assign({}, cleanCard, updatedProperties); + + cy.log(`Edit dashboard card ${id}`); + return cy.request("PUT", `/api/dashboard/${dashboard_id}`, { + dashcards: [updatedCard], + }); +}; + +/** + * Remove `created_at` and `updated_at` fields from the dashboard card that was previously added to the dashboard. + * We don't want to hard code these fields in the next request that we'll pass the card object to. + * + * @param card - "Old", or the existing dashboard card. + */ +function sanitizeCard(card: DashboardCard) { + return _.omit(card, ["created_at", "updated_at"]); +} diff --git a/e2e/support/helpers/api/index.ts b/e2e/support/helpers/api/index.ts index ae10089e5d0542b7e77ab42d6b624a25fd47fa0c..da50b0f46650dd5353e1bbc4236c632536446d8a 100644 --- a/e2e/support/helpers/api/index.ts +++ b/e2e/support/helpers/api/index.ts @@ -7,6 +7,7 @@ export { createApiKey } from "./createApiKey"; export { createCollection } from "./createCollection"; export { createDashboard } from "./createDashboard"; export type { DashboardDetails } from "./createDashboard"; +export { createDashboardWithQuestions } from "./createDashboardWithQuestions"; export { createDashboardWithTabs } from "./createDashboardWithTabs"; export { createModerationReview } from "./createModerationReview"; export { createNativeQuestion } from "./createNativeQuestion"; @@ -18,10 +19,12 @@ export type { QuestionDetails, StructuredQuestionDetails, } from "./createQuestion"; +export { createQuestionAndAddToDashboard } from "./createQuestionAndAddToDashboard"; export { createQuestionAndDashboard } from "./createQuestionAndDashboard"; export { createTimeline } from "./createTimeline"; export { createTimelineEvent } from "./createTimelineEvent"; export { createTimelineWithEvents } from "./createTimelineWithEvents"; +export { editDashboardCard } from "./editDashboardCard"; export { getCurrentUser } from "./getCurrentUser"; export { remapDisplayValueToFK } from "./remapDisplayValueToFK"; export { updateDashboardCards } from "./updateDashboardCards";