calabash-android 0.2.11 → 0.2.12

Sign up to get free protection for your applications and to get access to all the features.
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
+ }