/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.apps.common.testing.ui.espresso.assertion; import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.assertThat; import static com.google.android.apps.common.testing.ui.espresso.util.TreeIterables.breadthFirstViewTraversal; import static com.google.common.base.Preconditions.checkNotNull; import static org.hamcrest.Matchers.is; import com.google.android.apps.common.testing.ui.espresso.NoMatchingViewException; import com.google.android.apps.common.testing.ui.espresso.ViewAssertion; import com.google.android.apps.common.testing.ui.espresso.util.HumanReadables; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import android.util.Log; import android.view.View; import junit.framework.AssertionFailedError; import org.hamcrest.Matcher; import org.hamcrest.StringDescription; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * A collection of common {@link ViewAssertion}s. */ public final class ViewAssertions { private static final String TAG = ViewAssertions.class.getSimpleName(); private ViewAssertions() {} /** * Returns an assert that ensures the view matcher does not find any matching view in the * hierarchy. */ public static ViewAssertion doesNotExist() { return new ViewAssertion() { @Override public void check(View view, NoMatchingViewException noView) { if (view != null) { assertThat("View is present in the hierarchy: " + HumanReadables.describe(view), true, is(false)); } } }; } /** * Returns a generic {@link ViewAssertion} that asserts that a view exists in the view hierarchy * and is matched by the given view matcher. */ public static ViewAssertion matches(final Matcher viewMatcher) { checkNotNull(viewMatcher); return new ViewAssertion() { @Override public void check(View view, NoMatchingViewException noViewException) { StringDescription description = new StringDescription(); description.appendText("'"); viewMatcher.describeTo(description); if (noViewException != null) { description.appendText(String.format( "' check could not be performed because view '%s' was not found.\n", viewMatcher)); Log.e(TAG, description.toString()); throw noViewException; } else { // TODO(valeraz): ideally, we should append the matcher used to find the view // This can be done in DefaultFailureHandler (just like we currently to with // PerformException) description.appendText("' doesn't match the selected view."); assertThat(description.toString(), view, viewMatcher); } } }; } /** * Returns a generic {@link ViewAssertion} that asserts that the descendant views selected by the * selector match the specified matcher. * * Example: onView(rootView).check(selectedDescendantsMatch( * not(isAssignableFrom(TextView.class)), hasContentDescription())); */ public static ViewAssertion selectedDescendantsMatch( final Matcher selector, final Matcher matcher) { return new ViewAssertion() { @SuppressWarnings("unchecked") @Override public void check(View view, NoMatchingViewException noViewException) { Preconditions.checkNotNull(view); final Predicate viewPredicate = new Predicate() { @Override public boolean apply(View input) { return selector.matches(input); } }; Iterator selectedViewIterator = Iterables.filter(breadthFirstViewTraversal(view), viewPredicate).iterator(); List nonMatchingViews = new ArrayList(); while (selectedViewIterator.hasNext()) { View selectedView = selectedViewIterator.next(); if (!matcher.matches(selectedView)) { nonMatchingViews.add(selectedView); } } if (nonMatchingViews.size() > 0) { String errorMessage = HumanReadables.getViewHierarchyErrorMessage(view, nonMatchingViews, String.format("At least one view did not match the required matcher: %s", matcher), "****DOES NOT MATCH****"); throw new AssertionFailedError(errorMessage); } } }; } }