Files
ark-stronghold-protocol/test/skland/captcha.test.js
T
herryyun 7c25c89992
CI / test (ubuntu-latest, node 22) (push) Failing after 33m10s
CI / test (ubuntu-latest, node 24) (push) Failing after 34m41s
CI / tooling (lint, imports, typecheck) (push) Failing after 36m11s
CI / test (windows-latest, node 22) (push) Canceled after 0s
CI / test (windows-latest, node 24) (push) Canceled after 0s
实现账号绑定、持久登录与跨设备对局恢复
2026-10-14 23:22:50 +08:00

67 lines
3.2 KiB
JavaScript

// Provider contract tests use a fake widget and fake proof only; no real captcha or network request is performed.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { setImmediate } from 'node:timers/promises';
import { solveCaptcha } from '../../public/js/skland/captcha.js';
function fakeProvider(proof) {
const events = {};
const calls = { shown: 0, destroyed: 0, options: null };
const widget = {
onReady(fn) { events.ready = fn; }, onSuccess(fn) { events.success = fn; },
onClose(fn) { events.close = fn; }, onError(fn) { events.error = fn; },
getValidate() { return proof; },
showCaptcha() { calls.shown++; }, verify() { calls.shown++; },
destroy() { calls.destroyed++; events.close?.(); },
};
const init = (options, ready) => { calls.options = options; ready(widget); };
return { events, calls, scope: { initGeetest4: init, initGeetest: init }, load: async () => {} };
}
test('captcha v4 opens the provider widget and preserves its proof plus the upstream challenge', async () => {
const proof = { captcha_id: 'TEST', lot_number: 'TEST-LOT', captcha_output: 'TEST-PROOF', pass_token: 'TEST-PASS', gen_time: '1' };
const provider = fakeProvider(proof);
const pending = solveCaptcha({ geetestId: 'TEST', challenge: 'TEST-CHALLENGE', riskType: 'slide' }, provider);
await setImmediate();
assert.equal(provider.calls.shown, 0, 'initialization does not submit a proof');
provider.events.ready();
assert.equal(provider.calls.shown, 1);
assert.deepEqual(provider.calls.options, { captchaId: 'TEST', riskType: 'slide', product: 'bind', language: 'zho' });
provider.events.success();
assert.deepEqual(await pending, { ...proof, challenge: 'TEST-CHALLENGE' });
assert.equal(provider.calls.destroyed, 1);
});
test('captcha v3 preserves the three provider fields for the SMS retry', async () => {
const proof = { geetest_challenge: 'TEST-CH', geetest_validate: 'TEST-VALIDATE', geetest_seccode: 'TEST-SEC' };
const provider = fakeProvider(proof);
const pending = solveCaptcha({ gt: 'TEST', challenge: 'TEST-CH', new_captcha: true }, provider);
await setImmediate();
provider.events.ready();
provider.events.success();
assert.deepEqual(await pending, proof);
assert.equal(provider.calls.options.https, true);
});
test('captcha cancellation never yields a proof, including after a provider retry event', async () => {
const provider = fakeProvider({ captcha_output: 'UNUSED-TEST-PROOF' });
const pending = solveCaptcha({ geetestId: 'TEST' }, provider);
await setImmediate();
provider.events.error({ error_code: 'error_01' });
provider.events.close();
provider.events.success();
assert.equal(await pending, null);
assert.equal(provider.calls.destroyed, 1);
});
test('unsupported or failed captchas are rejected so the UI can offer retry', async () => {
await assert.rejects(() => solveCaptcha({}), /captcha_type/);
await assert.rejects(() => solveCaptcha({ geetestId: 'TEST' }, { load: async () => { throw new Error('captcha_load'); } }), /captcha_load/);
const provider = fakeProvider(null);
const pending = solveCaptcha({ geetestId: 'TEST' }, provider);
const rejected = assert.rejects(pending, /captcha_validate/);
await setImmediate();
provider.events.success();
await rejected;
});