win32-autogui 0.2.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.
- data/.gitattributes +1 -0
- data/.gitignore +10 -0
- data/.yardopts +6 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +60 -0
- data/HISTORY.markdown +11 -0
- data/LICENSE +20 -0
- data/README.markdown +265 -0
- data/Rakefile +55 -0
- data/TODO.markdown +9 -0
- data/VERSION +1 -0
- data/config/cucumber.yml +7 -0
- data/examples/quicknote/.gitignore +8 -0
- data/examples/quicknote/FormAboutU.dfm +44 -0
- data/examples/quicknote/FormAboutU.pas +36 -0
- data/examples/quicknote/FormMainU.dfm +110 -0
- data/examples/quicknote/FormMainU.pas +268 -0
- data/examples/quicknote/FormSplashU.dfm +32 -0
- data/examples/quicknote/FormSplashU.pas +52 -0
- data/examples/quicknote/LICENSE +20 -0
- data/examples/quicknote/README.markdown +28 -0
- data/examples/quicknote/Rakefile +12 -0
- data/examples/quicknote/TODO.markdown +15 -0
- data/examples/quicknote/dcu/.gitignore +1 -0
- data/examples/quicknote/exe/.gitignore +0 -0
- data/examples/quicknote/exe/quicknote.exe +0 -0
- data/examples/quicknote/lib/quicknote.rb +140 -0
- data/examples/quicknote/quicknote.cfg +37 -0
- data/examples/quicknote/quicknote.dof +158 -0
- data/examples/quicknote/quicknote.dpr +16 -0
- data/examples/quicknote/quicknote.res +0 -0
- data/examples/quicknote/spec/quicknote/form_about_spec.rb +50 -0
- data/examples/quicknote/spec/quicknote/form_main_spec.rb +274 -0
- data/examples/quicknote/spec/quicknote/form_splash_spec.rb +44 -0
- data/examples/quicknote/spec/spec.opts +2 -0
- data/examples/quicknote/spec/spec_helper.rb +34 -0
- data/examples/quicknote/spec/watchr.rb +143 -0
- data/examples/skeleton/.gitignore +8 -0
- data/examples/skeleton/LICENSE +20 -0
- data/examples/skeleton/README.markdown +62 -0
- data/examples/skeleton/Rakefile +21 -0
- data/examples/skeleton/TODO.markdown +9 -0
- data/examples/skeleton/config/cucumber.yml +7 -0
- data/examples/skeleton/dcu/.gitignore +1 -0
- data/examples/skeleton/exe/.gitignore +1 -0
- data/examples/skeleton/features/basic.feature +6 -0
- data/examples/skeleton/features/step_definitions/.gitignore +0 -0
- data/examples/skeleton/features/step_definitions/application_steps.rb +43 -0
- data/examples/skeleton/features/support/env.rb +5 -0
- data/examples/skeleton/lib/myapp.rb +73 -0
- data/examples/skeleton/spec/myapp/form_about_spec.rb +50 -0
- data/examples/skeleton/spec/myapp/form_main_spec.rb +60 -0
- data/examples/skeleton/spec/spec.opts +2 -0
- data/examples/skeleton/spec/spec_helper.rb +29 -0
- data/examples/skeleton/spec/watchr.rb +143 -0
- data/features/automating_an_application.feature +11 -0
- data/features/step_definitions/.gitignore +0 -0
- data/features/step_definitions/calculator_steps.rb +37 -0
- data/features/support/env.rb +4 -0
- data/lib/win32/autogui.rb +27 -0
- data/lib/win32/autogui/application.rb +249 -0
- data/lib/win32/autogui/input.rb +238 -0
- data/lib/win32/autogui/window.rb +191 -0
- data/lib/win32/autogui/windows/window.rb +22 -0
- data/spec/applications/calculator.rb +34 -0
- data/spec/auto_gui/application_spec.rb +132 -0
- data/spec/basic_gem/basic_gem_spec.rb +13 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/watchr.rb +144 -0
- data/win32-autogui.gemspec +43 -0
- metadata +329 -0
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'windows/window'
|
2
|
+
require 'windows/window/message'
|
3
|
+
require 'win32/autogui/windows/window'
|
4
|
+
|
5
|
+
module Autogui
|
6
|
+
|
7
|
+
# Enumerate desktop child windows
|
8
|
+
#
|
9
|
+
# Start at the desktop and work down through all the child windows
|
10
|
+
#
|
11
|
+
class EnumerateDesktopWindows
|
12
|
+
include Enumerable
|
13
|
+
include Windows::Window
|
14
|
+
|
15
|
+
def each
|
16
|
+
child_after = 0
|
17
|
+
while (child_after = FindWindowEx(nil, child_after, nil, nil)) > 0 do
|
18
|
+
yield Window.new child_after
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Enumerate just the child windows one level down from the parent window
|
24
|
+
#
|
25
|
+
class Children
|
26
|
+
include Enumerable
|
27
|
+
include Windows::Window
|
28
|
+
|
29
|
+
# @param [Number] parent window handle
|
30
|
+
#
|
31
|
+
def initialize(parent)
|
32
|
+
@parent = parent
|
33
|
+
end
|
34
|
+
|
35
|
+
# @yield [Window]
|
36
|
+
#
|
37
|
+
def each
|
38
|
+
child_after = 0
|
39
|
+
while (child_after = FindWindowEx(@parent.handle, child_after, nil, nil)) > 0 do
|
40
|
+
window = Window.new child_after
|
41
|
+
# immediate children only
|
42
|
+
yield window if (window.parent.handle == @parent.handle)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Wrapper for window
|
48
|
+
#
|
49
|
+
class Window
|
50
|
+
include Windows::Window # instance methods from windows-pr gem
|
51
|
+
include Windows::Window::Message # PostMessage and constants
|
52
|
+
|
53
|
+
attr_reader :handle
|
54
|
+
|
55
|
+
def initialize(handle)
|
56
|
+
@handle = handle
|
57
|
+
end
|
58
|
+
|
59
|
+
# enumerable immeadiate child windows
|
60
|
+
#
|
61
|
+
# @see Children
|
62
|
+
#
|
63
|
+
def children
|
64
|
+
Children.new(self)
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [Object] Window or nil
|
68
|
+
#
|
69
|
+
def parent
|
70
|
+
h = GetParent(handle)
|
71
|
+
Window.new h if h > 0
|
72
|
+
end
|
73
|
+
|
74
|
+
# PostMessage SC_CLOSE and optionally wait for the window to close
|
75
|
+
#
|
76
|
+
# @param [Hash] options
|
77
|
+
# @option options [Boolean] :wait_for_close (true) sleep while waiting for timeout or close
|
78
|
+
# @option options [Boolean] :timeout (5) wait_for_close timeout in seconds
|
79
|
+
#
|
80
|
+
def close(options={})
|
81
|
+
PostMessage(handle, WM_SYSCOMMAND, SC_CLOSE, 0)
|
82
|
+
wait_for_close(options) if (options[:wait_for_close] == true)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Wait for the window to close
|
86
|
+
#
|
87
|
+
# @param [Hash] options
|
88
|
+
# @option options [Boolean] :timeout (5) timeout in seconds
|
89
|
+
#
|
90
|
+
def wait_for_close(options={})
|
91
|
+
seconds = options[:timeout] || 5
|
92
|
+
timeout(seconds) do
|
93
|
+
sleep 0.05 until 0 == IsWindow(handle)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# @return [String] the ANSI Windows ClassName
|
98
|
+
#
|
99
|
+
def window_class
|
100
|
+
buffer = "\0" * 255
|
101
|
+
length = GetClassNameA(handle, buffer, buffer.length)
|
102
|
+
length == 0 ? '' : buffer[0..length - 1]
|
103
|
+
end
|
104
|
+
|
105
|
+
# Window text (WM_GETTEXT)
|
106
|
+
#
|
107
|
+
# @param [Number] max_length (2048)
|
108
|
+
#
|
109
|
+
# @return [String] of max_length (2048)
|
110
|
+
#
|
111
|
+
def text(max_length = 2048)
|
112
|
+
buffer = "\0" * max_length
|
113
|
+
length = SendMessageA(handle, WM_GETTEXT, buffer.length, buffer)
|
114
|
+
length == 0 ? '' : buffer[0..length - 1]
|
115
|
+
end
|
116
|
+
alias :title :text
|
117
|
+
|
118
|
+
# Determines whether the specified window handle identifies an existing window
|
119
|
+
#
|
120
|
+
# @return [Boolean]
|
121
|
+
#
|
122
|
+
def is_window?
|
123
|
+
(handle != 0) && (IsWindow(handle) != 0)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Brings the window into the foreground and activates it.
|
127
|
+
# Keyboard input is directed to the window, and various visual cues
|
128
|
+
# are changed for the user.
|
129
|
+
#
|
130
|
+
# @return [Number] nonzero number if sucessful, nil or zero if failed
|
131
|
+
#
|
132
|
+
def set_focus
|
133
|
+
SetForegroundWindow(handle) if is_window?
|
134
|
+
end
|
135
|
+
|
136
|
+
# The identifier (pid) of the process that created the window
|
137
|
+
#
|
138
|
+
# @return [Integer] process id if the window exists, otherwise nil
|
139
|
+
#
|
140
|
+
def pid
|
141
|
+
return nil unless is_window?
|
142
|
+
process_id = 0.chr * 4
|
143
|
+
GetWindowThreadProcessId(handle, process_id)
|
144
|
+
process_id = process_id.unpack('L').first
|
145
|
+
end
|
146
|
+
|
147
|
+
# The identifier of the thread that created the window
|
148
|
+
#
|
149
|
+
# @return [Integer] thread id if the window exists, otherwise nil
|
150
|
+
#
|
151
|
+
def thread_id
|
152
|
+
return nil unless is_window?
|
153
|
+
GetWindowThreadProcessId(handle, nil)
|
154
|
+
end
|
155
|
+
|
156
|
+
# The window text including all child windows
|
157
|
+
# joined together with newlines. Faciliates matching text.
|
158
|
+
# Text from any given window is limited to 2048 characters
|
159
|
+
#
|
160
|
+
# @example partial match of the Window's calulator's about dialog copywrite text
|
161
|
+
#
|
162
|
+
# dialog_about = @calculator.dialog_about
|
163
|
+
# dialog_about.title.should == "About Calculator"
|
164
|
+
# dialog_about.combined_text.should match(/Microsoft . Calculator/)
|
165
|
+
#
|
166
|
+
# @return [String] with newlines
|
167
|
+
#
|
168
|
+
def combined_text
|
169
|
+
return unless is_window?
|
170
|
+
t = []
|
171
|
+
t << text unless text == ''
|
172
|
+
children.each do |w|
|
173
|
+
t << w.combined_text unless w.combined_text == ''
|
174
|
+
end
|
175
|
+
t.join("\n")
|
176
|
+
end
|
177
|
+
|
178
|
+
# Debugging information
|
179
|
+
#
|
180
|
+
# @return [String] with child window information
|
181
|
+
def inspect
|
182
|
+
c = []
|
183
|
+
children.each do |w|
|
184
|
+
c << w.inspect
|
185
|
+
end
|
186
|
+
s = super + " #{self.class}=<window_class:#{window_class} pid:#{pid} thread_id:#{thread_id} title:\"#{title}\" children=<" + c.join("\n") + ">>"
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Reopen module and supply missing constants and
|
2
|
+
# functions from windows-pr gem
|
3
|
+
#
|
4
|
+
# TODO: Fork and send pull request for Windows::Window module, be sure to lock bundle before sending request
|
5
|
+
#
|
6
|
+
module Windows
|
7
|
+
module Window
|
8
|
+
|
9
|
+
SC_CLOSE = 0xF060
|
10
|
+
|
11
|
+
API.auto_namespace = 'Windows::Window'
|
12
|
+
API.auto_constant = true
|
13
|
+
API.auto_method = true
|
14
|
+
API.auto_unicode = false
|
15
|
+
|
16
|
+
API.new('IsWindow', 'L', 'I', 'user32')
|
17
|
+
API.new('SetForegroundWindow', 'L', 'I', 'user32')
|
18
|
+
API.new('SendMessageA', 'LIIP', 'I', 'user32')
|
19
|
+
API.new('GetClassNameA', 'LPI', 'I', 'user32')
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
class Calculator < Autogui::Application
|
4
|
+
|
5
|
+
# initialize with the binary name 'calc' and the window title
|
6
|
+
# 'Calculator' used along with the application pid to find the
|
7
|
+
# main application window
|
8
|
+
def initialize(options = {})
|
9
|
+
defaults = {
|
10
|
+
:name => "calc",
|
11
|
+
:title => "Calculator"
|
12
|
+
}
|
13
|
+
super defaults.merge(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
# the calculator's results window
|
17
|
+
def edit_window
|
18
|
+
main_window.children.find {|w| w.window_class == 'Edit'}
|
19
|
+
end
|
20
|
+
|
21
|
+
# About dialog, hotkey (VK_MENU, VK_H, VK_A)
|
22
|
+
def dialog_about
|
23
|
+
Autogui::EnumerateDesktopWindows.new.find do |w|
|
24
|
+
w.title.match(/About Calculator/) && (w.pid == pid)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# the 'CE' button
|
29
|
+
def clear_entry
|
30
|
+
set_focus
|
31
|
+
keystroke(VK_DELETE)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
include Autogui::Input
|
4
|
+
|
5
|
+
describe Autogui::Application do
|
6
|
+
|
7
|
+
describe "driving calc.exe" do
|
8
|
+
|
9
|
+
before(:all) do
|
10
|
+
@calculator = Calculator.new
|
11
|
+
@calculator.set_focus
|
12
|
+
end
|
13
|
+
|
14
|
+
after(:all) do
|
15
|
+
@calculator.close(:wait_for_close => true) if @calculator.running?
|
16
|
+
@calculator.should_not be_running
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should start when initialized" do
|
20
|
+
@calculator.should be_running
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should die when sending the kill signal" do
|
24
|
+
killme = Calculator.new
|
25
|
+
killme.should be_running
|
26
|
+
killme.kill
|
27
|
+
killme.should_not be_running
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should have the title 'Calculator' that matches the main_window title" do
|
31
|
+
@calculator.main_window.title.should == 'Calculator'
|
32
|
+
@calculator.main_window.title.should == @calculator.title
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should have an inspect method showing child window information" do
|
36
|
+
@calculator.inspect.should match(/children=</)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should raise an error if setting focus and the application title is incorrect" do
|
40
|
+
goodcalc = Calculator.new :title => "Calculator"
|
41
|
+
lambda { goodcalc.set_focus }.should_not raise_error
|
42
|
+
goodcalc.close
|
43
|
+
|
44
|
+
badcalc = Calculator.new :title => "BaDTitle"
|
45
|
+
lambda {
|
46
|
+
begin
|
47
|
+
badcalc.setfocus
|
48
|
+
ensure
|
49
|
+
badcalc.kill
|
50
|
+
end
|
51
|
+
}.should raise_error
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should control the focus with 'set_focus'" do
|
55
|
+
@calculator.set_focus
|
56
|
+
keystroke(VK_9)
|
57
|
+
@calculator.edit_window.text.strip.should == "9."
|
58
|
+
|
59
|
+
calculator2 = Calculator.new
|
60
|
+
calculator2.pid.should_not == @calculator.pid
|
61
|
+
calculator2.set_focus
|
62
|
+
keystroke(VK_1, VK_0)
|
63
|
+
calculator2.edit_window.text.strip.should == "10."
|
64
|
+
|
65
|
+
@calculator.set_focus
|
66
|
+
@calculator.edit_window.text.strip.should == "9."
|
67
|
+
|
68
|
+
calculator2.close(:wait_for_close => true)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should open and close the 'About Calculator' dialog via (VK_MENU, VK_H, VK_A)" do
|
72
|
+
@calculator.set_focus
|
73
|
+
dialog_about = @calculator.dialog_about
|
74
|
+
dialog_about.should be_nil
|
75
|
+
keystroke(VK_MENU, VK_H, VK_A)
|
76
|
+
dialog_about = @calculator.dialog_about
|
77
|
+
dialog_about.title.should == "About Calculator"
|
78
|
+
dialog_about.combined_text.should match(/Microsoft . Calculator/)
|
79
|
+
dialog_about.close
|
80
|
+
@calculator.dialog_about.should be_nil
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "calculations" do
|
84
|
+
before(:each) do
|
85
|
+
@calculator.clear_entry
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should calculate '2+2=4' using the keystroke method" do
|
89
|
+
@calculator.set_focus
|
90
|
+
keystroke(VK_2, VK_ADD, VK_2, VK_RETURN)
|
91
|
+
@calculator.edit_window.text.strip.should == "4."
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should calculate '2+12=14' using the type_in method" do
|
95
|
+
@calculator.set_focus
|
96
|
+
type_in("2+12=")
|
97
|
+
@calculator.edit_window.text.strip.should == "14."
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "clipboard" do
|
102
|
+
before(:each) do
|
103
|
+
@calculator.clear_entry
|
104
|
+
@calculator.clipboard.text = ""
|
105
|
+
@calculator.clipboard.text.should == ""
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "copy (VK_CONTROL, VK_C)" do
|
109
|
+
it "should copy the edit window" do
|
110
|
+
@calculator.set_focus
|
111
|
+
type_in("3002")
|
112
|
+
@calculator.edit_window.text.strip.should == "3,002."
|
113
|
+
@calculator.edit_window.set_focus
|
114
|
+
keystroke(VK_CONTROL, VK_C)
|
115
|
+
@calculator.clipboard.text.should == "3002"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "paste (VK_CONTROL, VK_V)" do
|
120
|
+
it "should paste into the edit window" do
|
121
|
+
@calculator.edit_window.set_focus
|
122
|
+
@calculator.clipboard.text = "12345"
|
123
|
+
@calculator.edit_window.text.strip.should == "0."
|
124
|
+
keystroke(VK_CONTROL, VK_V)
|
125
|
+
@calculator.edit_window.text.strip.should == "12,345."
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Autogui do
|
4
|
+
|
5
|
+
describe 'version' do
|
6
|
+
|
7
|
+
it "should return a string formatted '#.#.#'" do
|
8
|
+
Autogui::version.should match(/(^[\d]+\.[\d]+\.[\d]+$)/)
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('..', __FILE__) unless
|
2
|
+
$LOAD_PATH.include? File.expand_path('..', __FILE__)
|
3
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) unless
|
4
|
+
$LOAD_PATH.include? File.expand_path('../../lib', __FILE__)
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'win32/autogui'
|
8
|
+
require 'spec'
|
9
|
+
require 'spec/autorun'
|
10
|
+
require 'aruba/api'
|
11
|
+
|
12
|
+
# applications
|
13
|
+
require File.expand_path(File.dirname(__FILE__) + '/applications/calculator')
|
14
|
+
|
15
|
+
# aruba helper, returns full path to files in the aruba tmp folder
|
16
|
+
def fullpath(filename)
|
17
|
+
path = File.expand_path(File.join(current_dir, filename))
|
18
|
+
path = `cygpath -w #{path}`.chomp if path.match(/^\/cygdrive/) # cygwin?
|
19
|
+
path
|
20
|
+
end
|
21
|
+
|
22
|
+
# return the contents of "filename" in the aruba tmp folder
|
23
|
+
def get_file_content(filename)
|
24
|
+
in_current_dir do
|
25
|
+
IO.read(filename)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Spec::Runner.configure do |config|
|
30
|
+
config.include Aruba::Api
|
31
|
+
end
|
data/spec/watchr.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
# Watchr: Autotest like functionality
|
2
|
+
#
|
3
|
+
# gem install watchr
|
4
|
+
#
|
5
|
+
# Run me with:
|
6
|
+
#
|
7
|
+
# $ watchr spec/watchr.rb
|
8
|
+
|
9
|
+
require 'term/ansicolor'
|
10
|
+
|
11
|
+
$c = Term::ANSIColor
|
12
|
+
|
13
|
+
def getch
|
14
|
+
state = `stty -g`
|
15
|
+
begin
|
16
|
+
`stty raw -echo cbreak`
|
17
|
+
$stdin.getc
|
18
|
+
ensure
|
19
|
+
`stty #{state}`
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# --------------------------------------------------
|
24
|
+
# Convenience Methods
|
25
|
+
# --------------------------------------------------
|
26
|
+
def all_feature_files
|
27
|
+
Dir['features/*.feature']
|
28
|
+
end
|
29
|
+
|
30
|
+
def all_spec_files
|
31
|
+
files = Dir['spec/**/*_spec\.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
def run(cmd)
|
35
|
+
|
36
|
+
pid = fork do
|
37
|
+
puts "\n"
|
38
|
+
print $c.cyan, cmd, $c.clear, "\n"
|
39
|
+
exec(cmd)
|
40
|
+
end
|
41
|
+
Signal.trap('INT') do
|
42
|
+
puts "sending KILL to pid: #{pid}"
|
43
|
+
Process.kill("KILL", pid)
|
44
|
+
end
|
45
|
+
Process.waitpid(pid)
|
46
|
+
|
47
|
+
prompt
|
48
|
+
end
|
49
|
+
|
50
|
+
def run_all
|
51
|
+
run_all_specs
|
52
|
+
run_default_cucumber
|
53
|
+
end
|
54
|
+
|
55
|
+
# allow cucumber rerun.txt smarts
|
56
|
+
def run_default_cucumber
|
57
|
+
cmd = "cucumber"
|
58
|
+
run(cmd)
|
59
|
+
end
|
60
|
+
|
61
|
+
def run_all_features
|
62
|
+
cmd = "cucumber #{all_feature_files.join(' ')}"
|
63
|
+
run(cmd)
|
64
|
+
end
|
65
|
+
|
66
|
+
def run_feature(feature)
|
67
|
+
cmd = "cucumber #{feature}"
|
68
|
+
$last_feature = feature
|
69
|
+
run(cmd)
|
70
|
+
end
|
71
|
+
|
72
|
+
def run_last_feature
|
73
|
+
run_feature($last_feature) if $last_feature
|
74
|
+
end
|
75
|
+
|
76
|
+
def run_default_spec
|
77
|
+
cmd = "spec _1.3.1_ --color --format s ./spec"
|
78
|
+
run(cmd)
|
79
|
+
end
|
80
|
+
|
81
|
+
def run_all_specs
|
82
|
+
cmd = "spec _1.3.1_ --color --format s #{all_spec_files.join(' ')}"
|
83
|
+
p cmd
|
84
|
+
run(cmd)
|
85
|
+
end
|
86
|
+
|
87
|
+
def run_spec(spec)
|
88
|
+
cmd = "spec _1.3.1_ --color --format s #{spec}"
|
89
|
+
$last_spec = spec
|
90
|
+
run(cmd)
|
91
|
+
end
|
92
|
+
|
93
|
+
def run_last_spec
|
94
|
+
run_spec($last_spec) if $last_spec
|
95
|
+
end
|
96
|
+
|
97
|
+
def prompt
|
98
|
+
puts "Ctrl-\\ for menu, Ctrl-C to quit"
|
99
|
+
end
|
100
|
+
|
101
|
+
# init
|
102
|
+
$last_feature = nil
|
103
|
+
prompt
|
104
|
+
|
105
|
+
# --------------------------------------------------
|
106
|
+
# Watchr Rules
|
107
|
+
# --------------------------------------------------
|
108
|
+
watch( '^features/(.*)\.feature' ) { run_default_cucumber }
|
109
|
+
|
110
|
+
watch( '^bin/(.*)' ) { run_default_cucumber }
|
111
|
+
watch( '^lib/(.*)' ) { run_default_cucumber }
|
112
|
+
|
113
|
+
watch( '^features/step_definitions/(.*)\.rb' ) { run_default_cucumber }
|
114
|
+
watch( '^features/support/(.*)\.rb' ) { run_default_cucumber }
|
115
|
+
|
116
|
+
watch( '^spec/(.*)_spec\.rb' ) { |m| run_spec(m[0]) }
|
117
|
+
# specify just the lib files that have specs
|
118
|
+
# TODO: This can be determined automatically from the spec file naming convention
|
119
|
+
watch( '^lib/(.*)' ) { run_default_spec }
|
120
|
+
watch( '^spec/applications/(.*)' ) { run_default_spec }
|
121
|
+
|
122
|
+
# --------------------------------------------------
|
123
|
+
# Signal Handling
|
124
|
+
# --------------------------------------------------
|
125
|
+
|
126
|
+
# Ctrl-\
|
127
|
+
Signal.trap('QUIT') do
|
128
|
+
|
129
|
+
puts "\n\nMENU: a = all , f = features s = specs, l = last feature (#{$last_feature ? $last_feature : 'none'}), q = quit\n\n"
|
130
|
+
c = getch
|
131
|
+
puts c.chr
|
132
|
+
if c.chr == "a"
|
133
|
+
run_all
|
134
|
+
elsif c.chr == "f"
|
135
|
+
run_default_cucumber
|
136
|
+
elsif c.chr == "s"
|
137
|
+
run_all_specs
|
138
|
+
elsif c.chr == "q"
|
139
|
+
abort("exiting\n")
|
140
|
+
elsif c.chr == "l"
|
141
|
+
run_last_feature
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|