chromium/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/RefreshIphScrollListenerTest.java

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.feed;

import static org.mockito.Mockito.when;

import android.view.View;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import org.chromium.base.test.params.BlockJUnit4RunnerDelegate;
import org.chromium.base.test.params.ParameterAnnotations;
import org.chromium.base.test.params.ParameterProvider;
import org.chromium.base.test.params.ParameterSet;
import org.chromium.base.test.params.ParameterizedRunner;
import org.chromium.base.test.util.Feature;
import org.chromium.components.feature_engagement.FeatureConstants;
import org.chromium.components.feature_engagement.Tracker;
import org.chromium.components.feature_engagement.TriggerState;

import java.util.ArrayList;
import java.util.List;

/** Unit test for {@link RefreshIphScrollListener}. */
@RunWith(ParameterizedRunner.class)
@ParameterAnnotations.UseRunnerDelegate(BlockJUnit4RunnerDelegate.class)
public final class RefreshIphScrollListenerTest {
    /** Parameter provider for testing the trigger of the IPH. */
    public static class TestParams implements ParameterProvider {
        @Override
        public Iterable<ParameterSet> getParameters() {
            List<ParameterSet> parameters = new ArrayList<>();
            // Trigger the IPH when the user is signed in.
            parameters.add(
                    new ParameterSet()
                            .value(
                                    true,
                                    10,
                                    TriggerState.HAS_NOT_BEEN_DISPLAYED,
                                    true,
                                    true,
                                    100,
                                    100 + RefreshIphScrollListener.FETCH_TIME_AGE_THREASHOLD_MS,
                                    false));
            // Trigger the IPH when the user is not signed in.
            parameters.add(
                    new ParameterSet()
                            .value(
                                    true,
                                    10,
                                    TriggerState.HAS_NOT_BEEN_DISPLAYED,
                                    true,
                                    false,
                                    100,
                                    100 + RefreshIphScrollListener.FETCH_TIME_AGE_THREASHOLD_MS,
                                    false));
            // Don't trigger the IPH because the state is not set to has been displayed.
            parameters.add(
                    new ParameterSet()
                            .value(
                                    false,
                                    10,
                                    TriggerState.HAS_BEEN_DISPLAYED,
                                    true,
                                    true,
                                    100,
                                    100 + RefreshIphScrollListener.FETCH_TIME_AGE_THREASHOLD_MS,
                                    false));
            // Don't trigger the IPH because the feed is not expanded.
            parameters.add(
                    new ParameterSet()
                            .value(
                                    false,
                                    10,
                                    TriggerState.HAS_NOT_BEEN_DISPLAYED,
                                    false,
                                    true,
                                    100,
                                    100 + RefreshIphScrollListener.FETCH_TIME_AGE_THREASHOLD_MS,
                                    false));
            // Don't trigger the IPH because the scrollY is 0.
            parameters.add(
                    new ParameterSet()
                            .value(
                                    false,
                                    0,
                                    TriggerState.HAS_NOT_BEEN_DISPLAYED,
                                    true,
                                    true,
                                    100,
                                    100 + RefreshIphScrollListener.FETCH_TIME_AGE_THREASHOLD_MS,
                                    false));
            // Don't trigger the IPH because the last fetch time is not available.
            parameters.add(
                    new ParameterSet()
                            .value(
                                    false,
                                    10,
                                    TriggerState.HAS_NOT_BEEN_DISPLAYED,
                                    true,
                                    true,
                                    0,
                                    100 + RefreshIphScrollListener.FETCH_TIME_AGE_THREASHOLD_MS + 1,
                                    false));
            // Don't trigger the IPH because the last fetch time is still within the threshold.
            parameters.add(
                    new ParameterSet()
                            .value(
                                    false,
                                    10,
                                    TriggerState.HAS_NOT_BEEN_DISPLAYED,
                                    true,
                                    true,
                                    100,
                                    99 + RefreshIphScrollListener.FETCH_TIME_AGE_THREASHOLD_MS,
                                    false));
            // Don't trigger the IPH because the page can still scroll up.
            parameters.add(
                    new ParameterSet()
                            .value(
                                    false,
                                    0,
                                    TriggerState.HAS_NOT_BEEN_DISPLAYED,
                                    true,
                                    true,
                                    100,
                                    100 + RefreshIphScrollListener.FETCH_TIME_AGE_THREASHOLD_MS,
                                    true));
            return parameters;
        }
    }

    @Mock private Tracker mTracker;

    private boolean mHasShownIPH;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    @Feature({"Feed"})
    @ParameterAnnotations.UseMethodParameter(TestParams.class)
    public void triggerIph(
            boolean expectEnabled,
            int scrollY,
            int triggerState,
            boolean isFeedExpanded,
            boolean isSignedIn,
            long lastFetchTimeMs,
            long currentTimeMs,
            boolean canScrollUp) {
        // Set Tracker mock.
        when(mTracker.getTriggerState(FeatureConstants.FEED_SWIPE_REFRESH_FEATURE))
                .thenReturn(triggerState);

        FeedBubbleDelegate delegate =
                new FeedBubbleDelegate() {
                    @Override
                    public Tracker getFeatureEngagementTracker() {
                        return mTracker;
                    }

                    @Override
                    public boolean isFeedExpanded() {
                        return isFeedExpanded;
                    }

                    @Override
                    public boolean isSignedIn() {
                        return isSignedIn;
                    }

                    @Override
                    public boolean isFeedHeaderPositionInContainerSuitableForIPH(
                            float headerMaxPosFraction) {
                        return false;
                    }

                    @Override
                    public long getCurrentTimeMs() {
                        return currentTimeMs;
                    }

                    @Override
                    public long getLastFetchTimeMs() {
                        return lastFetchTimeMs;
                    }

                    @Override
                    public boolean canScrollUp() {
                        return canScrollUp;
                    }
                };

        ScrollableContainerDelegate scrollableContainerDelegate =
                new ScrollableContainerDelegate() {
                    @Override
                    public void addScrollListener(ScrollListener listener) {}

                    @Override
                    public void removeScrollListener(ScrollListener listener) {}

                    @Override
                    public int getVerticalScrollOffset() {
                        return 10;
                    }

                    @Override
                    public int getRootViewHeight() {
                        return 100;
                    }

                    @Override
                    public int getTopPositionRelativeToContainerView(View childView) {
                        return 0;
                    }
                };

        // Trigger IPH through the scroll listener.
        RefreshIphScrollListener listener =
                new RefreshIphScrollListener(
                        delegate,
                        scrollableContainerDelegate,
                        () -> {
                            mHasShownIPH = true;
                        });
        listener.onScrolled(0, scrollY);

        if (expectEnabled) {
            Assert.assertTrue(mHasShownIPH);
        } else {
            Assert.assertFalse(mHasShownIPH);
        }
    }
}