muzzle-velocity/test/note_list_dpad_test.dart
2026-10-13 12:10:38 +03:00

128 lines
4.5 KiB
Dart

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:muzzle_velocity/note_editor.dart';
import 'package:muzzle_velocity/note_list.dart';
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'package:shared_preferences/shared_preferences.dart';
class _FakePathProvider extends PathProviderPlatform
with MockPlatformInterfaceMixin {
_FakePathProvider(this.path);
final String path;
@override
Future<String?> getApplicationDocumentsPath() async => path;
}
Future<void> _press(WidgetTester tester, LogicalKeyboardKey key) async {
await tester.sendKeyEvent(key);
await tester.pumpAndSettle();
}
void main() {
late Directory notes;
setUp(() {
notes = Directory.systemTemp.createTempSync('mv-list');
for (final name in ['alpha', 'beta', 'gamma']) {
File('${notes.path}/$name.md').writeAsStringSync('# $name\n');
}
PathProviderPlatform.instance = _FakePathProvider(notes.path);
SharedPreferences.setMockInitialValues({'initialized': true});
});
Future<void> pumpList(WidgetTester tester) async {
await tester.runAsync(() async {
await tester.pumpWidget(MaterialApp(
home: NoteListScreen(
toggleTheme: (_) {},
toggleTags: () {},
isDarkMode: false,
showTags: true,
),
));
await Future<void>.delayed(const Duration(milliseconds: 200));
});
await tester.pumpAndSettle();
}
bool searchHasFocus(WidgetTester tester) =>
tester.widget<TextField>(find.byType(TextField)).focusNode!.hasFocus;
List<String> highlighted(WidgetTester tester) => [
for (final tile in tester.widgetList<ListTile>(find.byType(ListTile)))
if (tile.selected) (tile.title as Text).data!,
];
testWidgets('Down enters the list, Up and Left return to the search field',
(tester) async {
await pumpList(tester);
expect(searchHasFocus(tester), isTrue);
await _press(tester, LogicalKeyboardKey.arrowDown);
expect(searchHasFocus(tester), isFalse);
expect(highlighted(tester), hasLength(1));
final first = highlighted(tester).single;
await _press(tester, LogicalKeyboardKey.arrowDown);
expect(highlighted(tester).single, isNot(first));
await _press(tester, LogicalKeyboardKey.arrowUp);
expect(highlighted(tester).single, first);
await _press(tester, LogicalKeyboardKey.arrowUp);
expect(searchHasFocus(tester), isTrue);
expect(highlighted(tester), isEmpty);
await _press(tester, LogicalKeyboardKey.arrowDown);
await _press(tester, LogicalKeyboardKey.arrowLeft);
expect(searchHasFocus(tester), isTrue);
expect(highlighted(tester), isEmpty);
});
testWidgets('Right clears the search text, then opens the menu',
(tester) async {
await pumpList(tester);
await tester.enterText(find.byType(TextField), 'alp');
await tester.pumpAndSettle();
await _press(tester, LogicalKeyboardKey.arrowRight);
expect(find.text('alp'), findsNothing);
await _press(tester, LogicalKeyboardKey.arrowRight);
expect(find.text('About'), findsOneWidget);
});
testWidgets('Right in the list opens the menu with D-pad navigation',
(tester) async {
await pumpList(tester);
await _press(tester, LogicalKeyboardKey.arrowDown);
await _press(tester, LogicalKeyboardKey.arrowRight);
expect(find.text('About'), findsOneWidget);
// The first item has focus, so moving down and selecting picks the second.
await _press(tester, LogicalKeyboardKey.arrowDown);
await _press(tester, LogicalKeyboardKey.select);
expect(find.text('About'), findsNothing);
});
testWidgets('center opens the highlighted note', (tester) async {
await pumpList(tester);
await _press(tester, LogicalKeyboardKey.arrowDown);
// The note reads its file with real I/O, so let it finish between frames.
await tester.runAsync(() async {
await tester.sendKeyEvent(LogicalKeyboardKey.select);
for (var i = 0; i < 10; i++) {
await tester.pump(const Duration(milliseconds: 50));
await Future<void>.delayed(const Duration(milliseconds: 20));
}
});
expect(find.byType(NoteEditorScreen), findsOneWidget);
final editor = tester.widget<TextField>(find.descendant(
of: find.byType(NoteEditorScreen), matching: find.byType(TextField)));
expect(editor.controller!.text, matches(r'^# (alpha|beta|gamma)\n$'));
});
}