From 7938cf7bfbf17b8c225dae5fee02725d135aed15 Mon Sep 17 00:00:00 2001 From: Eoic Date: Mon, 27 Jul 2026 17:13:21 +0300 Subject: [PATCH 1/3] chore: set Flutter version to 3.41.2 in CI pipeline scripts --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 22f8596..d937f3c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ concurrency: cancel-in-progress: true env: - FLUTTER_VERSION: '3.38.5' + FLUTTER_VERSION: '3.41.2' jobs: quality: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b81f4d6..953c340 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,7 @@ on: - 'v*' env: - FLUTTER_VERSION: '3.38.5' + FLUTTER_VERSION: '3.41.2' PAPYRUS_API_BASE_URL: ${{ vars.PAPYRUS_API_BASE_URL }} POWERSYNC_SERVICE_URL: ${{ vars.POWERSYNC_SERVICE_URL }} From 887af556e9c90872826137a9b4a301633c66ff4c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Jul 2026 14:20:34 +0000 Subject: [PATCH 2/3] fix: format online books header widget tests --- .../library/online_books_header_test.dart | 158 +++++++++++++----- 1 file changed, 120 insertions(+), 38 deletions(-) diff --git a/app/test/widgets/library/online_books_header_test.dart b/app/test/widgets/library/online_books_header_test.dart index e4169f1..e7184f9 100644 --- a/app/test/widgets/library/online_books_header_test.dart +++ b/app/test/widgets/library/online_books_header_test.dart @@ -33,7 +33,9 @@ void main() { } IconButton searchButton(WidgetTester tester) { - return tester.widget(find.widgetWithIcon(IconButton, Icons.search)); + return tester.widget( + find.widgetWithIcon(IconButton, Icons.search), + ); } testWidgets('renders back control, title, and search field', (tester) async { @@ -41,7 +43,13 @@ void main() { addTearDown(controller.dispose); var backCalls = 0; - await tester.pumpWidget(buildHeader(controller: controller, onBack: () => backCalls++, onSearch: (_) {})); + await tester.pumpWidget( + buildHeader( + controller: controller, + onBack: () => backCalls++, + onSearch: (_) {}, + ), + ); expect(find.byTooltip('Back'), findsOneWidget); expect(find.bySemanticsLabel('Back'), findsOneWidget); @@ -55,7 +63,9 @@ void main() { expect(backCalls, 1); }); - testWidgets('matches the library search field height on desktop', (tester) async { + testWidgets('matches the library search field height on desktop', ( + tester, + ) async { final controller = TextEditingController(); addTearDown(controller.dispose); @@ -82,10 +92,19 @@ void main() { ), ); - final onlineField = find.descendant(of: find.byType(OnlineBooksHeader), matching: find.byType(TextField)); - final libraryField = find.descendant(of: find.byType(LibrarySearchBar), matching: find.byType(TextField)); + final onlineField = find.descendant( + of: find.byType(OnlineBooksHeader), + matching: find.byType(TextField), + ); + final libraryField = find.descendant( + of: find.byType(LibrarySearchBar), + matching: find.byType(TextField), + ); - expect(tester.getSize(onlineField).height, tester.getSize(libraryField).height); + expect( + tester.getSize(onlineField).height, + tester.getSize(libraryField).height, + ); }); testWidgets('matches the selection header height on desktop', (tester) async { @@ -132,22 +151,42 @@ void main() { ); }); - testWidgets('honors autofocus for online search entered from add book', (tester) async { + testWidgets('honors autofocus for online search entered from add book', ( + tester, + ) async { final controller = TextEditingController(); addTearDown(controller.dispose); - await tester.pumpWidget(buildHeader(controller: controller, autofocus: true, onBack: () {}, onSearch: (_) {})); + await tester.pumpWidget( + buildHeader( + controller: controller, + autofocus: true, + onBack: () {}, + onSearch: (_) {}, + ), + ); await tester.pump(); - expect(tester.widget(find.byType(EditableText)).focusNode.hasFocus, isTrue); + expect( + tester.widget(find.byType(EditableText)).focusNode.hasFocus, + isTrue, + ); }); - testWidgets('typing does not search and enables search for non-empty input', (tester) async { + testWidgets('typing does not search and enables search for non-empty input', ( + tester, + ) async { final controller = TextEditingController(); addTearDown(controller.dispose); final submitted = []; - await tester.pumpWidget(buildHeader(controller: controller, onBack: () {}, onSearch: submitted.add)); + await tester.pumpWidget( + buildHeader( + controller: controller, + onBack: () {}, + onSearch: submitted.add, + ), + ); expect(searchButton(tester).onPressed, isNull); @@ -163,25 +202,44 @@ void main() { expect(searchButton(tester).onPressed, isNull); }); - testWidgets('keyboard submit searches the trimmed query exactly once', (tester) async { + testWidgets('keyboard submit searches the trimmed query exactly once', ( + tester, + ) async { final controller = TextEditingController(); addTearDown(controller.dispose); final submitted = []; - await tester.pumpWidget(buildHeader(controller: controller, onBack: () {}, onSearch: submitted.add)); + await tester.pumpWidget( + buildHeader( + controller: controller, + onBack: () {}, + onSearch: submitted.add, + ), + ); - await tester.enterText(find.byType(TextField), ' The Left Hand of Darkness '); + await tester.enterText( + find.byType(TextField), + ' The Left Hand of Darkness ', + ); await tester.testTextInput.receiveAction(TextInputAction.search); expect(submitted, ['The Left Hand of Darkness']); }); - testWidgets('search icon submits the trimmed query exactly once', (tester) async { + testWidgets('search icon submits the trimmed query exactly once', ( + tester, + ) async { final controller = TextEditingController(); addTearDown(controller.dispose); final submitted = []; - await tester.pumpWidget(buildHeader(controller: controller, onBack: () {}, onSearch: submitted.add)); + await tester.pumpWidget( + buildHeader( + controller: controller, + onBack: () {}, + onSearch: submitted.add, + ), + ); await tester.enterText(find.byType(TextField), ' A Wizard of Earthsea '); await tester.pump(); @@ -194,12 +252,21 @@ void main() { final controller = TextEditingController(text: 'Dune'); addTearDown(controller.dispose); - await tester.pumpWidget(buildHeader(controller: controller, isSearching: true, onBack: () {}, onSearch: (_) {})); + await tester.pumpWidget( + buildHeader( + controller: controller, + isSearching: true, + onBack: () {}, + onSearch: (_) {}, + ), + ); expect(searchButton(tester).onPressed, isNull); }); - testWidgets('fits compact mobile and desktop layouts with the dark theme', (tester) async { + testWidgets('fits compact mobile and desktop layouts with the dark theme', ( + tester, + ) async { final controller = TextEditingController(); addTearDown(controller.dispose); tester.view.devicePixelRatio = 1; @@ -208,32 +275,47 @@ void main() { for (final size in [const Size(320, 640), const Size(1280, 800)]) { tester.view.physicalSize = size; - await tester.pumpWidget(buildHeader(controller: controller, onBack: () {}, onSearch: (_) {})); + await tester.pumpWidget( + buildHeader(controller: controller, onBack: () {}, onSearch: (_) {}), + ); await tester.pump(); expect(tester.takeException(), isNull); } }); - testWidgets('keeps search visible near the desktop breakpoint with large text', (tester) async { - final controller = TextEditingController(); - addTearDown(controller.dispose); - tester.view.devicePixelRatio = 1; - tester.view.physicalSize = const Size(640, 640); - addTearDown(tester.view.reset); - - await tester.pumpWidget( - MediaQuery( - data: const MediaQueryData(textScaler: TextScaler.linear(2)), - child: buildHeader(controller: controller, onBack: () {}, onSearch: (_) {}), - ), - ); - await tester.pump(); + testWidgets( + 'keeps search visible near the desktop breakpoint with large text', + (tester) async { + final controller = TextEditingController(); + addTearDown(controller.dispose); + tester.view.devicePixelRatio = 1; + tester.view.physicalSize = const Size(640, 640); + addTearDown(tester.view.reset); + + await tester.pumpWidget( + MediaQuery( + data: const MediaQueryData(textScaler: TextScaler.linear(2)), + child: buildHeader( + controller: controller, + onBack: () {}, + onSearch: (_) {}, + ), + ), + ); + await tester.pump(); - final headerRow = find.ancestor(of: find.text('Online results'), matching: find.byType(Row)); + final headerRow = find.ancestor( + of: find.text('Online results'), + matching: find.byType(Row), + ); - expect(headerRow, findsOneWidget); - expect(find.descendant(of: headerRow, matching: find.byType(TextField)), findsOneWidget); - expect(tester.takeException(), isNull); - }); + expect(headerRow, findsOneWidget); + expect( + find.descendant(of: headerRow, matching: find.byType(TextField)), + findsOneWidget, + ); + expect(tester.takeException(), isNull); + }, + ); } From 041e7b96fe2a153c0df4f032e7a721be48a03b9b Mon Sep 17 00:00:00 2001 From: Eoic Date: Mon, 27 Jul 2026 17:37:20 +0300 Subject: [PATCH 3/3] chore: apply formatter --- .../library/online_books_header_test.dart | 163 +++++------------- 1 file changed, 39 insertions(+), 124 deletions(-) diff --git a/app/test/widgets/library/online_books_header_test.dart b/app/test/widgets/library/online_books_header_test.dart index e7184f9..e31a317 100644 --- a/app/test/widgets/library/online_books_header_test.dart +++ b/app/test/widgets/library/online_books_header_test.dart @@ -33,9 +33,7 @@ void main() { } IconButton searchButton(WidgetTester tester) { - return tester.widget( - find.widgetWithIcon(IconButton, Icons.search), - ); + return tester.widget(find.widgetWithIcon(IconButton, Icons.search)); } testWidgets('renders back control, title, and search field', (tester) async { @@ -43,13 +41,7 @@ void main() { addTearDown(controller.dispose); var backCalls = 0; - await tester.pumpWidget( - buildHeader( - controller: controller, - onBack: () => backCalls++, - onSearch: (_) {}, - ), - ); + await tester.pumpWidget(buildHeader(controller: controller, onBack: () => backCalls++, onSearch: (_) {})); expect(find.byTooltip('Back'), findsOneWidget); expect(find.bySemanticsLabel('Back'), findsOneWidget); @@ -63,9 +55,7 @@ void main() { expect(backCalls, 1); }); - testWidgets('matches the library search field height on desktop', ( - tester, - ) async { + testWidgets('matches the library search field height on desktop', (tester) async { final controller = TextEditingController(); addTearDown(controller.dispose); @@ -92,19 +82,10 @@ void main() { ), ); - final onlineField = find.descendant( - of: find.byType(OnlineBooksHeader), - matching: find.byType(TextField), - ); - final libraryField = find.descendant( - of: find.byType(LibrarySearchBar), - matching: find.byType(TextField), - ); + final onlineField = find.descendant(of: find.byType(OnlineBooksHeader), matching: find.byType(TextField)); + final libraryField = find.descendant(of: find.byType(LibrarySearchBar), matching: find.byType(TextField)); - expect( - tester.getSize(onlineField).height, - tester.getSize(libraryField).height, - ); + expect(tester.getSize(onlineField).height, tester.getSize(libraryField).height); }); testWidgets('matches the selection header height on desktop', (tester) async { @@ -145,48 +126,25 @@ void main() { ), ); - expect( - tester.getSize(find.byType(OnlineBooksHeader)).height, - tester.getSize(find.byType(SelectionHeader)).height, - ); + expect(tester.getSize(find.byType(OnlineBooksHeader)).height, tester.getSize(find.byType(SelectionHeader)).height); }); - testWidgets('honors autofocus for online search entered from add book', ( - tester, - ) async { + testWidgets('honors autofocus for online search entered from add book', (tester) async { final controller = TextEditingController(); addTearDown(controller.dispose); - await tester.pumpWidget( - buildHeader( - controller: controller, - autofocus: true, - onBack: () {}, - onSearch: (_) {}, - ), - ); + await tester.pumpWidget(buildHeader(controller: controller, autofocus: true, onBack: () {}, onSearch: (_) {})); await tester.pump(); - expect( - tester.widget(find.byType(EditableText)).focusNode.hasFocus, - isTrue, - ); + expect(tester.widget(find.byType(EditableText)).focusNode.hasFocus, isTrue); }); - testWidgets('typing does not search and enables search for non-empty input', ( - tester, - ) async { + testWidgets('typing does not search and enables search for non-empty input', (tester) async { final controller = TextEditingController(); addTearDown(controller.dispose); final submitted = []; - await tester.pumpWidget( - buildHeader( - controller: controller, - onBack: () {}, - onSearch: submitted.add, - ), - ); + await tester.pumpWidget(buildHeader(controller: controller, onBack: () {}, onSearch: submitted.add)); expect(searchButton(tester).onPressed, isNull); @@ -202,44 +160,25 @@ void main() { expect(searchButton(tester).onPressed, isNull); }); - testWidgets('keyboard submit searches the trimmed query exactly once', ( - tester, - ) async { + testWidgets('keyboard submit searches the trimmed query exactly once', (tester) async { final controller = TextEditingController(); addTearDown(controller.dispose); final submitted = []; - await tester.pumpWidget( - buildHeader( - controller: controller, - onBack: () {}, - onSearch: submitted.add, - ), - ); + await tester.pumpWidget(buildHeader(controller: controller, onBack: () {}, onSearch: submitted.add)); - await tester.enterText( - find.byType(TextField), - ' The Left Hand of Darkness ', - ); + await tester.enterText(find.byType(TextField), ' The Left Hand of Darkness '); await tester.testTextInput.receiveAction(TextInputAction.search); expect(submitted, ['The Left Hand of Darkness']); }); - testWidgets('search icon submits the trimmed query exactly once', ( - tester, - ) async { + testWidgets('search icon submits the trimmed query exactly once', (tester) async { final controller = TextEditingController(); addTearDown(controller.dispose); final submitted = []; - await tester.pumpWidget( - buildHeader( - controller: controller, - onBack: () {}, - onSearch: submitted.add, - ), - ); + await tester.pumpWidget(buildHeader(controller: controller, onBack: () {}, onSearch: submitted.add)); await tester.enterText(find.byType(TextField), ' A Wizard of Earthsea '); await tester.pump(); @@ -252,21 +191,12 @@ void main() { final controller = TextEditingController(text: 'Dune'); addTearDown(controller.dispose); - await tester.pumpWidget( - buildHeader( - controller: controller, - isSearching: true, - onBack: () {}, - onSearch: (_) {}, - ), - ); + await tester.pumpWidget(buildHeader(controller: controller, isSearching: true, onBack: () {}, onSearch: (_) {})); expect(searchButton(tester).onPressed, isNull); }); - testWidgets('fits compact mobile and desktop layouts with the dark theme', ( - tester, - ) async { + testWidgets('fits compact mobile and desktop layouts with the dark theme', (tester) async { final controller = TextEditingController(); addTearDown(controller.dispose); tester.view.devicePixelRatio = 1; @@ -275,47 +205,32 @@ void main() { for (final size in [const Size(320, 640), const Size(1280, 800)]) { tester.view.physicalSize = size; - await tester.pumpWidget( - buildHeader(controller: controller, onBack: () {}, onSearch: (_) {}), - ); + await tester.pumpWidget(buildHeader(controller: controller, onBack: () {}, onSearch: (_) {})); await tester.pump(); expect(tester.takeException(), isNull); } }); - testWidgets( - 'keeps search visible near the desktop breakpoint with large text', - (tester) async { - final controller = TextEditingController(); - addTearDown(controller.dispose); - tester.view.devicePixelRatio = 1; - tester.view.physicalSize = const Size(640, 640); - addTearDown(tester.view.reset); - - await tester.pumpWidget( - MediaQuery( - data: const MediaQueryData(textScaler: TextScaler.linear(2)), - child: buildHeader( - controller: controller, - onBack: () {}, - onSearch: (_) {}, - ), - ), - ); - await tester.pump(); + testWidgets('keeps search visible near the desktop breakpoint with large text', (tester) async { + final controller = TextEditingController(); + addTearDown(controller.dispose); + tester.view.devicePixelRatio = 1; + tester.view.physicalSize = const Size(640, 640); + addTearDown(tester.view.reset); - final headerRow = find.ancestor( - of: find.text('Online results'), - matching: find.byType(Row), - ); + await tester.pumpWidget( + MediaQuery( + data: const MediaQueryData(textScaler: TextScaler.linear(2)), + child: buildHeader(controller: controller, onBack: () {}, onSearch: (_) {}), + ), + ); + await tester.pump(); - expect(headerRow, findsOneWidget); - expect( - find.descendant(of: headerRow, matching: find.byType(TextField)), - findsOneWidget, - ); - expect(tester.takeException(), isNull); - }, - ); + final headerRow = find.ancestor(of: find.text('Online results'), matching: find.byType(Row)); + + expect(headerRow, findsOneWidget); + expect(find.descendant(of: headerRow, matching: find.byType(TextField)), findsOneWidget); + expect(tester.takeException(), isNull); + }); }