background-step6-retry-limit.test.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. const test = require('node:test');
  2. const assert = require('node:assert/strict');
  3. const fs = require('node:fs');
  4. test('step 6 runs cookie cleanup and completes from background', async () => {
  5. const source = fs.readFileSync('background/steps/clear-login-cookies.js', 'utf8');
  6. const globalScope = {};
  7. const api = new Function('self', `${source}; return self.MultiPageBackgroundStep6;`)(globalScope);
  8. const events = {
  9. cleanupCalls: 0,
  10. completedSteps: [],
  11. };
  12. const executor = api.createStep6Executor({
  13. completeStepFromBackground: async (step) => {
  14. events.completedSteps.push(step);
  15. },
  16. runPreStep6CookieCleanup: async () => {
  17. events.cleanupCalls += 1;
  18. },
  19. });
  20. await executor.executeStep6();
  21. assert.equal(events.cleanupCalls, 1);
  22. assert.deepStrictEqual(events.completedSteps, [6]);
  23. });
  24. test('step 7 retries up to configured limit and then fails', async () => {
  25. const source = fs.readFileSync('background/steps/oauth-login.js', 'utf8');
  26. const globalScope = {};
  27. const api = new Function('self', `${source}; return self.MultiPageBackgroundStep7;`)(globalScope);
  28. const events = {
  29. refreshCalls: 0,
  30. sendCalls: 0,
  31. completed: 0,
  32. };
  33. const executor = api.createStep7Executor({
  34. addLog: async () => {},
  35. completeStepFromBackground: async () => {
  36. events.completed += 1;
  37. },
  38. getErrorMessage: (error) => error?.message || String(error || ''),
  39. getLoginAuthStateLabel: (state) => state || 'unknown',
  40. getState: async () => ({ email: 'user@example.com', password: 'secret' }),
  41. isStep6RecoverableResult: (result) => result?.step6Outcome === 'recoverable',
  42. isStep6SuccessResult: (result) => result?.step6Outcome === 'success',
  43. refreshOAuthUrlBeforeStep6: async () => {
  44. events.refreshCalls += 1;
  45. return `https://oauth.example/${events.refreshCalls}`;
  46. },
  47. reuseOrCreateTab: async () => {},
  48. sendToContentScriptResilient: async () => {
  49. events.sendCalls += 1;
  50. return {
  51. step6Outcome: 'recoverable',
  52. state: 'email_page',
  53. message: '当前仍停留在邮箱页。',
  54. };
  55. },
  56. shouldSkipLoginVerificationForCpaCallback: () => false,
  57. skipLoginVerificationStepsForCpaCallback: async () => {},
  58. STEP6_MAX_ATTEMPTS: 3,
  59. throwIfStopped: () => {},
  60. });
  61. await assert.rejects(
  62. () => executor.executeStep7({ email: 'user@example.com', password: 'secret' }),
  63. /已重试 2 次,仍未成功/
  64. );
  65. assert.equal(events.refreshCalls, 3);
  66. assert.equal(events.sendCalls, 3);
  67. assert.equal(events.completed, 0);
  68. });
  69. test('step 7 starts a new oauth timeout window for each refreshed oauth url', async () => {
  70. const source = fs.readFileSync('background/steps/oauth-login.js', 'utf8');
  71. const globalScope = {};
  72. const api = new Function('self', `${source}; return self.MultiPageBackgroundStep7;`)(globalScope);
  73. const events = {
  74. startedWindows: [],
  75. timeoutRequests: [],
  76. };
  77. const executor = api.createStep7Executor({
  78. addLog: async () => {},
  79. completeStepFromBackground: async () => {},
  80. getErrorMessage: (error) => error?.message || String(error || ''),
  81. getLoginAuthStateLabel: (state) => state || 'unknown',
  82. getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs, options) => {
  83. events.timeoutRequests.push({ defaultTimeoutMs, options });
  84. return 5000;
  85. },
  86. getState: async () => ({ email: 'user@example.com', password: 'secret' }),
  87. isStep6RecoverableResult: (result) => result?.step6Outcome === 'recoverable',
  88. isStep6SuccessResult: (result) => result?.step6Outcome === 'success',
  89. refreshOAuthUrlBeforeStep6: async () => 'https://oauth.example/latest',
  90. reuseOrCreateTab: async () => {},
  91. sendToContentScriptResilient: async (_source, _message, options) => ({
  92. step6Outcome: 'success',
  93. usedTimeoutMs: options.timeoutMs,
  94. }),
  95. shouldSkipLoginVerificationForCpaCallback: () => false,
  96. skipLoginVerificationStepsForCpaCallback: async () => {},
  97. startOAuthFlowTimeoutWindow: async (payload) => {
  98. events.startedWindows.push(payload);
  99. },
  100. STEP6_MAX_ATTEMPTS: 3,
  101. throwIfStopped: () => {},
  102. });
  103. await executor.executeStep7({ email: 'user@example.com', password: 'secret' });
  104. assert.deepStrictEqual(events.startedWindows, [
  105. { step: 7, oauthUrl: 'https://oauth.example/latest' },
  106. ]);
  107. assert.deepStrictEqual(events.timeoutRequests, [
  108. {
  109. defaultTimeoutMs: 180000,
  110. options: {
  111. step: 7,
  112. actionLabel: 'OAuth 登录并进入验证码页',
  113. },
  114. },
  115. ]);
  116. });