kolach-melomel 0.6.4
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.
- data/CHANGELOG +35 -0
- data/README.md +58 -0
- data/lib/melomel.rb +48 -0
- data/lib/melomel/bridge.rb +76 -0
- data/lib/melomel/bridge/messaging.rb +252 -0
- data/lib/melomel/bridge/ui.rb +155 -0
- data/lib/melomel/cucumber.rb +150 -0
- data/lib/melomel/cucumber/alert_steps.rb +40 -0
- data/lib/melomel/cucumber/button_steps.rb +17 -0
- data/lib/melomel/cucumber/color_picker_steps.rb +18 -0
- data/lib/melomel/cucumber/data_grid_steps.rb +83 -0
- data/lib/melomel/cucumber/date_steps.rb +22 -0
- data/lib/melomel/cucumber/list_steps.rb +30 -0
- data/lib/melomel/cucumber/slider_steps.rb +20 -0
- data/lib/melomel/cucumber/text_steps.rb +26 -0
- data/lib/melomel/date.rb +19 -0
- data/lib/melomel/error.rb +43 -0
- data/lib/melomel/flex.rb +46 -0
- data/lib/melomel/object_proxy.rb +94 -0
- data/lib/melomel/version.rb +3 -0
- data/lib/object.rb +30 -0
- data/test/helper.rb +28 -0
- data/test/sandbox.rb +14 -0
- data/test/test_bridge.rb +61 -0
- data/test/test_integration.rb +71 -0
- data/test/test_messaging.rb +109 -0
- data/test/test_object_proxy.rb +43 -0
- data/test/test_ui.rb +59 -0
- metadata +148 -0
@@ -0,0 +1,155 @@
|
|
1
|
+
# These add-on methods provide ease of use utility methods.
|
2
|
+
module Melomel
|
3
|
+
class Bridge
|
4
|
+
# Blocks until Melomel.busy in the Flash virtual machine is false.
|
5
|
+
#
|
6
|
+
# Returns nothing.
|
7
|
+
def wait()
|
8
|
+
loop do
|
9
|
+
break unless busy!
|
10
|
+
sleep 0.1
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Checks if the Flash virtual machine is currently busy. See Melomel#busy
|
15
|
+
# in the Melomel asdocs for more information.
|
16
|
+
#
|
17
|
+
# Returns true if the Flash virtual machine is busy. Otherwise returns
|
18
|
+
# false.
|
19
|
+
def busy!
|
20
|
+
get_class!('Melomel').busy!
|
21
|
+
end
|
22
|
+
|
23
|
+
# Finds a list of display objects matching a class and hash of properties.
|
24
|
+
#
|
25
|
+
# class_name - The type of objects to search for.
|
26
|
+
# root - The object to start searching from. (Defaults to the stage).
|
27
|
+
# properties - A list of properties to match on each object.
|
28
|
+
#
|
29
|
+
# Example:
|
30
|
+
#
|
31
|
+
# bridge.find_all('mx.controls.Button', :label => 'Click me')
|
32
|
+
# # => [<Melomel::ObjectProxy>, <Melomel::ObjectProxy>]
|
33
|
+
#
|
34
|
+
# Returns a list of display objects contained by root that match the
|
35
|
+
# properties and class specified.
|
36
|
+
def find_all(class_name, root={}, properties={})
|
37
|
+
# Merge hashes if no root is specified
|
38
|
+
if root.is_a?(Hash)
|
39
|
+
properties.merge!(root)
|
40
|
+
root = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
# Retrieve object
|
44
|
+
get_class('melomel.core.UI').findAll(class_name, root, properties)
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_all!(class_name, root={}, properties={})
|
48
|
+
objects = find_all(class_name, root, properties)
|
49
|
+
raise MelomelError.new("No objects found") if objects.empty?
|
50
|
+
return objects
|
51
|
+
end
|
52
|
+
|
53
|
+
# Finds the first display object matching a class and hash of properties.
|
54
|
+
#
|
55
|
+
# class_name - The type of object to search for.
|
56
|
+
# root - The object to start searching from. (Defaults to the stage).
|
57
|
+
# properties - A list of properties to match on the object.
|
58
|
+
#
|
59
|
+
# Example:
|
60
|
+
#
|
61
|
+
# bridge.find('mx.controls.Button', :label => 'Click me')
|
62
|
+
# # => <Melomel::ObjectProxy>
|
63
|
+
#
|
64
|
+
# Returns the first display object contained by root that matches the
|
65
|
+
# properties and class specified.
|
66
|
+
def find(class_name, root={}, properties={})
|
67
|
+
# Merge hashes if no root is specified
|
68
|
+
if root.is_a?(Hash)
|
69
|
+
properties.merge!(root)
|
70
|
+
root = nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# Retrieve object
|
74
|
+
get_class('melomel.core.UI').find(class_name, root, properties)
|
75
|
+
end
|
76
|
+
|
77
|
+
def find!(class_name, root={}, properties={})
|
78
|
+
object = find(class_name, root, properties)
|
79
|
+
raise MelomelError.new("No object found") if object.nil?
|
80
|
+
return object
|
81
|
+
end
|
82
|
+
|
83
|
+
# Finds a component based on the label of a nearby component. This works
|
84
|
+
# by first finding a Halo or Spark label component and then recursively
|
85
|
+
# searching the label's parent's children for a component of a given class.
|
86
|
+
#
|
87
|
+
# class_name - The type of object to search for.
|
88
|
+
# label_text - The label text to search for.
|
89
|
+
# root - The object to start searching from. (Defaults to the stage).
|
90
|
+
# properties - A list of properties to match on the object.
|
91
|
+
#
|
92
|
+
# Example:
|
93
|
+
#
|
94
|
+
# bridge.find_labeled('mx.controls.TextInput', 'First Name')
|
95
|
+
# # => <Melomel::ObjectProxy>
|
96
|
+
#
|
97
|
+
# Returns the first display object which is inside the parent of a given
|
98
|
+
# label.
|
99
|
+
def find_labeled(class_name, label_text, root={}, properties={})
|
100
|
+
# Merge hashes if no root is specified
|
101
|
+
if root.is_a?(Hash)
|
102
|
+
properties.merge!(root)
|
103
|
+
root = nil
|
104
|
+
end
|
105
|
+
|
106
|
+
# Retrieve object
|
107
|
+
get_class('melomel.core.UI').findLabeled(class_name, label_text, root, properties)
|
108
|
+
end
|
109
|
+
|
110
|
+
def find_labeled!(class_name, label_text, root={}, properties={})
|
111
|
+
object = find_labeled(class_name, label_text, root, properties)
|
112
|
+
raise MelomelError.new("No object found") if object.nil?
|
113
|
+
return object
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
# Imitates a click on a component
|
118
|
+
def click(component, properties={})
|
119
|
+
get_class('melomel.core.UI').click(component, properties)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Imitates a double click on a component
|
123
|
+
def double_click(component, properties={})
|
124
|
+
get_class('melomel.core.UI').doubleClick(component, properties)
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
# Imitates a key down on a component
|
129
|
+
def key_down(component, char, properties={})
|
130
|
+
get_class('melomel.core.UI').keyDown(component, char, properties)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Imitates a key up on a component
|
134
|
+
def key_up(component, char, properties={})
|
135
|
+
get_class('melomel.core.UI').keyUp(component, char, properties)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Imitates a key press on a component
|
139
|
+
def key_press(component, char, properties={})
|
140
|
+
get_class('melomel.core.UI').keyPress(component, char, properties)
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
# Generates a list of labels created by a data control or column based on a
|
145
|
+
# data set.
|
146
|
+
#
|
147
|
+
# component - The control or column which has an itemToLabel() method.
|
148
|
+
# data - The data set to generate labels from.
|
149
|
+
#
|
150
|
+
# Returns a Ruby array of labels.
|
151
|
+
def items_to_labels!(component, data)
|
152
|
+
get_class('melomel.core.UI').itemsToLabels!(component, data)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'cucumber'
|
2
|
+
require 'melomel'
|
3
|
+
Dir.glob(File.dirname(__FILE__) + '/cucumber/*', &method(:require))
|
4
|
+
|
5
|
+
# This class holds utility methods for running Cucumber steps.
|
6
|
+
module Melomel
|
7
|
+
class Cucumber
|
8
|
+
# A wrapper for Melomel actions that automatically waits until the Flash
|
9
|
+
# virtual machine is not busy before continuing.
|
10
|
+
#
|
11
|
+
# Returns nothing.
|
12
|
+
def self.run!
|
13
|
+
Melomel.wait()
|
14
|
+
yield
|
15
|
+
end
|
16
|
+
|
17
|
+
# Finds a component by id.
|
18
|
+
#
|
19
|
+
# class_name - The class or classes to match on.
|
20
|
+
# id - The id of the component.
|
21
|
+
# root - The root component to search from. Defaults to the stage.
|
22
|
+
# properties - Additional properties to search on.
|
23
|
+
#
|
24
|
+
# Returns a component matching the id and the additional properties.
|
25
|
+
def self.find_by_id!(class_name, id, root={}, properties={})
|
26
|
+
properties['id'] = id
|
27
|
+
Melomel.find!(class_name, root, properties)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Finds a component by label. If the first character is a "#" then the
|
31
|
+
# component should be found by id. Otherwise it is found by label.
|
32
|
+
#
|
33
|
+
# class_name - The class or classes to match on.
|
34
|
+
# label - The label of the component.
|
35
|
+
# root - The root component to search from. Defaults to the stage.
|
36
|
+
# properties - Additional properties to search on.
|
37
|
+
#
|
38
|
+
# Returns a component matching the label and the additional properties.
|
39
|
+
def self.find_by_label!(class_name, label, root={}, properties={})
|
40
|
+
set_properties_key(properties, 'label', label)
|
41
|
+
Melomel.find!(class_name, root, properties)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Finds a component that shares the same parent as a given label. If the
|
45
|
+
# first character is a "#" then the component should be found by id.
|
46
|
+
# Otherwise it is found by label.
|
47
|
+
#
|
48
|
+
# class_name - The class or classes to match on.
|
49
|
+
# label - The label text.
|
50
|
+
# root - The root component to search from. Defaults to the stage.
|
51
|
+
# properties - Additional properties to search on.
|
52
|
+
#
|
53
|
+
# Returns a component labeled by another component.
|
54
|
+
def self.find_labeled!(class_name, label, root={}, properties={})
|
55
|
+
if label.index('#') == 0
|
56
|
+
find_by_id!(class_name, label[1..-1], root, properties)
|
57
|
+
else
|
58
|
+
Melomel.find_labeled!(class_name, label, root, properties)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Finds a component by title. If the first character is a "#" then the
|
63
|
+
# component should be found by id. Otherwise it is found by title.
|
64
|
+
#
|
65
|
+
# class_name - The class or classes to match on.
|
66
|
+
# title - The title of the component.
|
67
|
+
# root - The root component to search from. Defaults to the stage.
|
68
|
+
# properties - Additional properties to search on.
|
69
|
+
#
|
70
|
+
# Returns a component matching the title and the additional properties.
|
71
|
+
def self.find_by_title!(class_name, title, root={}, properties={})
|
72
|
+
set_properties_key(properties, 'title', title)
|
73
|
+
Melomel.find!(class_name, root, properties)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Finds a component by text. If the first character is a "#" then the
|
77
|
+
# component should be found by id. Otherwise it is found by text.
|
78
|
+
#
|
79
|
+
# class_name - The class or classes to match on.
|
80
|
+
# text - The text property of the component.
|
81
|
+
# root - The root component to search from. Defaults to the stage.
|
82
|
+
# properties - Additional properties to search on.
|
83
|
+
#
|
84
|
+
# Returns a component matching the text and the additional properties.
|
85
|
+
def self.find_by_text!(class_name, text, root={}, properties={})
|
86
|
+
set_properties_key(properties, 'text', text)
|
87
|
+
Melomel.find!(class_name, root, properties)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Sets the key in the properties hash. If the first character is "#" then
|
91
|
+
# the key is "id". Otherwise it is set to the value of "key".
|
92
|
+
#
|
93
|
+
# properties - The properties hash.
|
94
|
+
# key - The name of the key to set.
|
95
|
+
# name - The name of the component.
|
96
|
+
#
|
97
|
+
# Returns nothing.
|
98
|
+
def self.set_properties_key(properties, key, name)
|
99
|
+
if name.index('#') == 0
|
100
|
+
properties['id'] = name[1..-1]
|
101
|
+
else
|
102
|
+
properties[key] = name
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Retrieves grid data as a 2D array of rows of columns. The first row
|
107
|
+
# contains the grid's header.
|
108
|
+
#
|
109
|
+
# grid - The grid to generate the table from.
|
110
|
+
#
|
111
|
+
# Returns a 2D array of rows of columns of data.
|
112
|
+
def self.get_grid_data(grid)
|
113
|
+
# Retrieve as columns of rows
|
114
|
+
data = []
|
115
|
+
grid.columns.length.times do |i|
|
116
|
+
column_data = []
|
117
|
+
column = grid.columns[i]
|
118
|
+
labels = Melomel.items_to_labels!(column, grid.dataProvider)
|
119
|
+
|
120
|
+
# Add column header
|
121
|
+
column_data << column.headerText
|
122
|
+
|
123
|
+
# Add label data
|
124
|
+
labels.length.times do |j|
|
125
|
+
column_data << labels[j]
|
126
|
+
end
|
127
|
+
|
128
|
+
# Add column data to data set
|
129
|
+
data << column_data
|
130
|
+
end
|
131
|
+
|
132
|
+
# Transpose and return
|
133
|
+
return data.transpose()
|
134
|
+
end
|
135
|
+
|
136
|
+
# The same grid data but stripped of whitespce
|
137
|
+
def self.get_striped_grid_data(grid)
|
138
|
+
data = get_grid_data(grid)
|
139
|
+
# Trim whitespace
|
140
|
+
data.each do |row|
|
141
|
+
row.each do |cell|
|
142
|
+
if cell != nil
|
143
|
+
cell.strip!
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
When /^I click the "([^"]*)" button on the alert$/ do |label|
|
2
|
+
Melomel::Cucumber.run! do
|
3
|
+
classes = Melomel::Flex.get_component_classes('alert')
|
4
|
+
alert = Melomel.find!(classes)
|
5
|
+
button = Melomel::Cucumber.find_by_label!('mx.controls.Button', label, alert)
|
6
|
+
button.setFocus()
|
7
|
+
Melomel.click(button)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
Then /^I should see an alert$/ do
|
12
|
+
Melomel::Cucumber.run! do
|
13
|
+
classes = Melomel::Flex.get_component_classes('alert')
|
14
|
+
Melomel.find!(classes)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Then /^I should see an alert with the title: "([^"]*)"$/ do |title|
|
19
|
+
Melomel::Cucumber.run! do
|
20
|
+
classes = Melomel::Flex.get_component_classes('alert')
|
21
|
+
alert = Melomel.find!(classes)
|
22
|
+
alert.title.should == title
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
Then /^I should see an alert with the message: "([^"]*)"$/ do |message|
|
27
|
+
Melomel::Cucumber.run! do
|
28
|
+
classes = Melomel::Flex.get_component_classes('alert')
|
29
|
+
alert = Melomel.find!(classes)
|
30
|
+
alert.text.should == message
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Then /^I should see an alert with the following message:$/ do |message|
|
35
|
+
Melomel::Cucumber.run! do
|
36
|
+
classes = Melomel::Flex.get_component_classes('alert')
|
37
|
+
alert = Melomel.find!(classes)
|
38
|
+
alert.text.should == message
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
When /^I click the "([^"]*)" (button|check box|radio button|tab)$/ do |name, type|
|
2
|
+
Melomel::Cucumber.run! do
|
3
|
+
classes = Melomel::Flex.get_component_classes(type)
|
4
|
+
button = Melomel::Cucumber.find_by_label!(classes, name)
|
5
|
+
button.setFocus()
|
6
|
+
Melomel.click(button)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
Then /^I should see the "([^"]*)" (button|check box|radio button) (not )?selected$/ do |name, type, neg|
|
11
|
+
Melomel::Cucumber.run! do
|
12
|
+
classes = Melomel::Flex.get_component_classes(type)
|
13
|
+
button = Melomel::Cucumber.find_by_label!(classes, name)
|
14
|
+
button.setFocus()
|
15
|
+
button.selected.should == neg.nil?
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
When /^I set the "([^"]*)" color picker to "#([0-9A-Fa-f]{6})"$/ do |name, color|
|
2
|
+
Melomel::Cucumber.run! do
|
3
|
+
classes = Melomel::Flex.get_component_classes('color picker')
|
4
|
+
picker = Melomel::Cucumber.find_labeled!(classes, name)
|
5
|
+
picker.setFocus()
|
6
|
+
picker.selectedColor = color.hex
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
Then /^I should see the "([^"]*)" color picker set to "#([0-9A-Fa-f]{6})"$/ do |name, color|
|
11
|
+
Melomel::Cucumber.run! do
|
12
|
+
classes = Melomel::Flex.get_component_classes('color picker')
|
13
|
+
picker = Melomel::Cucumber.find_labeled!(classes, name)
|
14
|
+
picker.setFocus()
|
15
|
+
sprintf('%06X', picker.selectedColor).should == color
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
When /^I select "([^"]*)" on the "([^"]*)" data grid$/ do |value, name|
|
2
|
+
Melomel::Cucumber.run! do
|
3
|
+
classes = Melomel::Flex.get_component_classes('data grid')
|
4
|
+
grid = Melomel::Cucumber.find_labeled!(classes, name)
|
5
|
+
grid.setFocus()
|
6
|
+
|
7
|
+
# Retrieve data and take off header row
|
8
|
+
data = Melomel::Cucumber.get_grid_data(grid)[1..-1]
|
9
|
+
|
10
|
+
# Loop data and check for matches
|
11
|
+
index = nil
|
12
|
+
data.each_index do |i|
|
13
|
+
row = data[i]
|
14
|
+
row.each do |cell|
|
15
|
+
if cell.strip == value
|
16
|
+
index = i
|
17
|
+
break
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
break unless index.nil?
|
22
|
+
end
|
23
|
+
|
24
|
+
# If we couldn't find a matching cell then throw an error
|
25
|
+
raise "Cannot find '#{value}' on data grid" if index.nil?
|
26
|
+
|
27
|
+
grid.selectedIndex = index
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
Then /^I should see "([^"]*)" selected on the "([^"]*)" data grid$/ do |value, name|
|
33
|
+
Melomel::Cucumber.run! do
|
34
|
+
classes = Melomel::Flex.get_component_classes('data grid')
|
35
|
+
grid = Melomel::Cucumber.find_labeled!(classes, name)
|
36
|
+
grid.setFocus()
|
37
|
+
|
38
|
+
# Retrieve data and take off header row
|
39
|
+
data = Melomel::Cucumber.get_grid_data(grid)[1..-1]
|
40
|
+
|
41
|
+
# Loop data and check for matches
|
42
|
+
index = nil
|
43
|
+
data.each_index do |i|
|
44
|
+
row = data[i]
|
45
|
+
row.each do |cell|
|
46
|
+
if cell.strip == value
|
47
|
+
index = i
|
48
|
+
break
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
break unless index.nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
grid.selectedIndex.should == index
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
Then /^I should see the following data in the "([^"]*)" data grid:$/ do |name, table|
|
60
|
+
Melomel::Cucumber.run! do
|
61
|
+
classes = Melomel::Flex.get_component_classes('data grid')
|
62
|
+
grid = Melomel::Cucumber.find_labeled!(classes, name)
|
63
|
+
grid.setFocus()
|
64
|
+
|
65
|
+
data = Melomel::Cucumber.get_striped_grid_data(grid)
|
66
|
+
|
67
|
+
table.diff!(data)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
Then /^I should see no data in the "([^"]*)" data grid$/ do |name|
|
73
|
+
Melomel::Cucumber.run! do
|
74
|
+
classes = Melomel::Flex.get_component_classes('data grid')
|
75
|
+
grid = Melomel::Cucumber.find_labeled!(classes, name)
|
76
|
+
grid.setFocus()
|
77
|
+
|
78
|
+
# Retrieve data and take off header row
|
79
|
+
data = Melomel::Cucumber.get_grid_data(grid)[1..-1]
|
80
|
+
|
81
|
+
data.size.should == 0
|
82
|
+
end
|
83
|
+
end
|