nobbie-wx-preview 0.0.1

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.
@@ -0,0 +1,79 @@
1
+ module Nobbie
2
+ module Wx
3
+ module Command
4
+
5
+ class TypeIntoCommand < ComponentAwareCommand #:nodoc:
6
+ def initialize(path, value)
7
+ super(path)
8
+ @value = value
9
+ end
10
+
11
+ def execute
12
+ highlight {
13
+ if component.is_a?(TextCtrl) || component.is_a?(ComboBox)
14
+ handle_text_ctrl_or_combo_box
15
+ else
16
+ handle_unsupported_operation_for_component
17
+ end
18
+ }
19
+ end
20
+
21
+ def describe
22
+ "Type #{@value} into #{@value}"
23
+ end
24
+
25
+ private
26
+
27
+ def handle_text_ctrl_or_combo_box
28
+ ensure_enabled
29
+
30
+ Kernel.raise(ComponentReadOnlyException,
31
+ "cannot type, component is readonly") if readonly
32
+
33
+ #todo: resolve issue with events when value does not change
34
+ #this is required because windows raises the event when the value is the same, OSX does not.
35
+ #return if component.value == @value
36
+
37
+ #todo: consider using 'clear' .. but symantics different for ComboBox (ControlWithItems)
38
+ type('')
39
+
40
+ @value.length.times {|n| type(@value[0..n]) }
41
+
42
+ #todo: this looks like a windows wx issue .. the docs state that set_value does nothing if the combo is readonly
43
+ Kernel.raise(ComponentReadOnlyException,
44
+ "cannot type, component is readonly") if component.value != @value && component.is_a?(ComboBox)
45
+ end
46
+
47
+ def readonly
48
+ if component.is_a?(TextCtrl)
49
+ return !component.is_editable
50
+ end
51
+
52
+ if component.is_a?(ComboBox)
53
+ #todo: fix ComboBox readonly
54
+ #there doesnt seem to be a method of determining when a combobox is readonly ...
55
+ #in theory you can try setting it to a value thats not in the list and see if that changes
56
+ #which doesnt work as expected on windows (see above))
57
+ return false
58
+ end
59
+ end
60
+
61
+ def type(text)
62
+ #todo: this isnt quite right .. should really use append (but combo's don't support it)
63
+
64
+ #todo: find a nicer way to handle OS specific bits ...
65
+ if Platform.windows?
66
+ event = CommandEvent.new(EVT_COMMAND_TEXT_UPDATED, component.get_id)
67
+ event.string = text
68
+ event.event_object = component
69
+ component.command(event) if component.is_a?(ComboBox)
70
+ end
71
+ component.value = text
72
+ component.refresh
73
+ component.update
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,25 @@
1
+ module Nobbie
2
+ module Wx
3
+ module Command
4
+
5
+ class Executor #:nodoc:
6
+ def execute(command)
7
+ puts "\n> #{command.describe}"
8
+ result = command.execute
9
+ puts "< #{render(result)}"
10
+ result
11
+ end
12
+
13
+ private
14
+
15
+ def render(component)
16
+ #todo: this needs improving to support other components
17
+ return '' if component.nil?
18
+ return component.value if component.respond_to?(:get_value)
19
+ return component
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,48 @@
1
+ require 'nobbie/wx/command'
2
+
3
+ command = File.dirname(__FILE__) + File::SEPARATOR + 'command'
4
+ Dir.glob("#{command}/**/*.rb") {|f| require "#{f}" }
5
+
6
+ require 'nobbie/wx/command_executor'
7
+
8
+ module Nobbie
9
+ module Wx
10
+ module Command
11
+
12
+ class Factory
13
+ def create_type_into_command(path, value)
14
+ TypeIntoCommand.new(path, value)
15
+ end
16
+
17
+ def create_get_component_command(path)
18
+ GetComponentCommand.new(path)
19
+ end
20
+
21
+ def create_click_on_command(path)
22
+ ClickOnCommand.new(path)
23
+ end
24
+
25
+ def create_get_selected_values_command(path)
26
+ GetSelectedValuesCommand.new(path)
27
+ end
28
+
29
+ def create_select_command(path, value)
30
+ SelectCommand.new(path, value)
31
+ end
32
+
33
+ def create_is_chosen_command(path)
34
+ IsChosenCommand.new(path)
35
+ end
36
+
37
+ def create_choose_command(path)
38
+ ChooseCommand.new(path)
39
+ end
40
+
41
+ def create_is_enabled_command(path)
42
+ IsEnabledCommand.new(path)
43
+ end
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,7 @@
1
+ require 'nobbie/wx/operations'
2
+
3
+ include Nobbie::Wx::Operations
4
+
5
+ def with_application
6
+ Nobbie::Wx::ApplicationLauncher.new.with_application { yield }
7
+ end
@@ -0,0 +1,41 @@
1
+ module Nobbie
2
+ module Wx
3
+
4
+ class ComponentNotFoundException < RuntimeError; end
5
+ class ValueNotFoundException < RuntimeError; end
6
+ class ComponentDisabledException < RuntimeError; end
7
+ class UnsupportedOperationForComponentException < RuntimeError; end
8
+ class ComponentReadOnlyException < RuntimeError; end
9
+
10
+ class ElementPathBuilder
11
+ def initialize(name)
12
+ @name = name
13
+ end
14
+
15
+ # Finds the component specified in the path. This implementation is about as dumb as its gets, but does
16
+ # handle named components and menus.
17
+ def find_component
18
+ #todo: make me properly navigate component tree
19
+ #todo: I should blow up if multiple windows with the same name are found ....
20
+
21
+ component = Window.find_window_by_name(@name)
22
+ return component unless component.nil?
23
+
24
+ menu_bar = APPLICATION_UNDER_TEST.get_top_window.get_menu_bar
25
+ unless menu_bar.nil?
26
+ component = menu_bar.get_menu(menu_bar.find_menu(@name))
27
+ end
28
+
29
+ #todo: pull this up ...
30
+ Kernel.raise(ComponentNotFoundException, "cannot find component with name: #{@name}") if component.nil?
31
+
32
+ component
33
+ end
34
+
35
+ def to_s
36
+ "#{@name}"
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ module Nobbie
2
+ module Wx
3
+
4
+ class ChoosableOperations
5
+ def initialize(operations, path)
6
+ @operations = operations
7
+ @path = path
8
+ end
9
+
10
+ # Chooses the component specified in the path.
11
+ # Supported components: RadioButton, CheckBox
12
+ def choose
13
+ execute(@operations.command_factory.create_choose_command(@path))
14
+ end
15
+
16
+ # Determines if the component specified in the path is chosen.
17
+ # Supported components: RadioButton, CheckBox
18
+ def chosen?
19
+ execute(@operations.command_factory.create_is_chosen_command(@path))
20
+ end
21
+
22
+ private
23
+
24
+ #todo: pullup execute
25
+ def execute(command)
26
+ Command::Executor.new.execute(command)
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ module Nobbie
2
+ module Wx
3
+
4
+ class SelectOperations
5
+ def initialize(operations, path)
6
+ @operations = operations
7
+ @path = path
8
+ end
9
+
10
+ # Retrieves the currently selected value for the component specified in the path.
11
+ # Supported components: Notebook, ComboBox, ListBox, Choice
12
+ def selected_value
13
+ execute(@operations.command_factory.create_get_selected_values_command(@path))
14
+ end
15
+
16
+ # Selects the given value for the component specified in the path.
17
+ # Supported components: Notebook, Menu, ComboBox, ListBox, Choice
18
+ def choose(value)
19
+ execute(@operations.command_factory.create_select_command(@path, value))
20
+ end
21
+
22
+ private
23
+
24
+ #todo: pullup execute
25
+ def execute(command)
26
+ Command::Executor.new.execute(command)
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,75 @@
1
+ module Nobbie
2
+ module Wx
3
+
4
+ module ApplicationUnderTest #:nodoc:
5
+ def init_timer
6
+ @aut_timer = Timer.new(self, -1)
7
+ @aut_timer.start(10)
8
+ evt_timer(@aut_timer.object_id) {|e| self.yield; Thread.pass }
9
+ end
10
+
11
+ #todo: think about adding evt_idle here as well.
12
+ end
13
+
14
+ class ApplicationLauncher #:nodoc:
15
+
16
+ AUT_NOT_WX_APP = "APPLICATION_UNDER_TEST must be an instance of a Wx::App"
17
+ AUT_NOT_DEFINED = "APPLICATION_UNDER_TEST must be set to be an instance of the application you wish to test"
18
+
19
+ def initialize
20
+ begin
21
+ app = get_application
22
+ unless app.is_a?(Wxruby2::App)
23
+ handle(AUT_NOT_WX_APP)
24
+ end
25
+ rescue NameError => e
26
+ handle(AUT_NOT_DEFINED)
27
+ end
28
+ @app = app
29
+ end
30
+
31
+ def with_application
32
+ start
33
+ result = yield
34
+ stop
35
+ result
36
+ end
37
+
38
+ def start
39
+ puts "\n>> Starting application: #{@app.class}"
40
+ start = Time.now
41
+
42
+ @app_thread = Thread.new {
43
+ @app.extend(ApplicationUnderTest)
44
+ @app.init_timer
45
+ @app.main_loop
46
+ }
47
+
48
+ @app_thread.priority = -1
49
+
50
+ sleep 1
51
+ finish = Time.now
52
+ puts "\n>> Took #{finish-start} seconds to start application"
53
+ Thread.pass
54
+ end
55
+
56
+ def stop
57
+ puts "\n>> Stopping application: #{@app.class}\n"
58
+
59
+ #todo: tbis would seem a polite way to exit .. but causes Bus/Segmentation Errors on OSX.
60
+ #@app.top_window.destroy
61
+ #@app.exit_main_loop
62
+ #@app = nil
63
+ end
64
+
65
+ def handle(message)
66
+ Kernel.raise message
67
+ end
68
+
69
+ def get_application
70
+ APPLICATION_UNDER_TEST
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,85 @@
1
+ require 'nobbie/wx/platform'
2
+ require 'nobbie/wx/command_factory'
3
+ require 'nobbie/wx/launcher'
4
+
5
+ impl = File.dirname(__FILE__) + File::SEPARATOR + 'impl'
6
+ Dir.glob("#{impl}/**/*.rb") {|f| require "#{f}" }
7
+
8
+ module Nobbie
9
+ module Wx
10
+
11
+ module Operations
12
+
13
+ # Types text into the component specified in the path.
14
+ # Supported components: TextCtrl, ComboBox
15
+ def type(text, path)
16
+ execute(command_factory.create_type_into_command(coerce_path(path), text))
17
+ end
18
+
19
+ # Clicks the component specified in the path.
20
+ # Supported components: Button
21
+ def click(path)
22
+ execute(command_factory.create_click_on_command(coerce_path(path)))
23
+ end
24
+
25
+ # Returns the component specified in the path. Useful for performing operations that Nobbie does not
26
+ # currently support.
27
+ # Supported components: Any
28
+ def component(path)
29
+ execute(command_factory.create_get_component_command(coerce_path(path)))
30
+ end
31
+
32
+ # Returns a SelectionOperations[link:classes/Nobbie/Wx/SelectOperations.html] for interacting with
33
+ # the component specified in the path
34
+ def selection(path)
35
+ SelectOperations.new(self, coerce_path(path))
36
+ end
37
+
38
+ # Returns a ChoosableOperations[link:classes/Nobbie/Wx/ChoosableOperations.html] for interacting with
39
+ # the component specified in the path
40
+ def choosable(path)
41
+ ChoosableOperations.new(self, coerce_path((path)))
42
+ end
43
+
44
+ # Determines if the component specified in the path is currently enabled.
45
+ # Supported components: Any
46
+ def enabled?(path)
47
+ execute(command_factory.create_is_enabled_command(coerce_path(path)))
48
+ end
49
+
50
+ # Creates an instance of the default {path builder}[link:classes/Nobbie/Wx/ElementPathBuilder.html].
51
+ # Override this method if you wish to provide an alternative default path builder (because coerce_path
52
+ # uses in_() internally).
53
+ def in_(name)
54
+ ElementPathBuilder.new(name)
55
+ end
56
+
57
+ # Creates an instance of the default {command factory}[link:classes/Nobbie/Wx/CommandFactory.html].
58
+ # Override this method if you wish to provide an alternative command factory.
59
+ def command_factory
60
+ Command::Factory.new
61
+ end
62
+
63
+ private
64
+
65
+ def coerce_path(path)
66
+ return path if path.respond_to?(:find_component)
67
+
68
+ if path.is_a?(Hash) && path.has_key?(:in)
69
+ return path[:in].is_a?(String) ? in_(path[:in]) : path[:in]
70
+ end
71
+
72
+ return in_(path.id2name) if path.is_a?(Symbol)
73
+ return in_(path) if path.is_a?(String)
74
+
75
+ Kernel.raise("Unable to coerce path: #{path}")
76
+ end
77
+
78
+ #todo: pull up
79
+ def execute(command)
80
+ Command::Executor.new.execute(command)
81
+ end
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require 'wx'
3
+
4
+ #todo: move this into Nobbie::Wx namespace ... once name clash is fixed
5
+ class Platform #:nodoc:
6
+ include Wx
7
+
8
+ WINDOWS = 'WXMSW'
9
+ MAC = 'WXMAC'
10
+ SUPPORTED_PLATFORMS = [WINDOWS, MAC]
11
+
12
+ def self.windows?
13
+ name == WINDOWS
14
+ end
15
+
16
+ def self.ensure_supported
17
+ Kernel.raise "Sorry '#{name}' is not currently supported, Nobbie-Wx is only available for the following platforms: [#{SUPPORTED_PLATFORMS.join(',')}]" unless supported?
18
+ end
19
+
20
+ private
21
+
22
+ def self.supported?
23
+ SUPPORTED_PLATFORMS.include?(name)
24
+ end
25
+
26
+ def self.name
27
+ Wx::PLATFORM
28
+ end
29
+ end
30
+
31
+ Platform.ensure_supported