Files
wordpress-theme-aksrt/tests/widget-system/account-settings-contract.test.cjs
T

121 lines
7.9 KiB
JavaScript

'use strict';
const assert = require('node:assert/strict');
const { execFileSync } = require('node:child_process');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const root = path.resolve(__dirname, '..', '..');
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
test('account dialogs expose configured email steps, phone steps, and reference password branches', () => {
const template = read('inc/helpers/user-center-renderers/settings-account.php');
const javascript = read('assets/js/routes/user-center.js');
const css = read('assets/css/routes/user-center/settings.css');
assert.match(template, /\$email_verify_required = \$account_email !== '' && !empty\(\$user_settings\['email_set_captch'\]\)/);
assert.match(template, /data-account-step="email-old"[\s\S]*?data-account-step="email-new"[\s\S]*?data-account-step="email-success"/);
assert.match(template, /if \(\$email_verify_required\)/);
assert.match(template, /if \(\$email_verify_required\)[\s\S]*?else : \?>[\s\S]*?aksrt-settings-modal__pane/);
assert.match(template, /if \(!empty\(\$user_settings\['email_set_captch'\]\)\)/);
assert.match(template, /data-account-step="phone-old"[\s\S]*?data-account-step="phone-new"[\s\S]*?data-account-step="phone-success"/);
assert.match(template, /if \(\$account_phone !== ''\)[\s\S]*?else : \?>[\s\S]*?aksrt-settings-modal__pane/);
assert.match(template, /data-aksrt-sms-scene="verify_old_phone"/);
assert.match(template, /data-aksrt-sms-scene="bind_new_phone"/);
assert.match(template, /\$account_close_icon = function_exists\('aksrt_icon_markup'\)/);
assert.match(template, /<i class="fa far fa-envelope"/);
assert.match(template, /aksrt_render_captcha\('change_email'\)[\s\S]*?id="email-code"/);
assert.match(template, /aksrt_render_captcha\('bind_phone'\)[\s\S]*?id="bind-phone-code"/);
assert.match(template, /if \(!\$account_oauth_new\)[\s\S]*?name="passwordold"/);
assert.match(template, /if \(!\$account_oauth_new && \$can_recover_password\)/);
assert.match(template, /data-account-password-pane="recovery"/);
assert.match(javascript, /\/auth\/code/);
assert.match(javascript, /\/auth\/reset-password/);
assert.match(javascript, /className: 'aksrt-user-settings-modal modal-mini full-sm'/);
assert.doesNotMatch(template + javascript + css, /pwd-strength|pwd-send-code|email-send-code/);
assert.match(css, /\.aksrt-modal-runtime\.aksrt-user-settings-modal\.aksrt-modal-runtime--bottom \.aksrt-modal-runtime__dialog \{[\s\S]*?width: 400px/);
assert.match(css, /@media \(max-width: 640px\)[\s\S]*?width: 100%/);
assert.match(css, /modal-line-field--code \.yztx\.abs-right[\s\S]*?top: 50%[\s\S]*?right: \.7em/);
assert.doesNotMatch(css, /\.code-send-btn \{ min-width:/);
const emailConditionStart = template.indexOf('<?php if ($email_verify_required) : ?>');
const emailElse = template.indexOf('<?php else : ?>', emailConditionStart);
const emailEnd = template.indexOf('<?php endif; ?>', emailElse);
const emailSteps = template.slice(emailConditionStart, emailElse);
const emailDirect = template.slice(emailElse, emailEnd);
const phoneConditionStart = template.indexOf('<?php if ($account_phone !== \'\') : ?>');
const phoneElse = template.indexOf('<?php else : ?>', phoneConditionStart);
const phoneEnd = template.indexOf('<?php endif; ?>', phoneElse);
const phoneSteps = template.slice(phoneConditionStart, phoneElse);
const phoneDirect = template.slice(phoneElse, phoneEnd);
assert.match(emailSteps, /account-step-list/);
assert.doesNotMatch(emailDirect, /account-step-list|data-account-step=/);
assert.match(phoneSteps, /account-step-list/);
assert.doesNotMatch(phoneDirect, /account-step-list|data-account-step=/);
});
test('account PHP harness exercises email and password state transitions', () => {
const harness = path.join(root, 'tests', 'widget-system', 'account-settings-harness.php');
const output = execFileSync('php', [harness], { encoding: 'utf8' });
assert.match(output, /Account settings harness passed/);
});
test('email update keeps both proofs through validation failures and consumes them only after the write', () => {
const ajax = read('inc/modules/user-parts/account-ajax.php');
const send = ajax.slice(ajax.indexOf("add_action('wp_ajax_aksrt_send_account_email_code'"), ajax.indexOf("add_action('wp_ajax_aksrt_verify_account_email'"));
const verify = ajax.slice(ajax.indexOf("add_action('wp_ajax_aksrt_verify_account_email'"), ajax.indexOf("add_action('wp_ajax_aksrt_change_password'"));
const change = ajax.slice(ajax.indexOf("add_action('wp_ajax_aksrt_change_email'"));
assert.match(send, /\['verify_email', 'change_email'\]/);
assert.match(send, /email_exists\(\$target\)/);
assert.match(verify, /aksrt_require_captcha\(\s*'change_email'/);
assert.match(verify, /aksrt_account_set_proof\(\$user_id, 'email_old'/);
assert.match(change, /aksrt_account_email_verification_required\(\)/);
assert.match(change, /aksrt_account_consume_proof\([^;]+false\)/);
assert.match(change, /aksrt_account_consume_code\([\s\S]*?false\s*\)/);
assert.ok(change.indexOf('wp_update_user(') < change.indexOf("delete_transient(aksrt_account_proof_key"));
assert.ok(change.indexOf('wp_update_user(') < change.indexOf("delete_transient(aksrt_account_code_key"));
assert.match(change, /\$agreement\['required'\]/);
assert.doesNotMatch(ajax, /wp_ajax_aksrt_send_email_code/);
});
test('phone replacement requires old-number proof and a new-number code while reusing bind side effects', () => {
const controller = read('src/Admin/UserControllerAuthPhoneOperations.php');
const routes = read('src/Admin/UserController.php');
const binding = read('src/Auth/ServicePart1b.php');
const code = controller.slice(controller.indexOf('public function user_phone_code('), controller.indexOf('public function user_verify_phone('));
const verify = controller.slice(controller.indexOf('public function user_verify_phone('), controller.indexOf('public function user_bind_phone('));
const bind = controller.slice(controller.indexOf('public function user_bind_phone('), controller.indexOf('public function test_sms_send('));
assert.match(routes, /\/user\/phone-verify[\s\S]*?user_verify_phone/);
assert.match(code, /verify_old_phone/);
assert.match(code, /bind_new_phone/);
assert.match(verify, /auth_consume_code\('account_verify_old_phone_' \./);
assert.match(verify, /aksrt_account_phone_old_/);
assert.match(bind, /aksrt_account_phone_old_/);
assert.match(bind, /auth_consume_code\(\$code_scene, \$phone, \$code, false\)/);
assert.ok(bind.indexOf("Auth\\Service::bind_phone") < bind.indexOf("delete_transient($this->auth_code_key($code_scene, $phone))"));
assert.match(bind, /aksrt_get_auth_agreement_view/);
assert.match(bind, /Auth\\Service::bind_phone\(\$payload, true\)/);
assert.match(binding, /aksrt_user_phone_changed/);
assert.doesNotMatch(controller, /auth_consume_code\('bind_phone'/);
});
test('password change retains old-password limits and captcha; recovery uses shared auth endpoints', () => {
const ajax = read('inc/modules/user-parts/account-ajax.php');
const javascript = read('assets/js/routes/user-center.js');
const password = ajax.slice(ajax.indexOf("add_action('wp_ajax_aksrt_change_password'"), ajax.indexOf("add_action('wp_ajax_aksrt_change_email'"));
assert.match(password, /get_user_meta\(\$user_id, 'oauth_new', true\)/);
assert.match(password, /wp_check_password\(\$old/);
assert.match(password, /aksrt_account_rate_limited\(\$user_id, 'change_password'\)/);
assert.match(password, /aksrt_require_captcha\('change_password'/);
assert.match(password, /Auth\\Service::validate_password\(\$password, \$password2\)/);
assert.doesNotMatch(password, /email.*code|password.*strength/i);
assert.match(javascript, /scene: 'resetpassword', target: target, channel:/);
assert.match(javascript, /\/auth\/code/);
assert.match(javascript, /\/auth\/reset-password/);
assert.match(javascript, /data-recovery-channel/);
});