perf: isolate focus and media rebuilds
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:plezy/focus/input_mode_tracker.dart';
|
||||
import 'package:plezy/services/gamepad_service.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
testWidgets('one-shot reads do not subscribe to input-mode changes', (tester) async {
|
||||
var listeningBuilds = 0;
|
||||
var oneShotBuilds = 0;
|
||||
InputMode? listeningMode;
|
||||
InputMode? oneShotMode;
|
||||
|
||||
await tester.pumpWidget(
|
||||
InputModeTracker(
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Row(
|
||||
children: [
|
||||
Builder(
|
||||
builder: (context) {
|
||||
listeningBuilds++;
|
||||
listeningMode = InputModeTracker.of(context);
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
oneShotBuilds++;
|
||||
oneShotMode = InputModeTracker.of(context, listen: false);
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(listeningMode, InputMode.pointer);
|
||||
expect(oneShotMode, InputMode.pointer);
|
||||
expect(listeningBuilds, 1);
|
||||
expect(oneShotBuilds, 1);
|
||||
|
||||
GamepadService.onGamepadInput!.call();
|
||||
await tester.pump();
|
||||
|
||||
expect(listeningMode, InputMode.keyboard);
|
||||
expect(listeningBuilds, 2);
|
||||
expect(oneShotMode, InputMode.pointer);
|
||||
expect(oneShotBuilds, 1);
|
||||
});
|
||||
}
|
||||
@@ -89,9 +89,13 @@ class _FakeConnectionRegistry extends ConnectionRegistry {
|
||||
_FakeConnectionRegistry(super.db, this.connections);
|
||||
|
||||
final List<Connection> connections;
|
||||
int watchCalls = 0;
|
||||
|
||||
@override
|
||||
Stream<List<Connection>> watchConnections() => Stream.value(connections);
|
||||
Stream<List<Connection>> watchConnections() {
|
||||
watchCalls++;
|
||||
return Stream.value(connections);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
@@ -102,7 +106,7 @@ void main() {
|
||||
late DownloadManagerService downloadManager;
|
||||
late MultiServerManager serverManager;
|
||||
MultiServerProvider? multiServerProvider;
|
||||
late ConnectionRegistry connectionRegistry;
|
||||
late _FakeConnectionRegistry connectionRegistry;
|
||||
late List<Connection> connections;
|
||||
|
||||
setUp(() async {
|
||||
@@ -251,6 +255,18 @@ void main() {
|
||||
expect(find.text('No sync rules'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('provider rebuilds reuse the connection stream subscription', (tester) async {
|
||||
multiServerProvider = MultiServerProvider(serverManager, DataAggregationService(serverManager));
|
||||
await insertRule(ServerId('orphan-srv'), '76672');
|
||||
await pumpScreen(tester);
|
||||
|
||||
expect(connectionRegistry.watchCalls, 1);
|
||||
await downloadProvider.updateSyncRuleCount(downloadProvider.syncRules.keys.single, 6);
|
||||
await tester.pump();
|
||||
|
||||
expect(connectionRegistry.watchCalls, 1);
|
||||
});
|
||||
|
||||
testWidgets('does not autofocus the first sync rule in pointer mode', (tester) async {
|
||||
multiServerProvider = MultiServerProvider(serverManager, DataAggregationService(serverManager));
|
||||
await insertRule(ServerId('orphan-srv'), '76672');
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:plezy/media/ids.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:plezy/i18n/strings.g.dart';
|
||||
import 'package:plezy/media/media_backend.dart';
|
||||
import 'package:plezy/media/media_kind.dart';
|
||||
@@ -516,4 +517,41 @@ void main() {
|
||||
|
||||
expect(selectedLibraryKey, hiddenServerALibrary.globalKey);
|
||||
});
|
||||
|
||||
testWidgets('rail item focus repaints locally without rebuilding its parent', (tester) async {
|
||||
final focusNode = FocusNode();
|
||||
addTearDown(focusNode.dispose);
|
||||
var parentBuilds = 0;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: ThemeData(extensions: const [_testTokens]),
|
||||
home: Scaffold(
|
||||
body: Builder(
|
||||
builder: (context) {
|
||||
parentBuilds++;
|
||||
return NavigationRailItem(
|
||||
icon: Symbols.home_rounded,
|
||||
label: const Text('Home'),
|
||||
isSelected: false,
|
||||
onTap: () {},
|
||||
focusNode: focusNode,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final item = find.byType(NavigationRailItem);
|
||||
expect(_railItemDecoration(tester, item)?.color, isNull);
|
||||
expect(parentBuilds, 1);
|
||||
|
||||
focusNode.requestFocus();
|
||||
await tester.pump();
|
||||
|
||||
expect(focusNode.hasFocus, isTrue);
|
||||
expect(_railItemDecoration(tester, item)?.color, isNotNull);
|
||||
expect(parentBuilds, 1);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user