fix: streamline subscription connection dialogs and actions

This commit is contained in:
amosblomqvist
2026-10-25 01:34:46 +03:00
parent d6e317c6fe
commit 55a6501a09
5 changed files with 163 additions and 70 deletions
+34 -7
View File
@@ -88,6 +88,9 @@ test("disconnected row uses shared Connect action; initial default is ON; cancel
assert(!document.body.textContent?.includes(CHATGPT_NOTICE)); const trigger = byTest("chatgpt-login");
assert.match(trigger.className, /cursor-pointer/); assert.match(trigger.className, /hover:bg-accent/); assert.match(trigger.className, /focus-visible:ring/);
await click(trigger); assert.equal(f.calls.login.length, 0); assert(document.body.textContent?.includes(CHATGPT_NOTICE));
assert.equal(byTest("chatgpt-account-dialog").querySelector("h2")?.textContent, "ChatGPT");
assert.equal(byTest("chatgpt-proceed").textContent, "Continue with ChatGPT"); assert(byTest("chatgpt-proceed").querySelector('img[src="/siwc/chatgpt-logo-white.svg"]'));
assert(!button("Continue", byTest("chatgpt-account-dialog")), "ChatGPT remains one step, with disclosure and toggle together");
assert.equal(toggle().getAttribute("aria-checked"), "true");
assert.equal(toggle().getAttribute("aria-label"), "Allow background use for all ChatGPT accounts");
assert(byTest("chatgpt-global-background").textContent?.includes("Background work counts toward your ChatGPT plan usage."));
@@ -180,23 +183,33 @@ test("email-only connected selector and Manage remain responsive; account select
assert.deepEqual([...document.querySelectorAll('select[aria-label="ChatGPT account"] option')].map((o) => o.textContent), ["learner@example.invalid", "second@example.invalid", "ChatGPT account"]);
assert.doesNotMatch(byTest("chatgpt-connection").textContent!, /private-registration|private-issued| · active|signed-out/);
assert.match(byTest("chatgpt-connection").firstElementChild!.className, /items-center/);
const row = byTest("chatgpt-connection").firstElementChild!;
assert.equal(row.children[0]!.textContent, "ChatGPT"); assert.equal(row.children[1]!.tagName, "SELECT"); assert.equal(row.children[2]!.textContent, "Manage…");
assert.match(row.children[2]!.className, /text-muted-foreground/); assert(!row.textContent?.includes("Connected"));
await selectAccount("ChatGPT account", "second"); assert.deepEqual(f.calls.select, ["second"]); assert.equal(f.status.allowBackgroundUse, false);
await click(button("Manage…")); assert(byTest("chatgpt-managed-identity").textContent?.includes("second@example.invalid"));
assert.deepEqual([...byTest("chatgpt-account-footer").querySelectorAll("button")].map((b) => b.textContent), ["Add accounts", "Close"]);
assert.deepEqual([...byTest("chatgpt-account-footer").querySelectorAll("button")].map((b) => b.textContent), ["Disconnect", "Close"]);
const dialog = byTest("chatgpt-account-dialog").innerHTML;
assert(dialog.indexOf('data-testid="chatgpt-managed-identity"') < dialog.indexOf('data-testid="chatgpt-account-actions"'));
assert(dialog.indexOf('data-testid="chatgpt-account-actions"') < dialog.indexOf('data-testid="chatgpt-global-background"'));
assert(dialog.indexOf('data-testid="chatgpt-global-background"') < dialog.indexOf('data-testid="chatgpt-managed-identity"'));
assert(!byTest("chatgpt-account-dialog").querySelector('[data-testid="chatgpt-privacy-notice"]'));
assert.equal(byTest("chatgpt-account-dialog").querySelector("h2")?.textContent, "ChatGPT");
assert(byTest("chatgpt-account-dialog").querySelector('a[href="https://openai.com/policies/privacy-policy/"]'));
assert(byTest("chatgpt-account-dialog").querySelector('a[href="https://chatgpt.com/settings/usage"]'));
await click(toggle()); assert.deepEqual(f.calls.consent, [true]); assert.equal(toggle().getAttribute("aria-checked"), "true");
await click(button("Close")); await selectAccount("ChatGPT account", "private-registration-id"); assert.equal(f.status.allowBackgroundUse, true);
assert.equal(f.calls.logout, 0);
});
test("Disconnect account stays under identity, outside the nondestructive Manage footer; confirmation Cancel never forgets", async () => {
test("Disconnect is the quiet red left-footer action below identity; confirmation Cancel never forgets", async () => {
assert.deepEqual(CHATGPT_DISCONNECT_COPY, { title: "Disconnect account?", body: "This removes the saved account and its credentials from this device." });
const f = fixture(connected()); await mount(); await click(button("Manage…"));
assert.equal(byTest("chatgpt-account-actions").textContent, "Disconnect account"); assert.match(byTest("chatgpt-disconnect").className, /text-red/);
assert(!byTest("chatgpt-account-footer").contains(byTest("chatgpt-disconnect")));
assert.equal(byTest("chatgpt-account-actions").textContent, "Disconnect"); assert.match(byTest("chatgpt-disconnect").className, /text-red/);
assert.equal(byTest("chatgpt-account-footer").firstElementChild, byTest("chatgpt-account-actions"));
assert.equal(byTest("chatgpt-account-footer").lastElementChild?.lastElementChild, button("Close"));
assert(!byTest("chatgpt-account-footer").contains(byTest("chatgpt-add-accounts")));
await click(byTest("chatgpt-disconnect")); assert.equal(f.calls.disconnect.length, 0);
assert.equal(byTest("chatgpt-account-dialog").querySelector("h2")?.textContent, "ChatGPT");
assert(byTest("chatgpt-disconnect-confirmation").textContent?.includes(CHATGPT_DISCONNECT_COPY.title));
assert(byTest("chatgpt-disconnect-confirmation").textContent?.includes(CHATGPT_DISCONNECT_COPY.body));
assert(!document.body.textContent?.includes("delete your OpenAI"));
@@ -208,8 +221,9 @@ for (const count of [1, 3]) test(`Add accounts reuses the actual add flow with $
const initial = connected(count === 3);
initial.accounts = [account(), ...Array.from({ length: count - 1 }, (_, i) => account(`saved-${i}`, `saved-${i}@example.invalid`))];
const f = fixture(initial); const before = structuredClone(initial); await mount(); await click(button("Manage…"));
assert.deepEqual([...byTest("chatgpt-account-footer").querySelectorAll("button")].map((b) => b.textContent), ["Add accounts", "Close"]);
assert.equal(byTest("chatgpt-add-accounts").getAttribute("data-variant"), "outline");
assert.deepEqual([...byTest("chatgpt-account-footer").querySelectorAll("button")].map((b) => b.textContent), ["Disconnect", "Close"]);
assert.equal(byTest("chatgpt-add-accounts").getAttribute("data-variant"), "ghost");
assert.equal(byTest("chatgpt-add-accounts").parentElement, byTest("chatgpt-account-strip"));
assert.match(byTest("chatgpt-add-accounts").className, /cursor-pointer/);
await click(byTest("chatgpt-add-accounts"));
assert(byTest("chatgpt-privacy-notice").textContent?.includes(CHATGPT_NOTICE)); assert(byTest("chatgpt-proceed"));
@@ -226,6 +240,19 @@ for (const count of [1, 3]) test(`Add accounts reuses the actual add flow with $
assert.deepEqual(f.status, before); assert.equal(f.calls.cancel, 1); assert.deepEqual(f.calls.consent, []); assert.deepEqual(f.calls.disconnect, []);
});
test("ChatGPT Sign in again is reserved for a saved disconnected account and keeps its existing registration flow", async () => {
const f = fixture(connected(false)); await mount(); await click(button("Manage…"));
assert(!button("Sign in again")); await selectAccount("Manage ChatGPT account", "signed-out-record"); assert(button("Sign in again"));
await click(button("Sign in again")); assert.equal(byTest("chatgpt-account-dialog").querySelector("h2")?.textContent, "ChatGPT");
assert(byTest("chatgpt-privacy-notice")); assert(toggle()); assert.equal(toggle().getAttribute("aria-checked"), "false");
assert.equal(document.querySelector<HTMLSelectElement>('select[aria-label="Saved ChatGPT account"]')?.value, "signed-out-record"); assert.deepEqual(f.calls.login, []);
await click(byTest("chatgpt-proceed")); assert.deepEqual(f.calls.login, ["signed-out-record"]); assert.equal(f.status.activeId, "private-registration-id");
await click(button("Cancel", byTest("chatgpt-account-dialog"))); assert.equal(f.calls.cancel, 1); assert.deepEqual(f.calls.disconnect, []); assert.deepEqual(f.calls.consent, []);
});
test("connected setup row retains the email dropdown before muted Manage", async () => {
fixture(connected()); await mount(<ChatGPTConnection setup />);
const row = byTest("chatgpt-connection").firstElementChild!; assert.equal(row.children[1]!.tagName, "SELECT"); assert.equal(row.children[2]!.textContent, "Manage…"); assert.match(row.children[2]!.className, /text-muted-foreground/);
});
test("Add accounts reuses the existing dialog's initial focus for the pre-auth transition", async () => {
const previous = HTMLElement.prototype.focus; const focused: HTMLElement[] = [];
HTMLElement.prototype.focus = function () { focused.push(this); };