const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const test = require('node:test'); const AuthPages = require('../public/js/auth-pages.js'); test('password login includes a verification token only when the PC strategy returns one', () => { assert.deepEqual( AuthPages.buildLoginBody({ phone: '13800000000', password: 'plain-password', validToken: 'captcha-ticket' }, (value) => 'md5-' + value), { phone: '13800000000', password: 'md5-plain-password', validToken: 'captcha-ticket' } ); assert.deepEqual( AuthPages.buildLoginBody({ phone: '13800000000', password: 'plain-password' }, (value) => 'md5-' + value), { phone: '13800000000', password: 'md5-plain-password' } ); }); test('registration body carries the SMS code but never sends confirmation or captcha fields', () => { assert.deepEqual( AuthPages.buildRegisterBody({ phone: '13800000000', smsCode: '123456', nickName: '小李', password: 'plain-password', confirmPassword: 'plain-password', validToken: 'captcha-ticket' }, (value) => 'md5-' + value), { phone: '13800000000', smsCode: '123456', nickName: '小李', password: 'md5-plain-password' } ); }); test('password reset body carries only the reset DTO fields', () => { assert.deepEqual( AuthPages.buildPasswordResetBody({ phone: '13800000000', smsCode: '123456', newPassword: 'plain-password', validToken: 'captcha-ticket' }, (value) => 'md5-' + value), { phone: '13800000000', smsCode: '123456', newPassword: 'md5-plain-password' } ); }); test('authentication forms use the PC verification operation codes defined by Apifox', () => { assert.equal(AuthPages.getCaptchaOperation('login'), 'password-login'); assert.equal(AuthPages.getCaptchaOperation('sms-login'), 'sms-login'); assert.equal(AuthPages.getCaptchaOperation('register'), 'register'); assert.equal(AuthPages.getCaptchaOperation('password-reset'), 'forgot-password'); }); test('registration validates SMS code and password confirmation before submission', () => { const base = { phone: '13800000000', smsCode: '1234', password: 'password', confirmPassword: 'password' }; assert.equal(AuthPages.validateAuthValues('register', base), ''); assert.equal(AuthPages.validateAuthValues('register', { ...base, smsCode: '' }), '请填写短信验证码'); assert.equal(AuthPages.validateAuthValues('register', { ...base, confirmPassword: 'different' }), '两次输入的密码不一致'); }); test('authentication forms reject SMS codes that are not exactly four digits', () => { const smsLogin = { phone: '13800000000', smsCode: '123456' }; const register = { phone: '13800000000', smsCode: '123456', password: 'password', confirmPassword: 'password' }; assert.equal(AuthPages.validateAuthValues('sms-login', smsLogin), '请输入正确的短信验证码'); assert.equal(AuthPages.validateAuthValues('register', register), '请输入正确的短信验证码'); }); test('registration page exposes the documented SMS and confirmation inputs', () => { const source = fs.readFileSync(path.join(__dirname, '..', 'register.html'), 'utf8'); assert.match(source, /name="smsCode"/); assert.match(source, /data-api-send-code/); assert.match(source, /name="confirmPassword"/); }); test('password login page retains a verification token field for the PC captcha strategy', () => { const source = fs.readFileSync(path.join(__dirname, '..', 'login.html'), 'utf8'); assert.match(source, /id="login-password-form"[\s\S]*name="validToken"/); }); test('authentication captcha ticket is removed from the form when consumed', () => { const field = { value: 'one-time-ticket' }; const form = { querySelector(selector) { return selector === 'input[name="validToken"]' ? field : null; } }; assert.equal(AuthPages.takeCaptchaToken(form), 'one-time-ticket'); assert.equal(field.value, ''); assert.equal(AuthPages.takeCaptchaToken(form), ''); }); test('successful login enters one real genealogy directly and otherwise opens the chooser', async () => { assert.equal( AuthPages.getPostLoginUrl([{ genealogyId: '2062179707935264769' }]), 'profile-family-home.html?genealogyId=2062179707935264769' ); assert.equal(AuthPages.getPostLoginUrl([]), 'profile-families.html'); assert.equal( AuthPages.getPostLoginUrl([{ genealogyId: '1' }, { genealogyId: '2' }]), 'profile-families.html' ); assert.equal( AuthPages.getPostLoginUrl([{ genealogyId: Number.MAX_SAFE_INTEGER + 1 }]), 'profile-families.html' ); assert.equal( await AuthPages.resolvePostLoginUrl({ genealogiesMine: async () => { throw new Error('temporary failure'); } }, 'login'), 'profile.html' ); });