From 5b6d39426712eab50106eea2a384b144fcc44331 Mon Sep 17 00:00:00 2001 From: Urvashi Sharma Date: Fri, 21 Feb 2025 13:19:59 +0530 Subject: [PATCH] feat(analytics): Enable debug view in development --- src/components/App/App.tsx | 7 ++- src/hooks/useAnalytics.test.ts | 64 ++++++++-------------------- src/hooks/useAnalytics.tsx | 3 +- src/store/middleware/model-poller.ts | 33 +++++++------- src/utils/analytics.test.ts | 64 ++++++++++++++++++++++++++++ src/utils/analytics.ts | 3 +- 6 files changed, 104 insertions(+), 70 deletions(-) create mode 100644 src/utils/analytics.test.ts diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx index a5de21151..36118aa2f 100644 --- a/src/components/App/App.tsx +++ b/src/components/App/App.tsx @@ -11,8 +11,11 @@ import "../../scss/index.scss"; function App() { const isProduction = import.meta.env.PROD; const analyticsEnabled = useAppSelector(getAnalyticsEnabled); - if (isProduction && analyticsEnabled) { - ReactGA.initialize("G-JHXHM8VXJ1"); // send { gaOptions: { debug_mode: true } } as second param to enable debugView + if (analyticsEnabled) { + ReactGA.initialize( + "G-JHXHM8VXJ1", + isProduction ? {} : { gaOptions: { debug_mode: true } }, + ); ReactGA.send({ hitType: "pageview", page: window.location.href.replace(window.location.origin, ""), diff --git a/src/hooks/useAnalytics.test.ts b/src/hooks/useAnalytics.test.ts index 8e3e734c8..cbf4c3978 100644 --- a/src/hooks/useAnalytics.test.ts +++ b/src/hooks/useAnalytics.test.ts @@ -7,50 +7,17 @@ import { configFactory, generalStateFactory } from "testing/factories/general"; import { renderWrappedHook } from "testing/utils"; import useAnalytics from "./useAnalytics"; - -vi.mock("react-ga4", () => ({ - default: { - initialize: vi.fn(), - send: vi.fn(), - event: vi.fn(), - set: vi.fn(), - }, -})); +import * as analyticsUtils from "utils/analytics"; describe("useAnalytics", () => { - let pageviewSpy: MockInstance; - let eventSpy: MockInstance; - beforeEach(() => { - vi.stubEnv("PROD", true); - eventSpy = vi.spyOn(ReactGA, "event"); - pageviewSpy = vi.spyOn(ReactGA, "send"); + vi.spyOn(analyticsUtils, "default").mockImplementation(() => vi.fn()); }); afterEach(() => { localStorage.clear(); }); - afterAll(() => { - vi.unstubAllEnvs(); - }); - - it("does not send events in development", () => { - vi.stubEnv("PROD", false); - const { result } = renderWrappedHook(() => useAnalytics(), { - state: rootStateFactory.build({ - general: generalStateFactory.build({ - config: configFactory.build({ - analyticsEnabled: true, - }), - }), - }), - }); - result.current({ path: "/some/path" }); - expect(eventSpy).not.toHaveBeenCalled(); - expect(pageviewSpy).not.toHaveBeenCalled(); - }); - it("does not send events if analytics are disabled", () => { const { result } = renderWrappedHook(() => useAnalytics(), { state: rootStateFactory.build({ @@ -62,8 +29,11 @@ describe("useAnalytics", () => { }), }); result.current({ path: "/some/path" }); - expect(eventSpy).not.toHaveBeenCalled(); - expect(pageviewSpy).not.toHaveBeenCalled(); + expect(analyticsUtils.default).toHaveBeenCalledWith( + false, + { controllerVersion: "", dashboardVersion: "", isJuju: "false" }, + { path: "/some/path" }, + ); }); it("can send pageview events", () => { @@ -77,10 +47,11 @@ describe("useAnalytics", () => { }), }); result.current({ path: "/some/path" }); - expect(pageviewSpy).toHaveBeenCalledWith({ - hitType: "pageview", - page: "/some/path", - }); + expect(analyticsUtils.default).toHaveBeenCalledWith( + true, + { controllerVersion: "", dashboardVersion: "", isJuju: "false" }, + { path: "/some/path" }, + ); }); it("can send events", () => { @@ -94,11 +65,10 @@ describe("useAnalytics", () => { }), }); result.current({ category: "sidebar", action: "toggle" }); - expect(eventSpy).toHaveBeenCalledWith("toggle", { - category: "sidebar", - controllerVersion: "", - dashboardVersion: "", - isJuju: "false", - }); + expect(analyticsUtils.default).toHaveBeenCalledWith( + true, + { controllerVersion: "", dashboardVersion: "", isJuju: "false" }, + { category: "sidebar", action: "toggle" }, + ); }); }); diff --git a/src/hooks/useAnalytics.tsx b/src/hooks/useAnalytics.tsx index fb09d98e6..8be73b51a 100644 --- a/src/hooks/useAnalytics.tsx +++ b/src/hooks/useAnalytics.tsx @@ -18,7 +18,6 @@ type AnalyticMessage = { export default function useAnalytics() { const analyticsEnabled = useAppSelector(getAnalyticsEnabled); - const isProduction = import.meta.env.PROD; const isJuju = useSelector(getIsJuju); const appVersion = useSelector(getAppVersion); const wsControllerURL = useAppSelector(getWSControllerURL); @@ -32,5 +31,5 @@ export default function useAnalytics() { }; return (props: AnalyticMessage) => - analytics(!!analyticsEnabled, isProduction, eventParams, props); + analytics(!!analyticsEnabled, eventParams, props); } diff --git a/src/store/middleware/model-poller.ts b/src/store/middleware/model-poller.ts index 550fd2345..63dc0fc1d 100644 --- a/src/store/middleware/model-poller.ts +++ b/src/store/middleware/model-poller.ts @@ -22,8 +22,8 @@ import { actions as jujuActions } from "store/juju"; import type { RootState, Store } from "store/store"; import { isSpecificAction } from "types"; import { toErrorString } from "utils"; -import { logger } from "utils/logger"; import analytics from "utils/analytics"; +import { logger } from "utils/logger"; export enum LoginError { LOG = "Unable to log into controller.", @@ -133,26 +133,25 @@ export const modelPollerMiddleware: Middleware< return; } - // XXX Now that we can register multiple controllers this needs - // to be sent per controller. const isProduction = import.meta.env.PROD; const analyticsEnabled = window.jujuDashboardConfig?.analyticsEnabled; - if (isProduction && analyticsEnabled) { - const isJuju = (!!getIsJuju(reduxStore.getState())).toString(); - const dashboardVersion = getAppVersion(reduxStore.getState()) ?? ""; - const controllerVersion = conn.info.serverVersion ?? ""; + const isJuju = (!!getIsJuju(reduxStore.getState())).toString(); + const dashboardVersion = getAppVersion(reduxStore.getState()) ?? ""; + const controllerVersion = conn.info.serverVersion ?? ""; - Sentry.setTag("jujuVersion", conn.info.serverVersion); + analytics( + !!analyticsEnabled, + { dashboardVersion, controllerVersion, isJuju }, + { + category: "Authentication", + action: `User Login (${authMethod})`, + }, + ); - analytics( - analyticsEnabled, - isProduction, - { dashboardVersion, controllerVersion, isJuju }, - { - category: "Authentication", - action: `User Login (${authMethod})`, - }, - ); + // XXX Now that we can register multiple controllers this needs + // to be sent per controller. + if (isProduction && analyticsEnabled) { + Sentry.setTag("jujuVersion", conn.info.serverVersion); } // Remove the getFacade function as this doesn't need to be stored in Redux. diff --git a/src/utils/analytics.test.ts b/src/utils/analytics.test.ts new file mode 100644 index 000000000..45ee548e1 --- /dev/null +++ b/src/utils/analytics.test.ts @@ -0,0 +1,64 @@ +import ReactGA from "react-ga4"; +import type { MockInstance } from "vitest"; +import { vi } from "vitest"; + +import analytics from "./analytics"; + +vi.mock("react-ga4", () => ({ + default: { + initialize: vi.fn(), + send: vi.fn(), + event: vi.fn(), + set: vi.fn(), + }, +})); + +describe("analytics", () => { + let pageviewSpy: MockInstance; + let eventSpy: MockInstance; + + beforeEach(() => { + eventSpy = vi.spyOn(ReactGA, "event"); + pageviewSpy = vi.spyOn(ReactGA, "send"); + }); + + afterEach(() => { + localStorage.clear(); + }); + + it("does not send events if analytics are disabled", () => { + const result = analytics( + false, + { dashboardVersion: "1.0.0", controllerVersion: "1.0.0", isJuju: "true" }, + { path: "/some/path" }, + ); + expect(eventSpy).not.toHaveBeenCalled(); + expect(pageviewSpy).not.toHaveBeenCalled(); + }); + + it("can send pageview events", () => { + const result = analytics( + true, + { dashboardVersion: "1.0.0", controllerVersion: "1.0.0", isJuju: "true" }, + { path: "/some/path" }, + ); + expect(pageviewSpy).toHaveBeenCalledWith({ + hitType: "pageview", + page: "/some/path", + }); + }); + + it("can send events", () => { + const result = analytics( + true, + { dashboardVersion: "1.0.0", controllerVersion: "1.0.0", isJuju: "true" }, + { category: "sidebar", action: "toggle" }, + ); + expect(eventSpy).toHaveBeenCalledWith("toggle", { + category: "sidebar", + controllerVersion: "1.0.0", + dashboardVersion: "1.0.0", + isJuju: "true", + }); + }); +}); diff --git a/src/utils/analytics.ts b/src/utils/analytics.ts index 2e375e967..18fc445b1 100644 --- a/src/utils/analytics.ts +++ b/src/utils/analytics.ts @@ -10,11 +10,10 @@ type AnalyticsMessage = { path?: string; category?: string; action?: string }; const analytics = ( analyticsEnabled: boolean, - isProduction: boolean, eventParams: EventParams, { path, category = "", action = "" }: AnalyticsMessage, ) => { - if (!isProduction || !analyticsEnabled) { + if (!analyticsEnabled) { return; } if (path) {