diff --git a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java index f9188015476f..c56379d7c049 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java @@ -36,6 +36,7 @@ import org.wordpress.android.ui.activitylog.list.ActivityLogListActivity; import org.wordpress.android.ui.comments.CommentsActivity; import org.wordpress.android.ui.domains.DomainRegistrationActivity; +import org.wordpress.android.ui.domains.DomainRegistrationActivity.DomainRegistrationPurpose; import org.wordpress.android.ui.giphy.GiphyPickerActivity; import org.wordpress.android.ui.history.HistoryDetailActivity; import org.wordpress.android.ui.history.HistoryDetailContainerFragment; @@ -414,6 +415,14 @@ public static void viewDomainRegistrationActivity(Activity activity, SiteModel s activity.startActivity(intent); } + public static void viewDomainRegistrationActivityForResult(Activity activity, SiteModel site, + DomainRegistrationPurpose purpose) { + Intent intent = new Intent(activity, DomainRegistrationActivity.class); + intent.putExtra(WordPress.SITE, site); + intent.putExtra(DomainRegistrationActivity.DOMAIN_REGISTRATION_PURPOSE_KEY, purpose); + activity.startActivityForResult(intent, RequestCodes.DOMAIN_REGISTRATION); + } + public static void viewActivityLogList(Activity activity, SiteModel site) { if (site == null) { ToastUtils.showToast(activity, R.string.blog_not_found, ToastUtils.Duration.SHORT); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java b/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java index d4fb98a8e5c8..3860953e1eee 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java @@ -46,4 +46,7 @@ public class RequestCodes { public static final int QUICK_START_REMINDER_NOTIFICATION = 4001; public static final int GIPHY_PICKER = 3200; + + // Domain Registration + public static final int DOMAIN_REGISTRATION = 5000; } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/DomainRegistrationActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/DomainRegistrationActivity.kt index f6d46f0875d3..7b877c54fc20 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/domains/DomainRegistrationActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/DomainRegistrationActivity.kt @@ -1,17 +1,33 @@ package org.wordpress.android.ui.domains +import android.app.Activity import android.os.Bundle import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity import kotlinx.android.synthetic.main.toolbar.* import org.wordpress.android.R +import org.wordpress.android.ui.domains.DomainRegistrationActivity.DomainRegistrationPurpose.CTA_DOMAIN_CREDIT_REDEMPTION class DomainRegistrationActivity : AppCompatActivity(), DomainRegistrationStepsListener { + enum class DomainRegistrationPurpose { + AUTOMATED_TRANSFER, + CTA_DOMAIN_CREDIT_REDEMPTION + } + + companion object { + const val DOMAIN_REGISTRATION_PURPOSE_KEY = "DOMAIN_REGISTRATION_PURPOSE_KEY" + } + + private var domainRegistrationPurpose: DomainRegistrationPurpose? = null + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_domain_suggestions_activity) + domainRegistrationPurpose = intent.getSerializableExtra(DOMAIN_REGISTRATION_PURPOSE_KEY) + as? DomainRegistrationPurpose + setSupportActionBar(toolbar) supportActionBar?.let { it.setHomeButtonEnabled(true) @@ -50,15 +66,20 @@ class DomainRegistrationActivity : AppCompatActivity(), DomainRegistrationStepsL } override fun onDomainRegistered(domainName: String) { - supportFragmentManager.beginTransaction() - .setCustomAnimations( - R.anim.activity_slide_in_from_right, R.anim.activity_slide_out_to_left, - R.anim.activity_slide_in_from_left, R.anim.activity_slide_out_to_right - ) - .replace( - R.id.fragment_container, - DomainRegistrationResultFragment.newInstance(domainName) - ) - .commit() + if (domainRegistrationPurpose == null || domainRegistrationPurpose == CTA_DOMAIN_CREDIT_REDEMPTION) { + supportFragmentManager.beginTransaction() + .setCustomAnimations( + R.anim.activity_slide_in_from_right, R.anim.activity_slide_out_to_left, + R.anim.activity_slide_in_from_left, R.anim.activity_slide_out_to_right + ) + .replace( + R.id.fragment_container, + DomainRegistrationResultFragment.newInstance(domainName) + ) + .commit() + } else { + setResult(Activity.RESULT_OK) + finish() + } } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/plugins/PluginDetailActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/plugins/PluginDetailActivity.java index 9e11d23f43e2..921fb7b99c3b 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/plugins/PluginDetailActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/plugins/PluginDetailActivity.java @@ -1,10 +1,12 @@ package org.wordpress.android.ui.plugins; import android.animation.ObjectAnimator; +import android.app.Activity; import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.text.Html; @@ -41,6 +43,7 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +import org.jetbrains.annotations.NotNull; import org.wordpress.android.BuildConfig; import org.wordpress.android.R; import org.wordpress.android.WordPress; @@ -71,6 +74,10 @@ import org.wordpress.android.fluxc.store.SiteStore.OnPlansFetched; import org.wordpress.android.fluxc.store.SiteStore.OnSiteChanged; import org.wordpress.android.ui.ActivityLauncher; +import org.wordpress.android.ui.RequestCodes; +import org.wordpress.android.ui.domains.DomainRegistrationActivity.DomainRegistrationPurpose; +import org.wordpress.android.ui.posts.BasicFragmentDialog; +import org.wordpress.android.ui.posts.BasicFragmentDialog.BasicDialogPositiveClickInterface; import org.wordpress.android.util.AniUtils; import org.wordpress.android.util.AppLog; import org.wordpress.android.util.AppLog.T; @@ -103,7 +110,8 @@ import javax.inject.Inject; -public class PluginDetailActivity extends AppCompatActivity implements OnDomainRegistrationRequestedListener { +public class PluginDetailActivity extends AppCompatActivity implements OnDomainRegistrationRequestedListener, + BasicDialogPositiveClickInterface { public static final String KEY_PLUGIN_SLUG = "KEY_PLUGIN_SLUG"; private static final String KEY_IS_CONFIGURING_PLUGIN = "KEY_IS_CONFIGURING_PLUGIN"; private static final String KEY_IS_INSTALLING_PLUGIN = "KEY_IS_INSTALLING_PLUGIN"; @@ -119,6 +127,12 @@ public class PluginDetailActivity extends AppCompatActivity implements OnDomainR = "KEY_IS_SHOWING_AUTOMATED_TRANSFER_PROGRESS"; private static final String KEY_IS_SHOWING_DOMAIN_CREDIT_CHECK_PROGRESS = "KEY_IS_SHOWING_DOMAIN_CREDIT_CHECK_PROGRESS"; + private static final String KEY_PLUGIN_RECHECKED_TIMES = "KEY_PLUGIN_RECHECKED_TIMES"; + private static final String TAG_ERROR_DIALOG = "ERROR_DIALOG"; + + private static final int MAX_PLUGIN_CHECK_TRIES = 10; + private static final int DEFAULT_RETRY_DELAY_MS = 3000; + private static final int PLUGIN_RETRY_DELAY_MS = 10000; private SiteModel mSite; private String mSlug; @@ -162,6 +176,8 @@ public class PluginDetailActivity extends AppCompatActivity implements OnDomainR protected boolean mIsShowingInstallFirstPluginConfirmationDialog; protected boolean mIsShowingAutomatedTransferProgress; + private int mPluginReCheckTimer = 0; + // These flags reflects the UI state protected boolean mIsActive; protected boolean mIsAutoUpdateEnabled; @@ -226,6 +242,7 @@ public void onCreate(Bundle savedInstanceState) { .getBoolean(KEY_IS_SHOWING_AUTOMATED_TRANSFER_PROGRESS); isShowingDomainCreditCheckProgress = savedInstanceState .getBoolean(KEY_IS_SHOWING_DOMAIN_CREDIT_CHECK_PROGRESS); + mPluginReCheckTimer = savedInstanceState.getInt(KEY_PLUGIN_RECHECKED_TIMES, 0); } setContentView(R.layout.plugin_detail_activity); @@ -310,7 +327,24 @@ public void onPlansFetched(OnPlansFetched event) { @Override public void onDomainRegistrationRequested() { - ActivityLauncher.viewDomainRegistrationActivity(this, mSite); + ActivityLauncher.viewDomainRegistrationActivityForResult(this, mSite, + DomainRegistrationPurpose.AUTOMATED_TRANSFER); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == RequestCodes.DOMAIN_REGISTRATION) { + if (resultCode != Activity.RESULT_OK || isFinishing()) { + return; + } + confirmInstallPluginForAutomatedTransfer(); + } + } + + @Override + public void onPositiveClicked(@NotNull String instanceTag) { + // do nothing } public static class DomainRegistrationPromptDialog extends DialogFragment { @@ -412,6 +446,7 @@ public void onSaveInstanceState(Bundle outState) { outState.putBoolean(KEY_IS_SHOWING_AUTOMATED_TRANSFER_PROGRESS, mIsShowingAutomatedTransferProgress); outState.putBoolean(KEY_IS_SHOWING_DOMAIN_CREDIT_CHECK_PROGRESS, mCheckingDomainCreditsProgressDialog != null && mCheckingDomainCreditsProgressDialog.isShowing()); + outState.putInt(KEY_PLUGIN_RECHECKED_TIMES, mPluginReCheckTimer); } // UI Helpers @@ -1353,6 +1388,7 @@ private void automatedTransferCompleted() { AnalyticsUtils.trackWithSiteDetails(Stat.AUTOMATED_TRANSFER_FLOW_COMPLETE, mSite); cancelAutomatedTransferDialog(); refreshPluginFromStore(); + dispatchConfigurePluginAction(true); refreshViews(); showSuccessfulInstallSnackbar(); invalidateOptionsMenu(); @@ -1364,7 +1400,10 @@ private void automatedTransferCompleted() { */ private void handleAutomatedTransferFailed(String errorMessage) { cancelAutomatedTransferDialog(); - ToastUtils.showToast(this, errorMessage, Duration.LONG); + BasicFragmentDialog errorDialog = new BasicFragmentDialog(); + errorDialog.initialize(TAG_ERROR_DIALOG, null, errorMessage, + getString(R.string.dialog_button_ok), null, null); + errorDialog.show(getSupportFragmentManager(), TAG_ERROR_DIALOG); } /** @@ -1483,7 +1522,7 @@ public void run() { // Wait 3 seconds before checking the status again mDispatcher.dispatch(SiteActionBuilder.newCheckAutomatedTransferStatusAction(mSite)); } - }, 3000); + }, DEFAULT_RETRY_DELAY_MS); } } } @@ -1514,10 +1553,9 @@ public void onSiteChanged(OnSiteChanged event) { // We try to fetch the site after Automated Transfer is completed so that we can fetch its plugins. If // we are still showing the AT progress and the site is AT site, we can continue with plugins fetch if (mSite.isAutomatedTransfer()) { - AppLog.v(T.PLUGINS, "Site is successfully fetched after Automated Transfer, fetching the site plugins " - + "to complete the process..."); - mDispatcher.dispatch(PluginActionBuilder.newFetchPluginDirectoryAction(new PluginStore - .FetchPluginDirectoryPayload(PluginDirectoryType.SITE, mSite, false))); + AppLog.v(T.PLUGINS, "Site is successfully fetched after Automated Transfer, fetching" + + " the site plugins to complete the process..."); + fetchPluginDirectory(0); } else { // Either an error occurred while fetching the site or Automated Transfer is not yet reflected in the // API response. We need to keep fetching the site until we get the updated site. Otherwise, any changes @@ -1532,7 +1570,7 @@ public void run() { // Wait 3 seconds before fetching the site again mDispatcher.dispatch(SiteActionBuilder.newFetchSiteAction(mSite)); } - }, 3000); + }, DEFAULT_RETRY_DELAY_MS); } } } @@ -1551,26 +1589,38 @@ public void onPluginDirectoryFetched(OnPluginDirectoryFetched event) { if (isFinishing()) { return; } + + refreshPluginFromStore(); + if (event.isError()) { if (mIsShowingAutomatedTransferProgress) { AppLog.e(T.PLUGINS, "Fetching the plugin directory after Automated Transfer has failed with error type" + event.error.type + " and message: " + event.error.message); // Although unlikely, fetching the plugins after a successful Automated Transfer can result in an error. // This should hopefully be an edge case and fetching the plugins again should - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - AppLog.v(T.PLUGINS, "Fetching the site plugins again after Automated Transfer since the" - + " changes are not yet reflected"); - // Wait 3 seconds before fetching the site plugins again - mDispatcher.dispatch(PluginActionBuilder.newFetchPluginDirectoryAction(new PluginStore - .FetchPluginDirectoryPayload(PluginDirectoryType.SITE, mSite, false))); - } - }, 3000); + AppLog.v(T.PLUGINS, "Fetching the site plugins again after Automated Transfer since the" + + " changes are not yet reflected"); + fetchPluginDirectory(PLUGIN_RETRY_DELAY_MS); } // We are safe to ignore the errors for this event unless it's for Automated Transfer since that's the only // one triggered in this page and only one we care about. return; + } else if (!mPlugin.isInstalled()) { + // it sometimes take a bit of time for plugin to get marked as installed, especially when + // Automated Transfer is performed right after domain registration + if (mIsShowingAutomatedTransferProgress) { + if (mPluginReCheckTimer < MAX_PLUGIN_CHECK_TRIES) { + AppLog.v(T.PLUGINS, "Targeted plugin is not marked as installed after Automated Transfer." + + " Fetching the site plugins to reflect the changes."); + fetchPluginDirectory(PLUGIN_RETRY_DELAY_MS); + mPluginReCheckTimer++; + return; + } else { + // if plugin is still not marked as installed, we ask user to check back later, and proceed to + // finish Automated Transfer + ToastUtils.showToast(this, R.string.plugin_fetching_error_after_at, Duration.LONG); + } + } } if (event.type == PluginDirectoryType.SITE && mIsShowingAutomatedTransferProgress) { // After Automated Transfer flow is completed, we fetch the site and then it's plugins. The only way site's @@ -1580,11 +1630,20 @@ public void run() { } else { // Although it's unlikely that a directory might be fetched while we are in the plugin detail page, we // should be safe to refresh the plugin and the view in case the plugin we are showing has changed - refreshPluginFromStore(); refreshViews(); } } + private void fetchPluginDirectory(int delay) { + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + mDispatcher.dispatch(PluginActionBuilder.newFetchPluginDirectoryAction(new PluginStore + .FetchPluginDirectoryPayload(PluginDirectoryType.SITE, mSite, false))); + } + }, delay); + } + private String getEligibilityErrorMessage(String errorCode) { int errorMessageRes; switch (errorCode) { diff --git a/WordPress/src/main/java/org/wordpress/android/viewmodel/domains/DomainRegistrationDetailsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/viewmodel/domains/DomainRegistrationDetailsViewModel.kt index 1e19d365b0b9..f6dc46f00b6b 100644 --- a/WordPress/src/main/java/org/wordpress/android/viewmodel/domains/DomainRegistrationDetailsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/viewmodel/domains/DomainRegistrationDetailsViewModel.kt @@ -3,7 +3,9 @@ package org.wordpress.android.viewmodel.domains import android.text.TextUtils import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import org.wordpress.android.fluxc.Dispatcher @@ -16,7 +18,10 @@ import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.network.rest.wpcom.site.SupportedStateResponse import org.wordpress.android.fluxc.network.rest.wpcom.transactions.SupportedDomainCountry import org.wordpress.android.fluxc.store.AccountStore.OnDomainContactFetched +import org.wordpress.android.fluxc.store.SiteStore +import org.wordpress.android.fluxc.store.SiteStore.DesignatePrimaryDomainPayload import org.wordpress.android.fluxc.store.SiteStore.OnDomainSupportedStatesFetched +import org.wordpress.android.fluxc.store.SiteStore.OnPrimaryDomainDesignated import org.wordpress.android.fluxc.store.SiteStore.OnSiteChanged import org.wordpress.android.fluxc.store.TransactionsStore import org.wordpress.android.fluxc.store.TransactionsStore.CreateShoppingCartPayload @@ -25,21 +30,31 @@ import org.wordpress.android.fluxc.store.TransactionsStore.OnShoppingCartRedeeme import org.wordpress.android.fluxc.store.TransactionsStore.OnSupportedCountriesFetched import org.wordpress.android.fluxc.store.TransactionsStore.RedeemShoppingCartError import org.wordpress.android.fluxc.store.TransactionsStore.RedeemShoppingCartPayload +import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.domains.DomainProductDetails import org.wordpress.android.util.AppLog import org.wordpress.android.util.AppLog.T +import org.wordpress.android.viewmodel.ScopedViewModel import org.wordpress.android.viewmodel.SingleLiveEvent import javax.inject.Inject +import javax.inject.Named + +const val SITE_CHECK_DELAY_MS = 5000L +const val MAX_SITE_CHECK_TRIES = 10 class DomainRegistrationDetailsViewModel @Inject constructor( private val dispatcher: Dispatcher, - private val transactionsStore: TransactionsStore // needed for events to work -) : ViewModel() { + private val transactionsStore: TransactionsStore, // needed for events to work + private val siteStore: SiteStore, + @param:Named(UI_THREAD) private val uiDispatcher: CoroutineDispatcher +) : ScopedViewModel(uiDispatcher) { private lateinit var site: SiteModel private lateinit var domainProductDetails: DomainProductDetails private var isStarted = false + private var siteCheckTries = 0 + private var supportedCountries: List? = null private val _supportedStates = MutableLiveData>() @@ -208,21 +223,68 @@ class DomainRegistrationDetailsViewModel @Inject constructor( return } + // after cart is redeemed, wait for a bit before manually setting domain as primary + launch { + delay(SITE_CHECK_DELAY_MS) + dispatcher.dispatch( + SiteActionBuilder.newDesignatePrimaryDomainAction( + DesignatePrimaryDomainPayload( + site, + domainProductDetails.domainName + ) + ) + ) + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onPrimaryDomainDesignated(event: OnPrimaryDomainDesignated) { + if (event.isError) { // in case of error we notify used and proceed to next step + _showErrorMessage.value = event.error.message + AppLog.e( + T.DOMAIN_REGISTRATION, + "An error occurred while redeeming a shopping cart : " + event.error.type + + " " + event.error.message + ) + } + dispatcher.dispatch(SiteActionBuilder.newFetchSiteAction(site)) } @Subscribe(threadMode = ThreadMode.MAIN) fun onSiteChanged(event: OnSiteChanged) { - _uiState.value = uiState.value?.copy(isRegistrationProgressIndicatorVisible = false) if (event.isError) { AppLog.e( T.DOMAIN_REGISTRATION, "An error occurred while updating site details : " + event.error.message ) _showErrorMessage.value = event.error.message + _uiState.value = uiState.value?.copy(isRegistrationProgressIndicatorVisible = false) + _handleCompletedDomainRegistration.postValue(domainProductDetails.domainName) + return } - _handleCompletedDomainRegistration.postValue(domainProductDetails.domainName) + val updatedSite = siteStore.getSiteByLocalId(site.id) + + // New domain is not is not reflected in SiteModel yet, try refreshing a site until we get it + if (updatedSite.url.endsWith(".wordpress.com") && siteCheckTries < MAX_SITE_CHECK_TRIES) { + AppLog.v( + T.DOMAIN_REGISTRATION, + "Newly registered domain is still not reflected in site model. Refreshing site model..." + ) + launch { + delay(SITE_CHECK_DELAY_MS) + dispatcher.dispatch(SiteActionBuilder.newFetchSiteAction(site)) + siteCheckTries++ + } + } else { + // Everything looks good! Let's wait a bit before moving on + launch { + delay(SITE_CHECK_DELAY_MS) + _uiState.value = uiState.value?.copy(isRegistrationProgressIndicatorVisible = false) + _handleCompletedDomainRegistration.postValue(domainProductDetails.domainName) + } + } } fun onCountrySelectorClicked() { diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index a516a371459d..87a933ab61d8 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -2018,7 +2018,7 @@ - Plugin feature is not available for this site. + If you just registered a domain name, please wait until we finish setting it up and try again.\n\nIf not, looks like something went wrong and plugin feature might not be available for this site. Plugin feature requires a custom domain. Plugin feature requires a business plan. Plugin feature requires the site to be public. @@ -2063,6 +2063,8 @@ External link Toggle text + It takes longer then usual to refresh plugin details. Please check again later. + My Profile First name diff --git a/WordPress/src/test/java/org/wordpress/android/util/NoDelayCoroutineDispatcher.kt b/WordPress/src/test/java/org/wordpress/android/util/NoDelayCoroutineDispatcher.kt new file mode 100644 index 000000000000..150806b57d6b --- /dev/null +++ b/WordPress/src/test/java/org/wordpress/android/util/NoDelayCoroutineDispatcher.kt @@ -0,0 +1,19 @@ +package org.wordpress.android.util + +import kotlinx.coroutines.CancellableContinuation +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Delay +import kotlinx.coroutines.InternalCoroutinesApi +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.resume + +@UseExperimental(InternalCoroutinesApi::class) +class NoDelayCoroutineDispatcher : CoroutineDispatcher(), Delay { + override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) { + continuation.resume(Unit) + } + + override fun dispatch(context: CoroutineContext, block: Runnable) { + block.run() + } +} diff --git a/WordPress/src/test/java/org/wordpress/android/viewmodel/domains/DomainRegistrationDetailsViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/viewmodel/domains/DomainRegistrationDetailsViewModelTest.kt index 1a85cfd8f40d..5c5f4eded1d0 100644 --- a/WordPress/src/test/java/org/wordpress/android/viewmodel/domains/DomainRegistrationDetailsViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/viewmodel/domains/DomainRegistrationDetailsViewModelTest.kt @@ -3,6 +3,7 @@ package org.wordpress.android.viewmodel.domains import androidx.lifecycle.Observer import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.argWhere +import com.nhaarman.mockitokotlin2.doReturn import com.nhaarman.mockitokotlin2.times import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever @@ -29,9 +30,14 @@ import org.wordpress.android.fluxc.network.rest.wpcom.transactions.TransactionsR import org.wordpress.android.fluxc.store.AccountStore.DomainContactError import org.wordpress.android.fluxc.store.AccountStore.DomainContactErrorType import org.wordpress.android.fluxc.store.AccountStore.OnDomainContactFetched +import org.wordpress.android.fluxc.store.SiteStore +import org.wordpress.android.fluxc.store.SiteStore.DesignatePrimaryDomainError +import org.wordpress.android.fluxc.store.SiteStore.DesignatePrimaryDomainErrorType +import org.wordpress.android.fluxc.store.SiteStore.DesignatePrimaryDomainPayload import org.wordpress.android.fluxc.store.SiteStore.DomainSupportedStatesError import org.wordpress.android.fluxc.store.SiteStore.DomainSupportedStatesErrorType import org.wordpress.android.fluxc.store.SiteStore.OnDomainSupportedStatesFetched +import org.wordpress.android.fluxc.store.SiteStore.OnPrimaryDomainDesignated import org.wordpress.android.fluxc.store.SiteStore.OnSiteChanged import org.wordpress.android.fluxc.store.SiteStore.SiteError import org.wordpress.android.fluxc.store.SiteStore.SiteErrorType @@ -49,12 +55,14 @@ import org.wordpress.android.fluxc.store.TransactionsStore.RedeemShoppingCartPay import org.wordpress.android.fluxc.store.TransactionsStore.TransactionErrorType.PHONE import org.wordpress.android.test import org.wordpress.android.ui.domains.DomainProductDetails +import org.wordpress.android.util.NoDelayCoroutineDispatcher import org.wordpress.android.viewmodel.domains.DomainRegistrationDetailsViewModel.DomainRegistrationDetailsUiState class DomainRegistrationDetailsViewModelTest : BaseUnitTest() { - @Mock private lateinit var store: TransactionsStore + @Mock private lateinit var transactionsStore: TransactionsStore + @Mock private lateinit var siteStore: SiteStore @Mock private lateinit var dispatcher: Dispatcher - @Mock private lateinit var site: SiteModel + private var site: SiteModel = SiteModel() @Mock private lateinit var domainContactDetailsObserver: Observer @Mock private lateinit var countryPickerDialogObserver: Observer> @@ -98,6 +106,10 @@ class DomainRegistrationDetailsViewModelTest : BaseUnitTest() { private val shoppingCartCreateError = CreateShoppingCartError(GENERIC_ERROR, "Error Creating Cart") private val shoppingCartRedeemError = RedeemShoppingCartError(PHONE, "Wrong phone number") private val siteChangedError = SiteError(SiteErrorType.GENERIC_ERROR, "Error fetching site") + private val primaryDomainError = DesignatePrimaryDomainError( + DesignatePrimaryDomainErrorType.GENERIC_ERROR, + "Error designating primary domain" + ) private val domainContactInformationFetchError = DomainContactError( DomainContactErrorType.GENERIC_ERROR, "Error fetching domain contact information" @@ -122,8 +134,16 @@ class DomainRegistrationDetailsViewModelTest : BaseUnitTest() { @Before fun setUp() { site.siteId = siteId + site.url = testDomainName + + whenever(siteStore.getSiteByLocalId(any())).doReturn(site) - viewModel = DomainRegistrationDetailsViewModel(dispatcher, store) + viewModel = DomainRegistrationDetailsViewModel( + dispatcher, + transactionsStore, + siteStore, + NoDelayCoroutineDispatcher() + ) // Setting up chain of actions setupFetchSupportedCountriesDispatcher(false) setupFetchDomainContactInformationDispatcher(false) @@ -131,6 +151,7 @@ class DomainRegistrationDetailsViewModelTest : BaseUnitTest() { setupCreateShoppingCartDispatcher(false) setupRedeemShoppingCartDispatcher(false) setupFetchSiteDispatcher(false) + setupPrimaryDomainDispatcher(false) uiStateResults.clear() viewModel.uiState.observeForever { if (it != null) uiStateResults.add(it) } @@ -406,13 +427,14 @@ class DomainRegistrationDetailsViewModelTest : BaseUnitTest() { assertThat(viewModel.domainContactDetails.value?.state).isEqualTo(primaryState.code) val captor = ArgumentCaptor.forClass(Action::class.java) - verify(dispatcher, times(6)).dispatch(captor.capture()) + verify(dispatcher, times(7)).dispatch(captor.capture()) val actionsDispatched = captor.allValues validateCreateCartAction(actionsDispatched[3]) validateRedeemCartAction(actionsDispatched[4]) - validateFetchSiteAction(actionsDispatched[5]) + validateDesignatePrimaryDomainActions(actionsDispatched[5]) + validateFetchSiteAction(actionsDispatched[6]) assertThat(uiStateResults.size).isEqualTo(2) @@ -488,13 +510,14 @@ class DomainRegistrationDetailsViewModelTest : BaseUnitTest() { viewModel.onRegisterDomainButtonClicked() val captor = ArgumentCaptor.forClass(Action::class.java) - verify(dispatcher, times(6)).dispatch(captor.capture()) + verify(dispatcher, times(7)).dispatch(captor.capture()) val actionsDispatched = captor.allValues validateCreateCartAction(actionsDispatched[3]) validateRedeemCartAction(actionsDispatched[4]) - validateFetchSiteAction(actionsDispatched[5]) + validateDesignatePrimaryDomainActions(actionsDispatched[5]) + validateFetchSiteAction(actionsDispatched[6]) assertThat(uiStateResults.size).isEqualTo(2) @@ -618,6 +641,18 @@ class DomainRegistrationDetailsViewModelTest : BaseUnitTest() { } } + private fun setupPrimaryDomainDispatcher(isError: Boolean) { + val event = OnPrimaryDomainDesignated(site, isError) + if (isError) { + event.error = primaryDomainError + } + whenever(dispatcher.dispatch(argWhere> { + it.type == SiteAction.DESIGNATE_PRIMARY_DOMAIN + })).then { + viewModel.onPrimaryDomainDesignated(event) + } + } + private fun validateFetchSupportedCountriesAction(action: Action<*>) { assertThat(action.type).isEqualTo(FETCH_SUPPORTED_COUNTRIES) assertThat(action.payload).isNull() @@ -664,6 +699,16 @@ class DomainRegistrationDetailsViewModelTest : BaseUnitTest() { assertThat(fetchSitePayload).isEqualTo(site) } + private fun validateDesignatePrimaryDomainActions(action: Action<*>) { + assertThat(action.type).isEqualTo(SiteAction.DESIGNATE_PRIMARY_DOMAIN) + assertThat(action.payload).isNotNull + assertThat(action.payload).isInstanceOf(DesignatePrimaryDomainPayload::class.java) + + val designatePrimaryDomainPayload = action.payload as DesignatePrimaryDomainPayload + assertThat(designatePrimaryDomainPayload.site).isEqualTo(site) + assertThat(designatePrimaryDomainPayload.domain).isEqualTo(testDomainName) + } + private fun clearPreLoadUiStateResult() { uiStateResults.clear() } diff --git a/build.gradle b/build.gradle index 4ef8bfda5b8f..857c6d4550b3 100644 --- a/build.gradle +++ b/build.gradle @@ -105,5 +105,5 @@ buildScan { ext { daggerVersion = '2.22.1' - fluxCVersion = '5f057c5fbb8d585c22761a7874327e2889a3f6dd' + fluxCVersion = 'f07192bb7b8bf9aa7c5f8717988d9f17590a0db3' }