From e4b5a3ea4590287f904ea0295b74c4284ba25103 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Wed, 31 May 2017 17:58:17 -0400 Subject: [PATCH] Implement acceptance tests for some basic features --- build.sh | 4 +- uhabits-android/build.gradle | 1 + .../src/androidTest/AndroidManifest.xml | 27 +++ .../src/androidTest/assets/test.db | Bin 0 -> 49152 bytes .../org/isoron/uhabits/BaseAndroidTest.java | 2 + .../isoron/uhabits/BaseUIAutomatorTest.java | 32 +++- .../isoron/uhabits/acceptance/AboutTest.java | 46 +++++ .../isoron/uhabits/acceptance/HabitsTest.java | 107 ++++++++++++ .../isoron/uhabits/acceptance/LinksTest.java | 63 +++++++ .../uhabits/acceptance/steps/CommonSteps.java | 127 ++++++++++++++ .../acceptance/steps/EditHabitSteps.java | 64 +++++++ .../acceptance/steps/ListHabitsSteps.java | 161 ++++++++++++++++++ .../uhabits/integration/SavedStateTest.java | 3 + .../uhabits/performance/PerformanceTest.java | 0 .../org/isoron/uhabits/HabitsApplication.java | 4 +- .../uhabits/activities/about/AboutScreen.java | 8 +- .../preferences/AndroidPreferences.java | 7 + .../core/ui/screens/about/AboutBehavior.java | 2 +- .../ui/screens/about/AboutBehaviorTest.java | 94 ++++++++++ 19 files changed, 739 insertions(+), 13 deletions(-) create mode 100644 uhabits-android/src/androidTest/AndroidManifest.xml create mode 100644 uhabits-android/src/androidTest/assets/test.db create mode 100644 uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/AboutTest.java create mode 100644 uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/HabitsTest.java create mode 100644 uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/LinksTest.java create mode 100644 uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/CommonSteps.java create mode 100644 uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/EditHabitSteps.java create mode 100644 uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/ListHabitsSteps.java rename {app => uhabits-android}/src/androidTest/java/org/isoron/uhabits/performance/PerformanceTest.java (100%) create mode 100644 uhabits-core/src/test/java/org/isoron/uhabits/core/ui/screens/about/AboutBehaviorTest.java diff --git a/build.sh b/build.sh index 12df11b39..15d46f661 100755 --- a/build.sh +++ b/build.sh @@ -147,9 +147,9 @@ install_test_apk() { run_instrumented_tests() { log_info "Running instrumented tests" $ADB shell am instrument \ - -r -e coverage true \ + -r -e coverage true -e size medium \ -w ${PACKAGE_NAME}.test/android.support.test.runner.AndroidJUnitRunner \ - > ${OUTPUTS_DIR}/instrument.txt + | tee ${OUTPUTS_DIR}/instrument.txt mkdir -p ${OUTPUTS_DIR}/code-coverage/connected/ $ADB pull /data/user/0/${PACKAGE_NAME}/files/coverage.ec \ diff --git a/uhabits-android/build.gradle b/uhabits-android/build.gradle index 633568fab..0bf33ad84 100644 --- a/uhabits-android/build.gradle +++ b/uhabits-android/build.gradle @@ -94,6 +94,7 @@ dependencies { androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2' androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1' androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2' + androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2.2' androidTestCompile 'com.linkedin.testbutler:test-butler-library:1.3.1' implementation('com.opencsv:opencsv:3.9') { diff --git a/uhabits-android/src/androidTest/AndroidManifest.xml b/uhabits-android/src/androidTest/AndroidManifest.xml new file mode 100644 index 000000000..f5c89202e --- /dev/null +++ b/uhabits-android/src/androidTest/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + \ No newline at end of file diff --git a/uhabits-android/src/androidTest/assets/test.db b/uhabits-android/src/androidTest/assets/test.db new file mode 100644 index 0000000000000000000000000000000000000000..5319030635db9233852b113123678a8fbee0e49a GIT binary patch literal 49152 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 + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.acceptance; + +import org.isoron.uhabits.*; +import org.junit.*; + +import static org.isoron.uhabits.acceptance.steps.CommonSteps.*; +import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.*; +import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.*; + +public class AboutTest extends BaseUIAutomatorTest +{ + @Test + public void shouldDisplayAboutScreen() + { + launchApp(); + clickMenu(ABOUT); + verifyDisplaysText("Loop Habit Tracker"); + verifyDisplaysText("Rate this app on Google Play"); + verifyDisplaysText("Developers"); + verifyDisplaysText("Translators"); + + launchApp(); + clickMenu(SETTINGS); + clickText("About"); + verifyDisplaysText("Translators"); + } +} diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/HabitsTest.java b/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/HabitsTest.java new file mode 100644 index 000000000..bad651adf --- /dev/null +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/HabitsTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2017 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.acceptance; + +import org.isoron.uhabits.*; +import org.junit.*; + +import static org.isoron.uhabits.acceptance.steps.CommonSteps.*; +import static org.isoron.uhabits.acceptance.steps.EditHabitSteps.*; +import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.*; +import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.*; + +public class HabitsTest extends BaseUIAutomatorTest +{ + @Test + public void shouldCreateHabit() throws Exception + { + launchApp(); + clickMenu(CREATE_HABIT); + typeName("Hello world"); + typeQuestion("Did you say hello to the world today?"); + pickFrequency("Every week"); + pickColor(5); + clickSave(); + verifyDisplaysText("Hello world"); + } + + @Test + public void shouldShowHabitStatistics() throws Exception + { + launchApp(); + clickText("Track time"); + verifyDisplayGraphs(); + } + + @Test + public void shouldDeleteHabit() throws Exception + { + launchApp(); + longClickText("Track time"); + clickMenu(DELETE); + clickOK(); + verifyDoesNotDisplayText("Track time"); + } + + @Test + public void shouldEditHabit() throws Exception + { + launchApp(); + longClickText("Track time"); + clickMenu(EDIT_HABIT); + typeName("Take a walk"); + typeQuestion("Did you take a walk today?"); + clickSave(); + verifyDisplaysText("Take a walk"); + verifyDoesNotDisplayText("Track time"); + } + + @Test + public void shouldEditHabit_fromStatisticsScreen() throws Exception + { + launchApp(); + clickText("Track time"); + clickMenu(EDIT_HABIT); + typeName("Take a walk"); + typeQuestion("Did you take a walk today?"); + pickColor(10); + clickSave(); + verifyDisplaysText("Take a walk"); + pressBack(); + verifyDisplaysText("Take a walk"); + verifyDoesNotDisplayText("Track time"); + } + + @Test + public void shouldArchiveAndUnarchiveHabits() throws Exception + { + launchApp(); + longClickText("Track time"); + clickMenu(ARCHIVE); + verifyDoesNotDisplayText("Track time"); + clickMenu(HIDE_ARCHIVED); + verifyDisplaysText("Track time"); + + longClickText("Track time"); + clickMenu(UNARCHIVE); + clickMenu(HIDE_ARCHIVED); + verifyDisplaysText("Track time"); + } +} diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/LinksTest.java b/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/LinksTest.java new file mode 100644 index 000000000..ad788acf4 --- /dev/null +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/LinksTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2017 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.acceptance; + +import org.isoron.uhabits.*; +import org.junit.*; + +import static org.isoron.uhabits.acceptance.steps.CommonSteps.*; +import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.*; +import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.ABOUT; +import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.HELP; +import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.SETTINGS; + +public class LinksTest extends BaseUIAutomatorTest +{ + @Test + public void shouldLinkToSourceCode() throws Exception + { + launchApp(); + clickMenu(ABOUT); + clickText("View source code at GitHub"); + verifyOpensWebsite("https://github.com/iSoron/uhabits"); + } + + @Test + public void shouldLinkToTranslationWebsite() throws Exception + { + launchApp(); + clickMenu(ABOUT); + clickText("Help translate this app"); + verifyOpensWebsite("translate.loophabits.org"); + } + + @Test + public void shouldLinkToHelp() throws Exception + { + launchApp(); + clickMenu(HELP); + verifyOpensWebsite("loophabits.org/faq.html"); + + launchApp(); + clickMenu(SETTINGS); + clickText("Help & FAQ"); + verifyOpensWebsite("loophabits.org/faq.html"); + } +} diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/CommonSteps.java b/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/CommonSteps.java new file mode 100644 index 000000000..d41d6f5c0 --- /dev/null +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/CommonSteps.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2017 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.acceptance.steps; + +import android.support.annotation.*; +import android.support.test.espresso.*; +import android.support.test.espresso.contrib.*; +import android.support.test.uiautomator.*; +import android.support.v7.widget.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.activities.habits.list.*; + +import static android.support.test.espresso.Espresso.*; +import static android.support.test.espresso.action.ViewActions.*; +import static android.support.test.espresso.assertion.ViewAssertions.*; +import static android.support.test.espresso.matcher.ViewMatchers.*; +import static junit.framework.Assert.*; +import static org.hamcrest.CoreMatchers.*; + +public class CommonSteps extends BaseUIAutomatorTest +{ + public static void clickOK() + { + clickText("OK"); + } + + public static void pressBack() + { + device.pressBack(); + } + + public static void clickText(String text) + { + scrollToText(text); + onView(withText(text)).perform(click()); + } + + public static void clickText(@StringRes int id) + { + onView(withText(id)).perform(click()); + } + + public static void launchApp() + { + startActivity(ListHabitsActivity.class); + assertTrue( + device.wait(Until.hasObject(By.pkg("org.isoron.uhabits")), 5000)); + device.waitForIdle(); + } + + public static void longClickText(String text) + { + scrollToText(text); + onView(withText(text)).perform(longClick()); + } + + public static void scrollToText(String text) + { + try + { + if (device + .findObject(new UiSelector().className(RecyclerView.class)) + .exists()) + { + onView(instanceOf(RecyclerView.class)).perform( + RecyclerViewActions.scrollTo( + hasDescendant(withText(text)))); + } + else + { + onView(withText(text)).perform(scrollTo()); + } + } + catch (PerformException e) + { + //ignored + } + } + + public static void verifyDisplayGraphs() + { + verifyDisplaysView("HistoryCard"); + verifyDisplaysView("ScoreCard"); + } + + public static void verifyDisplaysText(String text) + { + scrollToText(text); + onView(withText(text)).check(matches(isEnabled())); + } + + private static void verifyDisplaysView(String className) + { + onView(withClassName(endsWith(className))).check(matches(isEnabled())); + } + + public static void verifyDoesNotDisplayText(String text) + { + onView(withText(text)).check(doesNotExist()); + } + + public static void verifyOpensWebsite(String url) throws Exception + { + assertTrue( + device.wait(Until.hasObject(By.pkg("com.android.chrome")), 5000)); + device.waitForIdle(); + assertTrue(device.findObject(new UiSelector().text(url)).exists()); + } +} diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/EditHabitSteps.java b/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/EditHabitSteps.java new file mode 100644 index 000000000..9b4774427 --- /dev/null +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/EditHabitSteps.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.acceptance.steps; + +import android.support.test.uiautomator.*; + +import org.isoron.uhabits.*; + +import static android.support.test.espresso.Espresso.*; +import static android.support.test.espresso.action.ViewActions.*; +import static android.support.test.espresso.matcher.ViewMatchers.*; +import static org.isoron.uhabits.BaseUIAutomatorTest.device; + +public class EditHabitSteps +{ + public static void clickSave() + { + onView(withId(R.id.buttonSave)).perform(click()); + } + + public static void pickFrequency(String freq) + { + onView(withId(R.id.spinner)).perform(click()); + device.findObject(By.text(freq)).click(); + } + + public static void pickColor(int color) + { + onView(withId(R.id.buttonPickColor)).perform(click()); + device.findObject(By.descStartsWith(String.format("Color %d", color))).click(); + } + + public static void typeName(String name) + { + typeTextWithId(R.id.tvName, name); + } + + public static void typeQuestion(String name) + { + typeTextWithId(R.id.tvDescription, name); + } + + private static void typeTextWithId(int id, String name) + { + onView(withId(id)).perform(clearText(), typeText(name)); + } +} diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/ListHabitsSteps.java b/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/ListHabitsSteps.java new file mode 100644 index 000000000..f3328d146 --- /dev/null +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/ListHabitsSteps.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2017 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.acceptance.steps; + +import android.support.test.espresso.*; +import android.support.test.uiautomator.*; +import android.view.*; + +import org.hamcrest.*; +import org.isoron.uhabits.R; +import org.isoron.uhabits.activities.habits.list.views.*; + +import java.util.*; + +import static android.support.test.InstrumentationRegistry.*; +import static android.support.test.espresso.Espresso.*; +import static android.support.test.espresso.action.ViewActions.*; +import static android.support.test.espresso.matcher.ViewMatchers.*; +import static org.hamcrest.CoreMatchers.*; +import static org.isoron.uhabits.BaseUIAutomatorTest.device; +import static org.isoron.uhabits.acceptance.steps.CommonSteps.clickText; + +public abstract class ListHabitsSteps +{ + public static void clickMenu(MenuItem item) + { + switch (item) + { + case ABOUT: + clickTextInsideOverflowMenu(R.string.about); + break; + + case HELP: + clickTextInsideOverflowMenu(R.string.help); + break; + + case SETTINGS: + clickTextInsideOverflowMenu(R.string.settings); + break; + + case CREATE_HABIT: + clickViewWithId(R.id.actionAdd); + break; + + case EDIT_HABIT: + clickViewWithId(R.id.action_edit_habit); + break; + + case DELETE: + clickTextInsideOverflowMenu(R.string.delete); + break; + + case ARCHIVE: + clickTextInsideOverflowMenu(R.string.archive); + break; + + case UNARCHIVE: + clickTextInsideOverflowMenu(R.string.unarchive); + break; + + case HIDE_ARCHIVED: + clickViewWithId(R.id.action_filter); + clickText(R.string.hide_archived); + break; + } + } + + private static void clickTextInsideOverflowMenu(int id) + { + UiObject toolbar = device.findObject( + new UiSelector().resourceId("org.isoron.uhabits:id/toolbar")); + if(toolbar.exists()) + { + onView(allOf(withContentDescription("More options"), withParent + (withParent(withId(R.id.toolbar))))).perform(click()); + } + else + { + openActionBarOverflowOrOptionsMenu(getTargetContext()); + } + + onView(withText(id)).perform(click()); + } + + private static void clickViewWithId(int id) + { + onView(withId(id)).perform(click()); + } + + private static ViewAction longClickEveryDescendantWithClass(Class cls) + { + return new ViewAction() + { + + @Override + public Matcher getConstraints() + { + return isEnabled(); + } + + @Override + public String getDescription() + { + return "perform on children"; + } + + @Override + public void perform(UiController uiController, View view) + { + LinkedList stack = new LinkedList<>(); + if (view instanceof ViewGroup) stack.push((ViewGroup) view); + + while (!stack.isEmpty()) + { + ViewGroup vg = stack.pop(); + for (int i = 0; i < vg.getChildCount(); i++) + { + View v = vg.getChildAt(i); + if (v instanceof ViewGroup) stack.push((ViewGroup) v); + if (cls.isInstance(v)) + { + v.performLongClick(); + uiController.loopMainThreadUntilIdle(); + } + } + } + } + }; + } + + public static void longPressCheckmarks(String habit) + { + CommonSteps.scrollToText(habit); + onView(allOf(hasDescendant(withText(habit)), + withClassName(endsWith("HabitCardView")))).perform( + longClickEveryDescendantWithClass(CheckmarkButtonView.class)); + } + + public enum MenuItem + { + ABOUT, HELP, SETTINGS, EDIT_HABIT, DELETE, ARCHIVE, HIDE_ARCHIVED, + UNARCHIVE, CREATE_HABIT + } +} diff --git a/uhabits-android/src/androidTest/java/org/isoron/uhabits/integration/SavedStateTest.java b/uhabits-android/src/androidTest/java/org/isoron/uhabits/integration/SavedStateTest.java index 7250c62bd..8bc086e2f 100644 --- a/uhabits-android/src/androidTest/java/org/isoron/uhabits/integration/SavedStateTest.java +++ b/uhabits-android/src/androidTest/java/org/isoron/uhabits/integration/SavedStateTest.java @@ -19,6 +19,8 @@ package org.isoron.uhabits.integration; +import android.support.test.filters.*; + import org.isoron.uhabits.*; import org.isoron.uhabits.activities.about.*; import org.isoron.uhabits.activities.habits.list.*; @@ -26,6 +28,7 @@ import org.junit.*; import static java.lang.Thread.*; +@LargeTest public class SavedStateTest extends BaseUIAutomatorTest { /** diff --git a/app/src/androidTest/java/org/isoron/uhabits/performance/PerformanceTest.java b/uhabits-android/src/androidTest/java/org/isoron/uhabits/performance/PerformanceTest.java similarity index 100% rename from app/src/androidTest/java/org/isoron/uhabits/performance/PerformanceTest.java rename to uhabits-android/src/androidTest/java/org/isoron/uhabits/performance/PerformanceTest.java diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplication.java b/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplication.java index caaf25957..91bfbd037 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplication.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplication.java @@ -85,8 +85,8 @@ public class HabitsApplication extends Application if (isTestMode()) { - File db = DatabaseUtils.getDatabaseFile(context); - if (db.exists()) db.delete(); +// File db = DatabaseUtils.getDatabaseFile(context); +// if (db.exists()) db.delete(); } try diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutScreen.java b/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutScreen.java index de7037dfc..c8c5808c4 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutScreen.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutScreen.java @@ -23,11 +23,13 @@ import android.support.annotation.*; import android.widget.*; import org.isoron.androidbase.activities.*; -import org.isoron.uhabits.intents.*; import org.isoron.uhabits.core.ui.screens.about.*; +import org.isoron.uhabits.intents.*; import javax.inject.*; +import static org.isoron.uhabits.core.ui.screens.about.AboutBehavior.Message.*; + public class AboutScreen extends BaseScreen implements AboutBehavior.Screen { @NonNull @@ -44,7 +46,9 @@ public class AboutScreen extends BaseScreen implements AboutBehavior.Screen @Override public void showMessage(AboutBehavior.Message message) { - Toast.makeText(activity, "OK", Toast.LENGTH_LONG).show(); + if (message == YOU_ARE_NOW_A_DEVELOPER) Toast + .makeText(activity, "You are now a developer", Toast.LENGTH_LONG) + .show(); } @Override diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/preferences/AndroidPreferences.java b/uhabits-android/src/main/java/org/isoron/uhabits/preferences/AndroidPreferences.java index 76aad2696..4e4641aec 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/preferences/AndroidPreferences.java +++ b/uhabits-android/src/main/java/org/isoron/uhabits/preferences/AndroidPreferences.java @@ -80,6 +80,13 @@ public class AndroidPreferences } } + public void reset() + { + if(!HabitsApplication.isTestMode()) throw new IllegalStateException( + "this method can only be used while testing"); + prefs.edit().clear().commit(); + } + public void setDefaultOrder(HabitList.Order order) { prefs.edit().putString("pref_default_order", order.name()).apply(); diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/about/AboutBehavior.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/about/AboutBehavior.java index 996084e40..6fc091fad 100644 --- a/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/about/AboutBehavior.java +++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/about/AboutBehavior.java @@ -42,7 +42,7 @@ public class AboutBehavior public void onPressDeveloperCountdown() { developerCountdown--; - if (developerCountdown <= 0) + if (developerCountdown == 0) { prefs.setDeveloper(true); screen.showMessage(Message.YOU_ARE_NOW_A_DEVELOPER); diff --git a/uhabits-core/src/test/java/org/isoron/uhabits/core/ui/screens/about/AboutBehaviorTest.java b/uhabits-core/src/test/java/org/isoron/uhabits/core/ui/screens/about/AboutBehaviorTest.java new file mode 100644 index 000000000..f39984abb --- /dev/null +++ b/uhabits-core/src/test/java/org/isoron/uhabits/core/ui/screens/about/AboutBehaviorTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2017 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.core.ui.screens.about; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.core.preferences.*; +import org.junit.*; +import org.mockito.*; + +import static org.isoron.uhabits.core.ui.screens.about.AboutBehavior.Message.YOU_ARE_NOW_A_DEVELOPER; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +public class AboutBehaviorTest extends BaseUnitTest +{ + private AboutBehavior behavior; + + @Mock + private Preferences prefs; + + @Mock + private AboutBehavior.Screen screen; + + @Before + public void setUp() + { + behavior = new AboutBehavior(prefs, screen); + } + + @Test + public void onPressDeveloperCountdown() throws Exception + { + behavior.onPressDeveloperCountdown(); + behavior.onPressDeveloperCountdown(); + behavior.onPressDeveloperCountdown(); + behavior.onPressDeveloperCountdown(); + verifyZeroInteractions(screen); + verifyZeroInteractions(prefs); + + behavior.onPressDeveloperCountdown(); + verify(screen).showMessage(YOU_ARE_NOW_A_DEVELOPER); + verify(prefs).setDeveloper(true); + + behavior.onPressDeveloperCountdown(); + verifyZeroInteractions(screen); + verifyZeroInteractions(prefs); + } + + @Test + public void onRateApp() throws Exception + { + behavior.onRateApp(); + verify(screen).showRateAppWebsite(); + } + + @Test + public void onSendFeedback() throws Exception + { + behavior.onSendFeedback(); + verify(screen).showSendFeedbackScreen(); + } + + @Test + public void onTranslateApp() throws Exception + { + behavior.onTranslateApp(); + verify(screen).showTranslationWebsite(); + } + + @Test + public void onViewSourceCode() throws Exception + { + behavior.onViewSourceCode(); + verify(screen).showSourceCodeWebsite(); + } + +} \ No newline at end of file