background-step7-recovery.test.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. const test = require('node:test');
  2. const assert = require('node:assert/strict');
  3. const fs = require('node:fs');
  4. const source = fs.readFileSync('background/steps/fetch-login-code.js', 'utf8');
  5. const globalScope = {};
  6. const api = new Function('self', `${source}; return self.MultiPageBackgroundStep8;`)(globalScope);
  7. test('step 8 submits login verification directly without replaying step 7', async () => {
  8. const calls = {
  9. ensureReady: 0,
  10. ensureReadyOptions: [],
  11. executeStep7: 0,
  12. sleep: [],
  13. resolveOptions: null,
  14. };
  15. const realDateNow = Date.now;
  16. Date.now = () => 123456;
  17. const executor = api.createStep8Executor({
  18. addLog: async () => {},
  19. chrome: {
  20. tabs: {
  21. update: async () => {},
  22. },
  23. },
  24. CLOUDFLARE_TEMP_EMAIL_PROVIDER: 'cloudflare-temp-email',
  25. confirmCustomVerificationStepBypass: async () => {},
  26. ensureStep8VerificationPageReady: async (options) => {
  27. calls.ensureReady += 1;
  28. calls.ensureReadyOptions.push(options || null);
  29. return { state: 'verification_page' };
  30. },
  31. executeStep7: async () => {
  32. calls.executeStep7 += 1;
  33. },
  34. getOAuthFlowRemainingMs: async () => 5000,
  35. getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => Math.min(defaultTimeoutMs, 5000),
  36. getMailConfig: () => ({
  37. provider: 'qq',
  38. label: 'QQ 邮箱',
  39. source: 'mail-qq',
  40. url: 'https://mail.qq.com',
  41. navigateOnReuse: false,
  42. }),
  43. getState: async () => ({ email: 'user@example.com', password: 'secret' }),
  44. getTabId: async (sourceName) => (sourceName === 'signup-page' ? 1 : 2),
  45. HOTMAIL_PROVIDER: 'hotmail-api',
  46. isTabAlive: async () => true,
  47. isVerificationMailPollingError: () => false,
  48. LUCKMAIL_PROVIDER: 'luckmail-api',
  49. resolveVerificationStep: async (_step, _state, _mail, options) => {
  50. calls.resolveOptions = options;
  51. },
  52. reuseOrCreateTab: async () => {},
  53. setState: async () => {},
  54. setStepStatus: async () => {},
  55. shouldUseCustomRegistrationEmail: () => false,
  56. sleepWithStop: async (ms) => {
  57. calls.sleep.push(ms);
  58. },
  59. STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS: 25000,
  60. STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS: 8,
  61. throwIfStopped: () => {},
  62. });
  63. try {
  64. await executor.executeStep8({
  65. email: 'user@example.com',
  66. password: 'secret',
  67. oauthUrl: 'https://oauth.example/latest',
  68. });
  69. } finally {
  70. Date.now = realDateNow;
  71. }
  72. assert.equal(calls.resolveOptions.beforeSubmit, undefined);
  73. assert.equal(calls.ensureReady, 1);
  74. assert.equal(calls.executeStep7, 0);
  75. assert.deepStrictEqual(calls.sleep, []);
  76. assert.equal(calls.resolveOptions.filterAfterTimestamp, 123456);
  77. assert.equal(typeof calls.resolveOptions.getRemainingTimeMs, 'function');
  78. assert.equal(await calls.resolveOptions.getRemainingTimeMs({ actionLabel: '登录验证码流程' }), 5000);
  79. assert.equal(calls.resolveOptions.resendIntervalMs, 25000);
  80. assert.deepStrictEqual(calls.ensureReadyOptions, [
  81. { timeoutMs: 5000 },
  82. ]);
  83. });
  84. test('step 8 disables resend interval for 2925 mailbox polling', async () => {
  85. let capturedOptions = null;
  86. const executor = api.createStep8Executor({
  87. addLog: async () => {},
  88. chrome: {
  89. tabs: {
  90. update: async () => {},
  91. },
  92. },
  93. CLOUDFLARE_TEMP_EMAIL_PROVIDER: 'cloudflare-temp-email',
  94. confirmCustomVerificationStepBypass: async () => {},
  95. ensureStep8VerificationPageReady: async () => ({ state: 'verification_page' }),
  96. executeStep7: async () => {},
  97. getOAuthFlowRemainingMs: async () => 8000,
  98. getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => Math.min(defaultTimeoutMs, 8000),
  99. getMailConfig: () => ({
  100. provider: '2925',
  101. label: '2925 邮箱',
  102. source: 'mail-2925',
  103. url: 'https://2925.com',
  104. navigateOnReuse: false,
  105. }),
  106. getState: async () => ({ email: 'user@example.com', password: 'secret' }),
  107. getTabId: async (sourceName) => (sourceName === 'signup-page' ? 1 : 2),
  108. HOTMAIL_PROVIDER: 'hotmail-api',
  109. isTabAlive: async () => true,
  110. isVerificationMailPollingError: () => false,
  111. LUCKMAIL_PROVIDER: 'luckmail-api',
  112. resolveVerificationStep: async (_step, _state, _mail, options) => {
  113. capturedOptions = options;
  114. },
  115. reuseOrCreateTab: async () => {},
  116. setState: async () => {},
  117. setStepStatus: async () => {},
  118. shouldUseCustomRegistrationEmail: () => false,
  119. sleepWithStop: async () => {},
  120. STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS: 25000,
  121. STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS: 8,
  122. throwIfStopped: () => {},
  123. });
  124. await executor.executeStep8({
  125. email: 'user@example.com',
  126. password: 'secret',
  127. oauthUrl: 'https://oauth.example/latest',
  128. });
  129. assert.equal(capturedOptions.resendIntervalMs, 0);
  130. assert.equal(capturedOptions.beforeSubmit, undefined);
  131. assert.equal(typeof capturedOptions.getRemainingTimeMs, 'function');
  132. });
  133. test('step 8 does not rerun step 7 when verification submit lands on add-phone', async () => {
  134. const calls = {
  135. executeStep7: 0,
  136. logs: [],
  137. };
  138. const executor = api.createStep8Executor({
  139. addLog: async (message, level = 'info') => {
  140. calls.logs.push({ message, level });
  141. },
  142. chrome: {
  143. tabs: {
  144. update: async () => {},
  145. },
  146. },
  147. CLOUDFLARE_TEMP_EMAIL_PROVIDER: 'cloudflare-temp-email',
  148. confirmCustomVerificationStepBypass: async () => {},
  149. ensureStep8VerificationPageReady: async () => ({ state: 'verification_page' }),
  150. executeStep7: async () => {
  151. calls.executeStep7 += 1;
  152. },
  153. getOAuthFlowRemainingMs: async () => 8000,
  154. getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => Math.min(defaultTimeoutMs, 8000),
  155. getMailConfig: () => ({
  156. provider: 'qq',
  157. label: 'QQ 邮箱',
  158. source: 'mail-qq',
  159. url: 'https://mail.qq.com',
  160. navigateOnReuse: false,
  161. }),
  162. getState: async () => ({ email: 'user@example.com', password: 'secret' }),
  163. getTabId: async (sourceName) => (sourceName === 'signup-page' ? 1 : 2),
  164. HOTMAIL_PROVIDER: 'hotmail-api',
  165. isTabAlive: async () => true,
  166. isVerificationMailPollingError: () => false,
  167. LUCKMAIL_PROVIDER: 'luckmail-api',
  168. resolveVerificationStep: async () => {
  169. throw new Error('步骤 8:验证码提交后页面进入手机号页面,当前流程无法继续自动授权。 URL: https://auth.openai.com/add-phone');
  170. },
  171. reuseOrCreateTab: async () => {},
  172. setState: async () => {},
  173. setStepStatus: async () => {},
  174. shouldUseCustomRegistrationEmail: () => false,
  175. sleepWithStop: async () => {},
  176. STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS: 25000,
  177. STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS: 8,
  178. throwIfStopped: () => {},
  179. });
  180. await assert.rejects(
  181. () => executor.executeStep8({
  182. email: 'user@example.com',
  183. password: 'secret',
  184. oauthUrl: 'https://oauth.example/latest',
  185. }),
  186. /add-phone/
  187. );
  188. assert.equal(calls.executeStep7, 0);
  189. assert.ok(!calls.logs.some(({ message }) => /准备从步骤 7 重新开始/.test(message)));
  190. });