mohawk 0.0.3 → 0.0.4

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/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule "mohawk-testing-app"]
2
+ path = mohawk-testing-app
3
+ url = git://github.com/leviwilson/mohawk-testing-app.git
data/Changelog CHANGED
@@ -1,3 +1,8 @@
1
+ === Version 0.0.4 / 2013-05-24
2
+ * Enhancements
3
+ * sped up table access when there are many rows
4
+ * caches controls based on locator for quicker lookups
5
+
1
6
  === Version 0.0.3 / 2013-04-26
2
7
  * Enhancements
3
8
  * Bumped the RAutomation dependency so we can now interact with controls
data/Gemfile CHANGED
@@ -1,3 +1,4 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
+ gem 'coveralls', require: false
3
4
  gemspec
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Mohawk
2
-
3
2
  [![Build Status](https://travis-ci.org/leviwilson/mohawk.png)](https://travis-ci.org/leviwilson/mohawk)
3
+ [![Coverage Status](https://coveralls.io/repos/leviwilson/mohawk/badge.png?branch=master)](https://coveralls.io/r/leviwilson/mohawk?branch=master)
4
4
 
5
5
  A gem to assist in building page-object like structures for testing Windows applications.
6
6
 
@@ -15,3 +15,7 @@ Scenario: Determining if a window has text
15
15
  Scenario: Waiting for a particular control
16
16
  When we are using the "MainScreen"
17
17
  Then we can wait for the control with a value of "Data Entry Form"
18
+
19
+ Scenario: Specifying a parent container
20
+ When we are using the "ScreenWithContainer"
21
+ Then our parent is the container, not the main window
@@ -16,4 +16,9 @@ end
16
16
 
17
17
  Then /^we can wait for the control with a value of "(.*?)"$/ do |value|
18
18
  @screen.wait_for_control :value => value
19
- end
19
+ end
20
+
21
+ Then /^our parent is the container, not the main window$/ do
22
+ @screen.adapter.window.title.should_not match(/MainForm/)
23
+ @screen.up_view.control_name.should eq('Forward')
24
+ end
@@ -36,4 +36,18 @@ end
36
36
 
37
37
  Then /^the "(.*?)" for the row at index "(.*?)" is "(.*?)"$/ do |header, which_row, expected_value|
38
38
  on(DataEntryForm).people[which_row.to_i].send(header).should eq(expected_value)
39
- end
39
+ end
40
+
41
+ When(/^there are a lot of records in a table$/) do
42
+ on(DataEntryForm) do |screen|
43
+ 5.times { screen.add_more }
44
+ end
45
+ end
46
+
47
+ Then(/^the table count responds in a reasonable amount of time$/) do
48
+ Timeout.timeout(5.0) { on(DataEntryForm).people.count.should eq(252) }
49
+ end
50
+
51
+ Then(/^accessing the values in row "([^"]*)" should be snappy$/) do |which_row|
52
+ Timeout.timeout(5.0) { on(DataEntryForm).people[which_row.to_i].cells.should_not be_empty }
53
+ end
Binary file
@@ -3,6 +3,7 @@ class DataEntryForm
3
3
  window(:title => /DataEntry/)
4
4
 
5
5
  table(:people, :id => "personListView")
6
+ button(:add_more, :value => 'Add Many')
6
7
 
7
8
  def wait_until_present
8
9
  super
@@ -0,0 +1,8 @@
1
+ class ScreenWithContainer
2
+ include Mohawk
3
+ window(:title => /MainForm/)
4
+ parent(:id => 'numericUpDown')
5
+
6
+ button(:up, :id => 'SmallIncrement')
7
+ button(:down, :id => 'SmallDecrement')
8
+ end
@@ -31,3 +31,11 @@ Scenario: Retrieving the headers
31
31
 
32
32
  Scenario: Retrieving a row value by its header
33
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
@@ -18,11 +18,11 @@ module Mohawk
18
18
  end
19
19
 
20
20
  def [](row)
21
- TableRow.new(self, view.row(:index => row))
21
+ TableRow.new(self, row)
22
22
  end
23
23
 
24
24
  def each
25
- view.rows.map do |row|
25
+ view.row_count.times.map do |row|
26
26
  yield TableRow.new(self, row)
27
27
  end
28
28
  end
@@ -1,13 +1,15 @@
1
1
  module Mohawk
2
2
  module Accessors
3
3
  class TableRow
4
+ include RAutomation::Adapter::MsUia
5
+
4
6
  attr_reader :row
5
7
 
6
- def initialize(table, row)
7
- @row = row
8
+ def initialize(table, row_index)
8
9
  @table = table
10
+ @row = Row.new(@table.view, :index => row_index)
9
11
  end
10
-
12
+
11
13
  def selected?
12
14
  @table.view.selected? row.row
13
15
  end
@@ -23,7 +25,7 @@ module Mohawk
23
25
  def value_from_header(name)
24
26
  which_column = header_methods.index(name)
25
27
  raise ArgumentError, "#{name} column does not exist in #{header_methods}" if which_column.nil?
26
- cells[which_column]
28
+ Cell.new(row, :index => which_column).text
27
29
  end
28
30
 
29
31
  def method_missing(name, *args)
@@ -1,6 +1,6 @@
1
1
  module Mohawk
2
2
  module Accessors
3
-
3
+
4
4
  #
5
5
  # Defines the locator indicating the top-level window that will be used
6
6
  # to find controls in the page
@@ -11,11 +11,26 @@ module Mohawk
11
11
  # @param [Hash] locator for the top-level window that hosts the page
12
12
  #
13
13
  def window(locator)
14
- define_method("which_window") do
14
+ define_method(:which_window) do
15
15
  locator
16
16
  end
17
17
  end
18
-
18
+
19
+ #
20
+ # Defines a locator indicating a child container that is a descendant of
21
+ # the top-level window
22
+ #
23
+ # @example
24
+ # parent(:id => 'someOtherContainer')
25
+ #
26
+ # @param [Hash] locator for a more specific parent container
27
+ #
28
+ def parent(locator)
29
+ define_method(:parent_container) do
30
+ locator
31
+ end
32
+ end
33
+
19
34
  #
20
35
  # Generates methods to enter text into a text field, get its value
21
36
  # and clear the text field
@@ -28,7 +43,7 @@ module Mohawk
28
43
  # @param [Hash] locator for how the text is found
29
44
  #
30
45
  def text(name, locator)
31
- define_method("#{name}") do
46
+ define_method("#{name}") do
32
47
  adapter.text(locator).value
33
48
  end
34
49
  define_method("#{name}=") do |text|
@@ -44,9 +59,9 @@ module Mohawk
44
59
  adapter.text(locator).view
45
60
  end
46
61
  end
47
-
62
+
48
63
  #
49
- # Generates methods to click on a button as well as get the value of
64
+ # Generates methods to click on a button as well as get the value of
50
65
  # the button text
51
66
  #
52
67
  # @example
@@ -239,7 +254,7 @@ module Mohawk
239
254
  # @example
240
255
  # tree_view(:tree, :id => "treeId")
241
256
  # # will generate 'tree', 'tree=', 'tree_items', 'expand_tree_item' and 'collapse_tree_item'
242
- # # methods to get the tree value, set the tree value (index or string), get all of the
257
+ # # methods to get the tree value, set the tree value (index or string), get all of the
243
258
  # # items, expand an item (index or string) and collapse an item (index or string)
244
259
  #
245
260
  # @param [String] the name used for the generated methods
@@ -1,38 +1,55 @@
1
1
  module Mohawk
2
2
  module Adapters
3
3
  class UiaAdapter
4
- attr_reader :window
5
-
6
- def initialize(locator)
4
+ def initialize(locator, container=nil)
7
5
  @window = RAutomation::Window.new(locator.merge(:adapter => :ms_uia))
6
+ @container = container
7
+ end
8
+
9
+ def window
10
+ @actual_window ||= begin
11
+ if @container
12
+ control = @window.control(@container)
13
+ RAutomation::WaitHelper.wait_until { control.exist? }
14
+ @window = RAutomation::Window.new(:hwnd => control.hwnd, :adapter => :ms_uia)
15
+ end
16
+ @window
17
+ end
8
18
  end
9
19
 
10
20
  def combo(locator)
11
- Mohawk::Accessors::Combo.new(self, locator)
21
+ @combos ||= {}
22
+ @combos[locator] ||= Mohawk::Accessors::Combo.new(self, locator)
12
23
  end
13
24
 
14
25
  def checkbox(locator)
15
- Mohawk::Accessors::CheckBox.new(self, locator)
26
+ @checkbox ||= {}
27
+ @checkbox[locator] ||= Mohawk::Accessors::CheckBox.new(self, locator)
16
28
  end
17
29
 
18
30
  def text(locator)
19
- Mohawk::Accessors::Text.new(self, locator)
31
+ @text_fields ||= {}
32
+ @text_fields[locator] ||= Mohawk::Accessors::Text.new(self, locator)
20
33
  end
21
34
 
22
35
  def button(locator)
23
- Mohawk::Accessors::Button.new(self, locator)
36
+ @buttons ||= {}
37
+ @buttons[locator] ||= Mohawk::Accessors::Button.new(self, locator)
24
38
  end
25
39
 
26
40
  def radio(locator)
27
- Mohawk::Accessors::Radio.new(self, locator)
41
+ @radios ||= {}
42
+ @radios[locator] ||= Mohawk::Accessors::Radio.new(self, locator)
28
43
  end
29
44
 
30
45
  def label(locator)
31
- Mohawk::Accessors::Label.new(self, locator)
46
+ @labels ||= {}
47
+ @labels[locator] ||= Mohawk::Accessors::Label.new(self, locator)
32
48
  end
33
49
 
34
50
  def link(locator)
35
- Mohawk::Accessors::Link.new(self, locator)
51
+ @links ||= {}
52
+ @links[locator] ||= Mohawk::Accessors::Link.new(self, locator)
36
53
  end
37
54
 
38
55
  def menu_item(locator)
@@ -40,15 +57,18 @@ module Mohawk
40
57
  end
41
58
 
42
59
  def table(locator)
43
- Mohawk::Accessors::Table.new(self, locator)
60
+ @tables ||= {}
61
+ @tables[locator] ||= Mohawk::Accessors::Table.new(self, locator)
44
62
  end
45
63
 
46
64
  def tree_view(locator)
47
- Mohawk::Accessors::TreeView.new(self, locator)
65
+ @trees ||= {}
66
+ @trees[locator] ||= Mohawk::Accessors::TreeView.new(self, locator)
48
67
  end
49
68
 
50
69
  def value_control(locator)
51
- Mohawk::Accessors::Control.new(self, locator)
70
+ @controls ||= {}
71
+ @controls[locator] ||= Mohawk::Accessors::Control.new(self, locator)
52
72
  end
53
73
  end
54
74
  end
@@ -1,3 +1,3 @@
1
1
  module Mohawk
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
data/lib/mohawk.rb CHANGED
@@ -17,8 +17,10 @@ module Mohawk
17
17
  end
18
18
 
19
19
  def initialize(extra={})
20
- @adapter = Mohawk::Adapters::UiaAdapter.new(which_window.merge(extra))
21
- end
20
+ locator = [which_window.merge(extra)]
21
+ locator << parent_container if respond_to?(:parent_container)
22
+ @adapter = Mohawk::Adapters::UiaAdapter.new(*locator)
23
+ end
22
24
 
23
25
  #
24
26
  # Returns whether or not the window exists
data/mohawk.gemspec CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |gem|
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
19
 
20
- gem.add_dependency 'rautomation', '>= 0.9.1'
20
+ gem.add_dependency 'rautomation', '>= 0.9.2'
21
21
  gem.add_dependency 'require_all'
22
22
  gem.add_dependency 'page_navigation', '>= 0.7'
23
23
 
@@ -29,4 +29,5 @@ Gem::Specification.new do |gem|
29
29
  gem.add_development_dependency 'guard'
30
30
  gem.add_development_dependency 'guard-rspec'
31
31
  gem.add_development_dependency 'terminal-notifier-guard'
32
+ gem.add_development_dependency 'json', '~> 1.7.7'
32
33
  end
data/spec/ffi_stub.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'ffi'
2
+
3
+ module FFI
4
+ module Library
5
+ def ffi_lib(*names)
6
+ end
7
+
8
+ def ffi_libraries
9
+ end
10
+
11
+ def attach_function(name, func, args, returns = nil, options = nil)
12
+ end
13
+ end
14
+ end
15
+
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'ffi'
3
2
 
4
3
  class TableScreen
5
4
  include Mohawk
@@ -21,19 +20,6 @@ class FakeTableRow
21
20
  end
22
21
  end
23
22
 
24
- module FFI
25
- module Library
26
- def ffi_lib(*names)
27
- end
28
-
29
- def ffi_libraries
30
- end
31
-
32
- def attach_function(name, func, args, returns = nil, options = nil)
33
- end
34
- end
35
- end
36
-
37
23
  describe Mohawk::Accessors::Table do
38
24
  let(:screen) { TableScreen.new }
39
25
  let(:window) { double("RAutomation Window") }
@@ -59,9 +45,12 @@ describe Mohawk::Accessors::Table do
59
45
  end
60
46
 
61
47
  it "has rows" do
62
- fake_rows = [FakeTableRow.new("First Row", 0), FakeTableRow.new("Second Row", 1)]
63
- expected_rows = fake_rows.map {|r| {:text => r.text, :row => r.row} }
64
- table.should_receive(:rows).and_return(fake_rows)
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)
65
54
  screen.top.map(&:to_hash).should eq(expected_rows)
66
55
  end
67
56
 
@@ -80,7 +69,7 @@ describe Mohawk::Accessors::Table do
80
69
  let(:table_row) { double("RAutomation TableRow") }
81
70
 
82
71
  before(:each) do
83
- table.should_receive(:row).with(:index => 0).and_return(table_row)
72
+ RAutomation::Adapter::MsUia::Row.should_receive(:new).with(table, :index => 0).and_return(table_row)
84
73
  table_row.stub(:row).and_return 0
85
74
  end
86
75
 
@@ -107,8 +96,10 @@ describe Mohawk::Accessors::Table do
107
96
  it "can get cell values by header name" do
108
97
  RAutomation::Adapter::MsUia::UiaDll.should_receive(:table_headers).and_return(["First Header", "Second Header"])
109
98
  table.should_receive(:search_information)
110
- expected_cells = [FakeTableRow.new("Item 1", 0), FakeTableRow.new("Item 2", 1)]
111
- table_row.should_receive(:cells).and_return(expected_cells)
99
+
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)
112
103
  screen.top[0].second_header.should eq("Item 2")
113
104
  end
114
105
 
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ class AnyScreen
4
+ include Mohawk
5
+ window(:title => /Some Title/)
6
+ end
7
+
8
+ RSpec::Matchers.define(:use_the_cached) do |control_type, the_alias=nil|
9
+ match do |screen|
10
+ screen.adapter.window.stub(the_alias || control_type).and_return(double('first control'), double('second control'))
11
+ first, second = 2.times.map { screen.adapter.send(control_type, :id => 'id') }
12
+ first.eql?(second)
13
+ end
14
+ end
15
+
16
+ describe Mohawk::Adapters::UiaAdapter do
17
+ subject(:screen) { AnyScreen.new }
18
+
19
+ let(:window) { double('RAutomation Window') }
20
+ before(:each) { RAutomation::Window.stub(:new).and_return(window) }
21
+
22
+ context 'caching controls' do
23
+ it { should use_the_cached(:combo, :select_list) }
24
+ it { should use_the_cached(:checkbox) }
25
+ it { should use_the_cached(:text, :text_field) }
26
+ it { should use_the_cached(:button) }
27
+ it { should use_the_cached(:radio) }
28
+ it { should use_the_cached(:label) }
29
+ it { should use_the_cached(:link, :label) }
30
+ it { should use_the_cached(:table) }
31
+ it { should use_the_cached(:tree_view, :select_list) }
32
+ it { should use_the_cached(:value_control) }
33
+ end
34
+ end
data/spec/spec_helper.rb CHANGED
@@ -2,4 +2,8 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
2
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
3
 
4
4
  require 'rspec'
5
+ require 'ffi_stub'
5
6
  require 'mohawk'
7
+ require 'coveralls'
8
+
9
+ Coveralls.wear!
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.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-26 00:00:00.000000000 Z
12
+ date: 2013-05-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rautomation
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: 0.9.1
21
+ version: 0.9.2
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
- version: 0.9.1
29
+ version: 0.9.2
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: require_all
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -187,6 +187,22 @@ dependencies:
187
187
  - - ! '>='
188
188
  - !ruby/object:Gem::Version
189
189
  version: '0'
190
+ - !ruby/object:Gem::Dependency
191
+ name: json
192
+ requirement: !ruby/object:Gem::Requirement
193
+ none: false
194
+ requirements:
195
+ - - ~>
196
+ - !ruby/object:Gem::Version
197
+ version: 1.7.7
198
+ type: :development
199
+ prerelease: false
200
+ version_requirements: !ruby/object:Gem::Requirement
201
+ none: false
202
+ requirements:
203
+ - - ~>
204
+ - !ruby/object:Gem::Version
205
+ version: 1.7.7
190
206
  description: High level wrapper around windows applications
191
207
  email:
192
208
  - levi@leviwilson.com
@@ -195,6 +211,7 @@ extensions: []
195
211
  extra_rdoc_files: []
196
212
  files:
197
213
  - .gitignore
214
+ - .gitmodules
198
215
  - .travis.yml
199
216
  - Changelog
200
217
  - Gemfile
@@ -226,6 +243,7 @@ files:
226
243
  - features/step_definitions/table_steps.rb
227
244
  - features/step_definitions/text_steps.rb
228
245
  - features/step_definitions/tree_view_steps.rb
246
+ - features/support/FizzWare.NBuilder.dll
229
247
  - features/support/WindowsForms.exe
230
248
  - features/support/app/WindowsForms/WindowsForms.sln
231
249
  - features/support/app/WindowsForms/WindowsForms/AboutBox.Designer.cs
@@ -256,6 +274,7 @@ files:
256
274
  - features/support/screens/about_screen.rb
257
275
  - features/support/screens/data_entry_form.rb
258
276
  - features/support/screens/main_screen.rb
277
+ - features/support/screens/screen_with_container.rb
259
278
  - features/support/string.rb
260
279
  - features/table.feature
261
280
  - features/text.feature
@@ -279,6 +298,7 @@ files:
279
298
  - lib/mohawk/navigation.rb
280
299
  - lib/mohawk/version.rb
281
300
  - mohawk.gemspec
301
+ - spec/ffi_stub.rb
282
302
  - spec/lib/mohawk/accessors/button_spec.rb
283
303
  - spec/lib/mohawk/accessors/checkbox_spec.rb
284
304
  - spec/lib/mohawk/accessors/combo_spec.rb
@@ -290,6 +310,7 @@ files:
290
310
  - spec/lib/mohawk/accessors/table_spec.rb
291
311
  - spec/lib/mohawk/accessors/text_spec.rb
292
312
  - spec/lib/mohawk/accessors/tree_view_spec.rb
313
+ - spec/lib/mohawk/adapters/uia_adapter_spec.rb
293
314
  - spec/lib/mohawk_spec.rb
294
315
  - spec/lib/navigation_spec.rb
295
316
  - spec/spec_helper.rb
@@ -341,6 +362,7 @@ test_files:
341
362
  - features/step_definitions/table_steps.rb
342
363
  - features/step_definitions/text_steps.rb
343
364
  - features/step_definitions/tree_view_steps.rb
365
+ - features/support/FizzWare.NBuilder.dll
344
366
  - features/support/WindowsForms.exe
345
367
  - features/support/app/WindowsForms/WindowsForms.sln
346
368
  - features/support/app/WindowsForms/WindowsForms/AboutBox.Designer.cs
@@ -371,10 +393,12 @@ test_files:
371
393
  - features/support/screens/about_screen.rb
372
394
  - features/support/screens/data_entry_form.rb
373
395
  - features/support/screens/main_screen.rb
396
+ - features/support/screens/screen_with_container.rb
374
397
  - features/support/string.rb
375
398
  - features/table.feature
376
399
  - features/text.feature
377
400
  - features/tree_view.feature
401
+ - spec/ffi_stub.rb
378
402
  - spec/lib/mohawk/accessors/button_spec.rb
379
403
  - spec/lib/mohawk/accessors/checkbox_spec.rb
380
404
  - spec/lib/mohawk/accessors/combo_spec.rb
@@ -386,6 +410,7 @@ test_files:
386
410
  - spec/lib/mohawk/accessors/table_spec.rb
387
411
  - spec/lib/mohawk/accessors/text_spec.rb
388
412
  - spec/lib/mohawk/accessors/tree_view_spec.rb
413
+ - spec/lib/mohawk/adapters/uia_adapter_spec.rb
389
414
  - spec/lib/mohawk_spec.rb
390
415
  - spec/lib/navigation_spec.rb
391
416
  - spec/spec_helper.rb