'use strict'; const assert = require('assert'); const { _private } = require('../src/services/change_password_service'); function locator(overrides = {}) { return { scrollIntoViewIfNeeded: async () => {}, waitFor: async () => {}, isDisabled: async () => false, isVisible: async () => true, click: async () => {}, press: async () => {}, evaluate: async () => true, inputValue: async () => '', ...overrides, }; } (async () => { const calls = []; const button = locator({ click: async (options) => calls.push(['click', options]), evaluate: async () => { calls.push(['domClick']); return true; }, }); const confirm = locator({ press: async key => calls.push(['press', key]), }); const log = () => {}; assert.strictEqual(await _private.submitPasswordForm({}, button, confirm, 1, log), true); assert.deepStrictEqual(calls[0], ['click', { timeout: 10000, noWaitAfter: true }], '首次提交必须使用正常点击且禁止 force'); assert.strictEqual(await _private.submitPasswordForm({}, button, confirm, 2, log), true); assert.deepStrictEqual(calls[1], ['press', 'Enter'], '第二次必须复用原表单并按回车提交'); assert.strictEqual(await _private.submitPasswordForm({}, button, confirm, 3, log), true); assert.deepStrictEqual(calls[2], ['domClick'], '第三次必须走 DOM 按钮点击兜底'); assert.strictEqual(await _private.canRetryPasswordSubmit(button, locator(), confirm), true); assert.strictEqual(await _private.canRetryPasswordSubmit( locator({ isDisabled: async () => true }), locator(), confirm, ), false, '按钮仍禁用时不得重复提交'); assert.strictEqual(_private.hasPasswordFieldsInUrl( 'https://myaccount.google.com/signinoptions/password?password=secret&confirmation_password=secret', ), true, '密码字段进入 URL 时必须触发安全保护'); assert.strictEqual(_private.hasPasswordFieldsInUrl( 'https://myaccount.google.com/signinoptions/password?rapt=token', ), false); assert.strictEqual(_private.hasPasswordFieldsInUrl( 'https://myaccount.google.com/signinoptions/password?NEW_PASSWORD=secret', ), true, '敏感查询参数检查必须忽略大小写'); let passwordSelector = ''; const visibleInputs = await _private.pickVisiblePasswordInputs({ locator: (selector) => { passwordSelector = selector; return { count: async () => 2, nth: () => locator(), }; }, }); assert.strictEqual(visibleInputs.length, 2); assert(passwordSelector.includes('input[name="password"]'), '显示密码后 type 可能变成 text,仍必须按 name 识别密码控件'); let dialogConfirmClicks = 0; const makeDialog = (visible, hasConfirm) => ({ isVisible: async () => visible, locator: () => ({ first: () => ({ isVisible: async () => true }) }), getByRole: (_role, options) => { const isConfirm = /Change password/.test(String(options.name)); if (!isConfirm) return { first: () => ({ isVisible: async () => true }) }; return { count: async () => (hasConfirm ? 1 : 0), nth: () => locator({ click: async () => { dialogConfirmClicks++; } }), }; }, waitFor: async () => {}, }); const dialogs = [makeDialog(true, false), makeDialog(true, true)]; const dialogHandled = await _private.confirmPasswordChangeDialog({ locator: () => ({ count: async () => dialogs.length, nth: index => dialogs[index], }), }, log); assert.strictEqual(dialogHandled, true); assert.strictEqual(dialogConfirmClicks, 1, '必须点击弹窗作用域内的确认按钮'); console.log('change_password_submit_fallback.test.js: PASS'); })().catch(error => { console.error(error); process.exitCode = 1; });