calabash-android 0.4.6 → 0.4.7.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (20) hide show
  1. checksums.yaml +7 -0
  2. data/calabash-android.gemspec +1 -0
  3. data/doc/calabash-android-help.txt +6 -2
  4. data/lib/calabash-android/lib/TestServer.apk +0 -0
  5. data/lib/calabash-android/operations.rb +86 -14
  6. data/lib/calabash-android/steps/navigation_steps.rb +0 -0
  7. data/lib/calabash-android/version.rb +1 -1
  8. data/test-server/instrumentation-backend/.classpath +1 -0
  9. data/test-server/instrumentation-backend/project.properties +1 -1
  10. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/FranklyResult.java +2 -9
  11. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/HttpServer.java +41 -7
  12. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/ViewDump.java +50 -0
  13. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/softkey/PressMenu.java +0 -0
  14. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/softkey/SelectFromMenuByText.java +0 -0
  15. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/json/JSONUtils.java +18 -0
  16. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/Query.java +1 -2
  17. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ViewMapper.java +42 -24
  18. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/PartialFutureList.java +2 -2
  19. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryUtils.java +222 -9
  20. metadata +37 -35
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 64cf7a26d360f3483fdc88f29c24f82d995e4f59
4
+ data.tar.gz: b52dacd37f6444b16af0fa62c8453ea5b9e13fc9
5
+ SHA512:
6
+ metadata.gz: 07241d9e4d9b291bd9ccdd277badfbde8fc7ad774cc7491a8d960762d72e19b41985950b1d3554d693debca272373b7a0783d118461067711dc70b40fb91da33
7
+ data.tar.gz: 5988a802d01af1d3144c7ff391480dc28c34957b23081f0686a80789f9532dab480a258f0d7a74df9cbf7fc9beb5e86b80773c048dc41c5abd4879e5afb683a4
@@ -21,5 +21,6 @@ Gem::Specification.new do |s|
21
21
  s.add_dependency( "slowhandcuke" )
22
22
  s.add_dependency( "rubyzip" )
23
23
  s.add_dependency( "awesome_print" )
24
+ s.add_dependency( 'httpclient', '~> 2.3.2')
24
25
 
25
26
  end
@@ -5,6 +5,7 @@ Usage: calabash-android <command-name> [parameters] [options]
5
5
  setup
6
6
  build
7
7
  run [parameters]
8
+ console
8
9
  version
9
10
 
10
11
  Commands:
@@ -15,11 +16,14 @@ Usage: calabash-android <command-name> [parameters] [options]
15
16
  setup sets up a non-default keystore to use with this test project.
16
17
 
17
18
  build builds the test server that will be used when testing the app.
18
-
19
+
19
20
  run runs Cucumber in the current folder with the enviroment needed.
20
21
 
22
+ console opens an interactive irb shell. Here you can send commands
23
+ to the app and query for UI elements.
24
+
21
25
  version prints the gem version
22
26
 
23
27
 
24
28
  Options:
25
- -v, --verbose Turns on verbose logging
29
+ -v, --verbose Turns on verbose logging
@@ -1,3 +1,4 @@
1
+ require 'httpclient'
1
2
  require 'json'
2
3
  require 'net/http'
3
4
  require 'open-uri'
@@ -254,15 +255,86 @@ module Operations
254
255
 
255
256
  def http(path, data = {}, options = {})
256
257
  begin
257
- http = Net::HTTP.new "127.0.0.1", @server_port
258
- http.open_timeout = options[:open_timeout] if options[:open_timeout]
259
- http.read_timeout = options[:read_timeout] if options[:read_timeout]
260
- resp = http.post(path, "#{data.to_json}", {"Content-Type" => "application/json;charset=utf-8"})
261
- resp.body
262
- rescue EOFError => e
263
- log "It looks like your app is no longer running. \nIt could be because of a crash or because your test script shut it down."
264
- raise e
258
+
259
+ configure_http(@http, options)
260
+ make_http_request(
261
+ :method => :post,
262
+ :body => data.to_json,
263
+ :uri => url_for(path),
264
+ :header => {"Content-Type" => "application/json;charset=utf-8"})
265
+
266
+ rescue HTTPClient::TimeoutError,
267
+ HTTPClient::KeepAliveDisconnected,
268
+ Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ECONNABORTED,
269
+ Errno::ETIMEDOUT => e
270
+ log "It looks like your app is no longer running. \nIt could be because of a crash or because your test script shut it down."
271
+ raise e
272
+ end
273
+ end
274
+
275
+ def set_http(http)
276
+ @http = http
277
+ end
278
+
279
+ def url_for(method)
280
+ url = URI.parse(ENV['DEVICE_ENDPOINT']|| "http://127.0.0.1:#{@server_port}")
281
+ path = url.path
282
+ if path.end_with? "/"
283
+ path = "#{path}#{method}"
284
+ else
285
+ path = "#{path}/#{method}"
265
286
  end
287
+ url.path = path
288
+ url
289
+ end
290
+
291
+
292
+
293
+ def make_http_request(options)
294
+ body = nil
295
+ begin
296
+ unless @http
297
+ @http = init_request(options)
298
+ end
299
+ header = options[:header] || {}
300
+ header["Content-Type"] = "application/json;charset=utf-8"
301
+ options[:header] = header
302
+
303
+ if options[:method] == :post
304
+ body = @http.post(options[:uri], options).body
305
+ else
306
+ body = @http.get(options[:uri], options).body
307
+ end
308
+ rescue Exception => e
309
+ if @http
310
+ @http.reset_all
311
+ @http=nil
312
+ end
313
+ raise e
314
+ end
315
+ body
316
+ end
317
+
318
+ def init_request(options)
319
+ http = HTTPClient.new
320
+ configure_http(http, options)
321
+ end
322
+
323
+ def configure_http(http, options)
324
+ return unless http
325
+ http.connect_timeout = options[:open_timeout] || 15
326
+ http.send_timeout = options[:send_timeout] || 15
327
+ http.receive_timeout = options[:read_timeout] || 15
328
+ if options.has_key?(:debug) && options[:debug]
329
+ http.debug_dev= $stdout
330
+ else
331
+ if ENV['DEBUG_HTTP'] and (ENV['DEBUG_HTTP'] != '0')
332
+ http.debug_dev = $stdout
333
+ else
334
+ http.debug_dev= nil
335
+ end
336
+ end
337
+ http
266
338
  end
267
339
 
268
340
  def screenshot(options={:prefix => nil, :name => nil})
@@ -493,8 +565,8 @@ module Operations
493
565
  performAction("touch_coordinate", center_x, center_y)
494
566
  end
495
567
 
496
- def http(options, data=nil)
497
- default_device.http(options, data)
568
+ def http(path, data = {}, options = {})
569
+ default_device.http(path, data, options)
498
570
  end
499
571
 
500
572
  def html(q)
@@ -620,12 +692,12 @@ module Operations
620
692
  res['results']
621
693
  end
622
694
 
623
- def url_for( verb )
624
- ni
695
+ def url_for( method )
696
+ default_device.url_for(method)
625
697
  end
626
698
 
627
- def make_http_request( url, req )
628
- ni
699
+ def make_http_request(options)
700
+ default_device.make_http_request(options)
629
701
  end
630
702
 
631
703
  end
@@ -1,5 +1,5 @@
1
1
  module Calabash
2
2
  module Android
3
- VERSION = "0.4.6"
3
+ VERSION = "0.4.7.pre1"
4
4
  end
5
5
  end
@@ -6,5 +6,6 @@
6
6
  <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
7
7
  <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
8
8
  <classpathentry kind="lib" path="libs/robotium-solo-3.6.jar"/>
9
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
9
10
  <classpathentry kind="output" path="bin/classes"/>
10
11
  </classpath>
@@ -8,4 +8,4 @@
8
8
  # project structure.
9
9
 
10
10
  # Project target.
11
- target=Google Inc.:Google APIs:16
11
+ target=Google Inc.:Google APIs:17
@@ -1,14 +1,13 @@
1
1
  package sh.calaba.instrumentationbackend;
2
2
 
3
3
  import java.io.CharArrayWriter;
4
- import java.io.IOException;
5
4
  import java.io.PrintWriter;
6
5
  import java.util.Collections;
7
6
  import java.util.HashMap;
8
7
  import java.util.List;
9
8
  import java.util.Map;
10
9
 
11
- import sh.calaba.org.codehaus.jackson.map.ObjectMapper;
10
+ import sh.calaba.instrumentationbackend.json.JSONUtils;
12
11
 
13
12
  /**
14
13
  * Represents a response in the Frankly protocol.
@@ -52,13 +51,7 @@ public class FranklyResult {
52
51
  }
53
52
 
54
53
  public String asJson() {
55
- ObjectMapper mapper = new ObjectMapper();
56
-
57
- try {
58
- return mapper.writeValueAsString(asMap());
59
- } catch (IOException e) {
60
- throw new RuntimeException("Could not convert result to json", e);
61
- }
54
+ return JSONUtils.asJson(asMap());
62
55
  }
63
56
 
64
57
  public Map<String,Object> asMap()
@@ -3,7 +3,6 @@ package sh.calaba.instrumentationbackend.actions;
3
3
  import java.io.ByteArrayInputStream;
4
4
  import java.io.ByteArrayOutputStream;
5
5
  import java.io.File;
6
- import java.io.IOException;
7
6
  import java.io.PrintWriter;
8
7
  import java.io.StringWriter;
9
8
  import java.util.List;
@@ -17,8 +16,8 @@ import sh.calaba.instrumentationbackend.Command;
17
16
  import sh.calaba.instrumentationbackend.FranklyResult;
18
17
  import sh.calaba.instrumentationbackend.InstrumentationBackend;
19
18
  import sh.calaba.instrumentationbackend.Result;
19
+ import sh.calaba.instrumentationbackend.json.JSONUtils;
20
20
  import sh.calaba.instrumentationbackend.query.Query;
21
- import sh.calaba.instrumentationbackend.query.QueryResult;
22
21
  import sh.calaba.org.codehaus.jackson.map.DeserializationConfig.Feature;
23
22
  import sh.calaba.org.codehaus.jackson.map.ObjectMapper;
24
23
  import android.graphics.Bitmap;
@@ -61,14 +60,50 @@ public class HttpServer extends NanoHTTPD {
61
60
  super(testServerPort, new File("/"));
62
61
  }
63
62
 
64
- @SuppressWarnings("rawtypes")
63
+ @SuppressWarnings({ "rawtypes", "unchecked" })
65
64
  public Response serve(String uri, String method, Properties header,
66
65
  Properties params, Properties files) {
67
66
  System.out.println("URI: " + uri);
68
67
  if (uri.endsWith("/ping")) {
69
68
  return new NanoHTTPD.Response(HTTP_OK, MIME_HTML, "pong");
70
69
 
71
- } else if (uri.endsWith("/map")) {
70
+ }
71
+ else if (uri.endsWith("/dump")) {
72
+ FranklyResult errorResult = null;
73
+ try {
74
+
75
+
76
+ String json = params.getProperty("json");
77
+
78
+
79
+ if (json == null)
80
+ {
81
+ Map<?,?> dumpTree = new ViewDump().dumpWithoutElements();
82
+ return new NanoHTTPD.Response(HTTP_OK, "application/json;charset=utf-8", JSONUtils.asJson(dumpTree));
83
+ }
84
+ else
85
+ {
86
+ ObjectMapper mapper = new ObjectMapper();
87
+ Map dumpSpec = mapper.readValue(json, Map.class);
88
+
89
+ List<Integer> path = (List<Integer>) dumpSpec.get("path");
90
+ if (path == null)
91
+ {
92
+ Map<?,?> dumpTree = new ViewDump().dumpWithoutElements();
93
+ return new NanoHTTPD.Response(HTTP_OK, "application/json;charset=utf-8", JSONUtils.asJson(dumpTree));
94
+ }
95
+ Map<?,?> dumpTree = new ViewDump().dumpPathWithoutElements(path);
96
+ return new NanoHTTPD.Response(HTTP_OK, "application/json;charset=utf-8", JSONUtils.asJson(dumpTree));
97
+ }
98
+
99
+
100
+ } catch (Exception e ) {
101
+ e.printStackTrace();
102
+ errorResult = FranklyResult.fromThrowable(e);
103
+ }
104
+ return new NanoHTTPD.Response(HTTP_INTERNALERROR, "application/json;charset=utf-8", errorResult.asJson());
105
+ }
106
+ else if (uri.endsWith("/map")) {
72
107
  FranklyResult errorResult = null;
73
108
  try {
74
109
  String commandString = params.getProperty("json");
@@ -77,12 +112,11 @@ public class HttpServer extends NanoHTTPD {
77
112
 
78
113
  String uiQuery = (String) command.get("query");
79
114
  Map op = (Map) command.get("operation");
115
+ @SuppressWarnings("unused") //TODO: support other methods, e.g., flash
80
116
  String methodName = (String) op.get("method_name");
81
117
  List arguments = (List) op.get("arguments");
82
118
 
83
- //For now we only support query and query_all
84
- //query_all includes also invisible views, while query filters them
85
- boolean includeInVisible = "query_all".equals(methodName);
119
+ //For now we only support query
86
120
 
87
121
 
88
122
  List queryResult = new Query(uiQuery,arguments).executeQuery();
@@ -0,0 +1,50 @@
1
+ package sh.calaba.instrumentationbackend.actions;
2
+
3
+ import java.util.ArrayList;
4
+ import java.util.List;
5
+ import java.util.Map;
6
+ import java.util.concurrent.Callable;
7
+ import java.util.concurrent.atomic.AtomicReference;
8
+
9
+ import sh.calaba.instrumentationbackend.query.ast.UIQueryUtils;
10
+
11
+ @SuppressWarnings({ "rawtypes", "unchecked" })
12
+ public class ViewDump {
13
+
14
+ public Map<?,?> dumpWithoutElements() {
15
+ Map<?, ?> dumpTree = (Map) UIQueryUtils.evaluateSyncInMainThread(new Callable() {
16
+ public Object call() throws Exception {
17
+ return UIQueryUtils.dump();
18
+ }
19
+ });
20
+
21
+ return sameTreeWithoutElements(dumpTree);
22
+
23
+ }
24
+
25
+
26
+ public Map<?,?> dumpPathWithoutElements(final List<Integer> path) {
27
+ final AtomicReference<List<Integer>> ref = new AtomicReference<List<Integer>>(path);
28
+ Map<?, ?> dumpTree = (Map) UIQueryUtils.evaluateSyncInMainThread(new Callable() {
29
+ public Object call() throws Exception {
30
+ return UIQueryUtils.dumpByPath(ref.get());
31
+ }
32
+ });
33
+
34
+ return UIQueryUtils.mapWithElAsNull(dumpTree);
35
+ }
36
+
37
+
38
+ private Map<?, ?> sameTreeWithoutElements(Map<?, ?> dump) {
39
+ Map node = UIQueryUtils.mapWithElAsNull(dump);
40
+ List<Map> nodeChildren = (List<Map>) node.get("children");
41
+ List<Map> childrenNoEl = new ArrayList<Map>(nodeChildren.size());
42
+ for (Map child : nodeChildren) {
43
+ childrenNoEl.add(sameTreeWithoutElements(child));
44
+ }
45
+ node.put("children",childrenNoEl);
46
+ return node;
47
+ }
48
+
49
+
50
+ }
@@ -0,0 +1,18 @@
1
+ package sh.calaba.instrumentationbackend.json;
2
+
3
+ import java.io.IOException;
4
+ import java.util.Map;
5
+
6
+ import sh.calaba.org.codehaus.jackson.map.ObjectMapper;
7
+
8
+ public class JSONUtils {
9
+
10
+ public static String asJson(Map<?,?> map) {
11
+ ObjectMapper mapper = new ObjectMapper();
12
+ try {
13
+ return mapper.writeValueAsString(map);
14
+ } catch (IOException e) {
15
+ throw new RuntimeException("Could not convert result to json: "+map, e);
16
+ }
17
+ }
18
+ }
@@ -164,8 +164,7 @@ public class Query {
164
164
  Set<View> parents = new HashSet<View>();
165
165
  for (View v : viewFetcher.getAllViews(false))
166
166
  {
167
- View parent = viewFetcher.getTopParent(v);
168
- System.out.println(parent);
167
+ View parent = viewFetcher.getTopParent(v);
169
168
  parents.add(parent);
170
169
  }
171
170
  List<View> results = new ArrayList<View>();
@@ -18,33 +18,14 @@ public class ViewMapper {
18
18
  public static Object extractDataFromView(View v) {
19
19
 
20
20
  Map data = new HashMap();
21
- data.put("class", v.getClass().getName());
21
+ data.put("class", getClassNameForView(v));
22
22
  data.put("description", v.toString());
23
- CharSequence description = v.getContentDescription();
24
- data.put("contentDescription", description != null ? description.toString() : null);
23
+ data.put("contentDescription", getContentDescriptionForView(v));
25
24
  data.put("enabled", v.isEnabled());
26
- String id = null;
27
- try {
28
- id = InstrumentationBackend.solo.getCurrentActivity()
29
- .getResources().getResourceEntryName(v.getId());
30
- } catch (Resources.NotFoundException e) {
31
- System.out.println("Resource not found for " + v.getId()
32
- + ". Moving on.");
33
- }
34
- data.put("id", id);
35
-
36
- Map rect = new HashMap();
37
- int[] location = new int[2];
38
- v.getLocationOnScreen(location);
39
-
40
- rect.put("x", location[0]);
41
- rect.put("y", location[1]);
42
25
 
43
- rect.put("center_x", location[0] + v.getWidth()/2.0);
44
- rect.put("center_y", location[1] + v.getHeight()/2.0);
45
-
46
- rect.put("width", v.getWidth());
47
- rect.put("height", v.getHeight());
26
+ data.put("id", getIdForView(v));
27
+
28
+ Map rect = getRectForView(v);
48
29
 
49
30
  data.put("rect", rect);
50
31
 
@@ -64,6 +45,43 @@ public class ViewMapper {
64
45
 
65
46
  }
66
47
 
48
+ public static Map getRectForView(View v) {
49
+ Map rect = new HashMap();
50
+ int[] location = new int[2];
51
+ v.getLocationOnScreen(location);
52
+
53
+ rect.put("x", location[0]);
54
+ 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
+
59
+ rect.put("width", v.getWidth());
60
+ rect.put("height", v.getHeight());
61
+ return rect;
62
+ }
63
+
64
+ public static String getContentDescriptionForView(View v) {
65
+ CharSequence description = v.getContentDescription();
66
+ return description != null ? description.toString() : null;
67
+ }
68
+
69
+ public static String getClassNameForView(View v) {
70
+ return v.getClass().getName();
71
+ }
72
+
73
+ public static String getIdForView(View v) {
74
+ String id = null;
75
+ try {
76
+ id = InstrumentationBackend.solo.getCurrentActivity()
77
+ .getResources().getResourceEntryName(v.getId());
78
+ } catch (Resources.NotFoundException e) {
79
+ System.out.println("Resource not found for " + v.getId()
80
+ + ". Moving on.");
81
+ }
82
+ return id;
83
+ }
84
+
67
85
  @SuppressWarnings({ "unchecked", "rawtypes" })
68
86
  public static Object mapView(Object o) {
69
87
  if (o instanceof View) {
@@ -86,8 +86,8 @@ public class PartialFutureList implements Future {
86
86
  return result;
87
87
  }
88
88
 
89
- private Future extractFuture(List evaluateWithViews) {
90
- for (Object o : evaluateWithViews) {
89
+ private Future extractFuture(List list) {
90
+ for (Object o : list) {
91
91
  if (o instanceof Future) {
92
92
  return (Future) o;
93
93
  }
@@ -19,12 +19,17 @@ import org.antlr.runtime.tree.CommonTree;
19
19
  import sh.calaba.instrumentationbackend.InstrumentationBackend;
20
20
  import sh.calaba.instrumentationbackend.actions.webview.QueryHelper;
21
21
  import sh.calaba.instrumentationbackend.query.CompletedFuture;
22
+ import sh.calaba.instrumentationbackend.query.Query;
23
+ import sh.calaba.instrumentationbackend.query.ViewMapper;
22
24
  import sh.calaba.instrumentationbackend.query.antlr.UIQueryParser;
23
25
  import sh.calaba.org.codehaus.jackson.map.ObjectMapper;
24
26
  import sh.calaba.org.codehaus.jackson.type.TypeReference;
25
- import android.content.res.Resources.NotFoundException;
27
+ import android.text.InputType;
26
28
  import android.view.View;
27
29
  import android.webkit.WebView;
30
+ import android.widget.Button;
31
+ import android.widget.CheckBox;
32
+ import android.widget.TextView;
28
33
 
29
34
  public class UIQueryUtils {
30
35
 
@@ -150,7 +155,7 @@ public class UIQueryUtils {
150
155
  if (!(v instanceof View)) { return true; }
151
156
  View view = (View) v;
152
157
 
153
- if (view.getWidth() == 0 || view.getWidth() == 0) {
158
+ if (view.getHeight() == 0 || view.getWidth() == 0) {
154
159
  return false;
155
160
  }
156
161
 
@@ -158,13 +163,7 @@ public class UIQueryUtils {
158
163
  }
159
164
 
160
165
  public static String getId(View view) {
161
- try {
162
- return InstrumentationBackend.solo.getCurrentActivity()
163
- .getResources().getResourceEntryName(view.getId());
164
-
165
- }
166
- catch (NotFoundException e) {}
167
- return null;
166
+ return ViewMapper.getIdForView(view);
168
167
  }
169
168
 
170
169
  @SuppressWarnings("rawtypes")
@@ -274,4 +273,218 @@ public class UIQueryUtils {
274
273
 
275
274
  }
276
275
 
276
+ /*
277
+ *
278
+ {"rect"=>{"x"=>0, "y"=>0, "width"=>768, "height"=>1024},
279
+ "hit-point"=>{"x"=>384, "y"=>512},
280
+ "id"=>"",
281
+ "action"=>false,
282
+ "enabled"=>1,
283
+ "visible"=>1,
284
+ "value"=>nil,
285
+ "type"=>"[object UIAWindow]",
286
+ "name"=>nil,
287
+ "label"=>nil,
288
+ "children"=> [(samestructure)*]
289
+
290
+ */
291
+ public static Map<?,?> dump()
292
+ {
293
+ Query dummyQuery = new Query("not_used");
294
+
295
+ return dumpRecursively(emptyRootView(), dummyQuery.rootViews());
296
+ }
297
+
298
+ @SuppressWarnings({ "unchecked", "rawtypes" })
299
+ public static Map<?,?> mapWithElAsNull(Map<?,?> dump) {
300
+ HashMap result = new HashMap(dump);
301
+ result.put("el",null);
302
+ return result;
303
+ }
304
+
305
+
306
+
307
+ @SuppressWarnings({ "rawtypes", "unchecked" })
308
+ protected static Map<?,?> dumpRecursively(Map parentView,List<View> children)
309
+ {
310
+ ArrayList childrenArray = new ArrayList(32);
311
+ for (int i=0;i<children.size();i++) {
312
+ View view = children.get(i);
313
+ Map serializedChild = serializeViewToDump(view);
314
+ List<Integer> childPath = new ArrayList<Integer>((List) parentView.get("path"));
315
+ childPath.add(i);
316
+ serializedChild.put("path", childPath);
317
+ childrenArray.add(dumpRecursively(serializedChild, UIQueryUtils.subviews(view)));
318
+ }
319
+
320
+ parentView.put("children", childrenArray);
321
+
322
+ return parentView;
323
+ }
324
+
325
+ @SuppressWarnings({ "rawtypes", "unchecked" })
326
+ public static Map<?,?> dumpByPath(List<Integer> path) {
327
+ Query dummyQuery = new Query("not_used");
328
+
329
+ Map currentView = emptyRootView();
330
+ List<View> currentChildren = dummyQuery.rootViews();
331
+
332
+ for (Integer i:path) {
333
+ View child = currentChildren.get(i);
334
+ currentView = serializeViewToDump(child);
335
+ currentChildren = UIQueryUtils.subviews(child);
336
+ }
337
+
338
+ return currentView;
339
+ }
340
+
341
+ @SuppressWarnings({ "rawtypes", "unchecked" })
342
+ public static Map<?,?> serializeViewToDump(View view) {
343
+ if (view == null) {return null;}
344
+
345
+ Map m = new HashMap();
346
+
347
+ m.put("id",getId(view));
348
+ m.put("el",view);
349
+
350
+ Map rect = ViewMapper.getRectForView(view);
351
+ Map hitPoint = extractHitPointFromRect(rect);
352
+
353
+ m.put("rect",rect);
354
+ m.put("hit-point",hitPoint);
355
+ m.put("action",actionForView(view));
356
+ m.put("enabled",view.isEnabled());
357
+ m.put("visible",isVisible(view));
358
+ m.put("entry_types", elementEntryTypes(view));
359
+ m.put("value",extractValueFromView(view));
360
+ m.put("type",ViewMapper.getClassNameForView(view));
361
+ m.put("name",getNameForView(view));
362
+ m.put("label",ViewMapper.getContentDescriptionForView(view));
363
+ return m;
364
+ }
365
+
366
+ public static List<String> elementEntryTypes(View view) {
367
+ if (view instanceof TextView)
368
+ {
369
+ TextView textView = (TextView) view;
370
+ return mapTextViewInputTypes(textView.getInputType());
371
+ }
372
+ return null;
373
+
374
+ }
375
+
376
+ public static List<String> mapTextViewInputTypes(int inputType) {
377
+ List<String> inputTypes = new ArrayList<String>();
378
+ if (inputTypeHasTrait(inputType, InputType.TYPE_TEXT_VARIATION_PASSWORD) || inputTypeHasTrait(inputType, InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD)) {
379
+ inputTypes.add("password");
380
+ }
381
+ if (inputTypeHasTrait(inputType, InputType.TYPE_CLASS_NUMBER)) {
382
+ inputTypes.add("numeric");
383
+ }
384
+ inputTypes.add(String.valueOf(inputType));
385
+
386
+ return inputTypes;
387
+ }
388
+
389
+ private static boolean inputTypeHasTrait(int inputType,
390
+ int inputTypeTrait) {
391
+ return (inputType & inputTypeTrait) != 0;
392
+ }
393
+
394
+ private static Object getNameForView(View view)
395
+ {
396
+ Object result = null;
397
+ Method hintMethod = hasProperty(view,"hint");
398
+ if (hintMethod!=null)
399
+ {
400
+ result = getProperty(view, hintMethod);
401
+ }
402
+ if (result != null) {return result.toString();}
403
+ Method textMethod = hasProperty(view,"text");
404
+ if (textMethod!=null)
405
+ {
406
+ result = getProperty(view, textMethod);
407
+ }
408
+ if (result != null) {return result.toString();}
409
+
410
+ return null;
411
+ }
412
+
413
+ public static Object extractValueFromView(View view) {
414
+ if (view instanceof Button) {
415
+ Button b = (Button) view;
416
+ return b.getText().toString();
417
+ }
418
+ else if (view instanceof CheckBox) {
419
+ CheckBox c = (CheckBox) view;
420
+ return c.isChecked();
421
+ }
422
+ else if (view instanceof TextView) {
423
+ TextView t = (TextView) view;
424
+ return t.getText().toString();
425
+ }
426
+ return null;
427
+ }
428
+
429
+ /*
430
+ * function action(el)
431
+ {
432
+ var normalized = normalize(el);
433
+ if (!normalized) {
434
+ return false;
435
+ }
436
+ if (normalized instanceof UIAButton) {
437
+ return {
438
+ "type":'touch',
439
+ "gesture":'tap'
440
+ };
441
+ }
442
+ //TODO MORE
443
+ return false;
444
+ }
445
+ */
446
+ @SuppressWarnings({ "rawtypes", "unchecked" })
447
+ public static Map<?,?> actionForView(View view)
448
+ {
449
+ Map result = null;
450
+ if (view instanceof android.widget.Button || view instanceof android.widget.ImageButton) {
451
+ result = new HashMap();
452
+ result.put("type","touch");
453
+ result.put("gesture","tap");
454
+ }
455
+
456
+ //TODO: obviously many more!
457
+ return result;
458
+
459
+
460
+ }
461
+
462
+ @SuppressWarnings({ "rawtypes", "unchecked" })
463
+ public static Map extractHitPointFromRect(Map rect) {
464
+ Map hitPoint = new HashMap();
465
+ hitPoint.put("x", rect.get("center_x"));
466
+ hitPoint.put("y", rect.get("center_y"));
467
+ return hitPoint;
468
+ }
469
+
470
+ @SuppressWarnings({"unchecked", "rawtypes", "serial"})
471
+ private static Map<?,?> emptyRootView() {
472
+ return new HashMap() {{
473
+ put("id",null);
474
+ put("el",null);
475
+ put("rect",null);
476
+ put("hit-point",null);
477
+ put("action",false);
478
+ put("enabled",false);
479
+ put("visible",true);
480
+ put("value",null);
481
+ put("path",new ArrayList<Integer>());
482
+ put("type","[object CalabashRootView]");
483
+ put("name",null);
484
+ put("label",null);
485
+ }};
486
+ }
487
+
488
+
489
+
277
490
  }
metadata CHANGED
@@ -1,113 +1,114 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: calabash-android
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.6
5
- prerelease:
4
+ version: 0.4.7.pre1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Jonas Maturana Larsen
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-05-27 00:00:00.000000000 Z
11
+ date: 2013-06-05 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: cucumber
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: json
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: retriable
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: slowhandcuke
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - '>='
68
60
  - !ruby/object:Gem::Version
69
61
  version: '0'
70
62
  type: :runtime
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - '>='
76
67
  - !ruby/object:Gem::Version
77
68
  version: '0'
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: rubyzip
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
- - - ! '>='
73
+ - - '>='
84
74
  - !ruby/object:Gem::Version
85
75
  version: '0'
86
76
  type: :runtime
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
- - - ! '>='
80
+ - - '>='
92
81
  - !ruby/object:Gem::Version
93
82
  version: '0'
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: awesome_print
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
- - - ! '>='
87
+ - - '>='
100
88
  - !ruby/object:Gem::Version
101
89
  version: '0'
102
90
  type: :runtime
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
- - - ! '>='
94
+ - - '>='
108
95
  - !ruby/object:Gem::Version
109
96
  version: '0'
110
- description: ! 'calabash-android drives tests for native and hybrid Android apps. '
97
+ - !ruby/object:Gem::Dependency
98
+ name: httpclient
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: 2.3.2
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 2.3.2
111
+ description: 'calabash-android drives tests for native and hybrid Android apps. '
111
112
  email:
112
113
  - jonas@lesspainful.com
113
114
  executables:
@@ -296,6 +297,7 @@ files:
296
297
  - test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/HttpServer.java
297
298
  - test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/NanoHTTPD.java
298
299
  - test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/NullAction.java
300
+ - test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/ViewDump.java
299
301
  - test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/activity/FinishOpenedActivities.java
300
302
  - test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/activity/GetOpenedActivities.java
301
303
  - test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/activity/GoBackToActivity.java
@@ -398,6 +400,7 @@ files:
398
400
  - test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/ScrollTo.java
399
401
  - test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/SetPropertyByCssSelector.java
400
402
  - test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/SetText.java
403
+ - test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/json/JSONUtils.java
401
404
  - test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/CompletedFuture.java
402
405
  - test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/InvocationOperation.java
403
406
  - test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/Operation.java
@@ -836,26 +839,25 @@ files:
836
839
  - lib/calabash-android/lib/TestServer.apk
837
840
  homepage: http://github.com/calabash
838
841
  licenses: []
842
+ metadata: {}
839
843
  post_install_message:
840
844
  rdoc_options: []
841
845
  require_paths:
842
846
  - lib
843
847
  required_ruby_version: !ruby/object:Gem::Requirement
844
- none: false
845
848
  requirements:
846
- - - ! '>='
849
+ - - '>='
847
850
  - !ruby/object:Gem::Version
848
851
  version: '0'
849
852
  required_rubygems_version: !ruby/object:Gem::Requirement
850
- none: false
851
853
  requirements:
852
- - - ! '>='
854
+ - - '>'
853
855
  - !ruby/object:Gem::Version
854
- version: '0'
856
+ version: 1.3.1
855
857
  requirements: []
856
858
  rubyforge_project:
857
- rubygems_version: 1.8.23
859
+ rubygems_version: 2.0.2
858
860
  signing_key:
859
- specification_version: 3
861
+ specification_version: 4
860
862
  summary: Client for calabash-android for automated functional testing on Android
861
863
  test_files: []