Files
portifolio-evandrodevbr/app/components/GoogleAnalytics.test.tsx
T

285 lines
8.9 KiB
TypeScript

import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import type { ComponentProps } from "react";
import GoogleAnalytics from "./GoogleAnalytics";
jest.mock("next/script", () => ({
__esModule: true,
default: ({
strategy: _strategy,
...props
}: ComponentProps<"script"> & { strategy?: string }) => <script {...props} />,
}));
const CONSENT_KEY = "evandro.analytics-consent.v1";
const GA_DISABLE_KEY = "ga-disable-G-QTT9QT0T3S";
const GA_SRC = "https://www.googletagmanager.com/gtag/js?id=G-QTT9QT0T3S";
type AnalyticsWindow = Window & {
dataLayer?: IArguments[];
gtag?: (...args: unknown[]) => void;
[GA_DISABLE_KEY]?: boolean;
};
const analyticsWindow = window as AnalyticsWindow;
function consentUpdates(gtag: jest.Mock) {
return gtag.mock.calls.filter(
([command, action]) => command === "consent" && action === "update",
);
}
function setCookie(value: string) {
// biome-ignore lint/suspicious/noDocumentCookie: JSDOM cookie setup is the behavior boundary exercised by revocation.
document.cookie = value;
}
describe("GoogleAnalytics consent", () => {
beforeEach(() => {
jest.replaceProperty(process.env, "NODE_ENV", "production");
document.getElementById("google-analytics-script")?.remove();
localStorage.clear();
setCookie("_ga=; Max-Age=0; Path=/");
setCookie("_ga_PORTFOLIO=; Max-Age=0; Path=/");
setCookie("portfolio_session=; Max-Age=0; Path=/");
analyticsWindow.dataLayer = undefined;
analyticsWindow.gtag = undefined;
analyticsWindow[GA_DISABLE_KEY] = undefined;
});
afterEach(() => {
jest.restoreAllMocks();
});
it("waits for a decision before loading analytics", async () => {
render(<GoogleAnalytics />);
expect(
await screen.findByRole("button", { name: "Aceitar analytics" }),
).toBeVisible();
expect(screen.getByRole("dialog")).toHaveAccessibleDescription();
expect(
screen.queryByTestId("google-analytics-script"),
).not.toBeInTheDocument();
expect(
screen.getByRole("link", { name: "Ler política de privacidade" }),
).toHaveAttribute("href", "/privacidade");
});
it("persists acceptance and enables analytics without advertising consent", async () => {
const user = userEvent.setup();
render(<GoogleAnalytics />);
await user.click(
await screen.findByRole("button", { name: "Aceitar analytics" }),
);
expect(localStorage.getItem(CONSENT_KEY)).toBe("granted");
expect(screen.getByTestId("google-analytics-script")).toHaveAttribute(
"src",
GA_SRC,
);
const commands = analyticsWindow.dataLayer?.map((entry) =>
Array.from(entry),
);
expect(commands).toContainEqual([
"consent",
"default",
{
analytics_storage: "denied",
ad_storage: "denied",
ad_user_data: "denied",
ad_personalization: "denied",
},
]);
expect(commands).toContainEqual([
"consent",
"update",
{
analytics_storage: "granted",
ad_storage: "denied",
ad_user_data: "denied",
ad_personalization: "denied",
},
]);
expect(commands).toContainEqual([
"config",
"G-QTT9QT0T3S",
{
allow_ad_personalization_signals: false,
anonymize_ip: true,
},
]);
expect(commands).toContainEqual([
"config",
"AW-18432694647",
{ allow_ad_personalization_signals: false },
]);
expect(commands).toContainEqual(["set", "ads_data_redaction", true]);
expect(
screen.getByRole("button", { name: "Preferências de analytics" }),
).not.toHaveFocus();
});
it("persists refusal and keeps analytics absent after reload", async () => {
const user = userEvent.setup();
const firstRender = render(<GoogleAnalytics />);
await user.click(
await screen.findByRole("button", { name: "Recusar analytics" }),
);
expect(localStorage.getItem(CONSENT_KEY)).toBe("denied");
expect(
screen.queryByTestId("google-analytics-script"),
).not.toBeInTheDocument();
firstRender.unmount();
render(<GoogleAnalytics />);
expect(
await screen.findByRole("button", { name: "Preferências de analytics" }),
).toBeVisible();
expect(
screen.queryByTestId("google-analytics-script"),
).not.toBeInTheDocument();
});
it("keeps preferences reachable and fully revokes analytics only", async () => {
const user = userEvent.setup();
localStorage.setItem(CONSENT_KEY, "granted");
setCookie("_ga=main; Path=/");
setCookie("_ga_PORTFOLIO=property; Path=/");
setCookie("portfolio_session=keep; Path=/");
const gtag = jest.fn();
analyticsWindow.gtag = gtag;
render(<GoogleAnalytics />);
await screen.findByTestId("google-analytics-script");
await user.click(
screen.getByRole("button", { name: "Preferências de analytics" }),
);
await user.click(screen.getByRole("button", { name: "Revogar analytics" }));
expect(localStorage.getItem(CONSENT_KEY)).toBe("denied");
expect(analyticsWindow[GA_DISABLE_KEY]).toBe(true);
expect(consentUpdates(gtag)).toContainEqual([
"consent",
"update",
{
analytics_storage: "denied",
ad_storage: "denied",
ad_user_data: "denied",
ad_personalization: "denied",
},
]);
expect(document.cookie).not.toContain("_ga=");
expect(document.cookie).not.toContain("_ga_PORTFOLIO=");
expect(document.cookie).toContain("portfolio_session=keep");
expect(
screen.queryByTestId("google-analytics-script"),
).not.toBeInTheDocument();
const preferencesButton = screen.getByRole("button", {
name: "Preferências de analytics",
});
expect(preferencesButton).toBeVisible();
expect(preferencesButton).toHaveFocus();
});
it("restores focus after closing or applying opened preferences", async () => {
const user = userEvent.setup();
localStorage.setItem(CONSENT_KEY, "denied");
render(<GoogleAnalytics />);
const preferencesButton = await screen.findByRole("button", {
name: "Preferências de analytics",
});
await user.click(preferencesButton);
expect(
screen.getByRole("link", { name: "Ler política de privacidade" }),
).toHaveAttribute("href", "/privacidade");
await user.click(
screen.getByRole("button", { name: "Fechar preferências" }),
);
expect(preferencesButton).toHaveFocus();
await user.click(preferencesButton);
await user.click(screen.getByRole("button", { name: "Aceitar analytics" }));
expect(preferencesButton).toHaveFocus();
});
it("fails closed when local storage is unavailable", async () => {
const user = userEvent.setup();
jest.spyOn(Storage.prototype, "getItem").mockImplementation(() => {
throw new Error("storage blocked");
});
jest.spyOn(Storage.prototype, "setItem").mockImplementation(() => {
throw new Error("storage blocked");
});
render(<GoogleAnalytics />);
await user.click(
await screen.findByRole("button", { name: "Aceitar analytics" }),
);
await waitFor(() => {
expect(
screen.queryByTestId("google-analytics-script"),
).not.toBeInTheDocument();
});
});
it("reports when revocation cannot be persisted", async () => {
const user = userEvent.setup();
localStorage.setItem(CONSENT_KEY, "granted");
jest.spyOn(Storage.prototype, "setItem").mockImplementation(() => {
throw new Error("storage blocked");
});
render(<GoogleAnalytics />);
await screen.findByTestId("google-analytics-script");
await user.click(
screen.getByRole("button", { name: "Preferências de analytics" }),
);
await user.click(screen.getByRole("button", { name: "Revogar analytics" }));
expect(
screen.getByRole("alert", {
name: "Falha ao salvar o consentimento",
}),
).toHaveTextContent(
"Não foi possível salvar a revogação. Tente novamente para concluir.",
);
expect(analyticsWindow[GA_DISABLE_KEY]).not.toBe(true);
expect(
screen.getByRole("button", { name: "Revogar analytics" }),
).toBeVisible();
});
it("reports when refusal cannot be persisted", async () => {
const user = userEvent.setup();
jest.spyOn(Storage.prototype, "setItem").mockImplementation(() => {
throw new Error("storage blocked");
});
render(<GoogleAnalytics />);
await user.click(
await screen.findByRole("button", { name: "Recusar analytics" }),
);
expect(
screen.getByRole("alert", {
name: "Falha ao salvar o consentimento",
}),
).toHaveTextContent(
"Não foi possível salvar sua recusa. Tente novamente para concluir.",
);
expect(
screen.getByRole("button", { name: "Recusar analytics" }),
).toBeVisible();
expect(
screen.queryByRole("button", { name: "Preferências de analytics" }),
).not.toBeInTheDocument();
});
});