Merge pull request #290 from QLHazyCoder/codex/pr280-reimplement
feat(email): 使用英文名时间生成默认邮箱前缀
This commit is contained in:
@@ -45,6 +45,7 @@ importScripts(
|
|||||||
'flows/kiro/background/desktop-authorize-runner.js',
|
'flows/kiro/background/desktop-authorize-runner.js',
|
||||||
'flows/kiro/background/publisher-kiro-rs.js',
|
'flows/kiro/background/publisher-kiro-rs.js',
|
||||||
'flows/grok/background/publisher-webchat2api.js',
|
'flows/grok/background/publisher-webchat2api.js',
|
||||||
|
'background/email-local-part-helpers.js',
|
||||||
'background/generated-email-helpers.js',
|
'background/generated-email-helpers.js',
|
||||||
'background/signup-flow-helpers.js',
|
'background/signup-flow-helpers.js',
|
||||||
'background/mail-rule-registry.js',
|
'background/mail-rule-registry.js',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
(function cloudMailProviderModule(root, factory) {
|
(function cloudMailProviderModule(root, factory) {
|
||||||
root.MultiPageBackgroundCloudMailProvider = factory();
|
root.MultiPageBackgroundCloudMailProvider = factory(root);
|
||||||
})(typeof self !== 'undefined' ? self : globalThis, function createCloudMailProviderModule() {
|
})(typeof self !== 'undefined' ? self : globalThis, function createCloudMailProviderModule(root = globalThis) {
|
||||||
function createCloudMailProvider(deps = {}) {
|
function createCloudMailProvider(deps = {}) {
|
||||||
const {
|
const {
|
||||||
addLog = async () => {},
|
addLog = async () => {},
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
normalizeCloudMailDomains,
|
normalizeCloudMailDomains,
|
||||||
normalizeCloudMailMailApiMessages,
|
normalizeCloudMailMailApiMessages,
|
||||||
persistRegistrationEmailState = null,
|
persistRegistrationEmailState = null,
|
||||||
|
buildRandomNameDateTimeLocalPart = root.MultiPageEmailLocalPartHelpers?.buildRandomNameDateTimeLocalPart,
|
||||||
pickVerificationMessageWithTimeFallback,
|
pickVerificationMessageWithTimeFallback,
|
||||||
setEmailState = async () => {},
|
setEmailState = async () => {},
|
||||||
setPersistentSettings = async () => {},
|
setPersistentSettings = async () => {},
|
||||||
@@ -172,6 +173,12 @@
|
|||||||
return chars.join('');
|
return chars.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildCloudMailNameDateTimeLocalPart(date = new Date()) {
|
||||||
|
return typeof buildRandomNameDateTimeLocalPart === 'function'
|
||||||
|
? buildRandomNameDateTimeLocalPart(date)
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchCloudMailAddress(state, options = {}) {
|
async function fetchCloudMailAddress(state, options = {}) {
|
||||||
throwIfStopped();
|
throwIfStopped();
|
||||||
const latestState = state || await getState();
|
const latestState = state || await getState();
|
||||||
@@ -181,6 +188,7 @@
|
|||||||
requireDomain: true,
|
requireDomain: true,
|
||||||
});
|
});
|
||||||
const requestedLocal = String(options.localPart || options.name || '').trim().toLowerCase()
|
const requestedLocal = String(options.localPart || options.name || '').trim().toLowerCase()
|
||||||
|
|| buildCloudMailNameDateTimeLocalPart(options.date)
|
||||||
|| generateCloudMailAliasLocalPart();
|
|| generateCloudMailAliasLocalPart();
|
||||||
const address = `${requestedLocal}@${ensuredConfig.domain}`.toLowerCase();
|
const address = `${requestedLocal}@${ensuredConfig.domain}`.toLowerCase();
|
||||||
const payload = { list: [{ email: address }] };
|
const payload = { list: [{ email: address }] };
|
||||||
@@ -318,6 +326,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
buildCloudMailNameDateTimeLocalPart,
|
||||||
ensureCloudMailConfig,
|
ensureCloudMailConfig,
|
||||||
ensureCloudMailToken,
|
ensureCloudMailToken,
|
||||||
fetchCloudMailAddress,
|
fetchCloudMailAddress,
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
(function attachEmailLocalPartHelpers(root, factory) {
|
||||||
|
root.MultiPageEmailLocalPartHelpers = factory(root);
|
||||||
|
})(typeof self !== 'undefined' ? self : globalThis, function createEmailLocalPartHelpersModule(root = globalThis) {
|
||||||
|
const ENGLISH_NAME_PREFIXES = [
|
||||||
|
'james', 'john', 'robert', 'michael', 'william', 'david', 'richard', 'joseph',
|
||||||
|
'thomas', 'charles', 'mary', 'patricia', 'jennifer', 'linda', 'elizabeth',
|
||||||
|
'barbara', 'susan', 'jessica', 'sarah', 'karen', 'daniel', 'matthew',
|
||||||
|
'anthony', 'mark', 'donald', 'steven', 'paul', 'andrew', 'joshua', 'kevin',
|
||||||
|
'brian', 'george', 'edward', 'ronald', 'timothy', 'jason', 'jeffrey', 'ryan',
|
||||||
|
'jacob', 'gary', 'nicholas', 'eric', 'jonathan', 'stephen', 'larry', 'justin',
|
||||||
|
'scott', 'brandon', 'benjamin', 'samuel', 'gregory', 'alexander', 'patrick',
|
||||||
|
'frank', 'raymond', 'jack', 'dennis', 'jerry', 'tyler', 'aaron', 'henry',
|
||||||
|
'douglas', 'peter', 'adam', 'zachary', 'nathan', 'walter', 'harold', 'kyle',
|
||||||
|
'carl', 'arthur', 'gerald', 'roger', 'alice', 'emma', 'olivia', 'sophia',
|
||||||
|
'isabella', 'mia', 'amelia', 'harper', 'evelyn', 'abigail', 'emily', 'ella',
|
||||||
|
'scarlett', 'grace', 'chloe', 'victoria', 'riley', 'aria', 'lily', 'nora',
|
||||||
|
];
|
||||||
|
|
||||||
|
const RANDOM_SUFFIX_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
|
||||||
|
function getRandomInt(maxExclusive) {
|
||||||
|
const max = Math.floor(Number(maxExclusive));
|
||||||
|
if (!Number.isFinite(max) || max <= 0) return 0;
|
||||||
|
const cryptoApi = root.crypto;
|
||||||
|
if (cryptoApi && typeof cryptoApi.getRandomValues === 'function') {
|
||||||
|
const buffer = new Uint32Array(1);
|
||||||
|
cryptoApi.getRandomValues(buffer);
|
||||||
|
return buffer[0] % max;
|
||||||
|
}
|
||||||
|
return Math.floor(Math.random() * max);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickRandomEnglishNamePrefix() {
|
||||||
|
return ENGLISH_NAME_PREFIXES[getRandomInt(ENGLISH_NAME_PREFIXES.length)] || 'james';
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateTimeDigits(date = new Date()) {
|
||||||
|
const current = new Date(date);
|
||||||
|
if (Number.isNaN(current.getTime())) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const year = String(current.getFullYear());
|
||||||
|
const month = String(current.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(current.getDate()).padStart(2, '0');
|
||||||
|
const hour = String(current.getHours()).padStart(2, '0');
|
||||||
|
const minute = String(current.getMinutes()).padStart(2, '0');
|
||||||
|
const second = String(current.getSeconds()).padStart(2, '0');
|
||||||
|
const millisecond = String(current.getMilliseconds()).padStart(3, '0');
|
||||||
|
return `${year}${month}${day}${hour}${minute}${second}${millisecond}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRandomAlphaNumericSuffix(length = 4) {
|
||||||
|
const size = Math.max(0, Math.floor(Number(length) || 0));
|
||||||
|
const chars = [];
|
||||||
|
for (let index = 0; index < size; index += 1) {
|
||||||
|
chars.push(RANDOM_SUFFIX_CHARS[getRandomInt(RANDOM_SUFFIX_CHARS.length)] || 'a');
|
||||||
|
}
|
||||||
|
return chars.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRandomNameDateTimeLocalPart(date = new Date(), options = {}) {
|
||||||
|
const dateTimeDigits = formatDateTimeDigits(date);
|
||||||
|
if (!dateTimeDigits) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const suffixLength = Number.isFinite(Number(options.suffixLength))
|
||||||
|
? Math.max(0, Math.floor(Number(options.suffixLength)))
|
||||||
|
: 4;
|
||||||
|
return `${pickRandomEnglishNamePrefix()}${dateTimeDigits}${buildRandomAlphaNumericSuffix(suffixLength)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
buildRandomAlphaNumericSuffix,
|
||||||
|
buildRandomNameDateTimeLocalPart,
|
||||||
|
formatDateTimeDigits,
|
||||||
|
pickRandomEnglishNamePrefix,
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
(function attachGeneratedEmailHelpers(root, factory) {
|
(function attachGeneratedEmailHelpers(root, factory) {
|
||||||
root.MultiPageGeneratedEmailHelpers = factory();
|
root.MultiPageGeneratedEmailHelpers = factory(root);
|
||||||
})(typeof self !== 'undefined' ? self : globalThis, function createGeneratedEmailHelpersModule() {
|
})(typeof self !== 'undefined' ? self : globalThis, function createGeneratedEmailHelpersModule(root = globalThis) {
|
||||||
function createGeneratedEmailHelpers(deps = {}) {
|
function createGeneratedEmailHelpers(deps = {}) {
|
||||||
const {
|
const {
|
||||||
addLog,
|
addLog,
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
normalizeEmailGenerator,
|
normalizeEmailGenerator,
|
||||||
isGeneratedAliasProvider,
|
isGeneratedAliasProvider,
|
||||||
persistRegistrationEmailState = null,
|
persistRegistrationEmailState = null,
|
||||||
|
buildRandomNameDateTimeLocalPart = root.MultiPageEmailLocalPartHelpers?.buildRandomNameDateTimeLocalPart,
|
||||||
reuseOrCreateTab,
|
reuseOrCreateTab,
|
||||||
sendToContentScript,
|
sendToContentScript,
|
||||||
setEmailState,
|
setEmailState,
|
||||||
@@ -59,6 +60,12 @@
|
|||||||
return chars.join('');
|
return chars.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildDefaultGeneratedEmailLocalPart(date = new Date()) {
|
||||||
|
return typeof buildRandomNameDateTimeLocalPart === 'function'
|
||||||
|
? buildRandomNameDateTimeLocalPart(date)
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchCloudflareEmail(state, options = {}) {
|
async function fetchCloudflareEmail(state, options = {}) {
|
||||||
throwIfStopped();
|
throwIfStopped();
|
||||||
const latestState = state || await getState();
|
const latestState = state || await getState();
|
||||||
@@ -165,7 +172,9 @@
|
|||||||
requireAdminAuth: true,
|
requireAdminAuth: true,
|
||||||
requireDomain: true,
|
requireDomain: true,
|
||||||
});
|
});
|
||||||
const requestedName = String(options.localPart || options.name || '').trim().toLowerCase() || generateCloudflareAliasLocalPart();
|
const requestedName = String(options.localPart || options.name || '').trim().toLowerCase()
|
||||||
|
|| buildDefaultGeneratedEmailLocalPart(options.date)
|
||||||
|
|| generateCloudflareAliasLocalPart();
|
||||||
const effectiveDomain = config.effectiveDomain || (
|
const effectiveDomain = config.effectiveDomain || (
|
||||||
typeof buildCloudflareTempEmailEffectiveDomain === 'function'
|
typeof buildCloudflareTempEmailEffectiveDomain === 'function'
|
||||||
? buildCloudflareTempEmailEffectiveDomain(config)
|
? buildCloudflareTempEmailEffectiveDomain(config)
|
||||||
@@ -377,6 +386,8 @@
|
|||||||
fetchCloudflareTempEmailAddress,
|
fetchCloudflareTempEmailAddress,
|
||||||
fetchDuckEmail,
|
fetchDuckEmail,
|
||||||
fetchGeneratedEmail,
|
fetchGeneratedEmail,
|
||||||
|
buildDefaultGeneratedEmailLocalPart,
|
||||||
|
buildRandomNameDateTimeLocalPart,
|
||||||
generateCloudflareAliasLocalPart,
|
generateCloudflareAliasLocalPart,
|
||||||
requestCloudflareTempEmailJson,
|
requestCloudflareTempEmailJson,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,16 +3,38 @@ const assert = require('node:assert/strict');
|
|||||||
const fs = require('node:fs');
|
const fs = require('node:fs');
|
||||||
|
|
||||||
function loadGeneratedEmailHelpersApi() {
|
function loadGeneratedEmailHelpersApi() {
|
||||||
|
const localPartHelpersSource = fs.readFileSync('background/email-local-part-helpers.js', 'utf8');
|
||||||
const source = fs.readFileSync('background/generated-email-helpers.js', 'utf8');
|
const source = fs.readFileSync('background/generated-email-helpers.js', 'utf8');
|
||||||
const globalScope = {};
|
const globalScope = {};
|
||||||
return new Function('self', `${source}; return self.MultiPageGeneratedEmailHelpers;`)(globalScope);
|
return new Function('self', `${localPartHelpersSource}; ${source}; return self.MultiPageGeneratedEmailHelpers;`)(globalScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
test('background imports generated email helper module', () => {
|
test('background imports generated email helper module', () => {
|
||||||
const source = fs.readFileSync('background.js', 'utf8');
|
const source = fs.readFileSync('background.js', 'utf8');
|
||||||
|
assert.match(source, /importScripts\([\s\S]*'background\/email-local-part-helpers\.js'/);
|
||||||
assert.match(source, /importScripts\([\s\S]*'background\/generated-email-helpers\.js'/);
|
assert.match(source, /importScripts\([\s\S]*'background\/generated-email-helpers\.js'/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('email local-part helper builds english name, date-time, and random suffix', () => {
|
||||||
|
const source = fs.readFileSync('background/email-local-part-helpers.js', 'utf8');
|
||||||
|
let randomValues = [];
|
||||||
|
const globalScope = {
|
||||||
|
crypto: {
|
||||||
|
getRandomValues: (buffer) => {
|
||||||
|
buffer[0] = randomValues.shift() || 0;
|
||||||
|
return buffer;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const api = new Function('self', `${source}; return self.MultiPageEmailLocalPartHelpers;`)(globalScope);
|
||||||
|
|
||||||
|
assert.equal(api.formatDateTimeDigits('2026-05-17T08:09:10.123'), '20260517080910123');
|
||||||
|
randomValues = [0, 1, 2, 3];
|
||||||
|
assert.equal(api.buildRandomAlphaNumericSuffix(4), 'abcd');
|
||||||
|
randomValues = [0, 0, 1, 2, 3];
|
||||||
|
assert.equal(api.buildRandomNameDateTimeLocalPart('2026-05-17T08:09:10.123'), 'james20260517080910123abcd');
|
||||||
|
});
|
||||||
|
|
||||||
test('generated email helper module exposes a factory', () => {
|
test('generated email helper module exposes a factory', () => {
|
||||||
const api = loadGeneratedEmailHelpersApi();
|
const api = loadGeneratedEmailHelpersApi();
|
||||||
|
|
||||||
@@ -481,7 +503,7 @@ test('generated email helper uses the regular temp email domain when random subd
|
|||||||
name: requests[0].body.name,
|
name: requests[0].body.name,
|
||||||
domain: 'mail.example.com',
|
domain: 'mail.example.com',
|
||||||
});
|
});
|
||||||
assert.match(requests[0].body.name, /^[a-z0-9]+$/);
|
assert.match(requests[0].body.name, /^[a-z]+[0-9]{17}[a-z0-9]{4}$/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('generated email helper requests random subdomain creation while preserving the returned address', async () => {
|
test('generated email helper requests random subdomain creation while preserving the returned address', async () => {
|
||||||
@@ -639,6 +661,86 @@ test('generated email helper uses fixed subdomain as the effective temp email do
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('generated email helper combines default name format with fixed subdomain payload', async () => {
|
||||||
|
const api = loadGeneratedEmailHelpersApi();
|
||||||
|
const requests = [];
|
||||||
|
const savedEmails = [];
|
||||||
|
|
||||||
|
const helpers = api.createGeneratedEmailHelpers({
|
||||||
|
addLog: async () => {},
|
||||||
|
buildGeneratedAliasEmail: () => {
|
||||||
|
throw new Error('should not build managed alias');
|
||||||
|
},
|
||||||
|
buildCloudflareTempEmailEffectiveDomain: (config) => `${config.subdomainPrefix}.${config.domain}`,
|
||||||
|
buildCloudflareTempEmailHeaders: () => ({ 'x-admin-auth': 'admin-secret' }),
|
||||||
|
CLOUDFLARE_TEMP_EMAIL_GENERATOR: 'cloudflare-temp-email',
|
||||||
|
DUCK_AUTOFILL_URL: 'https://duckduckgo.com/email',
|
||||||
|
fetch: async (url, options = {}) => {
|
||||||
|
requests.push({
|
||||||
|
url,
|
||||||
|
method: options.method,
|
||||||
|
body: options.body ? JSON.parse(options.body) : null,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
text: async () => JSON.stringify({ address: `${requests[0].body.name}@team.mail.example.com` }),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
fetchIcloudHideMyEmail: async () => {
|
||||||
|
throw new Error('should not use icloud generator');
|
||||||
|
},
|
||||||
|
getCloudflareTempEmailAddressFromResponse: (payload) => payload.address,
|
||||||
|
getCloudflareTempEmailConfig: () => ({
|
||||||
|
baseUrl: 'https://temp.example.com',
|
||||||
|
adminAuth: 'admin-secret',
|
||||||
|
customAuth: '',
|
||||||
|
effectiveDomain: 'team.mail.example.com',
|
||||||
|
useRandomSubdomain: true,
|
||||||
|
useFixedSubdomain: true,
|
||||||
|
subdomainPrefix: 'team',
|
||||||
|
domain: 'mail.example.com',
|
||||||
|
}),
|
||||||
|
getState: async () => ({
|
||||||
|
mailProvider: '163',
|
||||||
|
emailGenerator: 'cloudflare-temp-email',
|
||||||
|
}),
|
||||||
|
ensureMail2925AccountForFlow: async () => {
|
||||||
|
throw new Error('should not allocate mail2925 account');
|
||||||
|
},
|
||||||
|
joinCloudflareTempEmailUrl: (baseUrl, path) => `${baseUrl}${path}`,
|
||||||
|
normalizeCloudflareDomain: () => '',
|
||||||
|
normalizeCloudflareTempEmailAddress: (value) => String(value || '').trim().toLowerCase(),
|
||||||
|
normalizeEmailGenerator: (value) => String(value || '').trim().toLowerCase(),
|
||||||
|
isGeneratedAliasProvider: () => false,
|
||||||
|
reuseOrCreateTab: async () => {},
|
||||||
|
sendToContentScript: async () => {
|
||||||
|
throw new Error('should not use duck generator');
|
||||||
|
},
|
||||||
|
setEmailState: async (email) => {
|
||||||
|
savedEmails.push(email);
|
||||||
|
},
|
||||||
|
throwIfStopped: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const email = await helpers.fetchGeneratedEmail({
|
||||||
|
emailGenerator: 'cloudflare-temp-email',
|
||||||
|
}, {
|
||||||
|
generator: 'cloudflare-temp-email',
|
||||||
|
date: '2026-05-17T08:09:10.123',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.match(email, /^[a-z]+20260517080910123[a-z0-9]{4}@team\.mail\.example\.com$/);
|
||||||
|
assert.deepEqual(savedEmails, [email]);
|
||||||
|
assert.equal(requests.length, 1);
|
||||||
|
assert.deepEqual(requests[0].body, {
|
||||||
|
enablePrefix: true,
|
||||||
|
enableRandomSubdomain: false,
|
||||||
|
name: requests[0].body.name,
|
||||||
|
domain: 'team.mail.example.com',
|
||||||
|
});
|
||||||
|
assert.match(requests[0].body.name, /^[a-z]+20260517080910123[a-z0-9]{4}$/);
|
||||||
|
});
|
||||||
|
|
||||||
test('generated email helper honors iCloud always-new fetch mode', async () => {
|
test('generated email helper honors iCloud always-new fetch mode', async () => {
|
||||||
const api = loadGeneratedEmailHelpersApi();
|
const api = loadGeneratedEmailHelpersApi();
|
||||||
const icloudOptions = [];
|
const icloudOptions = [];
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const test = require('node:test');
|
const test = require('node:test');
|
||||||
const assert = require('node:assert/strict');
|
const assert = require('node:assert/strict');
|
||||||
|
|
||||||
|
require('../background/email-local-part-helpers.js');
|
||||||
require('../background/cloudmail-provider.js');
|
require('../background/cloudmail-provider.js');
|
||||||
|
|
||||||
function createProviderApi(options = {}) {
|
function createProviderApi(options = {}) {
|
||||||
@@ -228,3 +229,56 @@ test('fetchCloudMailAddress preserves phone identity through the shared persiste
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('fetchCloudMailAddress builds english name, date-time, and suffix local part by default', async () => {
|
||||||
|
const api = globalThis.MultiPageBackgroundCloudMailProvider.createCloudMailProvider({
|
||||||
|
addLog: async () => {},
|
||||||
|
buildCloudMailHeaders: () => ({}),
|
||||||
|
CLOUD_MAIL_DEFAULT_PAGE_SIZE: 20,
|
||||||
|
CLOUD_MAIL_GENERATOR: 'cloudmail',
|
||||||
|
CLOUD_MAIL_PROVIDER: 'cloudmail',
|
||||||
|
fetchImpl: async (url) => {
|
||||||
|
if (String(url).includes('/api/public/addUser')) {
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
text: async () => JSON.stringify({ code: 200 }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
text: async () => JSON.stringify({ code: 200, data: { token: 'token' } }),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getCloudMailTokenFromResponse: () => 'token',
|
||||||
|
getState: async () => ({}),
|
||||||
|
joinCloudMailUrl: (baseUrl, path) => `${baseUrl}${path}`,
|
||||||
|
normalizeCloudMailAddress: (value) => String(value || '').trim().toLowerCase(),
|
||||||
|
normalizeCloudMailBaseUrl: (value) => String(value || '').trim(),
|
||||||
|
normalizeCloudMailDomain: (value) => String(value || '').trim(),
|
||||||
|
normalizeCloudMailDomains: (values) => values || [],
|
||||||
|
normalizeCloudMailMailApiMessages: () => [],
|
||||||
|
persistRegistrationEmailState: async () => {},
|
||||||
|
pickVerificationMessageWithTimeFallback: () => ({
|
||||||
|
match: null,
|
||||||
|
usedRelaxedFilters: false,
|
||||||
|
usedTimeFallback: false,
|
||||||
|
}),
|
||||||
|
setEmailState: async () => {},
|
||||||
|
setPersistentSettings: async () => {},
|
||||||
|
sleepWithStop: async () => {},
|
||||||
|
throwIfStopped: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
cloudMailBaseUrl: 'https://mail.example.com',
|
||||||
|
cloudMailAdminEmail: 'admin@example.com',
|
||||||
|
cloudMailAdminPassword: 'secret',
|
||||||
|
cloudMailToken: 'token',
|
||||||
|
cloudMailDomain: 'example.com',
|
||||||
|
};
|
||||||
|
const email = await api.fetchCloudMailAddress(state, {
|
||||||
|
date: '2026-05-17T08:09:10.123',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.match(email, /^[a-z]+20260517080910123[a-z0-9]{4}@example\.com$/);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user