Home mais curta (hero, vitrine de clientes, sobre, serviços, IA resumida, depoimentos e contato), faixa de clientes e telas reais no hero, sem números do GitHub. Barra de cookies fina, WhatsApp fixo no celular, formulário com e-mail opcional e alternativa por e-mail, página /ia, eventos de e-mail, redes e currículo e conversão do Ads opcional por variável. Rodapé em 3 colunas. Remove componentes órfãos (LighthouseMetrics, Skills, Experience, contagem do GitHub).
190 lines
5.7 KiB
TypeScript
190 lines
5.7 KiB
TypeScript
import {
|
|
GA_DISABLE_KEY,
|
|
readConsent,
|
|
trackEmailClick,
|
|
trackSocialClick,
|
|
trackWhatsAppOpen,
|
|
} from "./analytics";
|
|
|
|
/**
|
|
* Testes de caminhos-base de lib/analytics.ts (complemento de analytics.test.ts):
|
|
* modelo de rota de cada página (analyticsPagePath), cada condição que silencia o envio (sendEvent)
|
|
* e a conversão do Google Ads (sendAdsConversion) com e sem etiqueta, com e sem consentimento.
|
|
*/
|
|
|
|
const CONSENT_KEY = "evandro.analytics-consent.v1";
|
|
const ADS_LABEL = "NEXT_PUBLIC_GOOGLE_ADS_CONVERSION_LABEL";
|
|
|
|
beforeEach(() => {
|
|
jest.replaceProperty(process.env, "NODE_ENV", "production");
|
|
localStorage.setItem(CONSENT_KEY, "granted");
|
|
delete window[GA_DISABLE_KEY];
|
|
window.gtag = jest.fn();
|
|
window.history.replaceState({}, "", "/");
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.restoreAllMocks();
|
|
localStorage.clear();
|
|
delete window.gtag;
|
|
delete process.env[ADS_LABEL];
|
|
window.history.replaceState({}, "", "/");
|
|
});
|
|
|
|
function gtagCalls() {
|
|
return (window.gtag as jest.Mock).mock.calls;
|
|
}
|
|
|
|
describe("analyticsPagePath (via the page_path sent with each event)", () => {
|
|
it.each([
|
|
["/", "/"],
|
|
["/servicos", "/servicos"],
|
|
["/cases", "/cases"],
|
|
["/curriculo", "/curriculo"],
|
|
["/privacidade", "/privacidade"],
|
|
["/ia", "/ia"],
|
|
["/cases/", "/cases"],
|
|
["/ia/?utm=x#y", "/ia"],
|
|
["/servicos/criacao-de-sites", "/servicos/[slug]"],
|
|
["/servicos/qualquer/coisa/mais", "/servicos/[slug]"],
|
|
["/cases/sev-modaintima", "/cases/[slug]"],
|
|
["/cases/ana%40example.com?x=1", "/cases/[slug]"],
|
|
["/servicos-extra", "/other"],
|
|
["/cases-extra/abc", "/other"],
|
|
["/nao-existe", "/other"],
|
|
])("path: location %s -> page_path %s", (location, expected) => {
|
|
window.history.replaceState({}, "", location);
|
|
|
|
trackSocialClick({ network: "linkedin" });
|
|
|
|
expect(gtagCalls()).toHaveLength(1);
|
|
expect(gtagCalls()[0][2]).toMatchObject({
|
|
page_path: expected,
|
|
page_location: `https://evandro.dev.br${expected}`,
|
|
page_referrer: "",
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("sendEvent: cada condição que silencia o envio", () => {
|
|
it("path: gtag exists but is not a function -> nothing sent, no conversion, no error", () => {
|
|
process.env[ADS_LABEL] = "abc123";
|
|
window.gtag = "not-a-function" as never;
|
|
|
|
expect(() =>
|
|
trackWhatsAppOpen({ service: "S", placement: "p" }),
|
|
).not.toThrow();
|
|
expect(() => trackEmailClick({ placement: "p" })).not.toThrow();
|
|
});
|
|
|
|
it("path: stored consent is an unknown value -> treated as no consent", () => {
|
|
localStorage.setItem(CONSENT_KEY, "yes");
|
|
|
|
trackSocialClick({ network: "linkedin" });
|
|
|
|
expect(readConsent()).toBeNull();
|
|
expect(gtagCalls()).toHaveLength(0);
|
|
});
|
|
|
|
it("path: storage throws -> readConsent returns null and nothing is sent", () => {
|
|
jest.spyOn(Storage.prototype, "getItem").mockImplementation(() => {
|
|
throw new Error("storage blocked");
|
|
});
|
|
|
|
expect(readConsent()).toBeNull();
|
|
trackSocialClick({ network: "linkedin" });
|
|
|
|
expect(gtagCalls()).toHaveLength(0);
|
|
});
|
|
|
|
it("path: event counted -> one gtag call carrying the static params and the route template", () => {
|
|
window.history.replaceState({}, "", "/cases/sev-modaintima?q=1#x");
|
|
|
|
trackEmailClick({ placement: "footer" });
|
|
|
|
expect(gtagCalls()).toEqual([
|
|
[
|
|
"event",
|
|
"email_click",
|
|
{
|
|
send_to: "G-QTT9QT0T3S",
|
|
placement: "footer",
|
|
page_path: "/cases/[slug]",
|
|
page_location: "https://evandro.dev.br/cases/[slug]",
|
|
page_referrer: "",
|
|
},
|
|
],
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("sendAdsConversion: etiqueta x consentimento", () => {
|
|
it.each([
|
|
["is unset", undefined],
|
|
["is an empty string", ""],
|
|
])(
|
|
"path: label %s, consent granted -> only the GA4 event goes out",
|
|
(_l, label) => {
|
|
if (label !== undefined) process.env[ADS_LABEL] = label;
|
|
|
|
trackWhatsAppOpen({ service: "Contato", placement: "floating" });
|
|
trackEmailClick({ placement: "footer" });
|
|
|
|
expect(gtagCalls().map(([, name]) => name)).toEqual([
|
|
"whatsapp_open",
|
|
"email_click",
|
|
]);
|
|
},
|
|
);
|
|
|
|
it("path: label set, consent denied -> nothing at all, not even the conversion", () => {
|
|
process.env[ADS_LABEL] = "abc123";
|
|
localStorage.setItem(CONSENT_KEY, "denied");
|
|
|
|
trackWhatsAppOpen({ service: "Contato", placement: "floating" });
|
|
trackEmailClick({ placement: "footer" });
|
|
|
|
expect(gtagCalls()).toHaveLength(0);
|
|
});
|
|
|
|
it("path: label set, consent granted -> the conversion uses the Ads id and the label", () => {
|
|
process.env[ADS_LABEL] = "xyz-789";
|
|
|
|
trackWhatsAppOpen({ service: "Contato", placement: "floating" });
|
|
|
|
expect(gtagCalls().map(([, name]) => name)).toEqual([
|
|
"whatsapp_open",
|
|
"conversion",
|
|
]);
|
|
expect(gtagCalls()[1][2]).toEqual({
|
|
send_to: "AW-18432694647/xyz-789",
|
|
});
|
|
});
|
|
|
|
it("path: the conversion call throws -> the click handler still returns normally", () => {
|
|
process.env[ADS_LABEL] = "abc123";
|
|
window.gtag = jest.fn((_command: unknown, name: unknown) => {
|
|
if (name === "conversion") throw new Error("ads blocked");
|
|
});
|
|
|
|
expect(() =>
|
|
trackWhatsAppOpen({ service: "Contato", placement: "floating" }),
|
|
).not.toThrow();
|
|
expect(gtagCalls().map(([, name]) => name)).toEqual([
|
|
"whatsapp_open",
|
|
"conversion",
|
|
]);
|
|
});
|
|
|
|
it("path: gtag disappears right after the event was counted -> the conversion is skipped silently", () => {
|
|
process.env[ADS_LABEL] = "abc123";
|
|
const gtag = jest.fn(() => {
|
|
delete window.gtag;
|
|
});
|
|
window.gtag = gtag;
|
|
|
|
expect(() => trackEmailClick({ placement: "footer" })).not.toThrow();
|
|
expect(gtag).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|