nobbie-wx-preview 0.0.1

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