250 lines
7.4 KiB
JavaScript
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);
|
|
});
|