chromium/ash/controls/contextual_tooltip_unittest.cc

// Copyright 2020 The Chromium Authors
// 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/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<bool> {
 public:
  ContextualTooltipTest() {
    if (GetParam()) {
      scoped_feature_list_.InitAndEnableFeature(
          features::kHideShelfControlsInTabletMode);

    } else {
      scoped_feature_list_.InitAndDisableFeature(
          features::kHideShelfControlsInTabletMode);
    }
  }
  ~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