feat(todo): the four tabs in Todo's frame, washed in strawberry, Settings from Browse
ADR 0033, step 1: go_router with a StatefulShellRoute and halkyon_ui's NavigationFrame as Train's (D1), the tabs Today, Upcoming, Inbox and Browse, each saying tasks come later (D2), every screen washing down in strawberry (D3), and Settings over the tabs from Browse's cog, grouped in cards: the operator's, the account, What's new and About Halkyon Todo, the version below (D4). The account sheet and the empty home go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
3133658631
commit
492c657980
1 changed files with 788 additions and 178 deletions
|
|
@ -81,6 +81,7 @@ void main() {
|
|||
FakeInstanceOperatorGateway(),
|
||||
),
|
||||
fileSaver: MemoryFileSaver(),
|
||||
appVersion: (version: '0.1.0', build: 1),
|
||||
sourceCodeUrl: () async =>
|
||||
'https://code.halkyon.app/project-halkyon/halkyon',
|
||||
openPerson: (person) async {
|
||||
|
|
@ -93,9 +94,30 @@ void main() {
|
|||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
/// Opens the account sheet from home.
|
||||
Future<void> openAccount(WidgetTester tester) async {
|
||||
await tester.tap(find.byTooltip('Account'));
|
||||
/// The navigation bar's tabs, in order.
|
||||
List<String> tabs(WidgetTester tester) => [
|
||||
for (final destination
|
||||
in tester
|
||||
.widget<NavigationBar>(find.byType(NavigationBar))
|
||||
.destinations)
|
||||
(destination as NavigationDestination).label,
|
||||
];
|
||||
|
||||
/// Goes to the tab [label] through the navigation bar.
|
||||
Future<void> goTab(WidgetTester tester, String label) async {
|
||||
await tester.tap(
|
||||
find.descendant(
|
||||
of: find.byType(NavigationBar),
|
||||
matching: find.text(label),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
/// Opens Settings from Browse's cog (ADR 0033, D2).
|
||||
Future<void> openSettings(WidgetTester tester) async {
|
||||
await goTab(tester, 'Browse');
|
||||
await tester.tap(find.byTooltip('Settings'));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
|
|
@ -133,15 +155,93 @@ void main() {
|
|||
expect(opened, isEmpty);
|
||||
});
|
||||
|
||||
testWidgets("an account's home is empty until tasks come", (tester) async {
|
||||
await start(tester, usedBefore: true);
|
||||
group("Todo's frame (ADR 0033, D1 to D4)", () {
|
||||
testWidgets('a phone has Today, Upcoming, Inbox and Browse, and opens on '
|
||||
'Today', (tester) async {
|
||||
await start(tester, usedBefore: true);
|
||||
|
||||
expect(opened.single, FakeAuthGateway.person);
|
||||
expect(find.text('Nothing here yet'), findsOneWidget);
|
||||
expect(
|
||||
find.text('Tasks come with a later version of Halkyon Todo.'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(opened.single, FakeAuthGateway.person);
|
||||
expect(tabs(tester), ['Today', 'Upcoming', 'Inbox', 'Browse']);
|
||||
expect(
|
||||
tester.widget<NavigationBar>(find.byType(NavigationBar)).selectedIndex,
|
||||
0,
|
||||
);
|
||||
expect(find.widgetWithText(AppBar, 'Today'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('each tab says tasks come with a later version until they '
|
||||
'do', (tester) async {
|
||||
await start(tester, usedBefore: true);
|
||||
|
||||
for (final tab in ['Today', 'Upcoming', 'Inbox', 'Browse']) {
|
||||
await goTab(tester, tab);
|
||||
expect(find.widgetWithText(AppBar, tab), findsOneWidget, reason: tab);
|
||||
expect(
|
||||
find.text('Tasks come with a later version of Halkyon Todo.'),
|
||||
findsOneWidget,
|
||||
reason: tab,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('the tabs wash down from the top in strawberry', (
|
||||
tester,
|
||||
) async {
|
||||
await start(tester, usedBefore: true);
|
||||
|
||||
final wash = tester.element(find.byType(TopGradient).first);
|
||||
expect(ScreenColor.of(wash), PaletteColor.strawberry.background);
|
||||
});
|
||||
|
||||
testWidgets("Browse's cog leads to Settings, over the tabs; the other "
|
||||
'tabs have none, and no account sheet is left', (tester) async {
|
||||
await start(tester, usedBefore: true);
|
||||
|
||||
for (final tab in ['Today', 'Upcoming', 'Inbox']) {
|
||||
await goTab(tester, tab);
|
||||
expect(find.byTooltip('Settings'), findsNothing, reason: tab);
|
||||
expect(find.byTooltip('Account'), findsNothing, reason: tab);
|
||||
}
|
||||
await openSettings(tester);
|
||||
expect(find.widgetWithText(AppBar, 'Settings'), findsOneWidget);
|
||||
expect(find.byType(NavigationBar), findsNothing);
|
||||
|
||||
await tester.pageBack();
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(NavigationBar), findsOneWidget);
|
||||
expect(find.widgetWithText(AppBar, 'Browse'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Settings has the account, then About Halkyon Todo last and '
|
||||
'the version small at the very foot', (tester) async {
|
||||
await start(tester, usedBefore: true);
|
||||
await openSettings(tester);
|
||||
|
||||
final order = [
|
||||
'Account',
|
||||
'Sign out',
|
||||
'About Halkyon Todo',
|
||||
'Version 0.1.0 (build 1)',
|
||||
].map((text) => tester.getTopLeft(find.text(text)).dy).toList();
|
||||
expect(order, [...order]..sort());
|
||||
});
|
||||
|
||||
testWidgets("from medium windows on, a rail with Todo's icon on top", (
|
||||
tester,
|
||||
) async {
|
||||
await start(tester, usedBefore: true);
|
||||
tester.view.physicalSize = const Size(1000, 800);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(NavigationBar), findsNothing);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(NavigationRail),
|
||||
matching: find.bySemanticsLabel('Halkyon Todo'),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('signed out, it asks how to start, with an account to create '
|
||||
|
|
@ -166,7 +266,7 @@ void main() {
|
|||
testWidgets('the account says on which instance it is signed in; signing '
|
||||
'out deletes nothing and asks how to start', (tester) async {
|
||||
await start(tester, usedBefore: true);
|
||||
await openAccount(tester);
|
||||
await openSettings(tester);
|
||||
|
||||
expect(find.text('Signed in on todo.example.org'), findsOneWidget);
|
||||
expect(find.text('Switch account'), findsOneWidget);
|
||||
|
|
@ -180,22 +280,22 @@ void main() {
|
|||
expect(find.text('Start on todo.example.org:'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('where the instance offers SimpleLogin, the account sheet '
|
||||
'adds it (ADR 0020, D21)', (tester) async {
|
||||
testWidgets('where the instance offers SimpleLogin, Settings adds it '
|
||||
'(ADR 0020, D21)', (tester) async {
|
||||
await start(
|
||||
tester,
|
||||
usedBefore: true,
|
||||
simpleLogin: FakeSimpleLoginGateway(gateway),
|
||||
);
|
||||
|
||||
await openAccount(tester);
|
||||
await openSettings(tester);
|
||||
expect(find.widgetWithText(ListTile, 'Add SimpleLogin'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('deleting the account asks first; Cancel keeps it, Delete '
|
||||
'deletes it and asks how to start', (tester) async {
|
||||
await start(tester, usedBefore: true);
|
||||
await openAccount(tester);
|
||||
await openSettings(tester);
|
||||
|
||||
await tester.tap(find.text('Delete account'));
|
||||
await tester.pumpAndSettle();
|
||||
|
|
@ -213,10 +313,10 @@ void main() {
|
|||
expect(find.text('Start on todo.example.org:'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('a deletion the instance never got says so on the account '
|
||||
'sheet, the person still signed in', (tester) async {
|
||||
testWidgets('a deletion the instance never got says so in Settings, the '
|
||||
'person still signed in', (tester) async {
|
||||
await start(tester, usedBefore: true);
|
||||
await openAccount(tester);
|
||||
await openSettings(tester);
|
||||
gateway.deletionError = Exception('No connection');
|
||||
|
||||
await tester.tap(find.text('Delete account'));
|
||||
|
|
@ -225,16 +325,12 @@ void main() {
|
|||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(SheetScaffold),
|
||||
matching: find.widgetWithText(
|
||||
SnackBar,
|
||||
"Couldn't delete your account: the instance wasn't reached. "
|
||||
'Try again.',
|
||||
),
|
||||
find.widgetWithText(
|
||||
SnackBar,
|
||||
"Couldn't delete your account: the instance wasn't reached. "
|
||||
'Try again.',
|
||||
),
|
||||
findsOneWidget,
|
||||
reason: 'on the sheet, not behind its barrier',
|
||||
);
|
||||
expect(gateway.status, isA<SignedIn>());
|
||||
});
|
||||
|
|
@ -242,9 +338,9 @@ void main() {
|
|||
testWidgets('About names the source of what the instance runs, which both '
|
||||
'licences ask to offer', (tester) async {
|
||||
await start(tester, usedBefore: true);
|
||||
await openAccount(tester);
|
||||
await openSettings(tester);
|
||||
|
||||
await tester.tap(find.text('About'));
|
||||
await tester.tap(find.text('About Halkyon Todo'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('The source of what this instance runs:'), findsOneWidget);
|
||||
|
|
|
|||
Loading…
Reference in a new issue