rautomation 0.0.1

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/.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