// 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.browserservices.ui.controller.trustedwebactivity;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.chromium.chrome.browser.browserservices.ui.TrustedWebActivityModel.DISCLOSURE_EVENTS_CALLBACK;
import static org.chromium.chrome.browser.browserservices.ui.TrustedWebActivityModel.DISCLOSURE_FIRST_TIME;
import static org.chromium.chrome.browser.browserservices.ui.TrustedWebActivityModel.DISCLOSURE_SCOPE;
import static org.chromium.chrome.browser.browserservices.ui.TrustedWebActivityModel.DISCLOSURE_STATE;
import static org.chromium.chrome.browser.browserservices.ui.TrustedWebActivityModel.DISCLOSURE_STATE_DISMISSED_BY_USER;
import static org.chromium.chrome.browser.browserservices.ui.TrustedWebActivityModel.DISCLOSURE_STATE_NOT_SHOWN;
import static org.chromium.chrome.browser.browserservices.ui.TrustedWebActivityModel.DISCLOSURE_STATE_SHOWN;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.browserservices.BrowserServicesStore;
import org.chromium.chrome.browser.browserservices.metrics.TrustedWebActivityUmaRecorder;
import org.chromium.chrome.browser.browserservices.ui.TrustedWebActivityModel;
import org.chromium.chrome.browser.browserservices.ui.controller.CurrentPageVerifier;
import org.chromium.chrome.browser.browserservices.ui.controller.CurrentPageVerifier.VerificationState;
import org.chromium.chrome.browser.browserservices.ui.controller.CurrentPageVerifier.VerificationStatus;
import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
/** Tests for {@link TrustedWebActivityDisclosureController}. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class TrustedWebActivityDisclosureControllerTest {
private static final String CLIENT_PACKAGE = "com.example.twaclient";
private static final String SCOPE = "https://www.example.com";
@Mock public BrowserServicesStore mStore;
@Mock public ActivityLifecycleDispatcher mLifecycleDispatcher;
@Mock public CurrentPageVerifier mCurrentPageVerifier;
@Mock public TrustedWebActivityUmaRecorder mRecorder;
@Mock public ClientPackageNameProvider mClientPackageNameProvider;
@Captor public ArgumentCaptor<Runnable> mVerificationObserverCaptor;
public TrustedWebActivityModel mModel = new TrustedWebActivityModel();
private TrustedWebActivityDisclosureController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
doReturn(CLIENT_PACKAGE).when(mClientPackageNameProvider).get();
doNothing()
.when(mCurrentPageVerifier)
.addVerificationObserver(mVerificationObserverCaptor.capture());
doReturn(false).when(mStore).hasUserAcceptedTwaDisclosureForPackage(anyString());
mController =
new TrustedWebActivityDisclosureController(
mStore,
mModel,
mLifecycleDispatcher,
mCurrentPageVerifier,
mRecorder,
mClientPackageNameProvider);
}
@Test
@Feature("TrustedWebActivities")
public void showsWhenOriginVerified() {
enterVerifiedOrigin();
assertSnackbarShown();
assertScope(SCOPE);
}
@Test
@Feature("TrustedWebActivities")
public void dismissesWhenLeavingVerifiedOrigin() {
enterVerifiedOrigin();
exitVerifiedOrigin();
assertSnackbarNotShown();
assertScope(null);
}
@Test
@Feature("TrustedWebActivities")
public void showsAgainWhenReenteringTrustedOrigin() {
enterVerifiedOrigin();
exitVerifiedOrigin();
enterVerifiedOrigin();
assertSnackbarShown();
}
@Test
@Feature("TrustedWebActivities")
public void noShowIfAlreadyAccepted() {
doReturn(true).when(mStore).hasUserAcceptedTwaDisclosureForPackage(anyString());
enterVerifiedOrigin();
assertSnackbarNotShown();
}
@Test
@Feature("TrustedWebActivities")
public void recordDismiss() {
enterVerifiedOrigin();
dismissSnackbar();
verify(mStore).setUserAcceptedTwaDisclosureForPackage(CLIENT_PACKAGE);
}
@Test
@Feature("TrustedWebActivities")
public void reportsFirstTime_firstTime() {
doReturn(false).when(mStore).hasUserSeenTwaDisclosureForPackage(anyString());
enterVerifiedOrigin();
assertTrue(mModel.get(DISCLOSURE_FIRST_TIME));
}
@Test
@Feature("TrustedWebActivities")
public void reportsFirstTime_notFirstTime() {
doReturn(true).when(mStore).hasUserSeenTwaDisclosureForPackage(anyString());
enterVerifiedOrigin();
assertFalse(mModel.get(DISCLOSURE_FIRST_TIME));
}
@Test
@Feature("TrustedWebActivities")
public void reportsFirstTime_reportsSeenImmediately() {
doReturn(false).when(mStore).hasUserSeenTwaDisclosureForPackage(anyString());
enterVerifiedOrigin();
assertTrue(mModel.get(DISCLOSURE_FIRST_TIME));
mModel.get(DISCLOSURE_EVENTS_CALLBACK).onDisclosureShown();
assertFalse(mModel.get(DISCLOSURE_FIRST_TIME));
}
@Test
@Feature("TrustedWebActivities")
public void recordsShown() {
enterVerifiedOrigin();
mModel.get(DISCLOSURE_EVENTS_CALLBACK).onDisclosureShown();
verify(mStore).setUserSeenTwaDisclosureForPackage(CLIENT_PACKAGE);
}
@Test
@Feature("TrustedWebActivities")
public void noticesShouldShowDisclosureChanges() {
mController.onFinishNativeInitialization();
enterVerifiedOrigin();
assertSnackbarShown();
doReturn(true).when(mStore).hasUserAcceptedTwaDisclosureForPackage(anyString());
mController.onStopWithNative();
assertEquals(DISCLOSURE_STATE_DISMISSED_BY_USER, mModel.get(DISCLOSURE_STATE));
}
private void enterVerifiedOrigin() {
setVerificationState(new VerificationState(SCOPE, SCOPE, VerificationStatus.SUCCESS));
}
private void exitVerifiedOrigin() {
setVerificationState(new VerificationState(SCOPE, SCOPE, VerificationStatus.FAILURE));
}
private void setVerificationState(VerificationState state) {
doReturn(state).when(mCurrentPageVerifier).getState();
for (Runnable observer : mVerificationObserverCaptor.getAllValues()) {
observer.run();
}
}
private void assertSnackbarShown() {
assertEquals(DISCLOSURE_STATE_SHOWN, mModel.get(DISCLOSURE_STATE));
}
private void assertSnackbarNotShown() {
assertEquals(DISCLOSURE_STATE_NOT_SHOWN, mModel.get(DISCLOSURE_STATE));
}
private void assertScope(String scope) {
assertEquals(scope, mModel.get(DISCLOSURE_SCOPE));
}
private void dismissSnackbar() {
mModel.get(DISCLOSURE_EVENTS_CALLBACK).onDisclosureAccepted();
}
}