const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const source = fs.readFileSync('flows/openai/background/steps/gopay-approve.js', 'utf8'); function extractFunction(name) { const markers = [`async function ${name}(`, `function ${name}(`]; const start = markers .map((marker) => source.indexOf(marker)) .find((index) => index >= 0); if (start < 0) throw new Error(`missing function ${name}`); let parenDepth = 0; let signatureEnded = false; let braceStart = -1; for (let index = start; index < source.length; index += 1) { const ch = source[index]; if (ch === '(') parenDepth += 1; if (ch === ')') { parenDepth -= 1; if (parenDepth === 0) signatureEnded = true; } if (ch === '{' && signatureEnded) { braceStart = index; break; } } let depth = 0; let end = braceStart; for (; end < source.length; end += 1) { const ch = source[end]; if (ch === '{') depth += 1; if (ch === '}') { depth -= 1; if (depth === 0) { end += 1; break; } } } return source.slice(start, end); } test('GoPay OTP always requests manual confirmation even when a previous code exists', () => { const body = extractFunction('requestManualGoPayOtp'); assert.doesNotMatch(body, /if\s*\(existingCode\)\s*\{\s*return existingCode;\s*\}/); assert.match(body, /requestGoPayOtpInput\(\{ code: existingCode \}\)/); assert.match(body, /检测到上次保存的 GoPay 验证码/); }); test('GoPay approve handles final payment details iframe as an action frame', () => { assert.match(source, /GOPAY_PAYMENT_FRAME_URL_PATTERN/); assert.match(source, /payment\\\/details/); assert.match(source, /app\\\/challenge/); assert.match(source, /inspectGoPayFramesByDom/); assert.match(source, /getGoPayDomFramePriority/); assert.match(source, /paymentFrames/); assert.match(source, /frameState\?\.hasPayNowButton/); assert.match(source, /getGoPayDomFrameKind/); assert.match(source, /return 'payment'/); assert.match(source, /sendGoPayFrameCommand\(tabId, actionFrameId, 'GOPAY_CLICK_PAY_NOW'/); assert.match(source, /getGoPayDebuggerTargets/); assert.match(source, /chrome\.debugger\.getTargets/); assert.match(source, /targetId: picked\.targetId/); assert.match(source, /sendGoPayDebuggerTargetCommand\(actionTargetId, 'GOPAY_CLICK_PAY_NOW'/); assert.match(source, /sendGoPayDebuggerTargetCommand\(actionTargetId, 'GOPAY_SUBMIT_PIN'/); assert.match(source, /Input\.insertText/); assert.match(source, /最终 Bayar 确认/); }); test('GoPay debugger click does not reuse iframe-relative rects as top-level coordinates', () => { const body = extractFunction('clickGoPayTargetWithDebugger'); assert.match(body, /Number\.isInteger\(frameId\)/); assert.match(body, /debugger_click_skipped_for_frame_target/); assert.ok( body.indexOf('debugger_click_skipped_for_frame_target') < body.indexOf('clickWithDebugger(tabId, rect)') ); }); test('GoPay approve treats merchant validate-pin iframe as PIN entry frame', () => { assert.match(source, /GOPAY_PIN_FRAME_URL_PATTERN/); assert.match(source, /payment\\\/validate-pin/); assert.match(source, /kind: 'pin'/); assert.match(source, /GOPAY_SUBMIT_PIN/); }); test('GoPay approve closes terminal checkout but does not restart on top-level Pay now alone', () => { assert.match(source, /GOPAY_RESTART_FROM_STEP6::/); assert.match(source, /restartGoPayCheckoutFromStep6/); assert.match(source, /chrome\?\.tabs\?\.remove/); assert.match(source, /handleGoPayTerminalError\(pageState, tabId\)/); assert.match(source, /nextState\.hasTerminalError/); assert.doesNotMatch(source, /GoPay 顶层 Pay now 兜底点击后仍未进入下一步,当前支付会话需要重新创建/); }); test('GoPay approve falls back to clicking Bayar inside any iframe before top-level Pay now retry', () => { assert.match(source, /clickGoPayPayButtonInAnyFrame/); assert.match(source, /data-testid/); assert.match(source, /pay-button/); assert.match(source, /已在 GoPay iframe 中点击 Bayar 按钮/); assert.match(source, /不再自动回退步骤 6/); }); test('GoPay approve does not treat phone linking page as debugger iframe action', () => { assert.match(source, /type === 'tel'/); assert.match(source, /const hasContinueButton = !hasPayNowButton && !hasPhoneInput/); assert.match(source, /filter\(\(target\) => target\.type === 'iframe'\)/); }); test('GoPay approve waits and retries slowly on Hubungkan linking page', () => { assert.match(source, /GOPAY_LINKING_RETRY_WAIT_MS/); assert.match(source, /GOPAY_LINKING_STABLE_WAIT_MS/); assert.match(source, /createGoPayStableStateTracker/); assert.match(source, /clickGoPayContinueBestEffort/); assert.match(source, /hubungkan\|sambungkan\|tautkan/); assert.match(source, /先等待 linking 页面加载\/跳转/); assert.match(source, /改用兜底点击 Hubungkan\/确认按钮/); assert.doesNotMatch(source, /GoPay 确认按钮点击后页面仍未变化,已暂停自动重复点击。请手动点击页面上的确认按钮,插件会继续等待后续页面。/); }); test('background auto-run routes GoPay restart sentinel back to checkout-create node', () => { const backgroundSource = fs.readFileSync('background.js', 'utf8'); assert.match(backgroundSource, /isGoPayCheckoutRestartRequiredFailure/); assert.match(backgroundSource, /GOPAY_RESTART_FROM_STEP6::/); assert.match(backgroundSource, /nodeId === 'paypal-approve' && isGoPayCheckoutRestartRequiredFailure\(err\)/); assert.match(backgroundSource, /getNodeIndex\(await getState\(\), 'plus-checkout-create'\)/); assert.match(backgroundSource, /invalidateDownstreamAfterAutoRunNodeRestart\(getPreviousNodeId\('plus-checkout-create'/); }); test('GoPay approve gives PIN precedence over OTP on ambiguous second PIN pages', () => { assert.match(source, /pageState\.hasPinInput && !pinSubmitted/); assert.match(source, /pageState\.hasOtpInput && !pageState\.hasPinInput && !otpSubmitted/); assert.ok(source.indexOf('pageState.hasPinInput && !pinSubmitted') < source.indexOf('pageState.hasOtpInput && !pageState.hasPinInput && !otpSubmitted')); assert.doesNotMatch(source, /otp\|one\[-\\s\]\*time\|kode\|verification\|whatsapp\|code\|pin-input-field/); });