184 lines
6.7 KiB
Dart
184 lines
6.7 KiB
Dart
import 'dart:convert';
|
|
import 'package:aioa_mobile/app/app.dart';
|
|
import 'package:aioa_mobile/app/router.dart';
|
|
import 'package:aioa_mobile/core/config/runtime_config.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:integration_test/integration_test.dart';
|
|
|
|
void main() {
|
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
|
testWidgets('complete MVP frontend business scenario', (tester) async {
|
|
final api = _E2eApi(RuntimeConfig.e2eAccessToken);
|
|
await api.registerDevice('90000000-0000-4000-8000-000000000001');
|
|
await tester.pumpWidget(const ProviderScope(child: AioaApp()));
|
|
await tester.pumpAndSettle(const Duration(seconds: 10));
|
|
expect(find.text('工作台'), findsOneWidget);
|
|
switch (RuntimeConfig.e2eRole) {
|
|
case 'manager':
|
|
await _managerScenario(tester);
|
|
case 'admin':
|
|
await _adminScenario(tester);
|
|
default:
|
|
await _employeeScenario(tester, api);
|
|
}
|
|
});
|
|
}
|
|
|
|
Future<void> _employeeScenario(WidgetTester tester, _E2eApi api) async {
|
|
await tester.tap(find.text('发起请假'));
|
|
await tester.pumpAndSettle(const Duration(seconds: 8));
|
|
expect(find.text('请假申请'), findsOneWidget);
|
|
expect(find.text('千问表单助手'), findsOneWidget);
|
|
expect(find.byKey(const ValueKey('type')), findsOneWidget);
|
|
expect(find.byKey(const ValueKey('startsAt')), findsOneWidget);
|
|
expect(find.byKey(const ValueKey('endsAt')), findsOneWidget);
|
|
expect(find.byKey(const ValueKey('reason')), findsOneWidget);
|
|
appRouter.go('/workspace');
|
|
await tester.pumpAndSettle();
|
|
|
|
final reason = 'iOS MVP 集成测试 ${DateTime.now().millisecondsSinceEpoch}';
|
|
final draft = await api.createDraft(reason);
|
|
await tester.tap(find.text('我的请假申请'));
|
|
await tester.pumpAndSettle(const Duration(seconds: 5));
|
|
expect(find.textContaining(reason), findsOneWidget);
|
|
await tester.tap(find.textContaining(reason));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('草稿'), findsOneWidget);
|
|
await tester.tap(find.text('提交审批'));
|
|
await tester.pumpAndSettle();
|
|
await tester.tap(find.widgetWithText(FilledButton, '确认'));
|
|
await tester.pumpAndSettle(const Duration(seconds: 5));
|
|
expect(find.text('审批中'), findsOneWidget);
|
|
|
|
appRouter.go('/workspace');
|
|
await tester.pumpAndSettle();
|
|
await tester.tap(find.text('AI 助手'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('AI 流程助手'), findsOneWidget);
|
|
await tester.enterText(find.byType(TextField).first, '我最近提交的请假到哪一步了?');
|
|
await tester.tap(find.text('查询进度'));
|
|
await tester.pumpAndSettle(const Duration(seconds: 8));
|
|
expect(find.textContaining('查询失败'), findsWidgets);
|
|
|
|
await tester.tap(find.text('待办'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.textContaining('通知'), findsWidgets);
|
|
await tester.tap(find.text('我的'));
|
|
await tester.pumpAndSettle(const Duration(seconds: 5));
|
|
expect(find.text('登录设备'), findsOneWidget);
|
|
expect(draft['id'], isNotNull);
|
|
}
|
|
|
|
Future<void> _managerScenario(WidgetTester tester) async {
|
|
final employee = _E2eApi(RuntimeConfig.e2eEmployeeToken);
|
|
await employee.registerDevice('90000000-0000-4000-8000-000000000002');
|
|
final reason = '主管 iOS 审批测试 ${DateTime.now().millisecondsSinceEpoch}';
|
|
final draft = await employee.createDraft(
|
|
reason,
|
|
deviceId: '90000000-0000-4000-8000-000000000002',
|
|
);
|
|
await employee.submitDraft(
|
|
draft,
|
|
deviceId: '90000000-0000-4000-8000-000000000002',
|
|
);
|
|
await tester.tap(find.text('待办'));
|
|
await tester.pumpAndSettle(const Duration(seconds: 8));
|
|
expect(find.text(reason), findsOneWidget);
|
|
final taskCard = find.ancestor(
|
|
of: find.text(reason),
|
|
matching: find.byType(Card),
|
|
);
|
|
await tester.tap(find.descendant(of: taskCard, matching: find.text('批准')));
|
|
await tester.pumpAndSettle();
|
|
await tester.tap(find.widgetWithText(FilledButton, '确认批准'));
|
|
await tester.pumpAndSettle(const Duration(seconds: 8));
|
|
expect(find.text('已批准'), findsOneWidget);
|
|
}
|
|
|
|
Future<void> _adminScenario(WidgetTester tester) async {
|
|
await tester.tap(find.text('我的'));
|
|
await tester.pumpAndSettle(const Duration(seconds: 8));
|
|
expect(find.text('OA 管理中心'), findsOneWidget);
|
|
await tester.tap(find.text('OA 管理中心'));
|
|
await tester.pumpAndSettle(const Duration(seconds: 10));
|
|
expect(find.text('指标'), findsOneWidget);
|
|
expect(find.text('组织'), findsOneWidget);
|
|
expect(find.text('流程'), findsOneWidget);
|
|
expect(find.text('审计'), findsOneWidget);
|
|
await tester.tap(find.text('组织'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('新增部门'), findsOneWidget);
|
|
await tester.tap(find.text('流程'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('流程定义'), findsOneWidget);
|
|
await tester.tap(find.text('审计'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.byType(Card), findsWidgets);
|
|
}
|
|
|
|
class _E2eApi {
|
|
_E2eApi(this.token);
|
|
final String token;
|
|
String get base => RuntimeConfig.apiBaseUrl;
|
|
Map<String, String> headers([String? device]) => {
|
|
'Authorization': 'Bearer $token',
|
|
'Content-Type': 'application/json',
|
|
'X-AIOA-Device-Id': ?device,
|
|
};
|
|
static const device = '90000000-0000-4000-8000-000000000001';
|
|
Future<void> registerDevice(String id) async {
|
|
final r = await http.post(
|
|
Uri.parse('$base/devices/register'),
|
|
headers: headers(),
|
|
body: jsonEncode({
|
|
'id': id,
|
|
'name': 'iOS integration test',
|
|
'platform': 'IOS',
|
|
}),
|
|
);
|
|
expect(r.statusCode, 200);
|
|
}
|
|
|
|
Future<Map<String, Object?>> createDraft(
|
|
String reason, {
|
|
String deviceId = device,
|
|
}) async {
|
|
final now = DateTime.now().toUtc().add(const Duration(days: 2));
|
|
final r = await http.post(
|
|
Uri.parse('$base/leave-requests/drafts'),
|
|
headers: {
|
|
...headers(deviceId),
|
|
'Idempotency-Key': 'ios-e2e-${DateTime.now().microsecondsSinceEpoch}',
|
|
},
|
|
body: jsonEncode({
|
|
'type': 'PERSONAL',
|
|
'startsAt': now.toIso8601String(),
|
|
'endsAt': now.add(const Duration(hours: 4)).toIso8601String(),
|
|
'reason': reason,
|
|
'version': 0,
|
|
}),
|
|
);
|
|
expect(r.statusCode, 201);
|
|
return Map<String, Object?>.from(jsonDecode(r.body) as Map);
|
|
}
|
|
|
|
Future<void> submitDraft(
|
|
Map<String, Object?> draft, {
|
|
String deviceId = device,
|
|
}) async {
|
|
final r = await http.post(
|
|
Uri.parse('$base/leave-requests/${draft['id']}/submit'),
|
|
headers: {
|
|
...headers(deviceId),
|
|
'Idempotency-Key':
|
|
'ios-submit-${DateTime.now().microsecondsSinceEpoch}',
|
|
},
|
|
body: jsonEncode({'version': draft['version']}),
|
|
);
|
|
expect(r.statusCode, 200);
|
|
}
|
|
}
|