239 lines
6.3 KiB
JavaScript
239 lines
6.3 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
|
|
const source = fs.readFileSync('background.js', 'utf8');
|
|
|
|
function extractFunction(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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
test('generic stopped record resolves to next unfinished step during execution gap', async () => {
|
|
const bundle = [
|
|
extractFunction('getRunningSteps'),
|
|
extractFunction('inferStoppedRecordStep'),
|
|
extractFunction('resolveAccountRunRecordStatusForStop'),
|
|
extractFunction('extractStoppedStepFromRecordStatus'),
|
|
extractFunction('resolveAccountRunRecordReasonForStop'),
|
|
extractFunction('appendAndBroadcastAccountRunRecord'),
|
|
].join('\n');
|
|
|
|
const api = new Function(`
|
|
const STEP_IDS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
const STOP_ERROR_MESSAGE = '流程已被用户停止。';
|
|
const DEFAULT_STATE = {
|
|
stepStatuses: Object.fromEntries(STEP_IDS.map((step) => [step, 'pending'])),
|
|
};
|
|
let captured = null;
|
|
const accountRunHistoryHelpers = {
|
|
appendAccountRunRecord: async (status, state, reason) => {
|
|
captured = { status, state, reason };
|
|
return { status, state, reason };
|
|
},
|
|
};
|
|
async function broadcastAccountRunHistoryUpdate() {}
|
|
async function getState() {
|
|
return {};
|
|
}
|
|
${bundle}
|
|
return {
|
|
inferStoppedRecordStep,
|
|
resolveAccountRunRecordStatusForStop,
|
|
resolveAccountRunRecordReasonForStop,
|
|
appendAndBroadcastAccountRunRecord,
|
|
getCaptured() {
|
|
return captured;
|
|
},
|
|
};
|
|
`)();
|
|
|
|
const state = {
|
|
email: 'user@example.com',
|
|
password: 'secret',
|
|
stepStatuses: {
|
|
1: 'completed',
|
|
2: 'completed',
|
|
3: 'completed',
|
|
4: 'completed',
|
|
5: 'completed',
|
|
6: 'completed',
|
|
7: 'pending',
|
|
8: 'pending',
|
|
9: 'pending',
|
|
10: 'pending',
|
|
},
|
|
};
|
|
|
|
assert.equal(api.inferStoppedRecordStep(state), 7);
|
|
assert.equal(api.resolveAccountRunRecordStatusForStop('stopped', state), 'step7_stopped');
|
|
assert.equal(api.resolveAccountRunRecordReasonForStop('step7_stopped', '流程已被用户停止。'), '步骤 7 已被用户停止。');
|
|
assert.equal(
|
|
api.resolveAccountRunRecordReasonForStop('step2_stopped', '步骤 2 已使用邮箱,流程尚未完成。'),
|
|
'步骤 2 已停止:邮箱已设置,流程尚未完成。'
|
|
);
|
|
|
|
await api.appendAndBroadcastAccountRunRecord('stopped', state, '流程已被用户停止。');
|
|
assert.deepStrictEqual(api.getCaptured(), {
|
|
status: 'step7_stopped',
|
|
state,
|
|
reason: '步骤 7 已被用户停止。',
|
|
});
|
|
});
|
|
|
|
test('requestStop appends a stopped record for the next unfinished step when no step is running', async () => {
|
|
const bundle = [
|
|
extractFunction('normalizeAutoRunSessionId'),
|
|
extractFunction('clearCurrentAutoRunSessionId'),
|
|
extractFunction('cleanupStep8NavigationListeners'),
|
|
extractFunction('rejectPendingStep8'),
|
|
extractFunction('getRunningSteps'),
|
|
extractFunction('inferStoppedRecordStep'),
|
|
extractFunction('requestStop'),
|
|
].join('\n');
|
|
|
|
const api = new Function(`
|
|
let stopRequested = false;
|
|
let autoRunActive = false;
|
|
let autoRunCurrentRun = 0;
|
|
let autoRunTotalRuns = 1;
|
|
let autoRunAttemptRun = 0;
|
|
let autoRunSessionId = 99;
|
|
let webNavListener = null;
|
|
let webNavCommittedListener = null;
|
|
let step8TabUpdatedListener = null;
|
|
let step8PendingReject = null;
|
|
let resumeWaiter = null;
|
|
const STOP_ERROR_MESSAGE = '流程已被用户停止。';
|
|
const AUTO_RUN_TIMER_KIND_SCHEDULED_START = 'scheduled_start';
|
|
const DEFAULT_STATE = {
|
|
stepStatuses: Object.fromEntries([1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((step) => [step, 'pending'])),
|
|
};
|
|
const stepWaiters = new Map();
|
|
const appended = [];
|
|
const logs = [];
|
|
const chrome = {
|
|
webNavigation: {
|
|
onBeforeNavigate: { removeListener() {} },
|
|
onCommitted: { removeListener() {} },
|
|
},
|
|
tabs: {
|
|
onUpdated: { removeListener() {} },
|
|
},
|
|
};
|
|
|
|
function cancelPendingCommands() {}
|
|
function abortActiveIcloudRequests() {}
|
|
function getPendingAutoRunTimerPlan() {
|
|
return null;
|
|
}
|
|
async function cancelScheduledAutoRun() {}
|
|
async function clearAutoRunTimerAlarm() {}
|
|
function clearStopRequest() {
|
|
stopRequested = false;
|
|
}
|
|
async function addLog(message, level) {
|
|
logs.push({ message, level });
|
|
}
|
|
async function broadcastStopToContentScripts() {}
|
|
async function markRunningStepsStopped() {}
|
|
async function broadcastAutoRunStatus() {}
|
|
async function appendAndBroadcastAccountRunRecord(status, state, reason) {
|
|
appended.push({ status, state, reason });
|
|
return { status, state, reason };
|
|
}
|
|
async function getState() {
|
|
return {
|
|
email: 'user@example.com',
|
|
password: 'secret',
|
|
stepStatuses: {
|
|
1: 'completed',
|
|
2: 'completed',
|
|
3: 'completed',
|
|
4: 'completed',
|
|
5: 'completed',
|
|
6: 'completed',
|
|
7: 'pending',
|
|
8: 'pending',
|
|
9: 'pending',
|
|
10: 'pending',
|
|
},
|
|
};
|
|
}
|
|
|
|
${bundle}
|
|
|
|
return {
|
|
requestStop,
|
|
snapshot() {
|
|
return { appended, logs, stopRequested, autoRunSessionId };
|
|
},
|
|
};
|
|
`)();
|
|
|
|
await api.requestStop();
|
|
const state = api.snapshot();
|
|
|
|
assert.deepStrictEqual(state.appended, [{
|
|
status: 'stopped',
|
|
state: {
|
|
email: 'user@example.com',
|
|
password: 'secret',
|
|
stepStatuses: {
|
|
1: 'completed',
|
|
2: 'completed',
|
|
3: 'completed',
|
|
4: 'completed',
|
|
5: 'completed',
|
|
6: 'completed',
|
|
7: 'pending',
|
|
8: 'pending',
|
|
9: 'pending',
|
|
10: 'pending',
|
|
},
|
|
},
|
|
reason: '流程已被用户停止。',
|
|
}]);
|
|
assert.equal(state.autoRunSessionId, 0);
|
|
assert.equal(state.stopRequested, true);
|
|
});
|