Skip to content

Commit

Permalink
Merge pull request #5776 from seadowg/encrypted-bulk
Browse files Browse the repository at this point in the history
Improvements to bulk finalize
  • Loading branch information
lognaturel authored Oct 20, 2023
2 parents 17579c7 + f8dff30 commit 7b70b37
Show file tree
Hide file tree
Showing 24 changed files with 504 additions and 150 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ import android.widget.Button
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.lifecycle.Observer
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import org.odk.collect.androidshared.data.Consumable

/**
* Convenience wrapper around Android's [Snackbar] API.
Expand All @@ -40,6 +42,11 @@ object SnackbarUtils {
return showSnackbar(parentView, message, 3500, anchorView, action, displayDismissButton)
}

@JvmStatic
fun showLongSnackbar(parentView: View, snackbarDetails: SnackbarDetails) {
showLongSnackbar(parentView, snackbarDetails.text, action = snackbarDetails.action)
}

@JvmStatic
@JvmOverloads
fun showLongSnackbar(
Expand Down Expand Up @@ -74,7 +81,8 @@ object SnackbarUtils {

lastSnackbar?.dismiss()
lastSnackbar = Snackbar.make(parentView, message.trim(), duration).apply {
val textView = view.findViewById<TextView>(com.google.android.material.R.id.snackbar_text)
val textView =
view.findViewById<TextView>(com.google.android.material.R.id.snackbar_text)
textView.isSingleLine = false

if (anchorView?.visibility != View.GONE) {
Expand All @@ -88,7 +96,8 @@ object SnackbarUtils {
setOnClickListener {
dismiss()
}
contentDescription = context.getString(org.odk.collect.strings.R.string.close_snackbar)
contentDescription =
context.getString(org.odk.collect.strings.R.string.close_snackbar)
}

val params = LinearLayout.LayoutParams(
Expand Down Expand Up @@ -116,8 +125,23 @@ object SnackbarUtils {
lastSnackbar?.show()
}

data class Action(
data class SnackbarDetails @JvmOverloads constructor(
val text: String,
val listener: () -> Unit
val action: Action? = null
)

data class Action(val text: String, val listener: () -> Unit)

abstract class SnackbarPresenterObserver<T : Any?>(private val parentView: View) :
Observer<Consumable<T>> {

abstract fun getSnackbarDetails(value: T): SnackbarDetails

override fun onChanged(consumable: Consumable<T>) {
if (!consumable.isConsumed()) {
showLongSnackbar(parentView, getSnackbarDetails(consumable.value))
consumable.consume()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package org.odk.collect.android.feature.formmanagement

import androidx.test.ext.junit.runners.AndroidJUnit4
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith
import org.odk.collect.android.support.TestDependencies
import org.odk.collect.android.support.pages.AccessControlPage
import org.odk.collect.android.support.pages.EditSavedFormPage
import org.odk.collect.android.support.pages.FormEntryPage.QuestionAndAnswer
import org.odk.collect.android.support.pages.MainMenuPage
import org.odk.collect.android.support.pages.ProjectSettingsPage
import org.odk.collect.android.support.pages.SaveOrDiscardFormDialog
import org.odk.collect.android.support.rules.CollectTestRule
import org.odk.collect.android.support.rules.TestRuleChain
Expand All @@ -16,23 +22,24 @@ import org.odk.collect.strings.R.string
@RunWith(AndroidJUnit4::class)
class BulkFinalizationTest {

val rule = CollectTestRule()
val testDependencies = TestDependencies()
val rule = CollectTestRule(useDemoProject = false)

@get:Rule
val chain: RuleChain = TestRuleChain.chain().around(rule)
val chain: RuleChain = TestRuleChain.chain(testDependencies).around(rule)

@Test
fun canBulkFinalizeDrafts() {
rule.startAtMainMenu()
.copyForm("one-question.xml")
rule.withProject("http://example.com")
.copyForm("one-question.xml", "example.com")
.startBlankForm("One Question")
.fillOutAndSave(QuestionAndAnswer("what is your age", "97"))
.startBlankForm("One Question")
.fillOutAndSave(QuestionAndAnswer("what is your age", "98"))

.clickDrafts(2)
.clickOptionsIcon(string.finalize_all_forms)
.clickOnString(string.finalize_all_forms)
.clickFinalizeAll(2)
.clickFinalize()
.checkIsSnackbarWithQuantityDisplayed(plurals.bulk_finalize_success, 2)
.assertTextDoesNotExist("One Question")
.pressBack(MainMenuPage())
Expand All @@ -42,8 +49,8 @@ class BulkFinalizationTest {

@Test
fun whenThereAreDraftsWithConstraintViolations_marksFormsAsHavingErrors() {
rule.startAtMainMenu()
.copyForm("two-question-required.xml")
rule.withProject("http://example.com")
.copyForm("two-question-required.xml", "example.com")
.startBlankForm("Two Question Required")
.fillOut(QuestionAndAnswer("What is your name?", "Dan"))
.pressBack(SaveOrDiscardFormDialog(MainMenuPage()))
Expand All @@ -56,8 +63,8 @@ class BulkFinalizationTest {
)

.clickDrafts(2)
.clickOptionsIcon(string.finalize_all_forms)
.clickOnString(string.finalize_all_forms)
.clickFinalizeAll(2)
.clickFinalize()
.checkIsSnackbarWithMessageDisplayed(string.bulk_finalize_partial_success, 1, 1)
.assertText("Two Question Required")
.pressBack(MainMenuPage())
Expand All @@ -68,27 +75,28 @@ class BulkFinalizationTest {

@Test
fun whenADraftPreviouslyHadConstraintViolations_marksFormsAsHavingErrors() {
rule.startAtMainMenu()
.copyForm("two-question-required.xml")
rule.withProject("http://example.com")
.copyForm("two-question-required.xml", "example.com")
.startBlankForm("Two Question Required")
.fillOut(QuestionAndAnswer("What is your name?", "Dan"))
.pressBack(SaveOrDiscardFormDialog(MainMenuPage()))
.clickSaveChanges()

.clickDrafts(1)
.clickOptionsIcon(string.finalize_all_forms)
.clickOnString(string.finalize_all_forms)
.clickFinalizeAll(1)
.clickFinalize()
.checkIsSnackbarWithQuantityDisplayed(plurals.bulk_finalize_failure, 1)

.clickOptionsIcon(string.finalize_all_forms)
.clickOnString(string.finalize_all_forms)
.clickOnButtonInDialog(string.finalize, EditSavedFormPage(false))
.checkIsSnackbarWithQuantityDisplayed(plurals.bulk_finalize_failure, 1)
}

@Test
fun doesNotFinalizeInstancesWithSavePoints() {
rule.startAtMainMenu()
.copyForm("one-question.xml")
rule.withProject("http://example.com")
.copyForm("one-question.xml", "example.com")
.startBlankForm("One Question")
.swipeToEndScreen()
.clickSaveAsDraft()
Expand All @@ -108,22 +116,98 @@ class BulkFinalizationTest {
.assertNumberOfFinalizedForms(0)
}

@Test
fun doesNotFinalizeInstancesFromEncryptedForms() {
rule.withProject("http://example.com")
.copyForm("encrypted.xml", "example.com")
.startBlankForm("encrypted")
.swipeToEndScreen()
.clickSaveAsDraft()

.clickDrafts(1)
.clickFinalizeAll(1)
.clickFinalize()
.checkIsSnackbarWithMessageDisplayed(string.bulk_finalize_unsupported, 0)
.assertText("encrypted")
.pressBack(MainMenuPage())

.assertNumberOfEditableForms(1)
.assertNumberOfFinalizedForms(0)
}

@Test
fun doesNotFinalizeAlreadyFinalizedInstances() {
rule.startAtMainMenu()
.copyForm("one-question.xml")
rule.withProject("http://example.com")
.copyForm("one-question.xml", "example.com")
.startBlankForm("One Question")
.fillOutAndSave(QuestionAndAnswer("what is your age", "97"))
.startBlankForm("One Question")
.fillOutAndFinalize(QuestionAndAnswer("what is your age", "98"))

.clickDrafts(1)
.clickOptionsIcon(string.finalize_all_forms)
.clickOnString(string.finalize_all_forms)
.clickFinalizeAll(1)
.clickFinalize()
.checkIsSnackbarWithQuantityDisplayed(plurals.bulk_finalize_success, 1)
.assertTextDoesNotExist("One Question")
.pressBack(MainMenuPage())

.assertNumberOfFinalizedForms(2)
}

@Test
fun whenAutoSendIsEnabled_draftsAreSentAfterFinalizing() {
val mainMenuPage = rule.withProject(testDependencies.server.url)
.enableAutoSend()

.copyForm("one-question.xml", testDependencies.server.hostName)
.startBlankForm("One Question")
.fillOutAndSave(QuestionAndAnswer("what is your age", "97"))

.clickDrafts(1)
.clickFinalizeAll(1)
.clickFinalize()
.pressBack(MainMenuPage())

testDependencies.scheduler.runDeferredTasks()

mainMenuPage.clickViewSentForm(1)
.assertText("One Question")

assertThat(testDependencies.server.submissions.size, equalTo(1))
}

@Test
fun canCancel() {
rule.withProject("http://example.com")
.copyForm("one-question.xml", "example.com")
.startBlankForm("One Question")
.fillOutAndSave(QuestionAndAnswer("what is your age", "97"))

.clickDrafts(1)
.clickFinalizeAll(1)
.clickCancel()
.assertText("One Question")
.pressBack(MainMenuPage())

.assertNumberOfEditableForms(1)
}

@Test
fun canBeDisabled() {
rule.withProject("http://example.com")
.openProjectSettingsDialog()
.clickSettings()
.clickAccessControl()
.clickFormEntrySettings()
.clickOnString(string.finalize_all_forms)
.pressBack(AccessControlPage())
.pressBack(ProjectSettingsPage())
.pressBack(MainMenuPage())

.copyForm("one-question.xml", "example.com")
.startBlankForm("One Question")
.fillOutAndSave(QuestionAndAnswer("what is your age", "1892"))
.clickDrafts()
.assertNoOptionsMenu()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.odk.collect.android.support.pages

import android.app.Application
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.RootMatchers.isDialog
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withText
import org.odk.collect.strings.R
import org.odk.collect.strings.R.plurals
import org.odk.collect.strings.localization.getLocalizedQuantityString

class BulkFinalizationConfirmationDialogPage(private val count: Int) : Page<BulkFinalizationConfirmationDialogPage>() {
override fun assertOnPage(): BulkFinalizationConfirmationDialogPage {
val title = ApplicationProvider.getApplicationContext<Application>()
.getLocalizedQuantityString(plurals.bulk_finalize_confirmation, count, count)

onView(withText(title)).inRoot(isDialog()).check(matches(isDisplayed()))
return this
}

fun clickFinalize(): EditSavedFormPage {
return this.clickOnButtonInDialog(R.string.finalize, EditSavedFormPage(false))
}

fun clickCancel(): EditSavedFormPage {
return this.clickOnButtonInDialog(R.string.cancel, EditSavedFormPage(false))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@

package org.odk.collect.android.support.pages;

import android.widget.RelativeLayout;

import androidx.appcompat.widget.Toolbar;

import org.odk.collect.android.R;
import org.odk.collect.android.adapters.InstanceListCursorAdapter;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.replaceText;
Expand All @@ -37,6 +30,13 @@
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.Matchers.not;

import android.widget.RelativeLayout;

import androidx.appcompat.widget.Toolbar;

import org.odk.collect.android.R;
import org.odk.collect.android.adapters.InstanceListCursorAdapter;

public class EditSavedFormPage extends Page<EditSavedFormPage> {
private final boolean firstOpen;

Expand Down Expand Up @@ -106,4 +106,11 @@ public EditSavedFormPage searchInBar(String query) {
onView(withId(androidx.appcompat.R.id.search_src_text)).perform(replaceText(query));
return this;
}

public BulkFinalizationConfirmationDialogPage clickFinalizeAll(int count) {
this.clickOptionsIcon(org.odk.collect.strings.R.string.finalize_all_forms)
.clickOnString(org.odk.collect.strings.R.string.finalize_all_forms);

return new BulkFinalizationConfirmationDialogPage(count).assertOnPage();
}
}
Loading

0 comments on commit 7b70b37

Please sign in to comment.