Refine school script diagnostics and document-state capture

This commit is contained in:
Codex
2026-10-15 15:15:42 +08:00
parent a4c2df838e
commit 83b0c7e177
16 changed files with 435 additions and 27 deletions
@@ -0,0 +1,31 @@
const assert = require('node:assert/strict');
const fs = require('node:fs');
const vm = require('node:vm');
const path = require('node:path');
const src = fs.readFileSync(path.join(__dirname, '../../lib/features/school/school_browser_diagnostics.dart'), 'utf8');
const script = src.match(/schoolPageDiagnosticsScript = r'''([\s\S]*?)''';/)[1];
const url = 'https://eams.uestc.edu.cn/eams/home.action';
function state({redirect = false, password = false} = {}) {
const document = {
querySelector(s) { assert.equal(s, 'input[type="password"]'); return password ? {} : null; },
querySelectorAll(s) { assert.equal(s, 'link[rel~="stylesheet"]'); return [{sheet: {}}, {sheet: null}]; },
scripts: Array(4), styleSheets: Array(1),
};
// Reading private data or resource URLs must never be necessary.
for (const key of ['cookie', 'body', 'forms']) Object.defineProperty(document, key, {get() { throw Error('PRIVATE_DATA_READ'); }});
const resources = [
{initiatorType: 'script', responseStatus: 202},
{initiatorType: 'link', responseStatus: 202},
{initiatorType: 'script', responseStatus: 200},
{initiatorType: 'fetch', responseStatus: 202},
];
for (const r of resources) Object.defineProperty(r, 'name', {get() { throw Error('PRIVATE_RESOURCE_URL_READ'); }});
return JSON.parse(vm.runInNewContext(script.replace('__EXPECTED_URL__', JSON.stringify(url)), {
document, location: {href: redirect ? 'https://idas.uestc.edu.cn/authserver/login' : url},
window: {jQuery() {}, require: {}, beangle: null}, performance: {getEntriesByType(type) { assert.equal(type, 'resource'); return resources; }},
}));
}
assert.equal(state({redirect: true}), null);
assert.equal(state({password: true}), null);
assert.deepEqual(state(), {scripts: 4, styles: 1, links: 2, loaded: 1, jquery: true, beangle: false, require: false, script202: 1, style202: 1});
console.log('Browser diagnostics: actual counts, fixed library flags, redirect/password guards, no private data reads passed.');
@@ -0,0 +1,121 @@
import 'dart:convert';
import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:hepan_everyday/features/school/school_browser_diagnostics.dart';
import 'package:hepan_everyday/services/diagnostic_log.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../support/fake_school_web.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
test('a same URL reload invalidates a pending page diagnostic', () async {
final platform = FakeSchoolPlatform();
WebViewPlatform.instance = platform;
final web = WebViewController();
const url = 'https://eams.uestc.edu.cn/eams/home.action';
platform.web.url = url;
final pending = Completer<Object>();
platform.web.javascript = (_) => pending.future;
var current = true;
final capture = recordSchoolPageDiagnostics(
web,
'undergraduate',
url,
isCurrent: () => current,
);
await flushSchoolEvents();
current = false;
pending.complete(
jsonEncode({
'scripts': 9345,
'styles': 0,
'links': 0,
'loaded': 0,
'jquery': false,
'beangle': false,
'require': false,
'script202': 0,
'style202': 0,
}),
);
await capture;
expect(
utf8.decode(await DiagnosticLog.instance.export()),
isNot(contains('"count":9345')),
);
});
test('legacy library failures and syntax failures retain useful fixed categories', () {
expect(
schoolConsoleCode('Uncaught ReferenceError: jQuery is not defined'),
'jquery_missing',
);
expect(
schoolConsoleCode(r'Uncaught ReferenceError: $ is not defined'),
'jquery_missing',
);
expect(
schoolConsoleCode('Uncaught ReferenceError: beangle is not defined'),
'beangle_missing',
);
expect(
schoolConsoleCode('Uncaught ReferenceError: require is not defined'),
'require_missing',
);
expect(
schoolConsoleCode("Uncaught SyntaxError: Unexpected token '<'"),
'syntax_error',
);
expect(
schoolConsoleCode(
'Uncaught TypeError: PRIVATE_STUDENT is not a function',
),
'type_error',
);
expect(
schoolConsoleCode(
'Uncaught ReferenceError: PRIVATE_TOKEN is not defined',
),
'reference_error',
);
expect(
schoolConsoleCode('Blocked by CORS policy: PRIVATE_URL'),
'cross_origin',
);
expect(schoolConsoleCode('normal PRIVATE_CONSOLE message'), isNull);
});
test('page state accepts only bounded typed diagnostics and rejects malformed state', () async {
final log = DiagnosticLog(clock: () => DateTime.utc(2026, 10, 15, 5));
final uri = Uri.parse('https://eams.uestc.edu.cn/eams/home.action');
final data = {
'scripts': 12,
'styles': 0,
'links': 4,
'loaded': 0,
'jquery': false,
'beangle': false,
'require': false,
'script202': 3,
'style202': 2,
'html': 'PRIVATE_HTML',
'cookie': 'PRIVATE_COOKIE',
};
recordSchoolPageState(log, 'undergraduate', jsonEncode(data));
var text = utf8.decode(await log.export());
expect(text, contains('"code":"jquery","result":false'));
expect(text, contains('"code":"stylesheet_202","count":2'));
expect(text, isNot(contains('PRIVATE_')));
expect(text, isNot(contains(uri.toString())));
final before = text;
recordSchoolPageState(log, 'undergraduate', 'null');
recordSchoolPageState(
log,
'undergraduate',
jsonEncode({'scripts': -1, 'jquery': 'PRIVATE_TEXT'}),
);
text = utf8.decode(await log.export());
expect(text, before);
});
}