feat: add support for running status in account run history and UI
- Enhanced account run history to include 'running' status with appropriate translations and summaries. - Updated logging status to handle new error message format. - Introduced new properties for task progress in create-plus-checkout step. - Modified fill-plus-checkout step to handle additional error details. - Updated account records manager to display running status and summary correctly. - Added CSS styles for running status in account records. - Implemented logic to refresh account run history based on auto-run state changes. - Added tests for running status and idle log restart functionality in auto-run steps. - Improved account records manager tests to validate running state display and failure details.
This commit is contained in:
@@ -57,6 +57,12 @@ const bundle = [
|
||||
extractFunction('isAddPhoneAuthUrl'),
|
||||
extractFunction('isAddPhoneAuthState'),
|
||||
extractFunction('isGpcCheckoutRestartRequiredFailure'),
|
||||
extractFunction('getLatestLogTimestamp'),
|
||||
extractFunction('buildAutoRunStepIdleRestartError'),
|
||||
extractFunction('isAutoRunStepIdleRestartError'),
|
||||
extractFunction('startAutoRunStepIdleLogWatchdog'),
|
||||
extractFunction('runAutoStepActionWithIdleLogWatchdog'),
|
||||
extractFunction('executeStepAndWaitWithAutoRunIdleLogWatchdog'),
|
||||
extractFunction('getPostStep6AutoRestartDecision'),
|
||||
extractFunction('runAutoSequenceFromStep'),
|
||||
].join('\n');
|
||||
@@ -86,6 +92,10 @@ function createHarness(options = {}) {
|
||||
stepIds = Object.keys(stepDefinitions).map(Number).sort((a, b) => a - b),
|
||||
lastStepId = Math.max(...stepIds),
|
||||
finalOAuthChainStartStep = 7,
|
||||
idleLogTimeoutMs = 300000,
|
||||
idleLogCheckIntervalMs = 5000,
|
||||
hangStep = 0,
|
||||
hangBudget = 0,
|
||||
} = options;
|
||||
|
||||
return new Function(`
|
||||
@@ -94,6 +104,10 @@ const LAST_STEP_ID = ${JSON.stringify(lastStepId)};
|
||||
const FINAL_OAUTH_CHAIN_START_STEP = ${JSON.stringify(finalOAuthChainStartStep)};
|
||||
const SIGNUP_METHOD_PHONE = 'phone';
|
||||
const PLUS_PAYMENT_METHOD_GPC_HELPER = 'gpc-helper';
|
||||
const AUTO_RUN_STEP_IDLE_LOG_TIMEOUT_MS = ${JSON.stringify(idleLogTimeoutMs)};
|
||||
const AUTO_RUN_STEP_IDLE_LOG_CHECK_INTERVAL_MS = ${JSON.stringify(idleLogCheckIntervalMs)};
|
||||
const AUTO_RUN_STEP_IDLE_RESTART_MAX_ATTEMPTS = 3;
|
||||
const AUTO_RUN_STEP_IDLE_RESTART_ERROR_PREFIX = 'AUTO_RUN_STEP_IDLE_RESTART::';
|
||||
const LOG_PREFIX = '[test]';
|
||||
const chrome = {
|
||||
tabs: {
|
||||
@@ -102,14 +116,17 @@ const chrome = {
|
||||
};
|
||||
|
||||
let remainingFailures = ${JSON.stringify(failureBudget)};
|
||||
let remainingHangs = ${JSON.stringify(hangBudget)};
|
||||
const events = {
|
||||
steps: [],
|
||||
logs: [],
|
||||
invalidations: [],
|
||||
cancellations: [],
|
||||
stopBroadcasts: 0,
|
||||
};
|
||||
|
||||
async function addLog(message, level = 'info') {
|
||||
events.logs.push({ message, level });
|
||||
events.logs.push({ message, level, timestamp: Date.now() });
|
||||
}
|
||||
|
||||
async function ensureAutoEmailReady() {}
|
||||
@@ -119,6 +136,7 @@ async function getState() {
|
||||
return {
|
||||
stepStatuses: { 3: 'completed' },
|
||||
mailProvider: '163',
|
||||
logs: events.logs,
|
||||
...${JSON.stringify(customState)},
|
||||
};
|
||||
}
|
||||
@@ -140,6 +158,10 @@ function isStepDoneStatus(status) {
|
||||
}
|
||||
async function executeStepAndWait(step) {
|
||||
events.steps.push(step);
|
||||
if (step === ${JSON.stringify(hangStep)} && remainingHangs > 0) {
|
||||
remainingHangs -= 1;
|
||||
return new Promise(() => {});
|
||||
}
|
||||
if (step === ${JSON.stringify(failureStep)} && remainingFailures > 0) {
|
||||
remainingFailures -= 1;
|
||||
throw new Error(${JSON.stringify(failureMessage)});
|
||||
@@ -151,6 +173,12 @@ async function getTabId() {
|
||||
async function invalidateDownstreamAfterStepRestart(step, options = {}) {
|
||||
events.invalidations.push({ step, options });
|
||||
}
|
||||
function cancelPendingCommands(reason = '') {
|
||||
events.cancellations.push(reason);
|
||||
}
|
||||
async function broadcastStopToContentScripts() {
|
||||
events.stopBroadcasts += 1;
|
||||
}
|
||||
function getLoginAuthStateLabel(state) {
|
||||
return state || 'unknown';
|
||||
}
|
||||
@@ -224,6 +252,62 @@ test('auto-run keeps restarting from step 7 after post-login failures without a
|
||||
assert.ok(events.logs.some(({ message }) => /回到步骤 7 重新开始授权流程/.test(message)));
|
||||
});
|
||||
|
||||
test('auto-run restarts the current step after five minutes without new logs', async () => {
|
||||
const harness = createHarness({
|
||||
startStep: 10,
|
||||
failureStep: 0,
|
||||
hangStep: 10,
|
||||
hangBudget: 1,
|
||||
idleLogTimeoutMs: 20,
|
||||
idleLogCheckIntervalMs: 5,
|
||||
});
|
||||
|
||||
const events = await harness.run();
|
||||
|
||||
assert.deepStrictEqual(events.steps, [10, 10]);
|
||||
assert.deepStrictEqual(events.invalidations.map((entry) => entry.step), [9]);
|
||||
assert.equal(events.cancellations.length, 1);
|
||||
assert.equal(events.stopBroadcasts, 1);
|
||||
assert.ok(events.logs.some(({ message }) => /5 分钟没有新日志,准备重新开始当前步骤/.test(message)));
|
||||
});
|
||||
|
||||
test('auto-run applies the idle-log restart watchdog to early steps too', async () => {
|
||||
const harness = createHarness({
|
||||
startStep: 2,
|
||||
failureStep: 0,
|
||||
hangStep: 2,
|
||||
hangBudget: 1,
|
||||
idleLogTimeoutMs: 20,
|
||||
idleLogCheckIntervalMs: 5,
|
||||
});
|
||||
|
||||
const events = await harness.run();
|
||||
|
||||
assert.deepStrictEqual(events.steps, [2, 2, 4, 5, 6, 7, 8, 9, 10]);
|
||||
assert.deepStrictEqual(events.invalidations.map((entry) => entry.step), [1]);
|
||||
assert.equal(events.cancellations.length, 1);
|
||||
assert.equal(events.stopBroadcasts, 1);
|
||||
});
|
||||
|
||||
test('auto-run stops current-step idle restarts after the retry cap', async () => {
|
||||
const harness = createHarness({
|
||||
startStep: 10,
|
||||
failureStep: 0,
|
||||
hangStep: 10,
|
||||
hangBudget: 4,
|
||||
idleLogTimeoutMs: 20,
|
||||
idleLogCheckIntervalMs: 5,
|
||||
});
|
||||
|
||||
const result = await harness.runAndCaptureError();
|
||||
|
||||
assert.ok(result?.error);
|
||||
assert.match(result.error.message, /AUTO_RUN_STEP_IDLE_RESTART::步骤 10/);
|
||||
assert.deepStrictEqual(result.events.steps, [10, 10, 10, 10]);
|
||||
assert.deepStrictEqual(result.events.invalidations.map((entry) => entry.step), [9, 9, 9]);
|
||||
assert.ok(result.events.logs.some(({ message }) => /已连续 3 次因 5 分钟无新日志而重开/.test(message)));
|
||||
});
|
||||
|
||||
test('auto-run stops restarting once add-phone is detected', async () => {
|
||||
const harness = createHarness({
|
||||
failureStep: 7,
|
||||
@@ -429,6 +513,39 @@ test('auto-run restarts GPC checkout from step 6 when task status has no progres
|
||||
assert.ok(events.logs.some(({ message }) => /回到步骤 6 重新创建 GPC 任务/.test(message)));
|
||||
});
|
||||
|
||||
test('auto-run keeps rebuilding GPC checkout beyond three failures', async () => {
|
||||
const plusGpcSteps = {
|
||||
6: { key: 'plus-checkout-create' },
|
||||
7: { key: 'plus-checkout-billing' },
|
||||
10: { key: 'oauth-login' },
|
||||
11: { key: 'fetch-login-code' },
|
||||
12: { key: 'confirm-oauth' },
|
||||
13: { key: 'platform-verify' },
|
||||
};
|
||||
const harness = createHarness({
|
||||
startStep: 6,
|
||||
failureStep: 7,
|
||||
failureBudget: 4,
|
||||
failureMessage: 'GPC_TASK_ENDED::GPC task status stalled, recreate the task.',
|
||||
stepDefinitions: plusGpcSteps,
|
||||
finalOAuthChainStartStep: 10,
|
||||
customState: {
|
||||
stepStatuses: { 3: 'completed' },
|
||||
plusPaymentMethod: 'gpc-helper',
|
||||
plusCheckoutSource: 'gpc-helper',
|
||||
},
|
||||
});
|
||||
|
||||
const events = await harness.run();
|
||||
|
||||
assert.deepStrictEqual(
|
||||
events.steps,
|
||||
[6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 10, 11, 12, 13]
|
||||
);
|
||||
assert.deepStrictEqual(events.invalidations.map((entry) => entry.step), [5, 5, 5, 5]);
|
||||
assert.ok(events.logs.some(({ message }) => /第 4 次/.test(message)));
|
||||
});
|
||||
|
||||
test('auto-run does not restart GPC checkout when Plus account has no free-trial eligibility', async () => {
|
||||
const plusGpcSteps = {
|
||||
6: { key: 'plus-checkout-create' },
|
||||
|
||||
Reference in New Issue
Block a user