From 48f8a07cbf29052cb92ed4cfcb68fe01265b1457 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Sun, 5 Jan 2025 23:47:49 +0100 Subject: [PATCH 1/5] Improved tests to fail when label does not exist at all --- .../feature/formentry/AddRepeatTest.java | 11 ++-- .../resources/forms/repeat_without_label.xml | 56 ++++++++++++++++++- 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AddRepeatTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AddRepeatTest.java index 3ae7abe25ac..23c8e13875a 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AddRepeatTest.java +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AddRepeatTest.java @@ -121,15 +121,16 @@ public void whenInHierarchyForRepeatGroup_clickingPlus_addsRepeatAtEndOfSeries() } @Test - public void whenInRepeatWithoutLabel_swipingNext_andClickingAdd_addsAnotherRepeat() { + public void whenInRepeatWithoutLabel_swipingNext_displaysTheAddRepeatDialog() { rule.startAtMainMenu() .copyForm(REPEAT_WITHOUT_LABEL) .startBlankForm("Repeat without label") - .assertText("> 1") - .answerQuestion("First name", true, "Karan") + // group with no label .swipeToNextQuestionWithRepeatGroup("") - .clickOnAdd(new FormEntryPage("Repeat without label")) - .assertText("> 2"); + .clickOnDoNotAdd(new FormEntryPage("Repeat without label")) + // group with blank label + .swipeToNextQuestionWithRepeatGroup("") + .clickOnDoNotAdd(new FormEntryPage("Repeat without label")); } @Test diff --git a/test-forms/src/main/resources/forms/repeat_without_label.xml b/test-forms/src/main/resources/forms/repeat_without_label.xml index 473ceec7ec4..9f1fc675e16 100644 --- a/test-forms/src/main/resources/forms/repeat_without_label.xml +++ b/test-forms/src/main/resources/forms/repeat_without_label.xml @@ -1 +1,55 @@ -Repeat without label \ No newline at end of file + + + + Repeat without label + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 9575cdcd39894bb3524b38f7fd8a8c9004ad16e1 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Sun, 5 Jan 2025 23:48:20 +0100 Subject: [PATCH 2/5] Detect not existing labels --- .../odk/collect/android/formentry/repeats/AddRepeatDialog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collect_app/src/main/java/org/odk/collect/android/formentry/repeats/AddRepeatDialog.java b/collect_app/src/main/java/org/odk/collect/android/formentry/repeats/AddRepeatDialog.java index de7f421ff5d..2625ba974a6 100644 --- a/collect_app/src/main/java/org/odk/collect/android/formentry/repeats/AddRepeatDialog.java +++ b/collect_app/src/main/java/org/odk/collect/android/formentry/repeats/AddRepeatDialog.java @@ -25,7 +25,7 @@ public static void show(Context context, String groupLabel, Listener listener) { }; String dialogMessage; - if (groupLabel.isBlank()) { + if (groupLabel == null || groupLabel.isBlank()) { dialogMessage = context.getString(org.odk.collect.strings.R.string.add_another_question); } else { dialogMessage = context.getString(org.odk.collect.strings.R.string.add_repeat_question, From e58d42b76ebd361cb34b98d73dad67cdea4e8a39 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Mon, 6 Jan 2025 00:02:00 +0100 Subject: [PATCH 3/5] Converted AddRepeatDialog to kotlin --- .../formentry/repeats/AddRepeatDialog.java | 51 ------------------- .../formentry/repeats/AddRepeatDialog.kt | 50 ++++++++++++++++++ 2 files changed, 50 insertions(+), 51 deletions(-) delete mode 100644 collect_app/src/main/java/org/odk/collect/android/formentry/repeats/AddRepeatDialog.java create mode 100644 collect_app/src/main/java/org/odk/collect/android/formentry/repeats/AddRepeatDialog.kt diff --git a/collect_app/src/main/java/org/odk/collect/android/formentry/repeats/AddRepeatDialog.java b/collect_app/src/main/java/org/odk/collect/android/formentry/repeats/AddRepeatDialog.java deleted file mode 100644 index 2625ba974a6..00000000000 --- a/collect_app/src/main/java/org/odk/collect/android/formentry/repeats/AddRepeatDialog.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.odk.collect.android.formentry.repeats; - -import android.content.Context; -import android.content.DialogInterface; - -import androidx.appcompat.app.AlertDialog; - -import static android.content.DialogInterface.BUTTON_NEGATIVE; -import static android.content.DialogInterface.BUTTON_POSITIVE; - -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -public class AddRepeatDialog { - - private AddRepeatDialog() { - } - - public static void show(Context context, String groupLabel, Listener listener) { - AlertDialog alertDialog = new MaterialAlertDialogBuilder(context).create(); - DialogInterface.OnClickListener repeatListener = (dialog, i) -> { - switch (i) { - case BUTTON_POSITIVE -> listener.onAddRepeatClicked(); - case BUTTON_NEGATIVE -> listener.onCancelClicked(); - } - }; - - String dialogMessage; - if (groupLabel == null || groupLabel.isBlank()) { - dialogMessage = context.getString(org.odk.collect.strings.R.string.add_another_question); - } else { - dialogMessage = context.getString(org.odk.collect.strings.R.string.add_repeat_question, - groupLabel); - } - - alertDialog.setTitle(dialogMessage); - - alertDialog.setButton(BUTTON_POSITIVE, context.getString(org.odk.collect.strings.R.string.add_repeat), - repeatListener); - alertDialog.setButton(BUTTON_NEGATIVE, context.getString(org.odk.collect.strings.R.string.cancel), - repeatListener); - - alertDialog.setCancelable(false); - alertDialog.show(); - } - - public interface Listener { - void onAddRepeatClicked(); - - void onCancelClicked(); - } -} diff --git a/collect_app/src/main/java/org/odk/collect/android/formentry/repeats/AddRepeatDialog.kt b/collect_app/src/main/java/org/odk/collect/android/formentry/repeats/AddRepeatDialog.kt new file mode 100644 index 00000000000..d848eeed0d9 --- /dev/null +++ b/collect_app/src/main/java/org/odk/collect/android/formentry/repeats/AddRepeatDialog.kt @@ -0,0 +1,50 @@ +package org.odk.collect.android.formentry.repeats + +import android.content.Context +import android.content.DialogInterface +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.odk.collect.strings.R + +object AddRepeatDialog { + + @JvmStatic + fun show(context: Context, groupLabel: String?, listener: Listener) { + val alertDialog = MaterialAlertDialogBuilder(context).create() + + val repeatListener = + DialogInterface.OnClickListener { _: DialogInterface?, i: Int -> + when (i) { + DialogInterface.BUTTON_POSITIVE -> listener.onAddRepeatClicked() + DialogInterface.BUTTON_NEGATIVE -> listener.onCancelClicked() + } + } + + val dialogMessage = if (groupLabel.isNullOrBlank()) { + context.getString(R.string.add_another_question) + } else { + context.getString(R.string.add_repeat_question, groupLabel) + } + + alertDialog.setTitle(dialogMessage) + + alertDialog.setButton( + DialogInterface.BUTTON_POSITIVE, + context.getString(R.string.add_repeat), + repeatListener + ) + alertDialog.setButton( + DialogInterface.BUTTON_NEGATIVE, + context.getString(R.string.cancel), + repeatListener + ) + + alertDialog.setCancelable(false) + alertDialog.show() + } + + interface Listener { + fun onAddRepeatClicked() + + fun onCancelClicked() + } +} From c91e8394ae51e27281948bd8bd7f8694b9e447da Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Mon, 6 Jan 2025 00:13:35 +0100 Subject: [PATCH 4/5] Converted AddNewRepeatDialog to kotlin --- .../support/pages/AddNewRepeatDialog.java | 39 ------------------- .../support/pages/AddNewRepeatDialog.kt | 30 ++++++++++++++ 2 files changed, 30 insertions(+), 39 deletions(-) delete mode 100644 collect_app/src/androidTest/java/org/odk/collect/android/support/pages/AddNewRepeatDialog.java create mode 100644 collect_app/src/androidTest/java/org/odk/collect/android/support/pages/AddNewRepeatDialog.kt diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/support/pages/AddNewRepeatDialog.java b/collect_app/src/androidTest/java/org/odk/collect/android/support/pages/AddNewRepeatDialog.java deleted file mode 100644 index 144e42a7a91..00000000000 --- a/collect_app/src/androidTest/java/org/odk/collect/android/support/pages/AddNewRepeatDialog.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.odk.collect.android.support.pages; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.RootMatchers.isDialog; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withText; - -public class AddNewRepeatDialog extends Page { - - private final String repeatName; - - public AddNewRepeatDialog(String repeatName) { - this.repeatName = repeatName; - } - - @Override - public AddNewRepeatDialog assertOnPage() { - String dialogMessage; - if (repeatName.isBlank()) { - dialogMessage = getTranslatedString(org.odk.collect.strings.R.string.add_another_question); - } else { - dialogMessage = getTranslatedString(org.odk.collect.strings.R.string.add_repeat_question, repeatName); - } - onView(withText(dialogMessage)) - .inRoot(isDialog()) - .check(matches(isDisplayed())); - return this; - } - - public > D clickOnAdd(D destination) { - return clickOnTextInDialog(org.odk.collect.strings.R.string.add_repeat, destination); - } - - public > D clickOnDoNotAdd(D destination) { - return clickOnTextInDialog(org.odk.collect.strings.R.string.cancel, destination); - } - -} diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/support/pages/AddNewRepeatDialog.kt b/collect_app/src/androidTest/java/org/odk/collect/android/support/pages/AddNewRepeatDialog.kt new file mode 100644 index 00000000000..cab8d877031 --- /dev/null +++ b/collect_app/src/androidTest/java/org/odk/collect/android/support/pages/AddNewRepeatDialog.kt @@ -0,0 +1,30 @@ +package org.odk.collect.android.support.pages + +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 + +class AddNewRepeatDialog(private val repeatName: String?) : Page() { + override fun assertOnPage(): AddNewRepeatDialog { + val dialogMessage = if (repeatName.isNullOrBlank()) { + getTranslatedString(R.string.add_another_question) + } else { + getTranslatedString(R.string.add_repeat_question, repeatName) + } + onView(withText(dialogMessage)) + .inRoot(isDialog()) + .check(matches(isDisplayed())) + return this + } + + fun > clickOnAdd(destination: D): D { + return clickOnTextInDialog(R.string.add_repeat, destination) + } + + fun > clickOnDoNotAdd(destination: D): D { + return clickOnTextInDialog(R.string.cancel, destination) + } +} From f8ea5ca2e7dea9bf72d0c500a900e54abecb812d Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Sun, 12 Jan 2025 22:46:14 +0100 Subject: [PATCH 5/5] Converted AddRepeatTest to kotlin --- .../feature/formentry/AddRepeatTest.java | 150 ------------------ .../feature/formentry/AddRepeatTest.kt | 140 ++++++++++++++++ 2 files changed, 140 insertions(+), 150 deletions(-) delete mode 100644 collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AddRepeatTest.java create mode 100644 collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AddRepeatTest.kt diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AddRepeatTest.java b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AddRepeatTest.java deleted file mode 100644 index 23c8e13875a..00000000000 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AddRepeatTest.java +++ /dev/null @@ -1,150 +0,0 @@ -package org.odk.collect.android.feature.formentry; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; -import static androidx.test.espresso.matcher.ViewMatchers.withId; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.RuleChain; -import org.junit.runner.RunWith; -import org.odk.collect.android.R; -import org.odk.collect.android.support.pages.EndOfFormPage; -import org.odk.collect.android.support.pages.FormEndPage; -import org.odk.collect.android.support.pages.FormEntryPage; -import org.odk.collect.android.support.rules.CollectTestRule; -import org.odk.collect.android.support.rules.TestRuleChain; - -@RunWith(AndroidJUnit4.class) -public class AddRepeatTest { - - private static final String ONE_QUESTION_REPEAT = "one-question-repeat.xml"; - private static final String FIELD_LIST_REPEAT = "field-list-repeat.xml"; - private static final String FIXED_COUNT_REPEAT = "fixed-count-repeat.xml"; - private static final String REPEAT_WITHOUT_LABEL = "repeat_without_label.xml"; - - private final CollectTestRule rule = new CollectTestRule(); - - @Rule - public RuleChain copyFormChain = TestRuleChain.chain() - .around(rule); - - @Test - public void whenInRepeat_swipingNext_andClickingAdd_addsAnotherRepeat() { - rule.startAtMainMenu() - .copyForm(ONE_QUESTION_REPEAT) - .startBlankForm("One Question Repeat") - .assertText("Person > 1") - .swipeToNextQuestionWithRepeatGroup("Person") - .clickOnAdd(new FormEntryPage("One Question Repeat")) - .assertText("Person > 2"); - } - - @Test - public void whenInRepeat_swipingNext_andClickingDoNotAdd_leavesRepeatGroup() { - rule.startAtMainMenu() - .copyForm(ONE_QUESTION_REPEAT) - .startBlankForm("One Question Repeat") - .assertText("Person > 1") - .swipeToNextQuestionWithRepeatGroup("Person") - .clickOnDoNotAdd(new EndOfFormPage("One Question Repeat")); - } - - @Test - public void whenInRepeat_thatIsAFieldList_swipingNext_andClickingAdd_addsAnotherRepeat() { - rule.startAtMainMenu() - .copyForm(FIELD_LIST_REPEAT) - .startBlankForm("Field-List Repeat") - .assertText("Person > 1") - .assertText("What is their age?") - .assertText("What is their name?") - .swipeToNextQuestionWithRepeatGroup("Person") - .clickOnAdd(new FormEntryPage("Field-List Repeat")) - .assertText("Person > 2") - .assertText("What is their age?") - .assertText("What is their name?"); - } - - @Test - public void whenInRepeat_clickingPlus_andClickingAdd_addsRepeatToEndOfSeries() { - rule.startAtMainMenu() - .copyForm(ONE_QUESTION_REPEAT) - .startBlankForm("One Question Repeat") - .assertText("Person > 1") - .swipeToNextQuestionWithRepeatGroup("Person") - .clickOnAdd(new FormEntryPage("One Question Repeat")) - .swipeToPreviousQuestion("What is their age?") - .assertText("Person > 1") - .clickPlus("Person") - .clickOnAdd(new FormEntryPage("One Question Repeat")) - .assertText("Person > 3"); - } - - @Test - public void whenInARepeat_clickingPlus_andClickingDoNotAdd_returns() { - rule.startAtMainMenu() - .copyForm(ONE_QUESTION_REPEAT) - .startBlankForm("One Question Repeat") - .assertText("Person > 1") - .swipeToNextQuestionWithRepeatGroup("Person") - .clickOnAdd(new FormEntryPage("One Question Repeat")) - .swipeToPreviousQuestion("What is their age?") - .assertText("Person > 1") - .clickPlus("Person") - .clickOnDoNotAdd(new FormEntryPage("One Question Repeat")) - .assertText("Person > 1"); - } - - @Test - public void whenInRepeatWithFixedCount_noPlusButtonAppears() { - rule.startAtMainMenu() - .copyForm(FIXED_COUNT_REPEAT) - .startBlankForm("Fixed Count Repeat"); - - onView(withId(R.id.menu_add_repeat)).check(doesNotExist()); - } - - @Test - public void whenInHierarchyForRepeatGroup_clickingPlus_addsRepeatAtEndOfSeries() { - rule.startAtMainMenu() - .copyForm(ONE_QUESTION_REPEAT) - .startBlankForm("One Question Repeat") - .assertText("Person > 1") - .swipeToNextQuestionWithRepeatGroup("Person") - .clickOnAdd(new FormEntryPage("One Question Repeat")) - .clickGoToArrow() - .clickGoUpIcon() - .addGroup() - .assertText("Person > 3"); - } - - @Test - public void whenInRepeatWithoutLabel_swipingNext_displaysTheAddRepeatDialog() { - rule.startAtMainMenu() - .copyForm(REPEAT_WITHOUT_LABEL) - .startBlankForm("Repeat without label") - // group with no label - .swipeToNextQuestionWithRepeatGroup("") - .clickOnDoNotAdd(new FormEntryPage("Repeat without label")) - // group with blank label - .swipeToNextQuestionWithRepeatGroup("") - .clickOnDoNotAdd(new FormEntryPage("Repeat without label")); - } - - @Test - public void whenViewFormInHierarchyForRepeatGroup_noAddButtonAppears() { - rule.startAtMainMenu() - .copyForm(ONE_QUESTION_REPEAT) - .startBlankForm("One Question Repeat") - .swipeToNextQuestionWithRepeatGroup("Person") - .clickOnDoNotAdd(new FormEndPage("One Question Repeat")) - .clickFinalize() - - .clickSendFinalizedForm(1) - .clickOnForm("One Question Repeat") - .clickOnGroup("Person") - .assertNoId(R.id.menu_add_repeat); - } -} diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AddRepeatTest.kt b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AddRepeatTest.kt new file mode 100644 index 00000000000..17d32d99f78 --- /dev/null +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AddRepeatTest.kt @@ -0,0 +1,140 @@ +package org.odk.collect.android.feature.formentry + +import androidx.test.espresso.Espresso +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.runner.RunWith +import org.odk.collect.android.R +import org.odk.collect.android.support.pages.EndOfFormPage +import org.odk.collect.android.support.pages.FormEndPage +import org.odk.collect.android.support.pages.FormEntryPage +import org.odk.collect.android.support.rules.CollectTestRule +import org.odk.collect.android.support.rules.TestRuleChain.chain + +@RunWith(AndroidJUnit4::class) +class AddRepeatTest { + private val rule = CollectTestRule() + + @get:Rule + var copyFormChain: RuleChain = chain().around(rule) + + @Test + fun whenInRepeat_swipingNext_andClickingAdd_addsAnotherRepeat() { + rule.startAtMainMenu() + .copyForm("one-question-repeat.xml") + .startBlankForm("One Question Repeat") + .assertText("Person > 1") + .swipeToNextQuestionWithRepeatGroup("Person") + .clickOnAdd(FormEntryPage("One Question Repeat")) + .assertText("Person > 2") + } + + @Test + fun whenInRepeat_swipingNext_andClickingDoNotAdd_leavesRepeatGroup() { + rule.startAtMainMenu() + .copyForm("one-question-repeat.xml") + .startBlankForm("One Question Repeat") + .assertText("Person > 1") + .swipeToNextQuestionWithRepeatGroup("Person") + .clickOnDoNotAdd(EndOfFormPage("One Question Repeat")) + } + + @Test + fun whenInRepeat_thatIsAFieldList_swipingNext_andClickingAdd_addsAnotherRepeat() { + rule.startAtMainMenu() + .copyForm("field-list-repeat.xml") + .startBlankForm("Field-List Repeat") + .assertText("Person > 1") + .assertText("What is their age?") + .assertText("What is their name?") + .swipeToNextQuestionWithRepeatGroup("Person") + .clickOnAdd(FormEntryPage("Field-List Repeat")) + .assertText("Person > 2") + .assertText("What is their age?") + .assertText("What is their name?") + } + + @Test + fun whenInRepeat_clickingPlus_andClickingAdd_addsRepeatToEndOfSeries() { + rule.startAtMainMenu() + .copyForm("one-question-repeat.xml") + .startBlankForm("One Question Repeat") + .assertText("Person > 1") + .swipeToNextQuestionWithRepeatGroup("Person") + .clickOnAdd(FormEntryPage("One Question Repeat")) + .swipeToPreviousQuestion("What is their age?") + .assertText("Person > 1") + .clickPlus("Person") + .clickOnAdd(FormEntryPage("One Question Repeat")) + .assertText("Person > 3") + } + + @Test + fun whenInARepeat_clickingPlus_andClickingDoNotAdd_returns() { + rule.startAtMainMenu() + .copyForm("one-question-repeat.xml") + .startBlankForm("One Question Repeat") + .assertText("Person > 1") + .swipeToNextQuestionWithRepeatGroup("Person") + .clickOnAdd(FormEntryPage("One Question Repeat")) + .swipeToPreviousQuestion("What is their age?") + .assertText("Person > 1") + .clickPlus("Person") + .clickOnDoNotAdd(FormEntryPage("One Question Repeat")) + .assertText("Person > 1") + } + + @Test + fun whenInRepeatWithFixedCount_noPlusButtonAppears() { + rule.startAtMainMenu() + .copyForm("fixed-count-repeat.xml") + .startBlankForm("Fixed Count Repeat") + + Espresso.onView(ViewMatchers.withId(R.id.menu_add_repeat)) + .check(ViewAssertions.doesNotExist()) + } + + @Test + fun whenInHierarchyForRepeatGroup_clickingPlus_addsRepeatAtEndOfSeries() { + rule.startAtMainMenu() + .copyForm("one-question-repeat.xml") + .startBlankForm("One Question Repeat") + .assertText("Person > 1") + .swipeToNextQuestionWithRepeatGroup("Person") + .clickOnAdd(FormEntryPage("One Question Repeat")) + .clickGoToArrow() + .clickGoUpIcon() + .addGroup() + .assertText("Person > 3") + } + + @Test + fun whenInRepeatWithoutLabel_swipingNext_displaysTheAddRepeatDialog() { + rule.startAtMainMenu() + .copyForm("repeat_without_label.xml") + .startBlankForm("Repeat without label") // group with no label + .swipeToNextQuestionWithRepeatGroup("") + .clickOnDoNotAdd(FormEntryPage("Repeat without label")) // group with blank label + .swipeToNextQuestionWithRepeatGroup("") + .clickOnDoNotAdd(FormEntryPage("Repeat without label")) + } + + @Test + fun whenViewFormInHierarchyForRepeatGroup_noAddButtonAppears() { + rule.startAtMainMenu() + .copyForm("one-question-repeat.xml") + .startBlankForm("One Question Repeat") + .swipeToNextQuestionWithRepeatGroup("Person") + .clickOnDoNotAdd(FormEndPage("One Question Repeat")) + .clickFinalize() + + .clickSendFinalizedForm(1) + .clickOnForm("One Question Repeat") + .clickOnGroup("Person") + .assertNoId(R.id.menu_add_repeat) + } +}