Files
social-app/apps/test/features/chat/ui_schema_renderer_test.dart
T

278 lines
8.0 KiB
Dart
Raw Normal View History

2026-02-28 13:49:51 +08:00
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:go_router/go_router.dart';
2026-02-28 13:49:51 +08:00
import 'package:social_app/features/chat/ui/widgets/ui_schema_renderer.dart';
void main() {
group('UiSchemaRenderer', () {
testWidgets('renders stack title and badge', (tester) async {
final schema = {
'version': '2.0',
'locale': 'zh-CN',
'status': 'success',
'theme': 'default',
'root': {
'type': 'stack',
'direction': 'vertical',
'appearance': 'card',
'children': [
{'type': 'text', 'role': 'title', 'content': '日程已创建'},
{'type': 'badge', 'label': 'SUCCESS', 'status': 'success'},
],
},
};
await tester.pumpWidget(
MaterialApp(
home: Scaffold(body: UiSchemaRenderer.renderSchema(schema)),
),
);
expect(find.text('日程已创建'), findsOneWidget);
expect(find.text('SUCCESS'), findsOneWidget);
});
testWidgets('renders kv node values', (tester) async {
final schema = {
'version': '2.0',
'root': {
'type': 'stack',
'direction': 'vertical',
'appearance': 'card',
'children': [
{
'type': 'kv',
'items': [
{'key': 'title', 'label': '标题', 'value': '评审会'},
],
},
],
},
};
2026-02-28 13:49:51 +08:00
await tester.pumpWidget(
MaterialApp(
home: Scaffold(body: UiSchemaRenderer.renderSchema(schema)),
),
2026-02-28 13:49:51 +08:00
);
expect(find.text('标题'), findsOneWidget);
expect(find.text('评审会'), findsOneWidget);
2026-02-28 13:49:51 +08:00
});
testWidgets('renders batch result list items in one card', (tester) async {
final schema = {
'version': '2.0',
'root': {
'type': 'stack',
'direction': 'vertical',
'appearance': 'card',
'status': 'warning',
'children': [
{'type': 'text', 'role': 'title', 'content': '日历操作完成'},
{
'type': 'stack',
'direction': 'vertical',
'gap': 8,
'children': [
{
'type': 'stack',
'direction': 'vertical',
'appearance': 'card',
'children': [
{'type': 'text', 'role': 'body', 'content': '#1 create'},
{'type': 'text', 'role': 'caption', 'content': '成功'},
{'type': 'text', 'role': 'caption', 'content': '日程「晨会」已创建'},
],
},
{
'type': 'stack',
'direction': 'vertical',
'appearance': 'card',
'children': [
{'type': 'text', 'role': 'body', 'content': '#2 delete'},
{'type': 'text', 'role': 'caption', 'content': '失败'},
{
'type': 'text',
'role': 'caption',
'content': 'Schedule item not found',
},
],
},
],
},
],
},
};
await tester.pumpWidget(
MaterialApp(
home: Scaffold(body: UiSchemaRenderer.renderSchema(schema)),
),
);
expect(find.text('日历操作完成'), findsOneWidget);
expect(find.text('#1 create'), findsOneWidget);
expect(find.text('#2 delete'), findsOneWidget);
expect(find.text('成功'), findsOneWidget);
expect(find.text('失败'), findsOneWidget);
});
testWidgets('renders fallback for invalid schema', (tester) async {
2026-02-28 13:49:51 +08:00
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: UiSchemaRenderer.renderSchema({'version': '2.0'}),
),
),
2026-02-28 13:49:51 +08:00
);
expect(find.textContaining('无效 UI Schema'), findsOneWidget);
2026-02-28 13:49:51 +08:00
});
testWidgets('handles navigation action by pushing target page', (
tester,
) async {
final schema = {
'version': '2.0',
'root': {
'type': 'stack',
'direction': 'vertical',
'appearance': 'plain',
'children': [
{
'type': 'button',
'label': '查看待办',
'style': 'primary',
'action': {
'type': 'navigation',
'path': '/todo/123',
'params': {'from': 'assistant'},
},
},
],
},
};
final router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) =>
Scaffold(body: UiSchemaRenderer.renderSchema(schema)),
),
GoRoute(
path: '/todo/:id',
builder: (context, state) => Text(
'todo detail ${state.pathParameters['id']} from ${state.uri.queryParameters['from']}',
),
),
],
);
await tester.pumpWidget(MaterialApp.router(routerConfig: router));
await tester.tap(find.text('查看待办'));
await tester.pumpAndSettle();
expect(find.text('todo detail 123 from assistant'), findsOneWidget);
expect(router.canPop(), isTrue);
router.pop();
await tester.pumpAndSettle();
expect(find.text('查看待办'), findsOneWidget);
});
testWidgets('uses replace navigation when replace is true', (tester) async {
final schema = {
'version': '2.0',
'root': {
'type': 'stack',
'direction': 'vertical',
'appearance': 'plain',
'children': [
{
'type': 'button',
'label': '替换跳转',
'style': 'primary',
'action': {
'type': 'navigation',
'path': '/todo/456',
'replace': true,
},
},
],
},
};
final router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) =>
Scaffold(body: UiSchemaRenderer.renderSchema(schema)),
),
GoRoute(
path: '/todo/:id',
builder: (context, state) =>
Text('todo detail ${state.pathParameters['id']}'),
),
],
);
await tester.pumpWidget(MaterialApp.router(routerConfig: router));
await tester.tap(find.text('替换跳转'));
await tester.pumpAndSettle();
expect(find.text('todo detail 456'), findsOneWidget);
expect(router.canPop(), isFalse);
expect(find.text('todo detail 456'), findsOneWidget);
});
testWidgets('does not navigate for placeholder path', (tester) async {
final schema = {
'version': '2.0',
'root': {
'type': 'stack',
'direction': 'vertical',
'appearance': 'plain',
'children': [
{
'type': 'button',
'label': '坏路径',
'style': 'primary',
'action': {'type': 'navigation', 'path': '/todo/:id'},
},
],
},
};
final router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) =>
Scaffold(body: UiSchemaRenderer.renderSchema(schema)),
),
GoRoute(
path: '/todo/:id',
builder: (context, state) => const Text('detail'),
),
],
);
await tester.pumpWidget(MaterialApp.router(routerConfig: router));
await tester.tap(find.text('坏路径'));
await tester.pumpAndSettle();
await tester.pump(const Duration(seconds: 3));
expect(find.text('坏路径'), findsOneWidget);
expect(find.text('detail'), findsNothing);
});
2026-02-28 13:49:51 +08:00
});
}