fix: import unpublished exam rooms and add opt-in school HTML feedback
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:hepan_everyday/services/school_page_diagnostic.dart';
|
||||
import 'package:hepan_everyday/features/school/undergraduate/timetable_adapter.dart';
|
||||
|
||||
void main() {
|
||||
final uri = Uri.parse(
|
||||
'https://eams.uestc.edu.cn/eams/courseTableForStd.action',
|
||||
);
|
||||
test(
|
||||
'leading comments do not remove course declarations from the evidence',
|
||||
() {
|
||||
final store = SchoolPageDiagnostic();
|
||||
addTearDown(store.clear);
|
||||
final source = File('test/fixtures/school/undergraduate-timetable.html')
|
||||
.readAsStringSync()
|
||||
.replaceFirst(
|
||||
'var unitCount = 12;',
|
||||
'// units; keep this declaration\nvar unitCount = 12;',
|
||||
)
|
||||
.replaceAll(
|
||||
'activity = new TaskActivity',
|
||||
'/* course; declaration */activity = new TaskActivity',
|
||||
);
|
||||
expect(
|
||||
UndergraduateTimetableAdapter.extract(source, uri).courses,
|
||||
hasLength(18),
|
||||
);
|
||||
store.remember(source, uri);
|
||||
expect(
|
||||
UndergraduateTimetableAdapter.extract(
|
||||
utf8.decode(store.export()!),
|
||||
uri,
|
||||
).courses,
|
||||
hasLength(18),
|
||||
);
|
||||
},
|
||||
);
|
||||
test('malformed activity and index statements remain malformed in the exported evidence', () {
|
||||
final store = SchoolPageDiagnostic();
|
||||
addTearDown(store.clear);
|
||||
final fixture = File('test/fixtures/school/undergraduate-timetable.html')
|
||||
.readAsStringSync();
|
||||
for (final bad in [
|
||||
fixture.replaceFirst('"教师1"', 'null'),
|
||||
fixture.replaceFirst('2*unitCount+0', 'unknown()'),
|
||||
]) {
|
||||
expect(
|
||||
() => UndergraduateTimetableAdapter.extract(bad, uri),
|
||||
throwsFormatException,
|
||||
);
|
||||
store.remember(bad, uri);
|
||||
final output = utf8.decode(store.export()!);
|
||||
expect(
|
||||
() => UndergraduateTimetableAdapter.extract(output, uri),
|
||||
throwsFormatException,
|
||||
);
|
||||
expect(output, contains(bad.contains('null') ? 'null' : 'unknown()'));
|
||||
}
|
||||
});
|
||||
test('diagnostic snapshot preserves parser evidence but removes login material and active HTML', () {
|
||||
var now = DateTime.utc(2026, 10, 16, 5);
|
||||
final store = SchoolPageDiagnostic(clock: () => now);
|
||||
final fixture = File('test/fixtures/school/undergraduate-timetable.html')
|
||||
.readAsStringSync();
|
||||
store.remember(
|
||||
'''$fixture<input name="password" value="SECRET"><input name="params" value="ticket=SECRET"><a href="https://evil.test/?ticket=SECRET" onclick="steal()">链接</a><script>var token="SECRET";</script>''',
|
||||
uri,
|
||||
);
|
||||
final output = utf8.decode(store.export()!);
|
||||
expect(output, isNot(contains('SECRET')));
|
||||
expect(output, isNot(contains('onclick')));
|
||||
expect(output, isNot(contains('evil.test')));
|
||||
expect(output, contains('Content-Security-Policy'));
|
||||
expect(
|
||||
UndergraduateTimetableAdapter.extract(output, uri).courses,
|
||||
hasLength(18),
|
||||
);
|
||||
now = now.add(const Duration(minutes: 11));
|
||||
expect(store.export(), isNull);
|
||||
});
|
||||
test('only trusted school evidence can be kept and logout clears it', () {
|
||||
final store = SchoolPageDiagnostic();
|
||||
store.remember('<table id="manualArrangeCourseTable"></table>', uri);
|
||||
expect(store.export(), isNotNull);
|
||||
store.clear();
|
||||
expect(store.export(), isNull);
|
||||
store.remember(
|
||||
'<table>secret</table>',
|
||||
Uri.parse('https://idas.uestc.edu.cn/authserver/login'),
|
||||
);
|
||||
expect(store.export(), isNull);
|
||||
store.remember(
|
||||
'<input type="password"><table id="manualArrangeCourseTable"></table>',
|
||||
uri,
|
||||
);
|
||||
expect(store.export(), isNull);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user