File size: 6,337 Bytes
f871013
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package com.b44t.messenger;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.test.espresso.NoMatchingViewException;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.util.TreeIterables;
import androidx.test.ext.junit.rules.ActivityScenarioRule;

import org.hamcrest.Matcher;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.connect.AccountManager;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.util.AccessibilityUtil;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.Util;

public class TestUtils {
  private static int createdAccountId = 0;
  private static boolean resetEnterSends = false;

  public static void cleanupCreatedAccount(Context context) {
    DcAccounts accounts = DcHelper.getAccounts(context);
    if (createdAccountId != 0) {
      accounts.removeAccount(createdAccountId);
      createdAccountId = 0;
    }
  }

  public static void cleanup() {
    Context context = getInstrumentation().getTargetContext();
    cleanupCreatedAccount(context);
    if (resetEnterSends) {
      Prefs.setEnterSendsEnabled(getInstrumentation().getTargetContext(), false);
    }
  }

  public static void createOfflineAccount() {
    Context context = getInstrumentation().getTargetContext();
    cleanupCreatedAccount(context);
    createdAccountId = AccountManager.getInstance().beginAccountCreation(context);
    DcContext c = DcHelper.getContext(context);
    c.setConfig("configured_addr", "alice@example.org");
    c.setConfig("configured_mail_pw", "abcd");
    c.setConfig("configured", "1");
  }

  @NonNull
  public static ActivityScenarioRule<ConversationListActivity> getOfflineActivityRule(boolean useExistingChats) {
    Intent intent =
            Intent.makeMainActivity(
                    new ComponentName(getInstrumentation().getTargetContext(), ConversationListActivity.class));
    if (!useExistingChats) {
      createOfflineAccount();
    }
    prepare();
    return new ActivityScenarioRule<>(intent);
  }

  @NonNull
  public static <T extends Activity> ActivityScenarioRule<T> getOnlineActivityRule(Class<T> activityClass) {
    Context context = getInstrumentation().getTargetContext();
    AccountManager.getInstance().beginAccountCreation(context);
    prepare();
    return new ActivityScenarioRule<>(new Intent(getInstrumentation().getTargetContext(), activityClass));
  }

  private static void prepare() {
    Prefs.setBooleanPreference(getInstrumentation().getTargetContext(), Prefs.DOZE_ASKED_DIRECTLY, true);
    if (!AccessibilityUtil.areAnimationsDisabled(getInstrumentation().getTargetContext())) {
      throw new RuntimeException("To run the tests, disable animations at Developer options' " +
              "-> 'Window/Transition/Animator animation scale' -> Set all 3 to 'off'");
    }
  }

  /**
   * Perform action of waiting for a certain view within a single root view
   *
   * @param matcher Generic Matcher used to find our view
   */
  private static ViewAction searchFor(Matcher<View> matcher) {
    return new ViewAction() {

      public Matcher<View> getConstraints() {
        return isRoot();
      }

      public String getDescription() {
        return "searching for view $matcher in the root view";
      }

      public void perform(UiController uiController, View view) {

        Iterable<View> childViews = TreeIterables.breadthFirstViewTraversal(view);

        // Look for the match in the tree of childviews
        for (View it : childViews) {
          if (matcher.matches(it)) {
            // found the view
            return;
          }
        }

        throw new NoMatchingViewException.Builder()
                .withRootView(view)
                .withViewMatcher(matcher)
                .build();
      }
    };
  }

  /**
   * Perform action of implicitly waiting for a certain view.
   * This differs from EspressoExtensions.searchFor in that,
   * upon failure to locate an element, it will fetch a new root view
   * in which to traverse searching for our @param match
   *
   * @param viewMatcher ViewMatcher used to find our view
   */
  public static ViewInteraction waitForView(
          Matcher<View> viewMatcher,
          int waitMillis,
          int waitMillisPerTry
  ) {

    // Derive the max tries
    int maxTries = (int) (waitMillis / waitMillisPerTry);

    int tries = 0;

    for (int i = 0; i < maxTries; i++)
      try {
        // Track the amount of times we've tried
        tries++;

        // Search the root for the view
        onView(isRoot()).perform(searchFor(viewMatcher));

        // If we're here, we found our view. Now return it
        return onView(viewMatcher);

      } catch (Exception e) {
        if (tries == maxTries) {
          throw e;
        }
        Util.sleep(waitMillisPerTry);
      }

    throw new RuntimeException("Error finding a view matching $viewMatcher");
  }

  /**
   * Normally, you would do
   * onView(withId(R.id.send_button)).perform(click());
   * to send the draft message. However, in order to change the send button to the attach button
   * while there is no draft, the send button is made invisible and the attach button is made
   * visible instead. This confuses the test framework.<br/><br/>
   *
   * So, this is a workaround for pressing the send button.
   */
  public static void pressSend() {
    if (!Prefs.isEnterSendsEnabled(getInstrumentation().getTargetContext())) {
      resetEnterSends = true;
      Prefs.setEnterSendsEnabled(getInstrumentation().getTargetContext(), true);
    }
    waitForView(withHint(R.string.chat_input_placeholder), 10000, 100).perform(typeText("\n"));
  }
}