1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | package org.chromium.content.browser.test.util; |
6 | |
7 | import android.graphics.Rect; |
8 | import android.test.ActivityInstrumentationTestCase2; |
9 | import android.util.JsonReader; |
10 | |
11 | import java.io.IOException; |
12 | import java.io.StringReader; |
13 | import java.util.concurrent.TimeoutException; |
14 | |
15 | import junit.framework.Assert; |
16 | |
17 | import org.chromium.content.browser.ContentView; |
18 | |
19 | /** |
20 | * Collection of DOM-based utilities. |
21 | */ |
22 | public class DOMUtils { |
23 | |
24 | /** |
25 | * Returns the rect boundaries for a node by its id. |
26 | */ |
27 | public static Rect getNodeBounds( |
28 | final ContentView view, TestCallbackHelperContainer viewClient, String nodeId) |
29 | throws InterruptedException, TimeoutException { |
30 | StringBuilder sb = new StringBuilder(); |
31 | sb.append("(function() {"); |
32 | sb.append(" var node = document.getElementById('" + nodeId + "');"); |
33 | sb.append(" if (!node) return null;"); |
34 | sb.append(" var width = node.offsetWidth;"); |
35 | sb.append(" var height = node.offsetHeight;"); |
36 | sb.append(" var x = -window.scrollX;"); |
37 | sb.append(" var y = -window.scrollY;"); |
38 | sb.append(" do {"); |
39 | sb.append(" x += node.offsetLeft;"); |
40 | sb.append(" y += node.offsetTop;"); |
41 | sb.append(" } while (node = node.offsetParent);"); |
42 | sb.append(" return [ x, y, width, height ];"); |
43 | sb.append("})();"); |
44 | |
45 | String jsonText = JavaScriptUtils.executeJavaScriptAndWaitForResult( |
46 | view, viewClient, sb.toString()); |
47 | |
48 | Assert.assertFalse("Failed to retrieve bounds for " + nodeId, |
49 | jsonText.trim().equalsIgnoreCase("null")); |
50 | |
51 | JsonReader jsonReader = new JsonReader(new StringReader(jsonText)); |
52 | int[] bounds = new int[4]; |
53 | try { |
54 | jsonReader.beginArray(); |
55 | int i = 0; |
56 | while (jsonReader.hasNext()) { |
57 | bounds[i++] = jsonReader.nextInt(); |
58 | } |
59 | jsonReader.endArray(); |
60 | Assert.assertEquals("Invalid bounds returned.", 4, i); |
61 | |
62 | jsonReader.close(); |
63 | } catch (IOException exception) { |
64 | Assert.fail("Failed to evaluate JavaScript: " + jsonText + "\n" + exception); |
65 | } |
66 | |
67 | return new Rect(bounds[0], bounds[1], bounds[0] + bounds[2], bounds[1] + bounds[3]); |
68 | } |
69 | |
70 | /** |
71 | * Focus a DOM node by its id. |
72 | */ |
73 | public static void focusNode(ActivityInstrumentationTestCase2 activityTestCase, |
74 | final ContentView view, TestCallbackHelperContainer viewClient, String nodeId) |
75 | throws InterruptedException, TimeoutException { |
76 | StringBuilder sb = new StringBuilder(); |
77 | sb.append("(function() {"); |
78 | sb.append(" var node = document.getElementById('" + nodeId + "');"); |
79 | sb.append(" if (node) node.focus();"); |
80 | sb.append("})();"); |
81 | |
82 | JavaScriptUtils.executeJavaScriptAndWaitForResult(view, viewClient, sb.toString()); |
83 | } |
84 | |
85 | /** |
86 | * Click a DOM node by its id. |
87 | */ |
88 | public static void clickNode(ActivityInstrumentationTestCase2 activityTestCase, |
89 | final ContentView view, TestCallbackHelperContainer viewClient, String nodeId) |
90 | throws InterruptedException, TimeoutException { |
91 | int[] clickTarget = getClickTargetForNode(view, viewClient, nodeId); |
92 | TouchCommon touchCommon = new TouchCommon(activityTestCase); |
93 | touchCommon.singleClickView(view, clickTarget[0], clickTarget[1]); |
94 | } |
95 | |
96 | /** |
97 | * Long-press a DOM node by its id. |
98 | */ |
99 | public static void longPressNode(ActivityInstrumentationTestCase2 activityTestCase, |
100 | final ContentView view, TestCallbackHelperContainer viewClient, String nodeId) |
101 | throws InterruptedException, TimeoutException { |
102 | int[] clickTarget = getClickTargetForNode(view, viewClient, nodeId); |
103 | TouchCommon touchCommon = new TouchCommon(activityTestCase); |
104 | touchCommon.longPressView(view, clickTarget[0], clickTarget[1]); |
105 | } |
106 | |
107 | /** |
108 | * Scrolls the view to ensure that the required DOM node is visible. |
109 | */ |
110 | public static void scrollNodeIntoView(final ContentView view, |
111 | TestCallbackHelperContainer viewClient, String nodeId) |
112 | throws InterruptedException, TimeoutException { |
113 | JavaScriptUtils.executeJavaScriptAndWaitForResult(view, viewClient, |
114 | "document.getElementById('" + nodeId + "').scrollIntoView()"); |
115 | } |
116 | |
117 | /** |
118 | * Returns the contents of the node by its id. |
119 | */ |
120 | public static String getNodeContents(final ContentView view, |
121 | TestCallbackHelperContainer viewClient, String nodeId) |
122 | throws InterruptedException, TimeoutException { |
123 | return getNodeField("textContent", view, viewClient, nodeId); |
124 | } |
125 | |
126 | /** |
127 | * Returns the value of the node by its id. |
128 | */ |
129 | public static String getNodeValue(final ContentView view, |
130 | TestCallbackHelperContainer viewClient, String nodeId) |
131 | throws InterruptedException, TimeoutException { |
132 | return getNodeField("value", view, viewClient, nodeId); |
133 | } |
134 | |
135 | private static String getNodeField(String fieldName, final ContentView view, |
136 | TestCallbackHelperContainer viewClient, String nodeId) |
137 | throws InterruptedException, TimeoutException { |
138 | StringBuilder sb = new StringBuilder(); |
139 | sb.append("(function() {"); |
140 | sb.append(" var node = document.getElementById('" + nodeId + "');"); |
141 | sb.append(" if (!node) return null;"); |
142 | sb.append(" if (!node." + fieldName +") return null;"); |
143 | sb.append(" return [ node." + fieldName + " ];"); |
144 | sb.append("})();"); |
145 | |
146 | String jsonText = JavaScriptUtils.executeJavaScriptAndWaitForResult( |
147 | view, viewClient, sb.toString()); |
148 | Assert.assertFalse("Failed to retrieve contents for " + nodeId, |
149 | jsonText.trim().equalsIgnoreCase("null")); |
150 | |
151 | JsonReader jsonReader = new JsonReader(new StringReader(jsonText)); |
152 | String value = null; |
153 | try { |
154 | jsonReader.beginArray(); |
155 | if (jsonReader.hasNext()) value = jsonReader.nextString(); |
156 | jsonReader.endArray(); |
157 | Assert.assertNotNull("Invalid contents returned.", value); |
158 | |
159 | jsonReader.close(); |
160 | } catch (IOException exception) { |
161 | Assert.fail("Failed to evaluate JavaScript: " + jsonText + "\n" + exception); |
162 | } |
163 | return value; |
164 | } |
165 | |
166 | /** |
167 | * Returns click targets for a given DOM node. |
168 | */ |
169 | private static int[] getClickTargetForNode(final ContentView view, |
170 | TestCallbackHelperContainer viewClient, String nodeName) |
171 | throws InterruptedException, TimeoutException { |
172 | Rect bounds = getNodeBounds(view, viewClient, nodeName); |
173 | Assert.assertNotNull("Failed to get DOM element bounds of '" + nodeName + "'.", bounds); |
174 | |
175 | int clickX = (int) view.getRenderCoordinates().fromLocalCssToPix(bounds.exactCenterX()) |
176 | + (int) view.getContentViewCore().getViewportSizeOffsetWidthPix(); |
177 | int clickY = (int) view.getRenderCoordinates().fromLocalCssToPix(bounds.exactCenterY()) |
178 | + (int) view.getContentViewCore().getViewportSizeOffsetHeightPix(); |
179 | return new int[] { clickX, clickY }; |
180 | } |
181 | } |