// Copyright 2022 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.digitalgoods;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.chromium.chrome.browser.browserservices.digitalgoods.DigitalGoodsConverter.PLAY_BILLING_OK;
import android.os.Bundle;
import android.os.RemoteException;
import androidx.browser.trusted.TrustedWebActivityCallback;
import org.mockito.Mockito;
import org.chromium.chrome.browser.browserservices.TrustedWebActivityClient;
import org.chromium.chrome.browser.browserservices.TrustedWebActivityClientWrappers;
import org.chromium.components.embedder_support.util.Origin;
/**
* Helper class for {@link DigitalGoodsUnitTest} - it pretends to be a TrustedWebActivityClient
* talking to the Digital Goods handler in the TWA shell, providing hardcoded responses.
*/
public class MockTrustedWebActivityClient {
private TrustedWebActivityClientWrappers.Connection mConnection;
private TrustedWebActivityClient mClient;
private Runnable mCallback;
private int mVersion = 2;
private static final Bundle ITEM1 =
GetDetailsConverter.createItemDetailsBundle(
"id1", "title 1", "description 1", "GBP", "20");
private static final Bundle ITEM2 =
GetDetailsConverter.createItemDetailsBundle(
"id2",
"title 2",
"description 2",
"GBP",
"30",
"subs",
"https://www.example.com/image.png",
"2 weeks",
"month",
"GBP",
"20",
"week",
2);
private static final Bundle PURCHASE1 =
ListPurchasesConverter.createPurchaseReferenceBundle("id3", "abc");
private static final Bundle PURCHASE2 =
ListPurchasesConverter.createPurchaseReferenceBundle("id4", "def");
public MockTrustedWebActivityClient() {
mConnection = Mockito.mock(TrustedWebActivityClientWrappers.Connection.class);
mClient = Mockito.mock(TrustedWebActivityClient.class);
doAnswer(
invocation -> {
TrustedWebActivityClient.ExecutionCallback callback =
invocation.getArgument(1);
callback.onConnected(Mockito.mock(Origin.class), mConnection);
return null;
})
.when(mClient)
.connectAndExecute(any(), any());
try {
doAnswer(
invocation -> {
String command = invocation.getArgument(0);
Bundle args = invocation.getArgument(1);
TrustedWebActivityCallback callback = invocation.getArgument(2);
return handleWrapper(command, args, callback);
})
.when(mConnection)
.sendExtraCommand(any(), any(), any());
} catch (RemoteException e) {
// This ugliness is because we're mocking a method that could throw an exception.
throw new RuntimeException(e);
}
}
/** Returns a mocked {@link TrustedWebActivityClient} to be used in tests. */
public TrustedWebActivityClient getClient() {
return mClient;
}
/** Runs the pending callback. */
public void runCallback() {
mCallback.run();
mCallback = null;
}
/** Changes the clients behaviour to simulate older TWA shells. */
public void setVersion(int version) {
mVersion = version;
}
private boolean handle(String command, Bundle args, TrustedWebActivityCallback callback) {
// At this point, pretend we're in the TWA shell.
switch (command) {
case "getDetails":
mCallback =
() -> {
callback.onExtraCallback(
"getDetails.response",
GetDetailsConverter.createResponseBundle(
PLAY_BILLING_OK, ITEM1, ITEM2));
};
return true;
case "listPurchases":
mCallback =
() -> {
callback.onExtraCallback(
"listPurchases.response",
ListPurchasesConverter.createResponseBundle(
PLAY_BILLING_OK, PURCHASE1, PURCHASE2));
};
return true;
case "listPurchaseHistory":
// Version 1 clients did not have purchase history.
if (mVersion == 1) return false;
mCallback =
() -> {
// Swap the order of PURCHASE1 and PURCHASE2 as an easy way to make sure
// the correct branch of this switch statement is executed.
callback.onExtraCallback(
"listPurchaseHistory.response",
ListPurchaseHistoryConverter.createResponseBundle(
PLAY_BILLING_OK, PURCHASE2, PURCHASE1));
};
return true;
case "consume":
// Version 1 clients did not have consume.
if (mVersion == 1) return false;
mCallback =
() -> {
callback.onExtraCallback(
"consume.response",
ConsumeConverter.createResponseBundle(PLAY_BILLING_OK));
};
return true;
case "acknowledge":
// Only version 1 clients have acknowledge.
if (mVersion != 1) return false;
mCallback =
() -> {
callback.onExtraCallback(
"acknowledge.response",
AcknowledgeConverter.createResponseBundle(PLAY_BILLING_OK));
};
return true;
}
return false;
}
private Bundle handleWrapper(String command, Bundle args, TrustedWebActivityCallback callback) {
Bundle bundle = new Bundle();
bundle.putBoolean("success", handle(command, args, callback));
return bundle;
}
}