Skip to content

Commit 64d9846

Browse files
authored
Merge pull request #405 from android/screenshotsample
Add an espresso screenshot sample
2 parents e523dbf + ef6881a commit 64d9846

File tree

36 files changed

+875
-0
lines changed

36 files changed

+875
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ A collection of samples demonstrating different frameworks and techniques for au
2323

2424
**[RecyclerViewSample](https://github.com/googlesamples/android-testing/blob/main/ui/espresso/RecyclerViewSample)** - RecyclerView actions for Espresso
2525

26+
**[ScreenshotSample](https://github.com/googlesamples/android-testing/blob/main/ui/espresso/ScreenshotSample)** - Screenshot capturing and saving using Espresso and androidx.test.core APIs
27+
2628
**[WebBasicSample](https://github.com/googlesamples/android-testing/blob/main/ui/espresso/WebBasicSample)** - Use Espresso-web to interact with WebViews
2729

2830
**[BasicSampleBundled](https://github.com/googlesamples/android-testing/blob/main/ui/espresso/BasicSampleBundled)** - Basic sample for Eclipse and other IDEs

projects.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ ui/espresso/IntentsBasicSample
1212
ui/espresso/MultiWindowSample
1313
ui/espresso/MultiProcessSample
1414
ui/espresso/RecyclerViewSample
15+
ui/espresso/ScreenshotSample
1516
ui/espresso/WebBasicSample
1617
ui/uiautomator/BasicSample
1718
unit/BasicSample
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
*.iml
2+
.gradle
3+
/local.properties
4+
/.idea/caches
5+
/.idea/libraries
6+
/.idea/modules.xml
7+
/.idea/workspace.xml
8+
/.idea/navEditor.xml
9+
/.idea/assetWizardSettings.xml
10+
.DS_Store
11+
/build
12+
/captures
13+
.externalNativeBuild
14+
.cxx
15+
local.properties
16+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
plugins {
2+
id 'com.android.application'
3+
id 'kotlin-android'
4+
}
5+
apply plugin: 'kotlin-android'
6+
7+
android {
8+
compileSdk 31
9+
10+
defaultConfig {
11+
applicationId "com.example.android.testing.espresso.screenshotsample"
12+
minSdk 18
13+
targetSdk 31
14+
versionCode 1
15+
versionName "1.0"
16+
17+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18+
testInstrumentationRunnerArguments useTestStorageService: 'true'
19+
}
20+
21+
buildTypes {
22+
release {
23+
minifyEnabled false
24+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
25+
}
26+
}
27+
compileOptions {
28+
sourceCompatibility JavaVersion.VERSION_1_8
29+
targetCompatibility JavaVersion.VERSION_1_8
30+
}
31+
kotlinOptions {
32+
jvmTarget = '1.8'
33+
}
34+
}
35+
36+
dependencies {
37+
38+
implementation "androidx.core:core-ktx:$androidxCoreVersion"
39+
implementation "androidx.appcompat:appcompat:$androidxCompatVersion"
40+
implementation "com.google.android.material:material:1.4.0"
41+
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
42+
testImplementation 'junit:junit:4.13.2'
43+
androidTestImplementation "androidx.test.ext:junit-ktx:$extJUnitVersion"
44+
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
45+
androidTestUtil "androidx.test.services:test-services:$servicesVersion"
46+
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
47+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.example.android.testing.espresso.screenshotsample;
2+
3+
import static androidx.test.core.app.DeviceCapture.takeScreenshot;
4+
import static androidx.test.core.graphics.BitmapStorage.writeToTestStorage;
5+
import static androidx.test.espresso.Espresso.onView;
6+
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
7+
import static androidx.test.espresso.matcher.ViewMatchers.withText;
8+
import static androidx.test.espresso.screenshot.ViewInteractionCapture.captureToBitmap;
9+
10+
import android.view.View;
11+
12+
import androidx.concurrent.futures.ResolvableFuture;
13+
import androidx.test.core.app.ActivityScenario;
14+
import androidx.test.core.view.ViewCapture;
15+
import androidx.test.ext.junit.rules.ActivityScenarioRule;
16+
import androidx.test.ext.junit.runners.AndroidJUnit4;
17+
18+
import org.junit.Rule;
19+
import org.junit.Test;
20+
import org.junit.rules.TestName;
21+
import org.junit.runner.RunWith;
22+
23+
import java.io.IOException;
24+
import java.util.concurrent.ExecutionException;
25+
import java.util.concurrent.Future;
26+
27+
/**
28+
* Equivalent of {@link ScreenshotTest} for java.
29+
*/
30+
@RunWith(AndroidJUnit4.class)
31+
public class ScreenshotJavaTest {
32+
// a handy JUnit rule that stores the method name
33+
@Rule
34+
public TestName nameRule = new TestName();
35+
36+
@Rule
37+
public ActivityScenarioRule<MainActivity> activityScenarioRule =
38+
new ActivityScenarioRule<>(MainActivity.class);
39+
40+
/**
41+
* Captures and saves an image of the entire {@link MainActivity} contents.
42+
*/
43+
@Test
44+
public void saveActivityBitmap() throws IOException {
45+
writeToTestStorage(captureToBitmap(onView(isRoot())), nameRule.getMethodName());
46+
}
47+
48+
/**
49+
* Captures and saves an image of the 'Hello world' view.
50+
*/
51+
@Test
52+
public void saveViewBitmap() throws IOException {
53+
writeToTestStorage(captureToBitmap(onView(withText("Hello World!"))), nameRule.getMethodName());
54+
}
55+
56+
/**
57+
* Captures and saves an image of the entire device screen to storage.
58+
*/
59+
@Test
60+
public void saveDeviceScreenBitmap() throws IOException {
61+
writeToTestStorage(takeScreenshot(), nameRule.getMethodName());
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.example.android.testing.espresso.screenshotsample
2+
3+
import androidx.test.core.app.takeScreenshot
4+
import androidx.test.core.graphics.writeToTestStorage
5+
import androidx.test.espresso.Espresso.onView
6+
import androidx.test.espresso.matcher.ViewMatchers.isRoot
7+
import androidx.test.espresso.matcher.ViewMatchers.withText
8+
import androidx.test.espresso.screenshot.captureToBitmap
9+
import androidx.test.ext.junit.rules.activityScenarioRule
10+
import androidx.test.ext.junit.runners.AndroidJUnit4
11+
import org.junit.Rule
12+
import org.junit.Test
13+
import org.junit.rules.TestName
14+
import org.junit.runner.RunWith
15+
import java.io.IOException
16+
17+
/*
18+
* Illustrates usage of APIs to capture a bitmap from view and saving it to test storage.
19+
*
20+
* The exact path will vary based on android API version, but the saved files can be retrieved via
21+
* Device File Explorer at a path like
22+
* /storage/emulated/0/googletest/test_outputfiles
23+
*
24+
* A future Android Gradle Plugin version should auto-retrieve these files from the device onto the
25+
* host.
26+
*/
27+
@RunWith(AndroidJUnit4::class)
28+
class ScreenshotTest {
29+
30+
// a handy JUnit rule that stores the method name, so it can be used to generate unique
31+
// screenshot files per test method
32+
@get:Rule
33+
var nameRule = TestName()
34+
35+
@get:Rule
36+
val activityScenarioRule = activityScenarioRule<MainActivity>()
37+
38+
/**
39+
* Captures and saves an image of the entire [MainActivity] contents.
40+
*/
41+
@Test
42+
@Throws(IOException::class)
43+
fun saveActivityBitmap() {
44+
onView(isRoot())
45+
.captureToBitmap()
46+
.writeToTestStorage(nameRule.methodName)
47+
}
48+
49+
/**
50+
* Captures and saves an image of the 'Hello world' view.
51+
*/
52+
@Test
53+
@Throws(IOException::class)
54+
fun saveViewBitmap() {
55+
onView(withText("Hello World!"))
56+
.captureToBitmap()
57+
.writeToTestStorage(nameRule.methodName)
58+
}
59+
60+
/**
61+
* Captures and saves an image of the entire device screen to storage.
62+
*/
63+
@Test
64+
@Throws(IOException::class)
65+
fun saveDeviceScreenBitmap() {
66+
takeScreenshot()
67+
.writeToTestStorage(nameRule.methodName)
68+
}
69+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="com.example.android.testing.espresso.screenshotsample">
4+
5+
<application
6+
android:allowBackup="true"
7+
android:icon="@mipmap/ic_launcher"
8+
android:label="@string/app_name"
9+
android:roundIcon="@mipmap/ic_launcher_round"
10+
android:supportsRtl="true"
11+
android:theme="@style/Theme.ScreenshotSample">
12+
<activity
13+
android:name=".MainActivity"
14+
android:exported="true">
15+
<intent-filter>
16+
<action android:name="android.intent.action.MAIN" />
17+
<category android:name="android.intent.category.LAUNCHER" />
18+
</intent-filter>
19+
</activity>
20+
</application>
21+
22+
</manifest>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.example.android.testing.espresso.screenshotsample
2+
3+
import androidx.appcompat.app.AppCompatActivity
4+
import android.os.Bundle
5+
6+
/**
7+
* A simple [Activity], autogenerated via Studio's 'Empty Activity' wizard.
8+
*/
9+
class MainActivity : AppCompatActivity() {
10+
override fun onCreate(savedInstanceState: Bundle?) {
11+
super.onCreate(savedInstanceState)
12+
setContentView(R.layout.activity_main)
13+
}
14+
}

0 commit comments

Comments
 (0)