rukuli 1.0.0
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 +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
|
+
[](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
|