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 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: