重构 Kiro 注册页面状态收养
This commit is contained in:
@@ -68,6 +68,48 @@
|
|||||||
'code',
|
'code',
|
||||||
'aws',
|
'aws',
|
||||||
]);
|
]);
|
||||||
|
const KIRO_REGISTER_PAGE_STATES = Object.freeze([
|
||||||
|
'kiro_signin_page',
|
||||||
|
'email_entry',
|
||||||
|
'name_entry',
|
||||||
|
'register_otp_page',
|
||||||
|
'create_password_page',
|
||||||
|
'authorization_page',
|
||||||
|
'success_page',
|
||||||
|
'kiro_web_signed_in',
|
||||||
|
'login_password_page',
|
||||||
|
'login_otp_page',
|
||||||
|
]);
|
||||||
|
const KIRO_REGISTER_EXISTING_ACCOUNT_STATES = Object.freeze([
|
||||||
|
'login_password_page',
|
||||||
|
'login_otp_page',
|
||||||
|
]);
|
||||||
|
const KIRO_REGISTER_AFTER_EMAIL_STATES = Object.freeze([
|
||||||
|
'name_entry',
|
||||||
|
'register_otp_page',
|
||||||
|
'create_password_page',
|
||||||
|
'authorization_page',
|
||||||
|
'success_page',
|
||||||
|
'kiro_web_signed_in',
|
||||||
|
]);
|
||||||
|
const KIRO_REGISTER_AFTER_NAME_STATES = Object.freeze([
|
||||||
|
'register_otp_page',
|
||||||
|
'create_password_page',
|
||||||
|
'authorization_page',
|
||||||
|
'success_page',
|
||||||
|
'kiro_web_signed_in',
|
||||||
|
]);
|
||||||
|
const KIRO_REGISTER_AFTER_OTP_STATES = Object.freeze([
|
||||||
|
'create_password_page',
|
||||||
|
'authorization_page',
|
||||||
|
'success_page',
|
||||||
|
'kiro_web_signed_in',
|
||||||
|
]);
|
||||||
|
const KIRO_REGISTER_AFTER_PASSWORD_STATES = Object.freeze([
|
||||||
|
'authorization_page',
|
||||||
|
'success_page',
|
||||||
|
'kiro_web_signed_in',
|
||||||
|
]);
|
||||||
|
|
||||||
function isPlainObject(value) {
|
function isPlainObject(value) {
|
||||||
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
||||||
@@ -607,6 +649,7 @@
|
|||||||
fromStates: Array.isArray(options.fromStates) ? options.fromStates : [],
|
fromStates: Array.isArray(options.fromStates) ? options.fromStates : [],
|
||||||
timeoutMs: stateWaitTimeoutMs,
|
timeoutMs: stateWaitTimeoutMs,
|
||||||
retryDelayMs: Number(options.pageRetryDelayMs) || 250,
|
retryDelayMs: Number(options.pageRetryDelayMs) || 250,
|
||||||
|
returnOnCodeInvalid: Boolean(options.returnOnCodeInvalid),
|
||||||
timeoutMessage: options.timeoutMessage || '',
|
timeoutMessage: options.timeoutMessage || '',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
@@ -624,6 +667,109 @@
|
|||||||
return result || { state: '', url: '' };
|
return result || { state: '', url: '' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function readKiroRegisterPageState(tabId, options = {}) {
|
||||||
|
return ensureKiroPageState(tabId, {
|
||||||
|
...options,
|
||||||
|
targetStates: KIRO_REGISTER_PAGE_STATES,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function isKiroExistingAccountState(pageState = '') {
|
||||||
|
return KIRO_REGISTER_EXISTING_ACCOUNT_STATES.includes(cleanString(pageState));
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveKiroRegisterEmail(currentState = {}, pageState = {}, fallbackEmail = '') {
|
||||||
|
const runtimeState = readKiroRuntime(currentState);
|
||||||
|
return cleanString(
|
||||||
|
fallbackEmail
|
||||||
|
|| pageState?.email
|
||||||
|
|| pageState?.accountEmail
|
||||||
|
|| runtimeState.register?.email
|
||||||
|
|| currentState?.email
|
||||||
|
).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createKiroExistingAccountError(pageState = {}, currentState = {}, step = 0, fallbackEmail = '') {
|
||||||
|
const email = resolveKiroRegisterEmail(currentState, pageState, fallbackEmail);
|
||||||
|
const emailText = email ? ` ${email}` : '';
|
||||||
|
return new Error(
|
||||||
|
`步骤 ${step}:邮箱${emailText} 已进入 AWS Builder ID 登录页,说明该邮箱已存在或被 AWS 判定为已有账号;Kiro 注册流程只处理新账号注册,已停止,请换新邮箱重试。`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertKiroRegistrationOnlyState(pageState = {}, currentState = {}, step = 0, fallbackEmail = '') {
|
||||||
|
if (isKiroExistingAccountState(pageState?.state)) {
|
||||||
|
throw createKiroExistingAccountError(pageState, currentState, step, fallbackEmail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKiroRegisterStatusForPageState(pageState = '') {
|
||||||
|
switch (cleanString(pageState)) {
|
||||||
|
case 'email_entry':
|
||||||
|
return 'waiting_email';
|
||||||
|
case 'name_entry':
|
||||||
|
return 'waiting_name';
|
||||||
|
case 'register_otp_page':
|
||||||
|
return 'waiting_otp';
|
||||||
|
case 'create_password_page':
|
||||||
|
return 'waiting_password';
|
||||||
|
case 'authorization_page':
|
||||||
|
return 'waiting_consent';
|
||||||
|
case 'success_page':
|
||||||
|
case 'kiro_web_signed_in':
|
||||||
|
return 'completed';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildKiroRegisterStatePatch(currentState = {}, pageState = {}, options = {}) {
|
||||||
|
const resolvedEmail = resolveKiroRegisterEmail(currentState, pageState, options.email);
|
||||||
|
const nextStatus = cleanString(options.status) || getKiroRegisterStatusForPageState(pageState?.state);
|
||||||
|
const registerPatch = {};
|
||||||
|
if (resolvedEmail) {
|
||||||
|
registerPatch.email = resolvedEmail;
|
||||||
|
}
|
||||||
|
if (options.fullName !== undefined) {
|
||||||
|
registerPatch.fullName = cleanString(options.fullName);
|
||||||
|
}
|
||||||
|
if (options.verificationRequestedAt !== undefined) {
|
||||||
|
registerPatch.verificationRequestedAt = Math.max(0, Number(options.verificationRequestedAt) || 0);
|
||||||
|
}
|
||||||
|
if (nextStatus) {
|
||||||
|
registerPatch.status = nextStatus;
|
||||||
|
registerPatch.completedAt = nextStatus === 'completed' ? Date.now() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
session: {
|
||||||
|
currentStage: nextStatus === 'completed' ? 'desktop-authorize' : 'register',
|
||||||
|
pageState: pageState?.state || '',
|
||||||
|
pageUrl: pageState?.url || '',
|
||||||
|
lastError: '',
|
||||||
|
},
|
||||||
|
register: registerPatch,
|
||||||
|
upload: {
|
||||||
|
status: nextStatus === 'completed' ? 'waiting_desktop_authorize' : 'waiting_register',
|
||||||
|
error: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function adoptKiroRegisterPageState(currentState = {}, pageState = {}, nodeId = '', options = {}) {
|
||||||
|
const payload = await applyRuntimeState(
|
||||||
|
currentState,
|
||||||
|
buildKiroRegisterStatePatch(currentState, pageState, options)
|
||||||
|
);
|
||||||
|
await completeNodeFromBackground(nodeId, {
|
||||||
|
...payload,
|
||||||
|
email: resolveKiroRegisterEmail(currentState, pageState, options.email),
|
||||||
|
accountIdentifierType: 'email',
|
||||||
|
accountIdentifier: resolveKiroRegisterEmail(currentState, pageState, options.email),
|
||||||
|
});
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
function resolveKiroFullName(state = {}) {
|
function resolveKiroFullName(state = {}) {
|
||||||
const runtimeState = readKiroRuntime(state);
|
const runtimeState = readKiroRuntime(state);
|
||||||
const cachedName = cleanString(runtimeState.register?.fullName);
|
const cachedName = cleanString(runtimeState.register?.fullName);
|
||||||
@@ -753,7 +899,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const runtimeState = readKiroRuntime(state);
|
const runtimeState = readKiroRuntime(state);
|
||||||
const requestedAt = Math.max(0, Number(runtimeState.register?.verificationRequestedAt) || Date.now());
|
const recordedRequestedAt = Math.max(0, Number(runtimeState.register?.verificationRequestedAt) || 0);
|
||||||
|
const requestedAt = recordedRequestedAt || Math.max(0, Date.now() - MAIL_2925_FILTER_LOOKBACK_MS);
|
||||||
const filterAfterTimestamp = mail.provider === '2925'
|
const filterAfterTimestamp = mail.provider === '2925'
|
||||||
? Math.max(0, requestedAt - MAIL_2925_FILTER_LOOKBACK_MS)
|
? Math.max(0, requestedAt - MAIL_2925_FILTER_LOOKBACK_MS)
|
||||||
: requestedAt;
|
: requestedAt;
|
||||||
@@ -952,22 +1099,44 @@
|
|||||||
const nodeId = String(state?.nodeId || 'kiro-submit-email').trim();
|
const nodeId = String(state?.nodeId || 'kiro-submit-email').trim();
|
||||||
const currentState = await getExecutionState(state);
|
const currentState = await getExecutionState(state);
|
||||||
try {
|
try {
|
||||||
if (typeof resolveSignupEmailForFlow !== 'function') {
|
|
||||||
throw new Error('Kiro 邮箱步骤缺少公共邮箱解析能力,无法继续执行。');
|
|
||||||
}
|
|
||||||
|
|
||||||
const tabId = await activateKiroRegisterTab(currentState, {
|
const tabId = await activateKiroRegisterTab(currentState, {
|
||||||
missingUrlMessage: '缺少 Kiro 注册页地址,请先执行步骤 1。',
|
missingUrlMessage: '缺少 Kiro 注册页地址,请先执行步骤 1。',
|
||||||
openFailedMessage: '无法恢复 Kiro 注册页,请重新执行步骤 1。',
|
openFailedMessage: '无法恢复 Kiro 注册页,请重新执行步骤 1。',
|
||||||
});
|
});
|
||||||
await ensureKiroPageState(tabId, {
|
const currentPageState = await readKiroRegisterPageState(tabId, {
|
||||||
step: 2,
|
step: 2,
|
||||||
targetStates: ['email_entry'],
|
|
||||||
stableMs: 2500,
|
stableMs: 2500,
|
||||||
initialDelayMs: 300,
|
initialDelayMs: 300,
|
||||||
injectLogMessage: '步骤 2:Kiro 注册页内容脚本未就绪,正在等待页面恢复...',
|
injectLogMessage: '步骤 2:Kiro 注册页内容脚本未就绪,正在等待页面恢复...',
|
||||||
readyLogMessage: '步骤 2:正在等待 Kiro 注册页邮箱输入框加载完成...',
|
readyLogMessage: '步骤 2:正在读取 Kiro 注册页当前状态...',
|
||||||
});
|
});
|
||||||
|
assertKiroRegistrationOnlyState(currentPageState, currentState, 2);
|
||||||
|
|
||||||
|
if (KIRO_REGISTER_AFTER_EMAIL_STATES.includes(currentPageState?.state)) {
|
||||||
|
const runtimeState = readKiroRuntime(currentState);
|
||||||
|
const adoptedEmail = resolveKiroRegisterEmail(currentState, currentPageState);
|
||||||
|
if (!adoptedEmail) {
|
||||||
|
throw new Error('步骤 2:当前已不在邮箱页,但无法识别注册邮箱,请回到邮箱页重新提交或在配置中填入注册邮箱。');
|
||||||
|
}
|
||||||
|
const status = getKiroRegisterStatusForPageState(currentPageState.state);
|
||||||
|
await adoptKiroRegisterPageState(currentState, currentPageState, nodeId, {
|
||||||
|
email: adoptedEmail,
|
||||||
|
status,
|
||||||
|
verificationRequestedAt: currentPageState.state === 'register_otp_page'
|
||||||
|
? runtimeState.register?.verificationRequestedAt || 0
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
await log(`步骤 2:检测到当前已进入 ${currentPageState.state},已收养注册进度并继续。`, 'ok', nodeId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentPageState?.state !== 'email_entry') {
|
||||||
|
throw new Error(`步骤 2:当前页面状态为 ${currentPageState?.state || 'unknown'},不是 Kiro 注册邮箱页,请先执行步骤 1 或回到邮箱输入页。`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof resolveSignupEmailForFlow !== 'function') {
|
||||||
|
throw new Error('Kiro 邮箱步骤缺少公共邮箱解析能力,无法继续执行。');
|
||||||
|
}
|
||||||
|
|
||||||
const resolvedEmail = await resolveSignupEmailForFlow(currentState, {
|
const resolvedEmail = await resolveSignupEmailForFlow(currentState, {
|
||||||
preserveAccountIdentity: true,
|
preserveAccountIdentity: true,
|
||||||
@@ -993,19 +1162,27 @@
|
|||||||
throw new Error(submitResult.error);
|
throw new Error(submitResult.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const landingResult = await ensureKiroPageState(tabId, {
|
const landingResult = await waitForKiroPageChange(tabId, {
|
||||||
step: 2,
|
step: 2,
|
||||||
targetStates: ['name_entry'],
|
fromStates: ['email_entry'],
|
||||||
stableMs: 1500,
|
stableMs: 1500,
|
||||||
initialDelayMs: 150,
|
initialDelayMs: 150,
|
||||||
injectLogMessage: '步骤 2:邮箱提交后页面切换中,正在等待 Kiro 注册页恢复...',
|
injectLogMessage: '步骤 2:邮箱提交后页面切换中,正在等待 Kiro 注册页恢复...',
|
||||||
readyLogMessage: '步骤 2:邮箱已提交,正在等待 Kiro 姓名页加载完成...',
|
readyLogMessage: '步骤 2:邮箱已提交,正在等待 Kiro 注册链路进入下一页...',
|
||||||
timeoutMessage: '邮箱提交后未进入姓名页,请检查当前邮箱是否已注册或页面是否异常。',
|
timeoutMessage: '邮箱提交后页面没有离开邮箱页,请检查邮箱是否被拒绝、页面是否异常或代理是否卡住。',
|
||||||
});
|
});
|
||||||
|
assertKiroRegistrationOnlyState(landingResult, currentState, 2, resolvedEmail);
|
||||||
|
if (!KIRO_REGISTER_AFTER_EMAIL_STATES.includes(landingResult?.state)) {
|
||||||
|
throw new Error(`步骤 2:邮箱提交后进入了无法继续注册的页面状态:${landingResult?.state || 'unknown'}。`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const landedStatus = getKiroRegisterStatusForPageState(landingResult.state);
|
||||||
|
const requestedAt = landingResult.state === 'register_otp_page' ? Date.now() : 0;
|
||||||
|
const isCompleted = landedStatus === 'completed';
|
||||||
|
|
||||||
const payload = await applyRuntimeState(currentState, {
|
const payload = await applyRuntimeState(currentState, {
|
||||||
session: {
|
session: {
|
||||||
currentStage: 'register',
|
currentStage: isCompleted ? 'desktop-authorize' : 'register',
|
||||||
pageState: landingResult?.state || '',
|
pageState: landingResult?.state || '',
|
||||||
pageUrl: landingResult?.url || '',
|
pageUrl: landingResult?.url || '',
|
||||||
lastError: '',
|
lastError: '',
|
||||||
@@ -1013,15 +1190,16 @@
|
|||||||
register: {
|
register: {
|
||||||
email: resolvedEmail,
|
email: resolvedEmail,
|
||||||
fullName: '',
|
fullName: '',
|
||||||
verificationRequestedAt: 0,
|
verificationRequestedAt: requestedAt,
|
||||||
status: 'waiting_name',
|
status: landedStatus,
|
||||||
|
completedAt: isCompleted ? Date.now() : 0,
|
||||||
},
|
},
|
||||||
upload: {
|
upload: {
|
||||||
status: 'waiting_register',
|
status: isCompleted ? 'waiting_desktop_authorize' : 'waiting_register',
|
||||||
error: '',
|
error: '',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await log(`步骤 2:邮箱 ${resolvedEmail} 已提交,当前已进入姓名页。`, 'ok', nodeId);
|
await log(`步骤 2:邮箱 ${resolvedEmail} 已提交,当前页面状态:${landingResult?.state || 'unknown'}。`, 'ok', nodeId);
|
||||||
await completeNodeFromBackground(nodeId, {
|
await completeNodeFromBackground(nodeId, {
|
||||||
...payload,
|
...payload,
|
||||||
email: resolvedEmail,
|
email: resolvedEmail,
|
||||||
@@ -1040,22 +1218,41 @@
|
|||||||
const currentState = await getExecutionState(state);
|
const currentState = await getExecutionState(state);
|
||||||
try {
|
try {
|
||||||
const runtimeState = readKiroRuntime(currentState);
|
const runtimeState = readKiroRuntime(currentState);
|
||||||
if (!cleanString(runtimeState.register?.email || currentState?.email)) {
|
|
||||||
throw new Error('缺少 Kiro 注册邮箱,请先完成步骤 2。');
|
|
||||||
}
|
|
||||||
|
|
||||||
const tabId = await activateKiroRegisterTab(currentState, {
|
const tabId = await activateKiroRegisterTab(currentState, {
|
||||||
missingUrlMessage: '缺少 Kiro 注册页地址,请先执行步骤 1。',
|
missingUrlMessage: '缺少 Kiro 注册页地址,请先执行步骤 1。',
|
||||||
openFailedMessage: '无法恢复 Kiro 注册页,请重新执行步骤 1。',
|
openFailedMessage: '无法恢复 Kiro 注册页,请重新执行步骤 1。',
|
||||||
});
|
});
|
||||||
await ensureKiroPageState(tabId, {
|
const currentPageState = await readKiroRegisterPageState(tabId, {
|
||||||
step: 3,
|
step: 3,
|
||||||
targetStates: ['name_entry'],
|
|
||||||
stableMs: 1500,
|
stableMs: 1500,
|
||||||
initialDelayMs: 150,
|
initialDelayMs: 150,
|
||||||
injectLogMessage: '步骤 3:Kiro 姓名页内容脚本未就绪,正在等待页面恢复...',
|
injectLogMessage: '步骤 3:Kiro 姓名页内容脚本未就绪,正在等待页面恢复...',
|
||||||
readyLogMessage: '步骤 3:正在等待 Kiro 姓名页加载完成...',
|
readyLogMessage: '步骤 3:正在读取 Kiro 注册页当前状态...',
|
||||||
});
|
});
|
||||||
|
assertKiroRegistrationOnlyState(currentPageState, currentState, 3);
|
||||||
|
|
||||||
|
const currentEmail = resolveKiroRegisterEmail(currentState, currentPageState);
|
||||||
|
if (!currentEmail) {
|
||||||
|
throw new Error('步骤 3:缺少 Kiro 注册邮箱,请先完成步骤 2。');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (KIRO_REGISTER_AFTER_NAME_STATES.includes(currentPageState?.state)) {
|
||||||
|
const status = getKiroRegisterStatusForPageState(currentPageState.state);
|
||||||
|
await adoptKiroRegisterPageState(currentState, currentPageState, nodeId, {
|
||||||
|
email: currentEmail,
|
||||||
|
status,
|
||||||
|
verificationRequestedAt: currentPageState.state === 'register_otp_page'
|
||||||
|
? (runtimeState.register?.verificationRequestedAt || 0)
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
await log(`步骤 3:检测到当前已进入 ${currentPageState.state},已收养注册进度并继续。`, 'ok', nodeId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentPageState?.state !== 'name_entry') {
|
||||||
|
throw new Error(`步骤 3:当前页面状态为 ${currentPageState?.state || 'unknown'},不是 Kiro 注册姓名页,请先完成步骤 2。`);
|
||||||
|
}
|
||||||
|
|
||||||
const fullName = resolveKiroFullName(currentState);
|
const fullName = resolveKiroFullName(currentState);
|
||||||
const verificationRequestedAt = Date.now();
|
const verificationRequestedAt = Date.now();
|
||||||
@@ -1079,33 +1276,44 @@
|
|||||||
throw new Error(submitResult.error);
|
throw new Error(submitResult.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const landingResult = await ensureKiroPageState(tabId, {
|
const landingResult = await waitForKiroPageChange(tabId, {
|
||||||
step: 3,
|
step: 3,
|
||||||
targetStates: ['otp_page'],
|
fromStates: ['name_entry'],
|
||||||
stableMs: 1500,
|
stableMs: 1500,
|
||||||
initialDelayMs: 150,
|
initialDelayMs: 150,
|
||||||
injectLogMessage: '步骤 3:姓名提交后页面切换中,正在等待 Kiro 注册页恢复...',
|
injectLogMessage: '步骤 3:姓名提交后页面切换中,正在等待 Kiro 注册页恢复...',
|
||||||
readyLogMessage: '步骤 3:姓名已提交,正在等待 Kiro 验证码页加载完成...',
|
readyLogMessage: '步骤 3:姓名已提交,正在等待 Kiro 注册链路进入下一页...',
|
||||||
timeoutMessage: '姓名提交后未进入验证码页,请检查当前页面状态。',
|
timeoutMessage: '姓名提交后未进入验证码页,请检查当前页面状态。',
|
||||||
});
|
});
|
||||||
|
assertKiroRegistrationOnlyState(landingResult, currentState, 3, currentEmail);
|
||||||
|
if (!KIRO_REGISTER_AFTER_NAME_STATES.includes(landingResult?.state)) {
|
||||||
|
throw new Error(`步骤 3:姓名提交后进入了无法继续注册的页面状态:${landingResult?.state || 'unknown'}。`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const landedStatus = getKiroRegisterStatusForPageState(landingResult.state);
|
||||||
|
const isCompleted = landedStatus === 'completed';
|
||||||
const payload = await applyRuntimeState(currentState, {
|
const payload = await applyRuntimeState(currentState, {
|
||||||
session: {
|
session: {
|
||||||
currentStage: 'register',
|
currentStage: isCompleted ? 'desktop-authorize' : 'register',
|
||||||
pageState: landingResult?.state || '',
|
pageState: landingResult?.state || '',
|
||||||
pageUrl: landingResult?.url || '',
|
pageUrl: landingResult?.url || '',
|
||||||
lastError: '',
|
lastError: '',
|
||||||
},
|
},
|
||||||
register: {
|
register: {
|
||||||
|
email: currentEmail,
|
||||||
fullName,
|
fullName,
|
||||||
verificationRequestedAt,
|
verificationRequestedAt: landingResult.state === 'register_otp_page'
|
||||||
status: 'waiting_otp',
|
? verificationRequestedAt
|
||||||
|
: runtimeState.register?.verificationRequestedAt || 0,
|
||||||
|
status: landedStatus,
|
||||||
|
completedAt: isCompleted ? Date.now() : 0,
|
||||||
},
|
},
|
||||||
upload: {
|
upload: {
|
||||||
status: 'waiting_register',
|
status: isCompleted ? 'waiting_desktop_authorize' : 'waiting_register',
|
||||||
error: '',
|
error: '',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await log('步骤 3:姓名已提交,当前已进入验证码页。', 'ok', nodeId);
|
await log(`步骤 3:姓名已提交,当前页面状态:${landingResult?.state || 'unknown'}。`, 'ok', nodeId);
|
||||||
await completeNodeFromBackground(nodeId, payload);
|
await completeNodeFromBackground(nodeId, payload);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = getErrorMessage(error);
|
const message = getErrorMessage(error);
|
||||||
@@ -1118,25 +1326,48 @@
|
|||||||
const nodeId = String(state?.nodeId || 'kiro-submit-verification-code').trim();
|
const nodeId = String(state?.nodeId || 'kiro-submit-verification-code').trim();
|
||||||
const currentState = await getExecutionState(state);
|
const currentState = await getExecutionState(state);
|
||||||
try {
|
try {
|
||||||
const runtimeState = readKiroRuntime(currentState);
|
|
||||||
if (!cleanString(runtimeState.register?.email || currentState?.email)) {
|
|
||||||
throw new Error('缺少 Kiro 注册邮箱,请先完成步骤 2。');
|
|
||||||
}
|
|
||||||
|
|
||||||
const tabId = await activateKiroRegisterTab(currentState, {
|
const tabId = await activateKiroRegisterTab(currentState, {
|
||||||
missingUrlMessage: '缺少 Kiro 注册页地址,请先执行步骤 1。',
|
missingUrlMessage: '缺少 Kiro 注册页地址,请先执行步骤 1。',
|
||||||
openFailedMessage: '无法恢复 Kiro 注册页,请重新执行步骤 1。',
|
openFailedMessage: '无法恢复 Kiro 注册页,请重新执行步骤 1。',
|
||||||
});
|
});
|
||||||
await ensureKiroPageState(tabId, {
|
const currentPageState = await readKiroRegisterPageState(tabId, {
|
||||||
step: 4,
|
step: 4,
|
||||||
targetStates: ['otp_page'],
|
|
||||||
stableMs: 1500,
|
stableMs: 1500,
|
||||||
initialDelayMs: 150,
|
initialDelayMs: 150,
|
||||||
injectLogMessage: '步骤 4:Kiro 验证码页内容脚本未就绪,正在等待页面恢复...',
|
injectLogMessage: '步骤 4:Kiro 验证码页内容脚本未就绪,正在等待页面恢复...',
|
||||||
readyLogMessage: '步骤 4:正在等待 Kiro 验证码页加载完成...',
|
readyLogMessage: '步骤 4:正在读取 Kiro 注册页当前状态...',
|
||||||
});
|
});
|
||||||
|
assertKiroRegistrationOnlyState(currentPageState, currentState, 4);
|
||||||
|
|
||||||
const codeResult = await pollKiroVerificationCode(4, currentState, nodeId);
|
const currentEmail = resolveKiroRegisterEmail(currentState, currentPageState);
|
||||||
|
if (!currentEmail) {
|
||||||
|
throw new Error('步骤 4:缺少 Kiro 注册邮箱,请先完成步骤 2,或在当前验证码页显示注册邮箱后重试。');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (KIRO_REGISTER_AFTER_OTP_STATES.includes(currentPageState?.state)) {
|
||||||
|
const status = getKiroRegisterStatusForPageState(currentPageState.state);
|
||||||
|
await adoptKiroRegisterPageState(currentState, currentPageState, nodeId, {
|
||||||
|
email: currentEmail,
|
||||||
|
status,
|
||||||
|
});
|
||||||
|
await log(`步骤 4:检测到当前已进入 ${currentPageState.state},已收养注册进度并继续。`, 'ok', nodeId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentPageState?.state !== 'register_otp_page') {
|
||||||
|
throw new Error(`步骤 4:当前页面状态为 ${currentPageState?.state || 'unknown'},不是 Kiro 注册验证码页,请先完成前置注册步骤。`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pollingState = {
|
||||||
|
...currentState,
|
||||||
|
email: currentEmail,
|
||||||
|
kiroRuntime: deepMerge(readKiroRuntime(currentState), {
|
||||||
|
register: {
|
||||||
|
email: currentEmail,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
const codeResult = await pollKiroVerificationCode(4, pollingState, nodeId);
|
||||||
const code = cleanString(codeResult?.code);
|
const code = cleanString(codeResult?.code);
|
||||||
if (!code) {
|
if (!code) {
|
||||||
throw new Error('未能获取到 Kiro 邮箱验证码。');
|
throw new Error('未能获取到 Kiro 邮箱验证码。');
|
||||||
@@ -1162,31 +1393,44 @@
|
|||||||
throw new Error(submitResult.error);
|
throw new Error(submitResult.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const landingResult = await ensureKiroPageState(tabId, {
|
const landingResult = await waitForKiroPageChange(tabId, {
|
||||||
step: 4,
|
step: 4,
|
||||||
targetStates: ['password_page'],
|
fromStates: ['register_otp_page'],
|
||||||
stableMs: 1500,
|
stableMs: 1500,
|
||||||
initialDelayMs: 150,
|
initialDelayMs: 150,
|
||||||
injectLogMessage: '步骤 4:验证码提交后页面切换中,正在等待 Kiro 注册页恢复...',
|
injectLogMessage: '步骤 4:验证码提交后页面切换中,正在等待 Kiro 注册页恢复...',
|
||||||
readyLogMessage: '步骤 4:验证码已提交,正在等待 Kiro 密码页加载完成...',
|
readyLogMessage: '步骤 4:验证码已提交,正在等待 Kiro 密码页加载完成...',
|
||||||
|
returnOnCodeInvalid: true,
|
||||||
timeoutMessage: '验证码提交后未进入密码页,请检查验证码是否失效或页面是否异常。',
|
timeoutMessage: '验证码提交后未进入密码页,请检查验证码是否失效或页面是否异常。',
|
||||||
});
|
});
|
||||||
|
assertKiroRegistrationOnlyState(landingResult, currentState, 4, currentEmail);
|
||||||
|
if (landingResult?.state === 'register_otp_page' && landingResult?.codeInvalid) {
|
||||||
|
throw new Error('步骤 4:Kiro 提示验证码无效或已过期,已停止当前注册;请重新获取验证码或换邮箱重试。');
|
||||||
|
}
|
||||||
|
if (!KIRO_REGISTER_AFTER_OTP_STATES.includes(landingResult?.state)) {
|
||||||
|
throw new Error(`步骤 4:验证码提交后进入了无法继续注册的页面状态:${landingResult?.state || 'unknown'}。`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const landedStatus = getKiroRegisterStatusForPageState(landingResult.state);
|
||||||
|
const isCompleted = landedStatus === 'completed';
|
||||||
const payload = await applyRuntimeState(currentState, {
|
const payload = await applyRuntimeState(currentState, {
|
||||||
session: {
|
session: {
|
||||||
currentStage: 'register',
|
currentStage: isCompleted ? 'desktop-authorize' : 'register',
|
||||||
pageState: landingResult?.state || '',
|
pageState: landingResult?.state || '',
|
||||||
pageUrl: landingResult?.url || '',
|
pageUrl: landingResult?.url || '',
|
||||||
lastError: '',
|
lastError: '',
|
||||||
},
|
},
|
||||||
register: {
|
register: {
|
||||||
status: 'waiting_password',
|
email: currentEmail,
|
||||||
|
status: landedStatus,
|
||||||
|
completedAt: isCompleted ? Date.now() : 0,
|
||||||
},
|
},
|
||||||
upload: {
|
upload: {
|
||||||
status: 'waiting_register',
|
status: isCompleted ? 'waiting_desktop_authorize' : 'waiting_register',
|
||||||
error: '',
|
error: '',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await log('步骤 4:验证码已提交,当前已进入密码页。', 'ok', nodeId);
|
await log(`步骤 4:验证码已提交,当前页面状态:${landingResult?.state || 'unknown'}。`, 'ok', nodeId);
|
||||||
await completeNodeFromBackground(nodeId, {
|
await completeNodeFromBackground(nodeId, {
|
||||||
...payload,
|
...payload,
|
||||||
code,
|
code,
|
||||||
@@ -1208,14 +1452,29 @@
|
|||||||
missingUrlMessage: '缺少 Kiro 注册页地址,请先执行步骤 1。',
|
missingUrlMessage: '缺少 Kiro 注册页地址,请先执行步骤 1。',
|
||||||
openFailedMessage: '无法恢复 Kiro 注册页,请重新执行步骤 1。',
|
openFailedMessage: '无法恢复 Kiro 注册页,请重新执行步骤 1。',
|
||||||
});
|
});
|
||||||
await ensureKiroPageState(tabId, {
|
const currentPageState = await readKiroRegisterPageState(tabId, {
|
||||||
step: 5,
|
step: 5,
|
||||||
targetStates: ['password_page'],
|
|
||||||
stableMs: 1500,
|
stableMs: 1500,
|
||||||
initialDelayMs: 150,
|
initialDelayMs: 150,
|
||||||
injectLogMessage: '步骤 5:Kiro 密码页内容脚本未就绪,正在等待页面恢复...',
|
injectLogMessage: '步骤 5:Kiro 密码页内容脚本未就绪,正在等待页面恢复...',
|
||||||
readyLogMessage: '步骤 5:正在等待 Kiro 密码页加载完成...',
|
readyLogMessage: '步骤 5:正在读取 Kiro 注册页当前状态...',
|
||||||
});
|
});
|
||||||
|
assertKiroRegistrationOnlyState(currentPageState, currentState, 5);
|
||||||
|
|
||||||
|
const currentEmail = resolveKiroRegisterEmail(currentState, currentPageState);
|
||||||
|
if (KIRO_REGISTER_AFTER_PASSWORD_STATES.includes(currentPageState?.state)) {
|
||||||
|
const status = getKiroRegisterStatusForPageState(currentPageState.state);
|
||||||
|
await adoptKiroRegisterPageState(currentState, currentPageState, nodeId, {
|
||||||
|
email: currentEmail,
|
||||||
|
status,
|
||||||
|
});
|
||||||
|
await log(`步骤 5:检测到当前已进入 ${currentPageState.state},已收养注册进度并继续。`, 'ok', nodeId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentPageState?.state !== 'create_password_page') {
|
||||||
|
throw new Error(`步骤 5:当前页面状态为 ${currentPageState?.state || 'unknown'},不是 Kiro 注册密码页,请先完成前置注册步骤。`);
|
||||||
|
}
|
||||||
|
|
||||||
const passwordResolution = resolveKiroPassword(currentState);
|
const passwordResolution = resolveKiroPassword(currentState);
|
||||||
const password = passwordResolution.password;
|
const password = passwordResolution.password;
|
||||||
@@ -1253,28 +1512,33 @@
|
|||||||
|
|
||||||
const landingResult = await waitForKiroPageChange(tabId, {
|
const landingResult = await waitForKiroPageChange(tabId, {
|
||||||
step: 5,
|
step: 5,
|
||||||
fromStates: ['password_page'],
|
fromStates: ['create_password_page'],
|
||||||
stableMs: 1200,
|
stableMs: 1200,
|
||||||
initialDelayMs: 120,
|
initialDelayMs: 120,
|
||||||
injectLogMessage: '步骤 5:密码提交后页面切换中,正在等待 Kiro 注册页恢复...',
|
injectLogMessage: '步骤 5:密码提交后页面切换中,正在等待 Kiro 注册页恢复...',
|
||||||
readyLogMessage: '步骤 5:密码已提交,正在等待 Kiro 注册页完成跳转...',
|
readyLogMessage: '步骤 5:密码已提交,正在等待 Kiro 注册页完成跳转...',
|
||||||
timeoutMessage: '密码提交后页面未离开密码页,请检查密码规则或当前页面提示。',
|
timeoutMessage: '密码提交后页面未离开密码页,请检查密码规则或当前页面提示。',
|
||||||
});
|
});
|
||||||
const nextRegisterStatus = landingResult?.state === 'success_page'
|
assertKiroRegistrationOnlyState(landingResult, currentState, 5, currentEmail);
|
||||||
? 'completed'
|
if (!KIRO_REGISTER_AFTER_PASSWORD_STATES.includes(landingResult?.state)) {
|
||||||
: 'waiting_consent';
|
throw new Error(`步骤 5:密码提交后进入了无法继续注册的页面状态:${landingResult?.state || 'unknown'}。`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextRegisterStatus = getKiroRegisterStatusForPageState(landingResult?.state);
|
||||||
const payload = await applyRuntimeState(currentState, {
|
const payload = await applyRuntimeState(currentState, {
|
||||||
session: {
|
session: {
|
||||||
currentStage: 'register',
|
currentStage: nextRegisterStatus === 'completed' ? 'desktop-authorize' : 'register',
|
||||||
pageState: landingResult?.state || '',
|
pageState: landingResult?.state || '',
|
||||||
pageUrl: landingResult?.url || '',
|
pageUrl: landingResult?.url || '',
|
||||||
lastError: '',
|
lastError: '',
|
||||||
},
|
},
|
||||||
register: {
|
register: {
|
||||||
|
email: currentEmail || undefined,
|
||||||
status: nextRegisterStatus,
|
status: nextRegisterStatus,
|
||||||
|
completedAt: nextRegisterStatus === 'completed' ? Date.now() : 0,
|
||||||
},
|
},
|
||||||
upload: {
|
upload: {
|
||||||
status: 'waiting_register',
|
status: nextRegisterStatus === 'completed' ? 'waiting_desktop_authorize' : 'waiting_register',
|
||||||
error: '',
|
error: '',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1295,17 +1559,21 @@
|
|||||||
missingUrlMessage: '缺少 Kiro 注册页地址,请先执行步骤 1。',
|
missingUrlMessage: '缺少 Kiro 注册页地址,请先执行步骤 1。',
|
||||||
openFailedMessage: '无法恢复 Kiro 注册页,请重新执行步骤 1。',
|
openFailedMessage: '无法恢复 Kiro 注册页,请重新执行步骤 1。',
|
||||||
});
|
});
|
||||||
let landingResult = await ensureKiroPageState(tabId, {
|
let landingResult = await readKiroRegisterPageState(tabId, {
|
||||||
step: 6,
|
step: 6,
|
||||||
targetStates: ['authorization_page', 'kiro_web_signed_in'],
|
|
||||||
stableMs: 1500,
|
stableMs: 1500,
|
||||||
initialDelayMs: 150,
|
initialDelayMs: 150,
|
||||||
injectLogMessage: '步骤 6:Kiro 授权确认页内容脚本未就绪,正在等待页面恢复...',
|
injectLogMessage: '步骤 6:Kiro 授权确认页内容脚本未就绪,正在等待页面恢复...',
|
||||||
readyLogMessage: '步骤 6:正在等待 Kiro 授权确认页加载完成...',
|
readyLogMessage: '步骤 6:正在读取 Kiro 注册页当前状态...',
|
||||||
timeoutMessage: '未进入 Kiro 授权确认页,请检查当前页面状态。',
|
timeoutMessage: '未进入 Kiro 授权确认页,请检查当前页面状态。',
|
||||||
});
|
});
|
||||||
|
assertKiroRegistrationOnlyState(landingResult, currentState, 6);
|
||||||
|
|
||||||
if (landingResult?.state !== 'kiro_web_signed_in') {
|
if (!['authorization_page', 'success_page', 'kiro_web_signed_in'].includes(landingResult?.state)) {
|
||||||
|
throw new Error(`步骤 6:当前页面状态为 ${landingResult?.state || 'unknown'},不是 Kiro 注册授权确认页,请先完成前置注册步骤。`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (landingResult?.state === 'authorization_page') {
|
||||||
await log('步骤 6:正在确认访问并完成 Kiro 注册授权...', 'info', nodeId);
|
await log('步骤 6:正在确认访问并完成 Kiro 注册授权...', 'info', nodeId);
|
||||||
const submitResult = await sendToContentScriptResilient(KIRO_REGISTER_PAGE_SOURCE_ID, {
|
const submitResult = await sendToContentScriptResilient(KIRO_REGISTER_PAGE_SOURCE_ID, {
|
||||||
type: 'EXECUTE_NODE',
|
type: 'EXECUTE_NODE',
|
||||||
@@ -1326,13 +1594,14 @@
|
|||||||
}
|
}
|
||||||
landingResult = await ensureKiroPageState(tabId, {
|
landingResult = await ensureKiroPageState(tabId, {
|
||||||
step: 6,
|
step: 6,
|
||||||
targetStates: ['kiro_web_signed_in'],
|
targetStates: ['success_page', 'kiro_web_signed_in'],
|
||||||
stableMs: 2000,
|
stableMs: 2000,
|
||||||
initialDelayMs: 300,
|
initialDelayMs: 300,
|
||||||
injectLogMessage: '步骤 6:授权确认后页面跳转中,正在等待 Kiro Web 登录态恢复...',
|
injectLogMessage: '步骤 6:授权确认后页面跳转中,正在等待 Kiro Web 登录态恢复...',
|
||||||
readyLogMessage: '步骤 6:授权确认已提交,正在等待回到 Kiro Web...',
|
readyLogMessage: '步骤 6:授权确认已提交,正在等待回到 Kiro Web...',
|
||||||
timeoutMessage: '授权确认后未回到 Kiro Web 登录完成页,请检查当前页面或代理状态。',
|
timeoutMessage: '授权确认后未回到 Kiro Web 登录完成页,请检查当前页面或代理状态。',
|
||||||
});
|
});
|
||||||
|
assertKiroRegistrationOnlyState(landingResult, currentState, 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
const webAuthSummary = await captureKiroWebAuthSummary();
|
const webAuthSummary = await captureKiroWebAuthSummary();
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ const KIRO_PASSWORD_FIELD_SELECTOR = 'input:not([type="hidden"]):not([type="chec
|
|||||||
const KIRO_PASSWORD_TEXT_PATTERN = /password|\u5bc6\u7801/i;
|
const KIRO_PASSWORD_TEXT_PATTERN = /password|\u5bc6\u7801/i;
|
||||||
const KIRO_CONFIRM_PASSWORD_TEXT_PATTERN = /confirm\s*password|re[-\s]*enter\s*password|repeat\s*password|verify\s*password|\u786e\u8ba4\s*\u5bc6\u7801|\u518d\u6b21.*\u5bc6\u7801|\u91cd\u590d.*\u5bc6\u7801/i;
|
const KIRO_CONFIRM_PASSWORD_TEXT_PATTERN = /confirm\s*password|re[-\s]*enter\s*password|repeat\s*password|verify\s*password|\u786e\u8ba4\s*\u5bc6\u7801|\u518d\u6b21.*\u5bc6\u7801|\u91cd\u590d.*\u5bc6\u7801/i;
|
||||||
const KIRO_PRIMARY_PASSWORD_HINT_PATTERN = /enter\s*password|create\s*password|new\s*password|\u8f93\u5165.*\u5bc6\u7801|\u521b\u5efa.*\u5bc6\u7801|^\s*\u5bc6\u7801\s*$/i;
|
const KIRO_PRIMARY_PASSWORD_HINT_PATTERN = /enter\s*password|create\s*password|new\s*password|\u8f93\u5165.*\u5bc6\u7801|\u521b\u5efa.*\u5bc6\u7801|^\s*\u5bc6\u7801\s*$/i;
|
||||||
|
const KIRO_SIGN_IN_TEXT_PATTERN = /sign\s*in\s*with\s*your\s*aws\s*builder\s*id|aws\s*builder\s*id\s*sign\s*in/i;
|
||||||
|
const KIRO_CODE_INVALID_TEXT_PATTERN = /code\s*(?:is\s*)?invalid|invalid\s*code|\u4ee3\u7801\u65e0\u6548|\u9a8c\u8bc1\u7801\u65e0\u6548/i;
|
||||||
|
const KIRO_RESEND_CODE_TEXT_PATTERN = /resend|send\s*again|\u91cd\u65b0\u53d1\u9001/i;
|
||||||
|
|
||||||
function isVisibleKiroElement(element) {
|
function isVisibleKiroElement(element) {
|
||||||
if (!element) return false;
|
if (!element) return false;
|
||||||
@@ -114,6 +117,32 @@ function findKiroSignedInAccountEmail(pageText = '') {
|
|||||||
return extractFirstKiroAccountEmailFromText(pageText);
|
return extractFirstKiroAccountEmailFromText(pageText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findKiroPageAccountEmail(pageText = '') {
|
||||||
|
const selector = [
|
||||||
|
'input[type="email"]',
|
||||||
|
'input[name*="email" i]',
|
||||||
|
'[data-testid*="email" i]',
|
||||||
|
'[aria-label*="email" i]',
|
||||||
|
'[title*="email" i]',
|
||||||
|
'dd',
|
||||||
|
'span',
|
||||||
|
'div',
|
||||||
|
'p',
|
||||||
|
].join(', ');
|
||||||
|
for (const element of collectVisibleElements(selector)) {
|
||||||
|
const candidate = extractFirstKiroAccountEmailFromText([
|
||||||
|
element?.value,
|
||||||
|
element?.textContent,
|
||||||
|
element?.getAttribute?.('aria-label'),
|
||||||
|
element?.getAttribute?.('title'),
|
||||||
|
].filter(Boolean).join(' '));
|
||||||
|
if (candidate) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return extractFirstKiroAccountEmailFromText(pageText);
|
||||||
|
}
|
||||||
|
|
||||||
function collectVisibleElements(selector) {
|
function collectVisibleElements(selector) {
|
||||||
return Array.from(document.querySelectorAll(selector))
|
return Array.from(document.querySelectorAll(selector))
|
||||||
.filter((element) => isVisibleKiroElement(element));
|
.filter((element) => isVisibleKiroElement(element));
|
||||||
@@ -357,6 +386,14 @@ function findOtpVerifyButton(otpInput = null) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findOtpResendButton(otpInput = null) {
|
||||||
|
const form = otpInput?.form || otpInput?.closest?.('form') || null;
|
||||||
|
return findActionButton({
|
||||||
|
textPattern: KIRO_RESEND_CODE_TEXT_PATTERN,
|
||||||
|
formOwner: form,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function findPasswordContinueButton(passwordInput = null) {
|
function findPasswordContinueButton(passwordInput = null) {
|
||||||
const form = passwordInput?.form || passwordInput?.closest?.('form') || null;
|
const form = passwordInput?.form || passwordInput?.closest?.('form') || null;
|
||||||
return findActionButton({
|
return findActionButton({
|
||||||
@@ -380,6 +417,18 @@ function parseCurrentUrl() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getKiroAuthPathKind() {
|
||||||
|
const parsed = parseCurrentUrl();
|
||||||
|
const pathname = String(parsed?.pathname || '').toLowerCase();
|
||||||
|
if (/(^|\/)login(\/|$)/.test(pathname)) {
|
||||||
|
return 'login';
|
||||||
|
}
|
||||||
|
if (/(^|\/)signup(\/|$)/.test(pathname)) {
|
||||||
|
return 'signup';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
function findBuilderIdButton() {
|
function findBuilderIdButton() {
|
||||||
return findActionButton({
|
return findActionButton({
|
||||||
textPattern: KIRO_BUILDER_ID_TEXT_PATTERN,
|
textPattern: KIRO_BUILDER_ID_TEXT_PATTERN,
|
||||||
@@ -484,6 +533,8 @@ function getKiroFatalStateMessage(snapshot = {}) {
|
|||||||
function detectKiroRegisterPageState() {
|
function detectKiroRegisterPageState() {
|
||||||
const pageText = getKiroPageText();
|
const pageText = getKiroPageText();
|
||||||
const currentUrl = location.href;
|
const currentUrl = location.href;
|
||||||
|
const authPathKind = getKiroAuthPathKind();
|
||||||
|
const pageEmail = findKiroPageAccountEmail(pageText);
|
||||||
const fatalState = detectKiroFatalPageState(pageText, currentUrl, document.title || '');
|
const fatalState = detectKiroFatalPageState(pageText, currentUrl, document.title || '');
|
||||||
if (fatalState) {
|
if (fatalState) {
|
||||||
return fatalState;
|
return fatalState;
|
||||||
@@ -495,6 +546,8 @@ function detectKiroRegisterPageState() {
|
|||||||
return {
|
return {
|
||||||
state: 'kiro_signin_page',
|
state: 'kiro_signin_page',
|
||||||
url: currentUrl,
|
url: currentUrl,
|
||||||
|
email: pageEmail,
|
||||||
|
accountEmail: pageEmail,
|
||||||
actionButton: builderIdButton,
|
actionButton: builderIdButton,
|
||||||
actionText: getElementActionText(builderIdButton),
|
actionText: getElementActionText(builderIdButton),
|
||||||
};
|
};
|
||||||
@@ -513,9 +566,13 @@ function detectKiroRegisterPageState() {
|
|||||||
const passwordFields = findKiroPasswordFields();
|
const passwordFields = findKiroPasswordFields();
|
||||||
if (passwordFields.passwordInput) {
|
if (passwordFields.passwordInput) {
|
||||||
const passwordInput = passwordFields.passwordInput;
|
const passwordInput = passwordFields.passwordInput;
|
||||||
|
const isLoginPasswordPage = authPathKind === 'login'
|
||||||
|
|| (!passwordFields.confirmPasswordInput && KIRO_SIGN_IN_TEXT_PATTERN.test(pageText));
|
||||||
return {
|
return {
|
||||||
state: 'password_page',
|
state: isLoginPasswordPage ? 'login_password_page' : 'create_password_page',
|
||||||
url: currentUrl,
|
url: currentUrl,
|
||||||
|
email: pageEmail,
|
||||||
|
accountEmail: pageEmail,
|
||||||
passwordInput,
|
passwordInput,
|
||||||
confirmPasswordInput: passwordFields.confirmPasswordInput,
|
confirmPasswordInput: passwordFields.confirmPasswordInput,
|
||||||
continueButton: findPasswordContinueButton(passwordInput),
|
continueButton: findPasswordContinueButton(passwordInput),
|
||||||
@@ -528,6 +585,8 @@ function detectKiroRegisterPageState() {
|
|||||||
return {
|
return {
|
||||||
state: 'authorization_page',
|
state: 'authorization_page',
|
||||||
url: currentUrl,
|
url: currentUrl,
|
||||||
|
email: pageEmail,
|
||||||
|
accountEmail: pageEmail,
|
||||||
actionButton: authorizationButton,
|
actionButton: authorizationButton,
|
||||||
authorizationActionKind: getAuthorizationActionKind(authorizationActionText),
|
authorizationActionKind: getAuthorizationActionKind(authorizationActionText),
|
||||||
authorizationActionText,
|
authorizationActionText,
|
||||||
@@ -541,16 +600,22 @@ function detectKiroRegisterPageState() {
|
|||||||
return {
|
return {
|
||||||
state: 'success_page',
|
state: 'success_page',
|
||||||
url: currentUrl,
|
url: currentUrl,
|
||||||
|
email: pageEmail,
|
||||||
|
accountEmail: pageEmail,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const otpInput = findVisibleOtpInput();
|
const otpInput = findVisibleOtpInput();
|
||||||
if (otpInput) {
|
if (otpInput) {
|
||||||
return {
|
return {
|
||||||
state: 'otp_page',
|
state: authPathKind === 'login' ? 'login_otp_page' : 'register_otp_page',
|
||||||
url: currentUrl,
|
url: currentUrl,
|
||||||
|
email: pageEmail,
|
||||||
|
accountEmail: pageEmail,
|
||||||
otpInput,
|
otpInput,
|
||||||
verifyButton: findOtpVerifyButton(otpInput),
|
verifyButton: findOtpVerifyButton(otpInput),
|
||||||
|
resendButton: findOtpResendButton(otpInput),
|
||||||
|
codeInvalid: KIRO_CODE_INVALID_TEXT_PATTERN.test(pageText),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -559,6 +624,8 @@ function detectKiroRegisterPageState() {
|
|||||||
return {
|
return {
|
||||||
state: 'name_entry',
|
state: 'name_entry',
|
||||||
url: currentUrl,
|
url: currentUrl,
|
||||||
|
email: pageEmail,
|
||||||
|
accountEmail: pageEmail,
|
||||||
nameInput,
|
nameInput,
|
||||||
continueButton: findNameContinueButton(nameInput),
|
continueButton: findNameContinueButton(nameInput),
|
||||||
};
|
};
|
||||||
@@ -569,6 +636,8 @@ function detectKiroRegisterPageState() {
|
|||||||
return {
|
return {
|
||||||
state: 'email_entry',
|
state: 'email_entry',
|
||||||
url: currentUrl,
|
url: currentUrl,
|
||||||
|
email: pageEmail,
|
||||||
|
accountEmail: pageEmail,
|
||||||
emailInput,
|
emailInput,
|
||||||
continueButton: findEmailContinueButton(emailInput),
|
continueButton: findEmailContinueButton(emailInput),
|
||||||
};
|
};
|
||||||
@@ -577,6 +646,8 @@ function detectKiroRegisterPageState() {
|
|||||||
return {
|
return {
|
||||||
state: 'loading',
|
state: 'loading',
|
||||||
url: currentUrl,
|
url: currentUrl,
|
||||||
|
email: pageEmail,
|
||||||
|
accountEmail: pageEmail,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -631,7 +702,15 @@ async function waitForKiroRegisterStateChange(payload = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return waitForKiroState(
|
return waitForKiroState(
|
||||||
(detected) => detected.state !== 'loading' && !fromStates.includes(detected.state),
|
(detected) => {
|
||||||
|
if (detected.state === 'loading') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (payload?.returnOnCodeInvalid && detected.state === 'register_otp_page' && detected.codeInvalid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !fromStates.includes(detected.state);
|
||||||
|
},
|
||||||
{
|
{
|
||||||
timeoutMs: payload?.timeoutMs,
|
timeoutMs: payload?.timeoutMs,
|
||||||
retryDelayMs: payload?.retryDelayMs,
|
retryDelayMs: payload?.retryDelayMs,
|
||||||
@@ -741,7 +820,7 @@ async function submitKiroVerificationCode(payload = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const readyState = await ensureKiroRegisterPageState({
|
const readyState = await ensureKiroRegisterPageState({
|
||||||
targetStates: ['otp_page'],
|
targetStates: ['register_otp_page'],
|
||||||
timeoutMs: payload?.timeoutMs || DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS,
|
timeoutMs: payload?.timeoutMs || DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS,
|
||||||
retryDelayMs: payload?.retryDelayMs || 250,
|
retryDelayMs: payload?.retryDelayMs || 250,
|
||||||
});
|
});
|
||||||
@@ -766,7 +845,7 @@ async function submitKiroPassword(payload = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const readyState = await ensureKiroRegisterPageState({
|
const readyState = await ensureKiroRegisterPageState({
|
||||||
targetStates: ['password_page'],
|
targetStates: ['create_password_page'],
|
||||||
timeoutMs: payload?.timeoutMs || DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS,
|
timeoutMs: payload?.timeoutMs || DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS,
|
||||||
retryDelayMs: payload?.retryDelayMs || 250,
|
retryDelayMs: payload?.retryDelayMs || 250,
|
||||||
});
|
});
|
||||||
@@ -860,6 +939,7 @@ async function handleKiroRegisterCommand(message) {
|
|||||||
authorizationActionKind: detected?.authorizationActionKind || '',
|
authorizationActionKind: detected?.authorizationActionKind || '',
|
||||||
authorizationActionText: detected?.authorizationActionText || '',
|
authorizationActionText: detected?.authorizationActionText || '',
|
||||||
actionText: detected?.actionText || '',
|
actionText: detected?.actionText || '',
|
||||||
|
codeInvalid: Boolean(detected?.codeInvalid),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'ENSURE_KIRO_PAGE_STATE':
|
case 'ENSURE_KIRO_PAGE_STATE':
|
||||||
|
|||||||
@@ -35,7 +35,136 @@ test('kiro register runner uses a shared 3-minute page-load timeout budget', ()
|
|||||||
|
|
||||||
test('kiro register consent step treats Kiro Web signed-in page as completion', () => {
|
test('kiro register consent step treats Kiro Web signed-in page as completion', () => {
|
||||||
const source = fs.readFileSync('background/kiro/register-runner.js', 'utf8');
|
const source = fs.readFileSync('background/kiro/register-runner.js', 'utf8');
|
||||||
assert.match(source, /targetStates: \['authorization_page', 'kiro_web_signed_in'\]/);
|
assert.match(source, /readKiroRegisterPageState\(tabId, \{/);
|
||||||
assert.match(source, /landingResult\?\.state !== 'kiro_web_signed_in'/);
|
assert.match(source, /\['authorization_page', 'success_page', 'kiro_web_signed_in'\]\.includes\(landingResult\?\.state\)/);
|
||||||
|
assert.match(source, /landingResult\?\.state === 'authorization_page'/);
|
||||||
assert.doesNotMatch(source, /landingResult\?\.state !== 'success_page'/);
|
assert.doesNotMatch(source, /landingResult\?\.state !== 'success_page'/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('kiro register runner uses registration-only page states instead of shared OpenAI names', () => {
|
||||||
|
const source = fs.readFileSync('background/kiro/register-runner.js', 'utf8');
|
||||||
|
assert.match(source, /KIRO_REGISTER_PAGE_STATES/);
|
||||||
|
assert.match(source, /'register_otp_page'/);
|
||||||
|
assert.match(source, /'create_password_page'/);
|
||||||
|
assert.match(source, /'login_password_page'/);
|
||||||
|
assert.match(source, /'login_otp_page'/);
|
||||||
|
assert.doesNotMatch(source, /targetStates: \['otp_page'\]/);
|
||||||
|
assert.doesNotMatch(source, /targetStates: \['password_page'\]/);
|
||||||
|
assert.doesNotMatch(source, /fromStates: \['password_page'\]/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('kiro register runner fails existing-account login branches during registration', () => {
|
||||||
|
const source = fs.readFileSync('background/kiro/register-runner.js', 'utf8');
|
||||||
|
assert.match(source, /KIRO_REGISTER_EXISTING_ACCOUNT_STATES/);
|
||||||
|
assert.match(source, /assertKiroRegistrationOnlyState\(landingResult, currentState, 2, resolvedEmail\)/);
|
||||||
|
assert.match(source, /邮箱.*已进入 AWS Builder ID 登录页/);
|
||||||
|
assert.match(source, /Kiro 注册流程只处理新账号注册/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('kiro submit-email stops immediately when AWS routes the email to login', async () => {
|
||||||
|
const api = loadRegisterRunnerApi();
|
||||||
|
const currentState = {
|
||||||
|
kiroRuntime: {
|
||||||
|
session: {
|
||||||
|
registerTabId: 101,
|
||||||
|
},
|
||||||
|
register: {
|
||||||
|
loginUrl: 'https://app.kiro.dev/signin',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const sentMessages = [];
|
||||||
|
const statePatches = [];
|
||||||
|
let completed = false;
|
||||||
|
const runner = api.createKiroRegisterRunner({
|
||||||
|
addLog: async () => {},
|
||||||
|
chrome: {
|
||||||
|
tabs: {
|
||||||
|
update: async () => {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
completeNodeFromBackground: async () => {
|
||||||
|
completed = true;
|
||||||
|
},
|
||||||
|
getState: async () => currentState,
|
||||||
|
getTabId: async () => 101,
|
||||||
|
isTabAlive: async () => true,
|
||||||
|
resolveSignupEmailForFlow: async () => 'existing-user@duck.com',
|
||||||
|
sendToContentScriptResilient: async (_sourceId, message) => {
|
||||||
|
sentMessages.push(message);
|
||||||
|
if (message.type === 'ENSURE_KIRO_PAGE_STATE') {
|
||||||
|
return { state: 'email_entry', url: 'https://us-east-1.signin.aws/platform/d/signup' };
|
||||||
|
}
|
||||||
|
if (message.type === 'EXECUTE_NODE') {
|
||||||
|
return { submitted: true, state: 'email_submitted' };
|
||||||
|
}
|
||||||
|
if (message.type === 'ENSURE_KIRO_STATE_CHANGE') {
|
||||||
|
return {
|
||||||
|
state: 'login_password_page',
|
||||||
|
url: 'https://us-east-1.signin.aws/platform/d/login',
|
||||||
|
email: 'existing-user@duck.com',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
setState: async (patch) => {
|
||||||
|
statePatches.push(patch);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
() => runner.executeKiroSubmitEmail({ nodeId: 'kiro-submit-email', ...currentState }),
|
||||||
|
/existing-user@duck\.com.*已进入 AWS Builder ID 登录页/
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(completed, false);
|
||||||
|
assert.equal(sentMessages.some((message) => message.type === 'EXECUTE_NODE'), true);
|
||||||
|
assert.equal(statePatches.some((patch) => /已进入 AWS Builder ID 登录页/.test(patch.kiroRuntime?.session?.lastError || '')), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('kiro submit-email can adopt an already-open registration OTP page without allocating a new mailbox', async () => {
|
||||||
|
const api = loadRegisterRunnerApi();
|
||||||
|
const currentState = {
|
||||||
|
kiroRuntime: {
|
||||||
|
session: {
|
||||||
|
registerTabId: 102,
|
||||||
|
},
|
||||||
|
register: {
|
||||||
|
loginUrl: 'https://app.kiro.dev/signin',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const sentMessages = [];
|
||||||
|
let completedPayload = null;
|
||||||
|
const runner = api.createKiroRegisterRunner({
|
||||||
|
addLog: async () => {},
|
||||||
|
chrome: {
|
||||||
|
tabs: {
|
||||||
|
update: async () => {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
completeNodeFromBackground: async (_nodeId, payload) => {
|
||||||
|
completedPayload = payload;
|
||||||
|
},
|
||||||
|
getState: async () => currentState,
|
||||||
|
getTabId: async () => 102,
|
||||||
|
isTabAlive: async () => true,
|
||||||
|
sendToContentScriptResilient: async (_sourceId, message) => {
|
||||||
|
sentMessages.push(message);
|
||||||
|
assert.equal(message.type, 'ENSURE_KIRO_PAGE_STATE');
|
||||||
|
return {
|
||||||
|
state: 'register_otp_page',
|
||||||
|
url: 'https://us-east-1.signin.aws/platform/d/signup',
|
||||||
|
email: 'manual-user@duck.com',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
setState: async () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
await runner.executeKiroSubmitEmail({ nodeId: 'kiro-submit-email', ...currentState });
|
||||||
|
|
||||||
|
assert.equal(completedPayload?.kiroRuntime?.register?.email, 'manual-user@duck.com');
|
||||||
|
assert.equal(completedPayload?.kiroRuntime?.register?.status, 'waiting_otp');
|
||||||
|
assert.equal(completedPayload?.kiroRuntime?.register?.verificationRequestedAt, 0);
|
||||||
|
assert.equal(sentMessages.some((message) => message.type === 'EXECUTE_NODE'), false);
|
||||||
|
});
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ test('kiro state session patch accepts canonical nested runtime updates', () =>
|
|||||||
kiroRuntime: {
|
kiroRuntime: {
|
||||||
session: {
|
session: {
|
||||||
currentStage: 'register',
|
currentStage: 'register',
|
||||||
pageState: 'otp_page',
|
pageState: 'register_otp_page',
|
||||||
pageUrl: 'https://signin.aws/register',
|
pageUrl: 'https://signin.aws/register',
|
||||||
},
|
},
|
||||||
register: {
|
register: {
|
||||||
@@ -92,7 +92,7 @@ test('kiro state session patch accepts canonical nested runtime updates', () =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(patch.kiroRuntime.session.currentStage, 'register');
|
assert.equal(patch.kiroRuntime.session.currentStage, 'register');
|
||||||
assert.equal(patch.kiroRuntime.session.pageState, 'otp_page');
|
assert.equal(patch.kiroRuntime.session.pageState, 'register_otp_page');
|
||||||
assert.equal(patch.kiroRuntime.session.pageUrl, 'https://signin.aws/register');
|
assert.equal(patch.kiroRuntime.session.pageUrl, 'https://signin.aws/register');
|
||||||
assert.equal(patch.kiroRuntime.register.email, 'aws-user@example.com');
|
assert.equal(patch.kiroRuntime.register.email, 'aws-user@example.com');
|
||||||
assert.equal(patch.kiroRuntime.register.fullName, 'Ada Lovelace');
|
assert.equal(patch.kiroRuntime.register.fullName, 'Ada Lovelace');
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ function createDomHarness({
|
|||||||
}
|
}
|
||||||
const context = {
|
const context = {
|
||||||
console: { log() {}, warn() {}, error() {}, info() {} },
|
console: { log() {}, warn() {}, error() {}, info() {} },
|
||||||
|
URL,
|
||||||
location: { href, hostname },
|
location: { href, hostname },
|
||||||
document: {
|
document: {
|
||||||
title,
|
title,
|
||||||
@@ -109,6 +110,10 @@ function createDomHarness({
|
|||||||
if (String(selector).startsWith('label[for=')) {
|
if (String(selector).startsWith('label[for=')) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
const testIdMatch = String(selector).match(/\[data-testid="([^"]+)"\]/);
|
||||||
|
if (testIdMatch) {
|
||||||
|
return [...inputs, ...buttons].filter((element) => element.getAttribute?.('data-testid') === testIdMatch[1]);
|
||||||
|
}
|
||||||
if (String(selector).trim().startsWith('input')) {
|
if (String(selector).trim().startsWith('input')) {
|
||||||
return inputs;
|
return inputs;
|
||||||
}
|
}
|
||||||
@@ -234,7 +239,7 @@ test('kiro register content fills primary and confirm password fields separately
|
|||||||
harness.simulateClick = (element) => clicks.push(element);
|
harness.simulateClick = (element) => clicks.push(element);
|
||||||
|
|
||||||
const detected = harness.detectKiroRegisterPageState();
|
const detected = harness.detectKiroRegisterPageState();
|
||||||
assert.equal(detected.state, 'password_page');
|
assert.equal(detected.state, 'create_password_page');
|
||||||
assert.equal(detected.passwordInput, passwordInput);
|
assert.equal(detected.passwordInput, passwordInput);
|
||||||
assert.equal(detected.confirmPasswordInput, confirmPasswordInput);
|
assert.equal(detected.confirmPasswordInput, confirmPasswordInput);
|
||||||
|
|
||||||
@@ -245,3 +250,68 @@ test('kiro register content fills primary and confirm password fields separately
|
|||||||
assert.equal(confirmPasswordInput.value, 'mdy8U9_rzqhw6D');
|
assert.equal(confirmPasswordInput.value, 'mdy8U9_rzqhw6D');
|
||||||
assert.deepEqual(clicks, [continueButton]);
|
assert.deepEqual(clicks, [continueButton]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('kiro register content classifies AWS login password as an existing-account branch', () => {
|
||||||
|
const passwordInput = createInputElement({
|
||||||
|
id: 'formField15-1779237828927-4809',
|
||||||
|
type: 'text',
|
||||||
|
placeholder: 'Enter password',
|
||||||
|
label: '\u5bc6\u7801',
|
||||||
|
});
|
||||||
|
const continueButton = createButtonElement({ text: '\u7ee7\u7eed' });
|
||||||
|
|
||||||
|
const harness = createDomHarness({
|
||||||
|
href: 'https://us-east-1.signin.aws/platform/d-9067642ac7/login?workflowStateHandle=abc',
|
||||||
|
hostname: 'us-east-1.signin.aws',
|
||||||
|
title: 'Amazon Web Services',
|
||||||
|
bodyText: 'Sign in with your AWS Builder ID Email much-glance-avert@duck.com Change',
|
||||||
|
inputs: [passwordInput],
|
||||||
|
buttons: [continueButton],
|
||||||
|
});
|
||||||
|
|
||||||
|
const detected = harness.detectKiroRegisterPageState();
|
||||||
|
|
||||||
|
assert.equal(detected.state, 'login_password_page');
|
||||||
|
assert.equal(detected.email, 'much-glance-avert@duck.com');
|
||||||
|
assert.equal(detected.passwordInput, passwordInput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('kiro register content classifies signup verification separately from login verification', () => {
|
||||||
|
const registerOtpInput = createInputElement({
|
||||||
|
id: 'formField38-1779237828927-4809',
|
||||||
|
type: 'text',
|
||||||
|
placeholder: '6-digit',
|
||||||
|
label: '\u9a8c\u8bc1\u7801',
|
||||||
|
});
|
||||||
|
const loginOtpInput = createInputElement({
|
||||||
|
id: 'formField38-1779237828927-4810',
|
||||||
|
type: 'text',
|
||||||
|
placeholder: '6-digit',
|
||||||
|
label: '\u9a8c\u8bc1\u7801',
|
||||||
|
});
|
||||||
|
|
||||||
|
const registerHarness = createDomHarness({
|
||||||
|
href: 'https://us-east-1.signin.aws/platform/d-9067642ac7/signup?state=abc',
|
||||||
|
hostname: 'us-east-1.signin.aws',
|
||||||
|
title: 'Amazon Web Services',
|
||||||
|
bodyText: 'Verify your identity Email new-user@duck.com',
|
||||||
|
inputs: [registerOtpInput],
|
||||||
|
buttons: [createButtonElement()],
|
||||||
|
});
|
||||||
|
const loginHarness = createDomHarness({
|
||||||
|
href: 'https://us-east-1.signin.aws/platform/d-9067642ac7/login?workflowStateHandle=abc',
|
||||||
|
hostname: 'us-east-1.signin.aws',
|
||||||
|
title: 'Amazon Web Services',
|
||||||
|
bodyText: 'Verify your identity Email existing-user@duck.com',
|
||||||
|
inputs: [loginOtpInput],
|
||||||
|
buttons: [createButtonElement()],
|
||||||
|
});
|
||||||
|
|
||||||
|
const registerDetected = registerHarness.detectKiroRegisterPageState();
|
||||||
|
const loginDetected = loginHarness.detectKiroRegisterPageState();
|
||||||
|
|
||||||
|
assert.equal(registerDetected.state, 'register_otp_page');
|
||||||
|
assert.equal(registerDetected.email, 'new-user@duck.com');
|
||||||
|
assert.equal(loginDetected.state, 'login_otp_page');
|
||||||
|
assert.equal(loginDetected.email, 'existing-user@duck.com');
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user