calabash-android 0.4.22.pre4 → 0.5.0.pre1

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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/bin/calabash-android +66 -45
  3. data/lib/calabash-android/calabash_steps.rb +0 -8
  4. data/lib/calabash-android/canned_steps.md +2 -30
  5. data/lib/calabash-android/lib/TestServer.apk +0 -0
  6. data/lib/calabash-android/operations.rb +199 -23
  7. data/lib/calabash-android/removed_actions.txt +52 -0
  8. data/lib/calabash-android/steps/assert_steps.rb +9 -24
  9. data/lib/calabash-android/steps/check_box_steps.rb +2 -2
  10. data/lib/calabash-android/steps/context_menu_steps.rb +11 -4
  11. data/lib/calabash-android/steps/date_picker_steps.rb +2 -2
  12. data/lib/calabash-android/steps/enter_text_steps.rb +13 -13
  13. data/lib/calabash-android/steps/l10n_steps.rb +5 -5
  14. data/lib/calabash-android/steps/map_steps.rb +12 -12
  15. data/lib/calabash-android/steps/navigation_steps.rb +12 -12
  16. data/lib/calabash-android/steps/press_button_steps.rb +16 -12
  17. data/lib/calabash-android/steps/progress_steps.rb +19 -22
  18. data/lib/calabash-android/steps/search_steps.rb +2 -2
  19. data/lib/calabash-android/steps/spinner_steps.rb +10 -2
  20. data/lib/calabash-android/steps/time_picker_steps.rb +2 -2
  21. data/lib/calabash-android/version.rb +1 -1
  22. data/lib/calabash-android/wait_helpers.rb +29 -1
  23. data/test-server/instrumentation-backend/antlr/UIQuery.g +11 -11
  24. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/CalabashInstrumentationTestRunner.java +3 -2
  25. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/InstrumentationBackend.java +9 -4
  26. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/Result.java +16 -12
  27. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/application/Backdoor.java +55 -0
  28. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/gestures/LongPressCoordinate.java +19 -2
  29. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/JavaScriptExecuter.java +105 -0
  30. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/QueryHelper.java +27 -30
  31. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ViewMapper.java +27 -18
  32. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/ComparisonOperator.java +7 -1
  33. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTPredicate.java +16 -4
  34. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTWith.java +13 -6
  35. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryUtils.java +94 -48
  36. metadata +5 -59
  37. data/lib/calabash-android/steps/additions_manual_steps.rb +0 -11
  38. data/lib/calabash-android/steps/app_steps.rb +0 -10
  39. data/lib/calabash-android/steps/list_steps.rb +0 -41
  40. data/lib/calabash-android/steps/rotation_steps.rb +0 -7
  41. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/button/PressButtonNumber.java +0 -22
  42. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/button/PressButtonText.java +0 -27
  43. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/button/PressImageButtonDescription.java +0 -39
  44. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/button/PressImageButtonNumber.java +0 -22
  45. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/button/WaitForButton.java +0 -47
  46. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/checkbox/ToggleCheckboxNumber.java +0 -22
  47. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/contextmenu/LongPressText.java +0 -22
  48. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/contextmenu/LongPressTextAndSelectFromMenuById.java +0 -26
  49. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/contextmenu/LongPressTextAndSelectFromMenuByIndex.java +0 -22
  50. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/contextmenu/LongPressTextAndSelectFromMenuByText.java +0 -26
  51. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/helpers/InspectCurrentDialog.java +0 -76
  52. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/GetListData.java +0 -85
  53. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/GetListItemProperties.java +0 -194
  54. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/GetListItemText.java +0 -136
  55. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/LongPressListItems.java +0 -22
  56. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/PressListItems.java +0 -22
  57. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/scrolling/ScrollDown.java +0 -22
  58. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/scrolling/ScrollUp.java +0 -22
  59. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/search/EnterQueryByIndex.java +0 -29
  60. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/softkey/SelectFromMenuByText.java +0 -24
  61. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/spinner/GetSelectedSpinnerItemText.java +0 -36
  62. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/spinner/SelectSpinnerItemByContentDescription.java +0 -43
  63. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/AssertGridViewContainsNoDuplicates.java +0 -72
  64. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/AssertText.java +0 -31
  65. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/AssertTextOfSpecificTextViewByContentDescription.java +0 -32
  66. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/ClearTextById.java +0 -30
  67. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/ClearTextByIndex.java +0 -22
  68. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/ClearTextFieldByContentDescription.java +0 -33
  69. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/ClickOnText.java +0 -22
  70. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/EnterTextByContentDescription.java +0 -32
  71. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/EnterTextById.java +0 -33
  72. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/EnterTextByIndex.java +0 -22
  73. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/GetTextById.java +0 -42
  74. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/AssertViewProperty.java +0 -141
  75. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/ClickOnViewByDescription.java +0 -46
  76. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/ClickOnViewById.java +0 -56
  77. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/GetViewProperty.java +0 -101
  78. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/HasView.java +0 -31
  79. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/IsEnabled.java +0 -30
  80. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/LongPressOnViewById.java +0 -34
  81. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/Press.java +0 -89
  82. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/SelectTab.java +0 -110
  83. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/Wait.java +0 -24
  84. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForDialogClose.java +0 -21
  85. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForProgress.java +0 -47
  86. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForScreen.java +0 -54
  87. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForTab.java +0 -108
  88. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForText.java +0 -37
  89. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForView.java +0 -43
  90. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForViewById.java +0 -58
  91. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/JavaScriptOperation.java +0 -44
  92. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/PressByCssSelector.java +0 -70
  93. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/SetText.java +0 -64
@@ -11,58 +11,55 @@ import java.util.Map;
11
11
  import sh.calaba.instrumentationbackend.InstrumentationBackend;
12
12
  import sh.calaba.instrumentationbackend.actions.webview.CalabashChromeClient.WebFuture;
13
13
  import sh.calaba.org.codehaus.jackson.map.ObjectMapper;
14
+
15
+ import android.os.Build;
14
16
  import android.util.Log;
15
17
  import android.webkit.ValueCallback;
16
18
  import android.webkit.WebView;
17
19
 
18
20
  public class QueryHelper {
19
21
 
20
-
22
+
21
23
  @SuppressWarnings("unchecked")
22
24
  public static Map<String, Object> findFirstVisibleRectangle(List<HashMap<String,Object>> elements) {
23
- return (Map<String, Object>)findFirstVisibleElement(elements).get("rect");
25
+ return (Map<String, Object>)findFirstVisibleElement(elements).get("rect");
24
26
  }
25
-
27
+
26
28
  public static Map<String, Object> findFirstVisibleElement(List<HashMap<String,Object>> elements) {
27
29
  //TODO: Should do something more intelligent
28
- return (Map<String, Object>)elements.get(0);
30
+ return (Map<String, Object>)elements.get(0);
29
31
  }
30
-
32
+
31
33
  public static float translateCoordToScreen(int offset, float scale, Object point) {
32
34
  return offset + ((Number)point).floatValue() *scale;
33
35
  }
34
-
35
- public static Map<String, Object> translateRectToScreenCoordinates(WebView webView, Map<String, Object> rectangle) {
36
+
37
+ public static Map<String, Integer> translateRectToScreenCoordinates(WebView webView, Map<String, Integer> rectangle) {
36
38
  try {
37
-
38
39
  float scale = webView.getScale();
39
40
 
40
41
  int[] webviewLocation = new int[2];
41
42
  webView.getLocationOnScreen(webviewLocation);
42
43
  //center_x, center_y
43
44
  //left, top, width, height
44
- float center_x = translateCoordToScreen(webviewLocation[0], scale,
45
- rectangle.get("center_x"));
46
- float center_y = translateCoordToScreen(webviewLocation[1], scale,
47
- rectangle.get("center_y"));
48
-
49
- float x = translateCoordToScreen(webviewLocation[0], scale, rectangle.get("left"));
50
- float y = translateCoordToScreen(webviewLocation[0], scale, rectangle.get("top"));
51
- Map<String,Object> result = new HashMap<String, Object>(rectangle);
52
-
53
- result.put("x",x);
54
- result.put("y",y);
55
- result.put("center_x",center_x);
56
- result.put("center_y",center_y);
57
-
45
+ int center_x = (int)translateCoordToScreen(webviewLocation[0], scale, rectangle.get("center_x"));
46
+ int center_y = (int)translateCoordToScreen(webviewLocation[1], scale, rectangle.get("center_y"));
47
+
48
+ int x = (int)translateCoordToScreen(webviewLocation[0], scale, rectangle.get("left"));
49
+ int y = (int)translateCoordToScreen(webviewLocation[0], scale, rectangle.get("top"));
50
+ Map<String,Integer> result = new HashMap<String, Integer>(rectangle);
51
+
52
+ result.put("x", x);
53
+ result.put("y", y);
54
+ result.put("center_x", center_x);
55
+ result.put("center_y", center_y);
56
+
58
57
  return result;
59
-
60
-
61
58
  } catch (Exception e) {
62
59
  throw new RuntimeException(e);
63
60
  }
64
61
  }
65
-
62
+
66
63
  public static String toJsonString(Object o) {
67
64
  //http://www.mkyong.com/java/how-to-convert-java-map-to-from-json-jackson/
68
65
  try {
@@ -71,7 +68,7 @@ public class QueryHelper {
71
68
  throw new RuntimeException(e);
72
69
  }
73
70
  }
74
-
71
+
75
72
 
76
73
  private static String readJavascriptFromAsset(String scriptPath) {
77
74
  StringBuffer script = new StringBuffer();
@@ -88,7 +85,7 @@ public class QueryHelper {
88
85
  }
89
86
  return script.toString();
90
87
  }
91
-
88
+
92
89
  public static WebFuture executeAsyncJavascriptInWebviews(WebView webView,
93
90
  String scriptPath, String selector, String type) {
94
91
 
@@ -99,12 +96,12 @@ public class QueryHelper {
99
96
 
100
97
  CalabashChromeClient chromeClient = CalabashChromeClient.prepareWebView(webView);
101
98
 
102
- if (android.os.Build.VERSION.SDK_INT < 19) { // Android 4.4
103
- webView.loadUrl("javascript:calabash_result = " + script + ";prompt('calabash:' + calabash_result);");
99
+ if (Build.VERSION.SDK_INT < 19) { // Android 4.4
100
+ JavaScriptExecuter javaScriptExecuter = new JavaScriptExecuter(webView);
101
+ javaScriptExecuter.executeJavaScript("calabash_result = " + script + ";prompt('calabash:' + calabash_result);");
104
102
  } else {
105
103
  chromeClient.evaluateCalabashScript(script);
106
104
  }
107
-
108
105
  return chromeClient.getResult();
109
106
  }
110
107
  }
@@ -15,17 +15,18 @@ import android.widget.TextView;
15
15
  public class ViewMapper {
16
16
 
17
17
  @SuppressWarnings({ "rawtypes", "unchecked" })
18
- public static Object extractDataFromView(View v) {
19
-
18
+ public static Object extractDataFromView(View v) {
19
+
20
20
  Map data = new HashMap();
21
- data.put("class", getClassNameForView(v));
21
+ data.put("class", getClassNameForView(v));
22
22
  data.put("description", v.toString());
23
23
  data.put("contentDescription", getContentDescriptionForView(v));
24
24
  data.put("enabled", v.isEnabled());
25
-
25
+
26
26
  data.put("id", getIdForView(v));
27
+ data.put("tag", getTagForView(v));
27
28
 
28
- Map rect = getRectForView(v);
29
+ Map<String,Integer> rect = getRectForView(v);
29
30
 
30
31
  data.put("rect", rect);
31
32
 
@@ -45,19 +46,20 @@ public class ViewMapper {
45
46
 
46
47
  }
47
48
 
48
- public static Map getRectForView(View v) {
49
- Map rect = new HashMap();
49
+ public static Map<String, Integer> getRectForView(View v) {
50
+ Map<String,Integer> rect = new HashMap<String,Integer>();
50
51
  int[] location = new int[2];
51
52
  v.getLocationOnScreen(location);
52
53
 
53
54
  rect.put("x", location[0]);
54
55
  rect.put("y", location[1]);
55
-
56
- rect.put("center_x", location[0] + v.getWidth()/2.0);
57
- rect.put("center_y", location[1] + v.getHeight()/2.0);
58
-
56
+
57
+ rect.put("center_x", (int)(location[0] + v.getWidth()/2.0));
58
+ rect.put("center_y", (int)(location[1] + v.getHeight()/2.0));
59
+
59
60
  rect.put("width", v.getWidth());
60
61
  rect.put("height", v.getHeight());
62
+
61
63
  return rect;
62
64
  }
63
65
 
@@ -82,26 +84,33 @@ public class ViewMapper {
82
84
  return id;
83
85
  }
84
86
 
87
+ public static String getTagForView(View v) {
88
+ if (v.getTag() instanceof String || v.getTag() instanceof Integer) {
89
+ return v.getTag().toString();
90
+ }
91
+ return null;
92
+ }
93
+
85
94
  @SuppressWarnings({ "unchecked", "rawtypes" })
86
95
  public static Object mapView(Object o) {
87
96
  if (o instanceof View) {
88
97
  return extractDataFromView((View) o);
89
- }
90
- else if (o instanceof Map) {
91
- Map copy = new HashMap();
98
+ }
99
+ else if (o instanceof Map) {
100
+ Map copy = new HashMap();
92
101
  for (Object e : ((Map) o).entrySet()) {
93
102
  Map.Entry entry = (Entry) e;
94
103
  Object value = entry.getValue();
95
104
  if (value instanceof View) {
96
105
  copy.put(entry.getKey(), UIQueryUtils.getId((View) value));
97
- }
106
+ }
98
107
  else {
99
108
  copy.put(entry.getKey(),entry.getValue());
100
- }
109
+ }
101
110
  }
102
-
111
+
103
112
  return copy;
104
- }
113
+ }
105
114
  else if (o instanceof CharSequence) {
106
115
  return o.toString();
107
116
  }
@@ -25,7 +25,13 @@ public enum ComparisonOperator implements UIQueryASTPredicateRelation {
25
25
  if (firstValue == secondValue) {
26
26
  return true;
27
27
  }
28
- return (firstValue != null) && firstValue.equals(secondValue);
28
+ return (firstValue != null) && firstValue.equals(secondValue);
29
+ }
30
+ },
31
+
32
+ NOTEQUAL {
33
+ public boolean areRelated(Object firstValue, Object secondValue) {
34
+ return !EQUAL.areRelated(firstValue, secondValue);
29
35
  }
30
36
  },
31
37
 
@@ -80,6 +80,16 @@ public class UIQueryASTPredicate implements UIQueryAST {
80
80
  }
81
81
  }
82
82
 
83
+ // there's no tag property for non Views, handle them all here
84
+ if (this.propertyName.equals("tag")) {
85
+ String tag = (o instanceof View ? UIQueryUtils.getTag((View) o) : null);
86
+ if (this.relation.areRelated(tag, this.valueToMatch)) {
87
+ return o;
88
+ } else {
89
+ return null;
90
+ }
91
+ }
92
+
83
93
  Method propertyAccessor = UIQueryUtils
84
94
  .hasProperty(o, this.propertyName);
85
95
  if (propertyAccessor == null) {
@@ -90,8 +100,8 @@ public class UIQueryASTPredicate implements UIQueryAST {
90
100
  if (this.relation.areRelated(value, this.valueToMatch)) {
91
101
  return o;
92
102
  } else if (this.valueToMatch instanceof String
93
- && this.relation
94
- .areRelated(value.toString(), this.valueToMatch)) {
103
+ && value != null
104
+ && this.relation.areRelated(value.toString(), this.valueToMatch)) {
95
105
  return o;
96
106
  } else {
97
107
  return null;
@@ -109,7 +119,7 @@ public class UIQueryASTPredicate implements UIQueryAST {
109
119
  return new UIQueryASTPredicate(prop.getText(),
110
120
  UIQueryASTPredicate.parseRelation(rel),
111
121
  UIQueryUtils.parseValue(val));
112
-
122
+
113
123
  }
114
124
 
115
125
  private static UIQueryASTPredicateRelation parseRelation(CommonTree rel) {
@@ -120,7 +130,7 @@ public class UIQueryASTPredicate implements UIQueryAST {
120
130
  caseSensitive = false;
121
131
  relText = relText.substring(0,relText.length() - CASE_INSENSITIVE_SPEC.length());
122
132
  }
123
-
133
+
124
134
  if ("BEGINSWITH".equals(relText)) {
125
135
  return new BeginsWithRelation(caseSensitive);
126
136
  } else if ("ENDSWITH".equals(relText)) {
@@ -139,6 +149,8 @@ public class UIQueryASTPredicate implements UIQueryAST {
139
149
  return ComparisonOperator.GREATERTHAN;
140
150
  } else if (">=".equals(relText)) {
141
151
  return ComparisonOperator.GREATERTHANOREQUAL;
152
+ } else if ("!=".equals(relText) || "<>".equals(relText)) {
153
+ return ComparisonOperator.NOTEQUAL;
142
154
  } else {
143
155
  throw new IllegalStateException("Unsupported Relation: " + relText);
144
156
  }
@@ -63,13 +63,13 @@ public class UIQueryASTWith implements UIQueryAST {
63
63
  }
64
64
 
65
65
  }
66
- List visibilityFilteredResults = visibility.evaluateWithViews(futureResult, direction,
67
- visibility);
68
- return new PartialFutureList(visibilityFilteredResults);
66
+
67
+ return new PartialFutureList(futureResult);
69
68
  }
70
69
  });
71
70
 
72
- List processedResult = new ArrayList(queryResult.size());
71
+ final List processedResult = new ArrayList(queryResult.size());
72
+
73
73
  for (Object o : queryResult) {
74
74
  if (o instanceof Map) {
75
75
  Map m = (Map) o;
@@ -85,9 +85,16 @@ public class UIQueryASTWith implements UIQueryAST {
85
85
  processedResult.add(o);
86
86
  }
87
87
  }
88
- return processedResult;
89
-
90
88
 
89
+ List visibilityFilteredResults = (List) UIQueryUtils.evaluateSyncInMainThread(new Callable() {
90
+
91
+ @Override
92
+ public Object call() throws Exception {
93
+ return visibility.evaluateWithViews(processedResult, direction, visibility);
94
+ }
95
+ });
96
+
97
+ return visibilityFilteredResults;
91
98
  }
92
99
 
93
100
  private boolean isDomQuery() {
@@ -30,6 +30,7 @@ import sh.calaba.org.codehaus.jackson.type.TypeReference;
30
30
  import android.text.InputType;
31
31
  import android.util.Log;
32
32
  import android.view.View;
33
+ import android.view.ViewParent;
33
34
  import android.webkit.WebView;
34
35
  import android.widget.Button;
35
36
  import android.widget.CheckBox;
@@ -47,7 +48,7 @@ public class UIQueryUtils {
47
48
 
48
49
  @SuppressWarnings({ "unchecked", "rawtypes" })
49
50
  public static List subviews(Object o) {
50
-
51
+
51
52
  try {
52
53
  Method getChild = o.getClass().getMethod("getChildAt", int.class);
53
54
  getChild.setAccessible(true);
@@ -74,15 +75,15 @@ public class UIQueryUtils {
74
75
 
75
76
  @SuppressWarnings({ "rawtypes" })
76
77
  public static Future webViewSubViews(WebView o) {
77
-
78
+
78
79
  Log.i("Calabash", "About to webViewSubViews");
79
-
80
+
80
81
 
81
82
  WebFuture controls = QueryHelper.executeAsyncJavascriptInWebviews(o,
82
83
  "calabash.js", "input,button","css");
83
-
84
+
84
85
  return controls;
85
-
86
+
86
87
  }
87
88
 
88
89
  @SuppressWarnings({ "unchecked", "rawtypes" })
@@ -168,18 +169,44 @@ public class UIQueryUtils {
168
169
  }
169
170
 
170
171
  public static boolean isVisible(Object v) {
171
- if (!(v instanceof View)) {
172
- return true;
173
- }
174
- View view = (View) v;
172
+ if (v instanceof Map) {
173
+ Map map = (Map)v;
174
+ Map<String,Integer> viewRect = (Map<String,Integer>)map.get("rect");
175
+ Map<String,Integer> parentViewRec = ViewMapper.getRectForView((WebView)map.get("webView"));
175
176
 
176
- if (view.getHeight() == 0 || view.getWidth() == 0) {
177
- return false;
178
- }
177
+ return isViewSufficientlyShown(viewRect, parentViewRec);
178
+ } else if (v instanceof View) {
179
+ View view = (View) v;
179
180
 
180
- return view.isShown() && viewFetcher.isViewSufficientlyShown(view);
181
+ if (view.getHeight() == 0 || view.getWidth() == 0) {
182
+ return false;
183
+ }
184
+
185
+ return view.isShown() && isViewSufficientlyShown(view);
186
+ } else {
187
+ return true;
188
+ }
181
189
  }
182
190
 
191
+ public static boolean isViewSufficientlyShown(Map<String,Integer> viewRect, Map<String,Integer> parentViewRect) {
192
+ int centerX = viewRect.get("center_x");
193
+ int centerY = viewRect.get("center_y");
194
+
195
+ int parentX = parentViewRect.get("x");
196
+ int parentY = parentViewRect.get("y");
197
+ int parentWidth = parentViewRect.get("width");
198
+ int parentHeight = parentViewRect.get("height");
199
+ int windowWidth = parentX + parentWidth;
200
+ int windowHeight = parentY + parentHeight;
201
+
202
+ return (windowWidth > centerX && parentX < centerX &&
203
+ windowHeight > centerY && parentY < centerY);
204
+ }
205
+
206
+ public static boolean isViewSufficientlyShown(View view) {
207
+ return isViewSufficientlyShown(view, view.getParent());
208
+ }
209
+
183
210
  public static boolean isClickable(Object v) {
184
211
  if (!(v instanceof View)) {
185
212
  return true;
@@ -193,6 +220,10 @@ public class UIQueryUtils {
193
220
  return ViewMapper.getIdForView(view);
194
221
  }
195
222
 
223
+ public static String getTag(View view) {
224
+ return ViewMapper.getTagForView(view);
225
+ }
226
+
196
227
  @SuppressWarnings({ "rawtypes", "unchecked" })
197
228
  public static Future evaluateAsyncInMainThread(final Callable callable) throws Exception {
198
229
 
@@ -246,8 +277,8 @@ public class UIQueryUtils {
246
277
  new TypeReference<List<HashMap<String, Object>>>() {
247
278
  });
248
279
  for (Map<String, Object> data : parsedResult) {
249
- Map<String, Object> rect = (Map<String, Object>) data.get("rect");
250
- Map<String, Object> updatedRect = QueryHelper.translateRectToScreenCoordinates(webView, rect);
280
+ Map<String, Integer> rect = (Map<String, Integer>) data.get("rect");
281
+ Map<String, Integer> updatedRect = QueryHelper.translateRectToScreenCoordinates(webView, rect);
251
282
  data.put("rect", updatedRect);
252
283
  data.put("webView", webView);
253
284
  }
@@ -301,7 +332,7 @@ public class UIQueryUtils {
301
332
  }
302
333
 
303
334
  /*
304
- *
335
+ *
305
336
  * {"rect"=>{"x"=>0, "y"=>0, "width"=>768, "height"=>1024},
306
337
  * "hit-point"=>{"x"=>384, "y"=>512}, "id"=>"", "action"=>false,
307
338
  * "enabled"=>1, "visible"=>1, "value"=>nil, "type"=>"[object UIAWindow]",
@@ -343,7 +374,7 @@ public class UIQueryUtils {
343
374
  childrenArray.add(dumpRecursively(serializedChild,
344
375
  childrenList));
345
376
  }
346
-
377
+
347
378
  }
348
379
 
349
380
  parentView.put("children", childrenArray);
@@ -373,7 +404,7 @@ public class UIQueryUtils {
373
404
  }
374
405
 
375
406
  /*
376
- *
407
+ *
377
408
  "enabled" => true,
378
409
  "visible" => true,
379
410
  "children" => [],
@@ -412,16 +443,16 @@ public class UIQueryUtils {
412
443
  if (viewOrMap == null) {
413
444
  return null;
414
445
  }
415
-
416
- if (viewOrMap instanceof Map)
446
+
447
+ if (viewOrMap instanceof Map)
417
448
  {
418
- Map map = (Map) viewOrMap;
449
+ Map map = (Map) viewOrMap;
419
450
  map.put("el", map);
420
451
 
421
452
  Map rect = (Map) map.get("rect");
422
453
  Map hitPoint = extractHitPointFromRect(rect);
423
-
424
- map.put("hit-point", hitPoint);
454
+
455
+ map.put("hit-point", hitPoint);
425
456
  map.put("enabled", true);
426
457
  map.put("visible", true);
427
458
  map.put("value", null);
@@ -430,36 +461,36 @@ public class UIQueryUtils {
430
461
  map.put("label", null);
431
462
  map.put("children", Collections.EMPTY_LIST);
432
463
  String html = (String)map.get("html");
433
- String nodeName = (String) map.get("nodeName");
434
- if (nodeName != null && nodeName.toLowerCase().equals("input")) {
464
+ String nodeName = (String) map.get("nodeName");
465
+ if (nodeName != null && nodeName.toLowerCase().equals("input")) {
435
466
  String domType = extractDomType(html);
436
467
  if (isDomPasswordType(domType)) {
437
468
  map.put("entry_types", Collections.singletonList("password"));
438
469
  }
439
470
  else if (isDomTextType(domType)) {
440
471
  map.put("entry_types", Collections.singletonList("text"));
441
- }
472
+ }
442
473
  else {
443
- map.put("entry_types", Collections.emptyList());
444
- }
474
+ map.put("entry_types", Collections.emptyList());
475
+ }
445
476
  map.put("value", extractAttribute(html, "value"));
446
477
  map.put("type", "dom");
447
478
  map.put("name", extractAttribute(html, "name"));
448
479
  map.put("label", extractAttribute(html, "title"));
449
- }
450
-
451
- return map;
452
-
480
+ }
481
+
482
+ return map;
483
+
453
484
  }
454
- else
485
+ else
455
486
  {
456
487
  Map m = new HashMap();
457
-
488
+
458
489
  View view = (View) viewOrMap;
459
490
  m.put("id", getId(view));
460
491
  m.put("el", view);
461
492
 
462
- Map rect = ViewMapper.getRectForView(view);
493
+ Map<String,Integer> rect = ViewMapper.getRectForView(view);
463
494
  Map hitPoint = extractHitPointFromRect(rect);
464
495
 
465
496
  m.put("rect", rect);
@@ -472,14 +503,29 @@ public class UIQueryUtils {
472
503
  m.put("type", ViewMapper.getClassNameForView(view));
473
504
  m.put("name", getNameForView(view));
474
505
  m.put("label", ViewMapper.getContentDescriptionForView(view));
475
- return m;
506
+ return m;
476
507
  }
477
508
 
478
-
479
509
 
480
-
510
+
511
+
481
512
  }
482
513
 
514
+ private static boolean isViewSufficientlyShown(View view, ViewParent viewParent) {
515
+ if (!(viewParent instanceof View)) return true;
516
+
517
+ View parent = (View)viewParent;
518
+
519
+ if (view.equals(parent) || parent == null) {
520
+ return true;
521
+ }
522
+
523
+ Map<String,Integer> viewRect = ViewMapper.getRectForView(view);
524
+ Map<String,Integer> parentRect = ViewMapper.getRectForView(parent);
525
+
526
+ return isViewSufficientlyShown(viewRect, parentRect) && isViewSufficientlyShown(view, parent.getParent());
527
+ }
528
+
483
529
  private static boolean isDomTextType(String domType) {
484
530
  if (domType == null) {
485
531
  return true;
@@ -487,25 +533,25 @@ public class UIQueryUtils {
487
533
  return DOM_TEXT_TYPES.contains(domType);
488
534
  }
489
535
 
490
- private static boolean isDomPasswordType(String domType) {
536
+ private static boolean isDomPasswordType(String domType) {
491
537
  return "password".equalsIgnoreCase(domType);
492
538
  }
493
539
 
494
540
  // naive implementation only works for (valid) input tags
495
541
  public static String extractDomType(String input) {
496
- return extractAttribute(input, "type");
542
+ return extractAttribute(input, "type");
497
543
  }
498
-
544
+
499
545
  public static String extractAttribute(String input, String attribute) {
500
546
  String[] split = input.split(attribute+"=");
501
547
  if (split.length == 1) {
502
- split = input.split(attribute+" =");
548
+ split = input.split(attribute+" =");
503
549
  }
504
550
  if (split.length > 1) {
505
551
  String lastPart = split[1];
506
552
  if (lastPart == null) {
507
- return null;
508
- }
553
+ return null;
554
+ }
509
555
  if (lastPart.charAt(0) == '"' || lastPart.charAt(0) == '\'') {
510
556
  int endIndex = -1;
511
557
  for (int i=1;i<lastPart.length();i++) {
@@ -514,15 +560,15 @@ public class UIQueryUtils {
514
560
  break;
515
561
  }
516
562
  }
517
-
563
+
518
564
  if (endIndex > 0) {
519
565
  return lastPart.substring(1,endIndex);
520
566
  }
521
-
522
- }
567
+
568
+ }
523
569
  }
524
570
  return null;
525
-
571
+
526
572
  }
527
573
 
528
574