|
14 | 14 | #include "base/test/scoped_feature_list.h" |
15 | 15 | #include "base/test/test_future.h" |
16 | 16 | #include "base/threading/thread_restrictions.h" |
| 17 | +#include "base/timer/timer.h" |
| 18 | +#include "content/public/browser/page_navigator.h" |
17 | 19 | #include "base/command_line.h" |
18 | 20 | #include "chrome/browser/prefs/session_startup_pref.h" |
19 | 21 | #include "chrome/browser/profiles/profile.h" |
|
62 | 64 | #include "dao/browser/ui/views/dao_control_center_button.h" |
63 | 65 | #include "dao/browser/ui/views/dao_control_center_popup.h" |
64 | 66 | #include "dao/browser/ui/views/dao_corner_overlay_view.h" |
| 67 | +#include "dao/browser/ui/views/dao_load_progress_view.h" |
65 | 68 | #include "dao/browser/ui/views/dao_tab_commands.h" |
66 | 69 | #include "dao/browser/ui/views/dao_tab_identity.h" |
67 | 70 | #include "dao/browser/ui/views/dao_toast_view.h" |
@@ -2557,5 +2560,175 @@ IN_PROC_BROWSER_TEST_F(DaoAgentWorkspaceBrowserTest, |
2557 | 2560 | EXPECT_TRUE(base::DirectoryExists(stage)); |
2558 | 2561 | } |
2559 | 2562 |
|
| 2563 | +// ============================================================================= |
| 2564 | +// DaoLoadProgressBrowserTest |
| 2565 | +// ============================================================================= |
| 2566 | + |
| 2567 | +class DaoLoadProgressBrowserTest : public InProcessBrowserTest { |
| 2568 | + public: |
| 2569 | + void SetUpOnMainThread() override { |
| 2570 | + InProcessBrowserTest::SetUpOnMainThread(); |
| 2571 | + ASSERT_TRUE(embedded_test_server()->Start()); |
| 2572 | + } |
| 2573 | +}; |
| 2574 | + |
| 2575 | +IN_PROC_BROWSER_TEST_F(DaoLoadProgressBrowserTest, ViewExists) { |
| 2576 | + auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser()); |
| 2577 | + ASSERT_TRUE(browser_view); |
| 2578 | + auto* progress = browser_view->dao_load_progress(); |
| 2579 | + ASSERT_TRUE(progress); |
| 2580 | + // The view exists; its visible state depends on whether the active tab is |
| 2581 | + // currently loading at this moment (NTP can still be loading at test start), |
| 2582 | + // so we don't assert on opacity here. End-to-end load behavior is covered |
| 2583 | + // by tests added in Task 7. |
| 2584 | + ASSERT_TRUE(progress->layer()); |
| 2585 | +} |
| 2586 | + |
| 2587 | +IN_PROC_BROWSER_TEST_F(DaoLoadProgressBrowserTest, |
| 2588 | + StartLoadingMakesBarVisible) { |
| 2589 | + auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser()); |
| 2590 | + auto* progress = browser_view->dao_load_progress(); |
| 2591 | + ASSERT_TRUE(progress); |
| 2592 | + |
| 2593 | + progress->StartLoading(); |
| 2594 | + EXPECT_TRUE(progress->is_loading_for_testing()); |
| 2595 | + EXPECT_GT(progress->layer()->opacity(), 0.0f); |
| 2596 | + EXPECT_EQ(progress->displayed_progress_for_testing(), 0.0); |
| 2597 | +} |
| 2598 | + |
| 2599 | +IN_PROC_BROWSER_TEST_F(DaoLoadProgressBrowserTest, RealLoadShowsThenHides) { |
| 2600 | + auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser()); |
| 2601 | + auto* progress = browser_view->dao_load_progress(); |
| 2602 | + ASSERT_TRUE(progress); |
| 2603 | + |
| 2604 | + const GURL url = embedded_test_server()->GetURL("/title1.html"); |
| 2605 | + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| 2606 | + |
| 2607 | + // After NavigateToURL returns, DidStopLoading has fired and the controller |
| 2608 | + // begins the Completing/FadingOut sequence. Pump the message loop until the |
| 2609 | + // layer opacity falls back to ~0 (or 1s elapses, in which case the EXPECT |
| 2610 | + // below catches the failure). |
| 2611 | + base::RunLoop loop; |
| 2612 | + base::OneShotTimer timeout; |
| 2613 | + base::RepeatingTimer poller; |
| 2614 | + timeout.Start(FROM_HERE, base::Seconds(2), |
| 2615 | + base::BindLambdaForTesting([&]() { loop.Quit(); })); |
| 2616 | + poller.Start(FROM_HERE, base::Milliseconds(50), |
| 2617 | + base::BindLambdaForTesting([&]() { |
| 2618 | + if (progress->layer()->opacity() <= 0.01f) { |
| 2619 | + loop.Quit(); |
| 2620 | + } |
| 2621 | + })); |
| 2622 | + loop.Run(); |
| 2623 | + poller.Stop(); |
| 2624 | + timeout.Stop(); |
| 2625 | + |
| 2626 | + EXPECT_LE(progress->layer()->opacity(), 0.01f); |
| 2627 | + EXPECT_FALSE(progress->is_loading_for_testing()); |
| 2628 | +} |
| 2629 | + |
| 2630 | +IN_PROC_BROWSER_TEST_F(DaoLoadProgressBrowserTest, |
| 2631 | + SwitchingToFinishedTabHidesBar) { |
| 2632 | + auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser()); |
| 2633 | + auto* progress = browser_view->dao_load_progress(); |
| 2634 | + ASSERT_TRUE(progress); |
| 2635 | + |
| 2636 | + // Tab 0: load and finish. |
| 2637 | + const GURL url_a = embedded_test_server()->GetURL("/title1.html"); |
| 2638 | + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_a)); |
| 2639 | + |
| 2640 | + // Tab 1: open and load. AddTabAtIndex waits for the navigation to complete |
| 2641 | + // via an internal NavigationObserver, so on return the controller will have |
| 2642 | + // received FinishLoading() and the bar is either still fading out or already |
| 2643 | + // hidden. |
| 2644 | + const GURL url_b = embedded_test_server()->GetURL("/title2.html"); |
| 2645 | + ASSERT_TRUE(AddTabAtIndex(1, url_b, ui::PAGE_TRANSITION_TYPED)); |
| 2646 | + |
| 2647 | + // Poll until the in-flight fade-out animation has settled (opacity <= 0.01). |
| 2648 | + // This avoids relying on a fixed wall-clock delay. |
| 2649 | + { |
| 2650 | + base::RunLoop loop; |
| 2651 | + base::RepeatingTimer poller; |
| 2652 | + base::OneShotTimer timeout; |
| 2653 | + timeout.Start(FROM_HERE, base::Seconds(2), |
| 2654 | + base::BindLambdaForTesting([&]() { loop.Quit(); })); |
| 2655 | + poller.Start(FROM_HERE, base::Milliseconds(50), |
| 2656 | + base::BindLambdaForTesting([&]() { |
| 2657 | + if (progress->layer()->opacity() <= 0.01f) { |
| 2658 | + loop.Quit(); |
| 2659 | + } |
| 2660 | + })); |
| 2661 | + loop.Run(); |
| 2662 | + poller.Stop(); |
| 2663 | + timeout.Stop(); |
| 2664 | + } |
| 2665 | + |
| 2666 | + browser()->tab_strip_model()->ActivateTabAt(0); |
| 2667 | + // Allow the controller to react to the tab switch. |
| 2668 | + base::RunLoop().RunUntilIdle(); |
| 2669 | + EXPECT_LE(progress->layer()->opacity(), 0.01f); |
| 2670 | +} |
| 2671 | + |
| 2672 | +IN_PROC_BROWSER_TEST_F(DaoLoadProgressBrowserTest, StopCommandHidesBar) { |
| 2673 | + auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser()); |
| 2674 | + auto* progress = browser_view->dao_load_progress(); |
| 2675 | + ASSERT_TRUE(progress); |
| 2676 | + |
| 2677 | + // Start a navigation, then immediately stop. We don't await NavigateToURL |
| 2678 | + // — instead we kick off a slow navigation and call Stop before it |
| 2679 | + // completes. |
| 2680 | + const GURL url = embedded_test_server()->GetURL("/slow?2"); |
| 2681 | + auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| 2682 | + content::TestNavigationManager nav_manager(web_contents, url); |
| 2683 | + content::OpenURLParams open_params( |
| 2684 | + url, content::Referrer(), WindowOpenDisposition::CURRENT_TAB, |
| 2685 | + ui::PAGE_TRANSITION_TYPED, /*is_renderer_initiated=*/false); |
| 2686 | + browser()->OpenURL(open_params, /*navigation_handle_callback=*/{}); |
| 2687 | + |
| 2688 | + // Wait until the navigation actually starts on the network side — this is |
| 2689 | + // a reliable signal that DidStartLoading has been dispatched to observers. |
| 2690 | + ASSERT_TRUE(nav_manager.WaitForRequestStart()); |
| 2691 | + EXPECT_TRUE(progress->is_loading_for_testing()); |
| 2692 | + |
| 2693 | + chrome::Stop(browser()); |
| 2694 | + // Pump until DidStopLoading + fade-out completes. |
| 2695 | + base::RunLoop run_loop; |
| 2696 | + base::OneShotTimer timer; |
| 2697 | + timer.Start(FROM_HERE, base::Milliseconds(800), run_loop.QuitClosure()); |
| 2698 | + run_loop.Run(); |
| 2699 | + |
| 2700 | + EXPECT_LE(progress->layer()->opacity(), 0.01f); |
| 2701 | + EXPECT_FALSE(progress->is_loading_for_testing()); |
| 2702 | +} |
| 2703 | + |
| 2704 | +IN_PROC_BROWSER_TEST_F(DaoLoadProgressBrowserTest, |
| 2705 | + LayoutFollowsSidebarCollapse) { |
| 2706 | + auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser()); |
| 2707 | + auto* progress = browser_view->dao_load_progress(); |
| 2708 | + auto* sidebar = browser_view->dao_sidebar(); |
| 2709 | + ASSERT_TRUE(progress); |
| 2710 | + ASSERT_TRUE(sidebar); |
| 2711 | + |
| 2712 | + // Force a layout pass with expanded sidebar. |
| 2713 | + ASSERT_FALSE(sidebar->collapsed()); |
| 2714 | + browser_view->DeprecatedLayoutImmediately(); |
| 2715 | + const int expanded_x = progress->bounds().x(); |
| 2716 | + const int expanded_w = progress->bounds().width(); |
| 2717 | + |
| 2718 | + // Collapse the sidebar and re-layout. |
| 2719 | + sidebar->ToggleCollapsed(); |
| 2720 | + // The sidebar collapse animation runs ~250ms. Pump and wait. |
| 2721 | + { |
| 2722 | + base::RunLoop run_loop; |
| 2723 | + base::OneShotTimer timer; |
| 2724 | + timer.Start(FROM_HERE, base::Milliseconds(500), run_loop.QuitClosure()); |
| 2725 | + run_loop.Run(); |
| 2726 | + } |
| 2727 | + browser_view->DeprecatedLayoutImmediately(); |
| 2728 | + |
| 2729 | + EXPECT_NE(progress->bounds().x(), expanded_x); |
| 2730 | + EXPECT_NE(progress->bounds().width(), expanded_w); |
| 2731 | +} |
| 2732 | + |
2560 | 2733 | } // namespace |
2561 | 2734 | } // namespace dao |
0 commit comments