chromium/chrome/browser/ui/android/hats/internal/java/src/org/chromium/chrome/browser/ui/hats/SurveyClientBridgeUnitTest.java

// Copyright 2023 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.ui.hats;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import android.app.Activity;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.Robolectric;

import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcherProvider;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.ui.base.WindowAndroid;

import java.lang.ref.WeakReference;
import java.util.Map;

/** Unit test for {@link SurveyClientBridge}. */
@RunWith(BaseRobolectricTestRunner.class)
public class SurveyClientBridgeUnitTest {

    private static final long TEST_NATIVE_POINTER = 45312L;
    private static final String TEST_TRIGGER = "trigger";
    private static final String SUPPLIED_TRIGGER_ID = "SomeOtherSurveyTriggerId";

    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();

    LifecycleDispatcherActivity mActivity;
    @Mock SurveyClientFactory mFactory;
    @Mock SurveyClient mDelegateSurveyClient;
    @Mock ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
    @Mock Profile mProfile;

    @Before
    public void setup() {
        mActivity = Robolectric.buildActivity(LifecycleDispatcherActivity.class).get();
        mActivity.setLifecycleDispatcher(mActivityLifecycleDispatcher);
        SurveyClientFactory.setInstanceForTesting(mFactory);

        doReturn(mDelegateSurveyClient).when(mFactory).createClient(any(), any(), any());
    }

    @After
    public void tearDown() {
        mActivity.finish();
    }

    @Test
    public void showSurveyFromJava() {
        TestSurveyUtils.TestSurveyUiDelegate testDelegate =
                new TestSurveyUtils.TestSurveyUiDelegate();
        TestSurveyUtils.setTestSurveyConfigForTrigger(
                TEST_TRIGGER, new String[] {}, new String[] {});
        SurveyClientBridge bridge =
                SurveyClientBridge.create(
                        TEST_NATIVE_POINTER, TEST_TRIGGER, testDelegate, mProfile, "");
        assertNotNull(bridge);

        bridge.showSurvey(mActivity, mActivityLifecycleDispatcher);
        verify(mDelegateSurveyClient).showSurvey(mActivity, mActivityLifecycleDispatcher);

        ArgumentCaptor<SurveyConfig> surveyConfigArgumentCaptor =
                ArgumentCaptor.forClass(SurveyConfig.class);
        verify(mFactory).createClient(surveyConfigArgumentCaptor.capture(), any(), any());

        assertEquals(
                TestSurveyUtils.TEST_TRIGGER_ID_FOO,
                surveyConfigArgumentCaptor.getValue().mTriggerId);
    }

    @Test
    public void showSurveyFromJavaWithSuppliedTriggerId() {
        TestSurveyUtils.TestSurveyUiDelegate testDelegate =
                new TestSurveyUtils.TestSurveyUiDelegate();
        TestSurveyUtils.setTestSurveyConfigForTrigger(
                TEST_TRIGGER, new String[] {}, new String[] {});
        SurveyClientBridge bridge =
                SurveyClientBridge.create(
                        TEST_NATIVE_POINTER,
                        TEST_TRIGGER,
                        testDelegate,
                        mProfile,
                        SUPPLIED_TRIGGER_ID);
        assertNotNull(bridge);

        bridge.showSurvey(mActivity, mActivityLifecycleDispatcher);
        verify(mDelegateSurveyClient).showSurvey(mActivity, mActivityLifecycleDispatcher);

        ArgumentCaptor<SurveyConfig> surveyConfigArgumentCaptor =
                ArgumentCaptor.forClass(SurveyConfig.class);
        verify(mFactory).createClient(surveyConfigArgumentCaptor.capture(), any(), any());
        assertEquals(SUPPLIED_TRIGGER_ID, surveyConfigArgumentCaptor.getValue().mTriggerId);
    }

    @Test
    public void showSurveyFromJavaWithPsd() {
        TestSurveyUtils.TestSurveyUiDelegate testDelegate =
                new TestSurveyUtils.TestSurveyUiDelegate();
        TestSurveyUtils.setTestSurveyConfigForTrigger(
                TEST_TRIGGER, new String[] {"bit1", "bit2"}, new String[] {"string1", "string2"});
        SurveyClientBridge bridge =
                SurveyClientBridge.create(
                        TEST_NATIVE_POINTER, TEST_TRIGGER, testDelegate, mProfile, "");
        assertNotNull(bridge);

        Map<String, Boolean> bitValues = Map.of("bit1", true, "bit2", false);
        Map<String, String> stringValues = Map.of("string1", "stringVal1", "string2", "stringVal2");
        bridge.showSurvey(mActivity, mActivityLifecycleDispatcher, bitValues, stringValues);
        verify(mDelegateSurveyClient)
                .showSurvey(mActivity, mActivityLifecycleDispatcher, bitValues, stringValues);
    }

    @Test
    public void showSurveyFromNativeWithPsd() {
        String[] bitFields = new String[] {"fieldTrue", "fieldFalse"};
        String[] stringFields = new String[] {"string1", "string2"};
        TestSurveyUtils.TestSurveyUiDelegate testDelegate =
                new TestSurveyUtils.TestSurveyUiDelegate();
        TestSurveyUtils.setTestSurveyConfigForTrigger(TEST_TRIGGER, bitFields, stringFields);
        SurveyClientBridge bridge =
                SurveyClientBridge.create(
                        TEST_NATIVE_POINTER, TEST_TRIGGER, testDelegate, mProfile, "");
        assertNotNull(bridge);

        WindowAndroid window = mock(WindowAndroid.class);
        doReturn(new WeakReference<>(mActivity)).when(window).getActivity();

        bridge.showSurvey(
                window,
                bitFields,
                new boolean[] {true, false},
                stringFields,
                new String[] {"stringVal1", "stringVal2"});

        ArgumentCaptor<Map<String, Boolean>> bitValueCaptor = ArgumentCaptor.forClass(Map.class);
        ArgumentCaptor<Map<String, String>> stringValueCaptor = ArgumentCaptor.forClass(Map.class);

        verify(mDelegateSurveyClient)
                .showSurvey(
                        eq(mActivity),
                        eq(mActivityLifecycleDispatcher),
                        bitValueCaptor.capture(),
                        stringValueCaptor.capture());

        // Check bit values
        assertEquals("Bit PSD value mismatch.", true, bitValueCaptor.getValue().get("fieldTrue"));
        assertEquals("Bit PSD value mismatch.", false, bitValueCaptor.getValue().get("fieldFalse"));
        assertEquals(
                "String PSD value mismatch.",
                "stringVal1",
                stringValueCaptor.getValue().get("string1"));
        assertEquals(
                "String PSD value mismatch.",
                "stringVal2",
                stringValueCaptor.getValue().get("string2"));
    }

    // Test activity that allows ActivityLifecycleDispatcherProvider casting in code.
    static class LifecycleDispatcherActivity extends Activity
            implements ActivityLifecycleDispatcherProvider {

        private ActivityLifecycleDispatcher mDispatcher;

        public void setLifecycleDispatcher(ActivityLifecycleDispatcher dispatcher) {
            mDispatcher = dispatcher;
        }

        @Override
        public ActivityLifecycleDispatcher getLifecycleDispatcher() {
            return mDispatcher;
        }
    }
}