mohawk 0.1.4 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
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