fix: Revive local telemetry consent banner (#16183)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Rohit Malhotra
2026-07-29 17:58:08 -04:00
committed by GitHub
co-authored by openhands
parent a2fef1d73f
commit 8dfa1d510c
12 changed files with 471 additions and 26 deletions
@@ -127,6 +127,9 @@ export async function configureLiveAgentServer(
confirmation_mode: false,
max_iterations: 6,
},
misc_settings_diff: {
app_preferences: { user_consents_to_analytics: false },
},
},
});
expect(
@@ -68,6 +68,16 @@ test.describe("auth mode: fresh install with runtime-injected key", () => {
window.localStorage.setItem("openhands-telemetry-consent", "denied");
});
const consentResp = await request.patch(`${BACKEND_URL}/api/settings`, {
headers: { "X-Session-API-Key": SESSION_API_KEY },
data: {
misc_settings_diff: {
app_preferences: { user_consents_to_analytics: false },
},
},
});
expect(consentResp.ok()).toBe(true);
await routeSessionApiKey(page);
await page.goto("/", { waitUntil: "domcontentloaded" });
@@ -151,6 +151,26 @@ async function suppressAnalytics(page: Page) {
});
}
async function seedBackendAnalyticsConsent(backendUrl: string, apiKey: string) {
const response = await fetch(`${backendUrl}/api/settings`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
"X-Session-API-Key": apiKey,
},
body: JSON.stringify({
misc_settings_diff: {
app_preferences: { user_consents_to_analytics: false },
},
}),
});
expect(
response.ok,
`failed to seed analytics consent on ${backendUrl}: ${response.status}`,
).toBe(true);
}
async function addBackendViaOnboarding(
page: Page,
opts: { name: string; host: string; apiKey: string },
@@ -251,6 +271,7 @@ test.describe("cross-connect: frontend-only → backend-only", () => {
beStatus,
`Backend-only never ready.\nOutput: ${beOutput.get().slice(-800)}`,
).not.toBeNull();
await seedBackendAnalyticsConsent(beUrl, beEnv.sessionKey);
// ── 4. Open browser to the frontend-only instance ─────────────────
await suppressAnalytics(page);
@@ -297,12 +318,12 @@ test.describe("cross-connect: frontend-only → backend-only", () => {
).toBe(true);
// The manage-backends modal and auth screen must NOT be showing.
await expect(
page.getByTestId("manage-backends-modal"),
).not.toBeVisible({ timeout: 2_000 });
await expect(
page.getByTestId("api-key-entry-screen"),
).not.toBeVisible({ timeout: 2_000 });
await expect(page.getByTestId("manage-backends-modal")).not.toBeVisible({
timeout: 2_000,
});
await expect(page.getByTestId("api-key-entry-screen")).not.toBeVisible({
timeout: 2_000,
});
});
});
@@ -391,6 +412,10 @@ test.describe("cross-connect: frontend-only → multiple backends", () => {
]);
expect(infoA).toHaveProperty("version");
expect(infoB).toHaveProperty("version");
await Promise.all([
seedBackendAnalyticsConsent(beUrlA, beEnvA.sessionKey),
seedBackendAnalyticsConsent(beUrlB, beEnvB.sessionKey),
]);
// ── 4. Open browser to the frontend-only instance ─────────────────
await suppressAnalytics(page);
+26 -4
View File
@@ -70,7 +70,31 @@ export const SESSION_API_KEY = (() => {
* localStorage no longer needs this seeding to reach onboarding. See
* `auth mode: fresh install with runtime-injected key` in
* `mock-llm-auth-modes.spec.ts` for the test that covers that path. */
export async function seedBackendAnalyticsConsent(
backendUrl = BACKEND_URL,
apiKey = SESSION_API_KEY,
) {
const response = await fetch(`${backendUrl}/api/settings`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
"X-Session-API-Key": apiKey,
},
body: JSON.stringify({
misc_settings_diff: {
app_preferences: { user_consents_to_analytics: false },
},
}),
});
expect(
response.ok,
`failed to seed analytics consent on ${backendUrl}: ${response.status}`,
).toBe(true);
}
export async function seedLocalStorage(page: Page) {
await seedBackendAnalyticsConsent();
await page.addInitScript(
({ apiKey }) => {
window.localStorage.setItem("analytics-consent", "false");
@@ -141,7 +165,7 @@ export async function dismissAnalyticsModal(page: Page) {
// pointer events (e.g. the delete-profile confirmation left by a prior
// test).
try {
const form = page.getByTestId("user-capture-consent-form");
const form = page.getByTestId("telemetry-consent-form");
await form.waitFor({ state: "visible", timeout: 5_000 });
await form.getByRole("button", { name: "Confirm preferences" }).click();
// Wait for the modal to fully close so the backdrop no longer
@@ -892,9 +916,7 @@ export async function setChatInput(
// dismissAnalyticsModal's give-up window to paint the composer on a
// loaded CI runner, and the querySelector below would throw. Wait for
// the input the way a locator action would before setting text.
await page
.getByTestId(testId)
.waitFor({ state: "visible", timeout: 30_000 });
await page.getByTestId(testId).waitFor({ state: "visible", timeout: 30_000 });
await page.evaluate(
({ tid, inputText }) => {
const el = document.querySelector(`[data-testid="${tid}"]`);