calabash-android 0.2.11 → 0.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. data/CHANGES.txt +6 -0
  2. data/features-skeleton/support/app_installation_hooks.rb +11 -3
  3. data/features-skeleton/support/app_life_cycle_hooks.rb +9 -0
  4. data/features-skeleton/support/hooks.rb +18 -2
  5. data/lib/calabash-android/operations.rb +1 -1
  6. data/lib/calabash-android/steps/assert_steps.rb +12 -0
  7. data/lib/calabash-android/steps/list_steps.rb +40 -0
  8. data/lib/calabash-android/steps/map_steps.rb +61 -0
  9. data/lib/calabash-android/steps/navigation_steps.rb +10 -1
  10. data/lib/calabash-android/steps/progress_steps.rb +5 -0
  11. data/lib/calabash-android/version.rb +2 -2
  12. data/test-server/AndroidManifest.xml +5 -5
  13. data/test-server/build.xml +4 -0
  14. data/test-server/instrumentation-backend/.gitignore +1 -0
  15. data/test-server/instrumentation-backend/AndroidManifest.xml +4 -3
  16. data/test-server/instrumentation-backend/project.properties +1 -1
  17. data/test-server/instrumentation-backend/src/com/jayway/android/robotium/solo/MapViewUtils.java +328 -0
  18. data/test-server/instrumentation-backend/src/com/jayway/android/robotium/solo/SoloEnhanced.java +97 -0
  19. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/InstrumentationBackend.java +3 -3
  20. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/TestHelpers.java +51 -1
  21. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/GetListItemProperties.java +195 -0
  22. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/GetListItemText.java +136 -0
  23. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/map/GetMapBounds.java +27 -0
  24. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/map/GetMapCenter.java +27 -0
  25. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/map/GetMapMarker.java +31 -0
  26. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/map/GetMapMarkers.java +48 -0
  27. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/map/GetMapZoom.java +19 -0
  28. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/map/PanMapTo.java +23 -0
  29. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/map/SetMapCenter.java +23 -0
  30. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/map/SetMapZoom.java +34 -0
  31. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/map/TapAwayFromMarkers.java +28 -0
  32. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/map/TapMapMarker.java +29 -0
  33. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/spinner/GetSelectedSpinnerItemText.java +36 -0
  34. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/AssertGridViewContainsNoDuplicates.java +72 -0
  35. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/GetTextById.java +42 -0
  36. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/AssertViewProperty.java +141 -0
  37. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/ClickOnViewById.java +1 -8
  38. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/GetActivityName.java +32 -0
  39. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/GetViewProperty.java +107 -0
  40. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/HasView.java +31 -0
  41. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/Press.java +1 -1
  42. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/SelectTab.java +110 -0
  43. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForScreen.java +10 -6
  44. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForTab.java +108 -0
  45. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/{view → wait}/WaitForView.java +1 -1
  46. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/{view → wait}/WaitForViewById.java +21 -11
  47. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/QueryHelper.java +3 -3
  48. metadata +102 -88
@@ -0,0 +1,141 @@
1
+ package sh.calaba.instrumentationbackend.actions.view;
2
+
3
+ import java.lang.reflect.InvocationTargetException;
4
+ import java.lang.reflect.Method;
5
+
6
+ import android.graphics.Bitmap;
7
+ import android.graphics.drawable.BitmapDrawable;
8
+ import android.graphics.drawable.Drawable;
9
+ import android.graphics.drawable.DrawableContainer;
10
+ import android.util.Log;
11
+ import android.view.View;
12
+ import android.widget.TextView;
13
+ import sh.calaba.instrumentationbackend.Result;
14
+ import sh.calaba.instrumentationbackend.TestHelpers;
15
+ import sh.calaba.instrumentationbackend.actions.Action;
16
+
17
+
18
+ /**
19
+ * eg: performAction( 'assert_view_property', 'my_view', 'visibility', 'visible' ) // or invisible/gone
20
+ * eg: performAction( 'assert_view_property', 'my_view', 'drawable', 'expected_id' )
21
+ * eg: performAction( 'assert_view_property', 'my_view', 'compoundDrawables', 'left', 'expected_id' )
22
+ *
23
+ * @author Nicholas Albion
24
+ */
25
+ public class AssertViewProperty extends GetViewProperty implements Action {
26
+ private static final String TAG = "assert_view_property";
27
+
28
+ @Override
29
+ protected Result getPropertyValue( String propertyName, View view, String[] args ) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
30
+ if( "compoundDrawables".equals(propertyName) ) {
31
+ if( view instanceof TextView ) {
32
+ // performAction( 'get_view_property', 'my_view', 'compoundDrawables', 'left', 'expected_id'
33
+ String expected_id = args[3];
34
+ int pos;
35
+ if( "top".equals(args[2]) ) {
36
+ pos = 1;
37
+ } else if( "right".equals(args[2]) ) {
38
+ pos = 2;
39
+ } else if( "bottom".equals(args[2]) ) {
40
+ pos = 3;
41
+ } else {
42
+ pos = 0;
43
+ }
44
+ Drawable[] drawables = ((TextView)view).getCompoundDrawables(); // [left, top, right, bottom]
45
+ Drawable actualDrawable = drawables[pos];
46
+ Drawable expectedDrawable = TestHelpers.getDrawableById(expected_id);
47
+ return assertSameDrawables( propertyName + ", " + args[2], expectedDrawable, actualDrawable );
48
+ } else {
49
+ throw new IllegalArgumentException("compoundDrawables is only supported for subclasses of TextView, not " + view.getClass().getName());
50
+ }
51
+ } else {
52
+ return super.getPropertyValue(propertyName, view, args);
53
+ }
54
+ }
55
+
56
+ /**
57
+ * @param propertyName
58
+ * @param propertyValue
59
+ * @param args
60
+ * @return successResult if <code>propertyValue</code> is equal to <code>args[2]</code>
61
+ * otherwise provides a descriptive message and the expected and actual values in the <code>bonusInformation</code>
62
+ */
63
+ @Override
64
+ protected Result processProperty( String propertyName, Object propertyValue, String[] args ) {
65
+ String expected = args[2];
66
+ String value;
67
+ if( propertyValue == null ) {
68
+ if( expected == null || "null".equals(expected) ) {
69
+ return Result.successResult();
70
+ }
71
+ value = "null";
72
+ } else if( "drawable".equals(propertyName) ) {
73
+ Drawable actualDrawable = (Drawable)propertyValue; // ((ImageButton)view).getDrawable();
74
+ if( actualDrawable instanceof DrawableContainer ) {
75
+ actualDrawable = ((DrawableContainer)actualDrawable).getCurrent();
76
+ }
77
+ Drawable expectedDrawable = TestHelpers.getDrawableById(expected);
78
+ return assertSameDrawables( propertyName, expectedDrawable, actualDrawable );
79
+ } else {
80
+ value = propertyValue.toString();
81
+ if( value.equals(expected) ) {
82
+ return Result.successResult();
83
+ }
84
+ }
85
+ Result result = new Result(false, "For " + propertyName + " expected " + expected + " but was actually " + value);
86
+ result.addBonusInformation( expected );
87
+ result.addBonusInformation( value );
88
+ return result;
89
+ }
90
+
91
+ private Result assertSameDrawables( String propertyName, Drawable expectedDrawable, Drawable actualDrawable ) {
92
+ if( sameDrawables( expectedDrawable, actualDrawable) ) {
93
+ return Result.successResult();
94
+ } else {
95
+ Result result = new Result(false, "For " + propertyName + " expected " + expectedDrawable + " but was actually " + actualDrawable);
96
+ result.addBonusInformation( expectedDrawable == null ? "null" : expectedDrawable.toString() );
97
+ result.addBonusInformation( actualDrawable == null ? "null" : actualDrawable.toString() );
98
+ return result;
99
+ }
100
+ }
101
+
102
+ private boolean sameDrawables( Drawable expected, Drawable actual ) {
103
+ if( expected.equals(actual) ) {
104
+ return true;
105
+ }
106
+ if( expected instanceof DrawableContainer ) {
107
+ expected = ((DrawableContainer)expected).getCurrent();
108
+ }
109
+ if( actual instanceof DrawableContainer ) {
110
+ actual = ((DrawableContainer)actual).getCurrent();
111
+ }
112
+ if( (expected instanceof BitmapDrawable) && (actual instanceof BitmapDrawable) ) {
113
+ Bitmap expectedBitmap = ((BitmapDrawable)expected).getBitmap();
114
+ Bitmap actualBitmap = ((BitmapDrawable)actual).getBitmap();
115
+ try {
116
+ // As pointed out by kbielenberg, Bitmap.sameAs() was only added in level 12/Android 3.1/Honeycomb MR1
117
+ Method sameAs = Bitmap.class.getMethod("sameAs", Bitmap.class);
118
+ return (Boolean)sameAs.invoke( expectedBitmap, actualBitmap );
119
+ } catch (Exception e) {
120
+ if( expectedBitmap.getWidth() != actualBitmap.getWidth() ) { return false; }
121
+ if( expectedBitmap.getHeight() != actualBitmap.getHeight() ) { return false; }
122
+ if( expectedBitmap.getConfig() != actualBitmap.getConfig() ) { return false; }
123
+ boolean bitmapsEqual = expectedBitmap.equals(actualBitmap);
124
+ if( !bitmapsEqual ) {
125
+ Log.i(TAG, "Bitmaps are not equal");
126
+ }
127
+ boolean drawablesEqual = expected.equals(actual);
128
+ if( !drawablesEqual ) {
129
+ Log.i(TAG, "Drawables are not equal");
130
+ }
131
+ return bitmapsEqual && drawablesEqual;
132
+ }
133
+ }
134
+ return false;
135
+ }
136
+
137
+ @Override
138
+ public String key() {
139
+ return "assert_view_property";
140
+ }
141
+ }
@@ -32,14 +32,7 @@ public class ClickOnViewById implements Action {
32
32
  System.out.println(xy[0]);
33
33
  System.out.println(xy[1]);
34
34
 
35
- //InstrumentationBackend.solo.clickOnView(view);
36
- if (view.isClickable()) {
37
- InstrumentationBackend.solo.getCurrentActivity().runOnUiThread(new Runnable() {
38
- public void run() {
39
- view.performClick();
40
- }
41
- });
42
- }
35
+ InstrumentationBackend.solo.clickOnView(view);
43
36
  } catch(junit.framework.AssertionFailedError e) {
44
37
  System.out.println("solo.clickOnView failed - using fallback");
45
38
  if (view.isClickable()) {
@@ -0,0 +1,32 @@
1
+ package sh.calaba.instrumentationbackend.actions.view;
2
+
3
+ import sh.calaba.instrumentationbackend.InstrumentationBackend;
4
+ import sh.calaba.instrumentationbackend.Result;
5
+ import sh.calaba.instrumentationbackend.actions.Action;
6
+ import android.app.Activity;
7
+ import android.app.TabActivity;
8
+
9
+ /**
10
+ * @author Nicholas Albion
11
+ */
12
+ @SuppressWarnings("deprecation")
13
+ public class GetActivityName implements Action {
14
+
15
+ @Override
16
+ public Result execute(String... args) {
17
+ Activity currentActivity = InstrumentationBackend.solo.getCurrentActivity();
18
+
19
+ Result result = new Result(true, currentActivity.getClass().getSimpleName());
20
+
21
+ if( currentActivity instanceof TabActivity ) {
22
+ result.addBonusInformation( ((TabActivity)currentActivity).getTabHost().getCurrentTabTag() );
23
+ }
24
+
25
+ return result;
26
+ }
27
+
28
+ @Override
29
+ public String key() {
30
+ return "get_activity_name";
31
+ }
32
+ }
@@ -0,0 +1,107 @@
1
+ package sh.calaba.instrumentationbackend.actions.view;
2
+
3
+ import java.lang.reflect.InvocationTargetException;
4
+ import java.lang.reflect.Method;
5
+
6
+ import sh.calaba.instrumentationbackend.Result;
7
+ import sh.calaba.instrumentationbackend.actions.Action;
8
+ import sh.calaba.instrumentationbackend.actions.wait.WaitForViewById;
9
+ import android.view.View;
10
+
11
+ /**
12
+ * eg: performAction( 'get_view_property', 'my_view', 'visibility') => visible, invisible or gone
13
+ *
14
+ * @see AssertViewProperty
15
+ * @author Nicholas Albion
16
+ */
17
+ public class GetViewProperty extends WaitForViewById implements Action {
18
+
19
+ @Override
20
+ public Result execute(String... args) {
21
+ String viewId = args[0];
22
+
23
+ try {
24
+ View view = getViewById(viewId, 60000);
25
+ if( view != null ) {
26
+ // Result result = new Result(true);
27
+ //
28
+ // String propertyName = args[1].toLowerCase();
29
+ // String value = getPropertyValue(propertyName, view, args);
30
+ // result.setMessage( value );
31
+
32
+ String propertyName = args[1].toLowerCase();
33
+ return getPropertyValue(propertyName, view, args);
34
+ } else {
35
+ return new Result(false, "Timed out while waiting for view with id:'" + viewId + "'");
36
+ }
37
+ } catch( Exception e ) {
38
+ return Result.fromThrowable(e);
39
+ }
40
+ }
41
+
42
+ /**
43
+ * @param propertyName
44
+ * @param view
45
+ * @param args - used by {@link AssertViewProperty#getPropertyValue}
46
+ * @return
47
+ * @throws NoSuchMethodException
48
+ * @throws IllegalArgumentException
49
+ * @throws IllegalAccessException
50
+ * @throws InvocationTargetException
51
+ */
52
+ protected Result getPropertyValue( String propertyName, View view, String[] args ) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
53
+ if( "visibility".equals(propertyName) ) {
54
+ String value;
55
+
56
+ switch( view.getVisibility() ) {
57
+ case View.VISIBLE:
58
+ value = "visible";
59
+ break;
60
+ case View.INVISIBLE:
61
+ value = "invisible";
62
+ break;
63
+ case View.GONE:
64
+ value = "gone";
65
+ break;
66
+ default:
67
+ value = Integer.toString(view.getVisibility());
68
+ }
69
+
70
+ return new Result( true, value );
71
+ } else {
72
+ // resort to reflection
73
+ Class<? extends View> clazz = view.getClass();
74
+ Method method = null;
75
+ try {
76
+ String methodName = propertyName;
77
+ method = clazz.getMethod( methodName );
78
+ } catch( NoSuchMethodException e ) {
79
+ try {
80
+ String methodName = "get" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
81
+ method = clazz.getMethod( methodName );
82
+ } catch( NoSuchMethodException e2 ) {
83
+ String methodName = "is" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
84
+ method = clazz.getMethod( methodName );
85
+ }
86
+ }
87
+ Object valueObj = method.invoke(view);
88
+ return processProperty( propertyName, valueObj, args );
89
+ }
90
+ }
91
+
92
+ /**
93
+ * @param propertyName
94
+ * @param propertyValue
95
+ * @param args - used by {@link AssertViewProperty#getPropertyValue}
96
+ * @return a successful result with the requested property as the <code>message</code> field
97
+ */
98
+ protected Result processProperty( String propertyName, Object propertyValue, String[] args ) {
99
+ String message = (propertyValue == null) ? "null" : propertyValue.toString();
100
+ return new Result( true, message );
101
+ }
102
+
103
+ @Override
104
+ public String key() {
105
+ return "get_view_property";
106
+ }
107
+ }
@@ -0,0 +1,31 @@
1
+ package sh.calaba.instrumentationbackend.actions.view;
2
+
3
+ import sh.calaba.instrumentationbackend.Result;
4
+ import sh.calaba.instrumentationbackend.TestHelpers;
5
+ import sh.calaba.instrumentationbackend.actions.Action;
6
+ import android.view.View;
7
+
8
+ public class HasView implements Action {
9
+
10
+ @Override
11
+ public Result execute(String... args) {
12
+ final String idArgument = args[0];
13
+ final View foundView = TestHelpers.getViewById(idArgument);
14
+
15
+ if( null == foundView ) {
16
+ return notFoundResult(idArgument);
17
+ }
18
+
19
+ return Result.successResult();
20
+ }
21
+
22
+ @Override
23
+ public String key() {
24
+ return "has_view";
25
+ }
26
+
27
+ private Result notFoundResult(final String firstArgument) {
28
+ return Result.failedResult(String.format("View with id %s was not found", firstArgument));
29
+ }
30
+ }
31
+
@@ -29,7 +29,7 @@ public class Press implements Action {
29
29
  }
30
30
  }
31
31
 
32
- // Try to pres view with content description
32
+ // Try to press view with content description
33
33
  if (viewToPress == null) {
34
34
  helpText += "Content descriptions:\n";
35
35
  for (View view : InstrumentationBackend.solo.getCurrentViews()) {
@@ -0,0 +1,110 @@
1
+ package sh.calaba.instrumentationbackend.actions.view;
2
+
3
+ import java.util.LinkedList;
4
+ import java.util.List;
5
+
6
+ import android.app.Activity;
7
+ import android.app.TabActivity;
8
+ import android.util.Log;
9
+ import android.view.View;
10
+ import android.widget.TabHost;
11
+ import android.widget.TextView;
12
+ import sh.calaba.instrumentationbackend.InstrumentationBackend;
13
+ import sh.calaba.instrumentationbackend.Result;
14
+ import sh.calaba.instrumentationbackend.actions.Action;
15
+
16
+ /**
17
+ * Select the tab by 1-based index or by name
18
+ *
19
+ * @author Nicholas Albion
20
+ */
21
+ @SuppressWarnings("deprecation")
22
+ public class SelectTab implements Action {
23
+ @Override
24
+ public Result execute(String... args) {
25
+
26
+ Activity currentActivity = InstrumentationBackend.solo.getCurrentActivity();
27
+
28
+ if( currentActivity instanceof TabActivity == false ) {
29
+ Activity parent = currentActivity.getParent();
30
+ if( parent != null ) {
31
+ currentActivity = parent;
32
+ }
33
+ }
34
+
35
+ if( currentActivity instanceof TabActivity ) {
36
+ final TabHost tabHost = ((TabActivity)currentActivity).getTabHost();
37
+
38
+ String tabName = args[0];
39
+ if( tabName.length() == 1 ) {
40
+ char c = tabName.charAt(0);
41
+ if( c >= '0' && c <= '9' ) {
42
+ int index = (c - '0') - 1;
43
+ tabHost.setCurrentTab( index );
44
+ return Result.successResult();
45
+ }
46
+ }
47
+
48
+ List<String> foundTabs = new LinkedList<String>();
49
+ int numberOfTabs = ((TabActivity)currentActivity).getTabWidget().getTabCount();
50
+ Log.i("SelectTab", "numberOfTabs: " + numberOfTabs);
51
+
52
+ String resultMessage = null;
53
+ int tabIndex;
54
+ FIND_TAB:
55
+ for( tabIndex = 0; tabIndex < numberOfTabs; tabIndex++ ) {
56
+ if( tabName.equals( tabHost.getTag(tabIndex) ) ) {
57
+ resultMessage = "Selected the tab by tag";
58
+ break;
59
+ }
60
+
61
+ String currentTab;
62
+ View view = tabHost.getTabWidget().getChildTabViewAt(tabIndex);
63
+ if( view instanceof TextView ) {
64
+ currentTab = ((TextView)view).getText().toString();
65
+ if( tabName.equals(currentTab) ) {
66
+ resultMessage = "Selected the tab by text view";
67
+ break;
68
+ }
69
+ foundTabs.add(currentTab);
70
+ } else {
71
+ Log.i("SelectTab", "current tab view: " + view.getId() + ", " + view.getClass().getSimpleName());
72
+ List<TextView> textViews = InstrumentationBackend.solo.getCurrentTextViews(view);
73
+ for( TextView textView : textViews ) {
74
+ currentTab = textView.getText().toString();
75
+ Log.i("SelectTab", " child text view: " + currentTab);
76
+ if( tabName.equals(currentTab) ) {
77
+ resultMessage = "Selected the tab by child text view";
78
+ break FIND_TAB;
79
+ }
80
+ foundTabs.add(currentTab);
81
+ }
82
+ }
83
+ }
84
+
85
+ if( resultMessage != null ) {
86
+ final int matchingTabIndex = tabIndex;
87
+
88
+ currentActivity.runOnUiThread( new Runnable() {
89
+ @Override
90
+ public void run() {
91
+ tabHost.setCurrentTab( matchingTabIndex );
92
+ }
93
+ });
94
+
95
+ return new Result(true, resultMessage);
96
+ }
97
+
98
+ Result result = new Result(false, "Was unable to find a matching tab");
99
+ result.setExtras( foundTabs );
100
+ return result;
101
+ } else {
102
+ return new Result(false, "The current activity is not a TabActivity: " + currentActivity.getLocalClassName() );
103
+ }
104
+ }
105
+
106
+ @Override
107
+ public String key() {
108
+ return "select_tab";
109
+ }
110
+ }