Files
TPSchedule/mobile/test/school/undergraduate_exam_batch_test.dart
T

200 lines
6.6 KiB
Dart

import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
import 'package:hepan_everyday/features/school/undergraduate/session.dart';
import 'package:hepan_everyday/features/school/undergraduate/page_probe.dart';
import 'package:hepan_everyday/domain/school_term.dart';
import 'package:hepan_everyday/domain/school_identity.dart';
import 'package:hepan_everyday/domain/undergraduate_exam_query.dart';
import '../support/fake_school_web.dart';
import 'undergraduate_session_test.dart' show underEvidence;
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
late FakeSchoolPlatform platform;
late UndergraduateSession session;
final query = UndergraduateExamQuery(
term: SchoolTerm(503, school: SupportedSchool.uestcUndergraduate),
types: UndergraduateExamType.values.toSet(),
);
setUp(() {
platform = FakeSchoolPlatform();
WebViewPlatform.instance = platform;
session = UndergraduateSession();
});
tearDown(() => session.dispose());
String exam(int type) {
final text = File('test/fixtures/school/undergraduate-exams.html')
.readAsStringSync();
return text
.replaceAll('examType.id=1', 'examType.id=$type')
.replaceAll(
'name="examType.id" value="1"',
'name="examType.id" value="$type"',
)
.replaceAll(
'期末考试',
type == 2
? '期中考试'
: type == 3
? '补缓考'
: '期末考试',
);
}
Future<void> finish() async {
platform.delegate.started(platform.web.url!);
platform.delegate.finished(platform.web.url!);
for (var i = 0; i < 40; i++) {
await Future<void>.value();
}
}
test(
'all selected exam types complete before publishing one result',
() async {
await session.openExams(query);
platform.web.javascript = (s) async {
final id = int.tryParse(
Uri.parse(platform.web.url!).queryParameters['examType.id'] ?? '',
);
return underEvidence(
platform.web.url!,
html: s == undergraduateCaptureScript && id != null ? exam(id) : null,
kind: 'exams',
);
};
await finish();
expect(
platform.web.loads.last,
query.uriFor(UndergraduateExamType.finalExam).toString(),
);
await finish();
expect(session.pendingExams, isNull);
expect(
platform.web.loads.last,
query.uriFor(UndergraduateExamType.midterm).toString(),
);
await finish();
expect(session.pendingExams, isNull);
await finish();
expect(session.pendingExams!.records, hasLength(12));
expect(session.pendingExams!.requestedExamTypes, {1, 2, 3});
},
);
test('last type failure never publishes partial results', () async {
await session.openExams(query);
platform.web.javascript = (s) async {
final id = int.tryParse(
Uri.parse(platform.web.url!).queryParameters['examType.id'] ?? '',
);
return underEvidence(
platform.web.url!,
html: s == undergraduateCaptureScript && id != null
? (id == 3 ? exam(1) : exam(id))
: null,
kind: 'exams',
);
};
await finish();
await finish();
await finish();
await finish();
expect(session.pendingExams, isNull);
expect(session.message, contains('未完成'));
});
testWidgets('login waiting is excluded and active reads have timeouts', (
tester,
) async {
await session.openExams(query);
platform.web.javascript = (_) async =>
underEvidence(platform.web.url!, authenticated: false);
await finish();
await tester.pump(const Duration(seconds: 121));
expect(session.awaitingAuthentication, isTrue);
expect(session.message, isNot(contains('超时')));
platform.web.javascript = (_) async => underEvidence(platform.web.url!);
await finish();
await tester.pump(const Duration(seconds: 46));
expect(session.message, contains('超时'));
expect(session.pendingExams, isNull);
});
test('cancelled batch ignores late page callbacks', () async {
await session.openExams(query);
final generation = session.pageGeneration;
session.setReadingActive(false);
platform.web.javascript = (_) async =>
underEvidence(platform.web.url!, html: exam(1), kind: 'exams');
await finish();
expect(session.pendingExams, isNull);
expect(session.pageGeneration, greaterThan(generation));
});
test(
'expired authentication discards earlier parts and restarts every type',
() async {
await session.openExams(query);
var renewed = false;
platform.web.javascript = (s) async {
final uri = Uri.parse(platform.web.url!),
id = int.tryParse(uri.queryParameters['examType.id'] ?? '');
return underEvidence(
uri.toString(),
authenticated: uri.host == 'eams.uestc.edu.cn',
html: s == undergraduateCaptureScript && id != null
? exam(id).replaceAll('<td>E', '<td>${renewed ? 'NEW' : 'OLD'}E')
: null,
kind: 'exams',
);
};
await finish();
await finish();
expect(
platform.web.loads.last,
query.uriFor(UndergraduateExamType.midterm).toString(),
);
platform.web.url = 'https://idas.uestc.edu.cn/authserver/login';
await finish();
expect(session.awaitingAuthentication, isTrue);
renewed = true;
platform.web.url = 'https://eams.uestc.edu.cn/eams/';
await finish();
expect(
platform.web.loads.last,
query.uriFor(UndergraduateExamType.finalExam).toString(),
);
await finish();
await finish();
await finish();
expect(
session.pendingExams!.records.every((r) => r.code.startsWith('NEW')),
isTrue,
);
},
);
testWidgets('the overall batch timeout limits individually timely requests', (
tester,
) async {
await session.openExams(query);
platform.web.javascript = (s) async {
final id = int.tryParse(
Uri.parse(platform.web.url!).queryParameters['examType.id'] ?? '',
);
return underEvidence(
platform.web.url!,
html: s == undergraduateCaptureScript && id != null ? exam(id) : null,
kind: 'exams',
);
};
await finish();
await tester.pump(const Duration(seconds: 40));
await finish();
await tester.pump(const Duration(seconds: 40));
await finish();
await tester.pump(const Duration(seconds: 41));
expect(session.pendingExams, isNull);
expect(session.message, contains('超时'));
});
}