diff --git a/e2e/support/helpers/e2e-ad-hoc-question-helpers.js b/e2e/support/helpers/e2e-ad-hoc-question-helpers.js index 884402bfd48d7df3625b6a4eb6eef4041343b743..041e548811d43ea2435b86073e27713fb3d3864e 100644 --- a/e2e/support/helpers/e2e-ad-hoc-question-helpers.js +++ b/e2e/support/helpers/e2e-ad-hoc-question-helpers.js @@ -66,7 +66,7 @@ export function startNewNativeQuestion(alias = "editor") { */ export function visitQuestionAdhoc( question, - { callback, mode, autorun = true } = {}, + { callback, mode, autorun = true, skipWaiting = false } = {}, ) { const questionMode = mode === "notebook" ? "/notebook" : ""; @@ -78,7 +78,7 @@ export function visitQuestionAdhoc( runQueryIfNeeded(question, autorun); - if (mode !== "notebook") { + if (mode !== "notebook" && !skipWaiting) { cy.wait("@" + alias).then(xhr => callback && callback(xhr)); } } diff --git a/e2e/test/scenarios/question/document-title.cy.spec.js b/e2e/test/scenarios/question/document-title.cy.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..9b1ed6a9e02360ab281fd0a06e5e3b1bb10b297d --- /dev/null +++ b/e2e/test/scenarios/question/document-title.cy.spec.js @@ -0,0 +1,28 @@ +import { restore, visitQuestionAdhoc } from "e2e/support/helpers"; + +const PG_DB_ID = 2; +describe("question loading changes document title", () => { + beforeEach(() => { + restore("postgres-12"); + cy.signInAsNormalUser(); + }); + + it("should verify document title changes while loading a slow question (metabase#40051)", () => { + cy.log("run a slow question"); + + visitQuestionAdhoc( + { + dataset_query: { + type: "native", + native: { + query: "select pg_sleep(60)", + }, + database: PG_DB_ID, + }, + }, + { skipWaiting: true }, + ); + + cy.title().should("eq", "Doing science... · Metabase"); + }); +}); diff --git a/enterprise/frontend/src/metabase-enterprise/whitelabel/index.js b/enterprise/frontend/src/metabase-enterprise/whitelabel/index.js index 57e921fede644ccdfda5bb56a30e8fa569bc202f..920edb2a905aa3be06691b4d1db456b870207c15 100644 --- a/enterprise/frontend/src/metabase-enterprise/whitelabel/index.js +++ b/enterprise/frontend/src/metabase-enterprise/whitelabel/index.js @@ -2,7 +2,7 @@ import { t } from "ttag"; import MetabaseSettings from "metabase/lib/settings"; import { - PLUGIN_APP_INIT_FUCTIONS, + PLUGIN_APP_INIT_FUNCTIONS, PLUGIN_LANDING_PAGE, PLUGIN_LOGO_ICON_COMPONENTS, PLUGIN_ADMIN_SETTINGS_UPDATES, @@ -124,7 +124,7 @@ if (hasPremiumFeature("whitelabel")) { ...sections, })); - PLUGIN_APP_INIT_FUCTIONS.push(() => { + PLUGIN_APP_INIT_FUNCTIONS.push(() => { updateColors(); MetabaseSettings.on("application-colors", updateColors); }); @@ -133,7 +133,7 @@ if (hasPremiumFeature("whitelabel")) { PLUGIN_SELECTORS.canWhitelabel = () => true; // these selectors control whitelabeling UI - PLUGIN_SELECTORS.getLoadingMessage = getLoadingMessage; + PLUGIN_SELECTORS.getLoadingMessageFactory = getLoadingMessage; PLUGIN_SELECTORS.getIsWhiteLabeling = getIsWhiteLabeling; PLUGIN_SELECTORS.getApplicationName = getApplicationName; PLUGIN_SELECTORS.getShowMetabaseLinks = getShowMetabaseLinks; diff --git a/frontend/src/metabase/app.js b/frontend/src/metabase/app.js index 8cc0f77e906293d468bd9461e2e34f3d242c25ba..53ce42e0a19b8ddb9dc7dfc7da5aff59dc6d6515 100644 --- a/frontend/src/metabase/app.js +++ b/frontend/src/metabase/app.js @@ -42,7 +42,7 @@ import { createTracker } from "metabase/lib/analytics"; import api from "metabase/lib/api"; import { initializeEmbedding } from "metabase/lib/embed"; import MetabaseSettings from "metabase/lib/settings"; -import { PLUGIN_APP_INIT_FUCTIONS } from "metabase/plugins"; +import { PLUGIN_APP_INIT_FUNCTIONS } from "metabase/plugins"; import { refreshSiteSettings } from "metabase/redux/settings"; import { EmotionCacheProvider } from "metabase/styled-components/components/EmotionCacheProvider"; import GlobalStyles from "metabase/styled-components/containers/GlobalStyles"; @@ -90,7 +90,7 @@ function _init(reducers, getRoutes, callback) { store.dispatch(refreshSiteSettings()); - PLUGIN_APP_INIT_FUCTIONS.forEach(init => init({ root })); + PLUGIN_APP_INIT_FUNCTIONS.forEach(init => init({ root })); window.Metabase = window.Metabase || {}; window.Metabase.store = store; diff --git a/frontend/src/metabase/plugins/index.ts b/frontend/src/metabase/plugins/index.ts index 2b3469989056f686d9f892cccaa2d21ad307ffd6..5e4354018f67a4ecde0ae9f17bb98f2b352cf6c7 100644 --- a/frontend/src/metabase/plugins/index.ts +++ b/frontend/src/metabase/plugins/index.ts @@ -33,7 +33,7 @@ import type { AdminPathKey, State } from "metabase-types/store"; import type { GetAuthProviders, PluginGroupManagersType } from "./types"; // functions called when the application is started -export const PLUGIN_APP_INIT_FUCTIONS = []; +export const PLUGIN_APP_INIT_FUNCTIONS = []; // function to determine the landing page export const PLUGIN_LANDING_PAGE = []; @@ -146,7 +146,7 @@ export const PLUGIN_IS_PASSWORD_USER: ((user: User) => boolean)[] = []; // selectors that customize behavior between app versions export const PLUGIN_SELECTORS = { canWhitelabel: (_state: State) => false, - getLoadingMessage: (_state: State) => (isSlow: boolean) => + getLoadingMessageFactory: (_state: State) => (isSlow: boolean) => isSlow ? t`Waiting for results...` : t`Doing science...`, getIsWhiteLabeling: (_state: State) => false, getApplicationName: (_state: State) => "Metabase", diff --git a/frontend/src/metabase/query_builder/actions/querying.js b/frontend/src/metabase/query_builder/actions/querying.js index ab0af65a33763cbe53bafc83eefc4ac715d48ef0..dc7b4054c455ca2307a749a8a1c217b81c10ddb1 100644 --- a/frontend/src/metabase/query_builder/actions/querying.js +++ b/frontend/src/metabase/query_builder/actions/querying.js @@ -5,7 +5,7 @@ import * as MetabaseAnalytics from "metabase/lib/analytics"; import { startTimer } from "metabase/lib/performance"; import { defer } from "metabase/lib/promise"; import { createThunkAction } from "metabase/lib/redux"; -import { getWhiteLabeledLoadingMessage } from "metabase/selectors/whitelabel"; +import { getWhiteLabeledLoadingMessageFactory } from "metabase/selectors/whitelabel"; import { runQuestionQuery as apiRunQuestionQuery } from "metabase/services"; import { getSensibleDisplays } from "metabase/visualizations"; import * as Lib from "metabase-lib"; @@ -146,7 +146,9 @@ export const runQuestionQuery = ({ const loadStartUIControls = createThunkAction( LOAD_START_UI_CONTROLS, () => (dispatch, getState) => { - const loadingMessage = getWhiteLabeledLoadingMessage(getState()); + const getLoadingMessage = getWhiteLabeledLoadingMessageFactory(getState()); + const loadingMessage = getLoadingMessage(); + const title = { onceQueryIsRun: loadingMessage, ifQueryTakesLong: t`Still Here...`, diff --git a/frontend/src/metabase/query_builder/components/QueryVisualization.jsx b/frontend/src/metabase/query_builder/components/QueryVisualization.jsx index 11fc11c62f10213a4fa116828b60e59eb853a5c4..30564718cd24a3da4fd9d9852c788e737ad79d90 100644 --- a/frontend/src/metabase/query_builder/components/QueryVisualization.jsx +++ b/frontend/src/metabase/query_builder/components/QueryVisualization.jsx @@ -6,7 +6,7 @@ import { t } from "ttag"; import LoadingSpinner from "metabase/components/LoadingSpinner"; import { useSelector } from "metabase/lib/redux"; -import { getWhiteLabeledLoadingMessage } from "metabase/selectors/whitelabel"; +import { getWhiteLabeledLoadingMessageFactory } from "metabase/selectors/whitelabel"; import { HARD_ROW_LIMIT } from "metabase-lib/queries/utils"; import RunButtonWithTooltip from "./RunButtonWithTooltip"; @@ -82,11 +82,11 @@ export const VisualizationEmptyState = ({ className }) => ( export function VisualizationRunningState({ className = "" }) { const [isSlow] = useTimeout(SLOW_MESSAGE_TIMEOUT); - const loadingMessage = useSelector(getWhiteLabeledLoadingMessage); + const getLoadingMessage = useSelector(getWhiteLabeledLoadingMessageFactory); // show the slower loading message only when the loadingMessage is - // not customised - const message = loadingMessage(isSlow()); + // not customized + const message = getLoadingMessage(isSlow()); return ( <div diff --git a/frontend/src/metabase/query_builder/components/QueryVisualization.unit.spec.tsx b/frontend/src/metabase/query_builder/components/QueryVisualization.unit.spec.tsx index d4834a40c689293d2860c0e9a10ca8842c6b2a0c..b0eecc07a3a2c7db051f07505d546cf80ec86ce5 100644 --- a/frontend/src/metabase/query_builder/components/QueryVisualization.unit.spec.tsx +++ b/frontend/src/metabase/query_builder/components/QueryVisualization.unit.spec.tsx @@ -10,7 +10,7 @@ type SetupOpts = { function setup({ customMessage }: SetupOpts = {}) { if (customMessage) { jest - .spyOn(PLUGIN_SELECTORS, "getLoadingMessage") + .spyOn(PLUGIN_SELECTORS, "getLoadingMessageFactory") .mockImplementation(() => customMessage); } diff --git a/frontend/src/metabase/selectors/whitelabel/index.ts b/frontend/src/metabase/selectors/whitelabel/index.ts index b162607788468c5fb0b954d9f069002be25e9b0a..66786e4394a64c34a711299ab5dc75feafd36eeb 100644 --- a/frontend/src/metabase/selectors/whitelabel/index.ts +++ b/frontend/src/metabase/selectors/whitelabel/index.ts @@ -1,8 +1,8 @@ import { PLUGIN_SELECTORS } from "metabase/plugins"; import type { State } from "metabase-types/store"; -export function getWhiteLabeledLoadingMessage(state: State) { - return PLUGIN_SELECTORS.getLoadingMessage(state); +export function getWhiteLabeledLoadingMessageFactory(state: State) { + return PLUGIN_SELECTORS.getLoadingMessageFactory(state); } export function getIsWhiteLabeling(state: State) { diff --git a/frontend/src/metabase/selectors/whitelabel/tests/common.unit.spec.ts b/frontend/src/metabase/selectors/whitelabel/tests/common.unit.spec.ts index 5b72bb1a5d1a912851ef3afe341053f6c7c7a394..6e95a37bb22ae9ec718f09dd68607f1972795f64 100644 --- a/frontend/src/metabase/selectors/whitelabel/tests/common.unit.spec.ts +++ b/frontend/src/metabase/selectors/whitelabel/tests/common.unit.spec.ts @@ -2,7 +2,7 @@ import { getApplicationName, getIsWhiteLabeling, getShowMetabaseLinks, - getWhiteLabeledLoadingMessage, + getWhiteLabeledLoadingMessageFactory, getCanWhitelabel, } from ".."; @@ -12,10 +12,10 @@ describe("getWhiteLabeledLoadingMessage (OSS)", () => { it("should return 'Doing science...' when loading-message is set to 'doing-science'", () => { const { getState } = setup({ loadingMessage: "doing-science" }); - expect(getWhiteLabeledLoadingMessage(getState())(false)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(false)).toBe( "Doing science...", ); - expect(getWhiteLabeledLoadingMessage(getState())(true)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(true)).toBe( "Waiting for results...", ); }); @@ -23,10 +23,10 @@ describe("getWhiteLabeledLoadingMessage (OSS)", () => { it("should return 'Doing science...' when loading-message is set to 'loading-results'", () => { const { getState } = setup({ loadingMessage: "loading-results" }); - expect(getWhiteLabeledLoadingMessage(getState())(false)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(false)).toBe( "Doing science...", ); - expect(getWhiteLabeledLoadingMessage(getState())(true)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(true)).toBe( "Waiting for results...", ); }); @@ -34,10 +34,10 @@ describe("getWhiteLabeledLoadingMessage (OSS)", () => { it("should return 'Doing science...' when loading-message is set to 'running-query'", () => { const { getState } = setup({ loadingMessage: "running-query" }); - expect(getWhiteLabeledLoadingMessage(getState())(false)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(false)).toBe( "Doing science...", ); - expect(getWhiteLabeledLoadingMessage(getState())(true)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(true)).toBe( "Waiting for results...", ); }); diff --git a/frontend/src/metabase/selectors/whitelabel/tests/enterprise.unit.spec.ts b/frontend/src/metabase/selectors/whitelabel/tests/enterprise.unit.spec.ts index 32c8a42486b23fdb5d5e14acc025a5d8f39e7e5e..861df1d48e114ecc268269ebc39151d859e5d1fa 100644 --- a/frontend/src/metabase/selectors/whitelabel/tests/enterprise.unit.spec.ts +++ b/frontend/src/metabase/selectors/whitelabel/tests/enterprise.unit.spec.ts @@ -3,7 +3,7 @@ import { getCanWhitelabel, getIsWhiteLabeling, getShowMetabaseLinks, - getWhiteLabeledLoadingMessage, + getWhiteLabeledLoadingMessageFactory, } from ".."; import type { SetupOpts } from "./setup"; @@ -17,10 +17,10 @@ describe("getWhiteLabeledLoadingMessage (EE without token)", () => { it("should return 'Doing science...' when loading-message is set to 'doing-science'", () => { const { getState } = setup({ loadingMessage: "doing-science" }); - expect(getWhiteLabeledLoadingMessage(getState())(false)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(false)).toBe( "Doing science...", ); - expect(getWhiteLabeledLoadingMessage(getState())(true)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(true)).toBe( "Waiting for results...", ); }); @@ -28,10 +28,10 @@ describe("getWhiteLabeledLoadingMessage (EE without token)", () => { it("should return 'Doing science...' when loading-message is set to 'loading-results'", () => { const { getState } = setup({ loadingMessage: "loading-results" }); - expect(getWhiteLabeledLoadingMessage(getState())(false)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(false)).toBe( "Doing science...", ); - expect(getWhiteLabeledLoadingMessage(getState())(true)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(true)).toBe( "Waiting for results...", ); }); @@ -39,10 +39,10 @@ describe("getWhiteLabeledLoadingMessage (EE without token)", () => { it("should return 'Doing science...' when loading-message is set to 'running-query'", () => { const { getState } = setup({ loadingMessage: "running-query" }); - expect(getWhiteLabeledLoadingMessage(getState())(false)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(false)).toBe( "Doing science...", ); - expect(getWhiteLabeledLoadingMessage(getState())(true)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(true)).toBe( "Waiting for results...", ); }); diff --git a/frontend/src/metabase/selectors/whitelabel/tests/premium.unit.spec.ts b/frontend/src/metabase/selectors/whitelabel/tests/premium.unit.spec.ts index 7b8fbb663c1b60c58f21d681718eb250e57d71bb..ec95caf1afc30c2b23a88b3c423a09210394c450 100644 --- a/frontend/src/metabase/selectors/whitelabel/tests/premium.unit.spec.ts +++ b/frontend/src/metabase/selectors/whitelabel/tests/premium.unit.spec.ts @@ -3,7 +3,7 @@ import { getCanWhitelabel, getIsWhiteLabeling, getShowMetabaseLinks, - getWhiteLabeledLoadingMessage, + getWhiteLabeledLoadingMessageFactory, } from ".."; import type { SetupOpts } from "./setup"; @@ -21,10 +21,10 @@ describe("getWhiteLabeledLoadingMessage (EE with token)", () => { it("should return 'Doing science...' when loading-message is set to 'doing-science'", () => { const { getState } = setup({ loadingMessage: "doing-science" }); - expect(getWhiteLabeledLoadingMessage(getState())(false)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(false)).toBe( "Doing science...", ); - expect(getWhiteLabeledLoadingMessage(getState())(true)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(true)).toBe( "Waiting for results...", ); }); @@ -32,10 +32,10 @@ describe("getWhiteLabeledLoadingMessage (EE with token)", () => { it("should return 'Loading results...' when loading-message is set to 'loading-results'", () => { const { getState } = setup({ loadingMessage: "loading-results" }); - expect(getWhiteLabeledLoadingMessage(getState())(false)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(false)).toBe( "Loading results...", ); - expect(getWhiteLabeledLoadingMessage(getState())(true)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(true)).toBe( "Loading results...", ); }); @@ -43,10 +43,10 @@ describe("getWhiteLabeledLoadingMessage (EE with token)", () => { it("should return 'Running query...' when loading-message is set to 'running-query'", () => { const { getState } = setup({ loadingMessage: "running-query" }); - expect(getWhiteLabeledLoadingMessage(getState())(false)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(false)).toBe( "Running query...", ); - expect(getWhiteLabeledLoadingMessage(getState())(true)).toBe( + expect(getWhiteLabeledLoadingMessageFactory(getState())(true)).toBe( "Running query...", ); });