rautomation 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ .idea/*
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Jarmo Pertman
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,71 @@
1
+ = RAutomation
2
+
3
+ * Web: http://www.github.com/jarmo/RAutomation
4
+ * Author: Jarmo Pertman (mailto:jarmo.p[at]gmail.com)
5
+
6
+ == DESCRIPTION
7
+
8
+ RAutomation tries to be a small and easy to use library for helping out to automate windows and their controls
9
+ for automated testing.
10
+
11
+ RAutomation aims to provide:
12
+ * Easy to use and user-friendly API (inspired by Watir http://www.watir.com).
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 new implementation for RAutomation, due to the applied <em>Strategy Pattern</em>!
16
+
17
+ == USAGE
18
+
19
+ require "rautomation"
20
+
21
+ window = RAutomation::Window.new(:title => /part of the title/i)
22
+ window.exists? # => true
23
+
24
+ window.title # => "blah blah part Of the title blah"
25
+ window.text # => "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ultricies..."
26
+
27
+ window.text_field(:class_name => "Edit1").set "hello, world!"
28
+ button = window.button(:text => "&Save")
29
+ button.exists? # => true
30
+ button.click
31
+
32
+ See more examples in spec directory!
33
+
34
+ == INSTALL
35
+
36
+ === Windows
37
+
38
+ 1. gem install rautomation
39
+ 2. create some script and run it
40
+
41
+ You might need administrative privileges if running for the first time and you haven't installed AutoIt before!
42
+
43
+ === Linux
44
+
45
+ Feel yourself at home on Linux and know how to automate windows and their controls? I would be happy if you'd contact me
46
+ about that matter - or even better, send me a pull request!
47
+
48
+ === OS X
49
+
50
+ 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
51
+ about that matter - or even better, send me a pull request!
52
+
53
+ === Others
54
+
55
+ Feel yourself at home on some operating system not listed in here and know how to automate windows and their controls?
56
+ Does Ruby also work on that operating system? I would be happy if you'd contact me
57
+ about that matter - or even better, send me a pull request!
58
+
59
+ == Note on Patches/Pull Requests
60
+
61
+ * Fork the project.
62
+ * Make your feature addition or bug fix.
63
+ * Add tests for it. This is important so I don't break it in a
64
+ future version unintentionally.
65
+ * Commit, do not mess with rakefile, version, or history.
66
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
67
+ * Send me a pull request. Bonus points for topic branches.
68
+
69
+ == Copyright
70
+
71
+ Copyright (c) 2010 Jarmo Pertman. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "rautomation"
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
10
+ for automated testing.
11
+
12
+ RAutomation aims to provide:
13
+ * Easy to use and user-friendly API (inspired by Watir http://www.watir.com).
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 new implementation for RAutomation, due to the applied Strategy Pattern!}
17
+ gem.email = "jarmo.p@gmail.com"
18
+ gem.homepage = "http://github.com/jarmo/RAutomation"
19
+ gem.authors = ["Jarmo Pertman"]
20
+ gem.add_development_dependency "rspec", ">= 1.3.0"
21
+ end
22
+ Jeweler::GemcutterTasks.new
23
+ rescue LoadError
24
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
25
+ end
26
+
27
+ require 'spec/rake/spectask'
28
+ Spec::Rake::SpecTask.new(:spec) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.spec_files = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
34
+ spec.libs << 'lib' << 'spec'
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :spec => :check_dependencies
40
+
41
+ task :default => :spec
42
+
43
+ require 'rake/rdoctask'
44
+ Rake::RDocTask.new do |rdoc|
45
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "RAutomation #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
Binary file
Binary file
@@ -0,0 +1,5 @@
1
+ require "rautomation/wait_helper"
2
+ require "rautomation/implementations/helper"
3
+ require "rautomation/window"
4
+ require "rautomation/button"
5
+ require "rautomation/text_field"
@@ -0,0 +1,39 @@
1
+ module RAutomation
2
+ class Button
3
+ # This constructor is meant to be accessed only through RAutomation::Window#button method.
4
+ def initialize(window, locators) #:nodoc:
5
+ @window = window
6
+ @locators = locators
7
+ @button = @window.button(@locators)
8
+ end
9
+
10
+ # Performs a click on the Button.
11
+ #
12
+ # Raises an UnknownButtonException if the Button itself doesn't exist.
13
+ def click
14
+ assert_exists
15
+ @button.click
16
+ end
17
+
18
+ # Retrieves the value of the Button, usually the visible text.
19
+ #
20
+ # Raises an UnknownButtonException if the Button itself doesn't exist.
21
+ def value
22
+ assert_exists
23
+ @button.value
24
+ end
25
+
26
+ # Returns true if Button exists, false otherwise.
27
+ def exists?
28
+ @button.exists?
29
+ end
30
+
31
+ alias_method :exist?, :exists?
32
+
33
+ private
34
+
35
+ def assert_exists
36
+ raise UnknownButtonException.new("Button '#{@locators.inspect}' doesn't exist on window '#{@window.locators.inspect}'!") unless exists?
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,5 @@
1
+ require "win32ole"
2
+ require File.dirname(__FILE__) + "/autoit/locators"
3
+ require File.dirname(__FILE__) + "/autoit/window"
4
+ require File.dirname(__FILE__) + "/autoit/button"
5
+ require File.dirname(__FILE__) + "/autoit/text_field"
@@ -0,0 +1,40 @@
1
+ module RAutomation
2
+ module Implementations
3
+ module AutoIt
4
+ class Button
5
+ include WaitHelper
6
+ include Locators
7
+
8
+ # Special-cased locators
9
+ LOCATORS = {:class_name => :classnn}
10
+
11
+ # Possible locators are :text, :id, :class, :class_name and :instance.
12
+ def initialize(window, locators)
13
+ @window = window
14
+ extract(locators)
15
+ end
16
+
17
+ def click #:nodoc:
18
+ clicked = false
19
+ wait_until do
20
+ @window.activate
21
+ @window.active? &&
22
+ Window.autoit.ControlFocus(@window.locator_hwnd, "", @locators) == 1 &&
23
+ Window.autoit.ControlClick(@window.locator_hwnd, "", @locators) == 1 &&
24
+ clicked = true # is clicked at least once
25
+
26
+ clicked && !exists?
27
+ end
28
+ end
29
+
30
+ def value #:nodoc:
31
+ Window.autoit.ControlGetText(@window.locator_hwnd, "", @locators)
32
+ end
33
+
34
+ def exists? #:nodoc:
35
+ not Window.autoit.ControlGetHandle(@window.locator_hwnd, "", @locators).empty?
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ module RAutomation
2
+ module Implementations
3
+ module AutoIt
4
+ module Locators
5
+
6
+ private
7
+
8
+ def extract(locators) #:nodoc:
9
+ @locators = "[#{locators.map do |locator, value|
10
+ locator_key = self.class::LOCATORS[locator] || self.class::LOCATORS[[locator, value.class]]
11
+ value = value.to_s(16) if locator == :hwnd
12
+ "#{(locator_key || locator)}:#{value}"
13
+ end.join(";")}]"
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,41 @@
1
+ module RAutomation
2
+ module Implementations
3
+ module AutoIt
4
+ class TextField
5
+ include WaitHelper
6
+ include Locators
7
+
8
+ # Special-cased locators
9
+ LOCATORS = {:class_name => :classnn}
10
+
11
+ # Possible locators are :id, :class, :class_name and :instance.
12
+ def initialize(window, locators)
13
+ @window = window
14
+ extract(locators)
15
+ end
16
+
17
+ def set(text) #:nodoc:
18
+ wait_until do
19
+ @window.activate
20
+ @window.active? &&
21
+ Window.autoit.ControlFocus(@window.locator_hwnd, "", @locators) == 1 &&
22
+ Window.autoit.ControlSetText(@window.locator_hwnd, "", @locators, text) == 1 &&
23
+ value == text
24
+ end
25
+ end
26
+
27
+ def clear #:nodoc:
28
+ set ""
29
+ end
30
+
31
+ def value #:nodoc:
32
+ Window.autoit.ControlGetText(@window.locator_hwnd, "", @locators)
33
+ end
34
+
35
+ def exists? #:nodoc:
36
+ not Window.autoit.ControlGetHandle(@window.locator_hwnd, "", @locators).empty?
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,126 @@
1
+ module RAutomation
2
+ module Implementations
3
+ module AutoIt
4
+ class Window
5
+ include WaitHelper
6
+ include Locators
7
+
8
+ class << self
9
+ def autoit #:nodoc:
10
+ @@autoit
11
+ end
12
+
13
+ def load_autoit #:nodoc:
14
+ @@autoit = WIN32OLE.new('AutoItX3.Control')
15
+ rescue WIN32OLERuntimeError
16
+ dll = File.dirname(__FILE__) + "/../../../ext/AutoItX/AutoItX3.dll"
17
+ system("regsvr32.exe /s #{dll.gsub('/', '\\')}")
18
+ @@autoit = WIN32OLE.new('AutoItX3.Control')
19
+ end
20
+ end
21
+
22
+ load_autoit
23
+ @@autoit.AutoItSetOption("WinWaitDelay", 350)
24
+
25
+ attr_reader :locators
26
+
27
+ # Special-cased locators
28
+ LOCATORS = {[:title, String] => :title,
29
+ [:title, Regexp] => :regexptitle,
30
+ :hwnd => :handle}
31
+
32
+ # Possible locators are :title, :text, :hwnd and :class.
33
+ def initialize(locators)
34
+ @hwnd = locators[:hwnd]
35
+ @locator_text = locators.delete(:text)
36
+ extract(locators)
37
+ end
38
+
39
+ def hwnd #:nodoc:
40
+ @hwnd ||= @@autoit.WinList(@locators, @locator_text).pop.compact.
41
+ find {|handle| self.class.new(:hwnd => handle.hex).visible?}.hex rescue nil
42
+ end
43
+
44
+ def title #:nodoc:
45
+ @@autoit.WinGetTitle(locator_hwnd)
46
+ end
47
+
48
+ def activate #:nodoc:
49
+ @@autoit.WinWait(locator_hwnd, "", 1)
50
+ @@autoit.WinActivate(locator_hwnd)
51
+ sleep 1
52
+ end
53
+
54
+ def active? #:nodoc:
55
+ @@autoit.WinActive(locator_hwnd) == 1
56
+ end
57
+
58
+ def text #:nodoc:
59
+ @@autoit.WinGetText(locator_hwnd)
60
+ end
61
+
62
+ def exists? #:nodoc:
63
+ @@autoit.WinExists(locator_hwnd) == 1
64
+ end
65
+
66
+ def visible? #:nodoc:
67
+ @@autoit.WinGetState(locator_hwnd) & 2 == 2
68
+ end
69
+
70
+ def maximize #:nodoc:
71
+ @@autoit.WinSetState(locator_hwnd, "", @@autoit.SW_MAXIMIZE)
72
+ sleep 1
73
+ end
74
+
75
+ def minimize #:nodoc:
76
+ @@autoit.WinSetState(locator_hwnd, "", @@autoit.SW_MINIMIZE)
77
+ sleep 1
78
+ end
79
+
80
+ def minimized?
81
+ @@autoit.WinGetState(locator_hwnd) & 16 == 16
82
+ end
83
+
84
+ def restore
85
+ @@autoit.WinSetState(locator_hwnd, "", @@autoit.SW_RESTORE)
86
+ sleep 1
87
+ end
88
+
89
+ # Activates the Window and sends keys to it.
90
+ #
91
+ # Refer to AutoIt documentation for keys syntax.
92
+ def send_keys(keys)
93
+ wait_until do
94
+ restore if minimized?
95
+ activate
96
+ active?
97
+ end
98
+ @@autoit.Send(keys)
99
+ end
100
+
101
+ def close #:nodoc:
102
+ @@autoit.WinClose(locator_hwnd)
103
+ @@autoit.WinKill(locator_hwnd)
104
+ end
105
+
106
+ def button(locator) #:nodoc:
107
+ Button.new(self, locator)
108
+ end
109
+
110
+ def text_field(locator) #:nodoc:
111
+ TextField.new(self, locator)
112
+ end
113
+
114
+ def method_missing(name, *args) #:nodoc:
115
+ @@autoit.respond_to?(name) ? @@autoit.send(name, *args) : super
116
+ end
117
+
118
+ # Used internally.
119
+ # @private
120
+ def locator_hwnd
121
+ "[HANDLE:#{hwnd.to_i.to_s(16)}]"
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,19 @@
1
+ module RAutomation
2
+ module Implementations
3
+ autoload :AutoIt, File.dirname(__FILE__) + "/autoit.rb"
4
+
5
+ module Helper
6
+ extend self
7
+
8
+ # @private
9
+ def default_implementation
10
+ case RUBY_PLATFORM
11
+ when /mswin|msys|mingw32/
12
+ Implementations::AutoIt::Window
13
+ else
14
+ raise "unsupported platform for RAutomation: #{RUBY_PLATFORM}"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,47 @@
1
+ module RAutomation
2
+ class TextField
3
+ # This constructor is meant to be accessed only through RAutomation::Window#text_field method.
4
+ def initialize(window, locators) #:nodoc:
5
+ @window = window
6
+ @locators = locators
7
+ @text_field = @window.text_field(@locators)
8
+ end
9
+
10
+ # Sets TextField's text to +text+.
11
+ #
12
+ # Raises an UnknownTextFieldException if the TextField itself doesn't exist.
13
+ def set(text)
14
+ assert_exists
15
+ @text_field.set(text)
16
+ end
17
+
18
+ # Clears TextField's text.
19
+ #
20
+ # Raises an UnknownTextFieldException if the TextField itself doesn't exist.
21
+ def clear
22
+ assert_exists
23
+ @text_field.clear
24
+ end
25
+
26
+ # Returns TextField's text.
27
+ #
28
+ # Raises an UnknownTextFieldException if the TextField itself doesn't exist.
29
+ def value
30
+ assert_exists
31
+ @text_field.value
32
+ end
33
+
34
+ # Returns true if TextField exists, false otherwise.
35
+ def exists?
36
+ @text_field.exists?
37
+ end
38
+
39
+ alias_method :exist?, :exists?
40
+
41
+ private
42
+
43
+ def assert_exists
44
+ raise UnknownTextFieldException.new("Text field '#{@locators.inspect}' doesn't exist on window '#{@window.locators.inspect}'!") unless exists?
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,23 @@
1
+ module RAutomation
2
+ module WaitHelper
3
+ extend self
4
+
5
+ class TimeoutError < StandardError
6
+ end
7
+
8
+ #
9
+ # Wait until the block evaluates to true or times out.
10
+ #
11
+ def wait_until(timeout = 60, &block)
12
+ end_time = ::Time.now + timeout
13
+
14
+ until ::Time.now > end_time
15
+ result = yield(self)
16
+ return result if result
17
+ sleep 0.5
18
+ end
19
+
20
+ raise TimeoutError, "timed out after #{timeout} seconds"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,176 @@
1
+ module RAutomation
2
+ class UnknownWindowException < RuntimeError
3
+ end
4
+ class UnknownButtonException < RuntimeError
5
+ end
6
+ class UnknownTextFieldException < RuntimeError
7
+ end
8
+
9
+ class Window
10
+ include Implementations::Helper
11
+
12
+ attr_reader :implementation
13
+
14
+ # Creates a new Window object using the _locators_ Hash parameter.
15
+ #
16
+ # Possible Window _locators_ may depend of the used platform and implementation, but
17
+ # following examples will use :title, :class and :hwnd.
18
+ #
19
+ # Use window with _some title_ being part of it's title:
20
+ # RAutomation::Window.new(:title => "some title")
21
+ #
22
+ # Use window with Regexp title:
23
+ # RAutomation::Window.new(:title => /some title/i)
24
+ #
25
+ # Use window with handle _123456_:
26
+ # RAutomation::Window.new(:hwnd => 123456)
27
+ #
28
+ # It is possible to use multiple locators together where every locator will be matched (AND-ed) to the window:
29
+ # RAutomation::Window.new(:title => "some title", :class => "IEFrame")
30
+ #
31
+ # Refer to all possible locators in each implementation's documentation.
32
+ #
33
+ # _locators_ may also include a key called :implementation to change default implementation,
34
+ # which is dependent of the platform, to automate windows and their controls.
35
+ #
36
+ # It is also possible to change default implementation by using environment variable:
37
+ # <em>RAUTOMATION_IMPLEMENTATION</em>
38
+ #
39
+ # * Object creation doesn't check for window's existence.
40
+ # * Window to be searched for has to be visible!
41
+ def initialize(locators)
42
+ @implementation = locators.delete(:implementation) || ENV["RAUTOMATION_IMPLEMENTATION"] || default_implementation
43
+ @window = @implementation.new(locators)
44
+ end
45
+
46
+ # Returns handle of the Window.
47
+ #
48
+ # This handle will be used internally for all operations.
49
+ def hwnd
50
+ assert_exists
51
+ @window.hwnd
52
+ end
53
+
54
+ # Returns title of the Window.
55
+ #
56
+ # Raises an UnknownWindowException if the Window itself doesn't exist.
57
+ def title
58
+ assert_exists
59
+ @window.title
60
+ end
61
+
62
+ # Activates the Window, e.g. brings to the top.
63
+ def activate
64
+ @window.activate
65
+ end
66
+
67
+ # Returns true if the Window is active, false otherwise.
68
+ def active?
69
+ @window.active?
70
+ end
71
+
72
+ # Returns visible text of the Window.
73
+ #
74
+ # Raises an UnknownWindowException if the Window itself doesn't exist.
75
+ def text
76
+ assert_exists
77
+ @window.text
78
+ end
79
+
80
+ # Returns true if the Window exists, false otherwise.
81
+ def exists?
82
+ @window.exists?
83
+ end
84
+
85
+ alias_method :exist?, :exists?
86
+
87
+ # Returns true if the Window is visible, false otherwise.
88
+ # Window is also visible, if it is behind other windows or minimized.
89
+ #
90
+ # Raises an UnknownWindowException if the Window itself doesn't exist.
91
+ def visible?
92
+ assert_exists
93
+ @window.visible?
94
+ end
95
+
96
+ # Returns true if the Window exists and is visible, false otherwise.
97
+ def present?
98
+ exists? && visible?
99
+ end
100
+
101
+ # Maximizes the Window.
102
+ #
103
+ # Raises an UnknownWindowException if the Window itself doesn't exist.
104
+ def maximize
105
+ assert_exists
106
+ @window.maximize
107
+ end
108
+
109
+ # Minimizes the Window.
110
+ #
111
+ # Raises an UnknownWindowException if the Window itself doesn't exist.
112
+ def minimize
113
+ assert_exists
114
+ @window.minimize
115
+ end
116
+
117
+ # Returns true if the Window is minimized, false otherwise.
118
+ #
119
+ # Raises an UnknownWindowException if the Window itself doesn't exist.
120
+ def minimized?
121
+ assert_exists
122
+ @window.minimized?
123
+ end
124
+
125
+ # Restores the Window.
126
+ #
127
+ # Raises an UnknownWindowException if the Window itself doesn't exist.
128
+ def restore
129
+ assert_exists
130
+ @window.restore
131
+ end
132
+
133
+ # Sends keys to the Window. Refer to specific implementation's documentation for possible values.
134
+ #
135
+ # Raises an UnknownWindowException if the Window itself doesn't exist.
136
+ def send_keys(keys)
137
+ assert_exists
138
+ @window.send_keys(keys)
139
+ end
140
+
141
+ # Closes the Window if it exists.
142
+ def close
143
+ return unless @window.exists?
144
+ @window.close
145
+ end
146
+
147
+ # Returns the Button object by the _locators_ on the Window.
148
+ # Refer to specific implementation's documentation for possible parameters.
149
+ #
150
+ # Raises an UnknownWindowException if the Window itself doesn't exist.
151
+ def button(locators)
152
+ assert_exists
153
+ Button.new(@window, locators)
154
+ end
155
+
156
+ # Returns the TextField object by the _locators_ on the Window.
157
+ # Refer to specific implementation's documentation for possible parameters.
158
+ #
159
+ # Raises an UnknownWindowException if the Window itself doesn't exist.
160
+ def text_field(locators)
161
+ assert_exists
162
+ TextField.new(@window, locators)
163
+ end
164
+
165
+ # Allow to execute implementation's methods not part of the public API
166
+ def method_missing(name, *args)
167
+ @window.respond_to?(name) ? @window.send(name, *args) : super
168
+ end
169
+
170
+ private
171
+
172
+ def assert_exists
173
+ raise UnknownWindowException.new("Window with locator '#{@window.locators.inspect}' doesn't exist!") unless exists?
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,79 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rautomation}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Jarmo Pertman"]
12
+ s.date = %q{2010-10-13}
13
+ s.description = %q{RAutomation tries to be a small and easy to use library for helping out to automate windows and their controls
14
+ for automated testing.
15
+
16
+ RAutomation aims to provide:
17
+ * Easy to use and user-friendly API (inspired by Watir http://www.watir.com).
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 new implementation for RAutomation, due to the applied Strategy Pattern!}
21
+ s.email = %q{jarmo.p@gmail.com}
22
+ s.extra_rdoc_files = [
23
+ "LICENSE",
24
+ "README.rdoc"
25
+ ]
26
+ s.files = [
27
+ ".document",
28
+ ".gitignore",
29
+ "LICENSE",
30
+ "README.rdoc",
31
+ "Rakefile",
32
+ "VERSION",
33
+ "ext/AutoItX/AutoItX.chm",
34
+ "ext/AutoItX/AutoItX3.dll",
35
+ "lib/rautomation.rb",
36
+ "lib/rautomation/button.rb",
37
+ "lib/rautomation/implementations/autoit.rb",
38
+ "lib/rautomation/implementations/autoit/button.rb",
39
+ "lib/rautomation/implementations/autoit/locators.rb",
40
+ "lib/rautomation/implementations/autoit/text_field.rb",
41
+ "lib/rautomation/implementations/autoit/window.rb",
42
+ "lib/rautomation/implementations/helper.rb",
43
+ "lib/rautomation/text_field.rb",
44
+ "lib/rautomation/wait_helper.rb",
45
+ "lib/rautomation/window.rb",
46
+ "rautomation.gemspec",
47
+ "spec/button_spec.rb",
48
+ "spec/spec.opts",
49
+ "spec/spec_helper.rb",
50
+ "spec/test.html",
51
+ "spec/text_field_spec.rb",
52
+ "spec/window_spec.rb"
53
+ ]
54
+ s.homepage = %q{http://github.com/jarmo/RAutomation}
55
+ s.rdoc_options = ["--charset=UTF-8"]
56
+ s.require_paths = ["lib"]
57
+ s.rubygems_version = %q{1.3.7}
58
+ s.summary = %q{Automate windows and their controls through user-friendly API with Ruby}
59
+ s.test_files = [
60
+ "spec/button_spec.rb",
61
+ "spec/spec_helper.rb",
62
+ "spec/text_field_spec.rb",
63
+ "spec/window_spec.rb"
64
+ ]
65
+
66
+ if s.respond_to? :specification_version then
67
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
68
+ s.specification_version = 3
69
+
70
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
71
+ s.add_development_dependency(%q<rspec>, [">= 1.3.0"])
72
+ else
73
+ s.add_dependency(%q<rspec>, [">= 1.3.0"])
74
+ end
75
+ else
76
+ s.add_dependency(%q<rspec>, [">= 1.3.0"])
77
+ end
78
+ end
79
+
@@ -0,0 +1,35 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe RAutomation::Button do
4
+ it "#button" do
5
+ RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title]).
6
+ button(:text => SpecHelper::DATA[:window2_button_text]).should exist
7
+ lambda {RAutomation::Window.new(:title => "non-existing-window").button(:text => "Something")}.
8
+ should raise_exception(RAutomation::UnknownWindowException)
9
+ end
10
+
11
+ it "#value" do
12
+ RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title]).
13
+ button(:text => SpecHelper::DATA[:window2_button_text]).value.should == SpecHelper::DATA[:window2_button_text]
14
+ lambda {RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title]).button(:text => "non-existent-button").value}.
15
+ should raise_exception(RAutomation::UnknownButtonException)
16
+ end
17
+
18
+ it "#exists?" do
19
+ window = RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title])
20
+ window.button(:text => SpecHelper::DATA[:window2_button_text]).should exist
21
+ window.button(:text => "non-existent-button").should_not exist
22
+ end
23
+
24
+ it "#click" do
25
+ window = RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title])
26
+ lambda{window.button(:text => "non-existent-button").click}.
27
+ should raise_exception(RAutomation::UnknownButtonException)
28
+
29
+ button = window.button(:text => SpecHelper::DATA[:window2_button_text])
30
+ button.should exist
31
+ button.click
32
+ button.should_not exist
33
+ window.should_not exist
34
+ end
35
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,53 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'rautomation'
3
+ require 'spec'
4
+ require 'spec/autorun'
5
+
6
+ module SpecHelper
7
+ # Since implementations are different then the windows to be tested
8
+ # might be different also.
9
+ #
10
+ # This constant allows to create input data for specs which could differ between the implementations.
11
+ #
12
+ # There has to be 2 windows:
13
+ # 1) Some random window, which is maximizable, minimizable, close'able and etc.
14
+ # 2) Browser window, which opens up a test.html where JavaScript prompt with a Button and a TextField objects will be shown.
15
+ DATA = {
16
+ # This implementation needs Windows OS with Internet Explorer installed into 'c:\program files\internet explorer'.
17
+ "RAutomation::Implementations::AutoIt::Window" => {
18
+ # Path to some binary, which opens up a window, what can be
19
+ # minimized, maximized, activated, closed and etc.
20
+ :window1 => "mspaint",
21
+ # Window 1 title, has to be a Regexp.
22
+ :window1_title => /untitled - paint/i,
23
+ # Path to some browser's binary.
24
+ :window2 => '"c:\\program files\\internet explorer\\iexplore.exe"',
25
+ # Window 2 title, has to be a String.
26
+ :window2_title => "Explorer User Prompt",
27
+ # Window 2 should have this text on it.
28
+ :window2_text => "Where do you want to go today?",
29
+ # When sending ENTER on Window 2, then the window OK button should be pressed and Window 2 should be closed.
30
+ :window2_send_keys => "{ENTER}",
31
+ # Window 2 should have a button with the following text.
32
+ :window2_button_text => "OK",
33
+ # Window 2 should have a text field with the specified class name.
34
+ :window2_text_field_class_name => "Edit1"
35
+ }
36
+ }[ENV["RAUTOMATION_IMPLEMENTATION"] || RAutomation::Implementations::Helper.default_implementation.to_s]
37
+ end
38
+
39
+ Spec::Runner.configure do |config|
40
+ config.before(:all) do
41
+ @pid1 = IO.popen(SpecHelper::DATA[:window1]).pid
42
+ @pid2 = IO.popen(SpecHelper::DATA[:window2] + " " + File.dirname(__FILE__) + "/test.html").pid
43
+ window1 = RAutomation::Window.new(:title => SpecHelper::DATA[:window1_title])
44
+ RAutomation::WaitHelper.wait_until(15) {window1.present?}
45
+ window2 = RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title])
46
+ RAutomation::WaitHelper.wait_until(15) {window2.present?}
47
+ end
48
+
49
+ config.after(:all) do
50
+ Process.kill(9, @pid1) rescue nil
51
+ Process.kill(9, @pid2) rescue nil
52
+ end
53
+ end
data/spec/test.html ADDED
@@ -0,0 +1,13 @@
1
+ <html>
2
+ <head>
3
+ <title>RAutomation testing page</title>
4
+ <script type="text/javascript">
5
+ function showPrompt() {
6
+ prompt("Where do you want to go today?");
7
+ }
8
+ </script>
9
+ </head>
10
+ <body onload="setTimeout(showPrompt, 1000)">
11
+ <h1>RAutomation testing page</h1>
12
+ </body>
13
+ </html>
@@ -0,0 +1,47 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe RAutomation::TextField do
4
+ it "#text_field" do
5
+ RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title]).
6
+ text_field(:class_name => SpecHelper::DATA[:window2_text_field_class_name]).should exist
7
+ lambda {RAutomation::Window.new(:title => "non-existent-window").
8
+ text_field(:class_name => SpecHelper::DATA[:window2_text_field_class_name])}.
9
+ should raise_exception(RAutomation::UnknownWindowException)
10
+ end
11
+
12
+ it "#set" do
13
+ window = RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title])
14
+ window.text_field(:class_name => SpecHelper::DATA[:window2_text_field_class_name]).set "hello!"
15
+ lambda {window.text_field(:class_name => "non-existing-field").set "hello!"}.
16
+ should raise_exception(RAutomation::UnknownTextFieldException)
17
+ end
18
+
19
+ it "#clear"do
20
+ window = RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title])
21
+ field = window.text_field(:class_name => SpecHelper::DATA[:window2_text_field_class_name])
22
+ field.set "hello!"
23
+ field.value.should == "hello!"
24
+ field.clear
25
+ field.value.should be_empty
26
+
27
+ lambda {window.text_field(:class_name => "non-existent-field").clear}.
28
+ should raise_exception(RAutomation::UnknownTextFieldException)
29
+ end
30
+
31
+ it "#value" do
32
+ window = RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title])
33
+ field = window.text_field(:class_name => SpecHelper::DATA[:window2_text_field_class_name])
34
+ field.set "hello!"
35
+ field.value.should == "hello!"
36
+
37
+ lambda {window.text_field(:class_name => "non-existent-field").value}.
38
+ should raise_exception(RAutomation::UnknownTextFieldException)
39
+ end
40
+
41
+ it "#exists?" do
42
+ window = RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title])
43
+ field = window.text_field(:class_name => SpecHelper::DATA[:window2_text_field_class_name])
44
+ field.should exist
45
+ window.text_field(:class_name => "non-existent-field").should_not exist
46
+ end
47
+ end
@@ -0,0 +1,109 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe RAutomation::Window do
4
+ it "RAutomation::Window.implementation" do
5
+ RAutomation::Window.new(:title => "random").implementation.should == (ENV["RAUTOMATION_IMPLEMENTATION"] || RAutomation::Implementations::Helper.default_implementation)
6
+ end
7
+
8
+ it "Window#new by full title" do
9
+ RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title]).should exist
10
+ end
11
+
12
+ it "Window#new by regexp title" do
13
+ RAutomation::Window.new(:title => SpecHelper::DATA[:window1_title]).should exist
14
+ end
15
+
16
+ it "Window#new by hwnd" do
17
+ hwnd = RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title]).hwnd
18
+ window = RAutomation::Window.new(:hwnd => hwnd)
19
+ window.should exist
20
+ window.title.should == SpecHelper::DATA[:window2_title]
21
+ end
22
+
23
+ it "#exists?" do
24
+ RAutomation::Window.new(:title => SpecHelper::DATA[:window1_title]).should exist
25
+ RAutomation::Window.new(:title => "non-existing-window").should_not exist
26
+ end
27
+
28
+ it "#visible?"do
29
+ RAutomation::Window.new(:title => SpecHelper::DATA[:window1_title]).should be_visible
30
+ lambda{RAutomation::Window.new(:title => "non-existing-window").visible?}.
31
+ should raise_exception(RAutomation::UnknownWindowException)
32
+ end
33
+
34
+ it "#present?"do
35
+ RAutomation::Window.new(:title => SpecHelper::DATA[:window1_title]).should be_present
36
+ RAutomation::Window.new(:title => "non-existing-window").should_not be_present
37
+ end
38
+
39
+ it "#hwnd" do
40
+ RAutomation::Window.new(:title => SpecHelper::DATA[:window1_title]).hwnd.should be_a(Fixnum)
41
+ lambda {RAutomation::Window.new(:title => "non-existing-window").hwnd}.
42
+ should raise_exception(RAutomation::UnknownWindowException)
43
+ end
44
+
45
+ it "#title" do
46
+ RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title]).title.should == SpecHelper::DATA[:window2_title]
47
+ lambda {RAutomation::Window.new(:title => "non-existing-window").title}.
48
+ should raise_exception(RAutomation::UnknownWindowException)
49
+ end
50
+
51
+ it "#activate & #active?" do
52
+ window = RAutomation::Window.new(:title => SpecHelper::DATA[:window1_title])
53
+ window.activate
54
+ window.should be_active
55
+ non_existing_window = RAutomation::Window.new(:title => "non-existing-window")
56
+ non_existing_window.activate
57
+ non_existing_window.should_not be_active
58
+ end
59
+
60
+ it "#text" do
61
+ RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title]).text.should include(SpecHelper::DATA[:window2_text])
62
+ lambda {RAutomation::Window.new(:title => "non-existing-window").text}.
63
+ should raise_exception(RAutomation::UnknownWindowException)
64
+ end
65
+
66
+ it "#maximize" do
67
+ RAutomation::Window.new(:title => SpecHelper::DATA[:window1_title]).maximize
68
+ lambda {RAutomation::Window.new(:title => "non-existing-window").maximize}.
69
+ should raise_exception(RAutomation::UnknownWindowException)
70
+ end
71
+
72
+ it "#minimize && #minimized?" do
73
+ window = RAutomation::Window.new(:title => SpecHelper::DATA[:window1_title])
74
+ window.should_not be_minimized
75
+ window.minimize
76
+ window.should be_minimized
77
+
78
+ lambda {RAutomation::Window.new(:title => "non-existing-window").minimize}.
79
+ should raise_exception(RAutomation::UnknownWindowException)
80
+ lambda {RAutomation::Window.new(:title => "non-existing-window").minimized?}.
81
+ should raise_exception(RAutomation::UnknownWindowException)
82
+ end
83
+
84
+ it "#restore" do
85
+ RAutomation::Window.new(:title => SpecHelper::DATA[:window1_title]).restore
86
+ lambda {RAutomation::Window.new(:title => "non-existing-window").restore}.
87
+ should raise_exception(RAutomation::UnknownWindowException)
88
+ end
89
+
90
+ it "#send_keys"do
91
+ window = RAutomation::Window.new(:title => SpecHelper::DATA[:window2_title])
92
+ window.minimize # #send_keys should work even if window is minimized
93
+ window.send_keys(SpecHelper::DATA[:window2_send_keys])
94
+ RAutomation::WaitHelper.wait_until(15) {not window.exists?}
95
+
96
+ lambda {RAutomation::Window.new(:title => "non-existing-window").send_keys("123")}.
97
+ should raise_exception(RAutomation::UnknownWindowException)
98
+ end
99
+
100
+ it "#close" do
101
+ window = RAutomation::Window.new(:title => SpecHelper::DATA[:window1_title])
102
+ window.should exist
103
+ window.close
104
+ window.should_not exist
105
+
106
+ lambda {RAutomation::Window.new(:title => "non-existing-window").close}.
107
+ should_not raise_exception
108
+ end
109
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rautomation
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Jarmo Pertman
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-13 00:00:00 +03:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 27
30
+ segments:
31
+ - 1
32
+ - 3
33
+ - 0
34
+ version: 1.3.0
35
+ type: :development
36
+ version_requirements: *id001
37
+ description: |-
38
+ RAutomation tries to be a small and easy to use library for helping out to automate windows and their controls
39
+ for automated testing.
40
+
41
+ RAutomation aims to provide:
42
+ * Easy to use and user-friendly API (inspired by Watir http://www.watir.com).
43
+ * Cross-platform compatibility
44
+ * Easy extensibility - have some application, which uses some specialized technology, but isn't supported by RAutomation?
45
+ You can get dirty and create new implementation for RAutomation, due to the applied Strategy Pattern!
46
+ email: jarmo.p@gmail.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - LICENSE
53
+ - README.rdoc
54
+ files:
55
+ - .document
56
+ - .gitignore
57
+ - LICENSE
58
+ - README.rdoc
59
+ - Rakefile
60
+ - VERSION
61
+ - ext/AutoItX/AutoItX.chm
62
+ - ext/AutoItX/AutoItX3.dll
63
+ - lib/rautomation.rb
64
+ - lib/rautomation/button.rb
65
+ - lib/rautomation/implementations/autoit.rb
66
+ - lib/rautomation/implementations/autoit/button.rb
67
+ - lib/rautomation/implementations/autoit/locators.rb
68
+ - lib/rautomation/implementations/autoit/text_field.rb
69
+ - lib/rautomation/implementations/autoit/window.rb
70
+ - lib/rautomation/implementations/helper.rb
71
+ - lib/rautomation/text_field.rb
72
+ - lib/rautomation/wait_helper.rb
73
+ - lib/rautomation/window.rb
74
+ - rautomation.gemspec
75
+ - spec/button_spec.rb
76
+ - spec/spec.opts
77
+ - spec/spec_helper.rb
78
+ - spec/test.html
79
+ - spec/text_field_spec.rb
80
+ - spec/window_spec.rb
81
+ has_rdoc: true
82
+ homepage: http://github.com/jarmo/RAutomation
83
+ licenses: []
84
+
85
+ post_install_message:
86
+ rdoc_options:
87
+ - --charset=UTF-8
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ hash: 3
96
+ segments:
97
+ - 0
98
+ version: "0"
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ hash: 3
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ requirements: []
109
+
110
+ rubyforge_project:
111
+ rubygems_version: 1.3.7
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: Automate windows and their controls through user-friendly API with Ruby
115
+ test_files:
116
+ - spec/button_spec.rb
117
+ - spec/spec_helper.rb
118
+ - spec/text_field_spec.rb
119
+ - spec/window_spec.rb