// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/controls/contextual_tooltip.h" #include "ash/constants/ash_features.h" #include "ash/controls/contextual_tooltip.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "ash/test/ash_test_base.h" #include "base/json/values_util.h" #include "base/strings/string_util.h" #include "base/test/scoped_feature_list.h" #include "base/test/simple_test_clock.h" #include "base/time/time.h" #include "base/values.h" #include "components/prefs/scoped_user_pref_update.h" #include "ui/aura/window.h" #include "ui/wm/core/window_util.h" namespace ash { namespace contextual_tooltip { class ContextualTooltipTest : public AshTestBase, public testing::WithParamInterface { public: ContextualTooltipTest() { if (GetParam()) { scoped_feature_list_.InitWithFeatures( {ash::features::kContextualNudges, ash::features::kHideShelfControlsInTabletMode}, {}); } else { scoped_feature_list_.InitAndDisableFeature( ash::features::kContextualNudges); } } ~ContextualTooltipTest() override = default; base::SimpleTestClock* clock() { return &test_clock_; } // AshTestBase: void SetUp() override { AshTestBase::SetUp(); contextual_tooltip::OverrideClockForTesting(&test_clock_); test_clock_.Advance(base::Seconds(360)); } void TearDown() override { contextual_tooltip::ClearClockOverrideForTesting(); AshTestBase::TearDown(); } PrefService* GetPrefService() { return Shell::Get()->session_controller()->GetLastActiveUserPrefService(); } private: base::test::ScopedFeatureList scoped_feature_list_; base::SimpleTestClock test_clock_; }; using ContextualTooltipDisabledTest = ContextualTooltipTest; INSTANTIATE_TEST_SUITE_P(All, ContextualTooltipDisabledTest, testing::Values(false)); INSTANTIATE_TEST_SUITE_P(All, ContextualTooltipTest, testing::Values(true)); // Checks that nudges are not shown when the feature flag is disabled. TEST_P(ContextualTooltipDisabledTest, FeatureFlagDisabled) { EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, nullptr)); } TEST_P(ContextualTooltipTest, ShouldShowPersistentDragHandleNudge) { base::TimeDelta recheck_delay; EXPECT_TRUE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, &recheck_delay)); EXPECT_TRUE(recheck_delay.is_zero()); EXPECT_TRUE(contextual_tooltip::GetNudgeTimeout(GetPrefService(), TooltipType::kInAppToHome) .is_zero()); } // Checks that drag handle nudge has a timeout if it is not the first time it is // being shown. TEST_P(ContextualTooltipTest, NonPersistentDragHandleNudgeTimeout) { for (int shown_count = 1; shown_count < contextual_tooltip::kNotificationLimit; shown_count++) { contextual_tooltip::HandleNudgeShown(GetPrefService(), TooltipType::kInAppToHome); clock()->Advance(contextual_tooltip::kMinInterval); EXPECT_TRUE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, nullptr)); EXPECT_EQ(contextual_tooltip::GetNudgeTimeout(GetPrefService(), TooltipType::kInAppToHome), contextual_tooltip::kNudgeShowDuration); } } // Checks that drag handle nudge should be shown after kMinInterval has passed // since the last time it was shown but not before the time interval has passed. TEST_P(ContextualTooltipTest, ShouldShowTimedDragHandleNudge) { contextual_tooltip::HandleNudgeShown(GetPrefService(), TooltipType::kInAppToHome); base::TimeDelta recheck_delay; for (int shown_count = 1; shown_count < contextual_tooltip::kNotificationLimit; shown_count++) { EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, &recheck_delay)); EXPECT_EQ(contextual_tooltip::kMinInterval, recheck_delay); clock()->Advance(contextual_tooltip::kMinInterval / 2); EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, &recheck_delay)); EXPECT_EQ( contextual_tooltip::kMinInterval - contextual_tooltip::kMinInterval / 2, recheck_delay); clock()->Advance(contextual_tooltip::kMinInterval / 2); EXPECT_TRUE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, nullptr)); contextual_tooltip::HandleNudgeShown(GetPrefService(), TooltipType::kInAppToHome); } clock()->Advance(contextual_tooltip::kMinInterval); EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, &recheck_delay)); EXPECT_TRUE(recheck_delay.is_zero()); EXPECT_EQ(contextual_tooltip::GetNudgeTimeout(GetPrefService(), TooltipType::kInAppToHome), contextual_tooltip::kNudgeShowDuration); } // Tests that if the user has successfully performed the gesture for at least // |kSuccessLimit|, the corresponding nudge should not be shown. TEST_P(ContextualTooltipTest, ShouldNotShowNudgeAfterSuccessLimit) { EXPECT_TRUE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, nullptr)); for (int success_count = 0; success_count < contextual_tooltip::kSuccessLimitInAppToHome; success_count++) { contextual_tooltip::HandleGesturePerformed(GetPrefService(), TooltipType::kInAppToHome); } base::TimeDelta recheck_delay; EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, &recheck_delay)); EXPECT_TRUE(recheck_delay.is_zero()); } // Should not show back gesture nudge if drag handle nudge is expected to be // shown. TEST_P(ContextualTooltipTest, DoNotShowBackGestureNudgeIfDragHandleNudgeIsExpected) { EXPECT_TRUE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, nullptr)); // The drag handle nudge is expected to show, so back gesture nudge should not // be shown at the same time. base::TimeDelta recheck_delay; EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kBackGesture, &recheck_delay)); EXPECT_EQ(contextual_tooltip::kMinIntervalBetweenBackAndDragHandleNudge, recheck_delay); // After the nudge is shown, back gesture should remain hidden until // sufficient amount of time passes. contextual_tooltip::HandleNudgeShown(GetPrefService(), TooltipType::kInAppToHome); EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kBackGesture, &recheck_delay)); EXPECT_EQ(contextual_tooltip::kMinIntervalBetweenBackAndDragHandleNudge, recheck_delay); clock()->Advance( contextual_tooltip::kMinIntervalBetweenBackAndDragHandleNudge / 2); EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kBackGesture, &recheck_delay)); EXPECT_EQ( contextual_tooltip::kMinIntervalBetweenBackAndDragHandleNudge - contextual_tooltip::kMinIntervalBetweenBackAndDragHandleNudge / 2, recheck_delay); clock()->Advance(recheck_delay); EXPECT_TRUE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kBackGesture, nullptr)); // After the drag handle becomes eligible to show again, the back gesture // should be disabled. clock()->Advance(contextual_tooltip::kMinInterval); EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kBackGesture, &recheck_delay)); EXPECT_EQ(contextual_tooltip::kMinIntervalBetweenBackAndDragHandleNudge, recheck_delay); } // Tests that back gesture is allowed if the shelf is hidden, even if drag // handle would normally be available. TEST_P(ContextualTooltipTest, AllowBackGestureForHiddenShelf) { EXPECT_TRUE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, nullptr)); // The drag handle nudge is expected to show, so back gesture nudge should not // be shown at the same time. EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kBackGesture, nullptr)); // If drag handle nudge is disabled because the shelf is hidden, the back // gesture nudge should be allowed. contextual_tooltip::SetDragHandleNudgeDisabledForHiddenShelf(true); EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, nullptr)); EXPECT_TRUE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kBackGesture, nullptr)); // Disallow back gesture nudge if the shelf becomes visible. contextual_tooltip::SetDragHandleNudgeDisabledForHiddenShelf(false); EXPECT_TRUE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, nullptr)); EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kBackGesture, nullptr)); } // Tests that the drag handle nudge should not be shown while back gesture is // showing, or soon after it's been shown. TEST_P(ContextualTooltipTest, DoNotShowDragHandleNudgeIfBackGestureNudgeIsShown) { EXPECT_TRUE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, nullptr)); // Drag handle nudge not allowed if back gesture is showing. contextual_tooltip::SetDragHandleNudgeDisabledForHiddenShelf(true); contextual_tooltip::SetBackGestureNudgeShowing(true); contextual_tooltip::SetDragHandleNudgeDisabledForHiddenShelf(false); base::TimeDelta recheck_delay; EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, &recheck_delay)); EXPECT_EQ(contextual_tooltip::kMinIntervalBetweenBackAndDragHandleNudge, recheck_delay); // Allow drag handle only if sufficient amount of time passes since showing // the back gesture nudge. contextual_tooltip::SetBackGestureNudgeShowing(false); contextual_tooltip::HandleNudgeShown(GetPrefService(), TooltipType::kBackGesture); recheck_delay = base::TimeDelta(); EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, &recheck_delay)); EXPECT_EQ(contextual_tooltip::kMinIntervalBetweenBackAndDragHandleNudge, recheck_delay); clock()->Advance( contextual_tooltip::kMinIntervalBetweenBackAndDragHandleNudge / 2); EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, &recheck_delay)); clock()->Advance(recheck_delay); EXPECT_TRUE(contextual_tooltip::ShouldShowNudge( GetPrefService(), TooltipType::kInAppToHome, nullptr)); } } // namespace contextual_tooltip } // namespace ash