From 98e7653360f3ff9a5a62997cd6ab46b67b4c04fb Mon Sep 17 00:00:00 2001 From: Q3CC Date: Sun, 26 Apr 2026 14:21:52 +0800 Subject: [PATCH] fix: clean up iCloud forward mailbox provider tech debt --- README.md | 4 +- background.js | 44 +-- mail-provider-utils.js | 47 +++ sidepanel/sidepanel.html | 1 + sidepanel/sidepanel.js | 65 ++-- tests/background-icloud-mail-provider.test.js | 194 +++--------- tests/background-icloud.test.js | 13 +- tests/mail-provider-utils.test.js | 30 ++ ...dflare-temp-email-random-subdomain.test.js | 8 +- tests/sidepanel-contribution-mode.test.js | 8 +- tests/sidepanel-icloud-provider.test.js | 294 ++++++++++++++++++ tests/sidepanel-mail2925-base-email.test.js | 8 +- ...epanel-phone-verification-settings.test.js | 8 +- 项目完整链路说明.md | 17 +- 项目文件结构说明.md | 12 +- 15 files changed, 512 insertions(+), 241 deletions(-) diff --git a/README.md b/README.md index 7cf5f51..af19001 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ - 至少准备一种验证码接收方式: - DuckDuckGo `@duck.com` + QQ / 163 / Inbucket 转发 - Cloudflare 自定义域邮箱前缀 + QQ / 163 / Inbucket 转发 + - iCloud Hide My Email,可选择直接从 iCloud 收件箱收码,或转发到 QQ / 163 / 163 VIP / 126 / Gmail 后收码 - 手动填写一个可收信邮箱 - 如果使用 `QQ` / `163` / `163 VIP` / `126` / `Inbucket`,对应页面需要提前能正常打开 @@ -592,6 +593,7 @@ Cloudflare 模式下,插件不会再调用 Cloudflare API 创建路由。 支持: - `Hotmail`(远程服务 / 本地助手) +- `iCloud` 收件箱,或 iCloud Hide My Email 转发到 QQ / 163 / 163 VIP / 126 / Gmail 后收码 - `content/qq-mail.js` - `content/mail-163.js`(163 / 163 VIP / 126) - `content/inbucket-mail.js` @@ -773,7 +775,7 @@ content/utils.js 通用工具:等待元素、点击、日志、停 content/vps-panel.js CPA 面板步骤:内部 OAuth 刷新 / Step 10 content/signup-page.js ChatGPT 官网 + OpenAI 注册/登录页步骤:Step 1 / 2 / 3 / 5 / 7 / 9 hotmail-utils.js Hotmail 收信相关通用辅助 -mail-provider-utils.js 网页邮箱 provider 配置辅助 +mail-provider-utils.js 网页邮箱 provider 与 iCloud 转发收码配置辅助 content/duck-mail.js Duck 邮箱自动获取 content/qq-mail.js QQ 邮箱验证码轮询 content/mail-163.js 163 / 163 VIP / 126 邮箱验证码轮询 diff --git a/background.js b/background.js index 997b841..f12d704 100644 --- a/background.js +++ b/background.js @@ -34,6 +34,7 @@ importScripts( 'luckmail-utils.js', 'cloudflare-temp-email-utils.js', 'icloud-utils.js', + 'mail-provider-utils.js', 'content/activation-utils.js' ); @@ -124,6 +125,11 @@ const { pickReusableIcloudAlias, toNormalizedEmailSet, } = self.IcloudUtils; +const { + getIcloudForwardMailConfig: getSharedIcloudForwardMailConfig, + normalizeIcloudForwardMailProvider, + normalizeIcloudTargetMailboxType, +} = self.MailProviderUtils; const { isRecoverableStep9AuthFailure, } = self.MultiPageActivationUtils; @@ -694,19 +700,6 @@ function normalizeIcloudFetchMode(value = '') { return normalized === 'always_new' ? 'always_new' : 'reuse_existing'; } -function normalizeIcloudTargetMailboxType(value = '') { - return String(value || '').trim().toLowerCase() === 'forward-mailbox' - ? 'forward-mailbox' - : 'icloud-inbox'; -} - -function normalizeIcloudForwardMailProvider(value = '') { - const normalized = String(value || '').trim().toLowerCase(); - return ['qq', '163', '163-vip', '126', 'gmail'].includes(normalized) - ? normalized - : 'qq'; -} - function normalizeCustomEmailPool(value = []) { const source = Array.isArray(value) ? value @@ -6894,29 +6887,6 @@ async function executeStep3(state) { // Step 4: Get Signup Verification Code (qq-mail.js polls, then fills in signup-page.js) // ============================================================ -function getIcloudForwardMailConfig(state = {}, provider = 'qq') { - const normalizedProvider = normalizeIcloudForwardMailProvider(provider); - if (normalizedProvider === GMAIL_PROVIDER) { - return { - source: 'gmail-mail', - url: 'https://mail.google.com/mail/u/0/#inbox', - label: 'Gmail 邮箱', - inject: ['content/activation-utils.js', 'content/utils.js', 'content/gmail-mail.js'], - injectSource: 'gmail-mail', - }; - } - if (normalizedProvider === '163') { - return { source: 'mail-163', url: 'https://mail.163.com/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D', label: '163 邮箱' }; - } - if (normalizedProvider === '163-vip') { - return { source: 'mail-163', url: 'https://webmail.vip.163.com/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D', label: '163 VIP 邮箱' }; - } - if (normalizedProvider === '126') { - return { source: 'mail-163', url: 'https://mail.126.com/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D', label: '126 邮箱' }; - } - return { source: 'qq-mail', url: 'https://wx.mail.qq.com/', label: 'QQ 邮箱' }; -} - function getMailConfig(state) { const provider = state.mailProvider || 'qq'; if (provider === 'custom') { @@ -6933,7 +6903,7 @@ function getMailConfig(state) { const useForwardMailbox = targetMailboxType === 'forward-mailbox'; if (useForwardMailbox) { const forwardProvider = normalizeIcloudForwardMailProvider(state?.icloudForwardMailProvider); - const forwardConfig = getIcloudForwardMailConfig(state, forwardProvider); + const forwardConfig = getSharedIcloudForwardMailConfig(forwardProvider); return { ...forwardConfig, label: `iCloud 转发(${forwardConfig.label})`, diff --git a/mail-provider-utils.js b/mail-provider-utils.js index cccb000..6bd3960 100644 --- a/mail-provider-utils.js +++ b/mail-provider-utils.js @@ -1,5 +1,15 @@ const HOTMAIL_PROVIDER = 'hotmail-api'; +const GMAIL_PROVIDER = 'gmail'; const NETEASE_LIST_PATH = '/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D'; +const ICLOUD_TARGET_MAILBOX_TYPE_INBOX = 'icloud-inbox'; +const ICLOUD_TARGET_MAILBOX_TYPE_FORWARD = 'forward-mailbox'; +const ICLOUD_FORWARD_MAIL_PROVIDER_OPTIONS = [ + { value: 'qq', label: 'QQ 邮箱' }, + { value: '163', label: '163 邮箱' }, + { value: '163-vip', label: '163 VIP 邮箱' }, + { value: '126', label: '126 邮箱' }, + { value: GMAIL_PROVIDER, label: 'Gmail 邮箱' }, +]; function normalizeMailProvider(value = '') { const normalized = String(value || '').trim().toLowerCase(); @@ -16,6 +26,38 @@ function normalizeMailProvider(value = '') { } } +function normalizeIcloudTargetMailboxType(value = '') { + return String(value || '').trim().toLowerCase() === ICLOUD_TARGET_MAILBOX_TYPE_FORWARD + ? ICLOUD_TARGET_MAILBOX_TYPE_FORWARD + : ICLOUD_TARGET_MAILBOX_TYPE_INBOX; +} + +function normalizeIcloudForwardMailProvider(value = '') { + const normalized = String(value || '').trim().toLowerCase(); + return ICLOUD_FORWARD_MAIL_PROVIDER_OPTIONS.some((option) => option.value === normalized) + ? normalized + : 'qq'; +} + +function getIcloudForwardMailProviderOptions() { + return ICLOUD_FORWARD_MAIL_PROVIDER_OPTIONS.map((option) => ({ ...option })); +} + +function getIcloudForwardMailConfig(provider = 'qq') { + const normalizedProvider = normalizeIcloudForwardMailProvider(provider); + if (normalizedProvider === GMAIL_PROVIDER) { + return { + source: 'gmail-mail', + url: 'https://mail.google.com/mail/u/0/#inbox', + label: 'Gmail 邮箱', + inject: ['content/activation-utils.js', 'content/utils.js', 'content/gmail-mail.js'], + injectSource: 'gmail-mail', + }; + } + + return getMailProviderConfig({ mailProvider: normalizedProvider }); +} + function getMailProviderConfig(state = {}, options = {}) { const provider = normalizeMailProvider(state.mailProvider); const normalizeInbucketOrigin = options.normalizeInbucketOrigin || (() => ''); @@ -66,8 +108,13 @@ function getMailProviderConfig(state = {}, options = {}) { } const api = { + GMAIL_PROVIDER, HOTMAIL_PROVIDER, + getIcloudForwardMailConfig, + getIcloudForwardMailProviderOptions, getMailProviderConfig, + normalizeIcloudForwardMailProvider, + normalizeIcloudTargetMailboxType, normalizeMailProvider, }; diff --git a/sidepanel/sidepanel.html b/sidepanel/sidepanel.html index 8e2e478..e522de6 100644 --- a/sidepanel/sidepanel.html +++ b/sidepanel/sidepanel.html @@ -834,6 +834,7 @@ + diff --git a/sidepanel/sidepanel.js b/sidepanel/sidepanel.js index 81c427b..514f1e1 100644 --- a/sidepanel/sidepanel.js +++ b/sidepanel/sidepanel.js @@ -582,17 +582,28 @@ const normalizeIcloudFetchMode = (value) => { const normalized = String(value || '').trim().toLowerCase(); return normalized === 'always_new' ? 'always_new' : 'reuse_existing'; }; -const normalizeIcloudTargetMailboxType = (value) => { - return String(value || '').trim().toLowerCase() === 'forward-mailbox' +const normalizeIcloudTargetMailboxType = window.MailProviderUtils?.normalizeIcloudTargetMailboxType + || ((value) => String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' - : 'icloud-inbox'; -}; -const normalizeIcloudForwardMailProvider = (value) => { - const normalized = String(value || '').trim().toLowerCase(); - return ['qq', '163', '163-vip', '126', 'gmail'].includes(normalized) - ? normalized - : 'qq'; -}; + : 'icloud-inbox'); +const getIcloudForwardMailProviderOptions = window.MailProviderUtils?.getIcloudForwardMailProviderOptions + || (() => Array.from(selectIcloudForwardMailProvider?.options || []) + .map((option) => ({ + value: String(option?.value || '').trim().toLowerCase(), + label: String(option?.textContent || option?.label || option?.value || '').trim(), + })) + .filter((option) => option.value)); +const normalizeIcloudForwardMailProvider = window.MailProviderUtils?.normalizeIcloudForwardMailProvider + || ((value) => { + const normalized = String(value || '').trim().toLowerCase(); + const options = getIcloudForwardMailProviderOptions(); + return options.some((option) => option.value === normalized) + ? normalized + : (options[0]?.value || 'qq'); + }); +const ICLOUD_FORWARD_MAIL_PROVIDER_LABELS = Object.fromEntries( + getIcloudForwardMailProviderOptions().map((option) => [option.value, option.label]) +); const getIcloudLoginUrlForHost = window.IcloudUtils?.getIcloudLoginUrlForHost || ((host) => host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : (host === 'icloud.com' ? 'https://www.icloud.com/' : '')); @@ -1687,16 +1698,8 @@ function collectSettingsPayload() { const icloudForwardMailProviderValue = typeof selectIcloudForwardMailProvider !== 'undefined' ? selectIcloudForwardMailProvider?.value : ''; - const normalizedIcloudTargetMailboxType = typeof normalizeIcloudTargetMailboxType === 'function' - ? normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue) - : (String(icloudTargetMailboxTypeValue || '').trim().toLowerCase() === 'forward-mailbox' - ? 'forward-mailbox' - : 'icloud-inbox'); - const normalizedIcloudForwardMailProvider = typeof normalizeIcloudForwardMailProvider === 'function' - ? normalizeIcloudForwardMailProvider(icloudForwardMailProviderValue) - : (['qq', '163', '163-vip', '126', 'gmail'].includes(String(icloudForwardMailProviderValue || '').trim().toLowerCase()) - ? String(icloudForwardMailProviderValue || '').trim().toLowerCase() - : 'qq'); + const normalizedIcloudTargetMailboxType = normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue); + const normalizedIcloudForwardMailProvider = normalizeIcloudForwardMailProvider(icloudForwardMailProviderValue); const mail2925UseAccountPool = typeof inputMail2925UseAccountPool !== 'undefined' ? Boolean(inputMail2925UseAccountPool?.checked) : Boolean(latestState?.mail2925UseAccountPool); @@ -3229,11 +3232,7 @@ function updateMailProviderUI() { : (normalizeIcloudHostValue(icloudHostPreferenceValue || latestState?.icloudHostPreference || '') || normalizeIcloudHostValue(latestState?.preferredIcloudHost) || 'icloud.com'); - const icloudTargetMailboxType = typeof normalizeIcloudTargetMailboxType === 'function' - ? normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue) - : (String(icloudTargetMailboxTypeValue || '').trim().toLowerCase() === 'forward-mailbox' - ? 'forward-mailbox' - : 'icloud-inbox'); + const icloudTargetMailboxType = normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue); const isIcloudComCnHost = selectedIcloudHost === 'icloud.com.cn'; const showIcloudTargetMailboxType = useIcloudProvider; const showIcloudForwardMailProvider = useIcloudProvider && icloudTargetMailboxType === 'forward-mailbox'; @@ -3380,12 +3379,10 @@ function updateMailProviderUI() { autoHintText.textContent = '已启用随机子域名:扩展会按当前选中的 Temp 域名提交,并额外携带 enableRandomSubdomain;是否生效取决于后端 RANDOM_SUBDOMAIN_DOMAINS 配置。'; } if (autoHintText && useIcloudProvider && showIcloudForwardMailProvider) { - const forwardProvider = typeof normalizeIcloudForwardMailProvider === 'function' - ? normalizeIcloudForwardMailProvider(icloudForwardMailProviderValue) - : (['qq', '163', '163-vip', '126', 'gmail'].includes(String(icloudForwardMailProviderValue || '').trim().toLowerCase()) - ? String(icloudForwardMailProviderValue || '').trim().toLowerCase() - : 'qq'); - const forwardProviderLabel = MAIL_PROVIDER_LOGIN_CONFIGS[forwardProvider]?.label || '目标邮箱'; + const forwardProvider = normalizeIcloudForwardMailProvider(icloudForwardMailProviderValue); + const forwardProviderLabel = ICLOUD_FORWARD_MAIL_PROVIDER_LABELS[forwardProvider] + || MAIL_PROVIDER_LOGIN_CONFIGS[forwardProvider]?.label + || '目标邮箱'; autoHintText.textContent = `iCloud ${isIcloudComCnHost ? 'com.cn' : ''} 当前使用转发收码:第 4/8 步会从 ${forwardProviderLabel} 轮询验证码。`; } if (useHotmail) { @@ -3589,11 +3586,7 @@ function updateButtonStates() { selectIcloudTargetMailboxType.disabled = disableIcloudControls; } if (typeof selectIcloudForwardMailProvider !== 'undefined' && selectIcloudForwardMailProvider) { - const normalizedIcloudTargetMailboxType = typeof normalizeIcloudTargetMailboxType === 'function' - ? normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue) - : (String(icloudTargetMailboxTypeValue || '').trim().toLowerCase() === 'forward-mailbox' - ? 'forward-mailbox' - : 'icloud-inbox'); + const normalizedIcloudTargetMailboxType = normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue); const allowIcloudForwardMailProvider = isIcloudMailProvider() && normalizedIcloudTargetMailboxType === 'forward-mailbox'; selectIcloudForwardMailProvider.disabled = disableIcloudControls || !allowIcloudForwardMailProvider; diff --git a/tests/background-icloud-mail-provider.test.js b/tests/background-icloud-mail-provider.test.js index bd967ab..1e26b30 100644 --- a/tests/background-icloud-mail-provider.test.js +++ b/tests/background-icloud-mail-provider.test.js @@ -1,6 +1,11 @@ const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); +const { + getIcloudForwardMailConfig, + normalizeIcloudForwardMailProvider, + normalizeIcloudTargetMailboxType, +} = require('../mail-provider-utils.js'); const source = fs.readFileSync('background.js', 'utf8'); @@ -51,6 +56,41 @@ function extractFunction(name) { return source.slice(start, end); } +function createGetMailConfigApi() { + const bundle = extractFunction('getMailConfig'); + return new Function('shared', ` +const ICLOUD_PROVIDER = 'icloud'; +const GMAIL_PROVIDER = 'gmail'; +const HOTMAIL_PROVIDER = 'hotmail-api'; +const LUCKMAIL_PROVIDER = 'luckmail-api'; +const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email'; +const getSharedIcloudForwardMailConfig = shared.getIcloudForwardMailConfig; +const normalizeIcloudTargetMailboxType = shared.normalizeIcloudTargetMailboxType; +const normalizeIcloudForwardMailProvider = shared.normalizeIcloudForwardMailProvider; +function normalizeIcloudHost(value = '') { + const normalized = String(value || '').trim().toLowerCase(); + return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : ''; +} +function normalizeInbucketOrigin(value) { return String(value || '').trim(); } +function getConfiguredIcloudHostPreference(state) { + const normalized = String(state?.icloudHostPreference || '').trim().toLowerCase(); + return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : ''; +} +function getIcloudLoginUrlForHost(host) { + return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : 'https://www.icloud.com/'; +} +function getIcloudMailUrlForHost(host) { + return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/mail/' : 'https://www.icloud.com/mail/'; +} +${bundle} +return { getMailConfig }; +`)({ + getIcloudForwardMailConfig, + normalizeIcloudForwardMailProvider, + normalizeIcloudTargetMailboxType, + }); +} + test('normalizeMailProvider keeps icloud provider', () => { const bundle = extractFunction('normalizeMailProvider'); const api = new Function(` @@ -69,38 +109,7 @@ return { normalizeMailProvider }; }); test('getMailConfig returns icloud mail tab config with host preference', () => { - const bundle = `${extractFunction('getIcloudForwardMailConfig')}\n${extractFunction('getMailConfig')}`; - const api = new Function(` -const ICLOUD_PROVIDER = 'icloud'; -const GMAIL_PROVIDER = 'gmail'; -const HOTMAIL_PROVIDER = 'hotmail-api'; -const LUCKMAIL_PROVIDER = 'luckmail-api'; -const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email'; -function normalizeIcloudHost(value = '') { - const normalized = String(value || '').trim().toLowerCase(); - return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : ''; -} -function normalizeInbucketOrigin(value) { return String(value || '').trim(); } -function getConfiguredIcloudHostPreference(state) { - const normalized = String(state?.icloudHostPreference || '').trim().toLowerCase(); - return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : ''; -} -function getIcloudLoginUrlForHost(host) { - return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : 'https://www.icloud.com/'; -} -function getIcloudMailUrlForHost(host) { - return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/mail/' : 'https://www.icloud.com/mail/'; -} -function normalizeIcloudTargetMailboxType(value = '') { - return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'; -} -function normalizeIcloudForwardMailProvider(value = '') { - const normalized = String(value || '').trim().toLowerCase(); - return ['qq', '163', '163-vip', '126', 'gmail'].includes(normalized) ? normalized : 'qq'; -} -${bundle} -return { getMailConfig }; -`)(); + const api = createGetMailConfigApi(); assert.deepEqual(api.getMailConfig({ mailProvider: 'icloud', @@ -114,37 +123,7 @@ return { getMailConfig }; }); test('getMailConfig reuses preferred icloud host when preference is auto', () => { - const bundle = `${extractFunction('getIcloudForwardMailConfig')}\n${extractFunction('getMailConfig')}`; - const api = new Function(` -const ICLOUD_PROVIDER = 'icloud'; -const GMAIL_PROVIDER = 'gmail'; -const HOTMAIL_PROVIDER = 'hotmail-api'; -const LUCKMAIL_PROVIDER = 'luckmail-api'; -const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email'; -function normalizeIcloudHost(value = '') { - const normalized = String(value || '').trim().toLowerCase(); - return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : ''; -} -function normalizeInbucketOrigin(value) { return String(value || '').trim(); } -function getConfiguredIcloudHostPreference() { - return ''; -} -function getIcloudLoginUrlForHost(host) { - return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : 'https://www.icloud.com/'; -} -function getIcloudMailUrlForHost(host) { - return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/mail/' : 'https://www.icloud.com/mail/'; -} -function normalizeIcloudTargetMailboxType(value = '') { - return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'; -} -function normalizeIcloudForwardMailProvider(value = '') { - const normalized = String(value || '').trim().toLowerCase(); - return ['qq', '163', '163-vip', '126', 'gmail'].includes(normalized) ? normalized : 'qq'; -} -${bundle} -return { getMailConfig }; -`)(); + const api = createGetMailConfigApi(); assert.deepEqual(api.getMailConfig({ mailProvider: 'icloud', @@ -159,28 +138,7 @@ return { getMailConfig }; }); test('getMailConfig keeps provider metadata for 2925 mailboxes', () => { - const bundle = `${extractFunction('getIcloudForwardMailConfig')}\n${extractFunction('getMailConfig')}`; - const api = new Function(` -const ICLOUD_PROVIDER = 'icloud'; -const GMAIL_PROVIDER = 'gmail'; -const HOTMAIL_PROVIDER = 'hotmail-api'; -const LUCKMAIL_PROVIDER = 'luckmail-api'; -const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email'; -function normalizeIcloudHost(value = '') { return String(value || '').trim().toLowerCase(); } -function normalizeInbucketOrigin(value) { return String(value || '').trim(); } -function getConfiguredIcloudHostPreference() { return ''; } -function getIcloudLoginUrlForHost(host) { return host; } -function getIcloudMailUrlForHost(host) { return host; } -function normalizeIcloudTargetMailboxType(value = '') { - return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'; -} -function normalizeIcloudForwardMailProvider(value = '') { - const normalized = String(value || '').trim().toLowerCase(); - return ['qq', '163', '163-vip', '126', 'gmail'].includes(normalized) ? normalized : 'qq'; -} -${bundle} -return { getMailConfig }; -`)(); + const api = createGetMailConfigApi(); assert.deepEqual(api.getMailConfig({ mailProvider: '2925', @@ -195,38 +153,7 @@ return { getMailConfig }; }); test('getMailConfig uses icloud inbox config when host is com.cn and target mailbox is icloud inbox', () => { - const bundle = `${extractFunction('getIcloudForwardMailConfig')}\n${extractFunction('getMailConfig')}`; - const api = new Function(` -const ICLOUD_PROVIDER = 'icloud'; -const GMAIL_PROVIDER = 'gmail'; -const HOTMAIL_PROVIDER = 'hotmail-api'; -const LUCKMAIL_PROVIDER = 'luckmail-api'; -const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email'; -function normalizeIcloudHost(value = '') { - const normalized = String(value || '').trim().toLowerCase(); - return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : ''; -} -function normalizeInbucketOrigin(value) { return String(value || '').trim(); } -function getConfiguredIcloudHostPreference(state) { - const normalized = String(state?.icloudHostPreference || '').trim().toLowerCase(); - return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : ''; -} -function getIcloudLoginUrlForHost(host) { - return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : 'https://www.icloud.com/'; -} -function getIcloudMailUrlForHost(host) { - return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/mail/' : 'https://www.icloud.com/mail/'; -} -function normalizeIcloudTargetMailboxType(value = '') { - return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'; -} -function normalizeIcloudForwardMailProvider(value = '') { - const normalized = String(value || '').trim().toLowerCase(); - return ['qq', '163', '163-vip', '126', 'gmail'].includes(normalized) ? normalized : 'qq'; -} -${bundle} -return { getMailConfig }; -`)(); + const api = createGetMailConfigApi(); assert.deepEqual(api.getMailConfig({ mailProvider: 'icloud', @@ -242,38 +169,7 @@ return { getMailConfig }; }); test('getMailConfig uses forward mailbox config when target mailbox type is forward', () => { - const bundle = `${extractFunction('getIcloudForwardMailConfig')}\n${extractFunction('getMailConfig')}`; - const api = new Function(` -const ICLOUD_PROVIDER = 'icloud'; -const GMAIL_PROVIDER = 'gmail'; -const HOTMAIL_PROVIDER = 'hotmail-api'; -const LUCKMAIL_PROVIDER = 'luckmail-api'; -const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email'; -function normalizeIcloudHost(value = '') { - const normalized = String(value || '').trim().toLowerCase(); - return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : ''; -} -function normalizeInbucketOrigin(value) { return String(value || '').trim(); } -function getConfiguredIcloudHostPreference(state) { - const normalized = String(state?.icloudHostPreference || '').trim().toLowerCase(); - return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : ''; -} -function getIcloudLoginUrlForHost(host) { - return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : 'https://www.icloud.com/'; -} -function getIcloudMailUrlForHost(host) { - return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/mail/' : 'https://www.icloud.com/mail/'; -} -function normalizeIcloudTargetMailboxType(value = '') { - return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'; -} -function normalizeIcloudForwardMailProvider(value = '') { - const normalized = String(value || '').trim().toLowerCase(); - return ['qq', '163', '163-vip', '126', 'gmail'].includes(normalized) ? normalized : 'qq'; -} -${bundle} -return { getMailConfig }; -`)(); + const api = createGetMailConfigApi(); assert.deepEqual(api.getMailConfig({ mailProvider: 'icloud', diff --git a/tests/background-icloud.test.js b/tests/background-icloud.test.js index e4ead04..bc3d6a1 100644 --- a/tests/background-icloud.test.js +++ b/tests/background-icloud.test.js @@ -1,6 +1,10 @@ const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('fs'); +const { + normalizeIcloudForwardMailProvider, + normalizeIcloudTargetMailboxType, +} = require('../mail-provider-utils.js'); const source = fs.readFileSync('background.js', 'utf8'); @@ -55,8 +59,6 @@ const bundle = [ extractFunction('normalizeEmailGenerator'), extractFunction('getEmailGeneratorLabel'), extractFunction('normalizeVerificationResendCount'), - extractFunction('normalizeIcloudTargetMailboxType'), - extractFunction('normalizeIcloudForwardMailProvider'), extractFunction('normalizePersistentSettingValue'), extractFunction('finalizeIcloudAliasAfterSuccessfulFlow'), ].join('\n'); @@ -157,6 +159,8 @@ function findIcloudAliasByEmail(aliases, email) { function getErrorMessage(error) { return String(typeof error === 'string' ? error : error?.message || ''); } +const normalizeIcloudTargetMailboxType = overrides.normalizeIcloudTargetMailboxType; +const normalizeIcloudForwardMailProvider = overrides.normalizeIcloudForwardMailProvider; ${bundle} @@ -177,7 +181,10 @@ test('normalizeEmailGenerator and label support icloud', () => { }); test('normalizePersistentSettingValue handles icloud settings', () => { - const api = createApi(); + const api = createApi({ + normalizeIcloudTargetMailboxType, + normalizeIcloudForwardMailProvider, + }); assert.equal(api.normalizePersistentSettingValue('icloudHostPreference', 'icloud.com'), 'icloud.com'); assert.equal(api.normalizePersistentSettingValue('icloudHostPreference', 'bad-host'), 'auto'); assert.equal(api.normalizePersistentSettingValue('icloudTargetMailboxType', 'forward-mailbox'), 'forward-mailbox'); diff --git a/tests/mail-provider-utils.test.js b/tests/mail-provider-utils.test.js index a52ce10..8b21e17 100644 --- a/tests/mail-provider-utils.test.js +++ b/tests/mail-provider-utils.test.js @@ -3,7 +3,11 @@ const assert = require('node:assert/strict'); const { HOTMAIL_PROVIDER, + getIcloudForwardMailConfig, + getIcloudForwardMailProviderOptions, getMailProviderConfig, + normalizeIcloudForwardMailProvider, + normalizeIcloudTargetMailboxType, normalizeMailProvider, } = require('../mail-provider-utils.js'); @@ -33,3 +37,29 @@ test('getMailProviderConfig preserves the hotmail provider sentinel', () => { } ); }); + +test('iCloud forward mailbox helpers normalize and expose supported providers', () => { + assert.equal(normalizeIcloudTargetMailboxType('forward-mailbox'), 'forward-mailbox'); + assert.equal(normalizeIcloudTargetMailboxType('unknown'), 'icloud-inbox'); + assert.equal(normalizeIcloudForwardMailProvider('GMAIL'), 'gmail'); + assert.equal(normalizeIcloudForwardMailProvider('unknown'), 'qq'); + assert.deepEqual( + getIcloudForwardMailProviderOptions().map((option) => option.value), + ['qq', '163', '163-vip', '126', 'gmail'] + ); +}); + +test('getIcloudForwardMailConfig reuses shared mailbox provider configs', () => { + assert.deepEqual(getIcloudForwardMailConfig('126'), { + source: 'mail-163', + url: 'https://mail.126.com/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D', + label: '126 邮箱', + }); + assert.deepEqual(getIcloudForwardMailConfig('gmail'), { + source: 'gmail-mail', + url: 'https://mail.google.com/mail/u/0/#inbox', + label: 'Gmail 邮箱', + inject: ['content/activation-utils.js', 'content/utils.js', 'content/gmail-mail.js'], + injectSource: 'gmail-mail', + }); +}); diff --git a/tests/sidepanel-cloudflare-temp-email-random-subdomain.test.js b/tests/sidepanel-cloudflare-temp-email-random-subdomain.test.js index afa859f..d553d40 100644 --- a/tests/sidepanel-cloudflare-temp-email-random-subdomain.test.js +++ b/tests/sidepanel-cloudflare-temp-email-random-subdomain.test.js @@ -1,6 +1,10 @@ const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); +const { + normalizeIcloudForwardMailProvider, + normalizeIcloudTargetMailboxType, +} = require('../mail-provider-utils'); const source = fs.readFileSync('sidepanel/sidepanel.js', 'utf8'); @@ -172,7 +176,7 @@ return { test('updateMailProviderUI keeps the temp domain selector visible and updates the hint when random subdomain is enabled', () => { const bundle = extractFunction('updateMailProviderUI'); - const api = new Function(` + const api = new Function('normalizeIcloudTargetMailboxType', 'normalizeIcloudForwardMailProvider', ` let latestState = { cloudflareTempEmailDomains: ['mail.example.com'], }; @@ -254,7 +258,7 @@ return { autoHintText, calls, }; - `)(); + `)(normalizeIcloudTargetMailboxType, normalizeIcloudForwardMailProvider); api.updateMailProviderUI(); assert.equal(api.cloudflareTempEmailSection.style.display, ''); diff --git a/tests/sidepanel-contribution-mode.test.js b/tests/sidepanel-contribution-mode.test.js index dd5fb33..e794302 100644 --- a/tests/sidepanel-contribution-mode.test.js +++ b/tests/sidepanel-contribution-mode.test.js @@ -1,6 +1,10 @@ const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); +const { + normalizeIcloudForwardMailProvider, + normalizeIcloudTargetMailboxType, +} = require('../mail-provider-utils'); const sidepanelSource = fs.readFileSync('sidepanel/sidepanel.js', 'utf8'); @@ -121,7 +125,7 @@ test('sidepanel html contains contribution mode runtime UI and loads the module test('collectSettingsPayload omits custom password and local sync settings in contribution mode', () => { const bundle = extractFunction('collectSettingsPayload'); - const api = new Function(` + const api = new Function('normalizeIcloudTargetMailboxType', 'normalizeIcloudForwardMailProvider', ` let latestState = { contributionMode: true }; let cloudflareDomainEditMode = false; let cloudflareTempEmailDomainEditMode = false; @@ -188,7 +192,7 @@ return { collectSettingsPayload, setLatestState(nextState) { latestState = nextState; }, }; -`)(); +`)(normalizeIcloudTargetMailboxType, normalizeIcloudForwardMailProvider); const contributionPayload = api.collectSettingsPayload(); assert.equal('customPassword' in contributionPayload, false); diff --git a/tests/sidepanel-icloud-provider.test.js b/tests/sidepanel-icloud-provider.test.js index 58808e9..cc17ee4 100644 --- a/tests/sidepanel-icloud-provider.test.js +++ b/tests/sidepanel-icloud-provider.test.js @@ -79,3 +79,297 @@ return { getSelectedIcloudHostPreference, getMailProviderLoginUrl }; assert.equal(api.getSelectedIcloudHostPreference(), 'icloud.com.cn'); assert.equal(api.getMailProviderLoginUrl(), 'https://www.icloud.com.cn/'); }); + +test('collectSettingsPayload persists icloud target mailbox settings', () => { + const bundle = extractFunction('collectSettingsPayload'); + + const api = new Function(` +let latestState = { contributionMode: false }; +let cloudflareDomainEditMode = false; +let cloudflareTempEmailDomainEditMode = false; +const selectCfDomain = { value: '' }; +const selectTempEmailDomain = { value: '' }; +const selectPanelMode = { value: 'cpa' }; +const inputVpsUrl = { value: '' }; +const inputVpsPassword = { value: '' }; +const inputSub2ApiUrl = { value: '' }; +const inputSub2ApiEmail = { value: '' }; +const inputSub2ApiPassword = { value: '' }; +const inputSub2ApiGroup = { value: '' }; +const inputSub2ApiDefaultProxy = { value: '' }; +const inputCodex2ApiUrl = { value: '' }; +const inputCodex2ApiAdminKey = { value: '' }; +const inputPassword = { value: '' }; +const selectMailProvider = { value: 'icloud' }; +const selectEmailGenerator = { value: 'duck' }; +const checkboxAutoDeleteIcloud = { checked: false }; +const selectIcloudHostPreference = { value: 'auto' }; +const selectIcloudFetchMode = { value: 'reuse_existing' }; +const selectIcloudTargetMailboxType = { value: 'forward-mailbox' }; +const selectIcloudForwardMailProvider = { value: 'gmail' }; +const inputPhoneVerificationEnabled = { checked: false }; +const inputAccountRunHistoryTextEnabled = { checked: false }; +const inputAccountRunHistoryHelperBaseUrl = { value: '' }; +const inputInbucketHost = { value: '' }; +const inputInbucketMailbox = { value: '' }; +const inputHotmailRemoteBaseUrl = { value: '' }; +const inputHotmailLocalBaseUrl = { value: '' }; +const inputLuckmailApiKey = { value: '' }; +const inputLuckmailBaseUrl = { value: '' }; +const selectLuckmailEmailType = { value: 'ms_graph' }; +const inputLuckmailDomain = { value: '' }; +const inputTempEmailBaseUrl = { value: '' }; +const inputTempEmailAdminAuth = { value: '' }; +const inputTempEmailCustomAuth = { value: '' }; +const inputTempEmailReceiveMailbox = { value: '' }; +const inputTempEmailUseRandomSubdomain = { checked: false }; +const inputAutoSkipFailures = { checked: false }; +const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' }; +const inputAutoDelayEnabled = { checked: false }; +const inputAutoDelayMinutes = { value: '30' }; +const inputAutoStepDelaySeconds = { value: '' }; +const inputVerificationResendCount = { value: '4' }; +const DEFAULT_VERIFICATION_RESEND_COUNT = 4; +function getCloudflareDomainsFromState() { return { domains: [], activeDomain: '' }; } +function normalizeCloudflareDomainValue(value) { return String(value || '').trim(); } +function getCloudflareTempEmailDomainsFromState() { return { domains: [], activeDomain: '' }; } +function normalizeCloudflareTempEmailDomainValue(value) { return String(value || '').trim(); } +function getSelectedLocalCpaStep9Mode() { return 'submit'; } +function getSelectedMail2925Mode() { return 'provide'; } +function buildManagedAliasBaseEmailPayload() { return { gmailBaseEmail: '', mail2925BaseEmail: '', emailPrefix: '' }; } +function getSelectedHotmailServiceMode() { return 'local'; } +function normalizeLuckmailBaseUrl(value) { return String(value || '').trim(); } +function normalizeLuckmailEmailType(value) { return String(value || '').trim() || 'ms_graph'; } +function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value || '').trim(); } +function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); } +function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); } +function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; } +function normalizeAutoDelayMinutes(value) { return Number(value) || 30; } +function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); } +function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; } +function normalizeIcloudTargetMailboxType(value) { return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'; } +function normalizeIcloudForwardMailProvider(value) { return String(value || '').trim().toLowerCase() === 'gmail' ? 'gmail' : 'qq'; } +${bundle} +return { collectSettingsPayload }; +`)(); + + const payload = api.collectSettingsPayload(); + assert.equal(payload.icloudTargetMailboxType, 'forward-mailbox'); + assert.equal(payload.icloudForwardMailProvider, 'gmail'); +}); + +test('updateMailProviderUI toggles icloud forward mailbox controls and hint', () => { + const bundle = extractFunction('updateMailProviderUI'); + const createRow = (display = 'none') => ({ style: { display } }); + + const api = new Function('createRow', ` +let latestState = { icloudHostPreference: 'icloud.com.cn' }; +let cloudflareDomainEditMode = false; +let cloudflareTempEmailDomainEditMode = false; +const ICLOUD_PROVIDER = 'icloud'; +const GMAIL_PROVIDER = 'gmail'; +const GMAIL_ALIAS_GENERATOR = 'gmail-alias'; +const LUCKMAIL_PROVIDER = 'luckmail-api'; +const CUSTOM_EMAIL_POOL_GENERATOR = 'custom-pool'; +const HOTMAIL_SERVICE_MODE_REMOTE = 'remote'; +const HOTMAIL_SERVICE_MODE_LOCAL = 'local'; +const rowMail2925Mode = createRow(); +const rowMail2925PoolSettings = createRow(); +const rowEmailPrefix = createRow(); +const rowCustomMailProviderPool = createRow(); +const rowInbucketHost = createRow(); +const rowInbucketMailbox = createRow(); +const rowEmailGenerator = createRow(); +const rowCfDomain = createRow(); +const rowTempEmailBaseUrl = createRow(); +const rowTempEmailAdminAuth = createRow(); +const rowTempEmailCustomAuth = createRow(); +const rowTempEmailReceiveMailbox = createRow(); +const rowTempEmailRandomSubdomainToggle = createRow(); +const rowTempEmailDomain = createRow(); +const cloudflareTempEmailSection = createRow(); +const hotmailSection = createRow(); +const mail2925Section = createRow(); +const luckmailSection = createRow(); +const icloudSection = createRow(); +const rowIcloudTargetMailboxType = createRow(); +const rowIcloudForwardMailProvider = createRow(); +const labelEmailPrefix = { textContent: '' }; +const inputEmailPrefix = { placeholder: '', style: { display: '' }, readOnly: false }; +const labelMail2925UseAccountPool = createRow(); +const selectMail2925PoolAccount = { style: { display: 'none' }, disabled: false }; +const btnFetchEmail = { hidden: false, disabled: false, textContent: '' }; +const btnMailLogin = { disabled: false, textContent: '', title: '' }; +const inputEmail = { readOnly: false, placeholder: '', value: '' }; +const autoHintText = { textContent: '' }; +const rowHotmailServiceMode = createRow(); +const rowHotmailRemoteBaseUrl = createRow(); +const rowHotmailLocalBaseUrl = createRow(); +const inputMail2925UseAccountPool = { checked: false }; +const selectMailProvider = { value: 'icloud' }; +const selectEmailGenerator = { value: 'duck', disabled: false, options: [] }; +const selectIcloudTargetMailboxType = { value: 'icloud-inbox' }; +const selectIcloudForwardMailProvider = { value: 'gmail' }; +const selectIcloudHostPreference = { value: 'icloud.com.cn' }; +const inputTempEmailUseRandomSubdomain = { checked: false }; +const inputRunCount = { disabled: false }; +const currentAutoRun = { autoRunning: false }; +const MAIL_PROVIDER_LOGIN_CONFIGS = { gmail: { label: 'Gmail 邮箱' } }; +const ICLOUD_FORWARD_MAIL_PROVIDER_LABELS = { gmail: 'Gmail 邮箱' }; +function normalizeIcloudHost(value) { return String(value || '').trim().toLowerCase(); } +function normalizeIcloudTargetMailboxType(value) { return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'; } +function normalizeIcloudForwardMailProvider(value) { return String(value || '').trim().toLowerCase() === 'gmail' ? 'gmail' : 'qq'; } +function getSelectedIcloudHostPreference() { return selectIcloudHostPreference.value; } +function isLuckmailProvider() { return false; } +function isCustomMailProvider() { return false; } +function isIcloudMailProvider() { return selectMailProvider.value === ICLOUD_PROVIDER; } +function usesCustomMailProviderPool() { return false; } +function usesGeneratedAliasMailProvider() { return false; } +function getSelectedMail2925Mode() { return 'provide'; } +function getManagedAliasProviderUiCopy() { return null; } +function getCurrentRegistrationEmailUiCopy() { return { buttonLabel: '获取邮箱', placeholder: '邮箱', label: '邮箱' }; } +function updateMailLoginButtonState() {} +function getSelectedHotmailServiceMode() { return 'local'; } +function getCloudflareDomainsFromState() { return { domains: [], activeDomain: '' }; } +function setCloudflareDomainEditMode() {} +function getCloudflareTempEmailDomainsFromState() { return { domains: [], activeDomain: '' }; } +function setCloudflareTempEmailDomainEditMode() {} +function queueIcloudAliasRefresh() {} +function hideIcloudLoginHelp() {} +function syncMail2925PoolAccountOptions() {} +function getMail2925Accounts() { return []; } +function renderHotmailAccounts() {} +function renderMail2925Accounts() {} +function renderLuckmailPurchases() {} +function getSelectedEmailGenerator() { return selectEmailGenerator.value; } +function isAutoRunLockedPhase() { return false; } +function getCurrentHotmailEmail() { return ''; } +function getCurrentLuckmailEmail() { return ''; } +function getCustomEmailPoolSize() { return 0; } +function getCustomMailProviderPoolSize() { return 0; } +function syncRunCountFromCustomEmailPool() {} +function syncRunCountFromCustomMailProviderPool() {} +function shouldLockRunCountToEmailPool() { return false; } +${bundle} +return { + updateMailProviderUI, + rowIcloudTargetMailboxType, + rowIcloudForwardMailProvider, + selectIcloudTargetMailboxType, + autoHintText, +}; +`)(createRow); + + api.updateMailProviderUI(); + assert.equal(api.rowIcloudTargetMailboxType.style.display, ''); + assert.equal(api.rowIcloudForwardMailProvider.style.display, 'none'); + + api.selectIcloudTargetMailboxType.value = 'forward-mailbox'; + api.updateMailProviderUI(); + assert.equal(api.rowIcloudForwardMailProvider.style.display, ''); + assert.match(api.autoHintText.textContent, /Gmail 邮箱/); +}); + +test('applySettingsState restores icloud forward mailbox settings before UI refresh', () => { + const bundle = extractFunction('applySettingsState'); + const calls = []; + + const api = new Function('calls', ` +let latestState = {}; +const inputEmail = { value: '' }; +const inputVpsUrl = { value: '' }; +const inputVpsPassword = { value: '' }; +const selectPanelMode = { value: 'cpa' }; +const inputSub2ApiUrl = { value: '' }; +const inputSub2ApiEmail = { value: '' }; +const inputSub2ApiPassword = { value: '' }; +const inputSub2ApiGroup = { value: '' }; +const inputSub2ApiDefaultProxy = { value: '' }; +const inputCodex2ApiUrl = { value: '' }; +const inputCodex2ApiAdminKey = { value: '' }; +const ICLOUD_PROVIDER = 'icloud'; +const GMAIL_PROVIDER = 'gmail'; +const GMAIL_ALIAS_GENERATOR = 'gmail-alias'; +const CUSTOM_EMAIL_POOL_GENERATOR = 'custom-pool'; +const selectMailProvider = { value: '163' }; +const selectEmailGenerator = { value: 'duck' }; +const selectIcloudHostPreference = { value: 'auto' }; +const selectIcloudFetchMode = { value: 'reuse_existing' }; +const selectIcloudTargetMailboxType = { value: 'icloud-inbox' }; +const selectIcloudForwardMailProvider = { value: 'qq' }; +const checkboxAutoDeleteIcloud = { checked: false }; +const inputAccountRunHistoryHelperBaseUrl = { value: '' }; +const inputContributionNickname = { value: '' }; +const inputContributionQq = { value: '' }; +const inputMail2925UseAccountPool = { checked: false }; +const inputInbucketHost = { value: '' }; +const inputInbucketMailbox = { value: '' }; +const inputCustomMailProviderPool = { value: '' }; +const inputCustomEmailPool = { value: '' }; +const inputHotmailRemoteBaseUrl = { value: '' }; +const inputHotmailLocalBaseUrl = { value: '' }; +const inputLuckmailApiKey = { value: '' }; +const inputLuckmailBaseUrl = { value: '' }; +const selectLuckmailEmailType = { value: 'ms_graph' }; +const inputLuckmailDomain = { value: '' }; +const inputAutoSkipFailures = { checked: false }; +const inputAutoSkipFailuresThreadIntervalMinutes = { value: '' }; +const inputAutoDelayEnabled = { checked: false }; +const inputAutoDelayMinutes = { value: '' }; +const inputAutoStepDelaySeconds = { value: '' }; +const inputVerificationResendCount = { value: '' }; +const inputPhoneVerificationEnabled = { checked: false }; +const DEFAULT_PHONE_VERIFICATION_ENABLED = false; +const inputHeroSmsApiKey = { value: '' }; +const selectHeroSmsCountry = { value: '52', options: [{ value: '52' }] }; +const inputRunCount = { value: '' }; +const DEFAULT_VERIFICATION_RESEND_COUNT = 4; +function syncLatestState(state) { latestState = { ...latestState, ...state }; } +function syncAutoRunState() {} +function syncPasswordField() {} +function setLocalCpaStep9Mode() {} +function isCustomMailProvider() { return false; } +function setMail2925Mode() {} +function normalizeIcloudFetchMode(value) { return String(value || '') === 'always_new' ? 'always_new' : 'reuse_existing'; } +function normalizeIcloudTargetMailboxType(value) { return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'; } +function normalizeIcloudForwardMailProvider(value) { return String(value || '').trim().toLowerCase() === 'gmail' ? 'gmail' : 'qq'; } +function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); } +function setManagedAliasBaseEmailInputForProvider() {} +function normalizeCustomEmailPoolEntries(value) { return Array.isArray(value) ? value : []; } +function setHotmailServiceMode() {} +function normalizeLuckmailBaseUrl(value) { return String(value || '').trim(); } +function normalizeLuckmailEmailType(value) { return String(value || '').trim() || 'ms_graph'; } +function applyCloudflareTempEmailSettingsState() {} +function renderCloudflareDomainOptions() {} +function setCloudflareDomainEditMode() {} +function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; } +function normalizeAutoDelayMinutes(value) { return Number(value) || 30; } +function formatAutoStepDelayInputValue(value) { return value == null ? '' : String(value); } +function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; } +function normalizeHeroSmsCountryId() { return 52; } +function getSelectedHeroSmsCountryOption() { return { label: 'Thailand' }; } +function updateHeroSmsPlatformDisplay() {} +function applyAutoRunStatus() {} +function markSettingsDirty() {} +function updateAutoDelayInputState() {} +function updateFallbackThreadIntervalInputState() {} +function updateAccountRunHistorySettingsUI() {} +function updatePhoneVerificationSettingsUI() {} +function updatePanelModeUI() {} +function updateMailProviderUI() { calls.push({ target: selectIcloudTargetMailboxType.value, provider: selectIcloudForwardMailProvider.value }); } +function isLuckmailProvider() { return false; } +function updateButtonStates() {} +${bundle} +return { applySettingsState, selectIcloudTargetMailboxType, selectIcloudForwardMailProvider }; +`)(calls); + + api.applySettingsState({ + mailProvider: 'icloud', + icloudTargetMailboxType: 'forward-mailbox', + icloudForwardMailProvider: 'gmail', + }); + + assert.equal(api.selectIcloudTargetMailboxType.value, 'forward-mailbox'); + assert.equal(api.selectIcloudForwardMailProvider.value, 'gmail'); + assert.deepEqual(calls.at(-1), { target: 'forward-mailbox', provider: 'gmail' }); +}); diff --git a/tests/sidepanel-mail2925-base-email.test.js b/tests/sidepanel-mail2925-base-email.test.js index 492b1a7..aa6695e 100644 --- a/tests/sidepanel-mail2925-base-email.test.js +++ b/tests/sidepanel-mail2925-base-email.test.js @@ -1,6 +1,10 @@ const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); +const { + normalizeIcloudForwardMailProvider, + normalizeIcloudTargetMailboxType, +} = require('../mail-provider-utils'); const sidepanelSource = fs.readFileSync('sidepanel/sidepanel.js', 'utf8'); @@ -148,7 +152,7 @@ test('collectSettingsPayload persists currentMail2925AccountId for 2925 account extractFunction('collectSettingsPayload'), ].join('\n'); - const api = new Function(` + const api = new Function('normalizeIcloudTargetMailboxType', 'normalizeIcloudForwardMailProvider', ` let latestState = { contributionMode: false, mail2925UseAccountPool: true, @@ -222,7 +226,7 @@ function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Num function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; } ${bundle} return { collectSettingsPayload }; -`)(); +`)(normalizeIcloudTargetMailboxType, normalizeIcloudForwardMailProvider); const payload = api.collectSettingsPayload(); diff --git a/tests/sidepanel-phone-verification-settings.test.js b/tests/sidepanel-phone-verification-settings.test.js index 6d66b87..bc75814 100644 --- a/tests/sidepanel-phone-verification-settings.test.js +++ b/tests/sidepanel-phone-verification-settings.test.js @@ -1,6 +1,10 @@ const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); +const { + normalizeIcloudForwardMailProvider, + normalizeIcloudTargetMailboxType, +} = require('../mail-provider-utils'); const sidepanelSource = fs.readFileSync('sidepanel/sidepanel.js', 'utf8'); @@ -90,7 +94,7 @@ return { }); test('collectSettingsPayload keeps local helper sync enabled while persisting sms toggle state', () => { - const api = new Function(` + const api = new Function('normalizeIcloudTargetMailboxType', 'normalizeIcloudForwardMailProvider', ` let latestState = { contributionMode: false, mail2925UseAccountPool: false, @@ -168,7 +172,7 @@ ${extractFunction('normalizeHeroSmsCountryLabel')} ${extractFunction('getSelectedHeroSmsCountryOption')} ${extractFunction('collectSettingsPayload')} return { collectSettingsPayload }; -`)(); +`)(normalizeIcloudTargetMailboxType, normalizeIcloudForwardMailProvider); const payload = api.collectSettingsPayload(); diff --git a/项目完整链路说明.md b/项目完整链路说明.md index 39d7153..835489e 100644 --- a/项目完整链路说明.md +++ b/项目完整链路说明.md @@ -163,7 +163,7 @@ - 2925 当前选中的号池账号 ID `currentMail2925AccountId` - Cloudflare / Temp Email 设置 - 接码开关,以及 HeroSMS 的 API Key 与默认国家设置 -- iCloud 相关偏好 +- iCloud 相关偏好:Host、获取策略、成功后自动删除、目标邮箱类型与转发收码邮箱 provider - LuckMail API 配置 - 自动运行默认配置 - 账号运行历史 `accountRunHistory`(以邮箱为主键,保存该邮箱最近一次状态:成功/失败/停止) @@ -605,6 +605,7 @@ Codex2API 补充: - `163`、`163 VIP`、`126` 都走同一条“网易网页邮箱”验证码链路。 - sidepanel 只负责切换 provider 与展示登录入口;后台根据 provider 选择对应网页邮箱首页。 - 内容脚本来源统一归类到 `mail-163`,这样 Step 4 / Step 8 继续复用同一套验证码读取与邮件清理逻辑。 +- `mail-provider-utils.js` 同时承接 iCloud 转发收码可选 provider 的归一化与入口配置,避免 background / sidepanel 重新复制 QQ、网易和 Gmail 的收码地址、label 与注入脚本。 - `manifest.json` 需要同时覆盖: - `https://mail.163.com/*` - `https://webmail.vip.163.com/*` @@ -699,8 +700,22 @@ Codex2API 补充: 组成: - [icloud-utils.js](c:/Users/projectf/Downloads/codex注册扩展/icloud-utils.js) +- [mail-provider-utils.js](c:/Users/projectf/Downloads/codex注册扩展/mail-provider-utils.js) - [content/icloud-mail.js](c:/Users/projectf/Downloads/codex注册扩展/content/icloud-mail.js) +配置: + +- `icloudHostPreference` 只决定 iCloud 登录、别名管理和 iCloud Mail 收件箱 Host。 +- `icloudFetchMode` 决定生成注册邮箱时复用已有 Hide My Email 别名,还是始终创建新别名。 +- `icloudTargetMailboxType = icloud-inbox` 时,Step 4 / Step 8 直接打开 iCloud Mail 收件箱轮询验证码。 +- `icloudTargetMailboxType = forward-mailbox` 时,注册邮箱仍由 iCloud Hide My Email 生成,但 Step 4 / Step 8 改为打开 `icloudForwardMailProvider` 指定的转发目标邮箱收码;当前支持 `qq / 163 / 163-vip / 126 / gmail`。 + +维护约定: + +- iCloud 转发目标邮箱 provider 的 label、URL、source、inject 配置统一由 `mail-provider-utils.js` 输出。 +- `background.js` 只根据 `icloudTargetMailboxType` 选择 iCloud Mail 收件箱或共享转发邮箱配置,不再在主文件内硬编码各 provider 地址。 +- `sidepanel/sidepanel.js` 只负责展示、保存和回显目标邮箱类型 / 转发邮箱 provider,不重复定义 provider 业务清单。 + ## 8. 自动运行完整链路 文件: diff --git a/项目文件结构说明.md b/项目文件结构说明.md index 45966c6..fde9fec 100644 --- a/项目文件结构说明.md +++ b/项目文件结构说明.md @@ -26,7 +26,7 @@ - `hotmail-utils.js`:Hotmail 账号与验证码提取相关的纯工具函数,负责账号筛选、验证码匹配、第三方接口数据归一化。 - `icloud-utils.js`:iCloud 隐私邮箱相关的纯工具函数,负责 host、别名列表、保留状态、已用状态等归一化。 - `luckmail-utils.js`:LuckMail 相关的纯工具函数,负责邮箱购买记录、标签、邮件 cursor、验证码匹配等归一化。 -- `mail-provider-utils.js`:网页邮箱 provider 配置纯工具,负责 `163 / 163 VIP / 126 / QQ / Inbucket / Hotmail` 的基础归一化与页面入口配置。 +- `mail-provider-utils.js`:网页邮箱 provider 配置纯工具,负责 `163 / 163 VIP / 126 / QQ / Inbucket / Hotmail` 的基础归一化与页面入口配置,并统一承接 iCloud 转发收码目标邮箱 provider 的归一化、选项列表和收码入口配置。 - `mail2925-utils.js`:2925 账号池相关的纯工具函数,负责账号归一化、冷却期判断、可用账号挑选、批量导入解析与列表更新。 - `managed-alias-utils.js`:共享 Gmail / 2925 的别名邮箱规则,负责解析基邮箱、校验“当前完整注册邮箱”是否仍与基邮箱兼容、生成 Gmail `+tag` 与 2925 随机后缀邮箱,并输出 sidepanel 可复用的 UI 文案;当前还统一承接 `mail2925Mode` 的归一化与“2925 仅在 provide 模式下参与别名生成”的共享判定。 - `manifest.json`:Chrome 扩展清单,声明权限、背景脚本、侧边栏、内容脚本与规则集。 @@ -125,8 +125,8 @@ - `sidepanel/contribution-content-update-service.js`:侧边栏贡献内容更新服务,负责拉取 `https://apikey.qzz.io/api/content-summary`、缓存公开公告/教程摘要,并输出可用于提示展示的 `promptVersion` 与最新更新时间。 - `sidepanel/contribution-mode.js`:侧边栏贡献模式管理器,负责顶部“贡献”按钮、确认弹窗、贡献模式显隐、复用主自动流程启动、侧栏内贡献状态轮询、上传页跳转,以及贡献模式下对来源选择、配置入口、记录入口和敏感配置行(包括 Codex2API 配置)的禁用与隐藏。 - `sidepanel/sidepanel.css`:侧边栏样式文件;当前额外提供 Hotmail / 2925 共用的号池表单容器、操作按钮行与统一的收起态列表高度样式。 -- `sidepanel/sidepanel.html`:侧边栏页面结构;当前步骤列表已改为动态容器,日志区提供“记录”按钮并挂接邮箱记录覆盖层,顶部新增“贡献”按钮并在设置卡片中新增贡献模式主面板;贡献按钮下方额外挂接一个可关闭的轻提示气泡,用于提示公开公告 / 使用教程有更新;贡献面板内展示 `OAUTH / 回调 / 总状态` 三块真实运行态信息,同时把“接码”开关与“验证码重发”拆成同一行展示;只有开启接码后,下面的 HeroSMS 平台、国家与 API Key 行才会显示;页面继续加载 `managed-alias-utils.js`,并把旧的“邮箱前缀”字段语义改为“别名基邮箱”;当 provider 为 2925 时,会额外显示 `提供邮箱 / 接收邮箱` 模式切换,并把“2925 号池”从别名基邮箱行拆成独立配置行,避免 receive 模式把账号池开关一起隐藏;当前在 `邮箱生成` 区域新增 `自定义邮箱池` 选项和多行邮箱池输入框,并在 `邮箱服务 = 自定义邮箱` 时额外显示 `自定义号池` 文本框;来源下拉框当前支持 `CPA / SUB2API / Codex2API`,其中 Codex2API 额外提供后台地址和管理密钥配置行;Hotmail / 2925 两个账号池当前都使用统一的头部“添加账号/取消添加”按钮和共享表单容器。 -- `sidepanel/sidepanel.js`:侧边栏主入口脚本,负责 UI 状态同步、动态步骤渲染、按钮交互、共享验证码自动重发次数配置与广播接收,并装配 Hotmail / 2925 / iCloud / LuckMail / 贡献模式 / 邮箱记录面板 / 贡献内容更新服务;当前贡献模式的“开始贡献”会直接复用主自动流程启动逻辑,而独立 manager 负责贡献运行态展示与轮询,同时把 Gmail / 2925 的基邮箱输入、完整注册邮箱输入、自动生成按钮与兼容性校验统一接到共享别名逻辑上;当 provider 为 2925 时,会根据 `mail2925Mode` 决定是否启用别名基邮箱链路,而 2925 号池开关与当前账号选择则独立显示并同时服务于 provide / receive 两种模式;`自定义邮箱池` 模式会按邮箱池长度锁定自动轮数,并把当前输入的邮箱池配置参与自动启动前保存;`邮箱服务 = 自定义邮箱` 时,如果配置了 `customMailProviderPool`,也会按号池长度锁定自动轮数并在 Auto 中按轮次分配注册邮箱,同时在普通失败时继续复用当前邮箱,只有成功或出现手机号验证时才切换下一个邮箱;Step 8 的自定义邮箱确认弹窗当前额外提供“出现手机号验证”按钮,用于直接走与真实 add-phone 一致的 fatal 分支;HeroSMS 国家列表会在 sidepanel 初始化时拉取并恢复到本地保存的国家/API 设置,接码开关关闭时会直接隐藏相关配置;账号记录快照同步改为默认自动模式,只要本地 helper 可用就会自动落盘;新来源 Codex2API 在这里仅补充来源配置接线、表单显隐和 Step 10 按钮文案,不承接协议业务逻辑;Hotmail / 2925 的新增表单显隐统一接到 `sidepanel/account-pool-ui.js` 这一层共享 helper;侧边栏初始化与点击“自动”前会刷新一次贡献站公开内容摘要,并按本地关闭版本决定是否展示轻提示,同时在首次初始化后按现有规则决定是否弹出新手引导提示。 +- `sidepanel/sidepanel.html`:侧边栏页面结构;当前步骤列表已改为动态容器,日志区提供“记录”按钮并挂接邮箱记录覆盖层,顶部新增“贡献”按钮并在设置卡片中新增贡献模式主面板;贡献按钮下方额外挂接一个可关闭的轻提示气泡,用于提示公开公告 / 使用教程有更新;贡献面板内展示 `OAUTH / 回调 / 总状态` 三块真实运行态信息,同时把“接码”开关与“验证码重发”拆成同一行展示;只有开启接码后,下面的 HeroSMS 平台、国家与 API Key 行才会显示;页面继续加载 `managed-alias-utils.js` 与 `mail-provider-utils.js`,并把旧的“邮箱前缀”字段语义改为“别名基邮箱”;当 provider 为 2925 时,会额外显示 `提供邮箱 / 接收邮箱` 模式切换,并把“2925 号池”从别名基邮箱行拆成独立配置行,避免 receive 模式把账号池开关一起隐藏;当前在 `邮箱生成` 区域新增 `自定义邮箱池` 选项和多行邮箱池输入框,并在 `邮箱服务 = 自定义邮箱` 时额外显示 `自定义号池` 文本框;当邮箱服务为 iCloud 时,额外提供目标邮箱类型与转发邮箱 provider 配置,用于选择直接从 iCloud 收件箱收码或从 QQ / 网易 / Gmail 转发目标邮箱收码;来源下拉框当前支持 `CPA / SUB2API / Codex2API`,其中 Codex2API 额外提供后台地址和管理密钥配置行;Hotmail / 2925 两个账号池当前都使用统一的头部“添加账号/取消添加”按钮和共享表单容器。 +- `sidepanel/sidepanel.js`:侧边栏主入口脚本,负责 UI 状态同步、动态步骤渲染、按钮交互、共享验证码自动重发次数配置与广播接收,并装配 Hotmail / 2925 / iCloud / LuckMail / 贡献模式 / 邮箱记录面板 / 贡献内容更新服务;当前贡献模式的“开始贡献”会直接复用主自动流程启动逻辑,而独立 manager 负责贡献运行态展示与轮询,同时把 Gmail / 2925 的基邮箱输入、完整注册邮箱输入、自动生成按钮与兼容性校验统一接到共享别名逻辑上;当 provider 为 2925 时,会根据 `mail2925Mode` 决定是否启用别名基邮箱链路,而 2925 号池开关与当前账号选择则独立显示并同时服务于 provide / receive 两种模式;当 provider 为 iCloud 时,会保存并回显目标邮箱类型与转发邮箱 provider,相关 provider 选项和 label 复用 `mail-provider-utils.js`;`自定义邮箱池` 模式会按邮箱池长度锁定自动轮数,并把当前输入的邮箱池配置参与自动启动前保存;`邮箱服务 = 自定义邮箱` 时,如果配置了 `customMailProviderPool`,也会按号池长度锁定自动轮数并在 Auto 中按轮次分配注册邮箱,同时在普通失败时继续复用当前邮箱,只有成功或出现手机号验证时才切换下一个邮箱;Step 8 的自定义邮箱确认弹窗当前额外提供“出现手机号验证”按钮,用于直接走与真实 add-phone 一致的 fatal 分支;HeroSMS 国家列表会在 sidepanel 初始化时拉取并恢复到本地保存的国家/API 设置,接码开关关闭时会直接隐藏相关配置;账号记录快照同步改为默认自动模式,只要本地 helper 可用就会自动落盘;新来源 Codex2API 在这里仅补充来源配置接线、表单显隐和 Step 10 按钮文案,不承接协议业务逻辑;Hotmail / 2925 的新增表单显隐统一接到 `sidepanel/account-pool-ui.js` 这一层共享 helper;侧边栏初始化与点击“自动”前会刷新一次贡献站公开内容摘要,并按本地关闭版本决定是否展示轻提示,同时在首次初始化后按现有规则决定是否弹出新手引导提示。 - `sidepanel/update-service.js`:侧边栏更新检查服务,负责 GitHub Releases 查询、`Pro` / `v` 双版本族排序、缓存读取与版本展示。 ## `tests/` @@ -146,7 +146,7 @@ - `tests/background-custom-email-pool.test.js`:测试后台对自定义邮箱池生成方式,以及自定义邮箱服务号池的归一化、标签文案和按轮次取邮箱逻辑。 - `tests/background-generated-email-module.test.js`:测试生成邮箱辅助模块已接入且导出工厂,并覆盖 2925 receive 模式回退普通邮箱生成、自定义邮箱池按轮次取值、2925 provide 模式账号池预热,以及 iCloud `always_new` 传参。 - `tests/background-icloud.test.js`:测试 iCloud 相关后台纯函数与别名收尾逻辑。 -- `tests/background-icloud-mail-provider.test.js`:测试 iCloud 邮箱 provider 配置解析。 +- `tests/background-icloud-mail-provider.test.js`:测试 iCloud 邮箱 provider 配置解析,并覆盖转发收码模式复用共享 provider 配置。 - `tests/background-logging-status-module.test.js`:测试日志 / 状态模块已接入且导出工厂。 - `tests/background-luckmail.test.js`:测试 LuckMail 相关后台逻辑,如购买、复用、标记已用与重置。 - `tests/background-mail2925-session-module.test.js`:测试 2925 会话模块已接入且导出工厂,并覆盖命中上限后“禁用 24 小时 + 切下一个号 + 自动登录”的核心链路。 @@ -176,7 +176,7 @@ - `tests/icloud-mail-content.test.js`:测试 iCloud Mail 内容脚本读取邮件正文和选中状态逻辑。 - `tests/icloud-utils.test.js`:测试 iCloud 工具层的 host、别名列表、保留状态与筛选逻辑。 - `tests/luckmail-utils.test.js`:测试 LuckMail 工具层的购买记录、邮件、游标和验证码匹配逻辑。 -- `tests/mail-provider-utils.test.js`:测试网页邮箱 provider 配置工具对 `126` 等 provider 的归一化与入口配置解析。 +- `tests/mail-provider-utils.test.js`:测试网页邮箱 provider 配置工具对 `126` 等 provider 的归一化与入口配置解析,以及 iCloud 转发收码 provider 选项、归一化和配置复用。 - `tests/mail-2925-content.test.js`:测试 2925 邮箱内容脚本的轮询快照、忽略邮箱对比、按步骤会话隔离已试验证码、单封邮件删除与整箱删除逻辑。 - `tests/mail2925-utils.test.js`:测试 2925 账号池工具层的账号归一化、冷却期判断、可用账号挑选与批量导入解析。 - `tests/managed-alias-utils.test.js`:覆盖 Gmail / 2925 共享别名工具的解析、生成、兼容性判断。 @@ -189,7 +189,7 @@ - `tests/sidepanel-contribution-mode.test.js`:测试侧边栏贡献模式的 HTML 接线、runtime-only 设置保护,以及贡献模式 manager 复用主自动流启动、状态轮询和退出清理逻辑。 - `tests/sidepanel-auto-run-content-refresh.test.js`:测试点击“自动”时会先刷新贡献内容更新摘要,且刷新失败不会阻塞自动流程启动。 - `tests/sidepanel-icloud-manager.test.js`:测试侧边栏 iCloud 管理器模块接线与空态渲染。 -- `tests/sidepanel-icloud-provider.test.js`:测试侧边栏 iCloud 登录地址解析逻辑。 +- `tests/sidepanel-icloud-provider.test.js`:测试侧边栏 iCloud 登录地址解析、目标邮箱类型 / 转发邮箱 provider 保存、回显和显隐联动。 - `tests/sidepanel-luckmail-manager.test.js`:测试侧边栏 LuckMail 管理器模块接线与空态渲染。 - `tests/sidepanel-mail2925-manager.test.js`:测试侧边栏 2925 管理器模块接线、共享号池表单脚本加载顺序、显隐交互与空态渲染。 - `tests/sidepanel-mail2925-mode.test.js`:测试侧边栏保留 `2925 provide / receive` 模式行与独立的号池配置行,并验证 sidepanel 只在 provide 模式下把 2925 视为别名邮箱 provider。