From e1d67ebeb0e81db65d6d43dddcdef5e57c40a84c Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Sat, 10 Oct 2026 20:19:54 +0300 Subject: [PATCH 1/3] refactor(app): remove non-idempotent side effects and async loads from QueryaApp.build() Closes #1345 --- lib/app/app.dart | 8 -------- lib/main.dart | 4 ++++ test/app/querya_app_test.dart | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 test/app/querya_app_test.dart diff --git a/lib/app/app.dart b/lib/app/app.dart index 8a0e82ae..2e6b16c2 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -1,7 +1,3 @@ -import 'dart:async'; - -import 'package:querya_desktop/core/actions/querya_command_registry.dart'; -import 'package:querya_desktop/core/extensions/local_extension_registry.dart'; import 'package:querya_desktop/core/layout/ui_scale.dart'; import 'package:querya_desktop/core/layout/ui_scale_controller.dart'; import 'package:querya_desktop/core/motion/querya_motion_controller.dart'; @@ -12,7 +8,6 @@ import 'package:querya_desktop/core/theme/theme_controller.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'app_lifecycle_cleanup.dart'; -import 'app_wiring.dart'; import '../features/main_screen/main_screen.dart'; class QueryaApp extends StatelessWidget { @@ -20,9 +15,6 @@ class QueryaApp extends StatelessWidget { @override Widget build(BuildContext context) { - installAppWiring(); - QueryaCommandRegistry.instance.ensureCoreDefaults(); - unawaited(LocalExtensionRegistry.instance.load()); final themeController = ThemeController.instance; final uiScaleController = UiScaleController.instance; final motionController = QueryaMotionController.instance; diff --git a/lib/main.dart b/lib/main.dart index 748a4212..c7f06b6e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,7 +6,9 @@ import 'package:flutter/material.dart'; import 'app/app.dart'; import 'app/app_wiring.dart'; +import 'core/actions/querya_command_registry.dart'; import 'core/editor/syntax_highlight_service.dart'; +import 'core/extensions/local_extension_registry.dart'; import 'core/layout/ui_scale_controller.dart'; import 'core/mcp/mcp_server_controller.dart'; import 'core/motion/display_refresh_service.dart'; @@ -38,6 +40,8 @@ void main([List args = const []]) async { await ThemeController.instance.load(); await UiScaleController.instance.load(); await QueryaMotionController.instance.load(); + QueryaCommandRegistry.instance.ensureCoreDefaults(); + unawaited(LocalExtensionRegistry.instance.load()); unawaited(UpdateController.instance.initialize()); unawaited(cleanupStaleMongoTlsTempFiles()); unawaited(McpServerController.instance.startIfEnabled()); diff --git a/test/app/querya_app_test.dart b/test/app/querya_app_test.dart new file mode 100644 index 00000000..e3d59606 --- /dev/null +++ b/test/app/querya_app_test.dart @@ -0,0 +1,16 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:querya_desktop/app/app.dart'; + +void main() { + testWidgets('QueryaApp build is pure and does not crash or throw', (tester) async { + await tester.pumpWidget( + const QueryaApp(), + ); + // Rebuilding the root widget is idempotent and doesn't trigger side effects + await tester.pumpWidget( + const QueryaApp(), + ); + expect(find.byType(QueryaApp), findsOneWidget); + }); +} From e238c00f946e0e5494a9246ab4c6e3824029bdf1 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Sat, 10 Oct 2026 20:22:26 +0300 Subject: [PATCH 2/3] test(app): remove unused widgets import --- test/app/querya_app_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/test/app/querya_app_test.dart b/test/app/querya_app_test.dart index e3d59606..5f8ad0e3 100644 --- a/test/app/querya_app_test.dart +++ b/test/app/querya_app_test.dart @@ -1,4 +1,3 @@ -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:querya_desktop/app/app.dart'; From 12aba62e1c51c53e7dc90ff817ce0a1c5cee623a Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Sat, 10 Oct 2026 20:53:10 +0300 Subject: [PATCH 3/3] test(app): disable native window chrome in QueryaApp build test WindowTitleBarBox needs the bitsdojo native library, which flutter_tester does not have; use the same useNativeWindowChrome switch other widget tests use. --- test/app/querya_app_test.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/app/querya_app_test.dart b/test/app/querya_app_test.dart index 5f8ad0e3..94088e9a 100644 --- a/test/app/querya_app_test.dart +++ b/test/app/querya_app_test.dart @@ -1,7 +1,12 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:querya_desktop/app/app.dart'; +import 'package:querya_desktop/features/main_screen/querya_window_title_bar.dart'; void main() { + // Headless tests have no native bitsdojo symbols. + setUp(() => QueryaWindowTitleBar.useNativeWindowChrome = false); + tearDown(() => QueryaWindowTitleBar.useNativeWindowChrome = true); + testWidgets('QueryaApp build is pure and does not crash or throw', (tester) async { await tester.pumpWidget( const QueryaApp(),