assistant/tools/learning/test-session-detection.mjs

49 lines
4.2 KiB
JavaScript

import assert from 'node:assert/strict';
import { chromium } from 'playwright';
import { classifyLoginSignals, detectSession, inspectVisibleSessionSignals } from './session-detection.mjs';
// New empty browser + synthetic documents only; never any user account/profile.
const browser = await chromium.launch({ headless: true, channel: process.env.ASSISTANT_TEST_BROWSER_CHANNEL || (process.platform === 'win32' ? 'msedge' : 'chromium') });
const page = await browser.newPage({ viewport: { width: 1280, height: 840 } });
const navigation = '<nav><ul><li><a href="https://www.xiaohongshu.com/explore">首页</a></li><li><a href="https://www.xiaohongshu.com/notification">通知</a></li><li><a href="https://www.xiaohongshu.com/user/profile/fakeaccount123">我<img alt="avatar"></a></li></ul></nav>';
const results = [];
async function check(name, html, state) {
await page.setContent(`<style>body{font:18px sans-serif}nav{display:block;width:180px;height:240px}a,button{display:block;padding:8px}</style>${html}`);
// Reading input values or local session data would make the fixture fail.
await page.evaluate(() => {
Object.defineProperty(HTMLInputElement.prototype, 'value', { get() { throw Error('form value read'); }, configurable: true });
Object.defineProperty(document, 'cookie', { get() { throw Error('cookie read'); }, configurable: true });
});
const signals = await inspectVisibleSessionSignals(page);
assert.equal(classifyLoginSignals(signals).state, state, name);
assert.ok(!JSON.stringify(signals).includes('FakePrivateValue91'));
results.push(name);
}
try {
await check('visible account in main navigation', navigation, 'logged_in');
await check('public note author is not viewer login', '<article><a href="https://www.xiaohongshu.com/user/profile/fakeauthor123">笔记作者</a></article>', 'unknown');
await check('visible login entry', '<button>登录</button>', 'logged_out');
await check('hidden login dialog ignored', navigation + '<div role="dialog" style="display:none">扫码登录<input value="FakePrivateValue91"></div>', 'logged_in');
await check('hidden ancestor ignored', navigation + '<div style="display:none"><div role="dialog">手机号登录</div></div>', 'logged_in');
await check('offscreen login ignored', navigation + '<div role="dialog" style="position:absolute;top:1800px">扫码登录</div>', 'logged_in');
await check('active login dialog wins over old navigation', navigation + '<div role="dialog">扫码登录<input value="FakePrivateValue91"></div>', 'logged_out');
await check('verification widget wins', navigation + '<div class="captcha-container">请完成安全验证</div>', 'verification_required');
await check('hidden verification ignored', navigation + '<div class="captcha-container" hidden>请完成安全验证</div>', 'logged_in');
await check('hidden login text inside visible dialog ignored', navigation + '<div role="dialog"><span hidden>扫码登录</span><p>其他说明</p></div>', 'logged_in');
await check('hidden challenge text inside visible widget ignored', navigation + '<div class="captcha-container" style="height:30px"><span hidden>请完成安全验证</span></div>', 'logged_in');
await check('contradictory entry remains unknown', navigation + '<button>登录</button>', 'unknown');
await check('no stored-login inference', '<h1>欢迎</h1>', 'unknown');
assert.equal((await detectSession(page)).state, 'unknown');
assert.deepEqual((await detectSession(page)).evidence, ['unsupported_platform']);
// The URL facade tests official-host gating against the same synthetic DOM;
// there is no network navigation to a real platform.
await page.setContent(navigation);
const facade = { url: () => 'https://www.xiaohongshu.com/explore', evaluate: (...args) => page.evaluate(...args) };
const detected = await detectSession(facade);
assert.equal(detected.state, 'logged_in');
assert.equal(detected.basis, 'visible_page');
assert.ok(Number.isFinite(Date.parse(detected.observed_at)));
assert.equal((await detectSession({ ...facade, url: () => 'https://xiaohongshu.com.evil.invalid/' })).state, 'unknown');
console.log(JSON.stringify({ result: 'passed', visible_page_cases: results.length, host_boundary: true, no_cookies_or_form_values: true, real_platform: false }));
} finally { await browser.close(); }