mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 06:52:04 +00:00
fix(onboarding): stop the Withings connect dead-end and disclose BYO setup
The onboarding "Connect Withings" tap opened /api/withings/connect in a new tab, which returned a raw JSON 400 for every brand-new account (Withings credentials are per-user BYO with no env fallback, and the wizard never collects them). WHOOP, Fitbit, Polar, Oura, and Strava shared the identical raw-JSON-on-no-creds pattern on their own /connect routes, while every other failure branch already redirected with a toast reason. All six now redirect to /settings/integrations?<provider>=error&reason=nocreds instead, and the wizard's Withings card checks the integrations-status envelope first: unless credentials are already configured, the CTA reads "Set up in Settings" and links straight to the credentials form rather than launching a doomed OAuth handshake. Also surfaces the BYO-developer-app requirement, which was invisible until a user was deep inside a Settings card. The source-picker step now says up front that device sources need a free developer app on the user's own account; each of the six provider cards gets a compact 3-step guide with the exact callback URL to register, shown until the user has saved their own credentials.
This commit is contained in:
1 parent
4f7420905a
commit
fb73c97125
27 files changed
+744
-73
No files matched your search
@@ -148,3 +148,33 @@ describe("OAuthProviderCard — per-user BYO credentials form (v1.17.1)", () =>
|
||||
expect(html).toContain("Saved — enter new to replace");
|
||||
});
|
||||
});
|
||||
|
||||
describe("OAuthProviderCard — redirect-URI mini-guide (v1.29.x, UX audit H2)", () => {
|
||||
it("shows the callback URL guide before the user has BYO credentials", () => {
|
||||
process.env.NEXT_PUBLIC_APP_URL = "https://app.example";
|
||||
statusPayload = {
|
||||
connected: false,
|
||||
configured: false,
|
||||
available: true,
|
||||
hasOwnCredentials: false,
|
||||
};
|
||||
const html = render({ credentials: true });
|
||||
expect(html).toContain('data-testid="polar-redirect-guide"');
|
||||
expect(html).toContain('data-testid="polar-redirect-uri"');
|
||||
expect(html).toContain("https://app.example/api/polar/callback");
|
||||
});
|
||||
|
||||
it("hides the guide once the user has stored their own credentials", () => {
|
||||
statusPayload = {
|
||||
connected: true,
|
||||
configured: true,
|
||||
available: true,
|
||||
hasOwnCredentials: true,
|
||||
state: "connected",
|
||||
lastSuccessAt: null,
|
||||
lastError: null,
|
||||
};
|
||||
const html = render({ credentials: true });
|
||||
expect(html).not.toContain('data-testid="polar-redirect-guide"');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* v1.29.x — UX audit H2: the WHOOP card shows a compact callback-URL guide
|
||||
* while the account has no BYO credentials saved, and steps out of the way
|
||||
* once configured. Mirrors the OAuthProviderCard coverage for Polar/Oura.
|
||||
*/
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
|
||||
vi.mock("@tanstack/react-query", () => ({
|
||||
useMutation: () => ({ mutate: vi.fn(), isPending: false }),
|
||||
useQueryClient: () => ({ invalidateQueries: vi.fn() }),
|
||||
}));
|
||||
|
||||
import { I18nProvider } from "@/lib/i18n/context";
|
||||
import { WhoopCard } from "../whoop-card";
|
||||
import type { IntegrationStatusViewModel } from "../shared";
|
||||
|
||||
function render(viewModel?: Partial<IntegrationStatusViewModel>) {
|
||||
return renderToStaticMarkup(
|
||||
<I18nProvider initialLocale="en">
|
||||
<WhoopCard
|
||||
viewModel={viewModel as IntegrationStatusViewModel | undefined}
|
||||
/>
|
||||
</I18nProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
describe("<WhoopCard> redirect-URI mini-guide", () => {
|
||||
it("shows the callback URL guide before credentials are configured", () => {
|
||||
process.env.NEXT_PUBLIC_APP_URL = "https://app.example";
|
||||
const html = render({ connected: false, configured: false });
|
||||
expect(html).toContain('data-testid="whoop-redirect-guide"');
|
||||
expect(html).toContain('data-testid="whoop-redirect-uri"');
|
||||
expect(html).toContain("https://app.example/api/whoop/callback");
|
||||
});
|
||||
|
||||
it("hides the guide once credentials are configured", () => {
|
||||
const html = render({ connected: false, configured: true });
|
||||
expect(html).not.toContain('data-testid="whoop-redirect-guide"');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user