const test = require('node:test'); const assert = require('node:assert/strict'); require('../src/common'); const registerAccountRoutes = require('../src/web/routes/admin/account'); const pages = require('../src/web/pages'); const { validatePasswordStrength } = require('../src/auth'); test('required-password page shows only password controls and uses an inline warning', () => { const html = pages.renderRequiredPasswordPage({ id: 5, username: 'demo' }, { minimumLength: 12, minimumCategories: 3, requireLowercase: false, requireUppercase: false, requireNumber: false, requireSymbol: false }, 'Current password is incorrect.'); assert.match(html, /Password change required/); assert.match(html, /Please change your password before continuing\./); assert.match(html, /Current password is incorrect\./); assert.match(html, /name="current_password"/); assert.match(html, /name="new_password"/); assert.match(html, /name="confirm_password"/); assert.doesNotMatch(html, /account-profile-form|name="email"|Active sessions/); assert.doesNotMatch(html, /id="app-toast"/); }); test('account page presents Better Auth identity, credential, and session controls', () => { const html = pages.renderAccountPage({ id: 5, username: 'demo', name: 'Demo', email: 'demo@example.test', email_verified: true }, '', '/dashboard', true, [], { minimumLength: 10, minimumCategories: 3, requireLowercase: false, requireUppercase: false, requireNumber: false, requireSymbol: false }); assert.match(html, /Profile and email/); assert.match(html, /Verified/); assert.match(html, /Active sessions/); assert.match(html, /Change password/); assert.equal((html.match(/name="current_password"/g) || []).length, 2); assert.doesNotMatch(html, /Confirmation|data-account-current-password|type="hidden" name="current_password"/); }); test('two-factor account controls show setup or management only for the authenticated user', () => { const requirements = { minimumLength: 10, minimumCategories: 3 }; const render = user => pages.renderAccountPage(user, '', '/dashboard', false, [], requirements); const setupHtml = render({ id: 5, username: 'demo', twoFactorEnabled: false }); const enabledHtml = render({ id: 5, username: 'demo', twoFactorEnabled: true }); const viewAsHtml = render({ id: 8, username: 'preview', twoFactorEnabled: false, isViewingAs: true }); assert.match(setupHtml, /data-two-factor-enable/); assert.match(setupHtml, /data-two-factor-setup hidden/); assert.doesNotMatch(setupHtml, /optional/i); assert.match(setupHtml, /id="two-factor-setup-modal"/); assert.match(setupHtml, /class="card-body py-0"[\s\S]*class="list-group list-group-flush"/); assert.match(setupHtml, /list-group-item d-flex align-items-center justify-content-between gap-3 px-0/); assert.match(setupHtml, /data-two-factor-download/); assert.match(setupHtml, /aria-label="Download recovery codes"/); assert.doesNotMatch(setupHtml, /modal-body[^]*?
/); assert.doesNotMatch(setupHtml, /data-two-factor-disable/); assert.match(enabledHtml, /data-two-factor-disable/); assert.match(enabledHtml, /data-two-factor-regenerate-codes/); assert.match(enabledHtml, /id="two-factor-manage-modal"/); assert.match(enabledHtml, /aria-label="Download recovery codes"/); assert.doesNotMatch(enabledHtml, /Once enabled, two-step verification is required at password sign-in\./); assert.match(enabledHtml, /data-bs-target="#two-factor-manage-modal"/); assert.match(enabledHtml, /id="two-factor-manage-modal"/); assert.doesNotMatch(viewAsHtml, /Two-step verification|data-two-factor-enable/); }); test('passkey management is account-scoped and hidden while TOTP policy requires password sign-in', () => { const requirements = { minimumLength: 10, minimumCategories: 3 }; const render = (user, passkeys) => pages.renderAccountPage(user, '', '/dashboard', false, [], requirements, [], passkeys); const available = render({ id: 5, username: 'demo', twoFactorEnabled: false }, [{ id: 'pk-1', name: 'Laptop' }]); const protectedAccount = render({ id: 5, username: 'demo', twoFactorEnabled: true }, [{ id: 'pk-1', name: 'Laptop' }]); const viewAs = render({ id: 8, username: 'preview', isViewingAs: true }, []); assert.match(available, /data-passkey-add/); assert.match(available, /id="passkey-setup-modal"/); assert.match(available, /data-passkey-add-trigger[^>]*data-bs-toggle="modal"[^>]*data-bs-target="#passkey-setup-modal"/); assert.doesNotMatch(available, /optional/i); assert.match(available, /data-passkey-group[\s\S]*?data-passkey-list/); assert.match(available, /data-passkey-list[\s\S]*?data-passkey-delete="pk-1"/); assert.match(available, /
  • [\s\S]*Laptop/); assert.match(available, /bi-arrow-return-right[^>]*aria-hidden="true"/); assert.match(available, /Laptop/); assert.match(available, /data-passkey-delete="pk-1"/); assert.match(available, /js\/account\/passkeys\.js/); assert.doesNotMatch(protectedAccount, /data-passkey-add/); assert.match(protectedAccount, /Unavailable while authenticator verification is enabled/); assert.doesNotMatch(viewAs, /data-passkey-add|data-passkey-delete/); }); test('role-required two-factor enrollment is visible and cannot be unlinked from My account', () => { const render = twoFactorEnabled => pages.renderAccountPage({ id: 5, username: 'demo', requiresTwoFactor: true, twoFactorEnabled }, '', '/dashboard', false, []); const pending = render(false); assert.match(pending, /Required by role/); assert.match(pending, /Set up an authenticator to continue/); assert.match(pending, /data-two-factor-enable/); const enabled = render(true); assert.doesNotMatch(enabled, /data-two-factor-disable/); assert.match(enabled, /data-two-factor-regenerate-codes/); assert.match(enabled, />Manage<\/button>/); }); test('account email status and verification action stay inline with the address field', () => { const requirements = { minimumLength: 10, minimumCategories: 3, requireLowercase: false, requireUppercase: false, requireNumber: false, requireSymbol: false }; const render = user => pages.renderAccountPage(user, '', '/dashboard', false, [], requirements); const verifiedHtml = render({ username: 'demo', name: 'Demo', email: 'demo@example.test', email_verified: true }); assert.match(verifiedHtml, /