224 lines
7.4 KiB
JavaScript
224 lines
7.4 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
|
|
const guard = require('../erp_session_guard');
|
|
|
|
test('login page branding alone is not treated as an active ERP session', () => {
|
|
const result = guard.classifyErpSession({
|
|
bodyText: '用户登录 USERS LOGIN 立即登录 老挝联泰',
|
|
url: 'https://ltjt.yunzhi.run/System/Mainlt.asp',
|
|
});
|
|
|
|
assert.equal(result.ok, false);
|
|
assert.equal(result.reason, 'erp_login_required');
|
|
});
|
|
|
|
test('real ERP Chinese login-timeout alert is treated as login required', () => {
|
|
const result = guard.classifyErpSession({
|
|
bodyText: '修改团队 应收团款 下单备注',
|
|
dialogMessage: '您还没有登录或登录超时,请重新登录!',
|
|
url: 'https://ltjt.yunzhi.run/System/Business/orders_add.asp?ddid=14083',
|
|
});
|
|
|
|
assert.equal(result.ok, false);
|
|
assert.equal(result.reason, 'erp_login_required');
|
|
});
|
|
|
|
test('real ERP login page with account password captcha text is treated as login required', () => {
|
|
const result = guard.classifyErpSession({
|
|
bodyText: '中文 | ภาษาไทย 账 号: 密 码: 验证码: 刷新验证码',
|
|
url: 'https://ltjt.yunzhi.run/system/loginlt.html',
|
|
});
|
|
|
|
assert.equal(result.ok, false);
|
|
assert.equal(result.reason, 'erp_login_required');
|
|
});
|
|
|
|
test('real ERP Chinese workspace text is treated as an active session', () => {
|
|
const result = guard.classifyErpSession({
|
|
bodyText: '业务操作 独立团计划表 财务操作 统计中心 合作商管理 退出',
|
|
url: 'https://ltjt.yunzhi.run/System/Business/orders.asp',
|
|
});
|
|
|
|
assert.equal(result.ok, true);
|
|
assert.equal(result.reason, 'logged_in');
|
|
});
|
|
|
|
test('new dialog cursor ignores stale login prompts before a fresh action', () => {
|
|
const dialogState = {
|
|
messages: [{ type: 'alert', message: '您还没有登录或登录超时,请重新登录!' }],
|
|
};
|
|
const cursor = guard.dialogCursor(dialogState);
|
|
dialogState.messages.push({ type: 'alert', message: '修改成功/success' });
|
|
|
|
assert.deepEqual(
|
|
guard.dialogMessagesSince(dialogState, cursor),
|
|
[{ type: 'alert', message: '修改成功/success' }]
|
|
);
|
|
assert.doesNotThrow(() => guard.assertNoNewErpLoginRequired(dialogState, cursor, 'test.action'));
|
|
});
|
|
|
|
test('new login-timeout dialog after a fresh action is rejected', () => {
|
|
const dialogState = { messages: [] };
|
|
const cursor = guard.dialogCursor(dialogState);
|
|
dialogState.messages.push({ type: 'alert', message: '您还没有登录或登录超时,请重新登录!' });
|
|
|
|
assert.throws(
|
|
() => guard.assertNoNewErpLoginRequired(dialogState, cursor, 'test.action'),
|
|
(error) => error.code === 'erp_login_required' && /test\.action/.test(error.message)
|
|
);
|
|
});
|
|
|
|
test('ERP workspace text is treated as an active session despite stale dialog text', () => {
|
|
const result = guard.classifyErpSession({
|
|
bodyText: '业务操作 独立团计划表 财务操作 退出',
|
|
dialogMessage: '您还没有登录或登录超时,请重新登录!',
|
|
url: 'https://ltjt.yunzhi.run/System/Business/orders.asp',
|
|
});
|
|
|
|
assert.equal(result.ok, true);
|
|
assert.equal(result.reason, 'logged_in');
|
|
});
|
|
|
|
test('ensureErpSessionReady retries once when target page redirects back to login after manual login', async () => {
|
|
const texts = [
|
|
'用户登录 USERS LOGIN 立即登录',
|
|
'业务操作 财务操作 退出',
|
|
'用户登录 USERS LOGIN 立即登录',
|
|
'业务操作 财务操作 退出',
|
|
'业务操作 财务操作 退出',
|
|
];
|
|
let textIndex = 0;
|
|
const calls = { goto: 0 };
|
|
const page = {
|
|
url: () => 'https://ltjt.yunzhi.run/System/Business/orders.asp',
|
|
frames: () => [{
|
|
evaluate: async () => texts[Math.min(textIndex++, texts.length - 1)],
|
|
}],
|
|
goto: async () => {
|
|
calls.goto += 1;
|
|
},
|
|
waitForTimeout: async () => {},
|
|
};
|
|
|
|
const result = await guard.ensureErpSessionReady(page, null, {
|
|
source: 'test.retryAfterTargetRedirect',
|
|
targetUrl: 'https://ltjt.yunzhi.run/System/Business/orders.asp',
|
|
timeoutMs: 1000,
|
|
pollMs: 1,
|
|
});
|
|
|
|
assert.equal(result.ok, true);
|
|
assert.equal(calls.goto, 2);
|
|
});
|
|
|
|
test('ensureErpSessionReady navigates to the target page when a logged-in main page is detected first', async () => {
|
|
const calls = { goto: 0 };
|
|
let textIndex = 0;
|
|
const texts = [
|
|
'业务操作 财务操作 统计中心 退出',
|
|
'业务操作 独立团计划表 财务操作 退出',
|
|
];
|
|
let currentUrl = 'https://ltjt.yunzhi.run/System/Mainlt.asp';
|
|
const page = {
|
|
url: () => currentUrl,
|
|
frames: () => [{
|
|
evaluate: async () => texts[Math.min(textIndex++, texts.length - 1)],
|
|
}],
|
|
goto: async (url) => {
|
|
calls.goto += 1;
|
|
currentUrl = url;
|
|
},
|
|
waitForTimeout: async () => {},
|
|
};
|
|
|
|
const result = await guard.ensureErpSessionReady(page, null, {
|
|
source: 'test.forceTargetAfterLoggedInMain',
|
|
targetUrl: 'https://ltjt.yunzhi.run/System/Business/orders.asp',
|
|
timeoutMs: 1000,
|
|
pollMs: 1,
|
|
});
|
|
|
|
assert.equal(result.ok, true);
|
|
assert.equal(calls.goto, 1);
|
|
assert.equal(currentUrl, 'https://ltjt.yunzhi.run/System/Business/orders.asp');
|
|
});
|
|
|
|
test('ensureErpSessionReady waits when the target page is still an unknown shell', async () => {
|
|
const texts = [
|
|
'loading...',
|
|
'业务操作 独立团计划表 财务操作 退出',
|
|
];
|
|
let textIndex = 0;
|
|
const page = {
|
|
url: () => 'https://ltjt.yunzhi.run/System/Business/orders.asp',
|
|
frames: () => [{
|
|
evaluate: async () => texts[Math.min(textIndex++, texts.length - 1)],
|
|
}],
|
|
goto: async () => {},
|
|
waitForTimeout: async () => {},
|
|
};
|
|
|
|
const result = await guard.ensureErpSessionReady(page, null, {
|
|
source: 'test.waitForUnknownTargetShell',
|
|
targetUrl: 'https://ltjt.yunzhi.run/System/Business/orders.asp',
|
|
timeoutMs: 1000,
|
|
pollMs: 1,
|
|
});
|
|
|
|
assert.equal(result.ok, true);
|
|
assert.ok(textIndex >= 2);
|
|
});
|
|
|
|
test('waitForErpManualLogin detects a logged-in sibling page in the same browser context', async () => {
|
|
const loginPage = {
|
|
url: () => 'https://ltjt.yunzhi.run/system/loginlt.html',
|
|
frames: () => [{
|
|
evaluate: async () => '鐢ㄦ埛鐧诲綍 USERS LOGIN 绔嬪嵆鐧诲綍',
|
|
}],
|
|
waitForTimeout: async () => {},
|
|
};
|
|
const mainPage = {
|
|
url: () => 'https://ltjt.yunzhi.run/System/Mainlt.asp',
|
|
frames: () => [{
|
|
evaluate: async () => '涓氬姟鎿嶴綔 璐㈠姟鎿嶄綔 缁熻涓績 閫€鍑?',
|
|
}],
|
|
};
|
|
loginPage.context = () => ({
|
|
pages: () => [loginPage, mainPage],
|
|
});
|
|
|
|
const result = await guard.waitForErpManualLogin(loginPage, null, {
|
|
source: 'test.detectSiblingLogin',
|
|
timeoutMs: 20,
|
|
pollMs: 1,
|
|
});
|
|
|
|
assert.equal(result.ok, true);
|
|
assert.equal(result.reason, 'logged_in');
|
|
assert.equal(result.details.detectedPageUrl, 'https://ltjt.yunzhi.run/System/Mainlt.asp');
|
|
});
|
|
|
|
test('waitForErpManualLogin reports browser closure clearly', async () => {
|
|
const page = {
|
|
url: () => 'https://ltjt.yunzhi.run/system/loginlt.html',
|
|
frames: () => [{
|
|
evaluate: async () => '鐢ㄦ埛鐧诲綍 USERS LOGIN',
|
|
}],
|
|
waitForTimeout: async () => {
|
|
throw new Error('page.waitForTimeout: Target page, context or browser has been closed');
|
|
},
|
|
};
|
|
|
|
await assert.rejects(
|
|
() => guard.waitForErpManualLogin(page, null, {
|
|
source: 'test.browserClosed',
|
|
timeoutMs: 1000,
|
|
pollMs: 1,
|
|
}),
|
|
(error) => error.code === 'erp_browser_closed_during_login'
|
|
&& /ERP Chrome was closed/.test(error.message)
|
|
&& /test\.browserClosed/.test(error.message)
|
|
);
|
|
});
|