add screengrab support / refactoring

This commit is contained in:
David Luhmer 2016-03-19 20:07:43 +01:00
parent b95d4c10cd
commit 69311fd87b
11 changed files with 318 additions and 59 deletions

View file

@ -94,6 +94,13 @@ dependencies {
testCompile 'org.robolectric:robolectric:3.0-rc3' testCompile 'org.robolectric:robolectric:3.0-rc3'
testCompile 'junit:junit:4.1' testCompile 'junit:junit:4.1'
testCompile("org.mockito:mockito-core:1.10.19") {
exclude group: 'org.hamcrest'
}
testCompile 'com.google.dexmaker:dexmaker:1.2'
testCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
androidTestCompile 'com.squareup.okhttp:mockwebserver:2.5.0' androidTestCompile 'com.squareup.okhttp:mockwebserver:2.5.0'
androidTestCompile "com.android.support:support-annotations:${SUPPORT_VERSION}" androidTestCompile "com.android.support:support-annotations:${SUPPORT_VERSION}"
@ -112,5 +119,10 @@ dependencies {
compile 'com.nbsp:library:1.02' compile 'com.nbsp:library:1.02'
//androidTestCompile 'tools:fastlane:screengrab:0.3.0' androidTestCompile 'tools.fastlane:screengrab:0.3.1'
androidTestCompile("org.mockito:mockito-core:1.10.19") {
exclude group: 'org.hamcrest'
}
androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
} }

View file

@ -1,37 +0,0 @@
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import de.luhmer.owncloudnewsreader.NewsReaderListActivity;
import de.luhmer.owncloudnewsreader.R;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
/**
* Created by David on 06.03.2016.
*/
@RunWith(AndroidJUnit4.class)
public class ScreenshotTest {
/*
@ClassRule
public static final LocalTestRule localTestRule = new LocalTestRule();
@Rule
public ActivityTestRule<NewsReaderListActivity> mActivityRule = new ActivityTestRule<>(NewsReaderListActivity.class);
@Test
public void changeText_sameActivity() {
Screengrab
}
*/
}

View file

@ -0,0 +1,209 @@
package screengrab;
import android.app.Instrumentation;
import android.content.SharedPreferences;
import android.graphics.Point;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.support.v4.content.SharedPreferencesCompat;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.test.UiThreadTest;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mockito;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import de.luhmer.owncloudnewsreader.NewsReaderDetailFragment;
import de.luhmer.owncloudnewsreader.NewsReaderListActivity;
import de.luhmer.owncloudnewsreader.NewsReaderListFragment;
import de.luhmer.owncloudnewsreader.R;
import de.luhmer.owncloudnewsreader.SettingsActivity;
import de.luhmer.owncloudnewsreader.adapter.NewsListRecyclerAdapter;
import de.luhmer.owncloudnewsreader.adapter.ViewHolder;
import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm;
import de.luhmer.owncloudnewsreader.model.PodcastItem;
import de.luhmer.owncloudnewsreader.model.UserInfo;
import tools.fastlane.screengrab.Screengrab;
import tools.fastlane.screengrab.locale.LocaleTestRule;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
/**
* Created by David on 06.03.2016.
*/
@RunWith(AndroidJUnit4.class)
public class ScreenshotTest {
@ClassRule
public static final LocaleTestRule localTestRule = new LocaleTestRule();
@Rule
public ActivityTestRule<NewsReaderListActivity> mActivityRule = new ActivityTestRule<>(NewsReaderListActivity.class);
private MenuItem menuItem;
private NewsReaderListActivity activity;
private NewsReaderListFragment nrlf;
private NewsReaderDetailFragment nrdf;
private int itemPos = 5;
@Before
public void setup() {
menuItem = Mockito.mock(MenuItem.class);
activity = mActivityRule.getActivity();
nrlf = mActivityRule.getActivity().getSlidingListFragment();
nrdf = mActivityRule.getActivity().getNewsReaderDetailFragment();
SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(mActivityRule.getActivity());
UserInfo userInfo = new UserInfo.Builder()
.setUserId("1")
.setDisplayName("David")
.setAvatar(null)
.build();
try {
mPrefs.edit().putString("USER_INFO", NewsReaderListFragment.toString(userInfo)).commit();
mPrefs.edit().putBoolean(SettingsActivity.CB_SKIP_DETAILVIEW_AND_OPEN_BROWSER_DIRECTLY_STRING, false).commit();
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void testTakeScreenshots() {
Screengrab.screenshot("startup");
Mockito.when(menuItem.getItemId()).thenReturn(android.R.id.home);
activity.runOnUiThread(new Runnable() {
public void run() {
//Set url to mock
nrlf.bindUserInfoToUI(true);
mActivityRule.getActivity().onOptionsItemSelected(menuItem); //Open Drawer
nrlf.getListView().expandGroup(2);
}
});
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
Screengrab.screenshot("slider_open");
activity.runOnUiThread(new Runnable() {
public void run() {
activity.onOptionsItemSelected(menuItem); //Close Drawer
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
activity.onClick(null, itemPos); //Select item
}
});
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Screengrab.screenshot("detail_activity");
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
NewsListRecyclerAdapter na = (NewsListRecyclerAdapter) nrdf.getRecyclerView().getAdapter();
ViewHolder vh = (ViewHolder) nrdf.getRecyclerView().getChildViewHolder(nrdf.getRecyclerView().getLayoutManager().findViewByPosition(itemPos));
na.ChangeReadStateOfItem(vh, false);
}
});
}
@Test
public void testPodcast() {
activity.runOnUiThread(new Runnable() {
public void run() {
//Set url to mock
nrlf.bindUserInfoToUI(true);
mActivityRule.getActivity().onOptionsItemSelected(menuItem); //Open Drawer
nrlf.getListView().expandGroup(2);
nrlf.onChildClickListener.onChildClick(null, null, 2, 2, 0); //Click on Android Central Podcast
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Screengrab.screenshot("podcast_list");
activity.runOnUiThread(new Runnable() {
public void run() {
ViewHolder vh = (ViewHolder) nrdf.getRecyclerView().getChildViewHolder(nrdf.getRecyclerView().getLayoutManager().findViewByPosition(1));
PodcastItem podcastItem = DatabaseConnectionOrm.ParsePodcastItemFromRssItem(activity, vh.getRssItem());
activity.openMediaItem(podcastItem);
}
});
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Screengrab.screenshot("podcast_running");
activity.runOnUiThread(new Runnable() {
public void run() {
activity.pausePodcast();
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

View file

@ -42,6 +42,7 @@ import android.os.RemoteException;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
@ -845,11 +846,13 @@ public class NewsReaderListActivity extends PodcastFragmentActivity implements
} }
} }
protected NewsReaderListFragment getSlidingListFragment() { @VisibleForTesting
public NewsReaderListFragment getSlidingListFragment() {
return ((NewsReaderListFragment) getSupportFragmentManager().findFragmentById(R.id.left_drawer)); return ((NewsReaderListFragment) getSupportFragmentManager().findFragmentById(R.id.left_drawer));
} }
protected NewsReaderDetailFragment getNewsReaderDetailFragment() { @VisibleForTesting
public NewsReaderDetailFragment getNewsReaderDetailFragment() {
return (NewsReaderDetailFragment) getSupportFragmentManager().findFragmentById(R.id.content_frame); return (NewsReaderDetailFragment) getSupportFragmentManager().findFragmentById(R.id.content_frame);
} }

View file

@ -24,13 +24,13 @@ package de.luhmer.owncloudnewsreader;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Base64; import android.util.Base64;
import android.util.Log; import android.util.Log;
import android.util.TypedValue; import android.util.TypedValue;
@ -65,6 +65,7 @@ import de.luhmer.owncloudnewsreader.helper.AsyncTaskHelper;
import de.luhmer.owncloudnewsreader.helper.ThemeChooser; import de.luhmer.owncloudnewsreader.helper.ThemeChooser;
import de.luhmer.owncloudnewsreader.interfaces.ExpListTextClicked; import de.luhmer.owncloudnewsreader.interfaces.ExpListTextClicked;
import de.luhmer.owncloudnewsreader.model.FolderSubscribtionItem; import de.luhmer.owncloudnewsreader.model.FolderSubscribtionItem;
import de.luhmer.owncloudnewsreader.model.UserInfo;
import de.luhmer.owncloudnewsreader.reader.HttpJsonRequest; import de.luhmer.owncloudnewsreader.reader.HttpJsonRequest;
import de.luhmer.owncloudnewsreader.reader.owncloud.API; import de.luhmer.owncloudnewsreader.reader.owncloud.API;
import de.luhmer.owncloudnewsreader.reader.owncloud.OwnCloudReaderMethods; import de.luhmer.owncloudnewsreader.reader.owncloud.OwnCloudReaderMethods;
@ -212,7 +213,7 @@ public class NewsReaderListFragment extends Fragment implements OnCreateContextM
}; };
OnChildClickListener onChildClickListener = new OnChildClickListener() { public OnChildClickListener onChildClickListener = new OnChildClickListener() {
@Override @Override
public boolean onChildClick(ExpandableListView parent, View v, public boolean onChildClick(ExpandableListView parent, View v,
@ -248,7 +249,9 @@ public class NewsReaderListFragment extends Fragment implements OnCreateContextM
public ExpandableListView getListView() {
return eListView;
}
protected void showTapLogoToSyncShowcaseView() { protected void showTapLogoToSyncShowcaseView() {
@ -279,7 +282,7 @@ public class NewsReaderListFragment extends Fragment implements OnCreateContextM
return null; //API NOT SUPPORTED! return null; //API NOT SUPPORTED!
UserInfo ui = new UserInfo(); UserInfo.Builder ui = new UserInfo.Builder();
InputStream inputStream = HttpJsonRequest.getInstance().PerformJsonRequest(api.getUserUrl()); InputStream inputStream = HttpJsonRequest.getInstance().PerformJsonRequest(api.getUserUrl());
JsonReader reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8")); JsonReader reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8"));
@ -289,10 +292,10 @@ public class NewsReaderListFragment extends Fragment implements OnCreateContextM
while(reader.hasNext() && (currentName = reader.nextName()) != null) { while(reader.hasNext() && (currentName = reader.nextName()) != null) {
switch(currentName) { switch(currentName) {
case "userId": case "userId":
ui.mUserId = reader.nextString(); ui.setUserId(reader.nextString());
break; break;
case "displayName": case "displayName":
ui.mDisplayName = reader.nextString(); ui.setDisplayName(reader.nextString());
break; break;
case "avatar": case "avatar":
com.google.gson.stream.JsonToken jt = reader.peek(); com.google.gson.stream.JsonToken jt = reader.peek();
@ -307,7 +310,7 @@ public class NewsReaderListFragment extends Fragment implements OnCreateContextM
if (currentName.equals("data")) { if (currentName.equals("data")) {
String encodedImage = reader.nextString(); String encodedImage = reader.nextString();
byte[] decodedString = Base64.decode(encodedImage, Base64.DEFAULT); byte[] decodedString = Base64.decode(encodedImage, Base64.DEFAULT);
ui.mAvatar = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length); ui.setAvatar(BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length));
Log.v(TAG, encodedImage); Log.v(TAG, encodedImage);
} else { } else {
reader.skipValue(); reader.skipValue();
@ -323,7 +326,7 @@ public class NewsReaderListFragment extends Fragment implements OnCreateContextM
} }
reader.close(); reader.close();
return ui; return ui.build();
} catch (Exception e) { } catch (Exception e) {
if(e.getMessage().equals("Method Not Allowed")) { //Remove if old version is used if(e.getMessage().equals("Method Not Allowed")) { //Remove if old version is used
SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
@ -351,6 +354,10 @@ public class NewsReaderListFragment extends Fragment implements OnCreateContextM
} }
protected void bindUserInfoToUI() { protected void bindUserInfoToUI() {
bindUserInfoToUI(false);
}
public void bindUserInfoToUI(boolean testMode) {
SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
String mUsername = mPrefs.getString(SettingsActivity.EDT_USERNAME_STRING, null); String mUsername = mPrefs.getString(SettingsActivity.EDT_USERNAME_STRING, null);
String mOc_root_path = mPrefs.getString(SettingsActivity.EDT_OWNCLOUDROOTPATH_STRING, getString(R.string.app_name)); String mOc_root_path = mPrefs.getString(SettingsActivity.EDT_OWNCLOUDROOTPATH_STRING, getString(R.string.app_name));
@ -358,6 +365,10 @@ public class NewsReaderListFragment extends Fragment implements OnCreateContextM
userTextView.setText(mUsername); userTextView.setText(mUsername);
urlTextView.setText(mOc_root_path); urlTextView.setText(mOc_root_path);
if(testMode) { //Hide real url in test mode
urlTextView.setText("https://example.com/owncloud");
}
String uInfo = mPrefs.getString("USER_INFO", null); String uInfo = mPrefs.getString("USER_INFO", null);
if(uInfo == null) if(uInfo == null)
return; return;
@ -380,14 +391,8 @@ public class NewsReaderListFragment extends Fragment implements OnCreateContextM
} }
private class UserInfo implements Serializable {
private String mUserId;
private String mDisplayName;
private Bitmap mAvatar;
}
/** Read the object from Base64 string. */ /** Read the object from Base64 string. */
private static Object fromString(String s) throws IOException, public static Object fromString(String s) throws IOException,
ClassNotFoundException { ClassNotFoundException {
byte [] data = Base64.decode(s, Base64.DEFAULT); byte [] data = Base64.decode(s, Base64.DEFAULT);
ObjectInputStream ois = new ObjectInputStream( ObjectInputStream ois = new ObjectInputStream(
@ -398,7 +403,7 @@ public class NewsReaderListFragment extends Fragment implements OnCreateContextM
} }
/** Write the object to a Base64 string. */ /** Write the object to a Base64 string. */
private static String toString(Serializable o) throws IOException { public static String toString(Serializable o) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream( baos ); ObjectOutputStream oos = new ObjectOutputStream( baos );
oos.writeObject(o); oos.writeObject(o);

View file

@ -12,6 +12,7 @@ import android.content.res.Resources;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.VisibleForTesting;
import android.support.v4.view.GravityCompat; import android.support.v4.view.GravityCompat;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.util.Log; import android.util.Log;
@ -482,7 +483,8 @@ public class PodcastFragmentActivity extends AppCompatActivity implements IPlayP
return px; return px;
} }
protected void openMediaItem(MediaItem mediaItem) { @VisibleForTesting
public void openMediaItem(MediaItem mediaItem) {
Intent intent = new Intent(this, PodcastPlaybackService.class); Intent intent = new Intent(this, PodcastPlaybackService.class);
if(mediaItem instanceof TTSItem) if(mediaItem instanceof TTSItem)
intent.putExtra(PodcastPlaybackService.TTS_ITEM, mediaItem); intent.putExtra(PodcastPlaybackService.TTS_ITEM, mediaItem);

View file

@ -0,0 +1,46 @@
package de.luhmer.owncloudnewsreader.model;
import android.graphics.Bitmap;
import java.io.Serializable;
/**
* Created by dluhmer on 19.03.16.
*/
public class UserInfo implements Serializable {
public static class Builder {
private String userId;
private String displayName;
private Bitmap avatar;
public Builder setUserId(String userId) {
this.userId = userId;
return this;
}
public Builder setDisplayName(String displayName) {
this.displayName = displayName;
return this;
}
public Builder setAvatar(Bitmap avatar) {
this.avatar = avatar;
return this;
}
public UserInfo build() {
return new UserInfo(userId, displayName, avatar);
}
}
private UserInfo(String userId, String displayName, Bitmap avatar) {
this.mUserId = userId;
this.mDisplayName = displayName;
this.mAvatar = avatar;
}
public final String mUserId;
public final String mDisplayName;
public final Bitmap mAvatar;
}

16
Screengrabfile Normal file
View file

@ -0,0 +1,16 @@
# remove the leading '#' to uncomment lines
app_package_name 'de.luhmer.owncloudnewsreader'
use_tests_in_packages ['screengrab']
# use_tests_in_classes ['ScreenshotTest']
app_apk_path 'News-Android-App/build/outputs/apk/News-Android-App-debug-unaligned.apk'
tests_apk_path 'News-Android-App/build/outputs/apk/News-Android-App-debug-androidTest-unaligned.apk'
locales ['en-US', 'fr-FR', 'it-IT']
# clear all previously generated screenshots in your local output directory before creating new ones
clear_previous_screenshots true
# For more information about all available options run
# screengrab --help

3
executeScreengrab.sh Normal file
View file

@ -0,0 +1,3 @@
# export PATH=$PATH:/Users/dluhmer/Library/Android/sdk/platform-tools/
./gradlew assembleDebug assembleAndroidTest
screengrab

View file

@ -1,6 +1,6 @@
#Sun Dec 07 11:51:22 CET 2014 #Sat Mar 19 10:26:26 CET 2016
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-2.2-all.zip

0
gradlew vendored Normal file → Executable file
View file