Spoon and Cucumber take screenshot

Hi guys,

Today I will post the code that I’ve improved to make the Cucumber take the screenshots and spoon use them:

Your helper class:

public void takeScreenshot() {
    String[] formattedInfo = treatFeatureScenarioStrings();
    mFeature = formattedInfo[0];
    mScenario = formattedInfo[1];

    if (mScenario == null) {
        throw new ScreenshotException("Error taking screenshot: I'm missing a 
valid test mScenario to attach the screenshot to");
    }
    mSolo.waitForActivity(mSolo.getCurrentActivity().getLocalClassName());
    String tag = Thread.currentThread().getStackTrace()[3].getMethodName();
    ScreenshotTaker.screenshot(mSolo.getCurrentActivity(), 
mSolo.getCurrentViews(), 
tag, mFeature, mScenario);
}

 

/*
This method is formatting the mScenario info to get the mFeature and 
mScenario namesand format them for the same format which Spoon is 
using. The name mFeature needs to follow this
pattern: Feature Test something and mScenario: Scenario Test something
- First letter of the first word of mScenario in uppercase, the same 
for mFeature
- All the others letters need to be in lowercase
- Before mFeature's name put the word Feature and before mScenario's 
name put the word Scenario
- When Scenario Outline the prefix of the scenario needs to be diferent
*/

private String[] treatFeatureScenarioStrings() {
    String mFeatureCap = mScenarioInfo[0].substring(0, 1).toUpperCase() 
+ mScenarioInfo[0].substring(1);
    String mScenarioCap = mScenarioInfo[1].substring(0, 1).toUpperCase() 
+ mScenarioInfo[1].substring(1);
    mFeature = "Feature " + mFeatureCap.replace("-", " ");
    mScenario = "Scenario " + mScenarioCap.replace("-", " ");

    if (mScenarioInfo.length > 2) {
        mScenario = "Scenario Outline " + mScenarioCap.replace("-", " ");
    }

    return new String[]{mFeature, mScenario};
}

private static class ScreenshotException extends RuntimeException {
    ScreenshotException(final String message) {
        super(message);
    }
}

 

ScreenshotTaker class:

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import timber.log.Timber;
import static org.assertj.core.api.Assertions.assertThat;
public abstract class ScreenshotTaker {
private static Set<String> mClearedOutputDirectories = new HashSet();
 static final String NAME_SEPARATOR = "_";
 static final String SPOON_SCREENSHOTS = "spoon-screenshots";
 public static File screenshot(Activity activity, ArrayList<View> views
, String tag, String testClassName, String testMethodName) {
 return screenshot(activity, getRecentDecorView(views), tag, 
testClassName, testMethodName);
 }
public static File screenshot(Activity activity, View view, String tag, 
String testClassName, String testMethodName) {
 try {
 if (view == null) {
 File viewFile = null;
 Log.e("Spoon", "Unable to take screenshot of this view, because it is 
null.");
 return viewFile;
 }
File parentFolder = obtainScreenshotDirectory
(activity.getApplicationContext(), testClassName, testMethodName);
 assertThat(parentFolder).isNotNull();
 final String screenshotName = System.currentTimeMillis() + 
NAME_SEPARATOR + tag + ".png";
 File screenshotFile = new File(parentFolder, screenshotName);
getBitmapOfView(view, activity, screenshotFile);
 Log.d("Spoon", "Captured screenshot \'" + tag + "\'.");
 return screenshotFile;
 } catch (Exception var7) {
 return null;
 }
}
 private static void getBitmapOfView(final View view, Activity activity
, final File screenShotFile) {
 activity.runOnUiThread(new Runnable() {
 @Override
 public void run() {
 view.destroyDrawingCache();
 view.buildDrawingCache(false);
 Bitmap orig = view.getDrawingCache();
 android.graphics.Bitmap.Config config = null;
 if (orig == null) {
 Timber.e("Bitmap is null");
 } else {
 config = orig.getConfig();
 if (config == null) {
 config = android.graphics.Bitmap.Config.ARGB_8888;
 }
 Bitmap mBitmap = orig.copy(config, false);
FileOutputStream mFileOutput = null;
 try {
 mFileOutput = new FileOutputStream(screenShotFile);
 if (mFileOutput != null) {
 mBitmap.compress(Bitmap.CompressFormat.PNG, 100, mFileOutput);
 screenShotFile.setReadable(true, false);
 }
 mFileOutput.flush();
 mFileOutput.close();
 } catch (IOException e) {
 e.printStackTrace();
 }
 orig.recycle();
 view.destroyDrawingCache();
 }
 }
 });
 }
private static File obtainScreenshotDirectory(Context context, 
String testClassName, String testMethodName) {
 return filesDirectory(context, SPOON_SCREENSHOTS, testClassName, 
testMethodName);
 }
private static File filesDirectory(Context context, String directoryType,
 String testClassName, String testMethodName) {
 File directory = null;
 if (Build.VERSION.SDK_INT >= 21) {
 directory = new File(Environment.getExternalStorageDirectory(), "app_" + 
directoryType);
 } else {
 directory = context.getDir(directoryType, 1);
 }
if (!mClearedOutputDirectories.contains(directoryType)) {
 deletePath(directory, false);
 mClearedOutputDirectories.add(directoryType);
 }
File dirClass1 = new File(directory, testClassName);
 File dirMethod = new File(dirClass1, testMethodName);
if (!dirMethod.exists()) {
 createDir(dirMethod);
 }
return dirMethod;
 }
private static void createDir(File dir) {
 File parent = dir.getParentFile();
 if (!parent.exists()) {
 createDir(parent);
 }
if (!dir.exists() && !dir.mkdirs()) {
 Timber.e("Unable to create output dir: " + dir.getAbsolutePath());
 } else {
 Chmod.chmodPlusRWX(dir);
 }
 }
 private static void deletePath(File path, boolean inclusive) {
 if (path.isDirectory()) {
 File[] children = path.listFiles();
 if (children != null) {
 File[] arr$ = children;
 int len$ = children.length;
for (int i$ = 0; i$ < len$; ++i$) {
 File child = arr$[i$];
 deletePath(child, true);
 }
 }
 }
if (inclusive) {
 path.delete();
 }
}
/**
 * Returns the most recent DecorView
 *
 * @param views the views to check
 * @return the most recent DecorView
 */
public static final View getRecentDecorView(ArrayList<View> views) {
 if (views == null) {
 Timber.e("Error in getRecentDecorView: 0 views passed in.");
 return null;
 }
final View[] decorViews = new View[views.size()];
 int i = 0;
 View view;
for (int j = 0; j < views.size(); j++) {
 view = views.get(j);
 if (view != null && view.getClass().getName()
 .equals("com.android.internal.policy.impl.PhoneWindow$DecorView")) {
 decorViews[i] = view;
 i++;
 }
 }
 return getRecentContainer(decorViews);
 }
/**
 * Returns the most recent view container
 *
 * @param views the views to check
 * @return the most recent view container
 */
private static final View getRecentContainer(View[] views) {
 View container = null;
 long drawingTime = 0;
 View view;
for (int i = 0; i < views.length; i++) {
 view = views[i];
 if (view != null && view.isShown() && view.hasWindowFocus() && 
view.getDrawingTime() > drawingTime) {
 container = view;
 drawingTime = view.getDrawingTime();
 }
 }
 return container;
 }
}

Please fell free to improve if you have any comment and suggestion it would be great !
Thank you guys, see you next week !

Using Spoon with Cucumber

Hi guys,

Today I will post about Spoon which is a framework that I’ve been learning. I hope this helps someone too, because spoon is quite new and doesn’t have too much support if you want to run with Cucumber.

Spoon is a framework to run android reports and Cucumber is a BDD framework.

  • If you are using gradle, you need to open your build.gradle and add:
 classpath('com.stanfy.spoon:spoon-gradle-plugin:1.0.3') {
  exclude module: 'guava'
  }

  • In your app-build.gradle:
plugin 'spoon'

dependencies{
 androidTestCompile 'com.squareup.spoon:spoon-client:1.2.0'
 androidTestCompile 'info.cukes:cucumber-android:1.2.4'
 androidTestCompile 'info.cukes:cucumber-picocontainer:1.2.4'
 } 
  • Create Spoon task in the same file:

spoon {
 debug = true
 if (project.hasProperty('spoonFailNoConnectedDevice')) {
    failIfNoDeviceConnected = true
 }

 if (project.hasProperty('cucumberOptions')) {
    instrumentationArgs = ["cucumberOptions=" + "'${project.cucumberOptions}'"]
 }

}
  • The instrumentation runner:
public class Instrumentation extends CucumberInstrumentation {
@Override
public void onStart() {
    runOnMainSync(new Runnable() {
        @Override
        public void run() {
            Application app = (Application) getTargetContext().
getApplicationContext();
            String simpleName = Instrumentation.class.getSimpleName();

            // Unlock the device so that the tests can input keystrokes.
            ((KeyguardManager) app.getSystemService(KEYGUARD_SERVICE)) //
                .newKeyguardLock(simpleName) //
                .disableKeyguard();
            // Wake up the screen.
            ((PowerManager) app.getSystemService(POWER_SERVICE)) //
                .newWakeLock(FULL_WAKE_LOCK | ACQUIRE_CAUSES_WAKEUP 
| ON_AFTER_RELEASE, simpleName) //
                .acquire();
        }
    });

    super.onStart();
}

}
  • Now you can use gradle command line with spoon task and pass Cucumber arguments. Like this one:
gradle spoon -PspoonFailNoConnectedDevice -PcucumberOptions='--tags @smoke'
      • Or you can use adb command line – without spoon report generation:
adb shell am instrument -w -e cucumberOptions "'--tags @smoke'" 
com.rsouza.test/com.rsouza.test.Instrumentation
  • Instrument arguments
am instrument argument Description
-e count true Count the number of tests (scenarios)
-e debug true Wait for a debugger to attach before starting to execute the tests.
-e log true Enable Cucumber dry-run (same as –e dryRun true)
-e coverage true Enable EMMA code coverage
-e coverageFile “/path/coverage.ec Set the file name and path of the EMMA coverage report
  • Cucumber arguments

https://cucumber.io/docs/reference/jvm#third-party-runners

  • Example: Use Cucumber and adb arguments
adb shell am instrument -w -e log true -e cucumberOptions "'--tags @debug'"
 com.rsouza.test/com.rsouza.test.Instrumentation

Thank you guys ! See you next week 🙂