test: add release v2 browser QA campaign

This commit is contained in:
ajnart committed 2026-09-01 01:03:27 +02:00
1 parent f57a660088
commit ec95d18245
83 files changed
+20922 -19

No files matched your search

+11
View File
@@ -26,10 +26,21 @@ const getDevelopmentServiceAliases = () => {
};
};
const getDistDir = () => {
const qaDistDir = process.env.HOMARR_QA_NEXT_DIST_DIR;
if (!qaDistDir) return ".next";
if (!/^\.next-qa\/slot-[1-3]$/u.test(qaDistDir)) {
throw new Error("HOMARR_QA_NEXT_DIST_DIR must identify .next-qa/slot-1, slot-2, or slot-3");
}
return qaDistDir;
};
const nextConfig: NextConfig = {
// Next previews otherwise create agent instruction files in the application
// directory during development.
agentRules: false,
// Release-v2 QA runs multiple isolated development servers from one worktree.
distDir: getDistDir(),
env: {
HOMARR_VERSION: process.env.HOMARR_VERSION ?? "unknown",
},
@@ -0,0 +1,77 @@
// @vitest-environment jsdom
import { act, StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { afterEach, describe, expect, it, vi } from "vitest";
import { Analytics } from "./analytics";
const postHogMock = vi.hoisted(() => {
let resolveImport: (() => void) | undefined;
const importGate = new Promise<void>((resolve) => {
resolveImport = resolve;
});
return {
importGate,
init: vi.fn(),
releaseImport: () => resolveImport?.(),
};
});
vi.mock("posthog-js", async () => {
await postHogMock.importGate;
return { default: { init: postHogMock.init } };
});
const host = document.createElement("div");
const root = createRoot(host);
afterEach(() => {
act(() => root.unmount());
});
describe("Analytics", () => {
it("deduplicates Strict Mode initialization and retries after a failure", async () => {
const initializationError = new Error("initialization failed");
const consoleError = vi.spyOn(console, "error").mockImplementation(() => undefined);
postHogMock.init.mockImplementationOnce(() => {
throw initializationError;
});
await act(async () => {
root.render(
<StrictMode>
<Analytics enabled />
</StrictMode>,
);
});
postHogMock.releaseImport();
await vi.waitFor(() => {
expect(postHogMock.init).toHaveBeenCalledOnce();
expect(consoleError).toHaveBeenCalledWith("PostHog initialization failed", initializationError);
});
await act(async () => {
root.render(
<StrictMode>
<Analytics enabled={false} />
</StrictMode>,
);
});
await act(async () => {
root.render(
<StrictMode>
<Analytics enabled />
</StrictMode>,
);
});
await vi.waitFor(() => {
expect(postHogMock.init).toHaveBeenCalledTimes(2);
});
expect(consoleError).toHaveBeenCalledOnce();
});
});
@@ -5,19 +5,36 @@ import { useEffect } from "react";
const POSTHOG_KEY = "phc_vYBmGWNbRshvfeC7EHfeSmUm2pD2Neg5nGqzJuGvS8Hs";
const POSTHOG_HOST = "https://hog.homarr.dev";
let postHogInitialization: Promise<void> | undefined;
const initializePostHog = () => {
if (postHogInitialization) return;
const initializationAttempt = import("posthog-js").then(({ default: posthog }) => {
posthog.init(POSTHOG_KEY, {
api_host: POSTHOG_HOST,
capture_pageview: false,
capture_pageleave: false,
autocapture: false,
persistence: "memory",
});
});
postHogInitialization = initializationAttempt;
void initializationAttempt.catch((error: unknown) => {
if (postHogInitialization === initializationAttempt) {
postHogInitialization = undefined;
}
console.error("PostHog initialization failed", error);
});
};
export const Analytics = ({ enabled }: { enabled: boolean }) => {
useEffect(() => {
if (!enabled) return;
void import("posthog-js").then(({ default: posthog }) => {
posthog.init(POSTHOG_KEY, {
api_host: POSTHOG_HOST,
capture_pageview: false,
capture_pageleave: false,
autocapture: false,
persistence: "memory",
});
});
initializePostHog();
}, [enabled]);
return null;