chromium/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/PriceMessageServiceUnitTest.java

// 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.

package org.chromium.chrome.browser.tasks.tab_management;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;

import org.chromium.base.FeatureList;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.price_tracking.PriceDropNotificationManager;
import org.chromium.chrome.browser.price_tracking.PriceTrackingFeatures;
import org.chromium.chrome.browser.price_tracking.PriceTrackingUtilities;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.subscriptions.CommerceSubscriptionsServiceConfig;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData;
import org.chromium.chrome.browser.tasks.tab_management.MessageService.MessageType;
import org.chromium.chrome.browser.tasks.tab_management.PriceMessageService.PriceMessageType;
import org.chromium.chrome.browser.tasks.tab_management.PriceMessageService.PriceTabData;

/** Unit tests for {@link PriceMessageService}. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class PriceMessageServiceUnitTest {

    private static final int BINDING_TAB_ID = 456;
    private static final int INITIAL_SHOW_COUNT = 0;
    private static final int MAX_SHOW_COUNT = 20;

    private static final String PRICE = "$300";
    private static final String PREVIOUS_PRICE = "$400";

    @Mock PriceMessageService.PriceWelcomeMessageProvider mMessageProvider;
    @Mock PriceMessageService.PriceWelcomeMessageReviewActionProvider mReviewActionProvider;
    @Mock MessageService.MessageObserver mMessageObserver;
    @Mock PriceDropNotificationManager mNotificationManager;
    @Mock Profile mProfile;

    private PriceMessageService mMessageService;
    private PriceTabData mPriceTabData;

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

        mPriceTabData =
                new PriceTabData(
                        BINDING_TAB_ID,
                        new ShoppingPersistedTabData.PriceDrop(PRICE, PREVIOUS_PRICE));

        doNothing().when(mMessageObserver).messageReady(anyInt(), any());
        doNothing().when(mMessageObserver).messageInvalidate(anyInt());

        FeatureList.TestValues testValues = new FeatureList.TestValues();
        testValues.addFeatureFlagOverride(ChromeFeatureList.COMMERCE_PRICE_TRACKING, true);
        testValues.addFieldTrialParamOverride(
                ChromeFeatureList.COMMERCE_PRICE_TRACKING,
                CommerceSubscriptionsServiceConfig.IMPLICIT_SUBSCRIPTIONS_ENABLED_PARAM,
                "true");
        FeatureList.setTestValues(testValues);

        PriceTrackingFeatures.setPriceTrackingEnabledForTesting(true);
        PriceTrackingFeatures.setIsSignedInAndSyncEnabledForTesting(true);
        PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeBoolean(
                PriceTrackingUtilities.PRICE_WELCOME_MESSAGE_CARD, true);
        PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeInt(
                PriceTrackingUtilities.PRICE_WELCOME_MESSAGE_CARD_SHOW_COUNT, INITIAL_SHOW_COUNT);
        assertTrue(PriceTrackingUtilities.isPriceWelcomeMessageCardEnabled(mProfile));
        PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeBoolean(
                PriceTrackingUtilities.PRICE_ALERTS_MESSAGE_CARD, true);
        PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeInt(
                PriceTrackingUtilities.PRICE_ALERTS_MESSAGE_CARD_SHOW_COUNT, INITIAL_SHOW_COUNT);
        assertTrue(PriceTrackingUtilities.isPriceAlertsMessageCardEnabled(mProfile));

        mMessageService =
                new PriceMessageService(
                        mProfile,
                        () -> mMessageProvider,
                        () -> mReviewActionProvider,
                        mNotificationManager);
        mMessageService.addObserver(mMessageObserver);
    }

    @Test(expected = AssertionError.class)
    public void testPrepareMessage_PriceWelcome_MessageDisabled() {
        PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeBoolean(
                PriceTrackingUtilities.PRICE_WELCOME_MESSAGE_CARD, false);
        mMessageService.preparePriceMessage(PriceMessageType.PRICE_WELCOME, mPriceTabData);
    }

    @Test(expected = AssertionError.class)
    public void testPrepareMessage_PriceAlerts_MessageDisabled() {
        PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeBoolean(
                PriceTrackingUtilities.PRICE_ALERTS_MESSAGE_CARD, false);
        mMessageService.preparePriceMessage(PriceMessageType.PRICE_ALERTS, mPriceTabData);
    }

    @Test
    public void testPrepareMessage_PriceWelcome_ExceedMaxShowCount() {
        PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeInt(
                PriceTrackingUtilities.PRICE_WELCOME_MESSAGE_CARD_SHOW_COUNT, MAX_SHOW_COUNT);
        mMessageService.preparePriceMessage(PriceMessageType.PRICE_WELCOME, mPriceTabData);
        assertEquals(
                MAX_SHOW_COUNT + 1, PriceTrackingUtilities.getPriceWelcomeMessageCardShowCount());
        assertFalse(PriceTrackingUtilities.isPriceWelcomeMessageCardEnabled(mProfile));
    }

    @Test
    public void testPrepareMessage_PriceAlerts_ExceedMaxShowCount() {
        PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeInt(
                PriceTrackingUtilities.PRICE_ALERTS_MESSAGE_CARD_SHOW_COUNT, MAX_SHOW_COUNT);
        mMessageService.preparePriceMessage(PriceMessageType.PRICE_ALERTS, null);
        assertEquals(
                MAX_SHOW_COUNT + 1, PriceTrackingUtilities.getPriceAlertsMessageCardShowCount());
        assertFalse(PriceTrackingUtilities.isPriceAlertsMessageCardEnabled(mProfile));
    }

    @Test
    public void testPrepareMessage_PriceWelcome() {
        InOrder inOrder = Mockito.inOrder(mMessageObserver);
        mMessageService.preparePriceMessage(PriceMessageType.PRICE_WELCOME, mPriceTabData);
        inOrder.verify(mMessageObserver, times(1)).messageInvalidate(eq(MessageType.PRICE_MESSAGE));
        assertEquals(mPriceTabData, mMessageService.getPriceTabDataForTesting());
        inOrder.verify(mMessageObserver, times(1))
                .messageReady(
                        eq(MessageService.MessageType.PRICE_MESSAGE),
                        any(PriceMessageService.PriceMessageData.class));
        assertEquals(
                INITIAL_SHOW_COUNT - 1,
                PriceTrackingUtilities.getPriceAlertsMessageCardShowCount());
        assertEquals(
                INITIAL_SHOW_COUNT + 1,
                PriceTrackingUtilities.getPriceWelcomeMessageCardShowCount());
    }

    // Preparing PriceWelcomeMessage shouldn't decrease the show count of PriceAlertsMessage if it's
    // currently disabled.
    @Test
    public void testPrepareMessage_PriceWelcome_PriceAlertsMessageDisabled() {
        PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeBoolean(
                PriceTrackingUtilities.PRICE_ALERTS_MESSAGE_CARD, false);

        InOrder inOrder = Mockito.inOrder(mMessageObserver);
        mMessageService.preparePriceMessage(PriceMessageType.PRICE_WELCOME, mPriceTabData);
        inOrder.verify(mMessageObserver, times(1)).messageInvalidate(eq(MessageType.PRICE_MESSAGE));
        assertEquals(mPriceTabData, mMessageService.getPriceTabDataForTesting());
        inOrder.verify(mMessageObserver, times(1))
                .messageReady(
                        eq(MessageService.MessageType.PRICE_MESSAGE),
                        any(PriceMessageService.PriceMessageData.class));
        assertEquals(
                INITIAL_SHOW_COUNT, PriceTrackingUtilities.getPriceAlertsMessageCardShowCount());
        assertEquals(
                INITIAL_SHOW_COUNT + 1,
                PriceTrackingUtilities.getPriceWelcomeMessageCardShowCount());
    }

    @Test
    public void testReview_PriceWelcome() {
        mMessageService.preparePriceMessage(PriceMessageType.PRICE_WELCOME, mPriceTabData);
        assertEquals(mPriceTabData, mMessageService.getPriceTabDataForTesting());

        int index = 1;
        doReturn(index).when(mMessageProvider).getTabIndexFromTabId(BINDING_TAB_ID);
        doNothing().when(mReviewActionProvider).scrollToTab(anyInt());
        mMessageService.review(PriceMessageType.PRICE_WELCOME);
        verify(mReviewActionProvider).scrollToTab(index);
        verify(mMessageProvider).showPriceDropTooltip(index);
        assertFalse(PriceTrackingUtilities.isPriceWelcomeMessageCardEnabled(mProfile));
        assertNull(mMessageService.getPriceTabDataForTesting());
    }

    @Test
    public void testReview_PriceAlerts_NotificationsEnabled() {
        doReturn(true).when(mNotificationManager).areAppNotificationsEnabled();
        mMessageService.review(PriceMessageType.PRICE_ALERTS);
        verify(mNotificationManager).createNotificationChannel();
        assertFalse(PriceTrackingUtilities.isPriceAlertsMessageCardEnabled(mProfile));
    }

    @Test
    public void testReview_PriceAlerts_NotificationsDisabled() {
        doReturn(false).when(mNotificationManager).areAppNotificationsEnabled();
        mMessageService.review(PriceMessageType.PRICE_ALERTS);
        verify(mNotificationManager).launchNotificationSettings();
        assertFalse(PriceTrackingUtilities.isPriceAlertsMessageCardEnabled(mProfile));
    }

    @Test
    public void testDismiss_PriceWelcome() {
        mMessageService.preparePriceMessage(PriceMessageType.PRICE_WELCOME, mPriceTabData);
        assertEquals(mPriceTabData, mMessageService.getPriceTabDataForTesting());

        mMessageService.dismiss(PriceMessageType.PRICE_WELCOME);
        assertFalse(PriceTrackingUtilities.isPriceWelcomeMessageCardEnabled(mProfile));
        assertNull(mMessageService.getPriceTabDataForTesting());
    }

    @Test
    public void testDismiss_PriceAlerts() {
        mMessageService.dismiss(PriceMessageType.PRICE_ALERTS);
        assertFalse(PriceTrackingUtilities.isPriceAlertsMessageCardEnabled(mProfile));
    }

    @Test
    public void testGetBindingTabId() {
        assertEquals(Tab.INVALID_TAB_ID, mMessageService.getBindingTabId());
        mMessageService.preparePriceMessage(PriceMessageType.PRICE_WELCOME, mPriceTabData);
        assertEquals(BINDING_TAB_ID, mMessageService.getBindingTabId());
    }

    @Test
    public void testInvalidateMessage() {
        mMessageService.preparePriceMessage(PriceMessageType.PRICE_WELCOME, mPriceTabData);
        assertEquals(mPriceTabData, mMessageService.getPriceTabDataForTesting());
        verify(mMessageObserver, times(1)).messageInvalidate(eq(MessageType.PRICE_MESSAGE));
        mMessageService.invalidateMessage();
        assertNull(mMessageService.getPriceTabDataForTesting());
        verify(mMessageObserver, times(2)).messageInvalidate(eq(MessageType.PRICE_MESSAGE));
    }
}