Skip to content
This repository has been archived by the owner on Apr 26, 2020. It is now read-only.

Commit

Permalink
Fixes for Beta 5 (#235)
Browse files Browse the repository at this point in the history
* Fixed crash when selecting target for standard round
* Refactored MultiSelector and SingleSelector to share a common base class and respect LSP.
* Added UI test
* Fixed round delete bug
* Added additional info on settings crash
  • Loading branch information
DreierF authored Jan 25, 2017
1 parent 8065398 commit e21a58b
Show file tree
Hide file tree
Showing 32 changed files with 528 additions and 171 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,21 @@
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.replaceText;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.contrib.RecyclerViewActions.actionOnItem;
import static android.support.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withParent;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static de.dreier.mytargets.features.training.edit.EditTrainingFragment.CREATE_FREE_TRAINING_ACTION;
import static de.dreier.mytargets.features.training.edit.EditTrainingFragment.CREATE_TRAINING_WITH_STANDARD_ROUND_ACTION;
import static de.dreier.mytargets.shared.models.Dimension.Unit.CENTIMETER;
import static de.dreier.mytargets.shared.models.Dimension.Unit.METER;
import static de.dreier.mytargets.test.utils.PermissionGranter.allowPermissionsIfNeeded;
import static de.dreier.mytargets.test.utils.assertions.RecyclerViewAssertions.itemCount;
import static de.dreier.mytargets.test.utils.matchers.MatcherUtils.containsStringRes;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsString;

Expand All @@ -64,6 +70,7 @@ public class EditTrainingActivityTest extends UITestBase {

@Before
public void setUp() {
SettingsManager.setStandardRound(93L);
SettingsManager.setTarget(new Target(WAFull.ID, 0, new Dimension(122, CENTIMETER)));
SettingsManager.setDistance(new Dimension(50, METER));
SettingsManager.setIndoor(false);
Expand All @@ -74,7 +81,7 @@ public void setUp() {
}

@Test
public void editTrainingActivityTest() {
public void createFreeTraining() {
Intent intent = new Intent();
intent.setAction(CREATE_FREE_TRAINING_ACTION);
activityTestRule.launchActivity(intent);
Expand Down Expand Up @@ -135,8 +142,49 @@ public void editTrainingActivityTest() {
.format(new LocalDate(2016, 8, 10).toDate());
onView(withId(R.id.trainingDate)).check(matches(withText(formattedDate)));

onView(withId(R.id.action_save)).perform(click());
clickActionBarItem(R.id.action_save, R.string.save);
pressBack();
pressBack();
}

@Test
public void createTrainingWithStandardRound() {
Intent intent = new Intent();
intent.setAction(CREATE_TRAINING_WITH_STANDARD_ROUND_ACTION);
activityTestRule.launchActivity(intent);

allowPermissionsIfNeeded(activityTestRule.getActivity(), ACCESS_FINE_LOCATION);

// Has last used standard round been restored
onView(withId(R.id.standardRound))
.check(matches(hasDescendant(withText(R.string.warwick))));

// Change standard round
onView(withId(R.id.standardRound)).perform(nestedScrollTo(), click());
onView(withId(R.id.recyclerView))
.perform(actionOnItem(hasDescendant(withText(R.string.wa_standard)), click()),
actionOnItem(hasDescendant(withText(R.string.wa_standard)), click()));
onView(withId(R.id.standardRound))
.check(matches(hasDescendant(withText(R.string.wa_standard))));

onView(withText(R.string.change_target_face)).perform(nestedScrollTo(), click());

onView(withId(R.id.recyclerView))
.check(matches(hasDescendant(withText(R.string.wa_full))))
.check(matches(hasDescendant(withText(R.string.wa_3_ring))))
.check(itemCount(is(5)));

onView(allOf(withId(R.id.recyclerView), isDisplayed()))
.perform(actionOnItemAtPosition(4, click()));
navigateUp();

clickActionBarItem(R.id.action_save, R.string.save);

navigateUp();
navigateUp();

onView(withId(R.id.detail_round_info))
.check(matches(allOf(containsStringRes(R.string.wa_standard),
containsStringRes(R.string.wa_3_ring))));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright (C) 2017 Florian Dreier
*
* This file is part of MyTargets.
*
* MyTargets is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* MyTargets 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.
*/

package de.dreier.mytargets.features.training.standardround;


import android.content.Intent;
import android.support.test.espresso.intent.rule.IntentsTestRule;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import de.dreier.mytargets.R;
import de.dreier.mytargets.features.settings.SettingsManager;
import de.dreier.mytargets.features.training.edit.EditTrainingActivity;
import de.dreier.mytargets.shared.models.Dimension;
import de.dreier.mytargets.shared.models.Target;
import de.dreier.mytargets.shared.targets.models.WAFull;
import de.dreier.mytargets.test.base.UITestBase;

import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.scrollTo;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.contrib.RecyclerViewActions.actionOnItem;
import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static de.dreier.mytargets.features.training.edit.EditTrainingFragment.CREATE_TRAINING_WITH_STANDARD_ROUND_ACTION;
import static de.dreier.mytargets.shared.models.Dimension.Unit.CENTIMETER;
import static de.dreier.mytargets.shared.models.Dimension.Unit.METER;
import static de.dreier.mytargets.test.utils.PermissionGranter.allowPermissionsIfNeeded;
import static de.dreier.mytargets.test.utils.matchers.MatcherUtils.withRecyclerView;
import static de.dreier.mytargets.test.utils.matchers.ParentViewMatcher.isNestedChildOfView;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.startsWith;

@RunWith(AndroidJUnit4.class)
public class EditStandardRoundActivityTest extends UITestBase {

@Rule
public IntentsTestRule<EditTrainingActivity> activityTestRule = new IntentsTestRule<>(
EditTrainingActivity.class, true, false);

@Before
public void setUp() {
SettingsManager.setTarget(new Target(WAFull.ID, 0, new Dimension(122, CENTIMETER)));
SettingsManager.setDistance(new Dimension(50, METER));
SettingsManager.setTimerEnabled(false);
SettingsManager.setShotsPerEnd(3);
SettingsManager.setEndCount(10);
SettingsManager.setDistance(new Dimension(10, METER));
}

@Test
public void editStandardRoundActivity() {
Intent intent = new Intent();
intent.setAction(CREATE_TRAINING_WITH_STANDARD_ROUND_ACTION);
activityTestRule.launchActivity(intent);

allowPermissionsIfNeeded(activityTestRule.getActivity(), ACCESS_FINE_LOCATION);

onView(withId(R.id.standardRound)).perform(scrollTo(), click());

onView(withId(R.id.fab)).perform(click());

onView(withId(R.id.distance)).perform(nestedScrollTo(), click());
onView(allOf(withId(R.id.recyclerView), isDisplayed()))
.perform(actionOnItem(hasDescendant(withText("20m")), click()));

onView(withId(R.id.target)).perform(nestedScrollTo(), click());
onView(withId(R.id.recyclerView))
.perform(actionOnItem(hasDescendant(withText(R.string.wa_danage_6_spot)), click()));
navigateUp();

onView(withId(R.id.addButton)).perform(nestedScrollTo(), click());

onView(withRecyclerView(R.id.rounds).atPositionOnView(1, R.id.distance))
.perform(nestedScrollTo(), click());
onView(allOf(withId(R.id.recyclerView), isDisplayed()))
.perform(actionOnItem(hasDescendant(withText("15m")), click()));

onView(allOf(withId(R.id.number_increment), isNestedChildOfView(withId(R.id.shotCount)),
isNestedChildOfView(withRecyclerView(R.id.rounds).atPosition(1))))
.perform(nestedScrollTo(), click(), click(), click());

onView(allOf(withId(R.id.number_decrement), isNestedChildOfView(withId(R.id.endCount)),
isNestedChildOfView(withRecyclerView(R.id.rounds).atPosition(1))))
.perform(nestedScrollTo(), click(), click(), click(), click(), click());

clickActionBarItem(R.id.action_save, R.string.save);

onView(withId(R.id.standardRound))
.check(matches(hasDescendant(withText(R.string.custom_round))));

onView(withId(R.id.standardRound))
.check(matches(hasDescendant(withText(
allOf(startsWith("20m: 10 × 3"), containsString("15m: 5 × 6"))))));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2017 Florian Dreier
*
* This file is part of MyTargets.
*
* MyTargets is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* MyTargets 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.
*/

package de.dreier.mytargets.test.utils.assertions;

import android.support.test.espresso.ViewAssertion;
import android.support.v7.widget.RecyclerView;

import org.hamcrest.Matcher;

import static android.support.test.espresso.matcher.ViewMatchers.assertThat;

public class RecyclerViewAssertions {
public static ViewAssertion itemCount(Matcher<Integer> matcher) {
return (view, noViewFoundException) -> {
if (noViewFoundException != null) {
throw noViewFoundException;
}

RecyclerView recyclerView = (RecyclerView) view;
RecyclerView.Adapter adapter = recyclerView.getAdapter();
assertThat(adapter.getItemCount(), matcher);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@

package de.dreier.mytargets.test.utils.matchers;

import android.content.res.Resources;
import android.support.test.espresso.ViewInteraction;
import android.support.test.espresso.assertion.ViewAssertions;
import android.support.test.espresso.matcher.BoundedMatcher;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
Expand Down Expand Up @@ -89,4 +91,49 @@ public static View getMatchingParent(View view, Matcher<View> matcher) {
}
return null;
}

/**
* Returns a matcher that matches a descendant of {@link TextView} that is displaying the string
* associated with the given resource id.
*
* @param resourceId the string resource the text view is expected to hold.
*/
public static Matcher<View> containsStringRes(final int resourceId) {
return new BoundedMatcher<View, TextView>(TextView.class) {
private String resourceName = null;
private String expectedText = null;

@Override
public void describeTo(Description description) {
description.appendText("contains string from resource id: ");
description.appendValue(resourceId);
if (resourceName != null) {
description.appendText("[");
description.appendText(resourceName);
description.appendText("]");
}
if (expectedText != null) {
description.appendText(" value: ");
description.appendText(expectedText);
}
}

@Override
public boolean matchesSafely(TextView textView) {
if (expectedText == null) {
try {
expectedText = textView.getResources().getString(resourceId);
resourceName = textView.getResources().getResourceEntryName(resourceId);
} catch (Resources.NotFoundException ignored) {
/* view could be from a context unaware of the resource id. */
}
}
CharSequence actualText = textView.getText();
// FYI: actualText may not be string ... its just a char sequence convert to string.
return expectedText != null && actualText != null &&
actualText.toString().contains(expectedText);
}
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2017 Florian Dreier
*
* This file is part of MyTargets.
*
* MyTargets is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* MyTargets 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.
*/

package de.dreier.mytargets.test.utils.matchers;

import android.view.View;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;

public class ParentViewMatcher {
public static Matcher<View> isNestedChildOfView(Matcher<View> parentViewMatcher) {
return new TypeSafeMatcher<View>() {
public void describeTo(Description description) {
description.appendText("is nested child of view ");
parentViewMatcher.describeTo(description);
}

public boolean matchesSafely(View view) {
return MatcherUtils.getMatchingParent(view, parentViewMatcher) != null;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private void checkFlags(int setFlags) {
}

/**
* check if the view is due for rebiding
* Check if the view is due for rebinding
*
* @param flag
* @return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
package de.dreier.mytargets.base.fragments;

import android.support.annotation.PluralsRes;
import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode;
Expand Down Expand Up @@ -50,12 +49,6 @@ public abstract class EditableListFragmentBase<T extends IIdSettable & Model,
@State(MultiSelectorBundler.class)
protected MultiSelector selector = new MultiSelector();

/**
* Resource describing FAB action
*/
@StringRes
protected int newStringRes;

/**
* Resource used to set title when items are selected.
*/
Expand Down Expand Up @@ -182,12 +175,12 @@ private void updateTitle() {
}

@Override
public void onClick(SelectableViewHolder<T> holder, T mItem) {
if (mItem == null) {
public void onClick(SelectableViewHolder<T> holder, T item) {
if (item == null) {
return;
}
if (!selector.tapSelection(holder)) {
onSelected(mItem);
onSelected(item);
} else {
updateTitle();
}
Expand Down
Loading

0 comments on commit e21a58b

Please sign in to comment.