130 lines
5.7 KiB
JavaScript
130 lines
5.7 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
|
|
const source = fs.readFileSync('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 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 step 6', () => {
|
|
const backgroundSource = fs.readFileSync('background.js', 'utf8');
|
|
assert.match(backgroundSource, /isGoPayCheckoutRestartRequiredFailure/);
|
|
assert.match(backgroundSource, /GOPAY_RESTART_FROM_STEP6::/);
|
|
assert.match(backgroundSource, /step === 8 && isGoPayCheckoutRestartRequiredFailure\(err\)/);
|
|
assert.match(backgroundSource, /step = 6/);
|
|
assert.match(backgroundSource, /invalidateDownstreamAfterStepRestart\(5/);
|
|
});
|
|
|
|
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/);
|
|
});
|