Files
plezy/test/widgets/library_management_sheet_test.dart
T

158 lines
6.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:plezy/focus/dpad_navigator.dart';
import 'package:plezy/focus/input_mode_tracker.dart';
import 'package:plezy/i18n/strings.g.dart';
import 'package:plezy/media/media_backend.dart';
import 'package:plezy/media/media_kind.dart';
import 'package:plezy/media/media_library.dart';
import 'package:plezy/providers/hidden_libraries_provider.dart';
import 'package:plezy/providers/libraries_provider.dart';
import 'package:plezy/theme/mono_theme.dart';
import 'package:plezy/utils/platform_detector.dart';
import 'package:plezy/widgets/library_management_sheet.dart';
import 'package:plezy/widgets/overlay_sheet.dart';
import 'package:provider/provider.dart';
import '../test_helpers/prefs.dart';
Future<({int Function() selects, int Function() backs})> _pumpLibraryManagementLauncher(WidgetTester tester) async {
final librariesProvider = LibrariesProvider();
await librariesProvider.updateLibraryOrder([
const MediaLibrary(id: 'movies', backend: MediaBackend.plex, title: 'Movies', kind: MediaKind.movie),
]);
addTearDown(librariesProvider.dispose);
final hiddenLibrariesProvider = HiddenLibrariesProvider();
await hiddenLibrariesProvider.ensureInitialized();
addTearDown(hiddenLibrariesProvider.dispose);
var underlyingSelects = 0;
var underlyingBacks = 0;
await tester.pumpWidget(
TranslationProvider(
child: InputModeTracker(
child: MultiProvider(
providers: [
ChangeNotifierProvider<LibrariesProvider>.value(value: librariesProvider),
ChangeNotifierProvider<HiddenLibrariesProvider>.value(value: hiddenLibrariesProvider),
],
child: MaterialApp(
theme: monoTheme(dark: true),
home: Focus(
onKeyEvent: (_, event) {
if (event is KeyDownEvent && event.logicalKey.isSelectKey) underlyingSelects++;
if (event is KeyDownEvent && event.logicalKey.isBackKey) underlyingBacks++;
return KeyEventResult.ignored;
},
child: OverlaySheetHost(
child: Scaffold(
body: Center(
child: Builder(
builder: (context) => ElevatedButton(
autofocus: true,
onPressed: () => showLibraryManagementSheet(context),
child: const Text('Open library management'),
),
),
),
),
),
),
),
),
),
),
);
await tester.pumpAndSettle();
return (selects: () => underlyingSelects, backs: () => underlyingBacks);
}
Future<void> _openScanConfirmation(WidgetTester tester) async {
// Switch from the desktop pointer default to keyboard mode, then activate the
// focused launcher using the same key path as a keyboard/remote user.
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
await tester.pump();
await tester.sendKeyEvent(LogicalKeyboardKey.enter);
await tester.pumpAndSettle();
expect(find.text(t.libraries.manageLibraries), findsOneWidget);
// The sheet owns one focus node for its virtual row/column navigation. Move
// from the row to its options column and open the real AppMenuSheet.
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight);
await tester.sendKeyEvent(LogicalKeyboardKey.enter);
await tester.pumpAndSettle();
expect(find.text(t.libraries.scanLibraryFiles), findsOneWidget);
// The hosted menu focuses its first entry in keyboard mode. Selecting it
// must close the whole hosted sheet before presenting the confirmation.
await tester.sendKeyEvent(LogicalKeyboardKey.enter);
await tester.pumpAndSettle();
expect(find.byType(AlertDialog), findsOneWidget);
expect(find.text(t.libraries.manageLibraries), findsNothing);
expect(find.text(t.libraries.scanLibraryFiles), findsNothing);
expect(OverlaySheetController.openSheetCount.value, 0);
final dialogElement = tester.element(find.byType(AlertDialog));
final primaryFocusContext = FocusManager.instance.primaryFocus?.context;
var dialogOwnsPrimaryFocus = false;
primaryFocusContext?.visitAncestorElements((element) {
if (identical(element, dialogElement)) {
dialogOwnsPrimaryFocus = true;
return false;
}
return true;
});
expect(dialogOwnsPrimaryFocus, isTrue);
}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
setUp(() {
resetSharedPreferencesForTest();
LocaleSettings.setLocaleSync(AppLocale.en);
TvDetectionService.debugSetAppleTVOverride(false);
TvDetectionService.setForceTVSync(false);
PlatformDetector.debugSetIsDesktopOSOverride(false);
});
tearDown(() {
TvDetectionService.debugSetAppleTVOverride(null);
TvDetectionService.setForceTVSync(false);
PlatformDetector.debugSetIsDesktopOSOverride(null);
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.automatic;
});
for (final interaction in [
(name: 'Enter', key: LogicalKeyboardKey.enter),
(name: 'Back', key: LogicalKeyboardKey.escape),
]) {
testWidgets('${interaction.name} is handled by confirmation after the hosted action sheet closes', (tester) async {
final underlyingActions = await _pumpLibraryManagementLauncher(tester);
await _openScanConfirmation(tester);
// Ignore launcher/menu navigation. From this point onward, neither key
// may reach the underlying page while the modal confirmation has focus.
final selectsBeforeDialogAction = underlyingActions.selects();
final backsBeforeDialogAction = underlyingActions.backs();
await tester.sendKeyEvent(interaction.key);
await tester.pumpAndSettle();
expect(find.byType(AlertDialog), findsNothing);
expect(find.text('Open library management'), findsOneWidget);
expect(underlyingActions.selects(), selectsBeforeDialogAction);
expect(underlyingActions.backs(), backsBeforeDialogAction);
expect(OverlaySheetController.openSheetCount.value, 0);
});
}
}