casper 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +85 -0
- data/lib/casper.rb +115 -0
- data/lib/libxdo.rb +256 -0
- data/test/Vagrantfile +20 -0
- data/test/casper_test.rb +116 -0
- data/test/cookbooks/application/recipes/default.rb +6 -0
- data/test/cookbooks/application/recipes/vagrant.rb +1 -0
- data/test/cookbooks/application/templates/default/application.erb +11 -0
- data/test/cookbooks/apt/README.md +44 -0
- data/test/cookbooks/apt/files/default/apt-cacher +9 -0
- data/test/cookbooks/apt/files/default/apt-cacher.conf +144 -0
- data/test/cookbooks/apt/files/default/apt-proxy-v2.conf +50 -0
- data/test/cookbooks/apt/metadata.json +38 -0
- data/test/cookbooks/apt/metadata.rb +12 -0
- data/test/cookbooks/apt/recipes/cacher.rb +42 -0
- data/test/cookbooks/apt/recipes/default.rb +33 -0
- data/test/cookbooks/apt/recipes/proxy.rb +34 -0
- data/test/cookbooks/rvm/files/default/rvm-install-system-wide +99 -0
- data/test/cookbooks/rvm/files/default/source-rvm +1 -0
- data/test/cookbooks/rvm/recipes/default.rb +47 -0
- data/test/cookbooks/testing/files/default/Xwrapper.config +14 -0
- data/test/cookbooks/testing/files/default/xorg.conf +58 -0
- data/test/cookbooks/testing/recipes/default.rb +59 -0
- data/test/lib/public/jquery-ui.js +1012 -0
- data/test/lib/public/jquery.js +6240 -0
- data/test/lib/public/rxin_test.js +7 -0
- data/test/lib/server.rb +34 -0
- data/test/lib/test_helper.rb +69 -0
- metadata +166 -0
data/README.md
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
Casper
|
2
|
+
======
|
3
|
+
|
4
|
+
A DSL for automated mouse <del>and keyboard</del> input in X11.
|
5
|
+
|
6
|
+
Usage
|
7
|
+
-----
|
8
|
+
|
9
|
+
Right now Casper only does mouse input, it looks something like this:
|
10
|
+
|
11
|
+
Casper::Mouse.move 200, 300 # puts the cursor at x: 200px, y: 300px
|
12
|
+
Casper::Mouse.down # presses the primary mouse button
|
13
|
+
Casper::Mouse.up # releases the primary mouse button
|
14
|
+
Casper::Mouse.click # clicks with the primary mouse button
|
15
|
+
Casper::Mouse.location # => [ 200, 300 ]
|
16
|
+
|
17
|
+
Which is all well and good, but the more useful stuff looks like:
|
18
|
+
|
19
|
+
Casper::Mouse.drag :from => [ 200, 300 ], :distance => [ 30, 60 ], :increments => 10 do
|
20
|
+
Casper::Mouse.drag :distance => [ 80, 100 ] do
|
21
|
+
sleep 0.5
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
We've found it particularly useful in conjunction with Selenium-based
|
26
|
+
Javascript testing. We use it to reliably test complex movements (such as a
|
27
|
+
velocity-aware drag, or drag and drop between multiple containers with
|
28
|
+
timeouts, etc...)
|
29
|
+
|
30
|
+
Requirements
|
31
|
+
------------
|
32
|
+
|
33
|
+
Requires `libxdo` (comes with `xdotool`). Details and installation
|
34
|
+
instructions can be found at http://www.semicomplete.com/projects/xdotool/.
|
35
|
+
|
36
|
+
Quick build instructions for Ubuntu (tested on 10.04):
|
37
|
+
|
38
|
+
$ sudo apt-get install xorg-dev
|
39
|
+
$ wget http://semicomplete.googlecode.com/files/xdotool-2.20100818.3004.tar.gz
|
40
|
+
$ tar xzvf xdotool-2.20100818.3004.tar.gz
|
41
|
+
$ cd xdotool-2.20100818.3004
|
42
|
+
$ sudo make all install
|
43
|
+
|
44
|
+
Testing
|
45
|
+
-------
|
46
|
+
|
47
|
+
We test Casper in a VM environment set up by Vagrant. If you have Vagrant
|
48
|
+
(http://www.vagrantup.com/) set up and running, and you have the lucid32.box,
|
49
|
+
you can just do:
|
50
|
+
|
51
|
+
$ cd test && vagrant up
|
52
|
+
|
53
|
+
Once your VM is provisioned and running, ssh in (`vagrant ssh`) and then:
|
54
|
+
|
55
|
+
$ startx &
|
56
|
+
$ cd ~/casper/test
|
57
|
+
$ ruby casper_test.rb
|
58
|
+
|
59
|
+
If you don't have Vagrant set up, you can follow the getting started guide at
|
60
|
+
http://www.vagrantup.com/, or you can install the necessary dependencies on
|
61
|
+
your local machine and test locally. Instructions for installing the
|
62
|
+
dependencies can be found under `test/cookbooks`.
|
63
|
+
|
64
|
+
License
|
65
|
+
-------
|
66
|
+
|
67
|
+
Copyright (c) 2010 Ben Alavi and Chris Schneider
|
68
|
+
|
69
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
70
|
+
of this software and associated documentation files (the "Software"), to deal
|
71
|
+
in the Software without restriction, including without limitation the rights
|
72
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
73
|
+
copies of the Software, and to permit persons to whom the Software is
|
74
|
+
furnished to do so, subject to the following conditions:
|
75
|
+
|
76
|
+
The above copyright notice and this permission notice shall be included in
|
77
|
+
all copies or substantial portions of the Software.
|
78
|
+
|
79
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
80
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
81
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
82
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
83
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
84
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
85
|
+
THE SOFTWARE.
|
data/lib/casper.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require "libxdo"
|
2
|
+
|
3
|
+
module Casper
|
4
|
+
class Mouse
|
5
|
+
class << self
|
6
|
+
# Move the mouse directly to the specified x/y coordinates.
|
7
|
+
#
|
8
|
+
# If relative = true the x/y coordinates used are relative from the
|
9
|
+
# current position, otherwise they are considered to be absolute.
|
10
|
+
def move(x, y, relative=false)
|
11
|
+
relative ? relative(x, y) : absolute(x, y)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Press the given mouse button down (default is 1: primary)
|
15
|
+
def down(button=1)
|
16
|
+
Libxdo.xdo_mousedown xdo, Libxdo::CurrentWindow, button
|
17
|
+
end
|
18
|
+
|
19
|
+
# Release the given mouse button (default is 1: primary)
|
20
|
+
def up(button=1)
|
21
|
+
Libxdo.xdo_mouseup xdo, Libxdo::CurrentWindow, button
|
22
|
+
end
|
23
|
+
|
24
|
+
# Click the given mouse button (default is 1: primary)
|
25
|
+
def click(button=1)
|
26
|
+
Libxdo.xdo_click xdo, Libxdo::CurrentWindow, button
|
27
|
+
end
|
28
|
+
|
29
|
+
# Gives the current mouse position
|
30
|
+
def location
|
31
|
+
x = FFI::MemoryPointer.new :pointer
|
32
|
+
y = FFI::MemoryPointer.new :pointer
|
33
|
+
s = FFI::MemoryPointer.new :pointer
|
34
|
+
Libxdo.xdo_mouselocation xdo, x, y, s
|
35
|
+
[ x.read_int, y.read_int ]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Perform a drag operation with the given options. A drag is performed
|
39
|
+
# by moving the mouse to a starting location, pressing the primary mouse
|
40
|
+
# button down, incrementally moving to another location, and then
|
41
|
+
# releasing the primary mouse button.
|
42
|
+
#
|
43
|
+
# Available options are:
|
44
|
+
#
|
45
|
+
# * :from => [ x, y ] -- The absolute x/y coordinates to start from. If
|
46
|
+
# omitted the drag will be started from the current mouse location.
|
47
|
+
# * :to => [ x, y ] -- The absolute x/y coordinates to end at. Can use
|
48
|
+
# :distance instead for relative movements.
|
49
|
+
# * :distance => [ x, y ] -- Can be used instead of :to to provide an
|
50
|
+
# end point relative to the starting point.
|
51
|
+
# * :increments => i -- The number of increments to include in the drag
|
52
|
+
# from the start to the end, defaults to 10. More increments cause a
|
53
|
+
# smoother, slower drag. Fewer increments cause a faster "jerkier"
|
54
|
+
# drag.
|
55
|
+
#
|
56
|
+
# Also a block can be passed which will be yielded before the mouse is
|
57
|
+
# released. This can be used to drag and hover for a period of time, or
|
58
|
+
# chain multiple drags, etc...
|
59
|
+
#
|
60
|
+
# i.e.
|
61
|
+
#
|
62
|
+
# drag :to => [ 300, 350 ]
|
63
|
+
# drag :from => [ 200, 300 ], :to => [ 400, 800 ]
|
64
|
+
# drag :from => [ 200, 300 ], :distance => [ 200, 500 ]
|
65
|
+
# drag :from => [ 200, 300 ], :distance => [ 220, 340 ], :increments => 20
|
66
|
+
# drag :from => [ 200, 300 ], :to => [ 300, 400 ] do
|
67
|
+
# sleep 0.5
|
68
|
+
# end
|
69
|
+
# drag :distance => [ 20, 0 ] do
|
70
|
+
# drag :distance => [ 0, 30 ]
|
71
|
+
# end
|
72
|
+
def drag(options={}, &block)
|
73
|
+
raise ArgumentError.new(":to or :distance is required to provide ending location") unless options.has_key?(:to) || options.has_key?(:distance)
|
74
|
+
raise ArgumentError.new(":increments must be > 0") if options.has_key?(:increments) && options[:increments] <= 0
|
75
|
+
|
76
|
+
from ||= options[:from] || location
|
77
|
+
increments = options[:increments] || 10
|
78
|
+
distance = options[:distance] || [ options[:to][0] - from[0], options[:to][1] - from[1] ]
|
79
|
+
|
80
|
+
shift_x = distance[0] / increments
|
81
|
+
shift_y = distance[1] / increments
|
82
|
+
|
83
|
+
move from[0], from[1]
|
84
|
+
down
|
85
|
+
increments.times{ |i| move(shift_x, shift_y, true) }
|
86
|
+
yield if block_given?
|
87
|
+
up
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Returns a new xdo instance
|
93
|
+
def xdo
|
94
|
+
Libxdo.xdo_new(nil)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Performs an absolute mouse move
|
98
|
+
def absolute(x, y)
|
99
|
+
xdor = xdo
|
100
|
+
|
101
|
+
Libxdo.xdo_mousemove(xdor, x, y, 0)
|
102
|
+
Libxdo.xdo_mouse_wait_for_move_to(xdor, x, y)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Performs a relative mouse move
|
106
|
+
def relative(x, y)
|
107
|
+
xdor = xdo
|
108
|
+
loc = location
|
109
|
+
|
110
|
+
Libxdo.xdo_mousemove_relative(xdor, x, y)
|
111
|
+
Libxdo.xdo_mouse_wait_for_move_to(xdor, loc[0] + x, loc[1] + y)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/lib/libxdo.rb
ADDED
@@ -0,0 +1,256 @@
|
|
1
|
+
require "ffi"
|
2
|
+
|
3
|
+
module Casper
|
4
|
+
module Libxdo
|
5
|
+
extend FFI::Library
|
6
|
+
|
7
|
+
CurrentWindow = 0
|
8
|
+
|
9
|
+
ffi_lib "xdo"
|
10
|
+
|
11
|
+
# xdo_t * xdo_new (char *display)
|
12
|
+
attach_function "xdo_new", [ :string ], :pointer
|
13
|
+
|
14
|
+
# xdo_t * xdo_new_with_opened_display (Display *xdpy, const char *display, int close_display_when_freed)
|
15
|
+
attach_function "xdo_new_with_opened_display", [:pointer, :string, :int], :pointer
|
16
|
+
|
17
|
+
# const char * xdo_version (void)
|
18
|
+
attach_function "xdo_version", [], :string
|
19
|
+
|
20
|
+
# void xdo_free (xdo_t *xdo)
|
21
|
+
attach_function "xdo_free", [:pointer], :void
|
22
|
+
|
23
|
+
# int xdo_mousemove (const xdo_t *xdo, int x, int y, int screen)
|
24
|
+
attach_function "xdo_mousemove", [:pointer, :int, :int, :int], :int
|
25
|
+
|
26
|
+
# int xdo_mousemove_relative_to_window (const xdo_t *xdo, Window window, int x, int y)
|
27
|
+
# XXX: Window is not a pointer - find the typedef
|
28
|
+
attach_function "xdo_mousemove_relative_to_window", [:pointer, :pointer, :int, :int], :int
|
29
|
+
|
30
|
+
# int xdo_mousemove_relative (const xdo_t *xdo, int x, int y)
|
31
|
+
attach_function "xdo_mousemove_relative", [:pointer, :int, :int], :int
|
32
|
+
|
33
|
+
# int xdo_mousedown (const xdo_t *xdo, Window window, int button)
|
34
|
+
attach_function "xdo_mousedown", [:pointer, :int, :int], :int
|
35
|
+
|
36
|
+
# int xdo_mouseup (const xdo_t *xdo, Window window, int button)
|
37
|
+
attach_function "xdo_mouseup", [:pointer, :int, :int], :int
|
38
|
+
|
39
|
+
# int xdo_mouselocation (const xdo_t *xdo, int *x, int *y, int *screen_num)
|
40
|
+
attach_function "xdo_mouselocation", [:pointer, :pointer, :pointer, :pointer], :int
|
41
|
+
|
42
|
+
# int xdo_mouse_wait_for_move_from (const xdo_t *xdo, int origin_x, int origin_y)
|
43
|
+
attach_function "xdo_mouse_wait_for_move_from", [:pointer, :int, :int], :int
|
44
|
+
|
45
|
+
# int xdo_mouse_wait_for_move_to (const xdo_t *xdo, int dest_x, int dest_y)
|
46
|
+
attach_function "xdo_mouse_wait_for_move_to", [:pointer, :int, :int], :int
|
47
|
+
|
48
|
+
# int xdo_click (const xdo_t *xdo, Window window, int button)
|
49
|
+
attach_function "xdo_click", [:pointer, :int, :int], :int
|
50
|
+
|
51
|
+
# int xdo_type (const xdo_t *xdo, Window window, char *string, useconds_t delay)
|
52
|
+
# XXX: Map useconds_t
|
53
|
+
attach_function "xdo_type", [:pointer, :int, :string, :int], :int
|
54
|
+
|
55
|
+
# int xdo_keysequence (const xdo_t *xdo, Window window, const char *keysequence, useconds_t delay)
|
56
|
+
# XXX: Window != pointer
|
57
|
+
# XXX: Map useconds_t
|
58
|
+
attach_function "xdo_keysequence", [:pointer, :pointer, :string, :int], :int
|
59
|
+
|
60
|
+
# int xdo_keysequence_up (const xdo_t *xdo, Window window, const char *keysequence, useconds_t delay)
|
61
|
+
# XXX: Window != pointer
|
62
|
+
# XXX: Map useconds_t
|
63
|
+
attach_function "xdo_keysequence_up", [:pointer, :pointer, :string, :int], :int
|
64
|
+
|
65
|
+
# int xdo_keysequence_down (const xdo_t *xdo, Window window, const char *keysequence, useconds_t delay)
|
66
|
+
# XXX: Window != pointer
|
67
|
+
# XXX: Map useconds_t
|
68
|
+
attach_function "xdo_keysequence_down", [:pointer, :pointer, :string, :int], :int
|
69
|
+
|
70
|
+
# int xdo_keysequence_list_do (const xdo_t *xdo, Window window, charcodemap_t *keys, int nkeys, int pressed, int *modifier, useconds_t delay)
|
71
|
+
# XXX: Window != pointer
|
72
|
+
# XXX: What"s a charcodemap_t?
|
73
|
+
# XXX: Map useconds_t
|
74
|
+
attach_function "xdo_keysequence_list_do", [:pointer, :pointer, :pointer, :int, :int, :pointer, :int], :int
|
75
|
+
|
76
|
+
# int xdo_active_keys_to_keycode_list (const xdo_t *xdo, charcodemap_t **keys, int *nkeys)
|
77
|
+
# XXX: Map useconds_t
|
78
|
+
# XXX: What"s a pointer to pointer to charcodemap_t?
|
79
|
+
attach_function "xdo_active_keys_to_keycode_list", [:pointer, :pointer, :pointer], :int
|
80
|
+
|
81
|
+
# int xdo_window_wait_for_map_state (const xdo_t *xdo, Window wid, int map_state)
|
82
|
+
# XXX: Window != pointer
|
83
|
+
attach_function "xdo_window_wait_for_map_state", [:pointer, :pointer, :int], :int
|
84
|
+
|
85
|
+
# int xdo_window_move (const xdo_t *xdo, Window wid, int x, int y)
|
86
|
+
# XXX: Window != pointer
|
87
|
+
attach_function "xdo_window_move", [:pointer, :pointer, :int, :int], :int
|
88
|
+
|
89
|
+
# int xdo_window_setsize (const xdo_t *xdo, Window wid, int w, int h, int flags)
|
90
|
+
# XXX: Window != pointer
|
91
|
+
attach_function "xdo_window_setsize", [:pointer, :pointer, :int, :int, :int], :int
|
92
|
+
|
93
|
+
# int xdo_window_setprop (const xdo_t *xdo, Window wid, const char *property, const char *value)
|
94
|
+
# XXX: Window != pointer
|
95
|
+
attach_function "xdo_window_setprop", [:pointer, :pointer, :string, :string], :int
|
96
|
+
|
97
|
+
# int xdo_window_setclass (const xdo_t *xdo, Window wid, const char *name, const char *class)
|
98
|
+
# XXX: Window != pointer
|
99
|
+
attach_function "xdo_window_setclass", [:pointer, :pointer, :string, :string], :int
|
100
|
+
|
101
|
+
# int xdo_window_focus (const xdo_t *xdo, Window wid)
|
102
|
+
# XXX: Window != pointer
|
103
|
+
attach_function "xdo_window_focus", [:pointer, :pointer], :int
|
104
|
+
|
105
|
+
# int xdo_window_raise (const xdo_t *xdo, Window wid)
|
106
|
+
# XXX: Window != pointer
|
107
|
+
attach_function "xdo_window_raise", [:pointer, :pointer], :int
|
108
|
+
|
109
|
+
# int xdo_window_get_focus (const xdo_t *xdo, Window *window_ret)
|
110
|
+
# NOTE: This window is actually a pointer. Watch out
|
111
|
+
attach_function "xdo_window_get_focus", [:pointer, :pointer], :int
|
112
|
+
|
113
|
+
# int xdo_window_wait_for_focus (const xdo_t *xdo, Window window, int want_focus)
|
114
|
+
# XXX: Window != pointer
|
115
|
+
attach_function "xdo_window_wait_for_focus", [:pointer, :pointer, :int], :int
|
116
|
+
|
117
|
+
# int xdo_window_get_pid (const xdo_t *xdo, Window window)
|
118
|
+
# XXX: Window != pointer
|
119
|
+
attach_function "xdo_window_get_pid", [:pointer, :pointer], :int
|
120
|
+
|
121
|
+
# int xdo_window_sane_get_focus (const xdo_t *xdo, Window *window_ret)
|
122
|
+
# NOTE: This window is actually a pointer. Watch out
|
123
|
+
attach_function "xdo_window_sane_get_focus", [:pointer, :pointer], :int
|
124
|
+
|
125
|
+
# int xdo_window_activate (const xdo_t *xdo, Window wid)
|
126
|
+
# XXX: Window != pointer
|
127
|
+
attach_function "xdo_window_activate", [:pointer, :pointer], :int
|
128
|
+
|
129
|
+
# int xdo_window_wait_for_active (const xdo_t *xdo, Window window, int active)
|
130
|
+
# XXX: Window != pointer
|
131
|
+
attach_function "xdo_window_wait_for_active", [:pointer, :pointer, :int], :int
|
132
|
+
|
133
|
+
# int xdo_window_map (const xdo_t *xdo, Window wid)
|
134
|
+
# XXX: Window != pointer
|
135
|
+
attach_function "xdo_window_map", [:pointer, :pointer], :int
|
136
|
+
|
137
|
+
# int xdo_window_unmap (const xdo_t *xdo, Window wid)
|
138
|
+
attach_function "xdo_window_unmap", [:pointer, :pointer], :int
|
139
|
+
|
140
|
+
# int xdo_get_window_location (const xdo_t *xdo, Window wid, int *x_ret, int *y_ret, Screen **screen_ret)
|
141
|
+
# XXX: Window != pointer
|
142
|
+
# XXX: What"s a screen double pointer
|
143
|
+
attach_function "xdo_get_window_location", [:pointer, :pointer, :pointer, :pointer, :pointer], :int
|
144
|
+
|
145
|
+
# int xdo_get_window_size (const xdo_t *xdo, Window wid, unsigned int *width_ret, unsigned int *height_ret)
|
146
|
+
# XXX: Window != pointer
|
147
|
+
attach_function "xdo_get_window_size", [:pointer, :pointer, :pointer, :pointer], :int
|
148
|
+
|
149
|
+
# int xdo_window_get_active (const xdo_t *xdo, Window *window_ret)
|
150
|
+
# NOTE: This window is actually a pointer. Watch out
|
151
|
+
attach_function "xdo_window_get_active", [:pointer, :pointer], :int
|
152
|
+
|
153
|
+
# int xdo_set_number_of_desktops (const xdo_t *xdo, long ndesktops)
|
154
|
+
attach_function "xdo_set_number_of_desktops", [:pointer, :long], :int
|
155
|
+
|
156
|
+
# int xdo_get_number_of_desktops (const xdo_t *xdo, long *ndesktops)
|
157
|
+
attach_function "xdo_get_number_of_desktops", [:pointer, :pointer], :int
|
158
|
+
|
159
|
+
# int xdo_set_current_desktop (const xdo_t *xdo, long desktop)
|
160
|
+
attach_function "xdo_set_current_desktop", [:pointer, :long], :int
|
161
|
+
|
162
|
+
# int xdo_get_current_desktop (const xdo_t *xdo, long *desktop)
|
163
|
+
attach_function "xdo_get_current_desktop", [:pointer, :pointer], :int
|
164
|
+
|
165
|
+
# int xdo_set_desktop_for_window (const xdo_t *xdo, Window wid, long desktop)
|
166
|
+
# XXX: Window != pointer
|
167
|
+
attach_function "xdo_set_desktop_for_window", [:pointer, :pointer, :long], :int
|
168
|
+
|
169
|
+
# int xdo_get_desktop_for_window (const xdo_t *xdo, Window wid, long *desktop)
|
170
|
+
# XXX: Window != pointer
|
171
|
+
attach_function "xdo_get_desktop_for_window", [:pointer, :pointer, :pointer], :int
|
172
|
+
|
173
|
+
# int xdo_window_search (const xdo_t *xdo, const xdo_search_t *search, Window **windowlist_ret, int *nwindows_ret)
|
174
|
+
# XXX: xdo_search_t? How do I make that.
|
175
|
+
attach_function "xdo_window_search", [:pointer, :pointer, :pointer, :pointer], :int
|
176
|
+
|
177
|
+
# unsigned char * xdo_getwinprop (const xdo_t *xdo, Window window, Atom atom, long *nitems, Atom *type, int *size)
|
178
|
+
# XXX: Window != pointer
|
179
|
+
# XXX: Atom != pointer
|
180
|
+
# XXX: Unsigned char * != :string?
|
181
|
+
attach_function "xdo_getwinprop", [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :string
|
182
|
+
|
183
|
+
# unsigned int xdo_get_input_state (const xdo_t *xdo)
|
184
|
+
attach_function "xdo_get_input_state", [:pointer], :uint
|
185
|
+
|
186
|
+
# const keysym_charmap_t * xdo_keysym_charmap (void)
|
187
|
+
attach_function "xdo_keysym_charmap", [], :pointer
|
188
|
+
|
189
|
+
# XXX: char** is an array of strings?
|
190
|
+
# const char ** xdo_symbol_map (void)
|
191
|
+
attach_function "xdo_symbol_map", [], :string
|
192
|
+
|
193
|
+
# xdo_active_mods_t * xdo_get_active_modifiers (const xdo_t *xdo)
|
194
|
+
attach_function "xdo_get_active_modifiers", [:pointer], :pointer
|
195
|
+
|
196
|
+
# int xdo_clear_active_modifiers (const xdo_t *xdo, Window window, xdo_active_mods_t *active_mods)
|
197
|
+
attach_function "xdo_clear_active_modifiers", [:pointer, :pointer, :pointer], :int
|
198
|
+
|
199
|
+
# int xdo_set_active_modifiers (const xdo_t *xdo, Window window, const xdo_active_mods_t *active_mods)
|
200
|
+
# XXX: Window != pointer
|
201
|
+
attach_function "xdo_set_active_modifiers", [], :int
|
202
|
+
|
203
|
+
# void xdo_free_active_modifiers (xdo_active_mods_t *active_mods)
|
204
|
+
attach_function "xdo_free_active_modifiers", [:pointer], :void
|
205
|
+
|
206
|
+
class Xdo < FFI::Struct
|
207
|
+
# The Display for Xlib.
|
208
|
+
# Display * xdpy
|
209
|
+
layout :xdpy ,:pointer, # Display * xdpy
|
210
|
+
:display_name ,:string, # The display name.
|
211
|
+
:charcodes ,:pointer, # charcodemap_t * charcodes
|
212
|
+
:charcodes_len ,:int,
|
213
|
+
:modmap ,:pointer, # XModifierKeymap * modmap
|
214
|
+
:keymap ,:pointer, # KeySym * keymap
|
215
|
+
:keycode_high ,:int,
|
216
|
+
:keycode_low ,:int,
|
217
|
+
:keysyms_per_keycode ,:int,
|
218
|
+
:close_display_when_freed ,:int # Should we close the display when calling xdo_free?
|
219
|
+
end
|
220
|
+
|
221
|
+
class KeysymCharmap < FFI::Struct
|
222
|
+
layout :keysym ,:string,
|
223
|
+
:key ,:char
|
224
|
+
end
|
225
|
+
|
226
|
+
# XXX: Is the native documentation off by a field? Feels like it.
|
227
|
+
# class Charcodemap < FFI::Struct
|
228
|
+
# # XXX: keycode isn"t a type
|
229
|
+
# layout :code ,:KeyCode, # the letter for this key, like "a"
|
230
|
+
# :symbol ,:KeySym, # the keycode that this key is on
|
231
|
+
# :index ,:int, # the symbol representing this key
|
232
|
+
# :modmask ,:int, # the index in the keysym-per-keycode list that is this key
|
233
|
+
# :needs_binding ,:int # the modifiers activated by this key
|
234
|
+
# end
|
235
|
+
|
236
|
+
class XdoActiveMods < FFI::Struct
|
237
|
+
layout :keymods, :pointer, # charcodemap_t * keymods
|
238
|
+
:nkeymods, :int, # int nkeymods
|
239
|
+
:input_state, :uint # unsigned int input_state
|
240
|
+
end
|
241
|
+
|
242
|
+
# class XdoSearch < FFI::Struct
|
243
|
+
# layout :title ,:string, # char * title
|
244
|
+
# :winclass ,:string, # char * winclass # pattern to test against a window title
|
245
|
+
# :winclassname ,:string, # char * winclassname # pattern to test against a window class
|
246
|
+
# :winname ,:string, # char * winname # pattern to test against a window class
|
247
|
+
# :pid ,:int, # int pid # window pid (From window atom _NET_WM_PID)
|
248
|
+
# :max_depth ,:long, # long max_depth # depth of search.
|
249
|
+
# :only_visible ,:int, # only_visible boolean; set true to search only visible windows
|
250
|
+
# :screen ,:int, # screen
|
251
|
+
# # XXX: Map this enum. Not sure how that works.
|
252
|
+
# :xdo_search ,:search_enum, # what screen to search, if any.
|
253
|
+
# :searchmask ,:uint # unsigned int searchmask # bitmask of things you are searching for, such as SEARCH_NAME , etc.
|
254
|
+
# end
|
255
|
+
end
|
256
|
+
end
|
data/test/Vagrantfile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Vagrant::Config.run do |config|
|
2
|
+
config.vm.box = "base"
|
3
|
+
config.vm.customize do |vm|
|
4
|
+
vm.name = "casper"
|
5
|
+
vm.memory_size = 512
|
6
|
+
end
|
7
|
+
|
8
|
+
config.vm.network("33.33.33.10")
|
9
|
+
config.vm.share_folder("v-root", "~/casper", "..", :nfs => true)
|
10
|
+
config.vm.provisioner = :chef_solo
|
11
|
+
config.vm.boot_mode = :gui
|
12
|
+
|
13
|
+
config.chef.run_list = %w(
|
14
|
+
recipe[application::vagrant]
|
15
|
+
recipe[rvm]
|
16
|
+
recipe[testing]
|
17
|
+
)
|
18
|
+
|
19
|
+
config.chef.log_level = :debug
|
20
|
+
end
|
data/test/casper_test.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/lib/test_helper"
|
2
|
+
require "casper"
|
3
|
+
|
4
|
+
# Yeah, we use Capybara & Selenium & jQuery & Firefox and X11 and all sorts
|
5
|
+
# of other stuff when we could just as easily mock out the xdo lib and
|
6
|
+
# simply make sure we're sending the right commands.
|
7
|
+
#
|
8
|
+
# But this is way more fun =)
|
9
|
+
class CasperTest < Test::Unit::TestCase
|
10
|
+
# Places a target div onto the document body with the given attributes
|
11
|
+
def target!(id, x, y, width=50, height=50)
|
12
|
+
evaluate_script <<-JS
|
13
|
+
$('<div class="target" id="#{id}"></div>').
|
14
|
+
appendTo("body").
|
15
|
+
css({ left: #{x}, top: #{y}, width: #{width}, height: #{height }}).
|
16
|
+
draggable()
|
17
|
+
JS
|
18
|
+
end
|
19
|
+
|
20
|
+
setup do
|
21
|
+
Casper::Mouse.move 0, 0
|
22
|
+
# Gets rid of the menu if it was left open (i.e. from leaving the mouse
|
23
|
+
# button down and moving over it).
|
24
|
+
Casper::Mouse.click
|
25
|
+
resize_browser 1024, 768
|
26
|
+
visit "/"
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "mouse actions" do
|
30
|
+
setup do
|
31
|
+
target! "a", 300, 220
|
32
|
+
end
|
33
|
+
|
34
|
+
should "position the mouse at 320, 240" do
|
35
|
+
assert !has_class?("#a", "mouseover")
|
36
|
+
|
37
|
+
Casper::Mouse.move(320, 350)
|
38
|
+
assert has_class?("#a", "mouseover")
|
39
|
+
end
|
40
|
+
|
41
|
+
should "press the primary mouse button down on the element" do
|
42
|
+
assert !has_class?("#a", "mousedown")
|
43
|
+
|
44
|
+
Casper::Mouse.move(320, 350)
|
45
|
+
Casper::Mouse.down(1)
|
46
|
+
assert has_class?("#a", "mousedown")
|
47
|
+
end
|
48
|
+
|
49
|
+
should "release the primary mouse button on the element" do
|
50
|
+
assert !has_class?("#a", "mousedown")
|
51
|
+
|
52
|
+
Casper::Mouse.move(320, 350)
|
53
|
+
Casper::Mouse.down(1)
|
54
|
+
assert has_class?("#a", "mousedown")
|
55
|
+
|
56
|
+
Casper::Mouse.up(1)
|
57
|
+
assert !has_class?("#a", "mousedown")
|
58
|
+
end
|
59
|
+
|
60
|
+
should "provide the current mouse location" do
|
61
|
+
Casper::Mouse.move 318, 473
|
62
|
+
assert_equal [ 318, 473 ], Casper::Mouse.location
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "dragging" do
|
67
|
+
setup do
|
68
|
+
target! "a", 300, 220
|
69
|
+
end
|
70
|
+
|
71
|
+
should "drag an item" do
|
72
|
+
Casper::Mouse.drag :from => [ 320, 350 ], :to => [ 350, 400 ]
|
73
|
+
assert_has_position? "#a", :left => 330, :top => 270
|
74
|
+
end
|
75
|
+
|
76
|
+
should "use the current mouse position when no :from option is provided" do
|
77
|
+
Casper::Mouse.move 320, 350
|
78
|
+
assert_has_position? "#a", :left => 300, :top => 220
|
79
|
+
|
80
|
+
Casper::Mouse.drag :distance => [ 20, 20 ]
|
81
|
+
assert_has_position? "#a", :left => 320, :top => 240
|
82
|
+
end
|
83
|
+
|
84
|
+
should "move from the current mouse position to the specified position when :to is provided without :from" do
|
85
|
+
Casper::Mouse.move 320, 350
|
86
|
+
assert_has_position? "#a", :left => 300, :top => 220
|
87
|
+
|
88
|
+
Casper::Mouse.drag :to => [ 410, 510 ]
|
89
|
+
assert_has_position? "#a", :left => 390, :top => 380
|
90
|
+
end
|
91
|
+
|
92
|
+
should "have a default increments value, making it an optional parameter" do
|
93
|
+
Casper::Mouse.drag :from => [ 1, 1 ], :to => [ 2, 2 ]
|
94
|
+
end
|
95
|
+
|
96
|
+
should "not raise an argument error if you have a positive increment value" do
|
97
|
+
Casper::Mouse.drag :from => [ 1, 1 ], :to => [ 2, 2 ], :increment => 5
|
98
|
+
end
|
99
|
+
|
100
|
+
should "raise an argument error if you have a negative, or 0 increment" do
|
101
|
+
assert_raise ArgumentError do
|
102
|
+
Casper::Mouse.drag :from => [ 1, 1 ], :to => [ 2, 2 ], :increments => 0
|
103
|
+
end
|
104
|
+
|
105
|
+
assert_raise ArgumentError do
|
106
|
+
Casper::Mouse.drag :from => [ 1, 1 ], :to => [ 2, 2 ], :increments => -5
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
should "raise argument error if you did not provide either to or distance" do
|
111
|
+
assert_raise ArgumentError do
|
112
|
+
Casper::Mouse.drag :from => [ 1, 1 ]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_recipe "application"
|
@@ -0,0 +1,44 @@
|
|
1
|
+
DESCRIPTION
|
2
|
+
===========
|
3
|
+
|
4
|
+
Configures various APT components on Debian-like systems.
|
5
|
+
|
6
|
+
RECIPES
|
7
|
+
=======
|
8
|
+
|
9
|
+
default
|
10
|
+
-------
|
11
|
+
|
12
|
+
The default recipe runs apt-get update during the Compile Phase of the Chef run to ensure that the system's package cache is updated with the latest. It is recommended that this recipe appear first in a node's run list (directly or through a role) to ensure that when installing packages, Chef will be able to download the latest version available on the remote APT repository.
|
13
|
+
|
14
|
+
This recipe also sets up a local cache directory for preseeding packages.
|
15
|
+
|
16
|
+
cacher
|
17
|
+
------
|
18
|
+
|
19
|
+
Installs the apt-cacher package and service so the system can be an APT cache.
|
20
|
+
|
21
|
+
proxy
|
22
|
+
-----
|
23
|
+
|
24
|
+
Installs the apt-proxy package and service so the system can be an APT proxy.
|
25
|
+
|
26
|
+
LICENSE AND AUTHOR
|
27
|
+
==================
|
28
|
+
|
29
|
+
Author:: Joshua Timberman (<joshua@opscode.com>)
|
30
|
+
|
31
|
+
Copyright 2009, Opscode, Inc.
|
32
|
+
|
33
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
34
|
+
you may not use this file except in compliance with the License.
|
35
|
+
You may obtain a copy of the License at
|
36
|
+
|
37
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
38
|
+
|
39
|
+
Unless required by applicable law or agreed to in writing, software
|
40
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
41
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
42
|
+
See the License for the specific language governing permissions and
|
43
|
+
limitations under the License.
|
44
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# apt-cacher startup configuration file
|
2
|
+
|
3
|
+
# IMPORTANT: check the apt-cacher.conf file before using apt-cacher as daemon.
|
4
|
+
|
5
|
+
# set to 1 to start the daemon at boot time
|
6
|
+
AUTOSTART=1
|
7
|
+
|
8
|
+
# extra settings to override the ones in apt-cacher.conf
|
9
|
+
# EXTRAOPT=" daemon_port=3142 limit=30 "
|