SSO settings: show a verification outcome once, not twice

First real-browser pass over this feature. Chrome via puppeteer-core, driving the
actual settings page: login, the SSO card, and a real click on Verify.

The click path works — the loadSso fix holds, no ReferenceError, and a failure shows
the specific DNS answer ("no _screentinker-verify record found ... DNS can take a few
minutes") rather than the generic catch-all. But the outcome was rendered TWICE: the
server persists last_error on the row and the template drew it, while the click handler
wrote the same sentence into a second element underneath. Anyone retrying a failed
verification saw the identical line twice, in two different colours.

One element now owns the outcome, and the handler replaces its text. Also colours the
in-flight "Checking DNS…" as muted rather than leaving it red.

Found by looking at a screenshot. Parsing, VM rendering and mutation testing all passed
over it — none of them draws anything.

18/18 browser checks, 1598 unit tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bvjey4FNam49MN7ybjcq6A
This commit is contained in:
ScreenTinker 2026-08-10 21:25:35 -05:00
parent c91b96ab91
commit 481a44c15a

View file

@ -743,8 +743,12 @@ export async function render(container) {
${d.verified ? '' : `
<div style="margin-top:6px;color:var(--text-muted)">${esc(t('sso.dns_instructions'))}</div>
<code style="display:block;word-break:break-all;padding:6px;background:var(--bg-secondary);border-radius:4px;margin-top:4px">${esc(d.record_name)} TXT ${esc(d.txt_value)}</code>
${d.last_error ? `<div style="margin-top:4px;color:var(--danger,#b91c1c)">${esc(d.last_error)}</div>` : ''}`}
<div id="ssoVerify-${esc(p.id)}-${di}" style="margin-top:4px"></div>
`}
<!-- ONE place for the outcome. The last failure is persisted server-side and was
rendered here, while the click handler wrote the live result into a second
element below it so retrying showed the identical sentence twice, in two
different colours. The handler replaces this element's text instead. -->
<div id="ssoVerify-${esc(p.id)}-${di}" style="margin-top:4px;color:var(--danger,#b91c1c)">${d.verified ? '' : esc(d.last_error || '')}</div>
</div>`).join('')}
</div>` : ''}
@ -797,7 +801,7 @@ export async function render(container) {
// `a-b-test`, and getElementById would put one domain's answer in the other's box.
const out = document.getElementById(`ssoVerify-${id}-${btn.dataset.di}`);
btn.disabled = true;
if (out) out.textContent = t('sso.verifying');
if (out) { out.style.color = 'var(--text-muted)'; out.textContent = t('sso.verifying'); }
try {
const res = await fetch(`/api/organizations/${orgId}/sso/${id}/domains/${encodeURIComponent(domain)}/verify`, {
method: 'POST',
@ -816,9 +820,9 @@ export async function render(container) {
await loadSso();
return;
}
if (out) out.textContent = body.error || t('sso.verify_failed');
if (out) { out.style.color = 'var(--danger,#b91c1c)'; out.textContent = body.error || t('sso.verify_failed'); }
} catch {
if (out) out.textContent = t('sso.verify_failed');
if (out) { out.style.color = 'var(--danger,#b91c1c)'; out.textContent = t('sso.verify_failed'); }
} finally {
btn.disabled = false;
}