A pub workspace of the Dart packages Halkyon's apps share: halkyon_ui, Halkyon's look (the theme, fonts, layout and shared widgets, with texts in English and German), halkyon_core's low-level primitives and halkyon_interop's cross-app contracts. Dart 3.13 with LeanCode's lints, configured once at the root; apps take a package by git, pinned to a signed commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
216 lines
6.9 KiB
Dart
216 lines
6.9 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:halkyon_ui/halkyon_ui.dart';
|
|
import 'package:material_ui/material_ui.dart';
|
|
|
|
void main() {
|
|
/// Opens a sheet titled "New studyset" with [body] and a Create button, in
|
|
/// a window [width] wide, with a status bar [statusBar] high on top.
|
|
Future<void> open(
|
|
WidgetTester tester, {
|
|
required double width,
|
|
Widget body = const Text('Body'),
|
|
double statusBar = 0,
|
|
bool busy = false,
|
|
}) async {
|
|
tester.view.physicalSize = Size(width, 900);
|
|
tester.view.devicePixelRatio = 1;
|
|
tester.view.padding = FakeViewPadding(top: statusBar);
|
|
tester.view.viewPadding = FakeViewPadding(top: statusBar);
|
|
addTearDown(tester.view.reset);
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
localizationsDelegates: HalkyonUiLocalizations.localizationsDelegates,
|
|
supportedLocales: HalkyonUiLocalizations.supportedLocales,
|
|
home: Scaffold(
|
|
body: Builder(
|
|
builder: (context) => TextButton(
|
|
onPressed: () => showAdaptiveSheet<void>(
|
|
context,
|
|
swipeable: !busy,
|
|
builder: (context) => SheetScaffold(
|
|
title: 'New studyset',
|
|
busy: busy,
|
|
body: body,
|
|
footer: FilledButton(
|
|
onPressed: () {},
|
|
child: const Text('Create'),
|
|
),
|
|
),
|
|
),
|
|
child: const Text('Open'),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await tester.tap(find.text('Open'));
|
|
await tester.pumpAndSettle();
|
|
}
|
|
|
|
testWidgets('is a bottom sheet below expanded windows', (tester) async {
|
|
await open(tester, width: 839);
|
|
|
|
expect(find.byType(BottomSheet), findsOneWidget);
|
|
expect(find.byType(Dialog), findsNothing);
|
|
});
|
|
|
|
testWidgets('is a dialog from expanded windows on', (tester) async {
|
|
await open(tester, width: 840);
|
|
|
|
expect(find.byType(Dialog), findsOneWidget);
|
|
expect(find.byType(BottomSheet), findsNothing);
|
|
});
|
|
|
|
for (final width in [400.0, 1200.0]) {
|
|
testWidgets('keeps its close button and footer in view above a long body '
|
|
'(${width.round()} wide)', (tester) async {
|
|
await open(
|
|
tester,
|
|
width: width,
|
|
body: Column(
|
|
children: [for (var i = 0; i < 80; i++) Text('Line $i')],
|
|
),
|
|
);
|
|
|
|
expect(find.byTooltip('Close').hitTestable(), findsOneWidget);
|
|
expect(find.text('Create').hitTestable(), findsOneWidget);
|
|
});
|
|
}
|
|
|
|
testWidgets('stays below the status bar with the keyboard open', (
|
|
tester,
|
|
) async {
|
|
await open(
|
|
tester,
|
|
width: 400,
|
|
statusBar: 48,
|
|
body: Column(
|
|
children: [for (var i = 0; i < 80; i++) Text('Line $i')],
|
|
),
|
|
);
|
|
|
|
tester.view.viewInsets = const FakeViewPadding(bottom: 400);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(tester.getTopLeft(find.byType(BottomSheet)).dy, 48);
|
|
expect(find.byTooltip('Close').hitTestable(), findsOneWidget);
|
|
expect(find.text('Create').hitTestable(), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('closes with its close button', (tester) async {
|
|
await open(tester, width: 400);
|
|
|
|
await tester.tap(find.byTooltip('Close'));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('New studyset'), findsNothing);
|
|
});
|
|
|
|
testWidgets('while busy, its close button is off and back keeps it open', (
|
|
tester,
|
|
) async {
|
|
await open(tester, width: 400, busy: true);
|
|
|
|
expect(
|
|
tester
|
|
.widget<IconButton>(
|
|
find.ancestor(
|
|
of: find.byTooltip('Close'),
|
|
matching: find.byType(IconButton),
|
|
),
|
|
)
|
|
.onPressed,
|
|
isNull,
|
|
);
|
|
await tester.binding.handlePopRoute();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('New studyset'), findsOneWidget);
|
|
});
|
|
|
|
group('a body longer than the sheet', () {
|
|
final tall = Column(
|
|
children: [for (var i = 0; i < 40; i++) const Text('Line')],
|
|
);
|
|
Finder below() => find.byKey(SheetScaffold.moreBelowKey);
|
|
Finder above() => find.byKey(SheetScaffold.moreAboveKey);
|
|
|
|
testWidgets('shows a line above the buttons while more lies below, and '
|
|
'one under the title while more lies above', (tester) async {
|
|
// As a dialog: a modal bottom sheet takes a test's drag itself.
|
|
await open(tester, width: 1000, body: tall);
|
|
|
|
expect(below(), findsOneWidget);
|
|
expect(above(), findsNothing);
|
|
|
|
await tester.drag(
|
|
find.byType(SingleChildScrollView),
|
|
const Offset(0, -300),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
expect(below(), findsOneWidget);
|
|
expect(above(), findsOneWidget);
|
|
expect(
|
|
tester
|
|
.state<ScrollableState>(
|
|
find.descendant(
|
|
of: find.byType(SingleChildScrollView),
|
|
matching: find.byType(Scrollable),
|
|
),
|
|
)
|
|
.position
|
|
.pixels,
|
|
greaterThan(0),
|
|
reason: 'the line coming keeps where the body was scrolled to',
|
|
);
|
|
|
|
await tester.drag(
|
|
find.byType(SingleChildScrollView),
|
|
const Offset(0, -5000),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
expect(below(), findsNothing);
|
|
expect(above(), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('has a scrollbar, always shown on the desktop', (tester) async {
|
|
debugDefaultTargetPlatformOverride = TargetPlatform.linux;
|
|
addTearDown(() => debugDefaultTargetPlatformOverride = null);
|
|
await open(tester, width: 1000, body: tall);
|
|
|
|
final scrollbar = tester.widget<Scrollbar>(find.byType(Scrollbar));
|
|
expect(scrollbar.thumbVisibility, isTrue);
|
|
debugDefaultTargetPlatformOverride = null;
|
|
});
|
|
});
|
|
|
|
testWidgets('a body that fits shows no lines', (tester) async {
|
|
await open(tester, width: 400);
|
|
|
|
expect(find.byKey(SheetScaffold.moreBelowKey), findsNothing);
|
|
expect(find.byKey(SheetScaffold.moreAboveKey), findsNothing);
|
|
});
|
|
|
|
testWidgets("the body keeps clear of the scroll view's edges, where it is "
|
|
'cut and the scrollbar runs', (tester) async {
|
|
for (final width in [400.0, 1000.0]) {
|
|
await open(
|
|
tester,
|
|
width: width,
|
|
body: const TextField(
|
|
decoration: InputDecoration(border: OutlineInputBorder()),
|
|
),
|
|
);
|
|
|
|
final scrolling = tester.getRect(find.byType(SingleChildScrollView));
|
|
final field = tester.getRect(find.byType(TextField));
|
|
expect(field.left - scrolling.left, greaterThanOrEqualTo(16));
|
|
expect(scrolling.right - field.right, greaterThanOrEqualTo(16));
|
|
// The title and the button stay in line with the body.
|
|
expect(tester.getTopLeft(find.text('New studyset')).dx, field.left);
|
|
expect(tester.getTopRight(find.byType(FilledButton)).dx, field.right);
|
|
await tester.tap(find.byTooltip('Close'));
|
|
await tester.pumpAndSettle();
|
|
}
|
|
});
|
|
}
|