mohawk 0.1.4 → 0.2

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.
Files changed (73) hide show
  1. data/.gitmodules +3 -3
  2. data/Changelog +6 -1
  3. data/Rakefile +2 -3
  4. data/cucumber.yml +1 -1
  5. data/features/combo_box.feature +8 -0
  6. data/features/menu.feature +8 -0
  7. data/features/step_definitions/button_steps.rb +1 -1
  8. data/features/step_definitions/combo_box_steps.rb +13 -1
  9. data/features/step_definitions/control_steps.rb +1 -1
  10. data/features/step_definitions/menu_steps.rb +7 -2
  11. data/features/support/WindowsForms.exe +0 -0
  12. data/features/support/screens/main_screen.rb +3 -0
  13. data/features/table.feature +7 -7
  14. data/features/text.feature +4 -0
  15. data/lib/mohawk/accessors.rb +3 -0
  16. data/lib/mohawk/adapters/uia/button.rb +14 -0
  17. data/lib/mohawk/adapters/uia/checkbox.rb +22 -0
  18. data/lib/mohawk/adapters/uia/control.rb +83 -0
  19. data/lib/mohawk/adapters/uia/element_locator.rb +19 -0
  20. data/lib/mohawk/adapters/uia/menu_item.rb +48 -0
  21. data/lib/mohawk/adapters/uia/radio.rb +22 -0
  22. data/lib/mohawk/adapters/uia/select_list.rb +53 -0
  23. data/lib/mohawk/adapters/uia/spinner.rb +30 -0
  24. data/lib/mohawk/adapters/uia/tab_control.rb +26 -0
  25. data/lib/mohawk/adapters/uia/table.rb +62 -0
  26. data/lib/mohawk/adapters/uia/table_row.rb +73 -0
  27. data/lib/mohawk/adapters/uia/text_box.rb +47 -0
  28. data/lib/mohawk/adapters/uia/tree_view.rb +22 -0
  29. data/lib/mohawk/adapters/uia/value_control.rb +20 -0
  30. data/lib/mohawk/adapters/uia/window.rb +64 -0
  31. data/lib/mohawk/adapters/uia_adapter.rb +36 -50
  32. data/lib/mohawk/version.rb +1 -1
  33. data/lib/mohawk/waiter.rb +18 -0
  34. data/lib/mohawk/win_32.rb +12 -0
  35. data/lib/mohawk.rb +26 -14
  36. data/mohawk.gemspec +2 -2
  37. data/spec/lib/mohawk/adapters/uia/control_spec.rb +46 -0
  38. data/spec/lib/mohawk/adapters/uia/table_row_spec.rb +36 -0
  39. data/spec/lib/mohawk/waiter_spec.rb +39 -0
  40. data/spec/lib/mohawk_spec.rb +115 -92
  41. data/spec/lib/navigation_spec.rb +14 -20
  42. data/spec/spec_helper.rb +1 -1
  43. metadata +49 -67
  44. data/lib/mohawk/accessors/button.rb +0 -20
  45. data/lib/mohawk/accessors/checkbox.rb +0 -24
  46. data/lib/mohawk/accessors/combo.rb +0 -37
  47. data/lib/mohawk/accessors/control.rb +0 -24
  48. data/lib/mohawk/accessors/label.rb +0 -15
  49. data/lib/mohawk/accessors/link.rb +0 -9
  50. data/lib/mohawk/accessors/menu_item.rb +0 -21
  51. data/lib/mohawk/accessors/radio.rb +0 -19
  52. data/lib/mohawk/accessors/spinner.rb +0 -27
  53. data/lib/mohawk/accessors/table.rb +0 -69
  54. data/lib/mohawk/accessors/table_row.rb +0 -62
  55. data/lib/mohawk/accessors/tabs.rb +0 -30
  56. data/lib/mohawk/accessors/text.rb +0 -29
  57. data/lib/mohawk/accessors/tree_view.rb +0 -32
  58. data/spec/lib/mohawk/accessors/button_spec.rb +0 -45
  59. data/spec/lib/mohawk/accessors/checkbox_spec.rb +0 -46
  60. data/spec/lib/mohawk/accessors/combo_spec.rb +0 -119
  61. data/spec/lib/mohawk/accessors/control_spec.rb +0 -47
  62. data/spec/lib/mohawk/accessors/label_spec.rb +0 -29
  63. data/spec/lib/mohawk/accessors/link_spec.rb +0 -34
  64. data/spec/lib/mohawk/accessors/menu_spec.rb +0 -32
  65. data/spec/lib/mohawk/accessors/radio_spec.rb +0 -35
  66. data/spec/lib/mohawk/accessors/spinner_spec.rb +0 -43
  67. data/spec/lib/mohawk/accessors/table_row_spec.rb +0 -28
  68. data/spec/lib/mohawk/accessors/table_spec.rb +0 -262
  69. data/spec/lib/mohawk/accessors/tabs_spec.rb +0 -58
  70. data/spec/lib/mohawk/accessors/text_spec.rb +0 -51
  71. data/spec/lib/mohawk/accessors/tree_view_spec.rb +0 -98
  72. data/spec/lib/mohawk/adapters/uia_adapter_spec.rb +0 -55
  73. data/spec/table_stubber.rb +0 -55
@@ -0,0 +1,47 @@
1
+ module Mohawk
2
+ module Adapters
3
+ module UIA
4
+ class TextBox < Control
5
+ valid_patterns :text, :value
6
+
7
+ def enter(*args)
8
+ send_keys *args
9
+ end
10
+
11
+ def set(value)
12
+ if is_value?
13
+ value_pattern.value = value
14
+ else
15
+ text_pattern.text = value
16
+ end
17
+ end
18
+
19
+ def value
20
+ return value_pattern.value if is_value?
21
+ text_pattern.text
22
+ end
23
+
24
+ def clear
25
+ set ''
26
+ end
27
+
28
+ private
29
+ def is_value?
30
+ element.patterns.include? :value
31
+ end
32
+
33
+ def value_pattern
34
+ element.as :value
35
+ end
36
+
37
+ def is_text?
38
+ element.patterns.include? :text
39
+ end
40
+
41
+ def text_pattern
42
+ element.as :text
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,22 @@
1
+ module Mohawk
2
+ module Adapters
3
+ module UIA
4
+ class TreeView < SelectList
5
+ def expand(what)
6
+ find_element(what).as(:expand_collapse).expand
7
+ end
8
+
9
+ def collapse(what)
10
+ find_element(what).as(:expand_collapse).collapse
11
+ end
12
+
13
+ def value
14
+ selected_items.map(&:name).first
15
+ end
16
+
17
+ alias_method :items, :options
18
+ alias_method :select, :set
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module Mohawk
2
+ module Adapters
3
+ module UIA
4
+ class ValueControl < Control
5
+ def set(value)
6
+ value_pattern.value = value
7
+ end
8
+
9
+ def value
10
+ value_pattern.value
11
+ end
12
+
13
+ private
14
+ def value_pattern
15
+ element.as :value
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,64 @@
1
+ require 'uia'
2
+ require 'mohawk/win_32'
3
+
4
+ module Mohawk
5
+ module Adapters
6
+ module UIA
7
+ class Window
8
+ include Mohawk::Waiter
9
+ attr_reader :element
10
+
11
+ def initialize(locator, container)
12
+ @locator = locator
13
+ @container = container
14
+ end
15
+
16
+ def element
17
+ @element ||= Uia.find_element(@locator)
18
+ if @element && @container
19
+ @element = @element.find(@container) || @element
20
+ end
21
+ @element
22
+ end
23
+
24
+ def send_keys(*keys)
25
+ activate
26
+ element.send_keys keys
27
+ end
28
+
29
+ def active?
30
+ Mohawk::Win32.foreground_window == handle
31
+ end
32
+
33
+ def activate
34
+ Mohawk::Win32.set_foreground_window handle
35
+ Mohawk::Win32.activate handle
36
+ end
37
+
38
+ def exist?
39
+ Mohawk::Win32.is_window handle
40
+ end
41
+
42
+ def handle
43
+ element.handle
44
+ end
45
+
46
+ def title
47
+ element.name
48
+ end
49
+
50
+ def text
51
+ element.descendants.map &:name
52
+ end
53
+
54
+ def present?
55
+ element != nil
56
+ end
57
+
58
+ def wait_until_present
59
+ wait_until { element }
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,91 +1,77 @@
1
+ require 'mohawk/waiter'
2
+ require_rel 'uia'
3
+
1
4
  module Mohawk
2
5
  module Adapters
3
6
  class UiaAdapter
7
+ include UIA
8
+
4
9
  def initialize(locator, container=nil)
5
- @only_search_children = locator.delete(:children_only)
6
- @window = RAutomation::Window.new(locator.merge(:adapter => :ms_uia))
10
+ @children_only = locator.delete :children_only
11
+ @locator = locator
7
12
  @container = container
8
13
  end
9
14
 
10
15
  def window
11
- @actual_window ||= begin
12
- if @container
13
- control = @window.control(@container)
14
- RAutomation::WaitHelper.wait_until { control.exist? }
15
- @window = RAutomation::Window.new(:hwnd => control.hwnd, :adapter => :ms_uia)
16
- end
17
- @window
18
- end
16
+ @window ||= Window.new @locator, @container
19
17
  end
20
18
 
21
- def combo(locator)
22
- @combos ||= {}
23
- @combos[locator] ||= Mohawk::Accessors::Combo.new(self, merge(locator))
19
+ def value_control(locator)
20
+ ValueControl.new self, merge(locator)
24
21
  end
25
22
 
26
-
27
- def checkbox(locator)
28
- @checkbox ||= {}
29
- @checkbox[locator] ||= Mohawk::Accessors::CheckBox.new(self, merge(locator))
23
+ def button(locator)
24
+ Button.new self, merge(locator)
30
25
  end
31
26
 
32
- def text(locator)
33
- @text_fields ||= {}
34
- @text_fields[locator] ||= Mohawk::Accessors::Text.new(self, merge(locator))
27
+ def table(locator)
28
+ Table.new self, merge(locator)
35
29
  end
36
30
 
37
- def button(locator)
38
- @buttons ||= {}
39
- @buttons[locator] ||= Mohawk::Accessors::Button.new(self, merge(locator))
31
+ def checkbox(locator)
32
+ CheckBox.new self, merge(locator)
40
33
  end
41
34
 
42
- def radio(locator)
43
- @radios ||= {}
44
- @radios[locator] ||= Mohawk::Accessors::Radio.new(self, merge(locator))
45
- end
46
-
47
- def label(locator)
48
- @labels ||= {}
49
- @labels[locator] ||= Mohawk::Accessors::Label.new(self, merge(locator))
35
+ def combo(locator)
36
+ SelectList.new self, merge(locator)
50
37
  end
51
38
 
52
- def link(locator)
53
- @links ||= {}
54
- @links[locator] ||= Mohawk::Accessors::Link.new(self, merge(locator))
39
+ def tree_view(locator)
40
+ TreeView.new self, merge(locator)
55
41
  end
56
42
 
57
- def menu_item(locator)
58
- Mohawk::Accessors::MenuItem.new(self, merge(locator))
43
+ def radio(locator)
44
+ Radio.new self, merge(locator)
59
45
  end
60
46
 
61
- def table(locator)
62
- @tables ||= {}
63
- @tables[locator] ||= Mohawk::Accessors::Table.new(self, merge(locator))
47
+ def text(locator)
48
+ TextBox.new self, merge(locator)
64
49
  end
65
50
 
66
- def tree_view(locator)
67
- @trees ||= {}
68
- @trees[locator] ||= Mohawk::Accessors::TreeView.new(self, merge(locator))
51
+ def tab_control(locator)
52
+ TabControl.new self, merge(locator)
69
53
  end
70
54
 
71
- def value_control(locator)
72
- @controls ||= {}
73
- @controls[locator] ||= Mohawk::Accessors::Control.new(self, merge(locator))
55
+ def menu_item(locator)
56
+ MenuItem.new self, merge(locator)
74
57
  end
75
58
 
76
59
  def spinner(locator)
77
- Mohawk::Accessors::Spinner.new(self, merge(locator))
60
+ Spinner.new self, merge(locator)
78
61
  end
79
62
 
80
- def tab_control(locator)
81
- Mohawk::Accessors::Tabs.new(self, merge(locator))
63
+ def control(locator)
64
+ Control.new self, merge(locator)
82
65
  end
83
66
 
67
+ alias_method :label, :control
68
+ alias_method :link, :control
69
+
84
70
  private
85
71
  def merge(locator)
86
- locator = locator.merge(:children_only => true) if @only_search_children
72
+ locator = locator.merge children_only: true if @children_only
87
73
  locator
88
74
  end
89
75
  end
90
76
  end
91
- end
77
+ end
@@ -1,3 +1,3 @@
1
1
  module Mohawk
2
- VERSION = "0.1.4"
2
+ VERSION = "0.2"
3
3
  end
@@ -0,0 +1,18 @@
1
+ module Mohawk
2
+ module Waiter
3
+ extend self
4
+
5
+ class WaitTimeout < StandardError;
6
+ end
7
+
8
+ def wait_until(timeout=Mohawk.timeout, &block)
9
+ start = Time.now
10
+ until (result = block.call) || (Time.now - start > timeout)
11
+ sleep 0.25
12
+ end
13
+
14
+ raise WaitTimeout unless result
15
+ result
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ require 'ffi'
2
+
3
+ module Mohawk
4
+ module Win32
5
+ extend FFI::Library
6
+ ffi_lib 'user32.dll'
7
+ attach_function :set_foreground_window, :SetForegroundWindow, [:long], :bool
8
+ attach_function :foreground_window, :GetForegroundWindow, [], :int
9
+ attach_function :is_window, :IsWindow, [:int], :bool
10
+ attach_function :activate, :SetActiveWindow, [:int], :int
11
+ end
12
+ end
data/lib/mohawk.rb CHANGED
@@ -1,17 +1,16 @@
1
- require "rautomation"
2
1
  require 'childprocess'
3
- require "mohawk/version"
4
- require "require_all"
5
- require "mohawk/accessors"
6
- require "mohawk/navigation"
7
- require "mohawk/adapters/uia_adapter"
8
- require "mohawk/core_ext/string"
2
+ require 'mohawk/version'
3
+ require 'require_all'
4
+ require 'mohawk/accessors'
5
+ require 'mohawk/navigation'
6
+ require 'mohawk/adapters/uia_adapter'
7
+ require 'mohawk/core_ext/string'
9
8
 
10
- require_rel "mohawk/accessors"
9
+ require_rel 'mohawk/accessors'
11
10
 
12
11
  module Mohawk
13
- include RAutomation::WaitHelper
14
- extend RAutomation::WaitHelper
12
+ include Waiter
13
+ extend Waiter
15
14
 
16
15
  class InvalidApplicationPath < StandardError
17
16
  def initialize(message='You must set the Mohawk.app_path to start an application')
@@ -29,8 +28,7 @@ module Mohawk
29
28
  raise InvalidApplicationPath.new unless @app_path
30
29
  @app = ChildProcess.build(@app_path).start
31
30
 
32
- app_window = RAutomation::Window.new :pid => @app.pid
33
- wait_until { app_window.present? }
31
+ wait_until { Uia.find_element pid: @app.pid }
34
32
  end
35
33
 
36
34
  def self.stop
@@ -47,10 +45,24 @@ module Mohawk
47
45
  @app_path = path
48
46
  end
49
47
 
48
+ class << self
49
+ attr_accessor :timeout
50
+ attr_accessor :default_adapter
51
+ end
52
+ self.timeout = 60
53
+
54
+ def self.default_adapter
55
+ @default_adapter || Mohawk::Adapters::UiaAdapter
56
+ end
57
+
58
+ def self.default_adapter=(cls)
59
+ @default_adapter = cls
60
+ end
61
+
50
62
  def initialize(extra={})
51
63
  locator = [which_window.merge(extra)]
52
64
  locator << parent_container if respond_to?(:parent_container)
53
- @adapter = Mohawk::Adapters::UiaAdapter.new(*locator)
65
+ @adapter = Mohawk.default_adapter.new(*locator)
54
66
  end
55
67
 
56
68
  #
@@ -85,7 +97,7 @@ module Mohawk
85
97
  # Waits until a control exists
86
98
  #
87
99
  def wait_for_control(locator)
88
- control = adapter.window.control(locator)
100
+ control = adapter.control(locator)
89
101
  begin
90
102
  wait_until { control.exist? }
91
103
  rescue
data/mohawk.gemspec CHANGED
@@ -18,14 +18,14 @@ Gem::Specification.new do |gem|
18
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
19
  gem.require_paths = ["lib"]
20
20
 
21
- gem.add_dependency 'rautomation', '~> 0.14'
21
+ gem.add_dependency 'uia', '>= 0.3.1'
22
22
  gem.add_dependency 'require_all'
23
23
  gem.add_dependency 'page_navigation', '>= 0.7'
24
24
  gem.add_dependency 'childprocess', '~> 0.3.9'
25
- gem.add_dependency 'ffi', '1.9.0'
26
25
 
27
26
  gem.add_development_dependency 'cucumber'
28
27
  gem.add_development_dependency 'rspec', '>= 2.12.0'
28
+ gem.add_development_dependency 'rspec-given'
29
29
  gem.add_development_dependency 'rake'
30
30
  gem.add_development_dependency 'childprocess'
31
31
  gem.add_development_dependency 'rb-fsevent', '~> 0.9.1'
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mohawk::Adapters::UiaAdapter::Control do
4
+ let(:adapter) { double(window: window) }
5
+ let(:window) { double(element: parent) }
6
+ let(:parent) { double(find: element) }
7
+ let(:element) { double('uia element') }
8
+
9
+ def new_control(locator)
10
+ Mohawk::Adapters::UiaAdapter::Control.new adapter, locator
11
+ end
12
+
13
+ Given(:control) { new_control(id: 'whatever') }
14
+
15
+ context 'working with the element directly' do
16
+ context 'passes through if it can' do
17
+ When { expect(element).to receive(:bounding_rectangle).and_return [1, 2, 3, 4] }
18
+ Then { expect(control.bounding_rectangle).to eq [1, 2, 3, 4] }
19
+ end
20
+
21
+ context 'fails otherwise' do
22
+ When(:bad_method) { control.does_not_have }
23
+ Then { expect(bad_method).to have_failed NoMethodError }
24
+ end
25
+ end
26
+
27
+ class HasPattern < Mohawk::Adapters::UiaAdapter::Control
28
+ valid_patterns :this, :that
29
+ end
30
+
31
+ context '#patterns' do
32
+ Given { parent.stub(:find) {|l| @locator = l } }
33
+ Given(:no_filter) { new_control(id: 'hi') }
34
+ Given(:some_filter) { HasPattern.new adapter, id: 'hi' }
35
+ Given(:overridden) { HasPattern.new adapter, id: 'hi', pattern: :overridden }
36
+
37
+ def pattern(control)
38
+ control.exist?
39
+ @locator.delete(:pattern)
40
+ end
41
+
42
+ Then { pattern(no_filter) == nil }
43
+ Then { pattern(some_filter) == [:this, :that] }
44
+ Then { pattern(overridden) == :overridden }
45
+ end
46
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mohawk::Adapters::UIA::TableRow do
4
+ let(:table) { double 'table', element: table_element }
5
+ let(:table_element) { double 'table element', row_at: element }
6
+ let(:element) { double 'row Element' }
7
+
8
+ subject { Mohawk::Adapters::UIA::TableRow.new table, 123 }
9
+
10
+ def set_expected_cells(h)
11
+ element.stub(:items).and_return h.values.map {|v| UiaTableCell.new v }
12
+ table.stub(:headers).and_return h.keys.map(&:to_s)
13
+ end
14
+
15
+ context '#all_match?' do
16
+ it 'matches string values' do
17
+ set_expected_cells 'First Column' => 'Yo'
18
+
19
+ expect(subject).to be_all_match first_column: 'Yo'
20
+ end
21
+
22
+ it 'matches non-string values' do
23
+ set_expected_cells 'Date Field' => Date.today.to_s
24
+
25
+ expect(subject).to be_all_match date_field: Date.today
26
+ end
27
+ end
28
+
29
+ class UiaTableCell
30
+ attr_reader :name
31
+
32
+ def initialize(name)
33
+ @name = name
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ class WaiterStub
4
+ include Mohawk::Waiter
5
+ end
6
+
7
+ describe Mohawk::Waiter do
8
+ Given(:waiter) do
9
+ allow_any_instance_of(WaiterStub).to receive(:sleep) {}
10
+ WaiterStub.new
11
+ end
12
+
13
+ def fake_time(*times)
14
+ expect(Time).to receive(:now).exactly(times.count).times.and_return(*times)
15
+ end
16
+
17
+ def do_it
18
+ @fake_responses.shift
19
+ end
20
+
21
+ context 'default timeout' do
22
+ When { fake_time 0, 30, 60.1 }
23
+ Then { expect { waiter.wait_until { false } }.to raise_error Mohawk::WaitTimeout }
24
+ end
25
+
26
+ context 'specific timeout' do
27
+ When { fake_time 0, 30, 30.1 }
28
+ Then { expect { waiter.wait_until(30) { false } }.to raise_error Mohawk::WaitTimeout }
29
+ end
30
+
31
+ context 'waits in between' do
32
+ When do
33
+ @fake_responses = [false, true]
34
+ fake_time 0, 1
35
+ end
36
+
37
+ Then { expect { waiter.wait_until { do_it } }.not_to raise_error }
38
+ end
39
+ end