@@ -2453,4 +2453,145 @@ void main() {
24532453 ..status.equals (AnimationStatus .dismissed);
24542454 });
24552455 });
2456+
2457+ group ('app bar search button' , () {
2458+ // Tests for the search icon button that should appear in the app bar
2459+ // on the combined feed screen but not on other message list screens.
2460+ // We check the app bar's actions list directly to avoid confusion with
2461+ // the search icon that appears in the search input field.
2462+
2463+ testWidgets ('search icon button appears on CombinedFeedNarrow' , (tester) async {
2464+ // Set up the combined feed (all messages) view
2465+ await setupMessageListPage (tester, narrow: const CombinedFeedNarrow (), messages: []);
2466+
2467+ // Verify that the search button is present in the app bar actions
2468+ final appBar = tester.widget <ZulipAppBar >(find.byType (ZulipAppBar ));
2469+ final actions = appBar.actions ?? [];
2470+ final hasSearchButton = actions.any ((widget) =>
2471+ widget is IconButton &&
2472+ widget.icon is Icon &&
2473+ (widget.icon as Icon ).icon == ZulipIcons .search);
2474+ check (hasSearchButton).isTrue ();
2475+ });
2476+
2477+ testWidgets ('search icon button does not appear on ChannelNarrow' , (tester) async {
2478+ // Set up a single channel view
2479+ final stream = eg.stream ();
2480+ await setupMessageListPage (tester, narrow: ChannelNarrow (stream.streamId), messages: []);
2481+
2482+ // Verify search button is not present in channel views
2483+ final appBar = tester.widget <ZulipAppBar >(find.byType (ZulipAppBar ));
2484+ final actions = appBar.actions ?? [];
2485+ final hasSearchButton = actions.any ((widget) =>
2486+ widget is IconButton &&
2487+ widget.icon is Icon &&
2488+ (widget.icon as Icon ).icon == ZulipIcons .search);
2489+ check (hasSearchButton).isFalse ();
2490+ });
2491+
2492+ testWidgets ('search icon button does not appear on TopicNarrow' , (tester) async {
2493+ // Set up a single topic view within a channel
2494+ final stream = eg.stream ();
2495+ await setupMessageListPage (tester, narrow: eg.topicNarrow (stream.streamId, 'topic' ), messages: []);
2496+
2497+ // Verify search button is not present in topic views
2498+ final appBar = tester.widget <ZulipAppBar >(find.byType (ZulipAppBar ));
2499+ final actions = appBar.actions ?? [];
2500+ final hasSearchButton = actions.any ((widget) =>
2501+ widget is IconButton &&
2502+ widget.icon is Icon &&
2503+ (widget.icon as Icon ).icon == ZulipIcons .search);
2504+ check (hasSearchButton).isFalse ();
2505+ });
2506+
2507+ testWidgets ('search icon button does not appear on DmNarrow' , (tester) async {
2508+ // Set up a direct message conversation view
2509+ final narrow = DmNarrow .withUser (eg.otherUser.userId, selfUserId: eg.selfUser.userId);
2510+ await setupMessageListPage (tester, narrow: narrow, messages: []);
2511+
2512+ // Verify search button is not present in DM views
2513+ final appBar = tester.widget <ZulipAppBar >(find.byType (ZulipAppBar ));
2514+ final actions = appBar.actions ?? [];
2515+ final hasSearchButton = actions.any ((widget) =>
2516+ widget is IconButton &&
2517+ widget.icon is Icon &&
2518+ (widget.icon as Icon ).icon == ZulipIcons .search);
2519+ check (hasSearchButton).isFalse ();
2520+ });
2521+
2522+ testWidgets ('search icon button does not appear on MentionsNarrow' , (tester) async {
2523+ // Set up the mentions view (messages that mention the user)
2524+ await setupMessageListPage (tester, narrow: const MentionsNarrow (), messages: []);
2525+
2526+ // Verify search button is not present in mentions view
2527+ final appBar = tester.widget <ZulipAppBar >(find.byType (ZulipAppBar ));
2528+ final actions = appBar.actions ?? [];
2529+ final hasSearchButton = actions.any ((widget) =>
2530+ widget is IconButton &&
2531+ widget.icon is Icon &&
2532+ (widget.icon as Icon ).icon == ZulipIcons .search);
2533+ check (hasSearchButton).isFalse ();
2534+ });
2535+
2536+ testWidgets ('search icon button does not appear on StarredMessagesNarrow' , (tester) async {
2537+ // Set up the starred messages view
2538+ await setupMessageListPage (tester, narrow: const StarredMessagesNarrow (), messages: []);
2539+
2540+ // Verify search button is not present in starred messages view
2541+ final appBar = tester.widget <ZulipAppBar >(find.byType (ZulipAppBar ));
2542+ final actions = appBar.actions ?? [];
2543+ final hasSearchButton = actions.any ((widget) =>
2544+ widget is IconButton &&
2545+ widget.icon is Icon &&
2546+ (widget.icon as Icon ).icon == ZulipIcons .search);
2547+ check (hasSearchButton).isFalse ();
2548+ });
2549+
2550+ testWidgets ('search icon button does not appear on KeywordSearchNarrow' , (tester) async {
2551+ // Set up the search results view (this is the search page itself)
2552+ await setupMessageListPage (tester, narrow: KeywordSearchNarrow ('test' ), messages: []);
2553+
2554+ // Verify search button is not present on the search page itself
2555+ // (since the search input field is already present)
2556+ final appBar = tester.widget <ZulipAppBar >(find.byType (ZulipAppBar ));
2557+ final actions = appBar.actions ?? [];
2558+ final hasSearchButton = actions.any ((widget) =>
2559+ widget is IconButton &&
2560+ widget.icon is Icon &&
2561+ (widget.icon as Icon ).icon == ZulipIcons .search);
2562+ check (hasSearchButton).isFalse ();
2563+ });
2564+
2565+ testWidgets ('search icon button tapping search button on CombinedFeedNarrow navigates to search page' , (tester) async {
2566+ // Set up navigation tracking
2567+ final pushedRoutes = < Route <dynamic >> [];
2568+ final testNavObserver = TestNavigatorObserver ()
2569+ ..onPushed = (route, prevRoute) => pushedRoutes.add (route);
2570+
2571+ // Set up the combined feed view with navigation observer
2572+ await setupMessageListPage (tester,
2573+ narrow: const CombinedFeedNarrow (),
2574+ messages: [],
2575+ navObservers: [testNavObserver]);
2576+
2577+ // Clear any initial navigation that happened during setup
2578+ pushedRoutes.clear ();
2579+
2580+ // Mock the API call for the search page
2581+ connection.prepare (json: eg.newestGetMessagesResult (
2582+ foundOldest: true , messages: []).toJson ());
2583+
2584+ // Tap the search button in the app bar
2585+ await tester.tap (find.descendant (
2586+ of: find.byType (ZulipAppBar ),
2587+ matching: find.byIcon (ZulipIcons .search)));
2588+ await tester.pump ();
2589+
2590+ // Verify that we navigated to the search page with an empty search query
2591+ check (pushedRoutes).single.isA <WidgetRoute >().page
2592+ .isA <MessageListPage >()
2593+ .initNarrow.equals (KeywordSearchNarrow ('' ));
2594+ await tester.pump (Duration .zero); // Allow message list fetch to complete
2595+ });
2596+ });
24562597}
0 commit comments