chromium/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/button_group_component/KeyboardAccessoryButtonGroupControllerTest.java

// Copyright 2018 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.keyboard_accessory.button_group_component;

import static androidx.test.espresso.matcher.ViewMatchers.assertThat;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;

import static org.chromium.chrome.browser.keyboard_accessory.button_group_component.KeyboardAccessoryButtonGroupProperties.ACTIVE_TAB;
import static org.chromium.chrome.browser.keyboard_accessory.button_group_component.KeyboardAccessoryButtonGroupProperties.TABS;

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

import org.chromium.base.task.test.CustomShadowAsyncTask;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData;
import org.chromium.ui.modelutil.ListObservable;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyObservable.PropertyObserver;

/** Controller tests for the keyboard accessory tab layout component. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(
        manifest = Config.NONE,
        shadows = {CustomShadowAsyncTask.class})
public class KeyboardAccessoryButtonGroupControllerTest {

    @Mock private PropertyObserver<PropertyKey> mMockPropertyObserver;
    @Mock private ListObservable.ListObserver<Void> mMockTabListObserver;

    @Mock
    private KeyboardAccessoryButtonGroupCoordinator.AccessoryTabObserver mMockAccessoryTabObserver;

    private final KeyboardAccessoryData.Tab mTestTab =
            new KeyboardAccessoryData.Tab("Passwords", null, null, 0, 0, null);

    private KeyboardAccessoryButtonGroupCoordinator mCoordinator;
    private PropertyModel mModel;
    private KeyboardAccessoryButtonGroupMediator mMediator;

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

        mCoordinator = new KeyboardAccessoryButtonGroupCoordinator();
        mMediator = mCoordinator.getMediatorForTesting();
        mModel = mCoordinator.getModelForTesting();
        mCoordinator.setTabObserver(mMockAccessoryTabObserver);
    }

    @Test
    public void testCreatesValidSubComponents() {
        assertThat(mCoordinator, is(notNullValue()));
        assertThat(mMediator, is(notNullValue()));
        assertThat(mModel, is(notNullValue()));
    }

    @Test
    public void testChangingTabsNotifiesTabObserver() {
        mModel.get(TABS).addObserver(mMockTabListObserver);

        // Calling addTab on the coordinator should make the model propagate that it has a new tab.
        mCoordinator.getTabSwitchingDelegate().addTab(mTestTab);
        verify(mMockTabListObserver).onItemRangeInserted(mModel.get(TABS), 0, 1);
        assertThat(mModel.get(TABS).size(), is(1));
        assertThat(mModel.get(TABS).get(0), is(mTestTab));

        // Calling hide on the coordinator should make the model propagate that it's invisible.
        mCoordinator.getTabSwitchingDelegate().removeTab(mTestTab);
        verify(mMockTabListObserver).onItemRangeRemoved(mModel.get(TABS), 0, 1);
        assertThat(mModel.get(TABS).size(), is(0));
    }

    @Test
    public void testModelDoesntNotifyUnchangedActiveTab() {
        mModel.addObserver(mMockPropertyObserver);

        assertThat(mModel.get(ACTIVE_TAB), is(nullValue()));
        mModel.set(ACTIVE_TAB, null);
        assertThat(mModel.get(ACTIVE_TAB), is(nullValue()));
        verify(mMockPropertyObserver, never()).onPropertyChanged(mModel, ACTIVE_TAB);

        mModel.set(ACTIVE_TAB, 0);
        assertThat(mModel.get(ACTIVE_TAB), is(0));
        verify(mMockPropertyObserver).onPropertyChanged(mModel, ACTIVE_TAB);

        mModel.set(ACTIVE_TAB, 0);
        assertThat(mModel.get(ACTIVE_TAB), is(0));
        verify(mMockPropertyObserver).onPropertyChanged(mModel, ACTIVE_TAB);
    }

    @Test
    public void testClosingTabDismissesOpenSheet() {
        mModel.set(ACTIVE_TAB, 0);
        mModel.addObserver(mMockPropertyObserver);
        assertThat(mModel.get(ACTIVE_TAB), is(0));

        // Closing the active tab should reset the tab which should trigger the visibility delegate.
        mCoordinator.getTabSwitchingDelegate().closeActiveTab();
        assertThat(mModel.get(ACTIVE_TAB), is(nullValue()));
        verify(mMockPropertyObserver).onPropertyChanged(mModel, ACTIVE_TAB);
        verify(mMockAccessoryTabObserver).onActiveTabChanged(null);
    }

    @Test
    public void testClosingTabIsNoOpForAlreadyClosedTab() {
        mModel.set(ACTIVE_TAB, null);
        mModel.addObserver(mMockPropertyObserver);

        mCoordinator.getTabSwitchingDelegate().closeActiveTab();
        verifyNoMoreInteractions(
                mMockPropertyObserver, mMockTabListObserver, mMockAccessoryTabObserver);
    }

    @Test
    public void testSetActiveTab() {
        mModel.addObserver(mMockPropertyObserver);
        assertThat(mModel.get(ACTIVE_TAB), is(nullValue()));
        mCoordinator.getTabSwitchingDelegate().addTab(mTestTab);

        // Set the active tab type to 0 which is the recording_type of |mTestTab|.
        mCoordinator.getTabSwitchingDelegate().setActiveTab(0);

        verify(mMockPropertyObserver).onPropertyChanged(mModel, ACTIVE_TAB);
        assertThat(mModel.get(ACTIVE_TAB), is(0));
    }

    @Test(expected = AssertionError.class)
    public void testSetActiveTab_tabTypeNotFound_throwsException() {
        mModel.addObserver(mMockPropertyObserver);
        assertThat(mModel.get(ACTIVE_TAB), is(nullValue()));
        mCoordinator.getTabSwitchingDelegate().addTab(mTestTab);

        // Set the active tab type to 1 which is different from the recording_type of |mTestTab|.
        mCoordinator.getTabSwitchingDelegate().setActiveTab(1);
    }
}