const assert = require('assert'); const fs = require('fs'); const path = require('path'); const vm = require('vm'); const appSource = fs.readFileSync(path.join(__dirname, '..', 'public', 'app.js'), 'utf8'); const context = vm.createContext({ console, Set, Map, Date, JSON, Math, String, Object, Array, RegExp }); vm.runInContext(`${appSource}\nthis.__geminiApp = geminiApp;`, context, { filename: 'public/app.js' }); const app = context.__geminiApp(); app.accountLogs = {}; app.addLog = () => {}; app.accounts = [ { email: 'password@example.com', status_reason: 'PASSWORD_WRONG', message: '旧消息已被覆盖', raw_account_line: ' password@example.com|OriginalPass|KEEP-UPPERCASE ', }, { email: 'legacy-password@example.com', status_reason: 'UNKNOWN', message: '❌ 登录失败: 密码错误', password: 'CurrentPass', recovery_email: null, secret_key: 'current2fa', }, { email: 'twofa@example.com', message: 'Google连续两次明确拒绝2FA验证码', raw_account_line: 'twofa@example.com----RawTwoFa', }, { email: 'captcha@example.com', message: '⚠️ 登录受阻: 触发人机验证', raw_account_line: 'captcha@example.com----RawCaptcha', }, { email: 'wait7@example.com', ag_status: 'OAuth等待7天', message: 'OAuth授权失败:验证方式选择页无 Authenticator 选项', raw_account_line: 'wait7@example.com----MustNotExport', }, ]; assert.deepStrictEqual( JSON.parse(JSON.stringify(app.problemExportCategories)), { password: true, twofa: true, captcha: true }, '三类问题号应默认全选', ); assert.strictEqual(app.problemExportCount('password'), 2); assert.strictEqual(app.problemExportCount('twofa'), 1); assert.strictEqual(app.problemExportCount('captcha'), 1); assert.strictEqual(app._problemCategoryForAccount(app.accounts[4]), null, 'OAuth 等待7天不得误归2FA错误'); assert.strictEqual( app._problemExportAccountLine(app.accounts[0]), ' password@example.com|OriginalPass|KEEP-UPPERCASE ', '已保存的原始导入行必须逐字保留', ); assert.strictEqual( app._problemExportAccountLine(app.accounts[1]), 'legacy-password@example.com----CurrentPass--------current2fa', '旧账号应使用当前默认四字段兼容格式', ); const expectedAll = [ '密码错误:', ' password@example.com|OriginalPass|KEEP-UPPERCASE ', 'legacy-password@example.com----CurrentPass--------current2fa', '', '2fa错误:', 'twofa@example.com----RawTwoFa', '', '人机认证:', 'captcha@example.com----RawCaptcha', ].join('\n'); assert.strictEqual(app.buildProblemExportText(), expectedAll, '导出标题、顺序和类别空行应固定'); app.problemExportCategories.twofa = false; assert.strictEqual( app.buildProblemExportText(), [ '密码错误:', ' password@example.com|OriginalPass|KEEP-UPPERCASE ', 'legacy-password@example.com----CurrentPass--------current2fa', '', '人机认证:', 'captcha@example.com----RawCaptcha', ].join('\n'), '取消勾选的类别不应出现在 TXT 中', ); app.problemExportCategories = { password: false, twofa: false, captcha: false }; app.openProblemExportModal(); assert.strictEqual(app.showProblemExportModal, true); assert.deepStrictEqual( JSON.parse(JSON.stringify(app.problemExportCategories)), { password: true, twofa: true, captcha: true }, '每次打开都应恢复默认全选', ); const emittedLogs = []; let downloadedText = null; context.Blob = class BlobStub { constructor(parts) { downloadedText = parts.join(''); } }; context.URL = { createObjectURL: () => 'blob:test', revokeObjectURL: () => {} }; context.document = { body: { appendChild: () => {} }, createElement: () => ({ click: () => {}, remove: () => {} }), }; context.setTimeout = callback => callback(); app.addLog = (message, type) => emittedLogs.push({ message, type }); app.problemExportCategories = { password: true, twofa: false, captcha: false }; app.confirmProblemExport(); assert.strictEqual(downloadedText, [ '密码错误:', ' password@example.com|OriginalPass|KEEP-UPPERCASE ', 'legacy-password@example.com----CurrentPass--------current2fa', ].join('\n')); assert.strictEqual( emittedLogs.at(-1).message, '已导出 2 个问题号(密码错误 2)', '成功日志只应列出本次勾选导出的类别', ); console.log('problem_export.test.js: PASS');