Files
jiapuapp/tests/a01-password-login-runtime-smoke.js
T
2026-07-27 06:50:23 +08:00

250 lines
7.4 KiB
JavaScript

"use strict";
const assert = require("assert");
const fs = require("fs");
const path = require("path");
const source = fs.readFileSync(
path.join(__dirname, "../pages/auth/a01-entry.vue"),
"utf8",
);
const match = source.match(/<script setup>([\s\S]*?)<\/script>/);
assert(match, "A01 script setup is missing");
const pageScript = match[1].replace(
/import[\s\S]*?from\s+["'][^"']+["'];\s*/g,
"",
);
const createHarnessFactory = (
goRootResult = true,
goRootError = null,
initialSessionToken = "",
captchaRequired = true,
) => new Function(
"goRootResult",
"goRootError",
"initialSessionToken",
"captchaRequired",
`
"use strict";
const calls = [];
const ref = (value) => ({ value });
const onBackPress = () => {};
const showCallbacks = [];
const onShow = (callback) => showCallbacks.push(callback);
const onUnload = () => {};
const AuthPageShell = {};
const AppToast = {};
const TacVerification = {};
const createRequestController = () => ({
abort() {},
bind() {},
release() {},
});
const isRequestCancelled = () => false;
const isSmsDeliveryOutcomeUnknown = () => false;
const createAuthSmsCooldown = ({ onChange }) => ({
start() {
onChange(60);
},
sync() {
onChange(0);
return 0;
},
dispose() {},
});
const appApi = {
async getCaptchaRequirement(payload) {
calls.push({ type: "require", payload: { ...payload } });
return {
required: captchaRequired,
providerCode: "TIANAI",
captchaType: "SLIDER",
sceneCode: "APP_PASSWORD_LOGIN",
ttlSeconds: 300,
};
},
async loginWithPassword(payload) {
calls.push({ type: "password-login", payload: { ...payload } });
return { access_token: "token-password" };
},
async sendSmsCode(payload) {
calls.push({ type: "sms-code", payload: { ...payload } });
return null;
},
async loginWithSms(payload) {
calls.push({ type: "sms-login", payload: { ...payload } });
return { access_token: "token-sms" };
},
};
const AUTH_VERIFICATION_OPERATION = Object.freeze({
PASSWORD_LOGIN: "password-login",
SMS_LOGIN: "sms-login",
});
const createTacRenderContext = (value) => ({ ...value });
const isAuthPhone = (value) => /^1[3-9]\\d{9}$/.test(value);
const normalizeCaptchaRequirement = (value) => {
if (typeof value?.required !== "boolean") {
throw new Error("requirement invalid");
}
return value;
};
const normalizeTacSuccess = (value, expectedRequestId) => {
if (
!value ||
value.requestId !== expectedRequestId ||
typeof value.validToken !== "string" ||
!value.validToken
) {
throw new Error("TAC result invalid");
}
return value;
};
const runtimeConfig = {
baseUrl: "https://backend-api.ddxcjp.cn",
clientId: "client-1",
tenantId: "000000",
};
const calcMD5 = (value) => "md5:" + value;
const session = {
getToken: () => initialSessionToken,
};
const goRoot = async (pageId) => {
calls.push({ type: "go-root", pageId });
if (goRootError) throw goRootError;
return goRootResult;
};
const handleBackPress = () => false;
const openPage = () => {};
const runBackGuard = () => false;
const setTimeout = () => 1;
const clearTimeout = () => {};
const setInterval = () => 1;
const clearInterval = () => {};
${pageScript}
return {
calls,
phone,
password,
agreed,
tacVisible,
tacContext,
submitting,
authenticationCommitted,
feedbackMessage,
submitLogin,
completeTac,
closeTac,
async triggerShow() {
for (const callback of showCallbacks) await callback();
},
setPendingTacAction(value) {
pendingTacAction = value;
},
};
`,
)(goRootResult, goRootError, initialSessionToken, captchaRequired);
const createHarness = (options = {}) =>
createHarnessFactory(
options.goRootResult ?? true,
options.goRootError ?? null,
options.initialSessionToken ?? "",
options.captchaRequired ?? true,
);
const countCalls = (harness, type) =>
harness.calls.filter((call) => call.type === type).length;
const preparePassword = async (harness, passwordValue = "secret-1") => {
harness.phone.value = "13800138000";
harness.password.value = passwordValue;
harness.agreed.value = true;
await harness.submitLogin();
assert.deepStrictEqual(harness.calls.map((call) => call.type), ["require"]);
assert.strictEqual(harness.submitting.value, false);
assert.strictEqual(harness.tacVisible.value, true);
assert(harness.tacContext.value?.requestId?.startsWith("a01-password-"));
};
const completePasswordTac = (harness) =>
harness.completeTac({
requestId: harness.tacContext.value.requestId,
validToken: "ticket-1",
expireSeconds: 300,
});
const run = async () => {
const restored = createHarness({ initialSessionToken: "persisted-token" });
await restored.triggerShow();
assert.deepStrictEqual(
restored.calls.map((call) => call.type),
["go-root"],
"冷启动时已有会话必须直接进入家谱根页",
);
assert.strictEqual(restored.calls[0].pageId, "G01");
assert.strictEqual(restored.authenticationCommitted.value, true);
const success = createHarness();
await preparePassword(success);
assert.strictEqual(countCalls(success, "password-login"), 0);
await completePasswordTac(success);
assert.deepStrictEqual(
success.calls.map((call) => call.type),
["require", "password-login", "go-root"],
);
assert.deepStrictEqual(success.calls[1].payload, {
phone: "13800138000",
passwordHash: "md5:secret-1",
validToken: "ticket-1",
});
assert.strictEqual(success.calls[2].pageId, "G01");
assert.strictEqual(countCalls(success, "require"), 1);
assert.strictEqual(countCalls(success, "sms-code"), 0);
assert.strictEqual(success.tacVisible.value, false);
assert.strictEqual(success.tacContext.value, null);
const noCaptcha = createHarness({ captchaRequired: false });
noCaptcha.phone.value = "13800138000";
noCaptcha.password.value = "secret-1";
noCaptcha.agreed.value = true;
await noCaptcha.submitLogin();
assert.deepStrictEqual(
noCaptcha.calls.map((call) => call.type),
["require", "password-login", "go-root"],
"服务端关闭 password-login TAC 时必须直接走已发布密码登录请求",
);
assert.strictEqual(noCaptcha.tacVisible.value, false);
const navigationRejected = createHarness({ goRootResult: false });
await preparePassword(navigationRejected);
await completePasswordTac(navigationRejected);
assert.strictEqual(navigationRejected.authenticationCommitted.value, true);
assert.match(navigationRejected.feedbackMessage.value, /登录已完成/);
assert.strictEqual(countCalls(navigationRejected, "password-login"), 1);
await navigationRejected.submitLogin();
assert.strictEqual(countCalls(navigationRejected, "password-login"), 1);
assert.strictEqual(countCalls(navigationRejected, "go-root"), 2);
const cancelled = createHarness();
await preparePassword(cancelled);
cancelled.closeTac();
await cancelled.completeTac({
requestId: "stale-request",
validToken: "ticket-stale",
expireSeconds: 300,
});
assert.strictEqual(countCalls(cancelled, "password-login"), 0);
assert.strictEqual(countCalls(cancelled, "sms-code"), 0);
process.stdout.write("A01-PASSWORD-LOGIN-RUNTIME-SMOKE PASS\n");
};
run().catch((error) => {
process.stderr.write(`${error.stack || error.message}\n`);
process.exit(1);
});