Skip to content

Commit

Permalink
feat(analytics): Enable debug view in development
Browse files Browse the repository at this point in the history
  • Loading branch information
Ninfa-Jeon committed Feb 21, 2025
1 parent fca7e7f commit 5f17b69
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 72 deletions.
7 changes: 5 additions & 2 deletions src/components/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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, ""),
Expand Down
66 changes: 17 additions & 49 deletions src/hooks/useAnalytics.test.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,21 @@
import ReactGA from "react-ga4";
import type { MockInstance } from "vitest";
import { vi } from "vitest";

import { rootStateFactory } from "testing/factories";
import { configFactory, generalStateFactory } from "testing/factories/general";
import { renderWrappedHook } from "testing/utils";
import * as analyticsUtils from "utils/analytics";

import useAnalytics from "./useAnalytics";

vi.mock("react-ga4", () => ({
default: {
initialize: vi.fn(),
send: vi.fn(),
event: vi.fn(),
set: vi.fn(),
},
}));

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({
Expand All @@ -62,8 +27,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", () => {
Expand All @@ -77,10 +45,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", () => {
Expand All @@ -94,11 +63,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" },
);
});
});
3 changes: 1 addition & 2 deletions src/hooks/useAnalytics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -32,5 +31,5 @@ export default function useAnalytics() {
};

return (props: AnalyticMessage) =>
analytics(!!analyticsEnabled, isProduction, eventParams, props);
analytics(!!analyticsEnabled, eventParams, props);
}
33 changes: 16 additions & 17 deletions src/store/middleware/model-poller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down Expand Up @@ -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.
Expand Down
64 changes: 64 additions & 0 deletions src/utils/analytics.test.ts
Original file line number Diff line number Diff line change
@@ -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", () => {
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", () => {
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", () => {
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",
});
});
});
3 changes: 1 addition & 2 deletions src/utils/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit 5f17b69

Please sign in to comment.