rautomation 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.rdoc CHANGED
@@ -1,3 +1,7 @@
1
+ === Version 0.2.0 / 2010-12-17
2
+
3
+ * added Window#pid method
4
+
1
5
  === Version 0.1.0 / 2010-12-14
2
6
 
3
7
  * added new default adapter for Windows: FFI
data/README.rdoc CHANGED
@@ -5,14 +5,14 @@
5
5
 
6
6
  == DESCRIPTION
7
7
 
8
- RAutomation tries to be a small and easy to use library for helping out to automate windows and their controls
8
+ RAutomation is a small and easy to use library for helping out to automate windows and their controls
9
9
  for automated testing.
10
10
 
11
- RAutomation aims to provide:
12
- * Easy to use and user-friendly API (inspired by Watir http://www.watir.com).
11
+ RAutomation provides:
12
+ * Easy to use and user-friendly API (inspired by Watir http://www.watir.com)
13
13
  * Cross-platform compatibility
14
- * Easy extensibility - have some application, which uses some specialized technology, but isn't supported by RAutomation?
15
- You can get dirty and create a new adapter for RAutomation right away!
14
+ * Easy extensibility - with small scripting effort it's possible to add support for not yet
15
+ supported platforms or technologies
16
16
 
17
17
  == USAGE
18
18
 
@@ -30,16 +30,17 @@ RAutomation aims to provide:
30
30
  button.click
31
31
 
32
32
  window2 = RAutomation::Window.new(:title => "Other Title", :adapter => :autoit) # use AutoIt adapter
33
- window2.WinClose("[TITLE:Other Title]") # use adapter's (in this case AutoIt's) internal methods directly
33
+ # use adapter's (in this case AutoIt's) internal methods not part of the public API directly
34
+ window2.WinClose("[TITLE:Other Title]")
34
35
 
35
- See more examples in spec directory!
36
+ Check out the documentation for other possible usages!
36
37
 
37
38
  == INSTALL
38
39
 
39
40
  === Windows
40
41
 
41
42
  1. gem install rautomation
42
- 2. create some script and run it
43
+ 2. create script and run it
43
44
 
44
45
  Available adapters:
45
46
  * :ffi - uses Windows API directly with FFI (default)
@@ -51,18 +52,32 @@ You might need administrative privileges if running for the first time and you h
51
52
  === Linux
52
53
 
53
54
  Feel yourself at home on Linux and know how to automate windows and their controls? I would be happy if you'd contact me
54
- about that matter - or even better, send me a pull request!
55
+ about that matter - or even better, follow the instructions at "How to create a new adapter?"
55
56
 
56
57
  === OS X
57
58
 
58
59
  Feel yourself at home on OS X and know how to automate windows and their controls? I would be happy if you'd contact me
59
- about that matter - or even better, send me a pull request!
60
+ about that matter - or even better, follow the instructions at "How to create a new adapter?"
60
61
 
61
62
  === Others
62
63
 
63
64
  Feel yourself at home on some operating system not listed in here and know how to automate windows and their controls?
64
65
  Does Ruby also work on that operating system? I would be happy if you'd contact me
65
- about that matter - or even better, send me a pull request!
66
+ about that matter - or even better, follow the instructions at "How to create a new adapter?"
67
+
68
+ == How to create a new adapter?
69
+
70
+ 1. Fork the project.
71
+ 2. Create entry point file to lib/rautomation/adapter which should load all adapter specific files.
72
+ 3. Add `autoload` statement into lib/rautomation/adapter/helper.rb for that file.
73
+ 4. Copy button.rb, text_field.rb and window.rb from some of the existing adapter's directory.
74
+ 5. Add spec data for your adapter into spec/spec_helper DATA constant.
75
+ 6. Use environment variable __RAUTOMATION_ADAPTER__ to point to that adapter.
76
+ 7. Start coding and spec-ing.
77
+
78
+ Don't forget to fix the documentation for that adapter also!
79
+
80
+ In case of any problems, feel free to contact me.
66
81
 
67
82
  == Note on Patches/Pull Requests
68
83
 
data/Rakefile CHANGED
@@ -6,14 +6,14 @@ begin
6
6
  Jeweler::Tasks.new do |gem|
7
7
  gem.name = "rautomation"
8
8
  gem.summary = %Q{Automate windows and their controls through user-friendly API with Ruby}
9
- gem.description = %Q{RAutomation tries to be a small and easy to use library for helping out to automate windows and their controls
9
+ gem.description = %Q{RAutomation is a small and easy to use library for helping out to automate windows and their controls
10
10
  for automated testing.
11
11
 
12
- RAutomation aims to provide:
13
- * Easy to use and user-friendly API (inspired by Watir http://www.watir.com).
12
+ RAutomation provides:
13
+ * Easy to use and user-friendly API (inspired by Watir http://www.watir.com)
14
14
  * Cross-platform compatibility
15
- * Easy extensibility - have some application, which uses some specialized technology, but isn't supported by RAutomation?
16
- You can get dirty and create a new adapter for RAutomation!}
15
+ * Easy extensibility - with small scripting effort it's possible to add support for not yet
16
+ supported platforms or technologies}
17
17
  gem.email = "jarmo.p@gmail.com"
18
18
  gem.homepage = "http://github.com/jarmo/RAutomation"
19
19
  gem.authors = ["Jarmo Pertman"]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -5,6 +5,7 @@ module RAutomation
5
5
  include WaitHelper
6
6
  include Locators
7
7
 
8
+ # @private
8
9
  # Special-cased locators
9
10
  LOCATORS = {
10
11
  [:class, Regexp] => :regexpclass,
@@ -12,13 +13,22 @@ module RAutomation
12
13
  :value => :text
13
14
  }
14
15
 
15
- # Possible locators are :value, :id, :class and :index.
16
+ # Creates the button object.
17
+ # @note this method is not meant to be accessed directly, but only through {RAutomation::Window#button}!
18
+ # @param [RAutomation::Window] window this button belongs to.
19
+ # @param [Hash] locators for searching the button.
20
+ # @option locators [String, Regexp] :value Value (text) of the button
21
+ # @option locators [String, Regexp] :class Internal class name of the button
22
+ # @option locators [String, Fixnum] :id Internal ID of the button
23
+ # @option locators [String, Fixnum] :index 0-based index to specify n-th button if all other criteria match
24
+ # @see RAutomation::Window#button
16
25
  def initialize(window, locators)
17
26
  @window = window
18
27
  extract(locators)
19
28
  end
20
29
 
21
- def click #:nodoc:
30
+ # @see RAutomation::Button#click
31
+ def click
22
32
  clicked = false
23
33
  wait_until do
24
34
  @window.activate
@@ -31,11 +41,13 @@ module RAutomation
31
41
  end
32
42
  end
33
43
 
34
- def value #:nodoc:
44
+ # @see RAutomation::Button#value
45
+ def value
35
46
  Window.autoit.ControlGetText(@window.locator_hwnd, "", @locators)
36
47
  end
37
48
 
38
- def exists? #:nodoc:
49
+ # @see RAutomation::Button#exists?
50
+ def exists?
39
51
  not Window.autoit.ControlGetHandle(@window.locator_hwnd, "", @locators).empty?
40
52
  end
41
53
  end
@@ -1,11 +1,12 @@
1
1
  module RAutomation
2
2
  module Adapter
3
3
  module Autoit
4
+ # @private
4
5
  module Locators
5
6
 
6
7
  private
7
8
 
8
- def extract(locators) #:nodoc:
9
+ def extract(locators)
9
10
  @locators = "[#{locators.map do |locator, value|
10
11
  locator_key = self.class::LOCATORS[locator] || self.class::LOCATORS[[locator, value.class]]
11
12
  value = value.to_i + 1 if locator == :index # use 0-based indexing
@@ -5,19 +5,30 @@ module RAutomation
5
5
  include WaitHelper
6
6
  include Locators
7
7
 
8
+ # @private
8
9
  # Special-cased locators
9
10
  LOCATORS = {
10
11
  [:class, Regexp] => :regexpclass,
11
- :index => :instance
12
+ :index => :instance,
13
+ :value => :text
12
14
  }
13
15
 
14
- # Possible locators are :id, :class and :index.
16
+ # Creates the text field object.
17
+ # @note this method is not meant to be accessed directly, but only through {RAutomation::Window#text_field}!
18
+ # @param [RAutomation::Window] window this text field belongs to.
19
+ # @param [Hash] locators for searching the text field.
20
+ # @option locators [String, Regexp] :class Internal class name of the text field
21
+ # @option locators [String, Regexp] :value Value (text) of the text field
22
+ # @option locators [String, Fixnum] :id Internal ID of the text field
23
+ # @option locators [String, Fixnum] :index 0-based index to specify n-th text field if all other criteria match
24
+ # @see RAutomation::Window#text_field
15
25
  def initialize(window, locators)
16
26
  @window = window
17
27
  extract(locators)
18
28
  end
19
29
 
20
- def set(text) #:nodoc:
30
+ # @see RAutomation::TextField#set
31
+ def set(text)
21
32
  wait_until do
22
33
  @window.activate
23
34
  @window.active? &&
@@ -27,15 +38,18 @@ module RAutomation
27
38
  end
28
39
  end
29
40
 
30
- def clear #:nodoc:
41
+ # @see RAutomation::TextField#clear
42
+ def clear
31
43
  set ""
32
44
  end
33
45
 
34
- def value #:nodoc:
46
+ # @see RAutomation::TextField#value
47
+ def value
35
48
  Window.autoit.ControlGetText(@window.locator_hwnd, "", @locators)
36
49
  end
37
50
 
38
- def exists? #:nodoc:
51
+ # @see RAutomation::TextField#exists?
52
+ def exists?
39
53
  not Window.autoit.ControlGetHandle(@window.locator_hwnd, "", @locators).empty?
40
54
  end
41
55
  end
@@ -6,11 +6,13 @@ module RAutomation
6
6
  include Locators
7
7
 
8
8
  class << self
9
- def autoit #:nodoc:
9
+ # @private
10
+ def autoit
10
11
  @@autoit
11
12
  end
12
13
 
13
- def load_autoit #:nodoc:
14
+ # @private
15
+ def load_autoit
14
16
  @@autoit = WIN32OLE.new('AutoItX3.Control')
15
17
  rescue WIN32OLERuntimeError
16
18
  dll = File.dirname(__FILE__) + "/../../../../ext/AutoItX/AutoItX3.dll"
@@ -22,8 +24,10 @@ module RAutomation
22
24
  load_autoit
23
25
  @@autoit.AutoItSetOption("WinWaitDelay", 350)
24
26
 
27
+ # Locators of the window.
25
28
  attr_reader :locators
26
29
 
30
+ # @private
27
31
  # Special-cased locators
28
32
  LOCATORS = {
29
33
  [:title, Regexp] => :regexptitle,
@@ -31,69 +35,95 @@ module RAutomation
31
35
  :hwnd => :handle
32
36
  }
33
37
 
34
- # Possible locators are :title, :text, :hwnd, :class and :index.
38
+ # Creates the window object.
39
+ # @note this method is not meant to be accessed directly, but only through {RAutomation::Window#initialize}!
40
+ # @param [Hash] locators for searching the window.
41
+ # @option locators [String, Regexp] :title Title of the window
42
+ # @option locators [String, Regexp] :text Visible text of the window
43
+ # @option locators [String, Regexp] :class Internal class name of the window
44
+ # @option locators [String, Fixnum] :hwnd Window handle in decimal format
45
+ # @option locators [String, Fixnum] :index 0-based index to specify n-th window if all other criteria match
46
+ # @see RAutomation::Window#initialize
35
47
  def initialize(locators)
36
48
  @hwnd = locators[:hwnd]
37
49
  @locator_text = locators.delete(:text)
38
50
  extract(locators)
39
51
  end
40
52
 
41
- # Returns handle of the found window.
42
- # Searches only for visible windows with having some text at all.
43
- def hwnd #:nodoc:
53
+ # Retrieves handle of the window.
54
+ # @note Searches only for visible windows with having some text at all.
55
+ # @see RAutomation::Window#hwnd
56
+ def hwnd
44
57
  @hwnd ||= @@autoit.WinList(@locators, @locator_text).pop.compact.
45
58
  find {|handle| w = self.class.new(:hwnd => handle.hex); w.visible? && !w.text.empty?}.
46
59
  hex rescue nil
47
60
  end
48
61
 
49
- def title #:nodoc:
62
+ # @see RAutomation::Window#pid
63
+ def pid
64
+ @@autoit.WinGetProcess(hwnd)
65
+ end
66
+
67
+ # @see RAutomation::Window#title
68
+ def title
50
69
  @@autoit.WinGetTitle(locator_hwnd)
51
70
  end
52
71
 
53
- def activate #:nodoc:
72
+ # @see RAutomation::Window#activate
73
+ def activate
54
74
  @@autoit.WinWait(locator_hwnd, "", 1)
55
75
  @@autoit.WinActivate(locator_hwnd)
56
76
  sleep 1
57
77
  end
58
78
 
59
- def active? #:nodoc:
79
+ # @see RAutomation::Window#active?
80
+ def active?
60
81
  @@autoit.WinActive(locator_hwnd) == 1
61
82
  end
62
83
 
63
- def text #:nodoc:
84
+ # @see RAutomation::Window#text
85
+ def text
64
86
  @@autoit.WinGetText(locator_hwnd)
65
87
  end
66
88
 
67
- def exists? #:nodoc:
89
+ # @see RAutomation::Window#exists?
90
+ def exists?
68
91
  @@autoit.WinExists(locator_hwnd) == 1
69
92
  end
70
93
 
71
- def visible? #:nodoc:
94
+ # @see RAutomation::Window#visible?
95
+ def visible?
72
96
  @@autoit.WinGetState(locator_hwnd) & 2 == 2
73
97
  end
74
98
 
75
- def maximize #:nodoc:
99
+ # @see RAutomation::Window#maximize
100
+ def maximize
76
101
  @@autoit.WinSetState(locator_hwnd, "", @@autoit.SW_MAXIMIZE)
77
102
  sleep 1
78
103
  end
79
104
 
80
- def minimize #:nodoc:
105
+ # @see RAutomation::Window#minimize
106
+ def minimize
81
107
  @@autoit.WinSetState(locator_hwnd, "", @@autoit.SW_MINIMIZE)
82
108
  sleep 1
83
109
  end
84
110
 
85
- def minimized? #:nodoc:
111
+ # @see RAutomation::Window#minimized?
112
+ def minimized?
86
113
  @@autoit.WinGetState(locator_hwnd) & 16 == 16
87
114
  end
88
115
 
89
- def restore #:nodoc:
116
+ # @see RAutomation::Window#restore
117
+ def restore
90
118
  @@autoit.WinSetState(locator_hwnd, "", @@autoit.SW_RESTORE)
91
119
  sleep 1
92
120
  end
93
121
 
94
- # Activates the Window and sends keys to it.
122
+ # Activates the window and sends keys to it.
95
123
  #
96
- # Refer to AutoIt documentation for keys syntax.
124
+ # Refer to AutoIt documentation at http://www.autoitscript.com/autoit3/docs/appendix/SendKeys.htm
125
+ # for keys syntax.
126
+ # @see RAutomation::Window#send_keys
97
127
  def send_keys(keys)
98
128
  wait_until do
99
129
  activate
@@ -102,24 +132,32 @@ module RAutomation
102
132
  @@autoit.Send(keys)
103
133
  end
104
134
 
105
- def close #:nodoc:
135
+ # @see RAutomation::Window#close
136
+ def close
106
137
  @@autoit.WinClose(locator_hwnd)
107
138
  @@autoit.WinKill(locator_hwnd)
108
139
  end
109
140
 
110
- def button(locator) #:nodoc:
141
+ # @see Button#initialize
142
+ # @see RAutomation::Window#button
143
+ def button(locator)
111
144
  Button.new(self, locator)
112
145
  end
113
146
 
114
- def text_field(locator) #:nodoc:
147
+ # @see TextField#initialize
148
+ # @see RAutomation::Window#text_field
149
+ def text_field(locator)
115
150
  TextField.new(self, locator)
116
151
  end
117
152
 
118
- def method_missing(name, *args) #:nodoc:
153
+ # Redirects all method calls not part of the public API to the AutoIt directly.
154
+ # @example execute AutoIt's WinGetTitle function:
155
+ # RAutomation::Window.new(:hwnd => 123456).WinGetTitle(...)
156
+ # @see RAutomation::Window#method_missing
157
+ def method_missing(name, *args)
119
158
  @@autoit.send(name, *args)
120
159
  end
121
160
 
122
- # Used internally.
123
161
  # @private
124
162
  def locator_hwnd
125
163
  "[HANDLE:#{hwnd.to_i.to_s(16)}]"
@@ -5,13 +5,22 @@ module RAutomation
5
5
  include WaitHelper
6
6
  include Locators
7
7
 
8
- # Possible locators are :value, :id, :class and :index.
8
+ # Creates the button object.
9
+ # @note this method is not meant to be accessed directly, but only through {RAutomation::Window#button}!
10
+ # @param [RAutomation::Window] window this button belongs to.
11
+ # @param [Hash] locators for searching the button.
12
+ # @option locators [String, Regexp] :value Value (text) of the button
13
+ # @option locators [String, Regexp] :class Internal class name of the button
14
+ # @option locators [String, Fixnum] :id Internal ID of the button
15
+ # @option locators [String, Fixnum] :index 0-based index to specify n-th button if all other criteria match
16
+ # @see RAutomation::Window#button
9
17
  def initialize(window, locators)
10
18
  @window = window
11
19
  extract(locators)
12
20
  end
13
21
 
14
- def click #:nodoc:
22
+ # @see RAutomation::Button#click
23
+ def click
15
24
  clicked = false
16
25
  wait_until do
17
26
  hwnd = Functions.control_hwnd(@window.hwnd, @locators)
@@ -24,11 +33,13 @@ module RAutomation
24
33
  end
25
34
  end
26
35
 
27
- def value #:nodoc:
36
+ # @see RAutomation::Button#value
37
+ def value
28
38
  Functions.control_value(Functions.control_hwnd(@window.hwnd, @locators))
29
39
  end
30
40
 
31
- def exists? #:nodoc:
41
+ # @see RAutomation::Button#exists?
42
+ def exists?
32
43
  !!Functions.control_hwnd(@window.hwnd, @locators)
33
44
  end
34
45
 
@@ -1,6 +1,7 @@
1
1
  module RAutomation
2
2
  module Adapter
3
3
  module Ffi
4
+ # @private
4
5
  module Constants
5
6
  WM_GETTEXT = 0xD
6
7
  WM_SETTEXT = 0xC
@@ -20,10 +21,6 @@ module RAutomation
20
21
  BM_CLICK = 0xF5
21
22
 
22
23
  # keybd_event constants
23
- # http://msdn.microsoft.com/en-us/library/ms646304(VS.85).aspx
24
- #
25
- # keycodes themselves are at:
26
- # http://msdn.microsoft.com/en-us/library/dd375731(v=VS.85).aspx
27
24
  KEYEVENTF_EXTENDEDKEY = 0x1
28
25
  KEYEVENTF_KEYUP = 0x2
29
26
  end
@@ -1,6 +1,7 @@
1
1
  module RAutomation
2
2
  module Adapter
3
3
  module Ffi
4
+ # @private
4
5
  module Functions
5
6
  extend FFI::Library
6
7
 
@@ -88,6 +89,12 @@ module RAutomation
88
89
  end
89
90
  end
90
91
 
92
+ def window_pid(hwnd)
93
+ pid = FFI::MemoryPointer.new :int
94
+ window_thread_process_id(hwnd, pid)
95
+ pid.read_int
96
+ end
97
+
91
98
  def window_class(hwnd)
92
99
  class_name = FFI::MemoryPointer.new :char, 512
93
100
  _window_class(hwnd, class_name, 512)
@@ -102,9 +109,7 @@ module RAutomation
102
109
  0, nil, Constants::SMTO_ABORTIFHUNG, 1000, nil)
103
110
  # force it to close
104
111
  unless closed
105
- pid = FFI::MemoryPointer.new :int
106
- window_thread_process_id(hwnd, pid)
107
- process_hwnd = open_process(Constants::PROCESS_ALL_ACCESS, false, pid.read_int)
112
+ process_hwnd = open_process(Constants::PROCESS_ALL_ACCESS, false, window_pid(hwnd))
108
113
  terminate_process(process_hwnd, 0)
109
114
  close_handle(process_hwnd)
110
115
  end
@@ -1,13 +1,19 @@
1
1
  module RAutomation
2
2
  module Adapter
3
3
  module Ffi
4
+ # @private
4
5
  module Locators
5
6
 
6
7
  private
7
8
 
8
- def extract(locators) #:nodoc:
9
- locators[:id] = locators[:id].to_i if locators[:id]
9
+ def extract(locators)
10
+ # windows locators
11
+ @hwnd = locators[:hwnd].to_i if locators[:hwnd]
12
+ locators[:pid] = locators[:pid].to_i if locators[:pid]
10
13
  locators[:index] = locators[:index].to_i if locators[:index]
14
+
15
+ # control locator
16
+ locators[:id] = locators[:id].to_i if locators[:id]
11
17
  @locators = locators
12
18
  end
13
19
  end
@@ -5,13 +5,22 @@ module RAutomation
5
5
  include WaitHelper
6
6
  include Locators
7
7
 
8
- # Possible locators are :id, :class and :index.
8
+ # Creates the text field object.
9
+ # @note this method is not meant to be accessed directly, but only through {RAutomation::Window#text_field}!
10
+ # @param [RAutomation::Window] window this text field belongs to.
11
+ # @param [Hash] locators for searching the text field.
12
+ # @option locators [String, Regexp] :class Internal class name of the text field
13
+ # @option locators [String, Regexp] :value Value (text) of the text field
14
+ # @option locators [String, Fixnum] :id Internal ID of the text field
15
+ # @option locators [String, Fixnum] :index 0-based index to specify n-th text field if all other criteria match
16
+ # @see RAutomation::Window#text_field
9
17
  def initialize(window, locators)
10
18
  @window = window
11
19
  extract(locators)
12
20
  end
13
21
 
14
- def set(text) #:nodoc:
22
+ # @see RAutomation::TextField#set
23
+ def set(text)
15
24
  wait_until do
16
25
  hwnd = Functions.control_hwnd(@window.hwnd, @locators)
17
26
  @window.activate
@@ -22,15 +31,18 @@ module RAutomation
22
31
  end
23
32
  end
24
33
 
25
- def clear #:nodoc:
34
+ # @see RAutomation::TextField#clear
35
+ def clear
26
36
  set ""
27
37
  end
28
38
 
29
- def value #:nodoc:
39
+ # @see RAutomation::TextField#value
40
+ def value
30
41
  Functions.control_value(Functions.control_hwnd(@window.hwnd, @locators))
31
42
  end
32
43
 
33
- def exists? #:nodoc:
44
+ # @see RAutomation::TextField#exists?
45
+ def exists?
34
46
  !!Functions.control_hwnd(@window.hwnd, @locators)
35
47
  end
36
48
 
@@ -5,71 +5,100 @@ module RAutomation
5
5
  include WaitHelper
6
6
  include Locators
7
7
 
8
+ # Locators of the window.
8
9
  attr_reader :locators
9
10
 
10
- # Possible locators are :title, :text, :hwnd and :class.
11
+ # Possible locators are :title, :text, :hwnd, :pid, :class and :index.
12
+
13
+ # Creates the window object.
14
+ # @note this method is not meant to be accessed directly, but only through {RAutomation::Window#initialize}!
15
+ # @param [Hash] locators for searching the window.
16
+ # @option locators [String, Regexp] :title Title of the window
17
+ # @option locators [String, Regexp] :text Visible text of the window
18
+ # @option locators [String, Regexp] :class Internal class name of the window
19
+ # @option locators [String, Fixnum] :hwnd Window handle in decimal format
20
+ # @option locators [String, Fixnum] :pid Window process ID (PID)
21
+ # @option locators [String, Fixnum] :index 0-based index to specify n-th window if all other criteria match
22
+ # all other criteria match
23
+ # @see RAutomation::Window#initialize
11
24
  def initialize(locators)
12
- @hwnd = locators.delete(:hwnd)
13
25
  extract(locators)
14
26
  end
15
27
 
16
- # Returns handle of the found window.
17
- # Searches only for visible windows with having some text at all.
18
- def hwnd #:nodoc:
28
+ # Retrieves handle of the window.
29
+ # @note Searches only for visible windows with having some text at all.
30
+ # @see RAutomation::Window#hwnd
31
+ def hwnd
19
32
  @hwnd ||= Functions.window_hwnd(@locators)
20
33
  end
21
34
 
22
- def title #:nodoc:
35
+ # @see RAutomation::Window#pid
36
+ def pid
37
+ Functions.window_pid(hwnd)
38
+ end
39
+
40
+ # @see RAutomation::Window#title
41
+ def title
23
42
  Functions.window_title(hwnd)
24
43
  end
25
44
 
26
- def activate #:nodoc:
45
+ # @see RAutomation::Window#activate
46
+ def activate
27
47
  return if !exists? || active?
28
48
  restore if minimized?
29
49
  Functions.activate_window(hwnd)
30
50
  sleep 1
31
51
  end
32
52
 
33
- def active? #:nodoc:
53
+ # @see RAutomation::Window#active?
54
+ def active?
34
55
  exists? && Functions.foreground_window == hwnd
35
56
  end
36
57
 
37
- def text #:nodoc:
58
+ # @see RAutomation::Window#text
59
+ def text
38
60
  Functions.window_text(hwnd)
39
61
  end
40
62
 
41
- def exists? #:nodoc:
63
+ # @see RAutomation::Window#exists?
64
+ def exists?
42
65
  result = hwnd && Functions.window_exists(hwnd)
43
66
  !!result
44
67
  end
45
68
 
46
- def visible? #:nodoc:
69
+ # @see RAutomation::Window#visible?
70
+ def visible?
47
71
  Functions.window_visible(hwnd)
48
72
  end
49
73
 
50
- def maximize #:nodoc:
74
+ # @see RAutomation::Window#maximize
75
+ def maximize
51
76
  Functions.show_window(hwnd, Constants::SW_MAXIMIZE)
52
77
  sleep 1
53
78
  end
54
79
 
55
- def minimize #:nodoc:
80
+ # @see RAutomation::Window#minimize
81
+ def minimize
56
82
  Functions.show_window(hwnd, Constants::SW_MINIMIZE)
57
83
  sleep 1
58
84
  end
59
85
 
60
- def minimized? #:nodoc:
86
+ # @see RAutomation::Window#minimized?
87
+ def minimized?
61
88
  Functions.minimized(hwnd)
62
89
  end
63
90
 
64
- def restore #:nodoc:
91
+ # @see RAutomation::Window#restore
92
+ def restore
65
93
  Functions.show_window(hwnd, Constants::SW_RESTORE)
66
94
  sleep 1
67
95
  end
68
96
 
69
- # Activates the Window and sends keys to it.
97
+ # Activates the window and sends keys to it.
70
98
  #
71
99
  # Refer to MSDN documentation at http://msdn.microsoft.com/en-us/library/dd375731(v=VS.85).aspx
72
- # for keycodes.
100
+ # for the keycodes.
101
+ # @see RAutomation::Window#send_keys
73
102
  def send_keys(*keys)
74
103
  keys.each do |key|
75
104
  wait_until do
@@ -81,21 +110,29 @@ module RAutomation
81
110
  end
82
111
  end
83
112
 
84
- def close #:nodoc:
113
+ # @see RAutomation::Window#close
114
+ def close
85
115
  Functions.close_window(hwnd)
86
116
  end
87
117
 
88
- def button(locator) #:nodoc:
118
+ # @see Button#initialize
119
+ # @see RAutomation::Window#button
120
+ def button(locator)
89
121
  Button.new(self, locator)
90
122
  end
91
123
 
92
- def text_field(locator) #:nodoc:
124
+ # @see TextField#initialize
125
+ # @see RAutomation::Window#text_field
126
+ def text_field(locator)
93
127
  TextField.new(self, locator)
94
128
  end
95
129
 
96
- def method_missing(name, *args) #:nodoc:
130
+ # Redirects all method calls not part of the public API to the {Functions} directly.
131
+ # @see RAutomation::Window#method_missing
132
+ def method_missing(name, *args)
97
133
  Functions.respond_to?(name) ? Functions.send(name, *args) : super
98
134
  end
135
+
99
136
  end
100
137
  end
101
138
  end
@@ -7,6 +7,7 @@ module RAutomation
7
7
  extend self
8
8
 
9
9
  # @private
10
+ # Retrieves default {Adapter} for the current platform.
10
11
  def default_adapter
11
12
  case RUBY_PLATFORM
12
13
  when /mswin|msys|mingw32/
@@ -1,29 +1,30 @@
1
1
  module RAutomation
2
2
  class Button
3
- # This constructor is meant to be accessed only through RAutomation::Window#button method.
4
- def initialize(window, locators) #:nodoc:
3
+ # @private
4
+ # This constructor is meant to be accessed only through {Window#button} method.
5
+ def initialize(window, locators)
5
6
  @window = window
6
7
  @locators = locators
7
8
  @button = @window.button(@locators)
8
9
  end
9
10
 
10
11
  # Performs a click on the Button.
11
- #
12
- # Raises an UnknownButtonException if the Button itself doesn't exist.
12
+ # @raise [UnknownButtonException] if the button doesn't exist.
13
13
  def click
14
14
  wait_until_exists
15
15
  @button.click
16
16
  end
17
17
 
18
- # Retrieves the value of the Button, usually the visible text.
19
- #
20
- # Raises an UnknownButtonException if the Button itself doesn't exist.
18
+ # Retrieves the value (text) of the button, usually the visible text.
19
+ # @return [String] the value (text) of the button.
20
+ # @raise [UnknownButtonException] if the button doesn't exist.
21
21
  def value
22
22
  wait_until_exists
23
23
  @button.value
24
24
  end
25
25
 
26
- # Returns true if Button exists, false otherwise.
26
+ # Checks if the button exists.
27
+ # @return [Boolean] true if button exists, false otherwise.
27
28
  def exists?
28
29
  @button.exists?
29
30
  end
@@ -1,37 +1,38 @@
1
1
  module RAutomation
2
2
  class TextField
3
- # This constructor is meant to be accessed only through RAutomation::Window#text_field method.
3
+ # @private
4
+ # This constructor is meant to be accessed only through {Window#text_field} method.
4
5
  def initialize(window, locators) #:nodoc:
5
6
  @window = window
6
7
  @locators = locators
7
8
  @text_field = @window.text_field(@locators)
8
9
  end
9
10
 
10
- # Sets TextField's text to +text+.
11
- #
12
- # Raises an UnknownTextFieldException if the TextField itself doesn't exist.
11
+ # Sets text of the text field.
12
+ # @param [String] text of the field to set.
13
+ # @raise [UnknownTextFieldException] if the text field doesn't exist.
13
14
  def set(text)
14
15
  wait_until_exists
15
16
  @text_field.set(text)
16
17
  end
17
18
 
18
- # Clears TextField's text.
19
- #
20
- # Raises an UnknownTextFieldException if the TextField itself doesn't exist.
19
+ # Clears text field's text.
20
+ # @raise [UnknownTextFieldException] if the text field doesn't exist.
21
21
  def clear
22
22
  wait_until_exists
23
23
  @text_field.clear
24
24
  end
25
25
 
26
- # Returns TextField's text.
27
- #
28
- # Raises an UnknownTextFieldException if the TextField itself doesn't exist.
26
+ # Returns text field's current value (text).
27
+ # @return [String] the value (text) of the text field.
28
+ # @raise [UnknownTextFieldException] if the text field doesn't exist.
29
29
  def value
30
30
  wait_until_exists
31
31
  @text_field.value
32
32
  end
33
33
 
34
- # Returns true if TextField exists, false otherwise.
34
+ # Checks if the text field exists.
35
+ # @return [Boolean] true if text field exists, false otherwise.
35
36
  def exists?
36
37
  @text_field.exists?
37
38
  end
@@ -1,13 +1,13 @@
1
1
  module RAutomation
2
+ # Waiting with timeout
2
3
  module WaitHelper
3
4
  extend self
4
5
 
5
6
  class TimeoutError < StandardError
6
7
  end
7
8
 
8
- #
9
+ # @private
9
10
  # Wait until the block evaluates to true or times out.
10
- #
11
11
  def wait_until(timeout = 60, &block)
12
12
  end_time = ::Time.now + timeout
13
13
 
@@ -9,173 +9,181 @@ module RAutomation
9
9
  class Window
10
10
  include Adapter::Helper
11
11
 
12
+ # Currently used {Adapter}.
12
13
  attr_reader :adapter
13
14
 
14
- # Creates a new Window object using the _locators_ Hash parameter.
15
+ # Creates the window object.
15
16
  #
16
- # Possible Window _locators_ may depend of the used platform and adapter, but
17
+ # Possible window _locators_ may depend of the used platform and adapter, but
17
18
  # following examples will use :title, :class and :hwnd.
18
19
  #
19
- # Use window with _some title_ being part of it's title:
20
+ # @example Use window with some title:
20
21
  # RAutomation::Window.new(:title => "some title")
21
22
  #
22
- # Use window with Regexp title:
23
+ # @example Use window with Regexp title:
23
24
  # RAutomation::Window.new(:title => /some title/i)
24
25
  #
25
- # Use window with handle _123456_:
26
+ # @example Use window with handle (hwnd):
26
27
  # RAutomation::Window.new(:hwnd => 123456)
27
28
  #
28
- # It is possible to use multiple locators together where every locator will be matched (AND-ed) to the window:
29
+ # @example Use multiple locators, every locator will be matched (AND-ed) to the window:
29
30
  # RAutomation::Window.new(:title => "some title", :class => "IEFrame")
30
31
  #
31
- # Refer to all possible locators in each adapter's documentation.
32
+ # @note Refer to all possible _locators_ in each {Adapter} documentation.
32
33
  #
33
34
  # _locators_ may also include a key called :adapter to change default adapter,
34
35
  # which is dependent of the platform, to automate windows and their controls.
35
36
  #
36
- # It is also possible to change default adapter by using environment variable:
37
- # <em>RAUTOMATION_ADAPTER</em>
37
+ # It is also possible to change the default adapter by using environment variable called
38
+ # __RAUTOMATION_ADAPTER__
38
39
  #
39
- # * Object creation doesn't check for window's existence.
40
- # * Window to be searched for has to be visible!
40
+ # @note This constructor doesn't ensure window's existance.
41
+ # @note Window to be searched for has to be visible.
42
+ # @param [Hash] locators for the window.
41
43
  def initialize(locators)
42
44
  @adapter = locators.delete(:adapter) || ENV["RAUTOMATION_ADAPTER"] && ENV["RAUTOMATION_ADAPTER"].to_sym || default_adapter
43
45
  @window = Adapter.const_get(normalize(@adapter)).const_get(:Window).new(locators)
44
46
  end
45
47
 
46
48
  class << self
47
- # Timeout for waiting until object exists. If the timeout exceeds then an Exception is thrown.
49
+ # Timeout for waiting until object exists. If the timeout exceeds then an {WaitHelper::TimeoutError} is raised.
48
50
  @@wait_timeout = 60
49
51
 
52
+ # Change the timeout to wait before {WaitHelper::TimeoutError} is raised.
53
+ # @param [Fixnum] timeout in seconds.
50
54
  def wait_timeout=(timeout)
51
55
  @@wait_timeout = timeout
52
56
  end
53
57
 
58
+ # Retrieve current timeout in seconds to wait before {WaitHelper::TimeoutError} is raised.
59
+ # @return [Fixnum] timeout in seconds
54
60
  def wait_timeout
55
61
  @@wait_timeout
56
62
  end
57
63
  end
58
64
 
59
- # Returns handle of the Window.
60
- #
61
- # This handle will be used internally for all operations.
65
+ # @return [Fixnum] handle of the window which is used internally for other methods.
62
66
  def hwnd
63
67
  wait_until_exists
64
68
  @window.hwnd
65
69
  end
66
70
 
67
- # Returns title of the Window.
68
- #
69
- # Raises an UnknownWindowException if the Window itself doesn't exist.
71
+ # @return [Fixnum] process identifier (PID) of the window.
72
+ def pid
73
+ wait_until_exists
74
+ @window.pid
75
+ end
76
+
77
+ # @return [String] title of the window.
78
+ # @raise [UnknownWindowException] if the window doesn't exist.
70
79
  def title
71
80
  wait_until_exists
72
81
  @window.title
73
82
  end
74
83
 
75
- # Activates the Window, e.g. brings to the top.
84
+ # Activates the Window, e.g. brings it to the top of other windows.
76
85
  def activate
77
86
  @window.activate
78
87
  end
79
88
 
80
- # Returns true if the Window is active, false otherwise.
89
+ # Checks if the window is active, e.g. on the top of other windows.
90
+ # @return [Boolean] true if the window is active, false otherwise.
81
91
  def active?
82
92
  @window.active?
83
93
  end
84
94
 
85
95
  # Returns visible text of the Window.
86
- #
87
- # Raises an UnknownWindowException if the Window itself doesn't exist.
96
+ # @return [String] visible text of the window.
97
+ # @raise [UnknownWindowException] if the window doesn't exist.
88
98
  def text
89
99
  wait_until_exists
90
100
  @window.text
91
101
  end
92
102
 
93
- # Returns true if the Window exists, false otherwise.
103
+ # Checks if the window exists (does have to be visible).
104
+ # @return [Boolean] true if the window exists, false otherwise.
94
105
  def exists?
95
106
  @window.exists?
96
107
  end
97
108
 
98
109
  alias_method :exist?, :exists?
99
110
 
100
- # Returns true if the Window is visible, false otherwise.
101
- # Window is also visible, if it is behind other windows or minimized.
102
- #
103
- # Raises an UnknownWindowException if the Window itself doesn't exist.
111
+ # Checks if window is visible.
112
+ # @note Window is also visible, if it is behind other windows or minimized.
113
+ # @return [Boolean] true if window is visible, false otherwise.
114
+ # @raise [UnknownWindowException] if the window doesn't exist.
104
115
  def visible?
105
116
  wait_until_exists
106
117
  @window.visible?
107
118
  end
108
119
 
109
- # Returns true if the Window exists and is visible, false otherwise.
120
+ # Checks if the window exists and is visible.
121
+ # @return [Boolean] true if window exists and is visible, false otherwise
110
122
  def present?
111
123
  exists? && visible?
112
124
  end
113
125
 
114
- # Maximizes the Window.
115
- #
116
- # Raises an UnknownWindowException if the Window itself doesn't exist.
126
+ # Maximizes the window.
127
+ # @raise [UnknownWindowException] if the window doesn't exist.
117
128
  def maximize
118
129
  wait_until_exists
119
130
  @window.maximize
120
131
  end
121
132
 
122
- # Minimizes the Window.
123
- #
124
- # Raises an UnknownWindowException if the Window itself doesn't exist.
133
+ # Minimizes the window.
134
+ # @raise [UnknownWindowException] if the window doesn't exist.
125
135
  def minimize
126
136
  wait_until_exists
127
137
  @window.minimize
128
138
  end
129
139
 
130
- # Returns true if the Window is minimized, false otherwise.
131
- #
132
- # Raises an UnknownWindowException if the Window itself doesn't exist.
140
+ # Checks if window is minimized.
141
+ # @return [Boolean] true if window is minimized, false otherwise.
142
+ # @raise [UnknownWindowException] if the window doesn't exist.
133
143
  def minimized?
134
144
  wait_until_exists
135
145
  @window.minimized?
136
146
  end
137
147
 
138
- # Restores the Window.
139
- #
140
- # Raises an UnknownWindowException if the Window itself doesn't exist.
148
+ # Restores the window size and position.
149
+ # @note If the window is minimized, makes it visible again.
150
+ # @raise [UnknownWindowException] if the window doesn't exist.
141
151
  def restore
142
152
  wait_until_exists
143
153
  @window.restore
144
154
  end
145
155
 
146
- # Sends keys to the Window. Refer to specific adapter's documentation for possible values.
147
- #
148
- # Raises an UnknownWindowException if the Window itself doesn't exist.
156
+ # Sends keyboard keys to the window. Refer to specific {Adapter} documentation for all possible values.
157
+ # @raise [UnknownWindowException] if the window doesn't exist.
149
158
  def send_keys(*keys)
150
159
  wait_until_exists
151
160
  @window.send_keys(*keys)
152
161
  end
153
162
 
154
- # Closes the Window if it exists.
163
+ # Closes the window if it exists.
155
164
  def close
156
165
  return unless @window.exists?
157
166
  @window.close
158
167
  end
159
168
 
160
- # Returns the Button object by the _locators_ on the Window.
161
- # Refer to specific adapter's documentation for possible parameters.
162
- #
163
- # Raises an UnknownWindowException if the Window itself doesn't exist.
169
+ # Retrieves {Button} on the window.
170
+ # @note Refer to specific {Adapter} documentation for possible _locator_ parameters.
171
+ # @param [Hash] locators for the {Button}.
172
+ # @raise [UnknownWindowException] if the window doesn't exist.
164
173
  def button(locators)
165
174
  wait_until_exists
166
175
  Button.new(@window, locators)
167
176
  end
168
177
 
169
- # Returns the TextField object by the _locators_ on the Window.
170
- # Refer to specific adapter's documentation for possible parameters.
171
- #
172
- # Raises an UnknownWindowException if the Window itself doesn't exist.
178
+ # Retrieves {TextField} on the window.
179
+ # @note Refer to specific {Adapter} documentation for possible _locators_ parameters.
180
+ # @raise [UnknownWindowException] if the window doesn't exist.
173
181
  def text_field(locators)
174
182
  wait_until_exists
175
183
  TextField.new(@window, locators)
176
184
  end
177
185
 
178
- # Allow to execute adapter's methods not part of the public API
186
+ # Allows to execute specific {Adapter} methods not part of the public API.
179
187
  def method_missing(name, *args)
180
188
  @window.send(name, *args)
181
189
  end
data/rautomation.gemspec CHANGED
@@ -5,19 +5,19 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{rautomation}
8
- s.version = "0.1.0"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jarmo Pertman"]
12
- s.date = %q{2010-12-14}
13
- s.description = %q{RAutomation tries to be a small and easy to use library for helping out to automate windows and their controls
12
+ s.date = %q{2010-12-17}
13
+ s.description = %q{RAutomation is a small and easy to use library for helping out to automate windows and their controls
14
14
  for automated testing.
15
15
 
16
- RAutomation aims to provide:
17
- * Easy to use and user-friendly API (inspired by Watir http://www.watir.com).
16
+ RAutomation provides:
17
+ * Easy to use and user-friendly API (inspired by Watir http://www.watir.com)
18
18
  * Cross-platform compatibility
19
- * Easy extensibility - have some application, which uses some specialized technology, but isn't supported by RAutomation?
20
- You can get dirty and create a new adapter for RAutomation!}
19
+ * Easy extensibility - with small scripting effort it's possible to add support for not yet
20
+ supported platforms or technologies}
21
21
  s.email = %q{jarmo.p@gmail.com}
22
22
  s.extra_rdoc_files = [
23
23
  "LICENSE",
data/spec/spec_helper.rb CHANGED
@@ -31,6 +31,7 @@ module SpecHelper
31
31
  :window2_button_text => "OK",
32
32
  # Window 2 should have a text field with the specified class name.
33
33
  :window2_text_field_class => "Edit",
34
+ # Adapter internal method invocation for getting title of window2
34
35
  :title_proc => lambda {|win| win.WinGetTitle("[TITLE:Explorer User Prompt]")}
35
36
  },
36
37
  # This adapter needs Windows OS with Internet Explorer installed into 'c:\program files\internet explorer'.
@@ -53,6 +54,7 @@ module SpecHelper
53
54
  :window2_button_text => "OK",
54
55
  # Window 2 should have a text field with the specified class name.
55
56
  :window2_text_field_class => "Edit",
57
+ # Adapter internal method invocation for getting title of window2
56
58
  :title_proc => lambda {|win| win.window_title(win.hwnd)}
57
59
  }
58
60
  }[ENV["RAUTOMATION_ADAPTER"] && ENV["RAUTOMATION_ADAPTER"].to_sym || RAutomation::Adapter::Helper.default_adapter]
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rautomation
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jarmo Pertman
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-12-14 00:00:00 +02:00
18
+ date: 2010-12-17 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -34,14 +34,14 @@ dependencies:
34
34
  type: :development
35
35
  version_requirements: *id001
36
36
  description: |-
37
- RAutomation tries to be a small and easy to use library for helping out to automate windows and their controls
37
+ RAutomation is a small and easy to use library for helping out to automate windows and their controls
38
38
  for automated testing.
39
39
 
40
- RAutomation aims to provide:
41
- * Easy to use and user-friendly API (inspired by Watir http://www.watir.com).
40
+ RAutomation provides:
41
+ * Easy to use and user-friendly API (inspired by Watir http://www.watir.com)
42
42
  * Cross-platform compatibility
43
- * Easy extensibility - have some application, which uses some specialized technology, but isn't supported by RAutomation?
44
- You can get dirty and create a new adapter for RAutomation!
43
+ * Easy extensibility - with small scripting effort it's possible to add support for not yet
44
+ supported platforms or technologies
45
45
  email: jarmo.p@gmail.com
46
46
  executables: []
47
47