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).
104 lines
3.4 KiB
TypeScript
104 lines
3.4 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import ts from "typescript";
|
|
|
|
/**
|
|
* Caminhos de lib/analytics.ts quando não existe `window` (renderização no servidor): nenhum rastreio,
|
|
* nenhuma conversão e nenhum erro. O setup do projeto exige o jsdom e o `window` dele não pode ser
|
|
* escondido, então o próprio fonte é transpilado e executado com `window` e `localStorage` indefinidos.
|
|
*/
|
|
|
|
type AnalyticsModule = typeof import("./analytics");
|
|
|
|
const ADS_LABEL = "NEXT_PUBLIC_GOOGLE_ADS_CONVERSION_LABEL";
|
|
|
|
function loadWithoutWindow(storage?: Storage): AnalyticsModule {
|
|
const source = readFileSync(join(process.cwd(), "lib/analytics.ts"), "utf8");
|
|
const { outputText } = ts.transpileModule(source, {
|
|
compilerOptions: {
|
|
module: ts.ModuleKind.CommonJS,
|
|
target: ts.ScriptTarget.ES2020,
|
|
},
|
|
});
|
|
const loaded = { exports: {} as AnalyticsModule };
|
|
new Function("module", "exports", "window", "localStorage", outputText)(
|
|
loaded,
|
|
loaded.exports,
|
|
undefined,
|
|
storage,
|
|
);
|
|
return loaded.exports;
|
|
}
|
|
|
|
let gtag: jest.Mock;
|
|
|
|
beforeEach(() => {
|
|
jest.replaceProperty(process.env, "NODE_ENV", "production");
|
|
process.env[ADS_LABEL] = "abc123";
|
|
localStorage.setItem("evandro.analytics-consent.v1", "granted");
|
|
gtag = jest.fn();
|
|
window.gtag = gtag;
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.restoreAllMocks();
|
|
localStorage.clear();
|
|
delete window.gtag;
|
|
delete process.env[ADS_LABEL];
|
|
});
|
|
|
|
describe("analytics without a window", () => {
|
|
it("path: every tracker is silent and never throws, even with granted consent and a conversion label", () => {
|
|
// o consentimento está "granted" no storage real: só a ausência de `window` impede o envio
|
|
const analytics = loadWithoutWindow(localStorage);
|
|
|
|
expect(() => {
|
|
analytics.trackWhatsAppOpen({
|
|
service: "Contato",
|
|
placement: "floating",
|
|
});
|
|
analytics.trackEmailClick({ placement: "footer" });
|
|
analytics.trackSocialClick({ network: "linkedin" });
|
|
analytics.trackCvDownload({ placement: "curriculo" });
|
|
}).not.toThrow();
|
|
expect(gtag).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("path: readConsent finds no storage -> null", () => {
|
|
expect(loadWithoutWindow().readConsent()).toBeNull();
|
|
});
|
|
|
|
it("path: openCookiePreferences has no window to notify -> returns without error", () => {
|
|
const listener = jest.fn();
|
|
window.addEventListener("evandro:open-cookie-preferences", listener);
|
|
|
|
expect(() => loadWithoutWindow().openCookiePreferences()).not.toThrow();
|
|
|
|
window.removeEventListener("evandro:open-cookie-preferences", listener);
|
|
expect(listener).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("control: the same source with a window does dispatch the reopen event", () => {
|
|
const listener = jest.fn();
|
|
window.addEventListener("evandro:open-cookie-preferences", listener);
|
|
const source = readFileSync(
|
|
join(process.cwd(), "lib/analytics.ts"),
|
|
"utf8",
|
|
);
|
|
const { outputText } = ts.transpileModule(source, {
|
|
compilerOptions: { module: ts.ModuleKind.CommonJS },
|
|
});
|
|
const loaded = { exports: {} as AnalyticsModule };
|
|
new Function("module", "exports", "window", outputText)(
|
|
loaded,
|
|
loaded.exports,
|
|
window,
|
|
);
|
|
|
|
loaded.exports.openCookiePreferences();
|
|
|
|
window.removeEventListener("evandro:open-cookie-preferences", listener);
|
|
expect(listener).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|