feat(mobile): UI-03 forms and collaboration pages (09-12)
- Page 09 create: grouped form (Event details / Description & languages), required default-language selector, zh-CN/en tabs with per-locale title/description/venue/address, explicit timezone field, bottom save button kept keyboard-reachable, create mode has no Access tab - Page 10 edit: Details/Access tabs sharing one draft; Access has four switches with explanations; original downloads constrained by downloads (disabled + forced off in PATCH); unsaved-changes PopScope confirm; save commits all changes in one minimal PATCH - Page 11 members: Owner group + Collaborators list with initial avatars and per-row remove; full-width Invite colleague opens account-search bottom sheet (debounced prefix search, race guard, already-added state); remove confirm names the member - Page 12 categories: extracted to standalone page with per-row edit/ delete menu and New category sheet (language tabs + name + sort order); delete confirm mentions photo impact; collaborator browse-only; old inline section removed from album detail - l10n: new form/members/categories keys both locales; regenerated - tests: form 14, members 6, categories 7, detail nav 1; full suite green Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
/// 相册创建/编辑表单 widget 测试(P3 计划 Task 4)。
|
||||
/// 相册创建/编辑表单 widget 测试(UI-03 §4 页面 09-10 重设计后)。
|
||||
///
|
||||
/// 覆盖:校验矩阵(默认语言标题缺失/未选开始时间/endAt 早于 startAt)、
|
||||
/// 创建成功 POST 载荷形状、编辑预加载(字段与四开关初值、创建仅两开关)、
|
||||
/// PATCH 最小化(仅拨动的开关出现在请求体)、endAt 显式清除、
|
||||
/// 标题修改仅发全量 translations。
|
||||
/// 覆盖:创建两分组结构(Event details / Description & languages)与默认
|
||||
/// 语言必选、无 Access 页签;校验矩阵(默认语言标题缺失/未选开始时间/
|
||||
/// endAt 早于 startAt);创建 POST 载荷形状;编辑 Details/Access 页签共享
|
||||
/// 草稿(切页签不丢输入)、四开关与说明、原图下载受总下载约束(禁用 +
|
||||
/// PATCH 联动关断)、PATCH 最小化、endAt 显式清除、标题修改仅发全量
|
||||
/// translations;未保存退出确认;保存失败保留输入。
|
||||
library;
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
@@ -11,6 +13,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:photo_live/core/api_client.dart';
|
||||
import 'package:photo_live/core/error_codes.dart';
|
||||
import 'package:photo_live/features/albums/album_form.dart';
|
||||
import 'package:photo_live/features/albums/models.dart';
|
||||
import 'package:photo_live/l10n/app_localizations.dart';
|
||||
@@ -78,43 +81,43 @@ void main() {
|
||||
await tester.tap(finder);
|
||||
}
|
||||
|
||||
testWidgets('三分组结构(UI-01 §4.5):活动信息/多语言内容/访问设置 + 双语 tab',
|
||||
/// 切到 Access 页签(编辑模式顶栏 TabBar)。
|
||||
Future<void> openAccessTab(WidgetTester tester) async {
|
||||
await tester.tap(find.text('Access'));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
testWidgets('页面 09 创建:两分组 + 默认语言必选 + 双语 tab + 无 Access 页签',
|
||||
(tester) async {
|
||||
final adapter = FakeAdapter((_) => throw StateError('不应发起请求'));
|
||||
await pumpForm(tester, clientOf(adapter));
|
||||
|
||||
// 三个分组标题(SectionHeader)。
|
||||
expect(find.text('Event info'), findsOneWidget);
|
||||
expect(find.text('Multilingual content'), findsOneWidget);
|
||||
expect(find.text('Access settings'), findsOneWidget);
|
||||
|
||||
// 多语言内容:zh-CN/en 双语 tab(当前 zh tab → 标题已填默认语言星标)。
|
||||
// 「中文/English」同时出现在内容 tab 与默认语言分段选择中,需锚定 TabBar。
|
||||
final tabBar = find.byType(TabBar);
|
||||
expect(find.descendant(of: tabBar, matching: find.text('中文')), findsOneWidget);
|
||||
expect(find.text('Create album'), findsNWidgets(2), reason: '标题与底部按钮');
|
||||
// 两个分组标题(SectionHeader)。
|
||||
expect(find.text('Event details'), findsOneWidget);
|
||||
expect(find.text('Description & languages'), findsOneWidget);
|
||||
// 默认语言选择器(必选标记)+ 语言 tab(创建模式仅一个 TabBar)。
|
||||
expect(find.text('Default language *'), findsOneWidget);
|
||||
expect(find.byType(TabBar), findsOneWidget);
|
||||
expect(find.descendant(of: find.byType(TabBar), matching: find.text('中文')),
|
||||
findsOneWidget);
|
||||
expect(
|
||||
find.descendant(of: tabBar, matching: find.text('English')), findsOneWidget);
|
||||
expect(find.text('Title *'), findsOneWidget, reason: '默认语言标题必填星标');
|
||||
|
||||
// 切到 en tab → 英文字段(非默认语言,无星标)。
|
||||
await tester.tap(find.descendant(of: tabBar, matching: find.text('English')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Title'), findsOneWidget);
|
||||
expect(find.text('Title *'), findsNothing);
|
||||
|
||||
// 访问设置:默认语言分段选择(两项)。
|
||||
find.descendant(of: find.byType(TabBar), matching: find.text('English')),
|
||||
findsOneWidget);
|
||||
expect(find.byType(SegmentedButton<String>), findsOneWidget);
|
||||
// 创建模式:无 Access 页签、无开关(契约缺省)。
|
||||
expect(find.text('Access'), findsNothing);
|
||||
expect(find.byType(SwitchListTile), findsNothing);
|
||||
// 默认时区显式可见(不静默取设备时区)。
|
||||
expect(find.text('Asia/Shanghai'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('创建:空表单提交 → 汇总错误(默认语言标题/开始时间),不发请求',
|
||||
testWidgets('页面 09 创建:空表单提交 → 汇总错误(默认语言标题/开始时间),不发请求',
|
||||
(tester) async {
|
||||
final adapter = FakeAdapter((_) => throw StateError('不应发起请求'));
|
||||
await pumpForm(tester, clientOf(adapter));
|
||||
|
||||
// 创建模式仅两开关(download/original)。
|
||||
expect(find.byType(SwitchListTile), findsNWidgets(2));
|
||||
|
||||
await tapVisible(tester, find.text('Create'));
|
||||
await tapVisible(tester, find.widgetWithText(FilledButton, 'Create album'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Default locale must have a title'), findsOneWidget);
|
||||
@@ -123,7 +126,7 @@ void main() {
|
||||
expect(adapter.requests, isEmpty);
|
||||
});
|
||||
|
||||
testWidgets('创建:defaultLocale=en 而 en 标题空 → 默认语言覆盖错误',
|
||||
testWidgets('页面 09 创建:defaultLocale=en 而 en 标题空 → 默认语言覆盖错误',
|
||||
(tester) async {
|
||||
final adapter = FakeAdapter((_) => throw StateError('不应发起请求'));
|
||||
await pumpForm(
|
||||
@@ -132,14 +135,14 @@ void main() {
|
||||
initialDraft: createDraft(defaultLocale: 'en', enTitle: ''),
|
||||
);
|
||||
|
||||
await tapVisible(tester, find.text('Create'));
|
||||
await tapVisible(tester, find.widgetWithText(FilledButton, 'Create album'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Default locale must have a title'), findsOneWidget);
|
||||
expect(adapter.requests, isEmpty);
|
||||
});
|
||||
|
||||
testWidgets('创建:endAt 早于 startAt → 时间序错误', (tester) async {
|
||||
testWidgets('页面 09 创建:endAt 早于 startAt → 时间序错误', (tester) async {
|
||||
final adapter = FakeAdapter((_) => throw StateError('不应发起请求'));
|
||||
await pumpForm(
|
||||
tester,
|
||||
@@ -149,14 +152,14 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
await tapVisible(tester, find.text('Create'));
|
||||
await tapVisible(tester, find.widgetWithText(FilledButton, 'Create album'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('End time must be after start time'), findsOneWidget);
|
||||
expect(adapter.requests, isEmpty);
|
||||
});
|
||||
|
||||
testWidgets('创建:合法提交 → POST 载荷形状(双语 translations/UTC 时间/开关)',
|
||||
testWidgets('页面 09 创建:合法提交 → POST 载荷形状(双语 translations/UTC 时间)',
|
||||
(tester) async {
|
||||
final adapter = FakeAdapter(
|
||||
(o) => o.path == '/v1/albums'
|
||||
@@ -168,12 +171,10 @@ void main() {
|
||||
clientOf(adapter),
|
||||
initialDraft: createDraft(
|
||||
endAt: DateTime.utc(2026, 9, 19, 10, 0),
|
||||
downloadEnabled: false,
|
||||
originalDownloadEnabled: true,
|
||||
),
|
||||
);
|
||||
|
||||
await tapVisible(tester, find.text('Create'));
|
||||
await tapVisible(tester, find.widgetWithText(FilledButton, 'Create album'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final request = adapter.requests.single;
|
||||
@@ -200,12 +201,37 @@ void main() {
|
||||
'startAt': '2026-09-18T06:30:00.000Z',
|
||||
'endAt': '2026-09-19T10:00:00.000Z',
|
||||
'timezone': 'Asia/Shanghai',
|
||||
'downloadEnabled': false,
|
||||
'originalDownloadEnabled': true,
|
||||
// 创建无 Access 页签:download/original 走契约缺省。
|
||||
'downloadEnabled': true,
|
||||
'originalDownloadEnabled': false,
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('编辑:预加载既有值(字段、四开关独立初值、无变更时保存禁用)',
|
||||
testWidgets('页面 09 创建:保存失败 → 提示并保留输入(不弹回)', (tester) async {
|
||||
final adapter = FakeAdapter(
|
||||
(o) => o.path == '/v1/albums'
|
||||
? (500, errorBody(ErrorCodes.internalError))
|
||||
: throw StateError('unexpected ${o.path}'),
|
||||
);
|
||||
await pumpForm(tester, clientOf(adapter), initialDraft: createDraft());
|
||||
|
||||
await tapVisible(tester, find.widgetWithText(FilledButton, 'Create album'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(AlbumFormPage), findsOneWidget, reason: '失败不弹回');
|
||||
expect(
|
||||
find.text('Service temporarily unavailable. Please retry later'),
|
||||
findsOneWidget,
|
||||
);
|
||||
// 输入保留。
|
||||
expect(
|
||||
tester.widget<TextFormField>(find.byType(TextFormField).at(1)).controller!
|
||||
.text,
|
||||
'开幕晚宴',
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('页面 10 编辑:Details/Access 页签 + 预填 + 无变更保存禁用',
|
||||
(tester) async {
|
||||
final adapter = FakeAdapter(
|
||||
(o) => throw StateError('编辑预加载不应发起请求:${o.path}'),
|
||||
@@ -219,14 +245,98 @@ void main() {
|
||||
);
|
||||
await pumpForm(tester, clientOf(adapter), existing: existing);
|
||||
|
||||
// 编辑模式四开关。
|
||||
expect(find.byType(SwitchListTile), findsNWidgets(4));
|
||||
// 字段预填(当前 zh tab;UI-01 分组后 TextFormField 顺序 = 时区 →
|
||||
// 标题(zh tab),标题取第 2 个)。
|
||||
// 顶栏 TabBar(Details/Access)+ 内容区语言 TabBar。
|
||||
expect(find.text('Edit album'), findsOneWidget);
|
||||
expect(find.text('Details'), findsOneWidget);
|
||||
expect(find.text('Access'), findsOneWidget);
|
||||
expect(find.byType(TabBar), findsNWidgets(2));
|
||||
expect(find.text('Save changes'), findsOneWidget);
|
||||
// 字段预填(zh tab;TextFormField 顺序 = 时区 → 标题(zh tab))。
|
||||
final titleField = tester.widget<TextFormField>(
|
||||
find.byType(TextFormField).at(1),
|
||||
);
|
||||
expect(titleField.controller!.text, '开幕晚宴');
|
||||
// 时间回显按相册时区。
|
||||
expect(find.text('2026/9/18 14:30'), findsOneWidget);
|
||||
expect(find.text('2026/9/19 18:00'), findsOneWidget);
|
||||
// 无任何变更 → 保存禁用(PATCH minProperties 1)。
|
||||
expect(
|
||||
tester.widget<FilledButton>(find.byType(FilledButton)).onPressed,
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('页面 10 编辑:切页签共享草稿(不丢输入、不丢开关)', (tester) async {
|
||||
final adapter = FakeAdapter(
|
||||
(o) => throw StateError('不应发起请求:${o.path}'),
|
||||
);
|
||||
await pumpForm(
|
||||
tester,
|
||||
clientOf(adapter),
|
||||
existing: Album.fromJson(albumJson()),
|
||||
);
|
||||
|
||||
// Details 页签输入标题。
|
||||
await tester.enterText(find.byType(TextFormField).at(1), '闭幕演出');
|
||||
await tester.pump();
|
||||
// 切 Access → 切回 Details:输入保留(共享草稿)。
|
||||
await openAccessTab(tester);
|
||||
expect(find.byType(SwitchListTile), findsNWidgets(4));
|
||||
await tester.tap(find.text('Details'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
tester.widget<TextFormField>(find.byType(TextFormField).at(1)).controller!
|
||||
.text,
|
||||
'闭幕演出',
|
||||
);
|
||||
// 未保存 → 不发请求。
|
||||
expect(adapter.requests, isEmpty);
|
||||
});
|
||||
|
||||
testWidgets('页面 10 编辑:Access 四开关各配说明', (tester) async {
|
||||
final adapter = FakeAdapter(
|
||||
(o) => throw StateError('不应发起请求:${o.path}'),
|
||||
);
|
||||
await pumpForm(
|
||||
tester,
|
||||
clientOf(adapter),
|
||||
existing: Album.fromJson(albumJson()),
|
||||
);
|
||||
|
||||
await openAccessTab(tester);
|
||||
|
||||
expect(
|
||||
find.widgetWithText(SwitchListTile, 'Allow uploads'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.text('Allow staff to upload photos to this album'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.widgetWithText(SwitchListTile, 'Public access'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.text('Anyone with the link can view photos'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.widgetWithText(SwitchListTile, 'Allow downloads'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.text('Viewers can download browse-quality photos'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.widgetWithText(SwitchListTile, 'Original downloads'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.text('Viewers can download original photos'),
|
||||
findsOneWidget,
|
||||
);
|
||||
// 开关初值与原相册一致。
|
||||
expect(
|
||||
tester
|
||||
@@ -239,30 +349,47 @@ void main() {
|
||||
expect(
|
||||
tester
|
||||
.widget<SwitchListTile>(
|
||||
find.widgetWithText(SwitchListTile, 'Publicly visible'),
|
||||
)
|
||||
.value,
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
tester
|
||||
.widget<SwitchListTile>(
|
||||
find.widgetWithText(SwitchListTile, 'Allow original downloads'),
|
||||
find.widgetWithText(SwitchListTile, 'Public access'),
|
||||
)
|
||||
.value,
|
||||
true,
|
||||
);
|
||||
// 时间回显按相册时区。
|
||||
expect(find.text('2026/9/18 14:30'), findsOneWidget);
|
||||
expect(find.text('2026/9/19 18:00'), findsOneWidget);
|
||||
// 无任何变更 → 保存禁用(PATCH minProperties 1)。
|
||||
expect(
|
||||
tester.widget<FilledButton>(find.byType(FilledButton)).onPressed,
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('编辑:仅拨动 originalDownloadEnabled → PATCH 单键载荷',
|
||||
testWidgets('页面 10 编辑:原图下载受总下载约束(禁用 + PATCH 联动关断)',
|
||||
(tester) async {
|
||||
final adapter = FakeAdapter(
|
||||
(o) => o.path == '/v1/albums/alb-1'
|
||||
? (200, albumJson(downloadEnabled: false))
|
||||
: throw StateError('unexpected ${o.path}'),
|
||||
);
|
||||
// 原相册:下载开、原图开。
|
||||
final existing =
|
||||
Album.fromJson(albumJson(originalDownloadEnabled: true));
|
||||
await pumpForm(tester, clientOf(adapter), existing: existing);
|
||||
|
||||
await openAccessTab(tester);
|
||||
|
||||
// 关闭总下载 → 原图开关禁用且显示为关。
|
||||
await tester.tap(find.widgetWithText(SwitchListTile, 'Allow downloads'));
|
||||
await tester.pump();
|
||||
final original = find.widgetWithText(SwitchListTile, 'Original downloads');
|
||||
expect(tester.widget<SwitchListTile>(original).onChanged, isNull,
|
||||
reason: '总下载关闭 → 原图开关禁用');
|
||||
expect(tester.widget<SwitchListTile>(original).value, isFalse,
|
||||
reason: '生效值恒为 false');
|
||||
|
||||
// 保存 → PATCH 联动关断原图(不允许「原图开而下载关」的矛盾态)。
|
||||
await tester.tap(find.text('Save changes'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(adapter.requests.single.body, <String, dynamic>{
|
||||
'downloadEnabled': false,
|
||||
'originalDownloadEnabled': false,
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('页面 10 编辑:仅拨动 originalDownloadEnabled → PATCH 单键载荷',
|
||||
(tester) async {
|
||||
final adapter = FakeAdapter(
|
||||
(o) => o.path == '/v1/albums/alb-1'
|
||||
@@ -272,13 +399,14 @@ void main() {
|
||||
final existing = Album.fromJson(albumJson());
|
||||
await pumpForm(tester, clientOf(adapter), existing: existing);
|
||||
|
||||
await tapVisible(
|
||||
tester,
|
||||
find.widgetWithText(SwitchListTile, 'Allow original downloads'),
|
||||
);
|
||||
await openAccessTab(tester);
|
||||
await tester
|
||||
.tap(find.widgetWithText(SwitchListTile, 'Original downloads'));
|
||||
await tester.pump();
|
||||
|
||||
await tapVisible(tester, find.text('Save'));
|
||||
await tester.ensureVisible(find.text('Save changes'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Save changes'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final request = adapter.requests.single;
|
||||
@@ -289,7 +417,7 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('编辑:清除 endAt → PATCH 序列化 "endAt": null', (tester) async {
|
||||
testWidgets('页面 10 编辑:清除 endAt → PATCH 序列化 "endAt": null', (tester) async {
|
||||
final adapter = FakeAdapter(
|
||||
(o) => o.path == '/v1/albums/alb-1'
|
||||
? (200, albumJson(endAt: null))
|
||||
@@ -303,13 +431,13 @@ void main() {
|
||||
await tapVisible(tester, find.byTooltip('Clear end time'));
|
||||
await tester.pump();
|
||||
|
||||
await tapVisible(tester, find.text('Save'));
|
||||
await tapVisible(tester, find.text('Save changes'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(adapter.requests.single.body, <String, dynamic>{'endAt': null});
|
||||
});
|
||||
|
||||
testWidgets('编辑:修改 zh 标题 → 仅发全量 translations(不含开关/时间)',
|
||||
testWidgets('页面 10 编辑:修改 zh 标题 → 仅发全量 translations(不含开关/时间)',
|
||||
(tester) async {
|
||||
final adapter = FakeAdapter(
|
||||
(o) => o.path == '/v1/albums/alb-1'
|
||||
@@ -325,7 +453,7 @@ void main() {
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
await tapVisible(tester, find.text('Save'));
|
||||
await tapVisible(tester, find.text('Save changes'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final body = adapter.requests.single.body as Map<String, dynamic>;
|
||||
@@ -347,4 +475,81 @@ void main() {
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
testWidgets('页面 10 编辑:未保存变更退出 → 放弃确认(继续编辑/放弃两路)',
|
||||
(tester) async {
|
||||
final adapter = FakeAdapter(
|
||||
(o) => throw StateError('不应发起请求:${o.path}'),
|
||||
);
|
||||
final client = clientOf(adapter);
|
||||
|
||||
/// 以父路由承载(表单作为可弹出路由,AppBar 才有返回键)。
|
||||
Future<void> pumpPushedForm() async {
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [apiClientProvider.overrideWith((ref) => client)],
|
||||
child: MaterialApp(
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: Scaffold(
|
||||
body: Builder(
|
||||
builder: (context) => Center(
|
||||
child: TextButton(
|
||||
onPressed: () => Navigator.of(context).push(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (_) => AlbumFormPage(
|
||||
existing: Album.fromJson(albumJson()),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: const Text('open form'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('open form'));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
await pumpPushedForm();
|
||||
|
||||
// 无变更:系统返回直接弹出(无确认框)。
|
||||
await tester.pageBack();
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(AlbumFormPage), findsNothing);
|
||||
|
||||
// 重新进入并制造变更。
|
||||
await pumpPushedForm();
|
||||
await tester.enterText(find.byType(TextFormField).at(1), '闭幕演出');
|
||||
await tester.pump();
|
||||
|
||||
await tester.pageBack();
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Discard changes?'), findsOneWidget);
|
||||
expect(
|
||||
find.text('Your edits are not saved yet. Leaving now will discard them.'),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
// 继续编辑 → 留在页面,输入保留。
|
||||
await tester.tap(find.text('Keep editing'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(AlbumFormPage), findsOneWidget);
|
||||
expect(
|
||||
tester.widget<TextFormField>(find.byType(TextFormField).at(1)).controller!
|
||||
.text,
|
||||
'闭幕演出',
|
||||
);
|
||||
|
||||
// 放弃 → 弹出页面。
|
||||
await tester.pageBack();
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Discard'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(AlbumFormPage), findsNothing);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user