yui_rest_client 0.5.0

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 (49) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/publish.yml +33 -0
  3. data/.github/workflows/test.yml +26 -0
  4. data/.gitignore +15 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +41 -0
  7. data/Gemfile +8 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +216 -0
  10. data/Rakefile +20 -0
  11. data/bin/console +15 -0
  12. data/bin/setup +8 -0
  13. data/lib/yui_rest_client.rb +42 -0
  14. data/lib/yui_rest_client/actions.rb +12 -0
  15. data/lib/yui_rest_client/app.rb +230 -0
  16. data/lib/yui_rest_client/error.rb +13 -0
  17. data/lib/yui_rest_client/filter_extractor.rb +28 -0
  18. data/lib/yui_rest_client/http/http_client.rb +36 -0
  19. data/lib/yui_rest_client/http/response.rb +21 -0
  20. data/lib/yui_rest_client/http/version_controller.rb +26 -0
  21. data/lib/yui_rest_client/http/widget_controller.rb +54 -0
  22. data/lib/yui_rest_client/local_process.rb +73 -0
  23. data/lib/yui_rest_client/logger.rb +32 -0
  24. data/lib/yui_rest_client/timer.rb +20 -0
  25. data/lib/yui_rest_client/version.rb +6 -0
  26. data/lib/yui_rest_client/wait.rb +21 -0
  27. data/lib/yui_rest_client/waitable.rb +39 -0
  28. data/lib/yui_rest_client/widgets.rb +30 -0
  29. data/lib/yui_rest_client/widgets/bargraph.rb +62 -0
  30. data/lib/yui_rest_client/widgets/base.rb +114 -0
  31. data/lib/yui_rest_client/widgets/button.rb +33 -0
  32. data/lib/yui_rest_client/widgets/checkbox.rb +53 -0
  33. data/lib/yui_rest_client/widgets/combobox.rb +95 -0
  34. data/lib/yui_rest_client/widgets/datefield.rb +47 -0
  35. data/lib/yui_rest_client/widgets/label.rb +41 -0
  36. data/lib/yui_rest_client/widgets/menubutton.rb +48 -0
  37. data/lib/yui_rest_client/widgets/multilinebox.rb +84 -0
  38. data/lib/yui_rest_client/widgets/numberbox.rb +76 -0
  39. data/lib/yui_rest_client/widgets/progressbar.rb +45 -0
  40. data/lib/yui_rest_client/widgets/radiobutton.rb +35 -0
  41. data/lib/yui_rest_client/widgets/richtext.rb +36 -0
  42. data/lib/yui_rest_client/widgets/selectionbox.rb +87 -0
  43. data/lib/yui_rest_client/widgets/tab.rb +81 -0
  44. data/lib/yui_rest_client/widgets/table.rb +154 -0
  45. data/lib/yui_rest_client/widgets/textbox.rb +94 -0
  46. data/lib/yui_rest_client/widgets/timefield.rb +45 -0
  47. data/lib/yui_rest_client/widgets/tree.rb +149 -0
  48. data/yui_rest_client.gemspec +38 -0
  49. metadata +222 -0
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'logger'
5
+
6
+ module YuiRestClient
7
+ class Logger
8
+ extend Forwardable
9
+
10
+ def_delegators :@logger, :debug, :debug?,
11
+ :info, :info?,
12
+ :warn, :warn?,
13
+ :error, :error?,
14
+ :fatal, :fatal?,
15
+ :level
16
+
17
+ def initialize
18
+ @logger = create_logger($stdout)
19
+ end
20
+
21
+ def create_logger(output)
22
+ logger = ::Logger.new(output)
23
+ logger.progname = 'YuiRestClient'
24
+ logger.level = default_level
25
+ logger
26
+ end
27
+
28
+ def default_level
29
+ ENV['DEBUG'] ? :debug : :info
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YuiRestClient
4
+ module Timer
5
+ module_function
6
+
7
+ # Execute code block with the interval until timeout reached.
8
+ # @param timeout [Numeric] how many time in seconds to wait
9
+ # @param interval [Numeric] time in seconds between attempts
10
+ def wait(timeout, interval)
11
+ return yield if timeout.zero?
12
+
13
+ end_time = Time.now.to_f + timeout
14
+ while Time.now.to_f <= end_time
15
+ yield
16
+ sleep interval
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YuiRestClient
4
+ VERSION = '0.5.0'
5
+ API_VERSION = 'v1'
6
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YuiRestClient
4
+ module Wait
5
+ def self.until(timeout:, interval:, message: nil, object: nil)
6
+ Timer.wait(timeout, interval) do
7
+ result = yield(object)
8
+ return result if result
9
+ end
10
+ raise Error::TimeoutError, "timed out after #{timeout} seconds. #{message}"
11
+ end
12
+
13
+ def self.while(timeout:, interval:, message: nil, object: nil)
14
+ Timer.wait(timeout, interval) do
15
+ result = yield(object)
16
+ return unless result
17
+ end
18
+ raise Error::TimeoutError, "timed out after #{timeout} seconds. #{message}"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YuiRestClient
4
+ module Waitable
5
+ # Waits until the block evaluation will return true, raises Error::TimeoutError on timeout.
6
+ # @param timeout [Numeric] how long to wait (in seconds). Default is YuiRestClient.timeout.
7
+ # @param interval [Numeric] time in seconds between attempts. Default is YuiRestClient.interval.
8
+ # @param message [String] message to be sent in case timeout is reached.
9
+ # @raise Error::TimeoutError
10
+ # @return [Waitable] waitable object
11
+ # @example Wait for checkbox to be checked
12
+ # app.checkbox(id: 'test').wait_until(&:checked?)
13
+ def wait_until(timeout: YuiRestClient.timeout, interval: YuiRestClient.interval, message: nil, &block)
14
+ @timeout = timeout
15
+ @interval = interval
16
+ Wait.until(timeout: timeout, interval: interval, message: message, object: self, &block)
17
+ @timeout = YuiRestClient.timeout
18
+ @interval = YuiRestClient.interval
19
+ self
20
+ end
21
+
22
+ # Waits while the block evaluation returns true, raises Error::TimeoutError on timeout.
23
+ # @param timeout [Numeric] how long to wait (in seconds). Default is YuiRestClient.timeout.
24
+ # @param interval [Numeric] time in seconds between attempts. Default is YuiRestClient.interval.
25
+ # @param message [String] message to be sent in case timeout is reached.
26
+ # @raise Error::TimeoutError
27
+ # @return [Waitable] waitable object
28
+ # @example Wait for checkbox to be unchecked
29
+ # app.checkbox(id: 'test').wait_while(&:checked?)
30
+ def wait_while(timeout: YuiRestClient.timeout, interval: YuiRestClient.interval, message: nil, &block)
31
+ @timeout = timeout
32
+ @interval = interval
33
+ Wait.while(timeout: timeout, interval: interval, message: message, object: self, &block)
34
+ @timeout = YuiRestClient.timeout
35
+ @interval = YuiRestClient.interval
36
+ self
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YuiRestClient
4
+ module Widgets
5
+ require 'yui_rest_client/waitable'
6
+ require 'yui_rest_client/widgets/base'
7
+ require 'yui_rest_client/widgets/bargraph'
8
+ require 'yui_rest_client/widgets/button'
9
+ require 'yui_rest_client/widgets/checkbox'
10
+ require 'yui_rest_client/widgets/combobox'
11
+ require 'yui_rest_client/widgets/datefield'
12
+ require 'yui_rest_client/widgets/label'
13
+ require 'yui_rest_client/widgets/menubutton'
14
+ require 'yui_rest_client/widgets/multilinebox'
15
+ require 'yui_rest_client/widgets/numberbox'
16
+ require 'yui_rest_client/widgets/progressbar'
17
+ require 'yui_rest_client/widgets/radiobutton'
18
+ require 'yui_rest_client/widgets/selectionbox'
19
+ require 'yui_rest_client/widgets/tab'
20
+ require 'yui_rest_client/widgets/table'
21
+ require 'yui_rest_client/widgets/textbox'
22
+ require 'yui_rest_client/widgets/timefield'
23
+ require 'yui_rest_client/widgets/label'
24
+ require 'yui_rest_client/widgets/richtext'
25
+ require 'yui_rest_client/widgets/tree'
26
+ # Class representing a Wizard UI. It can be YWizard
27
+ class Wizard < Widgets::Base
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YuiRestClient
4
+ module Widgets
5
+ # Class representing a bar graph in the UI, namely YBarGraph.
6
+ class Bargraph < Widgets::Base
7
+ # Returns the list of segments in the bar graph. Each segment is defined by
8
+ # label and value. Segment length is defined by proportional ratio of the
9
+ # segment value. For instance, if segment values are 100, 200, 700: it means
10
+ # first segment will take 100/1000=10%, second 20% and third one 70% accordingly.
11
+ # @return [Array] array of [Hash] objects with :label and :value keys which
12
+ # store label and segment value for given segment
13
+ # @example Get segments from the bargraph with id "test_id"
14
+ # {
15
+ # "class" : "YBarGraph",
16
+ # "id" : "test_id",
17
+ # "segments" :
18
+ # [
19
+ # {
20
+ # "label" : "Windows\nused\n%1 MB",
21
+ # "value" : 600
22
+ # },
23
+ # {
24
+ # "label" : "Windows\nfree\n%1 MB",
25
+ # "value" : 350
26
+ # },
27
+ # {
28
+ # "label" : "Linux\n%1 MB",
29
+ # "value" : 800
30
+ # }
31
+ # ],
32
+ # }
33
+ # @example
34
+ # app.bargraph(id: 'test_id').segments
35
+ # @example
36
+ # app.bargraph(id: 'test_id').segments[1][:value]
37
+ # @example
38
+ # app.bargraph(id: 'test_id').segments[1][:label]
39
+ def segments
40
+ property(:segments)
41
+ end
42
+
43
+ # Returns the labels list of all segments in the bar graph.
44
+ # @return [Array] array of labels in order they are stored in segments hash.
45
+ # @example Get labels of the segments in the bargraph with id "test_id"
46
+ # @example
47
+ # app.bargraph(id: 'test_id').labels
48
+ def labels
49
+ segments.map { |segment| segment[:label] }
50
+ end
51
+
52
+ # Returns the values list of all segments in the bar graph.
53
+ # @return [Array] array of values in order they are stored in segments hash.
54
+ # @example Get values of the segments in the bargraph with id "test_id"
55
+ # @example
56
+ # app.bargraph(id: 'test_id').values
57
+ def values
58
+ segments.map { |segment| segment[:value] }
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YuiRestClient
4
+ module Widgets
5
+ class Base
6
+ include Waitable
7
+
8
+ def initialize(widget_controller, filter)
9
+ @widget_controller = widget_controller
10
+ @filter = filter
11
+ end
12
+
13
+ # Allows to check if widget exists on the current screen or not.
14
+ # Can be called against any widget.
15
+ # @return [Boolean] true if widget exists, false otherwise.
16
+ # @example Check if button with id 'test' exists
17
+ # app.button(id: 'test').exists? # true
18
+ def exists?
19
+ YuiRestClient.logger.info("Checking if #{class_name} with #{@filter} exists")
20
+ find_widgets
21
+ YuiRestClient.logger.info("#{class_name} exists: #{@filter}")
22
+ true
23
+ rescue Error::WidgetNotFoundError
24
+ YuiRestClient.logger.info("#{class_name} does not exist: #{@filter}")
25
+ false
26
+ end
27
+
28
+ # Allows to check if widget enabled or disabled.
29
+ # Can be called against any widget. Widget that does not have "enabled" property is counted as enabled.
30
+ # @return [Boolean] true if widget enabled, false otherwise.
31
+ # @example Check if button with id 'test' enabled
32
+ # app.button(id: 'test').enabled? # true
33
+ def enabled?
34
+ enabled_prop = property(:enabled)
35
+ enabled_prop.nil? || enabled_prop == true
36
+ end
37
+
38
+ # Returns debug_label value for widget.
39
+ # Can be called against any widget.
40
+ # @return [String] value of debug_label property
41
+ # @example Get debug_label value for button
42
+ # {
43
+ # "class": "YQWizardButton",
44
+ # "debug_label": "Cancel",
45
+ # "fkey": 9,
46
+ # "id": "cancel",
47
+ # "label": "&Cancel"
48
+ # }
49
+ # @example
50
+ # debug_label = app.button(id: 'cancel').debug_label # Cancel
51
+ def debug_label
52
+ property(:debug_label)
53
+ end
54
+
55
+ # Returns value of widget property.
56
+ # This method can be used to retrieve value of some specific property,
57
+ # but widget does not have a method to return the value.
58
+ # Can be called against any widget.
59
+ # @param property [Symbol] symbolic name of the property to get value for.
60
+ # @return [Object] value for property of a widget
61
+ # @example Get value of "label" property for button with id "test"
62
+ # value = app.button(id: 'test').property(:label)
63
+ def property(property)
64
+ YuiRestClient.logger.info("Get #{property} for #{class_name} #{@filter}")
65
+ result = find_widgets.first[property.to_sym]
66
+ YuiRestClient.logger.info("Found '#{property}=#{result}' for #{class_name} #{@filter}")
67
+ result
68
+ end
69
+
70
+ # This method can be used to send any action to widget.
71
+ # Can be called against any widget.
72
+ # @param params [Hash] actions to be sent (e.g. action: 'press').
73
+ # @example Send action 'press' to button widget.
74
+ # app.button(id: 'test').action(action: 'press')
75
+ def action(params)
76
+ unless @filter.regex.empty?
77
+ widget = find_widgets.first
78
+ @filter = FilterExtractor.new(widget)
79
+ end
80
+ YuiRestClient.logger.info("Send #{params} action for #{class_name} #{@filter}")
81
+ @widget_controller.send_action(@filter.plain, params)
82
+ end
83
+
84
+ # Get all widgets found with filter.
85
+ # The method is mainly introduced for "class" filter, which can return an array of widgets.
86
+ # It only makes sense to use this method whenever server side filters allow to find individually those
87
+ # collected widgets, otherwise those will not be able to access their internal properties.
88
+ # @return [Array] array of deserialized widgets.
89
+ # Then actions that are specified for the widget can be called while iterating over the returned array.
90
+ # @example Get all checkboxes and check all of them
91
+ # checkboxes = app.checkbox(class: "YCheckBox").collect_all
92
+ # checkboxes.each{ |checkbox| puts checkbox.check }
93
+ def collect_all
94
+ YuiRestClient.logger.info("Collect all #{class_name} widgets with filter #{@filter}")
95
+ widgets = find_widgets
96
+ YuiRestClient.logger.info("Found widgets for filter #{@filter}: #{widgets}")
97
+ widgets.map do |widget|
98
+ self.class.new(@widget_controller, FilterExtractor.new(widget))
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def class_name
105
+ self.class.name.split('::').last
106
+ end
107
+
108
+ def find_widgets
109
+ YuiRestClient.logger.info("Search for #{class_name} #{@filter}")
110
+ @widget_controller.find(@filter.plain).body(regex_filter: @filter.regex)
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YuiRestClient
4
+ module Widgets
5
+ # Class representing a button in UI. It can be YQWizardButton, YPushButton.
6
+ class Button < Widgets::Base
7
+ # Sends action to click the button in UI.
8
+ # @return [Button] in case action is successful
9
+ # @example Click button with id 'test'
10
+ # app.button(id: 'test').click
11
+ def click
12
+ action(action: Actions::PRESS)
13
+ self
14
+ end
15
+
16
+ # Returns fkey value for the button.
17
+ # @return [Integer] F (function) key that can be used as shortcut to press the button.
18
+ # @example Get fkey value for YQWizardButton
19
+ # {
20
+ # "class": "YQWizardButton",
21
+ # "debug_label": "Cancel",
22
+ # "fkey": 9,
23
+ # "id": "cancel",
24
+ # "label": "&Cancel"
25
+ # }
26
+ # @example
27
+ # fkey = app.button(id: 'cancel').fkey
28
+ def fkey
29
+ property(:fkey)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YuiRestClient
4
+ module Widgets
5
+ # Class representing a Checkbox in UI. It can be YCheckBox or YCheckBoxFrame.
6
+ class Checkbox < Widgets::Base
7
+ # Sends action to explicitly check the checkbox in UI (regardless of the current state).
8
+ # @return [Checkbox] in case action is successful
9
+ # @example Check checkbox with id 'test'
10
+ # app.checkbox(id: 'test').check
11
+ def check
12
+ action(action: Actions::CHECK)
13
+ self
14
+ end
15
+
16
+ # Returns the state of checkbox (checked/unchecked).
17
+ # Gets value from 'value' parameter in JSON representation of YCheckBox or YCheckBoxFrame.
18
+ # @return [Boolean] true if it is checked, false otherwise.
19
+ # @example Get checkbox state
20
+ # {
21
+ # "class": "YCheckBox",
22
+ # "debug_label": "Change the Time Now",
23
+ # "id": "change_now",
24
+ # "label": "Chan&ge the Time Now",
25
+ # "notify": true,
26
+ # "value": true
27
+ # }
28
+ # @example
29
+ # app.checkbox(id: 'change_now').checked? # true
30
+ def checked?
31
+ property(:value)
32
+ end
33
+
34
+ # Sends action to toggle the checkbox in UI (i.e. uncheck when checked, or check otherwise).
35
+ # @return [Checkbox] in case action is successful
36
+ # @example Toggle checkbox with id 'test'
37
+ # app.checkbox(id: 'test').toggle
38
+ def toggle
39
+ action(action: Actions::TOGGLE)
40
+ self
41
+ end
42
+
43
+ # Sends action to explicitly uncheck the checkbox in UI (regardless of the current state).
44
+ # @return [Checkbox] in case action is successful
45
+ # @example Uncheck checkbox with id 'test'
46
+ # checkbox(id: 'test').uncheck
47
+ def uncheck
48
+ action(action: Actions::UNCHECK)
49
+ self
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YuiRestClient
4
+ module Widgets
5
+ # Class representing a ComboBox in the UI. It can be YComboBox.
6
+ class Combobox < Widgets::Base
7
+ # Returns the list of items available to select from combobox.
8
+ # @return [Array<String>] array of strings.
9
+ # @example Get items from combobox with id "nfs_version"
10
+ # {
11
+ # "class": "YComboBox",
12
+ # "debug_label": "NFS Version",
13
+ # "icon_base_path": "",
14
+ # "id": "nfs_version",
15
+ # "items": [
16
+ # {
17
+ # "label": "Any (Highest Available)",
18
+ # "selected": true
19
+ # },
20
+ # {
21
+ # "label": "Force NFSv3"
22
+ # }
23
+ # ],
24
+ # "items_count": 5,
25
+ # "label": "NFS &Version",
26
+ # "value": "Any (Highest Available)"
27
+ # }
28
+ # @example
29
+ # app.combobox(id: 'nfs_version').items
30
+ # # Any (Highest Available)
31
+ # # Force NFSv3
32
+ def items
33
+ property(:items).map { |x| x[:label] }
34
+ end
35
+
36
+ # Sends action to select the item in combobox.
37
+ # @param item [String] item to select in combobox.
38
+ # List of items can be retrieved from JSON "items"->"label" manually or by using 'combobox(filter).items'.
39
+ # @return [Combobox] in case action is successful
40
+ # @raise YuiRestClient::Error::ItemNotFoundInWidgetError in case value is not found in combobox.
41
+ # @example Select "Force NFSv3" item in combobox with id "nfs_version"
42
+ # app.combobox(id: 'nfs_version').select('Force NFSv3')
43
+ def select(item)
44
+ action(action: Actions::SELECT, value: item)
45
+ self
46
+ end
47
+
48
+ # Returns selected item in combobox.
49
+ # @example Get selected item in combobox with id "nfs_version"
50
+ # {
51
+ # "class": "YComboBox",
52
+ # "debug_label": "NFS Version",
53
+ # "icon_base_path": "",
54
+ # "id": "nfs_version",
55
+ # "items": [
56
+ # {
57
+ # "label": "Any (Highest Available)",
58
+ # "selected": true
59
+ # },
60
+ # {
61
+ # "label": "Force NFSv3"
62
+ # }
63
+ # ],
64
+ # "items_count": 5,
65
+ # "label": "NFS &Version",
66
+ # "value": "Any (Highest Available)"
67
+ # }
68
+ # @example
69
+ # app.combobox(id: 'nfs_version').value
70
+ def value
71
+ property(:value)
72
+ end
73
+
74
+ # Sends action to set string to the editable combobox.
75
+ # @param item [String] value to set in the combobox.
76
+ # To check if combobox is editable, call editable?
77
+ # @return [Combobox] in case action is successful
78
+ # @raise YuiRestClient::Error::ItemNotFoundInWidgetError in case value is not found in combobox.
79
+ # @example Set "Custom Version" item in combobox with id "nfs_version"
80
+ # app.combobox(id: 'nfs_version').select('Custom Version')
81
+ def set(value)
82
+ action(action: Actions::ENTER_TEXT, value: value)
83
+ self
84
+ end
85
+
86
+ # Allows to check if combobox is editable and allows setting .
87
+ # @return [Boolean] true if widget enabled, false otherwise.
88
+ # @example Check if combobox with id 'test' editable
89
+ # app.combobox(id: 'test').editable? # true
90
+ def editable?
91
+ property(:editable) == true
92
+ end
93
+ end
94
+ end
95
+ end