chromium/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappLauncherActivityTest.java

// Copyright 2019 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.webapps;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;

import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.components.webapk.lib.client.WebApkValidator;
import org.chromium.components.webapk.lib.common.WebApkMetaDataKeys;
import org.chromium.webapk.lib.common.WebApkConstants;
import org.chromium.webapk.test.WebApkTestHelper;

/** JUnit test for WebappLauncherActivity. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
@CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
public class WebappLauncherActivityTest {
    private static final String WEBAPK_PACKAGE_NAME = "org.chromium.webapk.test_package";
    private static final String START_URL = "https://www.google.com/scope/a_is_for_apple";

    @Before
    public void setUp() {
        WebApkValidator.setDisableValidationForTesting(true);
    }

    /**
     * Test that WebappLauncherActivity modifies the passed-in intent so that
     * WebApkIntentDataProviderFactory#create() returns null if the intent does not refer to a valid
     * WebAPK.
     */
    @Test
    public void testTryCreateWebappInfoAltersIntentIfNotValidWebApk() {
        WebApkValidator.setDisableValidationForTesting(false);

        registerWebApk(WEBAPK_PACKAGE_NAME, START_URL);
        Intent intent = WebApkTestHelper.createMinimalWebApkIntent(WEBAPK_PACKAGE_NAME, START_URL);

        assertNotNull(WebApkIntentDataProviderFactory.create(intent));
        Robolectric.buildActivity(WebappLauncherActivity.class, intent).create();
        assertNull(WebApkIntentDataProviderFactory.create(intent));
    }

    /** Test the launch intent created by {@link WebappLauncherActivity} for old-style WebAPKs. */
    @Test
    public void testOldStyleLaunchIntent() {
        registerWebApk(WEBAPK_PACKAGE_NAME, START_URL);

        Intent intent = WebApkTestHelper.createMinimalWebApkIntent(WEBAPK_PACKAGE_NAME, START_URL);
        Robolectric.buildActivity(WebappLauncherActivity.class, intent).create();

        Intent launchIntent = getNextStartedActivity();
        assertEquals(WebappActivity.class.getName(), launchIntent.getComponent().getClassName());
        // WebAPK package name should be part of the intent URI to enable launching multiple
        // WebAPKs.
        assertEquals("webapp://webapk-" + WEBAPK_PACKAGE_NAME, launchIntent.getDataString());
        assertTrue((launchIntent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0);
        assertNotNull(WebApkIntentDataProviderFactory.create(launchIntent));
    }

    /** Test the launch intent created by {@link WebappLauncherActivity} for new-style WebAPKs. */
    @Test
    public void testNewStyleLaunchIntent() {
        registerWebApk(WEBAPK_PACKAGE_NAME, START_URL);

        Intent intent = WebApkTestHelper.createMinimalWebApkIntent(WEBAPK_PACKAGE_NAME, START_URL);
        intent.putExtra(WebApkConstants.EXTRA_SPLASH_PROVIDED_BY_WEBAPK, true);
        Robolectric.buildActivity(WebappLauncherActivity.class, intent).create();

        Intent launchIntent = getNextStartedActivity();
        assertEquals(
                SameTaskWebApkActivity.class.getName(), launchIntent.getComponent().getClassName());
        assertEquals(launchIntent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK, 0);
        assertNotNull(WebApkIntentDataProviderFactory.create(launchIntent));
    }

    /**
     * Test that an intent with only a URL causes an intent to {@link ChromeLauncherActivity} (ie, a
     * "launch in tab" action) rather than an intent to {@link SameTaskWebApkActivity} or {@link
     * WebappActivity}. In particular, that means that this intent did NOT make it past the security
     * checks that ensure that webapp launches must be triggered from trusted intents from Chrome.
     */
    @Test
    public void testUnauthenticatedNonWebApkIntentOpensInTabAndNotWebappMode() {
        Intent intent = new Intent();
        intent.setPackage(RuntimeEnvironment.application.getPackageName());
        intent.putExtra(WebApkConstants.EXTRA_URL, START_URL);

        Activity activity =
                Robolectric.buildActivity(WebappLauncherActivity.class, intent).setup().get();

        Intent nextIntent = Shadows.shadowOf(activity).getNextStartedActivityForResult().intent;
        assertEquals(
                "org.chromium.chrome.browser.document.ChromeLauncherActivity",
                nextIntent.getComponent().getClassName());

        // And just in case, one with an empty-string WebAPK Package Name.
        intent = new Intent();
        intent.setPackage(RuntimeEnvironment.application.getPackageName());
        intent.putExtra(WebApkConstants.EXTRA_URL, START_URL);
        intent.putExtra(WebApkConstants.EXTRA_WEBAPK_PACKAGE_NAME, "");

        activity = Robolectric.buildActivity(WebappLauncherActivity.class, intent).setup().get();

        nextIntent = Shadows.shadowOf(activity).getNextStartedActivityForResult().intent;
        assertEquals(
                "org.chromium.chrome.browser.document.ChromeLauncherActivity",
                nextIntent.getComponent().getClassName());
    }

    private void registerWebApk(String webApkPackage, String startUrl) {
        Bundle bundle = new Bundle();
        bundle.putString(WebApkMetaDataKeys.START_URL, START_URL);
        WebApkTestHelper.registerWebApkWithMetaData(
                webApkPackage, bundle, /* shareTargetMetaData= */ null);
        WebApkTestHelper.addIntentFilterForUrl(webApkPackage, startUrl);
    }

    private Intent getNextStartedActivity() {
        return ShadowApplication.getInstance().getNextStartedActivity();
    }
}