mohawk 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog +7 -0
- data/LICENSE.txt +16 -18
- data/features/control.feature +4 -0
- data/features/mohawk.feature +4 -0
- data/features/step_definitions/control_steps.rb +8 -0
- data/features/step_definitions/mohawk_steps.rb +11 -0
- data/features/step_definitions/navigation_steps.rb +1 -1
- data/features/step_definitions/table_steps.rb +6 -1
- data/features/support/WindowsForms.exe +0 -0
- data/features/support/env.rb +4 -4
- data/features/support/screens/main_screen.rb +2 -0
- data/features/support/screens/screen_scoped_to_children.rb +6 -0
- data/features/table.feature +45 -39
- data/lib/mohawk/accessors/control.rb +3 -2
- data/lib/mohawk/accessors/table.rb +17 -1
- data/lib/mohawk/accessors/table_row.rb +6 -0
- data/lib/mohawk/accessors.rb +6 -3
- data/lib/mohawk/adapters/uia_adapter.rb +19 -11
- data/lib/mohawk/version.rb +1 -1
- data/lib/mohawk.rb +33 -9
- data/mohawk.gemspec +3 -1
- data/spec/lib/mohawk/accessors/control_spec.rb +10 -1
- data/spec/lib/mohawk/accessors/table_spec.rb +79 -59
- data/spec/lib/mohawk/adapters/uia_adapter_spec.rb +22 -1
- data/spec/lib/mohawk_spec.rb +64 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/table_stubber.rb +49 -0
- metadata +28 -7
data/Changelog
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
=== Version 0.0.5 / 2013-07-23
|
2
|
+
* Enhancements
|
3
|
+
* can tell a screen to only consider child controls when searching (can
|
4
|
+
boost speed, especially on windows with data grid views)
|
5
|
+
* added a simplified way to start and stop an application
|
6
|
+
* added the ability to select a table row based on a hash
|
7
|
+
|
1
8
|
=== Version 0.0.4 / 2013-05-24
|
2
9
|
* Enhancements
|
3
10
|
* sped up table access when there are many rows
|
data/LICENSE.txt
CHANGED
@@ -1,22 +1,20 @@
|
|
1
|
-
|
1
|
+
The MIT License (MIT)
|
2
2
|
|
3
|
-
|
3
|
+
Copyright (c) 2013 Levi Wilson
|
4
4
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
the following conditions:
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
12
11
|
|
13
|
-
The above copyright notice and this permission notice shall be
|
14
|
-
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
15
14
|
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/features/control.feature
CHANGED
@@ -3,3 +3,7 @@ Feature: Working with controls
|
|
3
3
|
Scenario: Getting and setting the value of value controls
|
4
4
|
When I set the "value control field" to the value "12/25/2012"
|
5
5
|
Then the value of the "value control field" control should be "12/25/2012 12:00:00 AM"
|
6
|
+
|
7
|
+
Scenario: Clicking controls
|
8
|
+
When I click the control identified by "about_control"
|
9
|
+
Then the "About" screen should be shown
|
data/features/mohawk.feature
CHANGED
@@ -19,3 +19,7 @@ Scenario: Waiting for a particular control
|
|
19
19
|
Scenario: Specifying a parent container
|
20
20
|
When we are using the "ScreenWithContainer"
|
21
21
|
Then our parent is the container, not the main window
|
22
|
+
|
23
|
+
Scenario: Forcing the search scope to children only
|
24
|
+
When we tell the screen to limit searches to children only
|
25
|
+
Then we notice a performance increase, especially when data grid views are involved
|
@@ -1,3 +1,11 @@
|
|
1
1
|
Then /^the value of the "([^"]*)" control should be "(.*?)"$/ do |which, what|
|
2
2
|
on(MainScreen).send("#{which.to_method}_value").should eq(what)
|
3
|
+
end
|
4
|
+
|
5
|
+
When(/^I click the control identified by "([^"]*)"$/) do |what|
|
6
|
+
on(MainScreen).send("click_#{what}")
|
7
|
+
end
|
8
|
+
|
9
|
+
Then(/^the "([^"]*)" screen should be shown$/) do |screen|
|
10
|
+
on(AboutScreen).should be_active
|
3
11
|
end
|
@@ -21,4 +21,15 @@ end
|
|
21
21
|
Then /^our parent is the container, not the main window$/ do
|
22
22
|
@screen.adapter.window.title.should_not match(/MainForm/)
|
23
23
|
@screen.up_view.control_name.should eq('Forward')
|
24
|
+
end
|
25
|
+
|
26
|
+
When(/^we tell the screen to limit searches to children only$/) do
|
27
|
+
on(MainScreen).data_grid
|
28
|
+
on(ScreenScopedToChildren).should be_active
|
29
|
+
end
|
30
|
+
|
31
|
+
Then(/^we notice a performance increase, especially when data grid views are involved$/) do
|
32
|
+
start = Time.now
|
33
|
+
on(ScreenScopedToChildren).close
|
34
|
+
(Time.now - start).should be < 2
|
24
35
|
end
|
@@ -7,7 +7,7 @@ Then /^we should still arrive at our destination$/ do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
When /^we give extra navigational information to a screen$/ do
|
10
|
-
@which_screen = on(MainScreen, :pid =>
|
10
|
+
@which_screen = on(MainScreen, :pid => Mohawk.app.pid)
|
11
11
|
end
|
12
12
|
|
13
13
|
Then /^it should listen to every word that we say$/ do
|
@@ -17,6 +17,10 @@ When /^we select the table row with the value "([^"]*)"$/ do |row_value|
|
|
17
17
|
on(DataEntryForm).people = row_value
|
18
18
|
end
|
19
19
|
|
20
|
+
When(/^we select the table row with the following information:$/) do |table|
|
21
|
+
on(DataEntryForm).select_people table.hashes.first
|
22
|
+
end
|
23
|
+
|
20
24
|
When /^we select the "(.*?)"th table row$/ do |index|
|
21
25
|
on(DataEntryForm).people[index.to_i].select
|
22
26
|
end
|
@@ -50,4 +54,5 @@ end
|
|
50
54
|
|
51
55
|
Then(/^accessing the values in row "([^"]*)" should be snappy$/) do |which_row|
|
52
56
|
Timeout.timeout(5.0) { on(DataEntryForm).people[which_row.to_i].cells.should_not be_empty }
|
53
|
-
end
|
57
|
+
end
|
58
|
+
|
Binary file
|
data/features/support/env.rb
CHANGED
@@ -8,6 +8,8 @@ World(Mohawk::Navigation)
|
|
8
8
|
|
9
9
|
require_rel 'screens'
|
10
10
|
|
11
|
+
Mohawk.app_path = 'features/support/WindowsForms.exe'
|
12
|
+
|
11
13
|
Mohawk::Navigation.routes = {
|
12
14
|
:default => [
|
13
15
|
[MainScreen, :about],
|
@@ -18,11 +20,9 @@ Mohawk::Navigation.routes = {
|
|
18
20
|
}
|
19
21
|
|
20
22
|
Before do
|
21
|
-
|
22
|
-
@process.start
|
23
|
-
RAutomation::WaitHelper.wait_until {RAutomation::Window.new(:pid => @process.pid).present?}
|
23
|
+
Mohawk.start
|
24
24
|
end
|
25
25
|
|
26
26
|
After do
|
27
|
-
|
27
|
+
Mohawk.stop
|
28
28
|
end
|
@@ -6,6 +6,8 @@ class MainScreen
|
|
6
6
|
text(:masked_text_field, :id => "maskedTextBox")
|
7
7
|
button(:data_entry_form_button, :value => "Data Entry Form")
|
8
8
|
button(:about, :value => "About")
|
9
|
+
control(:about_control, :value => 'About')
|
10
|
+
button(:data_grid, :value => "Data Grid View")
|
9
11
|
combo_box(:fruits, :id => "FruitsComboBox")
|
10
12
|
checkbox(:first_checkbox, :id => "checkBox")
|
11
13
|
radio(:first_radio, :id => "radioButton1")
|
data/features/table.feature
CHANGED
@@ -1,41 +1,47 @@
|
|
1
1
|
Feature: Working with tables
|
2
2
|
|
3
|
-
Background:
|
4
|
-
|
5
|
-
|
6
|
-
Scenario: Selecting a row by index
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
Scenario: Selecting a row by value
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
Scenario: Selecting a row
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
Scenario: Retrieving the
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
Scenario:
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
3
|
+
Background:
|
4
|
+
Given we are observing the people table
|
5
|
+
|
6
|
+
Scenario: Selecting a row by index
|
7
|
+
When we select the table row with index "1"
|
8
|
+
Then the row with index "1" should be selected
|
9
|
+
|
10
|
+
Scenario: Selecting a row by value
|
11
|
+
When we select the table row with the value "Anna Doe"
|
12
|
+
Then the row with index "1" should be selected
|
13
|
+
|
14
|
+
Scenario: Selecting a row by matching cell information
|
15
|
+
When we select the table row with the following information:
|
16
|
+
| name | date_of_birth |
|
17
|
+
| Anna Doe | 3/4/1975 |
|
18
|
+
Then the row with index "1" should be selected
|
19
|
+
|
20
|
+
Scenario: Selecting a row from a child item
|
21
|
+
When we select the "1"th table row
|
22
|
+
Then the row with index "1" should be selected
|
23
|
+
|
24
|
+
Scenario: Rows have cells
|
25
|
+
Then the row with index "0" should look like the following:
|
26
|
+
| Name | Date of Birth | State |
|
27
|
+
| John Doe | 12/15/1967 | FL |
|
28
|
+
|
29
|
+
Scenario: Retrieving the row values
|
30
|
+
Then the table row information should look like the following:
|
31
|
+
| text | row |
|
32
|
+
| John Doe | 0 |
|
33
|
+
| Anna Doe | 1 |
|
34
|
+
|
35
|
+
Scenario: Retrieving the headers
|
36
|
+
Then the table headers are "Name, Date of birth, State"
|
37
|
+
|
38
|
+
Scenario: Retrieving a row value by its header
|
39
|
+
Then the "date_of_birth" for the row at index "1" is "3/4/1975"
|
40
|
+
|
41
|
+
Scenario: Working with lots of records counts
|
42
|
+
When there are a lot of records in a table
|
43
|
+
Then the table count responds in a reasonable amount of time
|
44
|
+
|
45
|
+
Scenario: Working with a row when there are a lot of records
|
46
|
+
When there are a lot of records in a table
|
47
|
+
Then accessing the values in row "12" should be snappy
|
@@ -10,7 +10,18 @@ module Mohawk
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def select(which_item)
|
13
|
-
|
13
|
+
case which_item
|
14
|
+
when Hash
|
15
|
+
find_row_with(which_item).select
|
16
|
+
else
|
17
|
+
select_by_value(which_item)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_row_with(row_info)
|
22
|
+
found_row = find { |r| r.all_match? row_info }
|
23
|
+
raise "A row with #{row_info} was not found" unless found_row
|
24
|
+
found_row
|
14
25
|
end
|
15
26
|
|
16
27
|
def headers
|
@@ -26,6 +37,11 @@ module Mohawk
|
|
26
37
|
yield TableRow.new(self, row)
|
27
38
|
end
|
28
39
|
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def select_by_value(which_item)
|
43
|
+
view.select which_item
|
44
|
+
end
|
29
45
|
end
|
30
46
|
end
|
31
47
|
end
|
@@ -22,6 +22,12 @@ module Mohawk
|
|
22
22
|
row.cells.map &:text
|
23
23
|
end
|
24
24
|
|
25
|
+
def all_match?(hash)
|
26
|
+
hash.all? do |key, value|
|
27
|
+
send(key) == "#{value}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
25
31
|
def value_from_header(name)
|
26
32
|
which_column = header_methods.index(name)
|
27
33
|
raise ArgumentError, "#{name} column does not exist in #{header_methods}" if which_column.nil?
|
data/lib/mohawk/accessors.rb
CHANGED
@@ -223,7 +223,7 @@ module Mohawk
|
|
223
223
|
#
|
224
224
|
# @example
|
225
225
|
# table(:some_table, :id => "tableId")
|
226
|
-
# # will generate 'some_table', 'some_table=', 'some_table_headers'
|
226
|
+
# # will generate 'some_table', 'some_table=', 'some_table_headers', 'select_some_table'
|
227
227
|
# # and 'some_table_view' methods to get an Enumerable of table rows,
|
228
228
|
# # select a table item, return all of the headers and get the raw view
|
229
229
|
#
|
@@ -241,6 +241,9 @@ module Mohawk
|
|
241
241
|
define_method("#{name}=") do |which_item|
|
242
242
|
adapter.table(locator).select which_item
|
243
243
|
end
|
244
|
+
define_method("select_#{name}") do |hash_info|
|
245
|
+
adapter.table(locator).select hash_info
|
246
|
+
end
|
244
247
|
define_method("#{name}_headers") do
|
245
248
|
adapter.table(locator).headers
|
246
249
|
end
|
@@ -292,8 +295,8 @@ module Mohawk
|
|
292
295
|
define_method("#{name}=") do |value|
|
293
296
|
adapter.value_control(locator).set value
|
294
297
|
end
|
295
|
-
define_method("click_#{name}") do
|
296
|
-
adapter.value_control(locator).click
|
298
|
+
define_method("click_#{name}") do |&block|
|
299
|
+
adapter.value_control(locator).click &block
|
297
300
|
end
|
298
301
|
define_method("#{name}_view") do
|
299
302
|
adapter.value_control(locator).view
|
@@ -2,6 +2,7 @@ module Mohawk
|
|
2
2
|
module Adapters
|
3
3
|
class UiaAdapter
|
4
4
|
def initialize(locator, container=nil)
|
5
|
+
@only_search_children = locator.delete(:children_only)
|
5
6
|
@window = RAutomation::Window.new(locator.merge(:adapter => :ms_uia))
|
6
7
|
@container = container
|
7
8
|
end
|
@@ -19,56 +20,63 @@ module Mohawk
|
|
19
20
|
|
20
21
|
def combo(locator)
|
21
22
|
@combos ||= {}
|
22
|
-
@combos[locator] ||= Mohawk::Accessors::Combo.new(self, locator)
|
23
|
+
@combos[locator] ||= Mohawk::Accessors::Combo.new(self, merge(locator))
|
23
24
|
end
|
24
25
|
|
26
|
+
|
25
27
|
def checkbox(locator)
|
26
28
|
@checkbox ||= {}
|
27
|
-
@checkbox[locator] ||= Mohawk::Accessors::CheckBox.new(self, locator)
|
29
|
+
@checkbox[locator] ||= Mohawk::Accessors::CheckBox.new(self, merge(locator))
|
28
30
|
end
|
29
31
|
|
30
32
|
def text(locator)
|
31
33
|
@text_fields ||= {}
|
32
|
-
@text_fields[locator] ||= Mohawk::Accessors::Text.new(self, locator)
|
34
|
+
@text_fields[locator] ||= Mohawk::Accessors::Text.new(self, merge(locator))
|
33
35
|
end
|
34
36
|
|
35
37
|
def button(locator)
|
36
38
|
@buttons ||= {}
|
37
|
-
@buttons[locator] ||= Mohawk::Accessors::Button.new(self, locator)
|
39
|
+
@buttons[locator] ||= Mohawk::Accessors::Button.new(self, merge(locator))
|
38
40
|
end
|
39
41
|
|
40
42
|
def radio(locator)
|
41
43
|
@radios ||= {}
|
42
|
-
@radios[locator] ||= Mohawk::Accessors::Radio.new(self, locator)
|
44
|
+
@radios[locator] ||= Mohawk::Accessors::Radio.new(self, merge(locator))
|
43
45
|
end
|
44
46
|
|
45
47
|
def label(locator)
|
46
48
|
@labels ||= {}
|
47
|
-
@labels[locator] ||= Mohawk::Accessors::Label.new(self, locator)
|
49
|
+
@labels[locator] ||= Mohawk::Accessors::Label.new(self, merge(locator))
|
48
50
|
end
|
49
51
|
|
50
52
|
def link(locator)
|
51
53
|
@links ||= {}
|
52
|
-
@links[locator] ||= Mohawk::Accessors::Link.new(self, locator)
|
54
|
+
@links[locator] ||= Mohawk::Accessors::Link.new(self, merge(locator))
|
53
55
|
end
|
54
56
|
|
55
57
|
def menu_item(locator)
|
56
|
-
Mohawk::Accessors::MenuItem.new(self, locator)
|
58
|
+
Mohawk::Accessors::MenuItem.new(self, merge(locator))
|
57
59
|
end
|
58
60
|
|
59
61
|
def table(locator)
|
60
62
|
@tables ||= {}
|
61
|
-
@tables[locator] ||= Mohawk::Accessors::Table.new(self, locator)
|
63
|
+
@tables[locator] ||= Mohawk::Accessors::Table.new(self, merge(locator))
|
62
64
|
end
|
63
65
|
|
64
66
|
def tree_view(locator)
|
65
67
|
@trees ||= {}
|
66
|
-
@trees[locator] ||= Mohawk::Accessors::TreeView.new(self, locator)
|
68
|
+
@trees[locator] ||= Mohawk::Accessors::TreeView.new(self, merge(locator))
|
67
69
|
end
|
68
70
|
|
69
71
|
def value_control(locator)
|
70
72
|
@controls ||= {}
|
71
|
-
@controls[locator] ||= Mohawk::Accessors::Control.new(self, locator)
|
73
|
+
@controls[locator] ||= Mohawk::Accessors::Control.new(self, merge(locator))
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def merge(locator)
|
78
|
+
locator = locator.merge(:children_only => true) if @only_search_children
|
79
|
+
locator
|
72
80
|
end
|
73
81
|
end
|
74
82
|
end
|
data/lib/mohawk/version.rb
CHANGED
data/lib/mohawk.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "rautomation"
|
2
|
+
require 'childprocess'
|
2
3
|
require "mohawk/version"
|
3
4
|
require "require_all"
|
4
5
|
require "mohawk/accessors"
|
@@ -9,13 +10,43 @@ require "mohawk/core_ext/string"
|
|
9
10
|
require_rel "mohawk/accessors"
|
10
11
|
|
11
12
|
module Mohawk
|
13
|
+
include RAutomation::WaitHelper
|
14
|
+
extend RAutomation::WaitHelper
|
12
15
|
|
13
|
-
|
16
|
+
class InvalidApplicationPath < StandardError
|
17
|
+
def initialize(message='You must set the Mohawk.app_path to start an application')
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
14
21
|
|
15
22
|
def self.included(cls)
|
16
23
|
cls.extend Mohawk::Accessors
|
17
24
|
end
|
18
25
|
|
26
|
+
attr_reader :adapter
|
27
|
+
|
28
|
+
def self.start
|
29
|
+
raise InvalidApplicationPath.new unless @app_path
|
30
|
+
@app = ChildProcess.build(@app_path).start
|
31
|
+
|
32
|
+
app_window = RAutomation::Window.new :pid => @app.pid
|
33
|
+
wait_until { app_window.present? }
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.stop
|
37
|
+
raise 'An application was never started' unless @app
|
38
|
+
@app.stop unless @app.exited?
|
39
|
+
@app = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.app
|
43
|
+
@app
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.app_path=(path)
|
47
|
+
@app_path = path
|
48
|
+
end
|
49
|
+
|
19
50
|
def initialize(extra={})
|
20
51
|
locator = [which_window.merge(extra)]
|
21
52
|
locator << parent_container if respond_to?(:parent_container)
|
@@ -56,19 +87,12 @@ module Mohawk
|
|
56
87
|
def wait_for_control(locator)
|
57
88
|
control = adapter.window.control(locator)
|
58
89
|
begin
|
59
|
-
|
90
|
+
wait_until { control.exist? }
|
60
91
|
rescue
|
61
92
|
raise "A control with #{locator} was not found"
|
62
93
|
end
|
63
94
|
end
|
64
95
|
|
65
|
-
#
|
66
|
-
# Waits until the block returns true
|
67
|
-
#
|
68
|
-
def wait_until(timeout=RAutomation::Window.wait_timeout, &block)
|
69
|
-
RAutomation::WaitHelper.wait_until timeout, &block
|
70
|
-
end
|
71
|
-
|
72
96
|
#
|
73
97
|
# Indicates if the window has text or not
|
74
98
|
#
|
data/mohawk.gemspec
CHANGED
@@ -11,15 +11,17 @@ Gem::Specification.new do |gem|
|
|
11
11
|
gem.description = %q{High level wrapper around windows applications}
|
12
12
|
gem.summary = %q{High level wrapper around windows applications}
|
13
13
|
gem.homepage = "http://github.com/leviwilson/mohawk"
|
14
|
+
gem.license = 'MIT'
|
14
15
|
|
15
16
|
gem.files = `git ls-files`.split($/)
|
16
17
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
19
|
gem.require_paths = ["lib"]
|
19
20
|
|
20
|
-
gem.add_dependency 'rautomation', '
|
21
|
+
gem.add_dependency 'rautomation', '~> 0.9.3'
|
21
22
|
gem.add_dependency 'require_all'
|
22
23
|
gem.add_dependency 'page_navigation', '>= 0.7'
|
24
|
+
gem.add_dependency 'childprocess', '~> 0.3.9'
|
23
25
|
|
24
26
|
gem.add_development_dependency 'cucumber'
|
25
27
|
gem.add_development_dependency 'rspec', '>= 2.12.0'
|
@@ -28,10 +28,19 @@ describe Mohawk::Accessors::Control do
|
|
28
28
|
end
|
29
29
|
|
30
30
|
it "can click the control" do
|
31
|
-
control.should_receive(:click)
|
31
|
+
control.should_receive(:click).and_yield
|
32
32
|
screen.click_control_yourself
|
33
33
|
end
|
34
34
|
|
35
|
+
it "can give a custom block to the click method" do
|
36
|
+
control.should_receive(:click).and_yield
|
37
|
+
result = false
|
38
|
+
screen.click_control_yourself do
|
39
|
+
result = true
|
40
|
+
end
|
41
|
+
result.should be_true
|
42
|
+
end
|
43
|
+
|
35
44
|
it "can work with the raw view" do
|
36
45
|
screen.control_yourself_view.should be(control)
|
37
46
|
end
|
@@ -4,122 +4,142 @@ class TableScreen
|
|
4
4
|
include Mohawk
|
5
5
|
window(:id => nil)
|
6
6
|
|
7
|
-
table(:top, :id =>
|
7
|
+
table(:top, :id => 'tableId')
|
8
8
|
|
9
9
|
# aliases
|
10
|
-
table(:my_default, :id =>
|
11
|
-
listview(:my_listview, :id =>
|
12
|
-
list_view(:my_list_view, :id =>
|
10
|
+
table(:my_default, :id => 'defaultAliasId')
|
11
|
+
listview(:my_listview, :id => 'listviewAliasId')
|
12
|
+
list_view(:my_list_view, :id => 'list_viewAliasId')
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
attr_reader :text, :row
|
17
|
-
def initialize(text, row)
|
18
|
-
@text = text
|
19
|
-
@row = row
|
20
|
-
end
|
21
|
-
end
|
15
|
+
include RAutomation::Adapter::MsUia
|
22
16
|
|
23
17
|
describe Mohawk::Accessors::Table do
|
24
18
|
let(:screen) { TableScreen.new }
|
25
|
-
let(:window) { double(
|
26
|
-
let(:table) { double(
|
19
|
+
let(:window) { double('RAutomation Window') }
|
20
|
+
let(:table) { double('Table') }
|
27
21
|
|
28
22
|
before(:each) do
|
29
23
|
RAutomation::Window.stub(:new).and_return(window)
|
30
24
|
end
|
31
25
|
|
32
|
-
context
|
26
|
+
context 'working with table controls' do
|
33
27
|
before(:each) do
|
34
|
-
window.should_receive(:table).with(:id =>
|
28
|
+
window.should_receive(:table).with(:id => 'tableId').and_return(table)
|
35
29
|
end
|
36
30
|
|
37
|
-
it
|
31
|
+
it 'can select a row by index' do
|
38
32
|
table.should_receive(:select).with(1)
|
39
33
|
screen.top = 1
|
40
34
|
end
|
41
35
|
|
42
|
-
it
|
43
|
-
table.should_receive(:select).with
|
44
|
-
screen.top =
|
36
|
+
it 'can select a row by value' do
|
37
|
+
table.should_receive(:select).with 'John Elway'
|
38
|
+
screen.top = 'John Elway'
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'selecting a row by hash' do
|
42
|
+
it 'selects the row if all values match' do
|
43
|
+
TableStubber.stub(table)
|
44
|
+
.with_headers('Column One', 'Column Two', 'Column Three')
|
45
|
+
.and_row('first', 'something', 'foo')
|
46
|
+
.and_row('second', 'another', 'bar')
|
47
|
+
|
48
|
+
table.should_receive(:select).with(1)
|
49
|
+
screen.select_top :column_one => 'second', :column_three => 'bar'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'can handle non-string values' do
|
53
|
+
TableStubber.stub(table)
|
54
|
+
.with_headers('name', 'age')
|
55
|
+
.and_row('Levi', '33')
|
56
|
+
|
57
|
+
table.should_receive(:select).with(0)
|
58
|
+
screen.select_top :age => 33
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'raises if no row is found' do
|
62
|
+
TableStubber.stub(table)
|
63
|
+
.with_headers('Column One', 'Column Two', 'Column Three')
|
64
|
+
.and_row('first', 'something', 'foo')
|
65
|
+
|
66
|
+
lambda { screen.select_top :column_one => 'not found' }.should raise_error "A row with {:column_one=>\"not found\"} was not found"
|
67
|
+
end
|
68
|
+
|
45
69
|
end
|
46
70
|
|
47
|
-
it
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
screen.top.map(&:to_hash).should eq(expected_rows)
|
71
|
+
it 'has rows' do
|
72
|
+
TableStubber.stub(table)
|
73
|
+
.with_headers('Column')
|
74
|
+
.and_row('First Row')
|
75
|
+
.and_row('Second Row')
|
76
|
+
|
77
|
+
screen.top.map(&:column).should eq(['First Row', 'Second Row'])
|
55
78
|
end
|
56
79
|
|
57
|
-
it
|
58
|
-
|
59
|
-
|
60
|
-
RAutomation::Adapter::MsUia::UiaDll.should_receive(:table_headers).with(1234).and_return(expected_headers)
|
61
|
-
screen.top_headers.should eq(expected_headers)
|
80
|
+
it 'has headers' do
|
81
|
+
TableStubber.stub(table).with_headers('first header', 'second header')
|
82
|
+
screen.top_headers.should eq(['first header', 'second header'])
|
62
83
|
end
|
63
84
|
|
64
|
-
it
|
85
|
+
it 'can return the raw view' do
|
65
86
|
screen.top_view.should_not be_nil
|
66
87
|
end
|
67
88
|
|
68
89
|
describe Mohawk::Accessors::TableRow do
|
69
|
-
let(:table_row) { double("RAutomation TableRow") }
|
70
|
-
|
71
90
|
before(:each) do
|
72
|
-
|
73
|
-
table_row.stub(:row).and_return 0
|
91
|
+
TableStubber.stub(table).with_headers('column').and_row('first row')
|
74
92
|
end
|
75
93
|
|
76
|
-
it
|
94
|
+
it 'can get an individual row' do
|
77
95
|
screen.top[0].should_not be_nil
|
78
96
|
end
|
79
97
|
|
80
|
-
it
|
98
|
+
it 'knows if it is selected' do
|
81
99
|
table.should_receive(:selected?).with(0).and_return(true)
|
82
100
|
screen.top[0].should be_selected
|
83
101
|
end
|
84
102
|
|
85
|
-
it
|
103
|
+
it 'can be selected' do
|
86
104
|
table.should_receive(:select).with(0)
|
87
105
|
screen.top[0].select
|
88
106
|
end
|
89
107
|
|
90
|
-
it
|
91
|
-
|
92
|
-
|
93
|
-
|
108
|
+
it 'has cells' do
|
109
|
+
TableStubber.stub(table)
|
110
|
+
.with_headers('first', 'second')
|
111
|
+
.and_row('Cell 1', 'Cell 2')
|
112
|
+
|
113
|
+
screen.top[0].cells.should eq(['Cell 1', 'Cell 2'])
|
94
114
|
end
|
95
115
|
|
96
|
-
it
|
97
|
-
|
98
|
-
|
116
|
+
it 'can get cell values by header name' do
|
117
|
+
TableStubber.stub(table)
|
118
|
+
.with_headers('First Header', 'Second Header')
|
119
|
+
.and_row('Item 1', 'Item 2')
|
99
120
|
|
100
|
-
|
101
|
-
expected_cell.should_receive(:text).and_return('Item 2')
|
102
|
-
RAutomation::Adapter::MsUia::Cell.should_receive(:new).with(table_row, :index => 1).and_return(expected_cell)
|
103
|
-
screen.top[0].second_header.should eq("Item 2")
|
121
|
+
screen.top[0].second_header.should eq('Item 2')
|
104
122
|
end
|
105
123
|
|
106
|
-
it
|
107
|
-
|
108
|
-
|
109
|
-
|
124
|
+
it 'clearly lets you know if a header is not there' do
|
125
|
+
TableStubber.stub(table)
|
126
|
+
.with_headers('First Header', 'Second Header')
|
127
|
+
.and_row('Item 1', 'Item 2')
|
128
|
+
|
129
|
+
lambda { screen.top[0].does_not_exist }.should raise_error ArgumentError, 'does_not_exist column does not exist in [:first_header, :second_header]'
|
110
130
|
end
|
111
131
|
end
|
112
132
|
end
|
113
133
|
|
114
|
-
context
|
115
|
-
let(:null_table) { double(
|
116
|
-
let(:table_aliases) { [
|
134
|
+
context 'aliases for table' do
|
135
|
+
let(:null_table) { double('Null ComboBox Field').as_null_object }
|
136
|
+
let(:table_aliases) { ['default', 'listview', 'list_view'] }
|
117
137
|
|
118
138
|
def expected_alias(id)
|
119
139
|
window.should_receive(:table).with(:id => "#{id}AliasId").ordered.and_return(null_table)
|
120
140
|
end
|
121
141
|
|
122
|
-
it
|
142
|
+
it 'has many aliases' do
|
123
143
|
table_aliases.each do |which_alias|
|
124
144
|
expected_alias which_alias
|
125
145
|
end
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
class AnyScreen
|
4
4
|
include Mohawk
|
5
|
-
window(:title => /Some Title
|
5
|
+
window(:title => /Some Title/, :children_only => true)
|
6
6
|
end
|
7
7
|
|
8
8
|
RSpec::Matchers.define(:use_the_cached) do |control_type, the_alias=nil|
|
@@ -13,12 +13,33 @@ RSpec::Matchers.define(:use_the_cached) do |control_type, the_alias=nil|
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
RSpec::Matchers.define(:only_search_children_for_a) do |control_type, the_alias=nil|
|
17
|
+
match do |screen|
|
18
|
+
screen.adapter.window.should_receive(the_alias || control_type).with(:id => 'id', :children_only => true).and_return(double('child control'))
|
19
|
+
screen.adapter.send(control_type, :id => 'id')
|
20
|
+
true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
16
24
|
describe Mohawk::Adapters::UiaAdapter do
|
17
25
|
subject(:screen) { AnyScreen.new }
|
18
26
|
|
19
27
|
let(:window) { double('RAutomation Window') }
|
20
28
|
before(:each) { RAutomation::Window.stub(:new).and_return(window) }
|
21
29
|
|
30
|
+
context 'limiting searches to children only' do
|
31
|
+
it { should only_search_children_for_a(:combo, :select_list) }
|
32
|
+
it { should only_search_children_for_a(:checkbox) }
|
33
|
+
it { should only_search_children_for_a(:text, :text_field) }
|
34
|
+
it { should only_search_children_for_a(:button) }
|
35
|
+
it { should only_search_children_for_a(:radio) }
|
36
|
+
it { should only_search_children_for_a(:label) }
|
37
|
+
it { should only_search_children_for_a(:link, :label) }
|
38
|
+
it { should only_search_children_for_a(:table) }
|
39
|
+
it { should only_search_children_for_a(:tree_view, :select_list) }
|
40
|
+
it { should only_search_children_for_a(:value_control) }
|
41
|
+
end
|
42
|
+
|
22
43
|
context 'caching controls' do
|
23
44
|
it { should use_the_cached(:combo, :select_list) }
|
24
45
|
it { should use_the_cached(:checkbox) }
|
data/spec/lib/mohawk_spec.rb
CHANGED
@@ -11,6 +11,11 @@ describe Mohawk do
|
|
11
11
|
let(:screen) { TestScreen.new }
|
12
12
|
let(:window) { double('RAutomation Window') }
|
13
13
|
|
14
|
+
before(:each) do
|
15
|
+
Mohawk.app_path = nil
|
16
|
+
Mohawk.instance_variable_set(:@app, nil)
|
17
|
+
end
|
18
|
+
|
14
19
|
it "uses the uia adapter by default" do
|
15
20
|
RAutomation::Window.should_receive(:new).with(:title => "Some Window Title", :adapter => :ms_uia).and_return(window)
|
16
21
|
TestScreen.new
|
@@ -21,6 +26,64 @@ describe Mohawk do
|
|
21
26
|
TestScreen.new :extra => 'test'
|
22
27
|
end
|
23
28
|
|
29
|
+
context 'launching an application' do
|
30
|
+
let(:process) { double('ChildProcess::Process') }
|
31
|
+
|
32
|
+
before(:each) do
|
33
|
+
process.stub(:start).and_return(process)
|
34
|
+
[:exited?, :stop, :pid].each &process.method(:stub)
|
35
|
+
ChildProcess.stub(:build).with('./the/app/path.exe').and_return(process)
|
36
|
+
|
37
|
+
RAutomation::Window.stub(:new).and_return(window)
|
38
|
+
window.stub(:present?).and_return(true)
|
39
|
+
|
40
|
+
Mohawk.app_path = './the/app/path.exe'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'requires a path' do
|
44
|
+
Mohawk.app_path = nil
|
45
|
+
lambda { Mohawk.start }.should raise_error(Mohawk::InvalidApplicationPath, 'You must set the Mohawk.app_path to start an application')
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'can start an application' do
|
49
|
+
process.should_receive(:start)
|
50
|
+
Mohawk.start
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'waits on the application' do
|
54
|
+
process.should_receive(:pid).and_return(123)
|
55
|
+
RAutomation::Window.should_receive(:new).with(:pid => 123).and_return(window)
|
56
|
+
window.should_receive(:present?).and_return(false, false, true)
|
57
|
+
|
58
|
+
Mohawk.start
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'can stop an application' do
|
62
|
+
process.should_receive(:stop)
|
63
|
+
Mohawk.start
|
64
|
+
|
65
|
+
Mohawk.stop
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'knows if a process was never started' do
|
69
|
+
lambda { Mohawk.stop }.should raise_error('An application was never started')
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'does nothing if the process has exited' do
|
73
|
+
process.should_receive(:exited?).and_return(true)
|
74
|
+
process.should_not_receive(:stop)
|
75
|
+
Mohawk.start
|
76
|
+
|
77
|
+
Mohawk.stop
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'has a clean slate if the application has been stopped' do
|
81
|
+
Mohawk.start
|
82
|
+
Mohawk.stop
|
83
|
+
Mohawk.app.should be_nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
24
87
|
context "using the UI Automation adapter" do
|
25
88
|
before(:each) do
|
26
89
|
RAutomation::Window.stub(:new).and_return(window)
|
@@ -60,7 +123,7 @@ describe Mohawk do
|
|
60
123
|
end
|
61
124
|
|
62
125
|
it "tells you what you were waiting for if it fails" do
|
63
|
-
|
126
|
+
screen.should_receive(:wait_until).and_raise("you should have caught me")
|
64
127
|
locator = {:id => "whatever", :index => 0}
|
65
128
|
window.should_receive(:control).with(locator)
|
66
129
|
lambda { screen.wait_for_control(:id => "whatever", :index => 0) }.should raise_error(Exception, "A control with #{locator} was not found")
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
class TableStubber
|
4
|
+
include RSpec::Mocks::ExampleMethods
|
5
|
+
|
6
|
+
attr_reader :table, :rows
|
7
|
+
|
8
|
+
def initialize(table)
|
9
|
+
@table = table
|
10
|
+
@id = SecureRandom.base64
|
11
|
+
@table.stub(:search_information).and_return(@id)
|
12
|
+
@rows = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.stub(table)
|
16
|
+
TableStubber.new(table)
|
17
|
+
end
|
18
|
+
|
19
|
+
def with_headers(*headers)
|
20
|
+
UiaDll.stub(:table_headers).with(@id).and_return(headers.map(&:to_s))
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def and_row(*values)
|
25
|
+
stub_cells_for(add_row, values)
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def add_row
|
31
|
+
row = double("table #{@id}, row #{rows.count}")
|
32
|
+
row.stub(:row).and_return(rows.count)
|
33
|
+
Row.stub(:new).with(table, :index => rows.count).and_return(row)
|
34
|
+
rows << row
|
35
|
+
table.stub(:row_count).and_return(rows.count)
|
36
|
+
row
|
37
|
+
end
|
38
|
+
|
39
|
+
def stub_cells_for(row, values)
|
40
|
+
cells = []
|
41
|
+
values.each_with_index do |value, index|
|
42
|
+
cell = double("Cell at #{row.row}, #{index}")
|
43
|
+
Cell.stub(:new).with(row, :index => index).and_return(cell)
|
44
|
+
cell.stub(:text).and_return(value)
|
45
|
+
cells << cell
|
46
|
+
end
|
47
|
+
row.stub(:cells).and_return(cells)
|
48
|
+
end
|
49
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mohawk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,24 +9,24 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-07-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rautomation
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.9.
|
21
|
+
version: 0.9.3
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
26
|
requirements:
|
27
|
-
- -
|
27
|
+
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 0.9.
|
29
|
+
version: 0.9.3
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: require_all
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -59,6 +59,22 @@ dependencies:
|
|
59
59
|
- - ! '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0.7'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: childprocess
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.3.9
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.3.9
|
62
78
|
- !ruby/object:Gem::Dependency
|
63
79
|
name: cucumber
|
64
80
|
requirement: !ruby/object:Gem::Requirement
|
@@ -274,6 +290,7 @@ files:
|
|
274
290
|
- features/support/screens/about_screen.rb
|
275
291
|
- features/support/screens/data_entry_form.rb
|
276
292
|
- features/support/screens/main_screen.rb
|
293
|
+
- features/support/screens/screen_scoped_to_children.rb
|
277
294
|
- features/support/screens/screen_with_container.rb
|
278
295
|
- features/support/string.rb
|
279
296
|
- features/table.feature
|
@@ -314,8 +331,10 @@ files:
|
|
314
331
|
- spec/lib/mohawk_spec.rb
|
315
332
|
- spec/lib/navigation_spec.rb
|
316
333
|
- spec/spec_helper.rb
|
334
|
+
- spec/table_stubber.rb
|
317
335
|
homepage: http://github.com/leviwilson/mohawk
|
318
|
-
licenses:
|
336
|
+
licenses:
|
337
|
+
- MIT
|
319
338
|
post_install_message:
|
320
339
|
rdoc_options: []
|
321
340
|
require_paths:
|
@@ -393,6 +412,7 @@ test_files:
|
|
393
412
|
- features/support/screens/about_screen.rb
|
394
413
|
- features/support/screens/data_entry_form.rb
|
395
414
|
- features/support/screens/main_screen.rb
|
415
|
+
- features/support/screens/screen_scoped_to_children.rb
|
396
416
|
- features/support/screens/screen_with_container.rb
|
397
417
|
- features/support/string.rb
|
398
418
|
- features/table.feature
|
@@ -414,4 +434,5 @@ test_files:
|
|
414
434
|
- spec/lib/mohawk_spec.rb
|
415
435
|
- spec/lib/navigation_spec.rb
|
416
436
|
- spec/spec_helper.rb
|
437
|
+
- spec/table_stubber.rb
|
417
438
|
has_rdoc:
|