rukuli 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/License.txt +22 -0
- data/README.md +67 -0
- data/Rakefile +1 -0
- data/lib/rukuli.rb +10 -0
- data/lib/rukuli/app.rb +33 -0
- data/lib/rukuli/clickable.rb +278 -0
- data/lib/rukuli/config.rb +67 -0
- data/lib/rukuli/exception.rb +14 -0
- data/lib/rukuli/key_code.rb +21 -0
- data/lib/rukuli/platform.rb +12 -0
- data/lib/rukuli/region.rb +85 -0
- data/lib/rukuli/screen.rb +20 -0
- data/lib/rukuli/searchable.rb +134 -0
- data/lib/rukuli/typeable.rb +32 -0
- data/lib/rukuli/version.rb +3 -0
- data/rukuli.gemspec +21 -0
- data/spec/rukuli/app_spec.rb +7 -0
- data/spec/rukuli/clickable_spec.rb +47 -0
- data/spec/rukuli/config_spec.rb +47 -0
- data/spec/rukuli/region_spec.rb +17 -0
- data/spec/rukuli/screen_spec.rb +12 -0
- data/spec/rukuli/searchable_spec.rb +70 -0
- data/spec/rukuli/typeable_spec.rb +26 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/applications.yml +15 -0
- data/spec/support/images/apple.png +0 -0
- data/spec/support/images/green_apple.png +0 -0
- data/spec/support/images/smiley_face.png +0 -0
- data/spec/support/images/test_area.jpg +0 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 79bcc51a1ba07f799f46d15d579e03cc5f239fc1
|
4
|
+
data.tar.gz: 65a8c98c4ba3e54433c2379e5a4818593cd33a81
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0a821a6a259c5587cba8d0ade7608a68928dc2a34d4c909c4e512576d5777fbaca20bcb627866a4db9ed6f82f1f53c1cc754b9f2c41b43c314a68a8b0c92a428
|
7
|
+
data.tar.gz: a7fb19df115ec199224408e31c6ed0623d0034c1415f912ebc7f6206456a59d7ec28dbf9cc7136a87248b364f8223b5c02f5276393352905668f0ba4aaac87bb
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/License.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 - 2014 André Anastácio
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Rukuli
|
2
|
+
|
3
|
+
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/andreanastacio/rukuli/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
|
4
|
+
|
5
|
+
This project is a fork of [sikuli_ruby](https://github.com/chaslemley/sikuli_ruby)!
|
6
|
+
|
7
|
+
[Sikuli](http://sikuli.org/) allows you to interact with your application's user interface using image based search to automate user actions.
|
8
|
+
|
9
|
+
## Requirements
|
10
|
+
* [Sikuli-Script 1.0.1](https://launchpad.net/sikuli/+download) (Install Sikuli-Script via sikuli-setup.jar)
|
11
|
+
* [JRuby](http://jruby.org/download) or ```rvm install jruby```
|
12
|
+
|
13
|
+
## Compatibility
|
14
|
+
|
15
|
+
Make sure to set SIKULI_HOME to the Sikuli installation directory and to add the Sikuli installation directory and Sikuli libs directory to the include path.
|
16
|
+
|
17
|
+
### Windows
|
18
|
+
|
19
|
+
```
|
20
|
+
setx SIKULI_HOME C:/path/to/sikuli-script.jar
|
21
|
+
```
|
22
|
+
|
23
|
+
### Linux / OSX
|
24
|
+
```bash
|
25
|
+
export SIKULI_HOME="~/path/to/sikuli-script.jar"
|
26
|
+
```
|
27
|
+
|
28
|
+
# Installation
|
29
|
+
```
|
30
|
+
gem install rukuli
|
31
|
+
```
|
32
|
+
|
33
|
+
# Usage
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
require 'java'
|
37
|
+
require 'rukuli'
|
38
|
+
|
39
|
+
Rukuli::Config.run do |config|
|
40
|
+
config.image_path = "#{Dir.pwd}/images/"
|
41
|
+
config.logging = false
|
42
|
+
end
|
43
|
+
|
44
|
+
screen = Rukuli::Screen.new
|
45
|
+
screen.click(10, 10) # should open your apple menu
|
46
|
+
|
47
|
+
app = Rukuli::App.new("iPhone Simulator")
|
48
|
+
app.window.click('ui_element.png') if app.window.find('ui_element.png')
|
49
|
+
```
|
50
|
+
|
51
|
+
# Running the test suite
|
52
|
+
|
53
|
+
1. You need to open `test_area.jpg` in **Preview** from `spec/support/images/` directory
|
54
|
+
before running tests.
|
55
|
+
2. You also need to open the **TextEdit** app
|
56
|
+
|
57
|
+
# Examples
|
58
|
+
|
59
|
+
* [Cucumber Suite](https://github.com/chaslemley/cucumber_sikuli)
|
60
|
+
|
61
|
+
# Contributing
|
62
|
+
|
63
|
+
* Fork it
|
64
|
+
* Create your feature branch (git checkout -b my-new-feature)
|
65
|
+
* Commit your changes (git commit -am 'Add some feature')
|
66
|
+
* Push to the branch (git push origin my-new-feature)
|
67
|
+
* Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/lib/rukuli.rb
ADDED
data/lib/rukuli/app.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# An App object represents a running app on the system.
|
2
|
+
#
|
3
|
+
module Rukuli
|
4
|
+
class App
|
5
|
+
|
6
|
+
# Public: creates a new App instance
|
7
|
+
#
|
8
|
+
# app_name - String name of the app
|
9
|
+
#
|
10
|
+
# Examples
|
11
|
+
#
|
12
|
+
# App.new("TextEdit")
|
13
|
+
#
|
14
|
+
# Returns the newly initialized App
|
15
|
+
def initialize(app_name)
|
16
|
+
@java_obj = org.sikuli.script::App.new(app_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Public: brings the App to focus
|
20
|
+
#
|
21
|
+
# Returns nothing
|
22
|
+
def focus
|
23
|
+
@java_obj.focus()
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: the Region instance representing the app's window
|
27
|
+
#
|
28
|
+
# Returns the newly initialized Region
|
29
|
+
def window
|
30
|
+
Region.new(@java_obj.window())
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,278 @@
|
|
1
|
+
# The Clickable module defines interaction with the mouse. It is included in
|
2
|
+
# the Region class.
|
3
|
+
#
|
4
|
+
module Rukuli
|
5
|
+
module Clickable
|
6
|
+
|
7
|
+
# Public: Performs a single click on an image match or point (x, y)
|
8
|
+
#
|
9
|
+
# args - String representing filename of image to find and click
|
10
|
+
# args - Fixnum, Fixnum representing x and y coordinates within
|
11
|
+
# a Region (0,0) is the top left
|
12
|
+
#
|
13
|
+
# Examples
|
14
|
+
#
|
15
|
+
# region.click('smile.png')
|
16
|
+
# region.click(123, 432)
|
17
|
+
#
|
18
|
+
# Returns nothing
|
19
|
+
def click(*args)
|
20
|
+
case args.length
|
21
|
+
when 1 then click_image(args[0])
|
22
|
+
when 2 then click_point(args[0], args[1])
|
23
|
+
else raise ArgumentError
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Public: Performs a double click on an image match or point (x, y)
|
28
|
+
#
|
29
|
+
# args - String representing filename of image to find and click
|
30
|
+
# args - Fixnum, Fixnum representing x and y coordinates within
|
31
|
+
# a Region (0,0) is the top left
|
32
|
+
#
|
33
|
+
# Examples
|
34
|
+
#
|
35
|
+
# region.double_click('smile.png')
|
36
|
+
# region.double_click(123, 432)
|
37
|
+
#
|
38
|
+
# Returns nothing
|
39
|
+
def double_click(*args)
|
40
|
+
case args.length
|
41
|
+
when 1 then click_image(args[0], true)
|
42
|
+
when 2 then click_point(args[0], args[1], true)
|
43
|
+
else raise ArgumentError
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Public: Performs a click and hold on an image match or point (x, y)
|
48
|
+
#
|
49
|
+
# args - String representing filename of image to find and click
|
50
|
+
# args - Fixnum, Fixnum representing x and y coordinates within
|
51
|
+
# a Region (0,0) is the top left
|
52
|
+
# seconds - Fixnum representing the number of seconds to hold down
|
53
|
+
# before releasing
|
54
|
+
#
|
55
|
+
# Examples
|
56
|
+
#
|
57
|
+
# region.click_and_hold('smile.png', 2)
|
58
|
+
# region.click_and_hold(123, 432, 2)
|
59
|
+
#
|
60
|
+
# Returns nothing
|
61
|
+
def click_and_hold(seconds = 1, *args)
|
62
|
+
case args.length
|
63
|
+
when 1 then click_image_and_hold(args[0], seconds)
|
64
|
+
when 2 then click_point_and_hold(args[0], args[1], seconds)
|
65
|
+
else raise ArgumentError
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Public: Performs a mouse down, drag, and mouse up
|
70
|
+
#
|
71
|
+
# start_x - Fixnum representing the x of the mouse down
|
72
|
+
# start_y - Fixnum representing the y of the mouse down
|
73
|
+
# end_x - Fixnum representing the x of the mouse up
|
74
|
+
# end_y - Fixnum representing the y of the mouse up
|
75
|
+
#
|
76
|
+
# Examples
|
77
|
+
#
|
78
|
+
# region.drag_drop(20, 12, 23, 44)
|
79
|
+
#
|
80
|
+
# Returns nothing
|
81
|
+
def drag_drop(start_x, start_y, end_x, end_y)
|
82
|
+
@java_obj.dragDrop(
|
83
|
+
offset_location(start_x, start_y),
|
84
|
+
offset_location(end_x, end_y)
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Public: Simulates turning of the mouse wheel up
|
89
|
+
#
|
90
|
+
# steps - Fixnum representing the number of steps to turn the mouse wheel
|
91
|
+
#
|
92
|
+
# Examples
|
93
|
+
#
|
94
|
+
# region.wheel_up(10)
|
95
|
+
#
|
96
|
+
# Returns nothing
|
97
|
+
def wheel_up(steps = 1)
|
98
|
+
@java_obj.wheel(-1, steps)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Public: Simulates turning of the mouse wheel down
|
102
|
+
#
|
103
|
+
# steps - Fixnum representing the number of steps to turn the mouse wheel
|
104
|
+
#
|
105
|
+
# Examples
|
106
|
+
#
|
107
|
+
# region.wheel_down(10)
|
108
|
+
#
|
109
|
+
# Returns nothing
|
110
|
+
def wheel_down(steps = 1)
|
111
|
+
@java_obj.wheel(1, steps)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Public: Performs a hover on an image match or point (x, y)
|
115
|
+
#
|
116
|
+
# args - String representing filename of image to find and hover
|
117
|
+
# args - Fixnum, Fixnum representing x and y coordinates within
|
118
|
+
# a Region (0,0) is the top left
|
119
|
+
#
|
120
|
+
# Examples
|
121
|
+
#
|
122
|
+
# region.hover('smile.png')
|
123
|
+
# region.hover(123, 432)
|
124
|
+
#
|
125
|
+
# Returns nothing
|
126
|
+
def hover(*args)
|
127
|
+
case args.length
|
128
|
+
when 1 then hover_image(args[0])
|
129
|
+
when 2 then hover_point(args[0], args[1])
|
130
|
+
else raise ArgumentError
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
# Private: turns the mouse wheel
|
137
|
+
#
|
138
|
+
# direction - Fixnum represeting direction to turn wheel
|
139
|
+
# steps - the number of steps to turn the mouse wheel
|
140
|
+
#
|
141
|
+
# Returns nothing
|
142
|
+
def wheel(direction, steps)
|
143
|
+
@java_obj.wheel(direction, steps)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Private: clicks on a matched Region based on an image based search
|
147
|
+
#
|
148
|
+
# filename - A String representation of the filename of the region to
|
149
|
+
# match against
|
150
|
+
# seconds - The length in seconds to hold the mouse
|
151
|
+
#
|
152
|
+
# Returns nothing
|
153
|
+
#
|
154
|
+
# Throws Rukuli::FileNotFound if the file could not be found on the system
|
155
|
+
# Throws Rukuli::ImageNotMatched if no matches are found within the region
|
156
|
+
def click_image_and_hold(filename, seconds)
|
157
|
+
begin
|
158
|
+
pattern = org.sikuli.script::Pattern.new(filename).similar(0.9)
|
159
|
+
@java_obj.hover(pattern)
|
160
|
+
@java_obj.mouseDown(java.awt.event.InputEvent::BUTTON1_MASK)
|
161
|
+
sleep(seconds.to_i)
|
162
|
+
@java_obj.mouseUp(0)
|
163
|
+
rescue NativeException => e
|
164
|
+
raise_exception e, filename
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Private: clicks on a point within the region
|
169
|
+
#
|
170
|
+
# filename - A String representation of the filename of the region to
|
171
|
+
# match against
|
172
|
+
#
|
173
|
+
# Returns nothing
|
174
|
+
#
|
175
|
+
# Throws Rukuli::FileNotFound if the file could not be found on the system
|
176
|
+
# Throws Rukuli::ImageNotMatched if no matches are found within the region
|
177
|
+
def click_point_and_hold(x, y, seconds)
|
178
|
+
begin
|
179
|
+
@java_obj.hover(location(x, y))
|
180
|
+
@java_obj.mouseDown(java.awt.event.InputEvent::BUTTON1_MASK)
|
181
|
+
sleep(seconds.to_i)
|
182
|
+
@java_obj.mouseUp(0)
|
183
|
+
rescue NativeException => e
|
184
|
+
raise_exception e, filename
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Private: clicks on a matched Region based on an image based search
|
189
|
+
#
|
190
|
+
# filename - A String representation of the filename of the region to
|
191
|
+
# match against
|
192
|
+
# is_double - (optional) Boolean determining if should be a double click
|
193
|
+
#
|
194
|
+
# Returns nothing
|
195
|
+
#
|
196
|
+
# Throws Rukuli::FileNotFound if the file could not be found on the system
|
197
|
+
# Throws Rukuli::ImageNotMatched if no matches are found within the region
|
198
|
+
def click_image(filename, is_double = false, and_hold = false)
|
199
|
+
begin
|
200
|
+
if is_double
|
201
|
+
@java_obj.doubleClick(filename, 0)
|
202
|
+
else
|
203
|
+
@java_obj.click(filename, 0)
|
204
|
+
end
|
205
|
+
rescue NativeException => e
|
206
|
+
raise_exception e, filename
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Private: clicks on a point relative to a Region's top left corner
|
211
|
+
#
|
212
|
+
# x - a Fixnum representing the x component of the point to click
|
213
|
+
# y - a Fixnum representing the y component of the point to click
|
214
|
+
# is_double - (optional) Boolean determining if should be a double click
|
215
|
+
#
|
216
|
+
# Returns nothing
|
217
|
+
#
|
218
|
+
# Throws Rukuli::FileNotFound if the file could not be found on the system
|
219
|
+
# Throws Rukuli::ImageNotMatched if no matches are found within the region
|
220
|
+
def click_point(x, y, is_double = false)
|
221
|
+
if is_double
|
222
|
+
@java_obj.doubleClick(offset_location(x, y))
|
223
|
+
else
|
224
|
+
@java_obj.click(offset_location(x, y))
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# Private: hovers on a matched Region based on an image based search
|
229
|
+
#
|
230
|
+
# filename - A String representation of the filename of the region to
|
231
|
+
# match against
|
232
|
+
#
|
233
|
+
# Returns nothing
|
234
|
+
#
|
235
|
+
# Throws Rukuli::FileNotFound if the file could not be found on the system
|
236
|
+
# Throws Rukuli::ImageNotMatched if no matches are found within the region
|
237
|
+
def hover_image(filename)
|
238
|
+
begin
|
239
|
+
@java_obj.hover(filename)
|
240
|
+
rescue NativeException => e
|
241
|
+
raise_exception e, filename
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# Private: hovers on a point relative to a Region's top left corner
|
246
|
+
#
|
247
|
+
# x - a Fixnum representing the x component of the point to hover
|
248
|
+
# y - a Fixnum representing the y component of the point to hover
|
249
|
+
#
|
250
|
+
# Returns nothing
|
251
|
+
#
|
252
|
+
# Throws Rukuli::FileNotFound if the file could not be found on the system
|
253
|
+
# Throws Rukuli::ImageNotMatched if no matches are found within the region
|
254
|
+
def hover_point(x, y)
|
255
|
+
@java_obj.hover(offset_location(x, y))
|
256
|
+
end
|
257
|
+
|
258
|
+
# Private: create a new instance of Location
|
259
|
+
#
|
260
|
+
# x - a Fixnum representing the x component of the point to hover
|
261
|
+
# y - a Fixnum representing the y component of the point to hover
|
262
|
+
#
|
263
|
+
# Return location class instance
|
264
|
+
def location(x, y)
|
265
|
+
org.sikuli.script::Location.new(x, y)
|
266
|
+
end
|
267
|
+
|
268
|
+
# Private: location with offset
|
269
|
+
#
|
270
|
+
# x - a Fixnum representing the x component of the point to hover
|
271
|
+
# y - a Fixnum representing the y component of the point to hover
|
272
|
+
#
|
273
|
+
# Return new location
|
274
|
+
def offset_location(x, y)
|
275
|
+
location(x, y).offset(x(), y())
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Config variables for the Sikuli driver
|
2
|
+
#
|
3
|
+
module Rukuli
|
4
|
+
class Config
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# Public: the Boolean representing whether or not to perform a 1 second
|
8
|
+
# highlight when an image is matched through Searchable#find,
|
9
|
+
# Searchable#find_all. Defaults to false.
|
10
|
+
attr_accessor :highlight_on_find
|
11
|
+
|
12
|
+
# Public: the absolute file path where Sikuli will look for images when
|
13
|
+
# a just a filename is passed to a search or click method
|
14
|
+
#
|
15
|
+
# Returns the String representation of the path
|
16
|
+
def image_path
|
17
|
+
java.lang.System.getProperty("SIKULI_IMAGE_PATH")
|
18
|
+
end
|
19
|
+
|
20
|
+
# Public: the setter for the absolute file path where Sikuli will search
|
21
|
+
# for images with given a filename as an image
|
22
|
+
#
|
23
|
+
# Examples
|
24
|
+
#
|
25
|
+
# Rukuli::Config.image_path = "/Users/andreanastacio/rukuli/images/"
|
26
|
+
#
|
27
|
+
# Returns nothing
|
28
|
+
def image_path=(path)
|
29
|
+
java.lang.System.setProperty("SIKULI_IMAGE_PATH", path)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: turns stdout logging on and off for the Sikuli java classes.
|
33
|
+
# Defaults to true.
|
34
|
+
#
|
35
|
+
# Examples
|
36
|
+
#
|
37
|
+
# Rukuli::Config.logging = false
|
38
|
+
#
|
39
|
+
# Returns nothing
|
40
|
+
def logging=(boolean)
|
41
|
+
return unless [TrueClass, FalseClass].include? boolean.class
|
42
|
+
org.sikuli.basics::Settings.InfoLogs = boolean
|
43
|
+
org.sikuli.basics::Settings.ActionLogs = boolean
|
44
|
+
org.sikuli.basics::Settings.DebugLogs = boolean
|
45
|
+
end
|
46
|
+
|
47
|
+
# Public: convienence method for grouping the setting of config
|
48
|
+
# variables
|
49
|
+
#
|
50
|
+
# Examples
|
51
|
+
#
|
52
|
+
# Rukuli::Config.run do |config|
|
53
|
+
# config.logging = true
|
54
|
+
# config.image_path = "/User/andreanastacio/images"
|
55
|
+
# config.highlight_on_find = true
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# Returns nothing
|
59
|
+
def run(*args)
|
60
|
+
if block_given?
|
61
|
+
yield self
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Exception classes for Sikuli image searching and matching
|
2
|
+
#
|
3
|
+
module Rukuli
|
4
|
+
|
5
|
+
# Thrown when Sikuli is unable to find a match within the region for the
|
6
|
+
# file given.
|
7
|
+
#
|
8
|
+
class ImageNotFound < StandardError; end
|
9
|
+
|
10
|
+
# Thrown when a filename is given that is not found on disk in the image
|
11
|
+
# path. Image path can be configured using Rukuli::Config.image_path
|
12
|
+
#
|
13
|
+
class FileDoesNotExist < StandardError; end
|
14
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'java'
|
2
|
+
java_import 'org.sikuli.script.Key'
|
3
|
+
java_import 'org.sikuli.script.KeyModifier'
|
4
|
+
|
5
|
+
#
|
6
|
+
# These constants represent keyboard codes for interacting with the keyboard.
|
7
|
+
# Keyboard interaction is defined in the Rukuli::Typeable module.
|
8
|
+
#
|
9
|
+
module Rukuli
|
10
|
+
KEY_CMD = KeyModifier::META
|
11
|
+
KEY_SHIFT = KeyModifier::SHIFT
|
12
|
+
KEY_CTRL = KeyModifier::CTRL
|
13
|
+
KEY_ALT = KeyModifier::ALT
|
14
|
+
|
15
|
+
KEY_BACKSPACE = Key::BACKSPACE
|
16
|
+
KEY_RETURN = Key::ENTER
|
17
|
+
LEFT_ARROW = Key::LEFT
|
18
|
+
RIGHT_ARROW = Key::RIGHT
|
19
|
+
UP_ARROW = Key::UP
|
20
|
+
DOWN_ARROW = Key::DOWN
|
21
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# A Region represents a rectangle on screen. Regions are the main point of
|
2
|
+
# interaction for Sikuli actions. Regions can receive actions from the mouse,
|
3
|
+
# keyboard, and image search.
|
4
|
+
#
|
5
|
+
require "rukuli/clickable"
|
6
|
+
require "rukuli/typeable"
|
7
|
+
require "rukuli/searchable"
|
8
|
+
|
9
|
+
module Rukuli
|
10
|
+
class Region
|
11
|
+
include Clickable
|
12
|
+
include Typeable
|
13
|
+
include Searchable
|
14
|
+
|
15
|
+
# Public: creates a new Region object
|
16
|
+
#
|
17
|
+
# args - Array representing x (left bound), y (top), width, height
|
18
|
+
# 4 Fixnums left, top, width, height
|
19
|
+
# An instance of an org.sikuli.script::Region
|
20
|
+
#
|
21
|
+
# Examples
|
22
|
+
#
|
23
|
+
# Region.new([10, 10, 200, 300])
|
24
|
+
# Region.new(10, 10, 200, 300)
|
25
|
+
# Region.new(another_region)
|
26
|
+
#
|
27
|
+
# Returns the newly initialized object
|
28
|
+
def initialize(*args)
|
29
|
+
@java_obj = org.sikuli.script::Region.new(*args)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: highlight the region with a ~ 5 pixel red border
|
33
|
+
#
|
34
|
+
# seconds - Fixnum length of time to show border
|
35
|
+
#
|
36
|
+
# Returns nothing
|
37
|
+
def highlight(seconds = 1)
|
38
|
+
@java_obj.java_send(:highlight, [Java::int], seconds)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Public: the x component of the top, left corner of the Region
|
42
|
+
def x
|
43
|
+
@java_obj.x()
|
44
|
+
end
|
45
|
+
|
46
|
+
# Public: the y component of the top, left corner of the Region
|
47
|
+
def y
|
48
|
+
@java_obj.y()
|
49
|
+
end
|
50
|
+
|
51
|
+
# Public: the width in pixels of the Region
|
52
|
+
def width
|
53
|
+
@java_obj.w()
|
54
|
+
end
|
55
|
+
|
56
|
+
# Public: the height in pixels of the Region
|
57
|
+
def height
|
58
|
+
@java_obj.h()
|
59
|
+
end
|
60
|
+
|
61
|
+
# Public: provide access to all region methods provided by the SikuliScript API
|
62
|
+
# See http://sikuli.org/doc/java/edu/mit/csail/uid/Region.html
|
63
|
+
def method_missing method_name, *args, &block
|
64
|
+
@java_obj.send method_name, *args, &block
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# Private: interpret a java NativeException and raises a more descriptive
|
70
|
+
# exception
|
71
|
+
#
|
72
|
+
# exception - The original java exception thrown by the sikuli java_obj
|
73
|
+
# filename - A string representing the filename to include in the
|
74
|
+
# exception message
|
75
|
+
#
|
76
|
+
# Returns nothing
|
77
|
+
def raise_exception(exception, filename)
|
78
|
+
if exception.message == "org.sikuli.script.FindFailed: ImageFile null not found on disk"
|
79
|
+
raise Rukuli::FileDoesNotExist, "The file '#{filename}' does not exist."
|
80
|
+
else
|
81
|
+
raise Rukuli::ImageNotFound, "The image '#{filename}' did not match in this region."
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# A Screen object defines a special type of Rukuli::Region that represents
|
2
|
+
# the entire screen.
|
3
|
+
#
|
4
|
+
# TODO: Test the Screen object with multiple monitors attached.
|
5
|
+
#
|
6
|
+
module Rukuli
|
7
|
+
class Screen < Region
|
8
|
+
|
9
|
+
# Public: creates a new Screen object
|
10
|
+
#
|
11
|
+
# Examples
|
12
|
+
#
|
13
|
+
# screen = Rukuli::Screen.new
|
14
|
+
#
|
15
|
+
# Returns the newly initialized Screen object
|
16
|
+
def initialize
|
17
|
+
@java_obj = org.sikuli.script::Screen.new()
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# The Rukuli::Searchable module is the heart of Sikuli. It defines the
|
2
|
+
# wrapper around Sikuli's on screen image searching and matching capability
|
3
|
+
# It is implemented by the Region class.
|
4
|
+
#
|
5
|
+
module Rukuli
|
6
|
+
module Searchable
|
7
|
+
|
8
|
+
# Public: search for an image within a Region
|
9
|
+
#
|
10
|
+
# filename - A String representation of the filename to match against
|
11
|
+
# similarity - A Float between 0 and 1 representing the threshold for
|
12
|
+
# matching an image. Passing 1 corresponds to a 100% pixel for pixel
|
13
|
+
# match. Defaults to 0.9 (90% match)
|
14
|
+
#
|
15
|
+
# Examples
|
16
|
+
#
|
17
|
+
# region.find('needle.png')
|
18
|
+
# region.find('needle.png', 0.5)
|
19
|
+
#
|
20
|
+
# Returns an instance of Region representing the best match
|
21
|
+
#
|
22
|
+
# Throws Rukuli::FileNotFound if the file could not be found on the system
|
23
|
+
# Throws Rukuli::ImageNotMatched if no matches are found within the region
|
24
|
+
def find(filename, similarity = 0.9)
|
25
|
+
begin
|
26
|
+
pattern = build_pattern(filename, similarity)
|
27
|
+
match = Region.new(@java_obj.find(pattern))
|
28
|
+
match.highlight if Rukuli::Config.highlight_on_find
|
29
|
+
match
|
30
|
+
rescue NativeException => e
|
31
|
+
raise_exception e, filename
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Public: search for an image within a region (does not raise ImageNotFound exceptions)
|
36
|
+
#
|
37
|
+
# filename - A String representation of the filename to match against
|
38
|
+
# similarity - A Float between 0 and 1 representing the threshold for
|
39
|
+
# matching an image. Passing 1 corresponds to a 100% pixel for pixel
|
40
|
+
# match. Defaults to 0.9 (90% match)
|
41
|
+
#
|
42
|
+
# Examples
|
43
|
+
#
|
44
|
+
# region.find!('needle.png')
|
45
|
+
# region.find!('needle.png', 0.5)
|
46
|
+
#
|
47
|
+
# Returns the match or nil if no match is found
|
48
|
+
def find!(filename, similarity = 0.9)
|
49
|
+
begin
|
50
|
+
find(filename, similarity)
|
51
|
+
rescue Rukuli::ImageNotFound => e
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Public: search for an image within a Region and return all matches
|
57
|
+
#
|
58
|
+
# TODO: Sort return results so they are always returned in the same order
|
59
|
+
# (top left to bottom right)
|
60
|
+
#
|
61
|
+
# filename - A String representation of the filename to match against
|
62
|
+
# similarity - A Float between 0 and 1 representing the threshold for
|
63
|
+
# matching an image. Passing 1 corresponds to a 100% pixel for pixel
|
64
|
+
# match. Defaults to 0.9 (90% match)
|
65
|
+
#
|
66
|
+
# Examples
|
67
|
+
#
|
68
|
+
# region.find_all('needle.png')
|
69
|
+
# region.find_all('needle.png', 0.5)
|
70
|
+
#
|
71
|
+
# Returns an array of Region objects that match the given file and
|
72
|
+
# threshold
|
73
|
+
#
|
74
|
+
# Throws Rukuli::FileNotFound if the file could not be found on the system
|
75
|
+
# Throws Rukuli::ImageNotMatched if no matches are found within the region
|
76
|
+
def find_all(filename, similarity = 0.9)
|
77
|
+
begin
|
78
|
+
pattern = build_pattern(filename, similarity)
|
79
|
+
matches = @java_obj.findAll(pattern)
|
80
|
+
regions = matches.collect do |r|
|
81
|
+
match = Region.new(r)
|
82
|
+
match.highlight if Rukuli::Config.highlight_on_find
|
83
|
+
match
|
84
|
+
end
|
85
|
+
regions
|
86
|
+
rescue NativeException => e
|
87
|
+
raise_exception e, filename
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Public: wait for a match to appear within a region
|
92
|
+
#
|
93
|
+
# filename - A String representation of the filename to match against
|
94
|
+
# time - A Fixnum representing the amount of time to wait defaults
|
95
|
+
# to 2 seconds
|
96
|
+
# similarity - A Float between 0 and 1 representing the threshold for
|
97
|
+
# matching an image. Passing 1 corresponds to a 100% pixel for pixel
|
98
|
+
# match. Defaults to 0.9 (90% match)
|
99
|
+
#
|
100
|
+
# Examples
|
101
|
+
#
|
102
|
+
# region.wait('needle.png') # wait for needle.png to appear for up to 1 second
|
103
|
+
# region.wait('needle.png', 10) # wait for needle.png to appear for 10 seconds
|
104
|
+
#
|
105
|
+
# Returns nothing
|
106
|
+
#
|
107
|
+
# Throws Rukuli::FileNotFound if the file could not be found on the system
|
108
|
+
# Throws Rukuli::ImageNotMatched if no matches are found within the region
|
109
|
+
def wait(filename, time = 2, similarity = 0.9)
|
110
|
+
begin
|
111
|
+
pattern = build_pattern(filename, similarity)
|
112
|
+
match = Region.new(@java_obj.wait(pattern, time))
|
113
|
+
match.highlight if Rukuli::Config.highlight_on_find
|
114
|
+
match
|
115
|
+
rescue NativeException => e
|
116
|
+
raise_exception e, filename
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
# Private: builds a java Pattern to check
|
123
|
+
#
|
124
|
+
# filename - A String representation of the filename to match against
|
125
|
+
# similarity - A Float between 0 and 1 representing the threshold for
|
126
|
+
# matching an image. Passing 1 corresponds to a 100% pixel for pixel
|
127
|
+
# match. Defaults to 0.9 (90% match)
|
128
|
+
#
|
129
|
+
# Returns a org.sikuli.script::Pattern object to match against
|
130
|
+
def build_pattern(filename, similarity)
|
131
|
+
org.sikuli.script::Pattern.new(filename).similar(similarity)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Defines interactions with the keyboard. Implemented in the Region class.
|
2
|
+
#
|
3
|
+
module Rukuli
|
4
|
+
module Typeable
|
5
|
+
|
6
|
+
# Public: Types text as if it was being typed on the keyboard with an
|
7
|
+
# optional key modifier
|
8
|
+
#
|
9
|
+
# text - String representing text to be typed on keyboard
|
10
|
+
# modifier - (optional) Sikilu constant (defined in key_code.rb)
|
11
|
+
# representing key to hold while typing text
|
12
|
+
#
|
13
|
+
# Examples
|
14
|
+
#
|
15
|
+
# region.type("Hello World")
|
16
|
+
# region.type("s", Rukuli::KEY_CMD) # saves a file
|
17
|
+
#
|
18
|
+
# Returns nothing
|
19
|
+
def type(text, modifier = 0)
|
20
|
+
@java_obj.type(nil, text, modifier)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Public: Types text then presses the return/enter key on the keyboard
|
24
|
+
#
|
25
|
+
# text - String
|
26
|
+
#
|
27
|
+
# Returns nothing
|
28
|
+
def enter(text)
|
29
|
+
@java_obj.type(text + Sikuli::KEY_RETURN)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/rukuli.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "rukuli/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.add_development_dependency "rspec"
|
7
|
+
s.name = "rukuli"
|
8
|
+
s.version = Rukuli::VERSION
|
9
|
+
s.authors = ["Chas Lemley"]
|
10
|
+
s.email = ["chas.lemley@gmail.com"]
|
11
|
+
s.homepage = "https://github.com/andreanastacio/rukuli"
|
12
|
+
s.summary = %q{Ruby wrapper for Sikuli GUI automation library}
|
13
|
+
s.description = %q{Sikuli allows you to interact with your application's user interface using image based search to automate user actions.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "rukuli"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rukuli::Region, "#Clickable" do
|
4
|
+
before(:all) do
|
5
|
+
@region = setup_test_area
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should perform a click at 10, 10" do
|
9
|
+
expect { @region.click(12, 12) }.not_to raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should perform a double click 10, 1040" do
|
13
|
+
expect { @region.double_click(12, 491) }.not_to raise_error
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should perform a double click on an image" do
|
17
|
+
expect { @region.double_click("smiley_face.png") }.not_to raise_error
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should perform a drag and drop" do
|
21
|
+
expect { @region.drag_drop(12, 12, 491, 491) }.not_to raise_error
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should perform a click on an image" do
|
25
|
+
expect { @region.click("smiley_face.png") }.not_to raise_error
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should not perform a click on an image that is outside of the region" do
|
29
|
+
expect { @region.click("apple.png") }.to raise_error(Rukuli::ImageNotFound, "The image 'apple.png' did not match in this region.")
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should perform a click and hold on an image" do
|
33
|
+
expect { @region.click_and_hold(2, "green_apple.png") }.not_to raise_error
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should perform a click and hold on a point" do
|
37
|
+
expect { @region.click_and_hold(2, 100, 100) }.not_to raise_error
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should simulate a mouse scroll wheel up event" do
|
41
|
+
expect { @region.wheel_up(10) }.not_to raise_error
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should simulate a mouse scroll wheel down event" do
|
45
|
+
expect { @region.wheel_down(10) }.not_to raise_error
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rukuli::Config do
|
4
|
+
it "should retain a list of directories to search in for images" do
|
5
|
+
Rukuli::Config.image_path = ""
|
6
|
+
java.lang.System.getProperty("SIKULI_IMAGE_PATH").should == ""
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should allow a path to be added" do
|
10
|
+
Rukuli::Config.run do |config|
|
11
|
+
config.image_path = '/Users/images/'
|
12
|
+
end
|
13
|
+
|
14
|
+
java.lang.System.getProperty("SIKULI_IMAGE_PATH").should == '/Users/images/'
|
15
|
+
Rukuli::Config.image_path.should == '/Users/images/'
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should allow logging to be turned on" do
|
19
|
+
Rukuli::Config.run do |config|
|
20
|
+
config.logging = true
|
21
|
+
end
|
22
|
+
|
23
|
+
org.sikuli.basics::Settings.InfoLogs.should be_true
|
24
|
+
org.sikuli.basics::Settings.ActionLogs.should be_true
|
25
|
+
org.sikuli.basics::Settings.DebugLogs.should be_true
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should allow logging to be turned off" do
|
29
|
+
Rukuli::Config.run do |config|
|
30
|
+
config.logging = false
|
31
|
+
end
|
32
|
+
|
33
|
+
org.sikuli.basics::Settings.InfoLogs.should be_false
|
34
|
+
org.sikuli.basics::Settings.ActionLogs.should be_false
|
35
|
+
org.sikuli.basics::Settings.DebugLogs.should be_false
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should allow us to turn off highlighting on find" do
|
39
|
+
Rukuli::Config.highlight_on_find = false
|
40
|
+
Rukuli::Config.highlight_on_find.should be_false
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should allow us to turn on highlighting on find" do
|
44
|
+
Rukuli::Config.highlight_on_find = true
|
45
|
+
Rukuli::Config.highlight_on_find.should be_true
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rukuli::Region, 'when initialized with a rectangle' do
|
4
|
+
subject { Rukuli::Region.new(10, 15, 250, 400) }
|
5
|
+
its(:x) { should == 10 }
|
6
|
+
its(:y) { should == 15 }
|
7
|
+
its(:width) { should == 250 }
|
8
|
+
its(:height) { should == 400 }
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Rukuli::Region, 'when initialzed with a sikuli region' do
|
12
|
+
subject { Rukuli::Region.new org.sikuli.script::Region.new(11, 16, 251, 401) }
|
13
|
+
its(:x) { should == 11 }
|
14
|
+
its(:y) { should == 16 }
|
15
|
+
its(:width) { should == 251 }
|
16
|
+
its(:height) { should == 401 }
|
17
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rukuli::Screen, 'when initialized' do
|
4
|
+
subject { Rukuli::Screen.new }
|
5
|
+
|
6
|
+
its(:x) { should == 0 }
|
7
|
+
its(:y) { should == 0 }
|
8
|
+
its(:width) { should > 0 } # screen's resolution
|
9
|
+
its(:width) { should be_instance_of Fixnum }
|
10
|
+
its(:height) { should > 0 } # screen's resolution
|
11
|
+
its(:height) { should be_instance_of Fixnum }
|
12
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rukuli::Region, "#Searchable" do
|
4
|
+
before(:all) do
|
5
|
+
@region = setup_test_area
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should find an image within the region" do
|
9
|
+
@region.find("smiley_face.png").should_not be_nil
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should return a region containing the found image" do
|
13
|
+
@region.find("smiley_face.png").should be_an_instance_of Rukuli::Region
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should raise an error if a file can not be found" do
|
17
|
+
expect { @region.find("no_photo.png") }.to raise_error(Rukuli::FileDoesNotExist, "The file 'no_photo.png' does not exist.")
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should not find an image that is not in the region" do
|
21
|
+
expect { @region.find("apple.png") }.to raise_error(Rukuli::ImageNotFound, "The image 'apple.png' did not match in this region.")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should return true if the image is found" do
|
25
|
+
@region.find!("smiley_face.png").should be_an_instance_of Rukuli::Region
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return nil if the image is not found" do
|
29
|
+
@region.find!("apple.png").should be_nil
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should raise an exception if the file does not exist" do
|
33
|
+
expect { @region.find!("no_photo.png") }.to raise_error(Rukuli::FileDoesNotExist, "The file 'no_photo.png' does not exist.")
|
34
|
+
end
|
35
|
+
|
36
|
+
context "#wait" do
|
37
|
+
it "should raise an error if no match is found after a given time" do
|
38
|
+
expect { @region.wait('apple.png') }.to raise_error(Rukuli::ImageNotFound, "The image 'apple.png' did not match in this region.")
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should return a Region object when a match is found" do
|
42
|
+
match = @region.wait('green_apple.png', 5)
|
43
|
+
match.should be_an_instance_of Rukuli::Region
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "#find_all" do
|
48
|
+
before(:all) do
|
49
|
+
@matches = @region.find_all("green_apple.png")
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should raise an error if no matches are found" do
|
53
|
+
expect { @region.find_all("apple.png") }.to raise_error(Rukuli::ImageNotFound, "The image 'apple.png' did not match in this region.")
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should return an array" do
|
57
|
+
@matches.should be_an_instance_of Array
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should contain 4 matches" do
|
61
|
+
@matches.length.should == 4
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should contain regions" do
|
65
|
+
@matches.each do |m|
|
66
|
+
m.should be_an_instance_of Rukuli::Region
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rukuli::Region, "#Typeable" do
|
4
|
+
before(:all) do
|
5
|
+
app = Rukuli::App.new(application "TEXT_EDIT")
|
6
|
+
app.focus
|
7
|
+
@region = app.window
|
8
|
+
end
|
9
|
+
|
10
|
+
context "modifying text input with" do
|
11
|
+
it "apple key" do
|
12
|
+
@region.type("n", Rukuli::KEY_CMD)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "shift key" do
|
16
|
+
@region.type("this should be lower case")
|
17
|
+
@region.type("this should be upper case", Rukuli::KEY_SHIFT)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "unicode characters" do
|
22
|
+
it("backspace") { @region.type(Rukuli::KEY_BACKSPACE * 50) }
|
23
|
+
it("return") { @region.type(Rukuli::KEY_RETURN) }
|
24
|
+
it("left arrow") { @region.type(Rukuli::LEFT_ARROW) }
|
25
|
+
end
|
26
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'rspec'
|
5
|
+
require 'rukuli'
|
6
|
+
|
7
|
+
APPLICATIONS = YAML.load(File.open("#{Dir.pwd}/spec/support/applications.yml"))
|
8
|
+
|
9
|
+
Rukuli::Config.logging = false
|
10
|
+
|
11
|
+
def application(app)
|
12
|
+
APPLICATIONS[app].select { |k, _| k =~ /#{RbConfig::CONFIG['host_os']}/ }.shift[1]
|
13
|
+
end
|
14
|
+
|
15
|
+
def setup_test_area
|
16
|
+
Rukuli::Config.image_path = "#{Dir.pwd}/spec/support/images/"
|
17
|
+
screen = Rukuli::Screen.new
|
18
|
+
app = Rukuli::App.new(application "IMAGE_VIEWER")
|
19
|
+
app.focus()
|
20
|
+
center = screen.find("#{Dir.pwd}/spec/support/images/smiley_face.png")
|
21
|
+
|
22
|
+
test_area = Rukuli::Region.new(center.x - 212, center.y - 212, 503, 503)
|
23
|
+
test_area.highlight
|
24
|
+
|
25
|
+
test_area
|
26
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
IMAGE_VIEWER:
|
2
|
+
darwin|mac os:
|
3
|
+
'Preview'
|
4
|
+
cygwin|mswin|mingw|bccwin|wince|emx|mswin32:
|
5
|
+
"explorer"
|
6
|
+
FILE_EXPLORER:
|
7
|
+
darwin|mac os:
|
8
|
+
'Finder'
|
9
|
+
cygwin|mswin|mingw|bccwin|wince|emx|mswin32:
|
10
|
+
"explorer"
|
11
|
+
TEXT_EDIT:
|
12
|
+
darwin|mac os:
|
13
|
+
'TextEdit'
|
14
|
+
cygwin|mswin|mingw|bccwin|wince|emx|mswin32:
|
15
|
+
"notepad"
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rukuli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chas Lemley
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
requirement: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - '>='
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '0'
|
25
|
+
prerelease: false
|
26
|
+
type: :development
|
27
|
+
description: Sikuli allows you to interact with your application's user interface using image based search to automate user actions.
|
28
|
+
email:
|
29
|
+
- chas.lemley@gmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- Gemfile
|
36
|
+
- License.txt
|
37
|
+
- README.md
|
38
|
+
- Rakefile
|
39
|
+
- lib/rukuli.rb
|
40
|
+
- lib/rukuli/app.rb
|
41
|
+
- lib/rukuli/clickable.rb
|
42
|
+
- lib/rukuli/config.rb
|
43
|
+
- lib/rukuli/exception.rb
|
44
|
+
- lib/rukuli/key_code.rb
|
45
|
+
- lib/rukuli/platform.rb
|
46
|
+
- lib/rukuli/region.rb
|
47
|
+
- lib/rukuli/screen.rb
|
48
|
+
- lib/rukuli/searchable.rb
|
49
|
+
- lib/rukuli/typeable.rb
|
50
|
+
- lib/rukuli/version.rb
|
51
|
+
- rukuli.gemspec
|
52
|
+
- spec/rukuli/app_spec.rb
|
53
|
+
- spec/rukuli/clickable_spec.rb
|
54
|
+
- spec/rukuli/config_spec.rb
|
55
|
+
- spec/rukuli/region_spec.rb
|
56
|
+
- spec/rukuli/screen_spec.rb
|
57
|
+
- spec/rukuli/searchable_spec.rb
|
58
|
+
- spec/rukuli/typeable_spec.rb
|
59
|
+
- spec/spec_helper.rb
|
60
|
+
- spec/support/applications.yml
|
61
|
+
- spec/support/images/apple.png
|
62
|
+
- spec/support/images/green_apple.png
|
63
|
+
- spec/support/images/smiley_face.png
|
64
|
+
- spec/support/images/test_area.jpg
|
65
|
+
homepage: https://github.com/andreanastacio/rukuli
|
66
|
+
licenses: []
|
67
|
+
metadata: {}
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
requirements: []
|
83
|
+
rubyforge_project: rukuli
|
84
|
+
rubygems_version: 2.1.9
|
85
|
+
signing_key:
|
86
|
+
specification_version: 4
|
87
|
+
summary: Ruby wrapper for Sikuli GUI automation library
|
88
|
+
test_files:
|
89
|
+
- spec/rukuli/app_spec.rb
|
90
|
+
- spec/rukuli/clickable_spec.rb
|
91
|
+
- spec/rukuli/config_spec.rb
|
92
|
+
- spec/rukuli/region_spec.rb
|
93
|
+
- spec/rukuli/screen_spec.rb
|
94
|
+
- spec/rukuli/searchable_spec.rb
|
95
|
+
- spec/rukuli/typeable_spec.rb
|
96
|
+
- spec/spec_helper.rb
|
97
|
+
- spec/support/applications.yml
|
98
|
+
- spec/support/images/apple.png
|
99
|
+
- spec/support/images/green_apple.png
|
100
|
+
- spec/support/images/smiley_face.png
|
101
|
+
- spec/support/images/test_area.jpg
|