mohawk 0.0.4 → 0.0.5

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/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
- Copyright (c) 2012 Levi Wilson
1
+ The MIT License (MIT)
2
2
 
3
- MIT License
3
+ Copyright (c) 2013 Levi Wilson
4
4
 
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
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
- included in all copies or substantial portions of the Software.
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
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
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.
@@ -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
@@ -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 => @process.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
@@ -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
- @process = ChildProcess.build('features\\support\\WindowsForms.exe')
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
- @process.stop
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")
@@ -0,0 +1,6 @@
1
+ class ScreenScopedToChildren
2
+ include Mohawk
3
+ window(:title => /DataGridViewForm/, :children_only => true)
4
+
5
+ button(:close, :value => 'Close')
6
+ end
@@ -1,41 +1,47 @@
1
1
  Feature: Working with tables
2
2
 
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 from a child item
15
- When we select the "1"th table row
16
- Then the row with index "1" should be selected
17
-
18
- Scenario: Rows have cells
19
- Then the row with index "0" should look like the following:
20
- | Name | Date of Birth | State |
21
- | John Doe | 12/15/1967 | FL |
22
-
23
- Scenario: Retrieving the row values
24
- Then the table row information should look like the following:
25
- | text | row |
26
- | John Doe | 0 |
27
- | Anna Doe | 1 |
28
-
29
- Scenario: Retrieving the headers
30
- Then the table headers are "Name, Date of birth, State"
31
-
32
- Scenario: Retrieving a row value by its header
33
- Then the "date_of_birth" for the row at index "1" is "3/4/1975"
34
-
35
- Scenario: Working with lots of records counts
36
- When there are a lot of records in a table
37
- Then the table count responds in a reasonable amount of time
38
-
39
- Scenario: Working with a row when there are a lot of records
40
- When there are a lot of records in a table
41
- Then accessing the values in row "12" should be snappy
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
@@ -15,8 +15,9 @@ module Mohawk
15
15
  @view.set value
16
16
  end
17
17
 
18
- def click
19
- @view.click
18
+ def click(&block)
19
+ @view.click &block if block
20
+ @view.click { true } unless block
20
21
  end
21
22
  end
22
23
  end
@@ -10,7 +10,18 @@ module Mohawk
10
10
  end
11
11
 
12
12
  def select(which_item)
13
- view.select which_item
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?
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Mohawk
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
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
- attr_reader :adapter
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
- RAutomation::WaitHelper.wait_until { control.exist? }
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', '>= 0.9.2'
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 => "tableId")
7
+ table(:top, :id => 'tableId')
8
8
 
9
9
  # aliases
10
- table(:my_default, :id => "defaultAliasId")
11
- listview(:my_listview, :id => "listviewAliasId")
12
- list_view(:my_list_view, :id => "list_viewAliasId")
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
- class FakeTableRow
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("RAutomation Window") }
26
- let(:table) { double("Table") }
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 "working with table controls" do
26
+ context 'working with table controls' do
33
27
  before(:each) do
34
- window.should_receive(:table).with(:id => "tableId").and_return(table)
28
+ window.should_receive(:table).with(:id => 'tableId').and_return(table)
35
29
  end
36
30
 
37
- it "can select a row by index" do
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 "can select a row by value" do
43
- table.should_receive(:select).with "John Elway"
44
- screen.top = "John Elway"
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 "has rows" do
48
- first_row = FakeTableRow.new "First Row", 0
49
- second_row = FakeTableRow.new "Second Row", 1
50
- expected_rows = [first_row, second_row].map {|r| {:text => r.text, :row => r.row} }
51
- table.should_receive(:row_count).and_return(2)
52
- RAutomation::Adapter::MsUia::Row.should_receive(:new).with(table, :index => 0).and_return(first_row)
53
- RAutomation::Adapter::MsUia::Row.should_receive(:new).with(table, :index => 1).and_return(second_row)
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 "has headers" do
58
- expected_headers = ["first header", "second header"]
59
- table.should_receive(:search_information).and_return(1234)
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 "can return the raw view" do
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
- RAutomation::Adapter::MsUia::Row.should_receive(:new).with(table, :index => 0).and_return(table_row)
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 "can get an individual row" do
94
+ it 'can get an individual row' do
77
95
  screen.top[0].should_not be_nil
78
96
  end
79
97
 
80
- it "knows if it is selected" do
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 "can be selected" do
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 "has cells" do
91
- expected_cells = [FakeTableRow.new("Item 1", 0), FakeTableRow.new("Item 2", 1)]
92
- table_row.should_receive(:cells).and_return(expected_cells)
93
- screen.top[0].cells.should eq expected_cells.map &:text
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 "can get cell values by header name" do
97
- RAutomation::Adapter::MsUia::UiaDll.should_receive(:table_headers).and_return(["First Header", "Second Header"])
98
- table.should_receive(:search_information)
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
- expected_cell = double('RAutomation Cell')
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 "clearly lets you know if a header is not there" do
107
- RAutomation::Adapter::MsUia::UiaDll.should_receive(:table_headers).and_return(["First Header", "Second Header"])
108
- table.should_receive(:search_information)
109
- lambda { screen.top[0].does_not_exist }.should raise_error ArgumentError, "does_not_exist column does not exist in [:first_header, :second_header]"
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 "aliases for table" do
115
- let(:null_table) { double("Null ComboBox Field").as_null_object }
116
- let(:table_aliases) { ["default", "listview", "list_view"] }
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 "has many aliases" do
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) }
@@ -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
- RAutomation::WaitHelper.should_receive(:wait_until).and_raise("you should have caught me")
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
@@ -5,5 +5,6 @@ require 'rspec'
5
5
  require 'ffi_stub'
6
6
  require 'mohawk'
7
7
  require 'coveralls'
8
+ require_rel 'table_stubber'
8
9
 
9
10
  Coveralls.wear!
@@ -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
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-05-24 00:00:00.000000000 Z
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.2
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.2
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: