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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  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. STEP6_MAX_ATTEMPTS: 3,
  57. throwIfStopped: () => {},
  58. });
  59. await assert.rejects(
  60. () => executor.executeStep7({ email: 'user@example.com', password: 'secret' }),
  61. /已重试 2 次,仍未成功/
  62. );
  63. assert.equal(events.refreshCalls, 3);
  64. assert.equal(events.sendCalls, 3);
  65. assert.equal(events.completed, 0);
  66. });
  67. test('step 7 starts a new oauth timeout window for each refreshed oauth url', async () => {
  68. const source = fs.readFileSync('background/steps/oauth-login.js', 'utf8');
  69. const globalScope = {};
  70. const api = new Function('self', `${source}; return self.MultiPageBackgroundStep7;`)(globalScope);
  71. const events = {
  72. startedWindows: [],
  73. timeoutRequests: [],
  74. };
  75. const executor = api.createStep7Executor({
  76. addLog: async () => {},
  77. completeStepFromBackground: async () => {},
  78. getErrorMessage: (error) => error?.message || String(error || ''),
  79. getLoginAuthStateLabel: (state) => state || 'unknown',
  80. getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs, options) => {
  81. events.timeoutRequests.push({ defaultTimeoutMs, options });
  82. return 5000;
  83. },
  84. getState: async () => ({ email: 'user@example.com', password: 'secret' }),
  85. isStep6RecoverableResult: (result) => result?.step6Outcome === 'recoverable',
  86. isStep6SuccessResult: (result) => result?.step6Outcome === 'success',
  87. refreshOAuthUrlBeforeStep6: async () => 'https://oauth.example/latest',
  88. reuseOrCreateTab: async () => {},
  89. sendToContentScriptResilient: async (_source, _message, options) => ({
  90. step6Outcome: 'success',
  91. usedTimeoutMs: options.timeoutMs,
  92. }),
  93. startOAuthFlowTimeoutWindow: async (payload) => {
  94. events.startedWindows.push(payload);
  95. },
  96. STEP6_MAX_ATTEMPTS: 3,
  97. throwIfStopped: () => {},
  98. });
  99. await executor.executeStep7({ email: 'user@example.com', password: 'secret' });
  100. assert.deepStrictEqual(events.startedWindows, [
  101. { step: 7, oauthUrl: 'https://oauth.example/latest' },
  102. ]);
  103. assert.deepStrictEqual(events.timeoutRequests, [
  104. {
  105. defaultTimeoutMs: 180000,
  106. options: {
  107. step: 7,
  108. actionLabel: 'OAuth 登录并进入验证码页',
  109. },
  110. },
  111. ]);
  112. });