feat: 更新手机号输入框逻辑,确保手动输入值在执行步骤前被保存,避免被旧状态覆盖
This commit is contained in:
@@ -63,7 +63,7 @@
|
|||||||
|| ''
|
|| ''
|
||||||
).trim();
|
).trim();
|
||||||
if (!email && !phoneNumber) {
|
if (!email && !phoneNumber) {
|
||||||
throw new Error('缺少登录账号,请先完成步骤 2 和步骤 3。');
|
throw new Error('缺少登录账号:请先完成步骤 2,或在侧栏“注册邮箱/注册手机号”中手动填写账号后再执行当前步骤。');
|
||||||
}
|
}
|
||||||
|
|
||||||
let attempt = 0;
|
let attempt = 0;
|
||||||
|
|||||||
+120
-18
@@ -1076,6 +1076,9 @@ let settingsDirty = false;
|
|||||||
let settingsSaveInFlight = false;
|
let settingsSaveInFlight = false;
|
||||||
let settingsAutoSaveTimer = null;
|
let settingsAutoSaveTimer = null;
|
||||||
let settingsSaveRevision = 0;
|
let settingsSaveRevision = 0;
|
||||||
|
let signupPhoneInputDirty = false;
|
||||||
|
let signupPhoneInputFocused = false;
|
||||||
|
let signupPhoneInputPersistPromise = null;
|
||||||
let cloudflareDomainEditMode = false;
|
let cloudflareDomainEditMode = false;
|
||||||
let cloudflareTempEmailDomainEditMode = false;
|
let cloudflareTempEmailDomainEditMode = false;
|
||||||
let modalChoiceResolver = null;
|
let modalChoiceResolver = null;
|
||||||
@@ -7012,10 +7015,29 @@ function getRuntimeSignupPhoneValue(state = latestState) {
|
|||||||
).trim();
|
).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSignupPhoneInputValue() {
|
||||||
|
return typeof inputSignupPhone !== 'undefined' && inputSignupPhone
|
||||||
|
? String(inputSignupPhone.value || '').trim()
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldPreserveSignupPhoneInputValue(stateSignupPhone = '') {
|
||||||
|
if (typeof inputSignupPhone === 'undefined' || !inputSignupPhone || !signupPhoneInputDirty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getSignupPhoneInputValue() === String(stateSignupPhone || '').trim()) {
|
||||||
|
signupPhoneInputDirty = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return signupPhoneInputFocused || (typeof document !== 'undefined' && document.activeElement === inputSignupPhone);
|
||||||
|
}
|
||||||
|
|
||||||
function syncSignupPhoneInputFromState(state = latestState) {
|
function syncSignupPhoneInputFromState(state = latestState) {
|
||||||
const signupPhone = getRuntimeSignupPhoneValue(state);
|
const signupPhone = getRuntimeSignupPhoneValue(state);
|
||||||
if (typeof inputSignupPhone !== 'undefined' && inputSignupPhone) {
|
if (typeof inputSignupPhone !== 'undefined' && inputSignupPhone) {
|
||||||
inputSignupPhone.value = signupPhone;
|
if (!shouldPreserveSignupPhoneInputValue(signupPhone)) {
|
||||||
|
inputSignupPhone.value = signupPhone;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (typeof rowSignupPhone !== 'undefined' && rowSignupPhone) {
|
if (typeof rowSignupPhone !== 'undefined' && rowSignupPhone) {
|
||||||
const phoneVerificationEnabled = typeof inputPhoneVerificationEnabled !== 'undefined' && inputPhoneVerificationEnabled
|
const phoneVerificationEnabled = typeof inputPhoneVerificationEnabled !== 'undefined' && inputPhoneVerificationEnabled
|
||||||
@@ -7029,7 +7051,10 @@ function syncSignupPhoneInputFromState(state = latestState) {
|
|||||||
const selectedMethod = typeof normalizeSignupMethod === 'function'
|
const selectedMethod = typeof normalizeSignupMethod === 'function'
|
||||||
? normalizeSignupMethod(rawSignupMethod)
|
? normalizeSignupMethod(rawSignupMethod)
|
||||||
: (String(rawSignupMethod || '').trim().toLowerCase() === 'phone' ? 'phone' : 'email');
|
: (String(rawSignupMethod || '').trim().toLowerCase() === 'phone' ? 'phone' : 'email');
|
||||||
rowSignupPhone.style.display = phoneVerificationEnabled && (selectedMethod === 'phone' || Boolean(signupPhone)) ? '' : 'none';
|
rowSignupPhone.style.display = phoneVerificationEnabled
|
||||||
|
&& (selectedMethod === 'phone' || Boolean(signupPhone) || Boolean(getSignupPhoneInputValue()) || signupPhoneInputDirty)
|
||||||
|
? ''
|
||||||
|
: 'none';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7048,6 +7073,74 @@ async function setRuntimeSignupPhoneState(phoneNumber) {
|
|||||||
return normalizedPhone;
|
return normalizedPhone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function persistSignupPhoneInputValue(options = {}) {
|
||||||
|
const { final = true, silent = true } = options;
|
||||||
|
if (typeof inputSignupPhone === 'undefined' || !inputSignupPhone) {
|
||||||
|
return getRuntimeSignupPhoneValue(latestState);
|
||||||
|
}
|
||||||
|
if (signupPhoneInputPersistPromise) {
|
||||||
|
return signupPhoneInputPersistPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
const phoneNumber = getSignupPhoneInputValue();
|
||||||
|
inputSignupPhone.value = phoneNumber;
|
||||||
|
const currentPhone = getRuntimeSignupPhoneValue(latestState);
|
||||||
|
if (!signupPhoneInputDirty && phoneNumber === currentPhone) {
|
||||||
|
return phoneNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
signupPhoneInputPersistPromise = (async () => {
|
||||||
|
const response = await chrome.runtime.sendMessage({
|
||||||
|
type: final ? 'SAVE_SIGNUP_PHONE' : 'SET_SIGNUP_PHONE_STATE',
|
||||||
|
source: 'sidepanel',
|
||||||
|
payload: { phoneNumber },
|
||||||
|
});
|
||||||
|
if (response?.error) {
|
||||||
|
throw new Error(response.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedPhone = String(response?.phoneNumber || phoneNumber || '').trim();
|
||||||
|
signupPhoneInputDirty = getSignupPhoneInputValue() !== normalizedPhone;
|
||||||
|
syncLatestState({
|
||||||
|
signupPhoneNumber: normalizedPhone,
|
||||||
|
...(normalizedPhone
|
||||||
|
? {
|
||||||
|
accountIdentifierType: 'phone',
|
||||||
|
accountIdentifier: normalizedPhone,
|
||||||
|
}
|
||||||
|
: (String(latestState?.accountIdentifierType || '').trim().toLowerCase() === 'phone'
|
||||||
|
? {
|
||||||
|
accountIdentifierType: null,
|
||||||
|
accountIdentifier: '',
|
||||||
|
}
|
||||||
|
: {})),
|
||||||
|
});
|
||||||
|
syncSignupPhoneInputFromState(latestState);
|
||||||
|
if (!silent) {
|
||||||
|
showToast(normalizedPhone ? '注册手机号已保存。' : '注册手机号已清空。', 'success', 1600);
|
||||||
|
}
|
||||||
|
return normalizedPhone;
|
||||||
|
})();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await signupPhoneInputPersistPromise;
|
||||||
|
} finally {
|
||||||
|
signupPhoneInputPersistPromise = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function persistSignupPhoneInputForAction() {
|
||||||
|
if (typeof inputSignupPhone === 'undefined' || !inputSignupPhone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const phoneNumber = getSignupPhoneInputValue();
|
||||||
|
const currentPhone = getRuntimeSignupPhoneValue(latestState);
|
||||||
|
if (!signupPhoneInputDirty && phoneNumber === currentPhone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await persistSignupPhoneInputValue({ final: true, silent: true });
|
||||||
|
}
|
||||||
|
|
||||||
async function openPlusManualConfirmationDialog(options = {}) {
|
async function openPlusManualConfirmationDialog(options = {}) {
|
||||||
const method = String(options.method || '').trim().toLowerCase();
|
const method = String(options.method || '').trim().toLowerCase();
|
||||||
const gopayValue = typeof PLUS_PAYMENT_METHOD_GOPAY !== 'undefined' ? PLUS_PAYMENT_METHOD_GOPAY : 'gopay';
|
const gopayValue = typeof PLUS_PAYMENT_METHOD_GOPAY !== 'undefined' ? PLUS_PAYMENT_METHOD_GOPAY : 'gopay';
|
||||||
@@ -7207,6 +7300,7 @@ async function clearRegistrationSignupPhone(options = {}) {
|
|||||||
if (typeof inputSignupPhone !== 'undefined' && inputSignupPhone) {
|
if (typeof inputSignupPhone !== 'undefined' && inputSignupPhone) {
|
||||||
inputSignupPhone.value = '';
|
inputSignupPhone.value = '';
|
||||||
}
|
}
|
||||||
|
signupPhoneInputDirty = false;
|
||||||
syncSignupPhoneInputFromState(latestState);
|
syncSignupPhoneInputFromState(latestState);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -7214,6 +7308,7 @@ async function clearRegistrationSignupPhone(options = {}) {
|
|||||||
if (typeof inputSignupPhone !== 'undefined' && inputSignupPhone) {
|
if (typeof inputSignupPhone !== 'undefined' && inputSignupPhone) {
|
||||||
inputSignupPhone.value = '';
|
inputSignupPhone.value = '';
|
||||||
}
|
}
|
||||||
|
signupPhoneInputDirty = false;
|
||||||
syncLatestState({
|
syncLatestState({
|
||||||
signupPhoneNumber: '',
|
signupPhoneNumber: '',
|
||||||
...(String(latestState?.accountIdentifierType || '').trim().toLowerCase() === 'phone'
|
...(String(latestState?.accountIdentifierType || '').trim().toLowerCase() === 'phone'
|
||||||
@@ -7309,6 +7404,7 @@ async function saveSettings(options = {}) {
|
|||||||
async function persistCurrentSettingsForAction() {
|
async function persistCurrentSettingsForAction() {
|
||||||
clearTimeout(settingsAutoSaveTimer);
|
clearTimeout(settingsAutoSaveTimer);
|
||||||
await waitForSettingsSaveIdle();
|
await waitForSettingsSaveIdle();
|
||||||
|
await persistSignupPhoneInputForAction();
|
||||||
await saveSettings({ silent: true, force: true });
|
await saveSettings({ silent: true, force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10203,6 +10299,8 @@ async function handleSkipStep(step) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await persistCurrentSettingsForAction();
|
||||||
|
|
||||||
const response = await chrome.runtime.sendMessage({
|
const response = await chrome.runtime.sendMessage({
|
||||||
type: 'SKIP_STEP',
|
type: 'SKIP_STEP',
|
||||||
source: 'sidepanel',
|
source: 'sidepanel',
|
||||||
@@ -10728,27 +10826,31 @@ inputEmail.addEventListener('change', async () => {
|
|||||||
});
|
});
|
||||||
inputEmail.addEventListener('input', updateButtonStates);
|
inputEmail.addEventListener('input', updateButtonStates);
|
||||||
if (typeof inputSignupPhone !== 'undefined' && inputSignupPhone) {
|
if (typeof inputSignupPhone !== 'undefined' && inputSignupPhone) {
|
||||||
inputSignupPhone.addEventListener('change', async () => {
|
inputSignupPhone.addEventListener('focus', () => {
|
||||||
const phoneNumber = inputSignupPhone.value.trim();
|
signupPhoneInputFocused = true;
|
||||||
inputSignupPhone.value = phoneNumber;
|
});
|
||||||
|
inputSignupPhone.addEventListener('blur', async () => {
|
||||||
|
signupPhoneInputFocused = false;
|
||||||
|
if (!signupPhoneInputDirty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (phoneNumber) {
|
await persistSignupPhoneInputValue({ final: true, silent: true });
|
||||||
const response = await chrome.runtime.sendMessage({
|
|
||||||
type: 'SAVE_SIGNUP_PHONE',
|
|
||||||
source: 'sidepanel',
|
|
||||||
payload: { phoneNumber },
|
|
||||||
});
|
|
||||||
if (response?.error) {
|
|
||||||
throw new Error(response.error);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await clearRegistrationSignupPhone();
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, 'error');
|
showToast(err.message, 'error');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
inputSignupPhone.addEventListener('input', updateButtonStates);
|
inputSignupPhone.addEventListener('change', async () => {
|
||||||
|
try {
|
||||||
|
await persistSignupPhoneInputValue({ final: true, silent: true });
|
||||||
|
} catch (err) {
|
||||||
|
showToast(err.message, 'error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
inputSignupPhone.addEventListener('input', () => {
|
||||||
|
signupPhoneInputDirty = true;
|
||||||
|
updateButtonStates();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
inputVpsUrl.addEventListener('input', () => {
|
inputVpsUrl.addEventListener('input', () => {
|
||||||
markSettingsDirty(true);
|
markSettingsDirty(true);
|
||||||
|
|||||||
@@ -296,6 +296,66 @@ test('step 7 forwards phone login identity payload when account identifier is ph
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('step 7 can start from a manually filled signup phone without completed step 2 or step 3 state', async () => {
|
||||||
|
const source = fs.readFileSync('background/steps/oauth-login.js', 'utf8');
|
||||||
|
const globalScope = {};
|
||||||
|
const api = new Function('self', `${source}; return self.MultiPageBackgroundStep7;`)(globalScope);
|
||||||
|
|
||||||
|
const events = {
|
||||||
|
payloads: [],
|
||||||
|
completions: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const executor = api.createStep7Executor({
|
||||||
|
addLog: async () => {},
|
||||||
|
completeStepFromBackground: async (step, payload) => {
|
||||||
|
events.completions.push({ step, payload });
|
||||||
|
},
|
||||||
|
getErrorMessage: (error) => error?.message || String(error || ''),
|
||||||
|
getLoginAuthStateLabel: (state) => state || 'unknown',
|
||||||
|
getState: async () => ({
|
||||||
|
accountIdentifierType: 'phone',
|
||||||
|
accountIdentifier: '+447780579093',
|
||||||
|
signupPhoneNumber: '+447780579093',
|
||||||
|
stepStatuses: { 2: 'pending', 3: 'pending' },
|
||||||
|
}),
|
||||||
|
isStep6RecoverableResult: (result) => result?.step6Outcome === 'recoverable',
|
||||||
|
isStep6SuccessResult: (result) => result?.step6Outcome === 'success',
|
||||||
|
refreshOAuthUrlBeforeStep6: async () => 'https://oauth.example/latest',
|
||||||
|
reuseOrCreateTab: async () => {},
|
||||||
|
sendToContentScriptResilient: async (_sourceName, message) => {
|
||||||
|
events.payloads.push(message.payload);
|
||||||
|
return {
|
||||||
|
step6Outcome: 'success',
|
||||||
|
state: 'phone_verification_page',
|
||||||
|
loginVerificationRequestedAt: 987654,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
STEP6_MAX_ATTEMPTS: 3,
|
||||||
|
throwIfStopped: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
await executor.executeStep7({
|
||||||
|
accountIdentifierType: 'phone',
|
||||||
|
accountIdentifier: '+447780579093',
|
||||||
|
signupPhoneNumber: '+447780579093',
|
||||||
|
stepStatuses: { 2: 'pending', 3: 'pending' },
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(events.payloads[0].loginIdentifierType, 'phone');
|
||||||
|
assert.equal(events.payloads[0].phoneNumber, '+447780579093');
|
||||||
|
assert.equal(events.payloads[0].email, '');
|
||||||
|
assert.equal(events.payloads[0].password, '');
|
||||||
|
assert.deepStrictEqual(events.completions, [
|
||||||
|
{
|
||||||
|
step: 7,
|
||||||
|
payload: {
|
||||||
|
loginVerificationRequestedAt: 987654,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
test('step 7 stops immediately when management secret is missing', async () => {
|
test('step 7 stops immediately when management secret is missing', async () => {
|
||||||
const source = fs.readFileSync('background/steps/oauth-login.js', 'utf8');
|
const source = fs.readFileSync('background/steps/oauth-login.js', 'utf8');
|
||||||
const globalScope = {};
|
const globalScope = {};
|
||||||
|
|||||||
@@ -282,10 +282,14 @@ let clearedTimer = null;
|
|||||||
let settingsSaveInFlight = false;
|
let settingsSaveInFlight = false;
|
||||||
let settingsDirty = false;
|
let settingsDirty = false;
|
||||||
let settingsSaveRevision = 0;
|
let settingsSaveRevision = 0;
|
||||||
|
let phonePersistCalls = 0;
|
||||||
const saveCalls = [];
|
const saveCalls = [];
|
||||||
function clearTimeout(value) {
|
function clearTimeout(value) {
|
||||||
clearedTimer = value;
|
clearedTimer = value;
|
||||||
}
|
}
|
||||||
|
async function persistSignupPhoneInputForAction() {
|
||||||
|
phonePersistCalls += 1;
|
||||||
|
}
|
||||||
function updateSaveButtonState() {}
|
function updateSaveButtonState() {}
|
||||||
function collectSettingsPayload() {
|
function collectSettingsPayload() {
|
||||||
return { luckmailApiKey: 'autofilled-key' };
|
return { luckmailApiKey: 'autofilled-key' };
|
||||||
@@ -308,7 +312,7 @@ ${bundle}
|
|||||||
return {
|
return {
|
||||||
persistCurrentSettingsForAction,
|
persistCurrentSettingsForAction,
|
||||||
getSnapshot() {
|
getSnapshot() {
|
||||||
return { clearedTimer, saveCalls };
|
return { clearedTimer, phonePersistCalls, saveCalls };
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
`)();
|
`)();
|
||||||
@@ -317,5 +321,6 @@ return {
|
|||||||
const snapshot = api.getSnapshot();
|
const snapshot = api.getSnapshot();
|
||||||
|
|
||||||
assert.equal(snapshot.clearedTimer, 123);
|
assert.equal(snapshot.clearedTimer, 123);
|
||||||
|
assert.equal(snapshot.phonePersistCalls, 1);
|
||||||
assert.deepStrictEqual(snapshot.saveCalls, [{ luckmailApiKey: 'autofilled-key' }]);
|
assert.deepStrictEqual(snapshot.saveCalls, [{ luckmailApiKey: 'autofilled-key' }]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -136,10 +136,57 @@ test('sidepanel html exposes phone verification toggle and multi-provider SMS ro
|
|||||||
|
|
||||||
test('sidepanel source wires runtime signup phone field to background sync messages', () => {
|
test('sidepanel source wires runtime signup phone field to background sync messages', () => {
|
||||||
assert.match(sidepanelSource, /function getRuntimeSignupPhoneValue\(state = latestState\)/);
|
assert.match(sidepanelSource, /function getRuntimeSignupPhoneValue\(state = latestState\)/);
|
||||||
|
assert.match(sidepanelSource, /function shouldPreserveSignupPhoneInputValue\(stateSignupPhone = ''\)/);
|
||||||
assert.match(sidepanelSource, /function syncSignupPhoneInputFromState\(state = latestState\)/);
|
assert.match(sidepanelSource, /function syncSignupPhoneInputFromState\(state = latestState\)/);
|
||||||
|
assert.match(sidepanelSource, /async function persistSignupPhoneInputForAction\(\)/);
|
||||||
assert.match(sidepanelSource, /type:\s*'SET_SIGNUP_PHONE_STATE'/);
|
assert.match(sidepanelSource, /type:\s*'SET_SIGNUP_PHONE_STATE'/);
|
||||||
assert.match(sidepanelSource, /type:\s*'SAVE_SIGNUP_PHONE'/);
|
assert.match(sidepanelSource, /final \? 'SAVE_SIGNUP_PHONE' : 'SET_SIGNUP_PHONE_STATE'/);
|
||||||
assert.match(sidepanelSource, /message\.payload\.signupPhoneNumber !== undefined/);
|
assert.match(sidepanelSource, /message\.payload\.signupPhoneNumber !== undefined/);
|
||||||
|
assert.match(sidepanelSource, /await persistSignupPhoneInputForAction\(\);\s*await saveSettings/);
|
||||||
|
assert.match(sidepanelSource, /async function handleSkipStep\(step\)[\s\S]*await persistCurrentSettingsForAction\(\);/);
|
||||||
|
assert.match(sidepanelSource, /inputSignupPhone\.addEventListener\('input'[\s\S]*signupPhoneInputDirty = true/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('runtime signup phone sync preserves active manual input until it is saved', () => {
|
||||||
|
const api = new Function(`
|
||||||
|
let latestState = { signupMethod: 'phone', phoneVerificationEnabled: true, signupPhoneNumber: '+441111111111' };
|
||||||
|
let signupPhoneInputDirty = true;
|
||||||
|
let signupPhoneInputFocused = true;
|
||||||
|
const inputSignupPhone = { value: '+442222222222' };
|
||||||
|
const rowSignupPhone = { style: { display: 'none' } };
|
||||||
|
const inputPhoneVerificationEnabled = { checked: true };
|
||||||
|
const document = { activeElement: inputSignupPhone };
|
||||||
|
function getSelectedSignupMethod() { return 'phone'; }
|
||||||
|
${extractFunction('normalizeSignupMethod')}
|
||||||
|
${extractFunction('getRuntimeSignupPhoneValue')}
|
||||||
|
${extractFunction('getSignupPhoneInputValue')}
|
||||||
|
${extractFunction('shouldPreserveSignupPhoneInputValue')}
|
||||||
|
${extractFunction('syncSignupPhoneInputFromState')}
|
||||||
|
return {
|
||||||
|
inputSignupPhone,
|
||||||
|
rowSignupPhone,
|
||||||
|
syncSignupPhoneInputFromState,
|
||||||
|
getDirty: () => signupPhoneInputDirty,
|
||||||
|
setFocused: (value) => { signupPhoneInputFocused = Boolean(value); document.activeElement = value ? inputSignupPhone : null; },
|
||||||
|
};
|
||||||
|
`)();
|
||||||
|
|
||||||
|
api.syncSignupPhoneInputFromState({
|
||||||
|
signupMethod: 'phone',
|
||||||
|
phoneVerificationEnabled: true,
|
||||||
|
signupPhoneNumber: '+441111111111',
|
||||||
|
});
|
||||||
|
assert.equal(api.inputSignupPhone.value, '+442222222222');
|
||||||
|
assert.equal(api.rowSignupPhone.style.display, '');
|
||||||
|
assert.equal(api.getDirty(), true);
|
||||||
|
|
||||||
|
api.setFocused(false);
|
||||||
|
api.syncSignupPhoneInputFromState({
|
||||||
|
signupMethod: 'phone',
|
||||||
|
phoneVerificationEnabled: true,
|
||||||
|
signupPhoneNumber: '+441111111111',
|
||||||
|
});
|
||||||
|
assert.equal(api.inputSignupPhone.value, '+441111111111');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('hero sms country helpers keep empty summary state and expose removable order handling', () => {
|
test('hero sms country helpers keep empty summary state and expose removable order handling', () => {
|
||||||
|
|||||||
+1
-1
@@ -155,7 +155,7 @@
|
|||||||
- 本轮联动自检重点:
|
- 本轮联动自检重点:
|
||||||
1. Step 2 一旦自动取到手机号,必须立刻广播并回填侧栏运行态手机号。
|
1. Step 2 一旦自动取到手机号,必须立刻广播并回填侧栏运行态手机号。
|
||||||
2. sidepanel 重新加载后,必须从当前 state 恢复“注册手机号”文本框,而不是只恢复邮箱。
|
2. sidepanel 重新加载后,必须从当前 state 恢复“注册手机号”文本框,而不是只恢复邮箱。
|
||||||
3. 用户手动修改“注册手机号”时,只能改运行态身份槽位,不能误写入持久配置或配置导入导出。
|
3. 用户手动修改“注册手机号”时,只能改运行态身份槽位,不能误写入持久配置或配置导入导出;编辑中的值不能被后台旧 state 覆盖,执行步骤或跳过步骤前必须先保存当前输入框值。
|
||||||
4. 第 4 步继续依赖当前手机号注册 activation 获取验证码;只有手机号字符串、没有 activation 时,只能作为页面填写来源,不能伪造成自动接码订单。
|
4. 第 4 步继续依赖当前手机号注册 activation 获取验证码;只有手机号字符串、没有 activation 时,只能作为页面填写来源,不能伪造成自动接码订单。
|
||||||
5. 显示与身份同步统一以侧栏运行态手机号为准,避免 UI 空白和后台状态脱节。
|
5. 显示与身份同步统一以侧栏运行态手机号为准,避免 UI 空白和后台状态脱节。
|
||||||
- 第 8 步固定的验证码页显示邮箱 `step8VerificationTargetEmail`
|
- 第 8 步固定的验证码页显示邮箱 `step8VerificationTargetEmail`
|
||||||
|
|||||||
Reference in New Issue
Block a user