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.
- checksums.yaml +4 -4
- data/bin/calabash-android +66 -45
- data/lib/calabash-android/calabash_steps.rb +0 -8
- data/lib/calabash-android/canned_steps.md +2 -30
- data/lib/calabash-android/lib/TestServer.apk +0 -0
- data/lib/calabash-android/operations.rb +199 -23
- data/lib/calabash-android/removed_actions.txt +52 -0
- data/lib/calabash-android/steps/assert_steps.rb +9 -24
- data/lib/calabash-android/steps/check_box_steps.rb +2 -2
- data/lib/calabash-android/steps/context_menu_steps.rb +11 -4
- data/lib/calabash-android/steps/date_picker_steps.rb +2 -2
- data/lib/calabash-android/steps/enter_text_steps.rb +13 -13
- data/lib/calabash-android/steps/l10n_steps.rb +5 -5
- data/lib/calabash-android/steps/map_steps.rb +12 -12
- data/lib/calabash-android/steps/navigation_steps.rb +12 -12
- data/lib/calabash-android/steps/press_button_steps.rb +16 -12
- data/lib/calabash-android/steps/progress_steps.rb +19 -22
- data/lib/calabash-android/steps/search_steps.rb +2 -2
- data/lib/calabash-android/steps/spinner_steps.rb +10 -2
- data/lib/calabash-android/steps/time_picker_steps.rb +2 -2
- data/lib/calabash-android/version.rb +1 -1
- data/lib/calabash-android/wait_helpers.rb +29 -1
- data/test-server/instrumentation-backend/antlr/UIQuery.g +11 -11
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/CalabashInstrumentationTestRunner.java +3 -2
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/InstrumentationBackend.java +9 -4
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/Result.java +16 -12
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/application/Backdoor.java +55 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/gestures/LongPressCoordinate.java +19 -2
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/JavaScriptExecuter.java +105 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/QueryHelper.java +27 -30
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ViewMapper.java +27 -18
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/ComparisonOperator.java +7 -1
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTPredicate.java +16 -4
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTWith.java +13 -6
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryUtils.java +94 -48
- metadata +5 -59
- data/lib/calabash-android/steps/additions_manual_steps.rb +0 -11
- data/lib/calabash-android/steps/app_steps.rb +0 -10
- data/lib/calabash-android/steps/list_steps.rb +0 -41
- data/lib/calabash-android/steps/rotation_steps.rb +0 -7
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/button/PressButtonNumber.java +0 -22
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/button/PressButtonText.java +0 -27
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/button/PressImageButtonDescription.java +0 -39
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/button/PressImageButtonNumber.java +0 -22
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/button/WaitForButton.java +0 -47
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/checkbox/ToggleCheckboxNumber.java +0 -22
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/contextmenu/LongPressText.java +0 -22
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/contextmenu/LongPressTextAndSelectFromMenuById.java +0 -26
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/contextmenu/LongPressTextAndSelectFromMenuByIndex.java +0 -22
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/contextmenu/LongPressTextAndSelectFromMenuByText.java +0 -26
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/helpers/InspectCurrentDialog.java +0 -76
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/GetListData.java +0 -85
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/GetListItemProperties.java +0 -194
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/GetListItemText.java +0 -136
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/LongPressListItems.java +0 -22
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/PressListItems.java +0 -22
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/scrolling/ScrollDown.java +0 -22
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/scrolling/ScrollUp.java +0 -22
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/search/EnterQueryByIndex.java +0 -29
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/softkey/SelectFromMenuByText.java +0 -24
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/spinner/GetSelectedSpinnerItemText.java +0 -36
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/spinner/SelectSpinnerItemByContentDescription.java +0 -43
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/AssertGridViewContainsNoDuplicates.java +0 -72
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/AssertText.java +0 -31
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/AssertTextOfSpecificTextViewByContentDescription.java +0 -32
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/ClearTextById.java +0 -30
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/ClearTextByIndex.java +0 -22
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/ClearTextFieldByContentDescription.java +0 -33
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/ClickOnText.java +0 -22
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/EnterTextByContentDescription.java +0 -32
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/EnterTextById.java +0 -33
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/EnterTextByIndex.java +0 -22
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/GetTextById.java +0 -42
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/AssertViewProperty.java +0 -141
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/ClickOnViewByDescription.java +0 -46
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/ClickOnViewById.java +0 -56
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/GetViewProperty.java +0 -101
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/HasView.java +0 -31
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/IsEnabled.java +0 -30
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/LongPressOnViewById.java +0 -34
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/Press.java +0 -89
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/SelectTab.java +0 -110
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/Wait.java +0 -24
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForDialogClose.java +0 -21
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForProgress.java +0 -47
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForScreen.java +0 -54
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForTab.java +0 -108
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForText.java +0 -37
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForView.java +0 -43
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForViewById.java +0 -58
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/JavaScriptOperation.java +0 -44
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/PressByCssSelector.java +0 -70
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/SetText.java +0 -64
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Then /^I enter "([^\"]*)" into search field$/ do |text|
|
|
2
|
-
|
|
2
|
+
enter_text("android.widget.SearchView index:0", text)
|
|
3
3
|
end
|
|
4
4
|
|
|
5
5
|
Then /^I enter "([^\"]*)" into search field number (\d+)$/ do |text, number|
|
|
6
|
-
|
|
6
|
+
enter_text("android.widget.SearchView index:#{number.to_i-1}", text)
|
|
7
7
|
end
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
-
Then /^I select "([^\"]*)" from "([^\"]*)"$/ do |item_text,
|
|
2
|
-
|
|
1
|
+
Then /^I select "([^\"]*)" from "([^\"]*)"$/ do |item_text, spinner_identifier|
|
|
2
|
+
spinner = query("android.widget.Spinner marked:'#{spinner_identifier}'")
|
|
3
|
+
|
|
4
|
+
if spinner.empty?
|
|
5
|
+
tap_when_element_exists("android.widget.Spinner * marked:'#{spinner_identifier}'")
|
|
6
|
+
else
|
|
7
|
+
touch(spinner)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
tap_when_element_exists("android.widget.PopupWindow$PopupViewContainer * marked:'#{item_text}'")
|
|
3
11
|
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
Given /^I set the time to "(\d\d:\d\d)" on TimePicker with index "([^\"]*)"$/ do |time, index|
|
|
3
|
-
|
|
3
|
+
perform_action('set_time_with_index', time, index)
|
|
4
4
|
end
|
|
5
5
|
|
|
6
6
|
Given /^I set the "([^\"]*)" time to "(\d\d:\d\d)"$/ do |content_description, time|
|
|
7
|
-
|
|
7
|
+
perform_action('set_time_with_description', content_description, time)
|
|
8
8
|
end
|
|
@@ -83,6 +83,11 @@ module Calabash
|
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
+
#options for wait_for apply
|
|
87
|
+
def wait_for_element_exists(uiquery, options={})
|
|
88
|
+
wait_for_elements_exist([uiquery], options)
|
|
89
|
+
end
|
|
90
|
+
|
|
86
91
|
#options for wait_for apply
|
|
87
92
|
def wait_for_elements_exist(elements_arr, options={})
|
|
88
93
|
if elements_arr.is_a?(String) || elements_arr.is_a?(Symbol)
|
|
@@ -93,6 +98,12 @@ module Calabash
|
|
|
93
98
|
elements_arr.all? { |q| element_exists(q) }
|
|
94
99
|
end
|
|
95
100
|
end
|
|
101
|
+
|
|
102
|
+
#options for wait_for apply
|
|
103
|
+
def wait_for_element_do_not_exist(uiquery, options={})
|
|
104
|
+
wait_for_elements_do_not_exist([uiquery], options)
|
|
105
|
+
end
|
|
106
|
+
|
|
96
107
|
#options for wait_for apply
|
|
97
108
|
def wait_for_elements_do_not_exist(elements_arr, options={})
|
|
98
109
|
if elements_arr.is_a?(String)
|
|
@@ -167,10 +178,27 @@ module Calabash
|
|
|
167
178
|
action = { :action => lambda { touch uiquery } }
|
|
168
179
|
opts = DEFAULT_OPTS.merge(action).merge(opts)
|
|
169
180
|
wait_for_elements_exist([uiquery], opts)
|
|
170
|
-
|
|
181
|
+
|
|
182
|
+
if opts[:action].parameters.length == 0
|
|
183
|
+
opts[:action].call
|
|
184
|
+
else
|
|
185
|
+
opts[:action].call(uiquery)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def wait_for_text(text, options={})
|
|
190
|
+
wait_for_element_exists("* {text CONTAINS[c] '#{text}'}", options)
|
|
171
191
|
end
|
|
172
192
|
|
|
193
|
+
def wait_for_text_to_disappear(text, options={})
|
|
194
|
+
wait_for_element_do_not_exist("* {text CONTAINS[c] '#{text}'}", options)
|
|
195
|
+
end
|
|
173
196
|
|
|
197
|
+
def wait_for_activity(activity_name, options={})
|
|
198
|
+
wait_for(options) do
|
|
199
|
+
perform_action('get_activity_name')['message'] == activity_name
|
|
200
|
+
end
|
|
201
|
+
end
|
|
174
202
|
end
|
|
175
203
|
end
|
|
176
204
|
end
|
|
@@ -32,13 +32,13 @@ options {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
query : expr (WHITE! expr)*
|
|
35
|
+
query : expr (WHITE! expr)*
|
|
36
36
|
;
|
|
37
|
-
|
|
38
37
|
|
|
39
|
-
|
|
38
|
+
|
|
39
|
+
expr : (className | filter | visibility | predicate | DIRECTION^)
|
|
40
40
|
;
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
DIRECTION : 'descendant' | 'child' | 'parent' | 'sibling'
|
|
43
43
|
;
|
|
44
44
|
|
|
@@ -66,12 +66,12 @@ BEGINPRED : '{'
|
|
|
66
66
|
;
|
|
67
67
|
ENDPRED : '}'
|
|
68
68
|
;
|
|
69
|
-
|
|
70
|
-
RELATION : | '=' | '>' | '>=' | '<' | '<=' |
|
|
71
|
-
(( 'BEGINSWITH' | 'ENDSWITH' | 'CONTAINS' | 'LIKE'
|
|
69
|
+
|
|
70
|
+
RELATION : | '=' | '>' | '>=' | '<' | '<=' | '!=' | '<>' |
|
|
71
|
+
(( 'BEGINSWITH' | 'ENDSWITH' | 'CONTAINS' | 'LIKE'
|
|
72
72
|
| 'beginswith' | 'endswith' | 'contains' | 'like') ('[' ('a'..'z' | 'A'..'Z')* ']')?)
|
|
73
|
-
|
|
74
|
-
;
|
|
73
|
+
|
|
74
|
+
;
|
|
75
75
|
|
|
76
76
|
INT : '0'..'9'+
|
|
77
77
|
;
|
|
@@ -86,8 +86,8 @@ NAME : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_'|'$')*
|
|
|
86
86
|
;
|
|
87
87
|
|
|
88
88
|
STRING
|
|
89
|
-
: '\'' ( ESC_SEQ | ~('\\'|'\'') )* '\''
|
|
90
|
-
| '"' ( DOUBLE_ESC_SEQ | ~('\\'|'"') )* '"'
|
|
89
|
+
: '\'' ( ESC_SEQ | ~('\\'|'\'') )* '\''
|
|
90
|
+
| '"' ( DOUBLE_ESC_SEQ | ~('\\'|'"') )* '"'
|
|
91
91
|
;
|
|
92
92
|
|
|
93
93
|
WHITE : ' '+ ;
|
|
@@ -2,8 +2,8 @@ package sh.calaba.instrumentationbackend;
|
|
|
2
2
|
|
|
3
3
|
import java.lang.reflect.Method;
|
|
4
4
|
|
|
5
|
-
import sh.calaba.instrumentationbackend.actions.HttpServer;
|
|
6
5
|
import android.app.Activity;
|
|
6
|
+
import sh.calaba.instrumentationbackend.actions.HttpServer;
|
|
7
7
|
import android.content.Context;
|
|
8
8
|
import android.os.Bundle;
|
|
9
9
|
import android.test.InstrumentationTestRunner;
|
|
@@ -17,6 +17,7 @@ public class CalabashInstrumentationTestRunner extends InstrumentationTestRunner
|
|
|
17
17
|
Method method = c.getDeclaredMethod ("LoadApplication", Context.class, String.class, String[].class);
|
|
18
18
|
method.invoke (null, context, null, new String[]{context.getApplicationInfo ().sourceDir});
|
|
19
19
|
System.out.println("Calabash loaded Mono");
|
|
20
|
+
InstrumentationBackend.mainActivity = Class.forName(arguments.getString("main_activity")).asSubclass(Activity.class);
|
|
20
21
|
} catch (Exception e) {
|
|
21
22
|
System.out.println("Calabash did not load Mono. This is only a problem if you are trying to test a Mono application");
|
|
22
23
|
}
|
|
@@ -28,7 +29,7 @@ public class CalabashInstrumentationTestRunner extends InstrumentationTestRunner
|
|
|
28
29
|
InstrumentationBackend.extras = arguments;
|
|
29
30
|
|
|
30
31
|
try {
|
|
31
|
-
InstrumentationBackend.
|
|
32
|
+
InstrumentationBackend.mainActivityName = arguments.getString("main_activity");
|
|
32
33
|
} catch (Exception e) {
|
|
33
34
|
throw new RuntimeException(e);
|
|
34
35
|
}
|
|
@@ -24,7 +24,8 @@ import java.util.concurrent.atomic.AtomicReference;
|
|
|
24
24
|
|
|
25
25
|
public class InstrumentationBackend extends ActivityInstrumentationTestCase2<Activity> {
|
|
26
26
|
public static String testPackage;
|
|
27
|
-
public static String
|
|
27
|
+
public static String mainActivityName;
|
|
28
|
+
public static Class<? extends Activity> mainActivity;
|
|
28
29
|
public static Bundle extras;
|
|
29
30
|
|
|
30
31
|
private static final String TAG = "InstrumentationBackend";
|
|
@@ -35,13 +36,17 @@ public class InstrumentationBackend extends ActivityInstrumentationTestCase2<Act
|
|
|
35
36
|
public static Actions actions;
|
|
36
37
|
|
|
37
38
|
public InstrumentationBackend() {
|
|
38
|
-
super(
|
|
39
|
+
super((Class<Activity>)mainActivity);
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
@Override
|
|
42
43
|
public Activity getActivity() {
|
|
44
|
+
if (mainActivity != null) {
|
|
45
|
+
return super.getActivity();
|
|
46
|
+
}
|
|
47
|
+
|
|
43
48
|
try {
|
|
44
|
-
setMainActivity(Class.forName(
|
|
49
|
+
setMainActivity(Class.forName(mainActivityName).asSubclass(Activity.class));
|
|
45
50
|
return super.getActivity();
|
|
46
51
|
} catch (ClassNotFoundException e) {
|
|
47
52
|
throw new RuntimeException(e);
|
|
@@ -62,7 +67,7 @@ public class InstrumentationBackend extends ActivityInstrumentationTestCase2<Act
|
|
|
62
67
|
protected void setUp() throws Exception {
|
|
63
68
|
super.setUp();
|
|
64
69
|
Intent i = new Intent(Intent.ACTION_MAIN);
|
|
65
|
-
i.setClassName(testPackage,
|
|
70
|
+
i.setClassName(testPackage, mainActivityName);
|
|
66
71
|
i.addCategory("android.intent.category.LAUNCHER");
|
|
67
72
|
i.putExtras(extras);
|
|
68
73
|
setActivityIntent(i);
|
|
@@ -6,18 +6,18 @@ import java.util.ArrayList;
|
|
|
6
6
|
import java.util.List;
|
|
7
7
|
|
|
8
8
|
public class Result {
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
boolean success;
|
|
11
11
|
String message;
|
|
12
12
|
List<String> bonusInformation = new ArrayList<String>();
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
public Result() {
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
public Result(boolean success) {
|
|
18
18
|
this(success, "");
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
public Result(boolean success, String message) {
|
|
22
22
|
this.success = success;
|
|
23
23
|
this.message = message;
|
|
@@ -32,19 +32,19 @@ public class Result {
|
|
|
32
32
|
public String getMessage() {
|
|
33
33
|
return message;
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
public void setMessage(String message) {
|
|
37
37
|
this.message = message;
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
public boolean isSuccess() {
|
|
41
41
|
return success;
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
public void setSuccess(boolean success) {
|
|
45
45
|
this.success = success;
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
public void addBonusInformation(String information) {
|
|
49
49
|
bonusInformation.add(information);
|
|
50
50
|
}
|
|
@@ -52,11 +52,11 @@ public class Result {
|
|
|
52
52
|
public List<String> getBonusInformation() {
|
|
53
53
|
return bonusInformation;
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
public void setExtras(List<String> bonusInformation) {
|
|
57
57
|
this.bonusInformation = bonusInformation;
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
public static Result fromThrowable(Throwable t) {
|
|
61
61
|
Result r = new Result(false, t.getMessage());
|
|
62
62
|
CharArrayWriter caw = new CharArrayWriter();
|
|
@@ -64,11 +64,15 @@ public class Result {
|
|
|
64
64
|
r.addBonusInformation("Exception stack trace:\n" + caw.toString());
|
|
65
65
|
return r;
|
|
66
66
|
}
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
public static Result successResult() {
|
|
69
69
|
return new Result(true);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
public static Result successResult(final String message) {
|
|
73
|
+
return new Result(true, message);
|
|
74
|
+
}
|
|
75
|
+
|
|
72
76
|
public static Result failedResult() {
|
|
73
77
|
return new Result(false);
|
|
74
78
|
}
|
|
@@ -76,7 +80,7 @@ public class Result {
|
|
|
76
80
|
public static Result failedResult(final String message) {
|
|
77
81
|
return new Result(false, message);
|
|
78
82
|
}
|
|
79
|
-
|
|
83
|
+
|
|
80
84
|
public String toString() {
|
|
81
85
|
return "Success: " + success + ", message: " + message;
|
|
82
86
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
package sh.calaba.instrumentationbackend.actions.application;
|
|
2
|
+
|
|
3
|
+
import java.util.ArrayList;
|
|
4
|
+
import java.util.List;
|
|
5
|
+
|
|
6
|
+
import android.app.Activity;
|
|
7
|
+
import android.app.Application;
|
|
8
|
+
import android.content.Context;
|
|
9
|
+
|
|
10
|
+
import sh.calaba.instrumentationbackend.InstrumentationBackend;
|
|
11
|
+
import sh.calaba.instrumentationbackend.Result;
|
|
12
|
+
import sh.calaba.instrumentationbackend.actions.Action;
|
|
13
|
+
import sh.calaba.instrumentationbackend.query.Operation;
|
|
14
|
+
import sh.calaba.instrumentationbackend.query.InvocationOperation;
|
|
15
|
+
import sh.calaba.instrumentationbackend.query.QueryResult;
|
|
16
|
+
import sh.calaba.instrumentationbackend.query.UIQueryResultVoid;
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
public class Backdoor implements Action {
|
|
20
|
+
|
|
21
|
+
private static final String TAG = "Backdoor";
|
|
22
|
+
|
|
23
|
+
@Override
|
|
24
|
+
public Result execute(String... args) {
|
|
25
|
+
if (args.length != 2) {
|
|
26
|
+
return Result.failedResult("You must provide method name and an argument.");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
String methodName = args[0];
|
|
30
|
+
List arguments = new ArrayList(1);
|
|
31
|
+
arguments.add(args[1]);
|
|
32
|
+
// create invocation operation to call method
|
|
33
|
+
Operation op = new InvocationOperation(methodName, arguments);
|
|
34
|
+
// get an application object to call operation on
|
|
35
|
+
Context app = InstrumentationBackend.solo.getCurrentActivity().getApplication();
|
|
36
|
+
String backdoorResult = null;
|
|
37
|
+
try {
|
|
38
|
+
backdoorResult = (String)op.apply(app);
|
|
39
|
+
} catch (Exception e) {
|
|
40
|
+
android.util.Log.e(TAG, android.util.Log.getStackTraceString(e));
|
|
41
|
+
return Result.failedResult("No such backdoor method found: public String " + op.getName() + "(String arg)");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// set backdoor result as bonus
|
|
45
|
+
Result result = Result.successResult();
|
|
46
|
+
result.addBonusInformation(backdoorResult);
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Override
|
|
51
|
+
public String key() {
|
|
52
|
+
return "backdoor";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
}
|
|
@@ -5,6 +5,19 @@ import sh.calaba.instrumentationbackend.InstrumentationBackend;
|
|
|
5
5
|
import sh.calaba.instrumentationbackend.Result;
|
|
6
6
|
import sh.calaba.instrumentationbackend.actions.Action;
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* <p>
|
|
10
|
+
* Action that performs a long press on given coordinates.
|
|
11
|
+
* </p>
|
|
12
|
+
* <p>
|
|
13
|
+
* Parameters:
|
|
14
|
+
* <ul>
|
|
15
|
+
* <li>args[0]: x coordinate (float)</li>
|
|
16
|
+
* <li>args[1]: y coordinate (float)</li>
|
|
17
|
+
* <li>args[2]: length of the long press in millisecond (optional, integer)</li>
|
|
18
|
+
* </ul>
|
|
19
|
+
* </p>
|
|
20
|
+
*/
|
|
8
21
|
public class LongPressCoordinate implements Action {
|
|
9
22
|
@Override
|
|
10
23
|
public Result execute(String... args) {
|
|
@@ -12,8 +25,12 @@ public class LongPressCoordinate implements Action {
|
|
|
12
25
|
|
|
13
26
|
float x = Float.parseFloat(args[0]);
|
|
14
27
|
float y = Float.parseFloat(args[1]);
|
|
15
|
-
|
|
16
|
-
|
|
28
|
+
if (args.length > 2) {
|
|
29
|
+
int time = Integer.parseInt(args[2]);
|
|
30
|
+
InstrumentationBackend.solo.clickLongOnScreen(x, y, time);
|
|
31
|
+
} else {
|
|
32
|
+
InstrumentationBackend.solo.clickLongOnScreen(x, y);
|
|
33
|
+
}
|
|
17
34
|
|
|
18
35
|
return Result.successResult();
|
|
19
36
|
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
package sh.calaba.instrumentationbackend.actions.webview;
|
|
2
|
+
|
|
3
|
+
import java.lang.reflect.Constructor;
|
|
4
|
+
import java.lang.reflect.Field;
|
|
5
|
+
import java.lang.reflect.InvocationTargetException;
|
|
6
|
+
import java.lang.reflect.Method;
|
|
7
|
+
|
|
8
|
+
import android.os.Build;
|
|
9
|
+
import android.webkit.WebView;
|
|
10
|
+
|
|
11
|
+
public class JavaScriptExecuter {
|
|
12
|
+
private WebView webView;
|
|
13
|
+
private Object provider;
|
|
14
|
+
|
|
15
|
+
public JavaScriptExecuter(WebView webView) {
|
|
16
|
+
this.webView = webView;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public void executeJavaScript(String javaScript) {
|
|
20
|
+
try {
|
|
21
|
+
provider = getProvider();
|
|
22
|
+
loadUrlImpl("javascript:" + javaScript);
|
|
23
|
+
} catch (Exception e) {
|
|
24
|
+
throw new RuntimeException(e);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private void callSwitchOutDrawHistory() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
|
|
29
|
+
Method method = provider.getClass().getDeclaredMethod("switchOutDrawHistory");
|
|
30
|
+
method.setAccessible(true);
|
|
31
|
+
method.invoke(provider);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private void loadUrlImpl(String url) throws NoSuchMethodException, IllegalAccessException, NoSuchFieldException, InstantiationException, InvocationTargetException, ClassNotFoundException {
|
|
35
|
+
callSwitchOutDrawHistory();
|
|
36
|
+
sendMessage(url);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private void sendMessage(String url) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException, ClassNotFoundException {
|
|
40
|
+
Object arg = getUrlData(url);
|
|
41
|
+
Object webViewCore = getWebViewCore();
|
|
42
|
+
|
|
43
|
+
int messageIdentifier = loadUrlMessage();
|
|
44
|
+
|
|
45
|
+
Method sendMessageMethod = webViewCore.getClass().getDeclaredMethod("sendMessage", int.class, Object.class);
|
|
46
|
+
sendMessageMethod.setAccessible(true);
|
|
47
|
+
sendMessageMethod.invoke(webViewCore, messageIdentifier, arg);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private final String EVENT_HUB_CLASS_NAME = "android.webkit.WebViewCore$EventHub";
|
|
51
|
+
|
|
52
|
+
private int loadUrlMessage() throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException {
|
|
53
|
+
Class<?> eventHubClass = Class.forName(EVENT_HUB_CLASS_NAME);
|
|
54
|
+
Field loadUrlField = eventHubClass.getDeclaredField("LOAD_URL");
|
|
55
|
+
loadUrlField.setAccessible(true);
|
|
56
|
+
|
|
57
|
+
return loadUrlField.getInt(String.class);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private final String GET_URL_DATA_CLASS_NAME = "android.webkit.WebViewCore$GetUrlData";
|
|
61
|
+
|
|
62
|
+
private Object getUrlData(String url) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
|
63
|
+
// For Android OS pre 2.2 the url was a string
|
|
64
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
|
|
65
|
+
return url;
|
|
66
|
+
} else {
|
|
67
|
+
Class<?> getUrlDataClass = Class.forName(GET_URL_DATA_CLASS_NAME);
|
|
68
|
+
Constructor<?> constructor = getUrlDataClass.getDeclaredConstructor();
|
|
69
|
+
constructor.setAccessible(true);
|
|
70
|
+
|
|
71
|
+
Field mUrlField = getUrlDataClass.getDeclaredField("mUrl");
|
|
72
|
+
mUrlField.setAccessible(true);
|
|
73
|
+
Field mExtraHeadersField = getUrlDataClass.getDeclaredField("mExtraHeaders");
|
|
74
|
+
mExtraHeadersField.setAccessible(true);
|
|
75
|
+
|
|
76
|
+
Object getUrlDataInstance = constructor.newInstance();
|
|
77
|
+
mUrlField.set(getUrlDataInstance, url);
|
|
78
|
+
mExtraHeadersField.set(getUrlDataInstance, null);
|
|
79
|
+
|
|
80
|
+
return getUrlDataInstance;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private Object getProvider() throws NoSuchFieldException, IllegalAccessException {
|
|
85
|
+
// For Android OS pre 4.1 the implementation of loadUrl was within the WebView widget class
|
|
86
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
|
87
|
+
return webView;
|
|
88
|
+
} else {
|
|
89
|
+
Field fieldProvider = WebView.class.getDeclaredField("mProvider");
|
|
90
|
+
fieldProvider.setAccessible(true);
|
|
91
|
+
|
|
92
|
+
return fieldProvider.get(webView);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private Object getWebViewCore() throws IllegalStateException, IllegalAccessException, NoSuchFieldException {
|
|
97
|
+
if (provider == null) throw new IllegalStateException("Provider is null");
|
|
98
|
+
|
|
99
|
+
Field fieldCore = provider.getClass().getDeclaredField("mWebViewCore");
|
|
100
|
+
fieldCore.setAccessible(true);
|
|
101
|
+
Object webViewCore = fieldCore.get(provider);
|
|
102
|
+
|
|
103
|
+
return webViewCore;
|
|
104
|
+
}
|
|
105
|
+
}
|