| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- const test = require('node:test');
- const assert = require('node:assert/strict');
- const fs = require('node:fs');
- const helperSource = fs.readFileSync('background.js', 'utf8');
- const autoRunModuleSource = fs.readFileSync('background/auto-run-controller.js', 'utf8');
- function extractFunction(source, name) {
- const markers = [`async function ${name}(`, `function ${name}(`];
- const start = markers.map((marker) => source.indexOf(marker)).find((index) => index >= 0);
- if (start < 0) throw new Error(`missing function ${name}`);
- let parenDepth = 0;
- let signatureEnded = false;
- let braceStart = -1;
- for (let i = start; i < source.length; i += 1) {
- const ch = source[i];
- if (ch === '(') parenDepth += 1;
- else if (ch === ')') {
- parenDepth -= 1;
- if (parenDepth === 0) signatureEnded = true;
- } else if (ch === '{' && signatureEnded) {
- braceStart = i;
- break;
- }
- }
- if (braceStart < 0) throw new Error(`missing body for function ${name}`);
- let depth = 0;
- let end = braceStart;
- for (; end < source.length; end += 1) {
- const ch = source[end];
- if (ch === '{') depth += 1;
- if (ch === '}') {
- depth -= 1;
- if (depth === 0) {
- end += 1;
- break;
- }
- }
- }
- return source.slice(start, end);
- }
- const helperBundle = [
- extractFunction(helperSource, 'clearStopRequest'),
- extractFunction(helperSource, 'normalizeAutoRunSessionId'),
- extractFunction(helperSource, 'throwIfStopped'),
- extractFunction(helperSource, 'isStopError'),
- extractFunction(helperSource, 'isStepDoneStatus'),
- extractFunction(helperSource, 'isRestartCurrentAttemptError'),
- extractFunction(helperSource, 'getFirstUnfinishedStep'),
- extractFunction(helperSource, 'hasSavedProgress'),
- extractFunction(helperSource, 'getRunningSteps'),
- extractFunction(helperSource, 'getAutoRunStatusPayload'),
- ].join('\n');
- test('auto-run fresh attempt preserves A4Sky mailbox tab context', async () => {
- const api = new Function('autoRunModuleSource', `
- const self = {};
- const STOP_ERROR_MESSAGE = 'Flow stopped.';
- const AUTO_RUN_MAX_RETRIES_PER_ROUND = 3;
- const AUTO_RUN_RETRY_DELAY_MS = 3000;
- const AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS = 'between_rounds';
- const AUTO_RUN_TIMER_KIND_BEFORE_RETRY = 'before_retry';
- const STEP_IDS = [1,2,3,4,5,6,7,8,9,10];
- const DEFAULT_STATE = {
- stepStatuses: { 1:'pending',2:'pending',3:'pending',4:'pending',5:'pending',6:'pending',7:'pending',8:'pending',9:'pending',10:'pending' },
- };
- let stopRequested = false;
- let runCalls = 0;
- let autoRunSessionId = 0;
- let autoRunSessionSeed = 1000;
- let currentState = {
- ...DEFAULT_STATE,
- stepStatuses: { ...DEFAULT_STATE.stepStatuses },
- vpsUrl: 'https://example.com/vps',
- vpsPassword: 'secret',
- customPassword: '',
- autoRunSkipFailures: false,
- autoRunFallbackThreadIntervalMinutes: 0,
- autoRunDelayEnabled: false,
- autoRunDelayMinutes: 30,
- autoStepDelaySeconds: null,
- mailProvider: 'a4sky',
- emailGenerator: 'duck',
- gmailBaseEmail: '',
- mail2925BaseEmail: '',
- emailPrefix: '',
- inbucketHost: '',
- inbucketMailbox: '',
- cloudflareDomain: '',
- cloudflareDomains: [],
- tabRegistry: {},
- sourceLastUrls: {},
- };
- async function getState() {
- return {
- ...currentState,
- stepStatuses: { ...(currentState.stepStatuses || {}) },
- tabRegistry: { ...(currentState.tabRegistry || {}) },
- sourceLastUrls: { ...(currentState.sourceLastUrls || {}) },
- };
- }
- async function setState(updates) {
- currentState = {
- ...currentState,
- ...updates,
- stepStatuses: updates.stepStatuses ? { ...updates.stepStatuses } : currentState.stepStatuses,
- tabRegistry: updates.tabRegistry ? { ...updates.tabRegistry } : currentState.tabRegistry,
- sourceLastUrls: updates.sourceLastUrls ? { ...updates.sourceLastUrls } : currentState.sourceLastUrls,
- };
- }
- async function resetState() {
- const prev = await getState();
- currentState = {
- ...DEFAULT_STATE,
- stepStatuses: { ...DEFAULT_STATE.stepStatuses },
- vpsUrl: prev.vpsUrl,
- vpsPassword: prev.vpsPassword,
- customPassword: prev.customPassword,
- autoRunSkipFailures: prev.autoRunSkipFailures,
- autoRunFallbackThreadIntervalMinutes: prev.autoRunFallbackThreadIntervalMinutes,
- autoRunDelayEnabled: prev.autoRunDelayEnabled,
- autoRunDelayMinutes: prev.autoRunDelayMinutes,
- autoStepDelaySeconds: prev.autoStepDelaySeconds,
- mailProvider: prev.mailProvider,
- emailGenerator: prev.emailGenerator,
- gmailBaseEmail: prev.gmailBaseEmail,
- mail2925BaseEmail: prev.mail2925BaseEmail,
- emailPrefix: prev.emailPrefix,
- inbucketHost: prev.inbucketHost,
- inbucketMailbox: prev.inbucketMailbox,
- cloudflareDomain: prev.cloudflareDomain,
- cloudflareDomains: [...(prev.cloudflareDomains || [])],
- tabRegistry: { ...(prev.tabRegistry || {}) },
- sourceLastUrls: { ...(prev.sourceLastUrls || {}) },
- };
- }
- async function addLog() {}
- async function broadcastAutoRunStatus(phase, payload = {}) {
- await setState({ ...getAutoRunStatusPayload(phase, payload) });
- }
- async function sleepWithStop() {}
- async function waitForRunningStepsToFinish() { return getState(); }
- async function broadcastStopToContentScripts() {}
- function cancelPendingCommands() {}
- function normalizeAutoRunFallbackThreadIntervalMinutes(value) { return Math.max(0, Math.floor(Number(value) || 0)); }
- async function persistAutoRunTimerPlan() {}
- async function launchAutoRunTimerPlan() { return false; }
- function getPendingAutoRunTimerPlan() { return null; }
- function getErrorMessage(error) { return error?.message || String(error || ''); }
- function createAutoRunSessionId() { autoRunSessionSeed += 1; autoRunSessionId = autoRunSessionSeed; return autoRunSessionId; }
- function throwIfAutoRunSessionStopped(sessionId) { if (sessionId && sessionId !== autoRunSessionId) throw new Error(STOP_ERROR_MESSAGE); throwIfStopped(); }
- const chrome = { runtime: { sendMessage() { return Promise.resolve(); } } };
- function getStopRequested() { return false; }
- async function runAutoSequenceFromStep() {
- runCalls += 1;
- const state = await getState();
- if (runCalls === 2) {
- if (state.tabRegistry['mail-phplife']?.tabId !== 77) {
- throw new Error('fresh auto-run attempt did not preserve mail-phplife tab id');
- }
- if (state.sourceLastUrls['mail-phplife'] !== 'https://mail.phplife.net/?_task=mail&_mbox=INBOX') {
- throw new Error('fresh auto-run attempt did not preserve mail-phplife sourceLastUrl');
- }
- }
- currentState = {
- ...currentState,
- stepStatuses: { 1:'completed',2:'completed',3:'completed',4:'completed',5:'completed',6:'completed',7:'completed',8:'completed',9:'completed',10:'completed' },
- tabRegistry: { 'mail-phplife': { tabId: 77, ready: true } },
- sourceLastUrls: { 'mail-phplife': 'https://mail.phplife.net/?_task=mail&_mbox=INBOX' },
- };
- }
- ${helperBundle}
- ${autoRunModuleSource}
- const runtime = {
- state: { autoRunActive:false, autoRunCurrentRun:0, autoRunTotalRuns:1, autoRunAttemptRun:0, autoRunSessionId:0 },
- get() { return { ...this.state }; },
- set(updates) { this.state = { ...this.state, ...updates }; },
- };
- const controller = self.MultiPageBackgroundAutoRunController.createAutoRunController({
- addLog,
- appendAccountRunRecord: async () => null,
- AUTO_RUN_MAX_RETRIES_PER_ROUND,
- AUTO_RUN_RETRY_DELAY_MS,
- AUTO_RUN_TIMER_KIND_BEFORE_RETRY,
- AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS,
- broadcastAutoRunStatus,
- broadcastStopToContentScripts,
- cancelPendingCommands,
- clearStopRequest,
- createAutoRunSessionId,
- getAutoRunStatusPayload,
- getErrorMessage,
- getFirstUnfinishedStep,
- getPendingAutoRunTimerPlan,
- getRunningSteps,
- getState,
- hasSavedProgress,
- isAddPhoneAuthFailure: () => false,
- isRestartCurrentAttemptError,
- isStopError,
- getStopRequested,
- launchAutoRunTimerPlan,
- normalizeAutoRunFallbackThreadIntervalMinutes,
- persistAutoRunTimerPlan,
- resetState,
- runAutoSequenceFromStep,
- runtime,
- setState,
- sleepWithStop,
- throwIfAutoRunSessionStopped,
- waitForRunningStepsToFinish,
- chrome,
- });
- return {
- autoRunLoop: controller.autoRunLoop,
- snapshot() { return { currentState, runCalls }; },
- };
- `)(autoRunModuleSource);
- await api.autoRunLoop(2, { autoRunSkipFailures: false, mode: 'restart' });
- const snapshot = api.snapshot();
- assert.equal(snapshot.runCalls, 2);
- assert.equal(snapshot.currentState.tabRegistry['mail-phplife']?.tabId, 77);
- assert.equal(snapshot.currentState.sourceLastUrls['mail-phplife'], 'https://mail.phplife.net/?_task=mail&_mbox=INBOX');
- });
|