lebowski 0.1.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.
- data/History.md +3 -0
- data/License.txt +20 -0
- data/Manifest.txt +84 -0
- data/README.md +146 -0
- data/Rakefile +42 -0
- data/bin/lebowski +26 -0
- data/bin/lebowski-spec +29 -0
- data/bin/lebowski-start-server +24 -0
- data/lib/lebowski.rb +15 -0
- data/lib/lebowski/core.rb +35 -0
- data/lib/lebowski/foundation.rb +52 -0
- data/lib/lebowski/foundation/application.rb +315 -0
- data/lib/lebowski/foundation/core.rb +61 -0
- data/lib/lebowski/foundation/core_query.rb +231 -0
- data/lib/lebowski/foundation/dom_element.rb +114 -0
- data/lib/lebowski/foundation/errors/argument_invalid_type.rb +31 -0
- data/lib/lebowski/foundation/errors/unexpected_type.rb +30 -0
- data/lib/lebowski/foundation/mixins/collection_item_view_support.rb +87 -0
- data/lib/lebowski/foundation/mixins/delegate_support.rb +22 -0
- data/lib/lebowski/foundation/mixins/inline_text_field_support.rb +29 -0
- data/lib/lebowski/foundation/mixins/key_check.rb +35 -0
- data/lib/lebowski/foundation/mixins/list_item_view_support.rb +36 -0
- data/lib/lebowski/foundation/mixins/positioned_element.rb +20 -0
- data/lib/lebowski/foundation/mixins/stall_support.rb +79 -0
- data/lib/lebowski/foundation/mixins/user_actions.rb +302 -0
- data/lib/lebowski/foundation/mixins/wait_actions.rb +44 -0
- data/lib/lebowski/foundation/object_array.rb +305 -0
- data/lib/lebowski/foundation/panes/alert.rb +117 -0
- data/lib/lebowski/foundation/panes/main.rb +20 -0
- data/lib/lebowski/foundation/panes/menu.rb +100 -0
- data/lib/lebowski/foundation/panes/modal.rb +21 -0
- data/lib/lebowski/foundation/panes/palette.rb +21 -0
- data/lib/lebowski/foundation/panes/pane.rb +24 -0
- data/lib/lebowski/foundation/panes/panel.rb +25 -0
- data/lib/lebowski/foundation/panes/picker.rb +43 -0
- data/lib/lebowski/foundation/panes/sheet.rb +21 -0
- data/lib/lebowski/foundation/proxy_factory.rb +87 -0
- data/lib/lebowski/foundation/proxy_object.rb +670 -0
- data/lib/lebowski/foundation/sc_object.rb +38 -0
- data/lib/lebowski/foundation/views/button.rb +20 -0
- data/lib/lebowski/foundation/views/checkbox.rb +63 -0
- data/lib/lebowski/foundation/views/collection.rb +304 -0
- data/lib/lebowski/foundation/views/container.rb +32 -0
- data/lib/lebowski/foundation/views/disclosure.rb +59 -0
- data/lib/lebowski/foundation/views/grid.rb +21 -0
- data/lib/lebowski/foundation/views/label.rb +30 -0
- data/lib/lebowski/foundation/views/list.rb +67 -0
- data/lib/lebowski/foundation/views/list_item.rb +280 -0
- data/lib/lebowski/foundation/views/menu_item.rb +27 -0
- data/lib/lebowski/foundation/views/radio.rb +32 -0
- data/lib/lebowski/foundation/views/segmented.rb +97 -0
- data/lib/lebowski/foundation/views/select_field.rb +139 -0
- data/lib/lebowski/foundation/views/support/simple_item_array.rb +249 -0
- data/lib/lebowski/foundation/views/text_field.rb +108 -0
- data/lib/lebowski/foundation/views/view.rb +108 -0
- data/lib/lebowski/runtime.rb +7 -0
- data/lib/lebowski/runtime/errors/remote_control_command_execution_error.rb +9 -0
- data/lib/lebowski/runtime/errors/remote_control_command_timeout_error.rb +9 -0
- data/lib/lebowski/runtime/errors/remote_control_error.rb +9 -0
- data/lib/lebowski/runtime/errors/selenium_server_error.rb +9 -0
- data/lib/lebowski/runtime/object_encoder.rb +123 -0
- data/lib/lebowski/runtime/sprout_core_driver.rb +14 -0
- data/lib/lebowski/runtime/sprout_core_extensions.rb +600 -0
- data/lib/lebowski/scui.rb +18 -0
- data/lib/lebowski/scui/mixins/node_item_view_support.rb +136 -0
- data/lib/lebowski/scui/mixins/terminal_view_support.rb +25 -0
- data/lib/lebowski/scui/views/combo_box.rb +119 -0
- data/lib/lebowski/scui/views/date_picker.rb +148 -0
- data/lib/lebowski/scui/views/linkit.rb +36 -0
- data/lib/lebowski/spec.rb +17 -0
- data/lib/lebowski/spec/core.rb +21 -0
- data/lib/lebowski/spec/matchers/be.rb +63 -0
- data/lib/lebowski/spec/matchers/has.rb +40 -0
- data/lib/lebowski/spec/matchers/match_supporters/has_object_function.rb +67 -0
- data/lib/lebowski/spec/matchers/match_supporters/has_predicate_with_no_prefix.rb +50 -0
- data/lib/lebowski/spec/matchers/match_supporters/has_predicate_with_prefix_has.rb +50 -0
- data/lib/lebowski/spec/matchers/match_supporters/match_supporter.rb +29 -0
- data/lib/lebowski/spec/matchers/method_missing.rb +24 -0
- data/lib/lebowski/spec/operators/operator.rb +20 -0
- data/lib/lebowski/spec/operators/that.rb +116 -0
- data/lib/lebowski/spec/util.rb +26 -0
- data/lib/lebowski/version.rb +17 -0
- data/resources/selenium-server.jar +0 -0
- data/resources/user-extensions.js +1421 -0
- metadata +198 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
# ==========================================================================
|
2
|
+
# Project: Lebowski Framework - The SproutCore Test Automation Framework
|
3
|
+
# License: Licensed under MIT license (see License.txt)
|
4
|
+
# ==========================================================================
|
5
|
+
|
6
|
+
module Lebowski
|
7
|
+
module Spec
|
8
|
+
module Util
|
9
|
+
|
10
|
+
#
|
11
|
+
# Will determine if the given two values match each other. If neither val1 or val2
|
12
|
+
# are regular expression then then will be compared using the standard == operator.
|
13
|
+
# Otherwise, if val1 or val2 is a regular expression, then it will be compared
|
14
|
+
# against the other value that must be a string
|
15
|
+
#
|
16
|
+
def self.match?(val1, val2)
|
17
|
+
if val1.kind_of? Regexp or val2.kind_of? Regexp
|
18
|
+
return (val1 =~ val2).nil? ? false : true
|
19
|
+
else
|
20
|
+
return val1 == val2
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# ==========================================================================
|
2
|
+
# Project: Lebowski Framework - The SproutCore Test Automation Framework
|
3
|
+
# License: Licensed under MIT license (see License.txt)
|
4
|
+
# ==========================================================================
|
5
|
+
|
6
|
+
module Lebowski # :nodoc:
|
7
|
+
module VERSION # :nodoc:
|
8
|
+
MAJOR = 0
|
9
|
+
MINOR = 1
|
10
|
+
TINY = 0
|
11
|
+
PRE = nil
|
12
|
+
|
13
|
+
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
|
14
|
+
|
15
|
+
SUMMARY = "lebowski #{STRING}"
|
16
|
+
end
|
17
|
+
end
|
Binary file
|
@@ -0,0 +1,1421 @@
|
|
1
|
+
// ==========================================================================
|
2
|
+
// Project: Lebowski Framework - The SproutCore Test Automation Framework
|
3
|
+
// License: Licensed under MIT license (see License.txt)
|
4
|
+
// ==========================================================================
|
5
|
+
/*globals Selenium PageBot selenium */
|
6
|
+
|
7
|
+
/**
|
8
|
+
This file contains all Lebowski framework extensions for the Selenium Core framework.
|
9
|
+
|
10
|
+
When starting up the selenium server, this file must be included in order for the
|
11
|
+
Lebowski framework to properly communicate with the SproutCore framework and application.
|
12
|
+
Example:
|
13
|
+
|
14
|
+
java -jar selenium_server -userExtensions user-extensions.js
|
15
|
+
|
16
|
+
There are three global variables used throughout this extension, which are:
|
17
|
+
|
18
|
+
$SC - References the SproutCore root object in the application window
|
19
|
+
$App - References the application's root object in the application window
|
20
|
+
$ScPath - The path parser used to access values for a given path using the SC
|
21
|
+
dot-path notation
|
22
|
+
|
23
|
+
In order to use selenium extensions correctly the doOpenScApplication method on the
|
24
|
+
Selenium object must be invoked. This method will assure that both the SproutCore framework
|
25
|
+
and application object are found.
|
26
|
+
|
27
|
+
*/
|
28
|
+
|
29
|
+
/**
|
30
|
+
There are two core global variables that are used to make it more convienient to program
|
31
|
+
against the SproutCore framework and the SC application under test.
|
32
|
+
*/
|
33
|
+
var $SC = null;
|
34
|
+
var $App = null;
|
35
|
+
|
36
|
+
/**
|
37
|
+
Root object that contains all the various helper objects for this extention
|
38
|
+
*/
|
39
|
+
var ScExt = {};
|
40
|
+
|
41
|
+
/**
|
42
|
+
Checks if the given value is indeed an SC.Object
|
43
|
+
*/
|
44
|
+
ScExt.isScObject = function(obj) {
|
45
|
+
return (obj && typeof(obj) === "object" && obj.kindOf && obj.kindOf($SC.Object));
|
46
|
+
};
|
47
|
+
|
48
|
+
/**
|
49
|
+
Converts an SC.IndexSet into a basic JavaScript array
|
50
|
+
*/
|
51
|
+
ScExt.indexSet2Array = function(indexSet) {
|
52
|
+
if (!indexSet) return [];
|
53
|
+
var indexes = [];
|
54
|
+
indexSet.forEach(function(index) {
|
55
|
+
indexes.push(index);
|
56
|
+
}, this);
|
57
|
+
return indexes;
|
58
|
+
};
|
59
|
+
|
60
|
+
/**
|
61
|
+
Converts a class name into an actuall SC class object. For instance, providing
|
62
|
+
"SC.CollectionView" will convert into the SC.CollectionView class object. If
|
63
|
+
a convertion can not be made then null is returned.
|
64
|
+
*/
|
65
|
+
ScExt.string2ScClass = function(className) {
|
66
|
+
var klassParts = className.split('.');
|
67
|
+
if (klassParts.length < 2) return null;
|
68
|
+
|
69
|
+
var klass = selenium.browserbot.getCurrentWindow();
|
70
|
+
for (var i = 0; i < klassParts.length; i++) {
|
71
|
+
var part = klassParts[i];
|
72
|
+
klass = klass[part];
|
73
|
+
var type = typeof(klass);
|
74
|
+
if (!klass || !(type === "object" || type === "function")) return null;
|
75
|
+
}
|
76
|
+
|
77
|
+
return klass;
|
78
|
+
};
|
79
|
+
|
80
|
+
/**
|
81
|
+
Will call a given view's scrollToVisible method to assure that it is indeed
|
82
|
+
visible to the user. The run loop is invoked to assure the views are all
|
83
|
+
updated.
|
84
|
+
*/
|
85
|
+
ScExt.viewScrollToVisible = function(view) {
|
86
|
+
$SC.RunLoop.begin();
|
87
|
+
view.scrollToVisible();
|
88
|
+
$SC.RunLoop.end();
|
89
|
+
};
|
90
|
+
|
91
|
+
/**
|
92
|
+
Gets all the class names that the given object inherits from. For instance, if
|
93
|
+
an object is of type SC.ButtonView, then the following will be returned
|
94
|
+
in order:
|
95
|
+
|
96
|
+
["SC.ButtonView", "SC.View", "SC.Object"]
|
97
|
+
*/
|
98
|
+
ScExt.getScObjectClassNames = function(obj) {
|
99
|
+
if (!ScExt.isScObject(obj)) return [];
|
100
|
+
|
101
|
+
var classNames = [];
|
102
|
+
var superclass = obj.constructor;
|
103
|
+
while (superclass) {
|
104
|
+
var sc = superclass.toString();
|
105
|
+
if (classNames.indexOf(sc) < 0) {
|
106
|
+
classNames.push(sc);
|
107
|
+
}
|
108
|
+
superclass = superclass.superclass;
|
109
|
+
}
|
110
|
+
return classNames;
|
111
|
+
};
|
112
|
+
|
113
|
+
/**
|
114
|
+
Determines the make up of the given array. Returns an SC type constant if
|
115
|
+
all the values in the array are of the same type. If the array is a mixture
|
116
|
+
of type then "anonymous" is returned
|
117
|
+
*/
|
118
|
+
ScExt.typeOfArrayContent = function(array) {
|
119
|
+
|
120
|
+
if (array.length === 0) return "empty";
|
121
|
+
|
122
|
+
var stringCount = 0;
|
123
|
+
var numberCount = 0;
|
124
|
+
var boolCount = 0;
|
125
|
+
var objectCount = 0;
|
126
|
+
var hashCount = 0;
|
127
|
+
var nullCount = 0;
|
128
|
+
var undefinedCount = 0;
|
129
|
+
|
130
|
+
for (var i = 0; i < array.length; i++ ) {
|
131
|
+
var value = array[i];
|
132
|
+
var type = $SC.typeOf(value);
|
133
|
+
if (type === $SC.T_STRING) {
|
134
|
+
stringCount++;
|
135
|
+
} else if (type === $SC.T_BOOL) {
|
136
|
+
boolCount++;
|
137
|
+
} else if (type === $SC.T_NUMBER) {
|
138
|
+
numberCount++;
|
139
|
+
} else if (type === $SC.T_OBJECT) {
|
140
|
+
objectCount++;
|
141
|
+
} else if (type === $SC.T_HASH) {
|
142
|
+
hashCount++;
|
143
|
+
}
|
144
|
+
}
|
145
|
+
|
146
|
+
if (stringCount === array.length) return $SC.T_STRING;
|
147
|
+
if (numberCount === array.length) return $SC.T_NUMBER;
|
148
|
+
if (boolCount === array.length) return $SC.T_BOOL;
|
149
|
+
if (objectCount === array.length) return $SC.T_OBJECT;
|
150
|
+
if (hashCount === array.length) return $SC.T_HASH;
|
151
|
+
return "anonymous";
|
152
|
+
|
153
|
+
};
|
154
|
+
|
155
|
+
/**
|
156
|
+
The path parser is used to parse a given property path and return a value based
|
157
|
+
on that path. If any part of the path evaluates to null then the path will stop
|
158
|
+
being parsed and null will be returned.
|
159
|
+
|
160
|
+
The standard SproutCore obj.getPath('some.path') approach is not used directly
|
161
|
+
since the property paths for this Selenium user extension has some additional
|
162
|
+
characteristics to make it easier to access values.
|
163
|
+
*/
|
164
|
+
ScExt.PathParser = {
|
165
|
+
|
166
|
+
_isArrayIndex: function (val) {
|
167
|
+
return isNaN(parseInt(val, 0)) === false;
|
168
|
+
},
|
169
|
+
|
170
|
+
_isRootObject: function(value) {
|
171
|
+
return value.match(/^\$/) === null ? false : true;
|
172
|
+
},
|
173
|
+
|
174
|
+
_isViewLayerId: function(value) {
|
175
|
+
return value.match(/^#/) === null ? false : true;
|
176
|
+
},
|
177
|
+
|
178
|
+
_getViewByLayerId: function(value) {
|
179
|
+
return $SC.View.views[value.replace('#', '')];
|
180
|
+
},
|
181
|
+
|
182
|
+
_getRootObject: function(value) {
|
183
|
+
var win = selenium.browserbot.getCurrentWindow();
|
184
|
+
return win[value.replace('$', '')];
|
185
|
+
},
|
186
|
+
|
187
|
+
_getObjectFromArray: function(array, index) {
|
188
|
+
var i = parseInt(index, 0);
|
189
|
+
if (array.objectAt) return array.objectAt(i);
|
190
|
+
return array[i];
|
191
|
+
},
|
192
|
+
|
193
|
+
_getObjectFromFunction: function(target, func, index) {
|
194
|
+
return func.call(target, parseInt(index, 0));
|
195
|
+
},
|
196
|
+
|
197
|
+
/**
|
198
|
+
Computes a complete object chain from the given path. The chain
|
199
|
+
represents all the objects used to access the final value from
|
200
|
+
the property path.
|
201
|
+
|
202
|
+
Parts that make up the property paths can include functions, arrays
|
203
|
+
and index values (e.g. 0, 1, 2...). When a function or array is
|
204
|
+
included it must be followed by an index value. The index will then
|
205
|
+
be passed back into the function or array. If a funtion is used then
|
206
|
+
the function must be preceeded by an object.
|
207
|
+
|
208
|
+
Examples:
|
209
|
+
|
210
|
+
1> 'objA.objB.someValue' // Property path not using indexing
|
211
|
+
2> 'objA.someArray.3' // Property path using indexing via an array (i.e. objA.someArray[3])
|
212
|
+
3> 'objA.someFunction.4' // Property path using indexing via a function (i.e. objA.someFunction(4))
|
213
|
+
|
214
|
+
In example 2, the index 3 is supplied which follows the array. In example 3,
|
215
|
+
the index 4 is supplied following a function. The function 'someFunction' will
|
216
|
+
be called against objA. In addition, it is assumed that someFunction only accepts
|
217
|
+
one value.
|
218
|
+
*/
|
219
|
+
computeObjectChain: function(path) {
|
220
|
+
var parts = path.split('.');
|
221
|
+
|
222
|
+
var objPathChain = [];
|
223
|
+
var current_obj = null;
|
224
|
+
|
225
|
+
// Determine what the starting object is
|
226
|
+
if (this._isRootObject(parts[0])) {
|
227
|
+
current_obj = this._getRootObject(parts[0]);
|
228
|
+
} else if (this._isViewLayerId(parts[0])) {
|
229
|
+
current_obj = this._getViewByLayerId(parts[0]);
|
230
|
+
} else {
|
231
|
+
current_obj = $App.getPath(parts[0]);
|
232
|
+
}
|
233
|
+
|
234
|
+
objPathChain.push(current_obj);
|
235
|
+
if ($SC.none(current_obj)) return objPathChain;
|
236
|
+
|
237
|
+
for (var i = 1; i < parts.length; i++) {
|
238
|
+
if (this._isArrayIndex(parts[i])) {
|
239
|
+
if (typeof objPathChain[i - 1] === "function") {
|
240
|
+
// Last part is a function, therefore invoke the function with the index
|
241
|
+
var target = objPathChain[i - 2];
|
242
|
+
var func = objPathChain[i - 1];
|
243
|
+
current_obj = this._getObjectFromFunction(target, func, parts[i]);
|
244
|
+
} else {
|
245
|
+
// Just assume that the previous object in the object chain is an actual array
|
246
|
+
var array = objPathChain[i - 1];
|
247
|
+
current_obj = this._getObjectFromArray(array, parts[i]);
|
248
|
+
}
|
249
|
+
} else {
|
250
|
+
if (current_obj.getPath && typeof current_obj.getPath === "function") {
|
251
|
+
// Object is a SC object. Use the get method
|
252
|
+
current_obj = current_obj.getPath(parts[i]);
|
253
|
+
} else if (current_obj.get && typeof current_obj.get === "function") {
|
254
|
+
current_obj = current_obj.get(parts[i]);
|
255
|
+
} else {
|
256
|
+
// Object is just a plain old JS object
|
257
|
+
current_obj = current_obj[parts[i]];
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
objPathChain.push(current_obj);
|
262
|
+
if ($SC.none(current_obj)) return objPathChain;
|
263
|
+
}
|
264
|
+
|
265
|
+
return objPathChain;
|
266
|
+
},
|
267
|
+
|
268
|
+
getPath: function(path, scClass) {
|
269
|
+
var chain = this.computeObjectChain(path);
|
270
|
+
if (chain.length === 0 ) return null;
|
271
|
+
var pathValue = chain[chain.length - 1];
|
272
|
+
|
273
|
+
if (!scClass) return pathValue;
|
274
|
+
|
275
|
+
if (ScExt.isScObject(pathValue) && pathValue.kindOf(scClass)) {
|
276
|
+
return pathValue;
|
277
|
+
} else {
|
278
|
+
return null;
|
279
|
+
}
|
280
|
+
}
|
281
|
+
};
|
282
|
+
|
283
|
+
/**
|
284
|
+
Convienient global variable to access the path parser since it is used so much. Use
|
285
|
+
like so:
|
286
|
+
|
287
|
+
var value = $ScPath.getPath('some.path.to.a.value');
|
288
|
+
|
289
|
+
*/
|
290
|
+
var $ScPath = ScExt.PathParser;
|
291
|
+
|
292
|
+
/**
|
293
|
+
Used to simulate a mouse event using SproutCore's SC.Event object
|
294
|
+
*/
|
295
|
+
ScExt.MouseEventSimulation = {
|
296
|
+
|
297
|
+
simulateEvent: function(mouseEvent, locator, x, y, button) {
|
298
|
+
var element = selenium.browserbot.findElement(locator);
|
299
|
+
event = $SC.Event.simulateEvent(element, mouseEvent, {
|
300
|
+
screenX: 0,
|
301
|
+
screenY: 0,
|
302
|
+
clientX: $SC.none(x) ? 0 : x,
|
303
|
+
clientY: $SC.none(x) ? 0 : y,
|
304
|
+
pageX: 0,
|
305
|
+
pageY: 0,
|
306
|
+
bubbles: true,
|
307
|
+
button: $SC.none(button) ? 0 : button,
|
308
|
+
altKey: selenium.browserbot.altKeyDown,
|
309
|
+
metaKey: selenium.browserbot.metaKeyDown,
|
310
|
+
ctrlKey: selenium.browserbot.controlKeyDown,
|
311
|
+
shiftKey: selenium.browserbot.shiftKeyDown
|
312
|
+
});
|
313
|
+
$SC.Event.trigger(element, mouseEvent, event);
|
314
|
+
},
|
315
|
+
|
316
|
+
/**
|
317
|
+
Will simulate a mouse down on a function key
|
318
|
+
*/
|
319
|
+
mouseDown: function(locator, x, y) {
|
320
|
+
this.simulateEvent('mousedown', locator, x, y, 0);
|
321
|
+
},
|
322
|
+
|
323
|
+
/**
|
324
|
+
Will simulate a mouse up event
|
325
|
+
*/
|
326
|
+
mouseUp: function(locator, x, y) {
|
327
|
+
this.simulateEvent('mouseup', locator, x, y, 0);
|
328
|
+
},
|
329
|
+
|
330
|
+
/**
|
331
|
+
Will simulate a right mouse down event
|
332
|
+
*/
|
333
|
+
mouseDownRight: function(locator, x, y) {
|
334
|
+
this.simulateEvent('mousedown', locator, x, y, Selenium.RIGHT_MOUSE_CLICK);
|
335
|
+
},
|
336
|
+
|
337
|
+
/**
|
338
|
+
Will simulate a right mouse up event
|
339
|
+
*/
|
340
|
+
mouseUpRight: function(locator, x, y) {
|
341
|
+
this.simulateEvent('mouseup', locator, x, y, Selenium.RIGHT_MOUSE_CLICK);
|
342
|
+
}
|
343
|
+
|
344
|
+
};
|
345
|
+
|
346
|
+
/**
|
347
|
+
Used to simulate a key event using SproutCore's SC.Event object. This is
|
348
|
+
needed since not all browsers all you to create a key event and change the
|
349
|
+
event's property. Meaning that the keyCode and charCode may be stuck with a
|
350
|
+
0 value implying null.
|
351
|
+
*/
|
352
|
+
ScExt.KeyEventSimulation = {
|
353
|
+
|
354
|
+
_functionKeyCode: function(value) {
|
355
|
+
for (var key in $SC.FUNCTION_KEYS) {
|
356
|
+
if ($SC.FUNCTION_KEYS[key] == value) return key;
|
357
|
+
}
|
358
|
+
|
359
|
+
return null;
|
360
|
+
},
|
361
|
+
|
362
|
+
_printableKeyCode: function(value) {
|
363
|
+
for (var key in $SC.PRINTABLE_KEYS) {
|
364
|
+
if ($SC.PRINTABLE_KEYS[key] == value) return key;
|
365
|
+
}
|
366
|
+
|
367
|
+
return null;
|
368
|
+
},
|
369
|
+
|
370
|
+
/**
|
371
|
+
Simulates a key event, such as key up, key down, key pressed. This is used to generate
|
372
|
+
a key event for a SproutCore application since in some browsers it is not possible to
|
373
|
+
dispatch a real key event, like in Apple's Safari. In Safari, when you try to generate
|
374
|
+
a KeyboardEvent, the keyCode and charCode properties are read-only, and their values
|
375
|
+
are set to 0 (null).
|
376
|
+
|
377
|
+
Note that since this only simulates a key event, the web browser will not be informed
|
378
|
+
of the event and as such any type of form input field will also not pick up on the event.
|
379
|
+
Therefore if you want to show a field being updated by the key event, you will also
|
380
|
+
have to update the input field's value seperately.
|
381
|
+
|
382
|
+
@param keyEvent {String} The key event to simulate (e.g. keyup, keydown)
|
383
|
+
@param locator {String} The locator path to the DOM element that is to "receive" the key event
|
384
|
+
@param keyCode {Integer} The key code for the key event. Represents the key on the keyboard
|
385
|
+
@param charCode {Integer} The char code for the key event. Represents the logical character code
|
386
|
+
*/
|
387
|
+
simulateEvent: function(keyEvent, locator, keyCode, charCode) {
|
388
|
+
if (!keyCode && !charCode) return;
|
389
|
+
|
390
|
+
var element = selenium.browserbot.findElement(locator);
|
391
|
+
event = $SC.Event.simulateEvent(element, keyEvent, {
|
392
|
+
which: $SC.none(keyCode) ? charCode : keyCode,
|
393
|
+
charCode: charCode || 0,
|
394
|
+
keyCode: keyCode || 0,
|
395
|
+
altKey: selenium.browserbot.altKeyDown,
|
396
|
+
metaKey: selenium.browserbot.metaKeyDown,
|
397
|
+
ctrlKey: selenium.browserbot.controlKeyDown,
|
398
|
+
shiftKey: selenium.browserbot.shiftKeyDown
|
399
|
+
});
|
400
|
+
|
401
|
+
$SC.Event.trigger(element, keyEvent, event);
|
402
|
+
},
|
403
|
+
|
404
|
+
/**
|
405
|
+
Will simulate a key down on a function key
|
406
|
+
*/
|
407
|
+
functionKeyDown: function(locator, key) {
|
408
|
+
this.simulateEvent('keydown', locator, this._functionKeyCode(key), null);
|
409
|
+
this.simulateEvent('keypress', locator, this._functionKeyCode(key), null);
|
410
|
+
},
|
411
|
+
|
412
|
+
/**
|
413
|
+
Will simulate a key up on a function key
|
414
|
+
*/
|
415
|
+
functionKeyUp: function(locator, key) {
|
416
|
+
this.simulateEvent('keyup', locator, this._functionKeyCode(key), null);
|
417
|
+
},
|
418
|
+
|
419
|
+
/**
|
420
|
+
Will simulate a key down on a printable character
|
421
|
+
*/
|
422
|
+
keyDown: function(locator, key) {
|
423
|
+
this.simulateEvent('keydown', locator, null, key.charCodeAt(0));
|
424
|
+
this.simulateEvent('keypress', locator, null, key.charCodeAt(0));
|
425
|
+
},
|
426
|
+
|
427
|
+
/**
|
428
|
+
Will simulate a key up on a printable character
|
429
|
+
*/
|
430
|
+
keyUp: function(locator, key) {
|
431
|
+
this.simulateEvent('keyup', locator, this._printableKeyCode(key), null);
|
432
|
+
}
|
433
|
+
|
434
|
+
};
|
435
|
+
|
436
|
+
|
437
|
+
/**
|
438
|
+
TODO: This needs to be redone. The scheme used should something like JSON, not a
|
439
|
+
custom scheme. Got too experimental here.
|
440
|
+
|
441
|
+
The object decoder is used to decode encoded hashes and arrays that have been sent to
|
442
|
+
the browser via some Selenium remote control.
|
443
|
+
|
444
|
+
The parts that make up a hash and array can have specified types that indicate how the
|
445
|
+
part should be decoded. Types accepted are the following:
|
446
|
+
|
447
|
+
int --> represent the value as an integer
|
448
|
+
bool --> represent the value as an boolean (true/false)
|
449
|
+
regexp --> represent the value as a regular expression (case sensitive)
|
450
|
+
regexpi --> represent the value as a regular expression (case insensitive)
|
451
|
+
hash --> represent the value as a hash object
|
452
|
+
array --> represent the value as an array object
|
453
|
+
|
454
|
+
If no type is specified then it is assumed the type is a string. all parts that are represented
|
455
|
+
as a string must have the characters ,, :, [, ], and = be represented as the
|
456
|
+
following: [cma], [cln], [lsb], [rsb], [eql].
|
457
|
+
|
458
|
+
Hashes (maps, dictionaries) follow a standard <key>=<value> pattern where <value> follows
|
459
|
+
the pattern [<type>:]<string> (see above). Examples of encoded hashes:
|
460
|
+
|
461
|
+
foo=bar --> { "foo": "bar" }
|
462
|
+
foo.bar=cat --> { "foo.bar": "cat"}
|
463
|
+
foo=cat,bar=dog --> { "foo": "cat", "bar": "dog" }
|
464
|
+
company=Acme[comma] Inc --> { "company": "Acme, Inc" }
|
465
|
+
foo=regexp:abc --> { "foo": /abc/ }
|
466
|
+
foo=regexpi:abc --> { "foo": /abc/i }
|
467
|
+
foo=regexp:a[eql]b --> { "foo": /a=b/ }
|
468
|
+
foo=int:100 --> { "foo": 100 }
|
469
|
+
foo=100 --> { "foo": "100" }
|
470
|
+
foo=bool:true --> { "foo": true }
|
471
|
+
foo=bool:false --> { "foo": false }
|
472
|
+
foo=true --> { "foo": "true" }
|
473
|
+
|
474
|
+
Depending on how the hash is being used, the key can be used as a SproutCore
|
475
|
+
property path. Therefore, the key can be something like "foo.bar.cat".
|
476
|
+
|
477
|
+
*/
|
478
|
+
ScExt.ObjectDecoder = {
|
479
|
+
|
480
|
+
_decodeEncodedChars: function(value) {
|
481
|
+
if (value.match(/\[.*\]/)) {
|
482
|
+
var val = value.replace(/\[cma\]/g, ",");
|
483
|
+
val = val.replace(/\[cln\]/g, ":");
|
484
|
+
val = val.replace(/\[eql\]/g, "=");
|
485
|
+
val = val.replace(/\[lsb\]/g, "[");
|
486
|
+
val = val.replace(/\[rsb\]/g, "]");
|
487
|
+
return val;
|
488
|
+
}
|
489
|
+
|
490
|
+
return value;
|
491
|
+
},
|
492
|
+
|
493
|
+
_decodeValue: function(encodedValue) {
|
494
|
+
var parts = encodedValue.match(/^(.*):(.*)/);
|
495
|
+
if (parts) {
|
496
|
+
var type = parts[1];
|
497
|
+
var value = parts[2];
|
498
|
+
|
499
|
+
if (type === "int") return parseInt(value, 10);
|
500
|
+
if (type === "bool") return value === "true" ? true : false;
|
501
|
+
if (type === "hash") return this.decodeHash(this._decodeEncodedChars(value));
|
502
|
+
if (type === "array") return this.decodeArray(this._decodeEncodedChars(value));
|
503
|
+
if (type === "regexp") return new RegExp(this._decodeEncodedChars(value));
|
504
|
+
if (type === "regexpi") return new RegExp(this._decodeEncodedChars(value), "i");
|
505
|
+
if (type === "null") return null;
|
506
|
+
if (type === "undefined") return undefined;
|
507
|
+
|
508
|
+
// Assume value is just a regular string
|
509
|
+
return this._decodeEncodedChars(value);
|
510
|
+
}
|
511
|
+
|
512
|
+
return this._decodeEncodedChars(encodedValue);
|
513
|
+
},
|
514
|
+
|
515
|
+
/**
|
516
|
+
Decodes an encoded array. Returns a hash object
|
517
|
+
*/
|
518
|
+
decodeArray: function(encodedArray) {
|
519
|
+
var parts = encodedArray.split(',');
|
520
|
+
|
521
|
+
var array = [];
|
522
|
+
for (var i = 0; i < parts.length; i++) {
|
523
|
+
var value = parts[i];
|
524
|
+
array.push(this._decodeValue(value));
|
525
|
+
}
|
526
|
+
|
527
|
+
return array;
|
528
|
+
},
|
529
|
+
|
530
|
+
/**
|
531
|
+
Decodes an encoded hash. Returns a hash object
|
532
|
+
*/
|
533
|
+
decodeHash: function(encodedHash) {
|
534
|
+
var parts = encodedHash.split(',');
|
535
|
+
|
536
|
+
var hash = {};
|
537
|
+
for (var i = 0; i < parts.length; i++) {
|
538
|
+
var part = parts[i];
|
539
|
+
var args = part.split('=');
|
540
|
+
var key = args[0];
|
541
|
+
var value = args[1];
|
542
|
+
hash[key] = this._decodeValue(value);
|
543
|
+
}
|
544
|
+
|
545
|
+
return hash;
|
546
|
+
}
|
547
|
+
|
548
|
+
};
|
549
|
+
|
550
|
+
/**
|
551
|
+
Used to lookup objects in an enumerable type based on a set of filter
|
552
|
+
criteria. Calling the lookup method will return an array of objects that
|
553
|
+
match the given filter criteria. Calling the lookupIndexes will return
|
554
|
+
an array of indexes for the objects that match the given filter criteria.
|
555
|
+
|
556
|
+
A filter is a hash object made up of key-value pairs. The key represent
|
557
|
+
the key/property on the objects to check against. The value is what to
|
558
|
+
match again for the given key. Values can either be strings, numbers,
|
559
|
+
boolean values, or regular expressions. There are special filter criteria
|
560
|
+
keys that can also be used such as sc_guid and sc_type.
|
561
|
+
|
562
|
+
Some examples filters are the following:
|
563
|
+
|
564
|
+
{ name: 'foo', age: 20 }
|
565
|
+
{ company: /inc$/i }
|
566
|
+
{ isEmployeed: true }
|
567
|
+
{ sc_type: 'SC.ButtonView' }
|
568
|
+
{ sc_guid: 'sc8934' }
|
569
|
+
|
570
|
+
A matching object are those objects that meet all of the filer criteria.
|
571
|
+
As an example, let's say we have the following object array:
|
572
|
+
|
573
|
+
[
|
574
|
+
SC.Object.create({ name: 'foo' }),
|
575
|
+
SC.Object.create({ name: 'foobar' }),
|
576
|
+
SC.Object.create({ name: 'bar' })
|
577
|
+
]
|
578
|
+
|
579
|
+
and our filter is { name: /foo/i }, then only the first and second objects
|
580
|
+
in the array match.
|
581
|
+
*/
|
582
|
+
ScExt.ObjectArrayLookup = {
|
583
|
+
|
584
|
+
LOOKUP_KEY_SC_GUID: "sc_guid",
|
585
|
+
LOOKUP_KEY_SC_TYPE: 'sc_type',
|
586
|
+
|
587
|
+
_isMatchingObject: function(object, filter) {
|
588
|
+
|
589
|
+
var klass = null;
|
590
|
+
if (!$SC.none(filter[this.LOOKUP_KEY_SC_TYPE])) {
|
591
|
+
klass = ScExt.string2ScClass(filter[this.LOOKUP_KEY_SC_TYPE]);
|
592
|
+
}
|
593
|
+
|
594
|
+
for (var key in filter) {
|
595
|
+
var value = filter[key];
|
596
|
+
if (key === this.LOOKUP_KEY_SC_GUID) {
|
597
|
+
if (value !== $SC.guidFor(object)) return false;
|
598
|
+
}
|
599
|
+
else if (key === this.LOOKUP_KEY_SC_TYPE) {
|
600
|
+
if (!object.isObject) return false;
|
601
|
+
if (!object.kindOf(klass)) return false;
|
602
|
+
}
|
603
|
+
else {
|
604
|
+
var objValue = object.get ? object.get(key) : object[key];
|
605
|
+
if (objValue === undefined) return false;
|
606
|
+
if (value instanceof RegExp) {
|
607
|
+
if (!(typeof(objValue) === "string")) return false;
|
608
|
+
if (!objValue.match(value)) return false;
|
609
|
+
} else {
|
610
|
+
if (objValue !== value) return false;
|
611
|
+
}
|
612
|
+
}
|
613
|
+
}
|
614
|
+
|
615
|
+
return true;
|
616
|
+
},
|
617
|
+
|
618
|
+
lookupIndexes: function(objects, filter) {
|
619
|
+
if (!objects || !filter) return null;
|
620
|
+
|
621
|
+
if (!(filter instanceof Object)) return null;
|
622
|
+
|
623
|
+
if (!objects.isSCArray) {
|
624
|
+
if (!objects.isEnumerable) return null;
|
625
|
+
objects = objects.toArray();
|
626
|
+
}
|
627
|
+
|
628
|
+
var matches = [];
|
629
|
+
for (var index = 0; index < objects.get('length'); index++) {
|
630
|
+
var obj = objects.objectAt(index);
|
631
|
+
var match = this._isMatchingObject(obj, filter);
|
632
|
+
if (match) {
|
633
|
+
matches.push(index);
|
634
|
+
if (!$SC.none(filter[this.LOOKUP_KEY_SC_GUID])) return matches;
|
635
|
+
}
|
636
|
+
}
|
637
|
+
|
638
|
+
return matches;
|
639
|
+
},
|
640
|
+
|
641
|
+
lookup: function(objects, filter) {
|
642
|
+
var indexes = this.lookupIndexes(objects, filter);
|
643
|
+
var objs = [];
|
644
|
+
for (var i = 0; i < indexes.length; i++) {
|
645
|
+
objs.push(objects.objectAt(indexes[i]));
|
646
|
+
}
|
647
|
+
|
648
|
+
return objs;
|
649
|
+
}
|
650
|
+
|
651
|
+
};
|
652
|
+
|
653
|
+
/**
|
654
|
+
Used specifically to check for special properties assigned to a collection
|
655
|
+
view's content objects via an assigned content delegate.
|
656
|
+
*/
|
657
|
+
ScExt.CollectionView = {
|
658
|
+
|
659
|
+
_validIndex: function(collectionView, index) {
|
660
|
+
var content = collectionView.get('content');
|
661
|
+
return (typeof index === "number" && index >= 0 && index < content.get('length'));
|
662
|
+
},
|
663
|
+
|
664
|
+
getContentGroupIndexes: function(collectionView) {
|
665
|
+
var content = collectionView.get('content');
|
666
|
+
var del = collectionView.get('contentDelegate');
|
667
|
+
var suggestedGroupIndexes = del.contentGroupIndexes(collectionView, content);
|
668
|
+
if (!suggestedGroupIndexes) return [];
|
669
|
+
|
670
|
+
var confirmedGroupindexes = [];
|
671
|
+
|
672
|
+
suggestedGroupIndexes.forEach(function(index) {
|
673
|
+
if (del.contentIndexIsGroup(collectionView, content, index)) {
|
674
|
+
confirmedGroupindexes.push(index);
|
675
|
+
}
|
676
|
+
}, this);
|
677
|
+
|
678
|
+
return confirmedGroupindexes;
|
679
|
+
},
|
680
|
+
|
681
|
+
getContentIsSelected: function(collectionView, index) {
|
682
|
+
if (!this._validIndex(collectionView, index)) return false;
|
683
|
+
var content = collectionView.get('content');
|
684
|
+
var obj = content.objectAt(index);
|
685
|
+
var selection = collectionView.get('selection');
|
686
|
+
if (!selection) return false;
|
687
|
+
var value = selection.containsObject(obj);
|
688
|
+
return value;
|
689
|
+
},
|
690
|
+
|
691
|
+
getContentIsGroup: function(collectionView, index) {
|
692
|
+
if (!this._validIndex(collectionView, index)) return false;
|
693
|
+
var content = collectionView.get('content');
|
694
|
+
var del = collectionView.get('contentDelegate');
|
695
|
+
var suggestedGroupIndexes = del.contentGroupIndexes(collectionView, content);
|
696
|
+
if (!suggestedGroupIndexes.contains(index)) return false;
|
697
|
+
var value = del.contentIndexIsGroup(collectionView, content, index);
|
698
|
+
return value;
|
699
|
+
},
|
700
|
+
|
701
|
+
getContentDisclosureState: function(collectionView, index) {
|
702
|
+
if (!this._validIndex(collectionView, index)) return -1;
|
703
|
+
var content = collectionView.get('content');
|
704
|
+
var del = collectionView.get('contentDelegate');
|
705
|
+
var value = del.contentIndexDisclosureState(collectionView, content, index);
|
706
|
+
return value;
|
707
|
+
},
|
708
|
+
|
709
|
+
getContentOutlineLevel: function(collectionView, index) {
|
710
|
+
if (!this._validIndex(collectionView, index)) return -1;
|
711
|
+
var content = collectionView.get('content');
|
712
|
+
var del = collectionView.get('contentDelegate');
|
713
|
+
var value = del.contentIndexOutlineLevel(collectionView, content, index);
|
714
|
+
return value;
|
715
|
+
}
|
716
|
+
|
717
|
+
};
|
718
|
+
|
719
|
+
///////////////////// Selenium Core API Extensions - Actions for Testing ////////////////////////////////////
|
720
|
+
|
721
|
+
/**
|
722
|
+
Private. This is intended for debugging/testing purposes only
|
723
|
+
*/
|
724
|
+
Selenium.prototype.doScTestObjectArrayLookup = function(params) {
|
725
|
+
var hash = ScExt.ObjectDecoder.decodeHash(params);
|
726
|
+
var key = hash.key;
|
727
|
+
var path = hash.path;
|
728
|
+
var lookupParams = hash.lookup;
|
729
|
+
|
730
|
+
var array = $ScPath.getPath(path);
|
731
|
+
var objs = ScExt.ObjectArrayLookup.lookup(array, lookupParams);
|
732
|
+
if (!window.$__looked_up_array_objects__) window.$__looked_up_array_objects__ = {};
|
733
|
+
window.$__looked_up_array_objects__[key] = {
|
734
|
+
path: path,
|
735
|
+
array: array,
|
736
|
+
lookupParams: lookupParams,
|
737
|
+
objects: objs
|
738
|
+
};
|
739
|
+
};
|
740
|
+
|
741
|
+
/**
|
742
|
+
Private. This is intended for debugging/testing purposes only
|
743
|
+
*/
|
744
|
+
Selenium.prototype.doScTestComputePropertyPath = function(key, path) {
|
745
|
+
var chain = ScExt.PathParser.computeObjectChain(path);
|
746
|
+
var value = ScExt.PathParser.getPath(path);
|
747
|
+
if (!window.$__computed_property_paths__) window.$__computed_property_paths__ = {};
|
748
|
+
window.$__computed_property_paths__[key] = {
|
749
|
+
path: path,
|
750
|
+
objChain: chain,
|
751
|
+
value: value
|
752
|
+
};
|
753
|
+
};
|
754
|
+
|
755
|
+
/**
|
756
|
+
Private. This is intended for debugging/testing purposes only
|
757
|
+
*/
|
758
|
+
Selenium.prototype.doScTestDecodingEncodedHash = function(key, hash) {
|
759
|
+
var obj = ScExt.ObjectDecoder.decodeHash(hash);
|
760
|
+
if (!window.$__decoded_hashes__) window.$__decoded_hashes__ = {};
|
761
|
+
window.$__decoded_hashes__[key] = {
|
762
|
+
encoded: hash,
|
763
|
+
decoded: obj
|
764
|
+
};
|
765
|
+
};
|
766
|
+
|
767
|
+
/**
|
768
|
+
Private. This is intended for debugging/testing purposes only
|
769
|
+
*/
|
770
|
+
Selenium.prototype.doScTestDecodingEncodedArray = function(key, array) {
|
771
|
+
var obj = ScExt.ObjectDecoder.decodeArray(array);
|
772
|
+
if (!window.$__decoded_arrays__) window.$__decoded_arrays__ = {};
|
773
|
+
window.$__decoded_arrays__[key] = {
|
774
|
+
encoded: array,
|
775
|
+
decoded: obj
|
776
|
+
};
|
777
|
+
};
|
778
|
+
|
779
|
+
///////////////////// Selenium Core API Extensions - Action to Setup User Extensions File ////////////////////////////////////
|
780
|
+
|
781
|
+
/**
|
782
|
+
Sets the name of the SproutCore-based application. This is important as
|
783
|
+
it will eventually be used to initalize the $App core global variable. This
|
784
|
+
must be done before calling doInitializeScSeleniumExtension.
|
785
|
+
*/
|
786
|
+
Selenium.prototype.doSetScApplicationName = function(name) {
|
787
|
+
this._sc_applicationName = name;
|
788
|
+
};
|
789
|
+
|
790
|
+
/**
|
791
|
+
Main method to be invoked before you start to test a SproutCore-based
|
792
|
+
application. This method will check to make sure the SproutCore framework
|
793
|
+
and application are indeed loaded and accessible. In addition, it is
|
794
|
+
solely responsible for setting up the core global variables to program
|
795
|
+
again the SC framework and application.
|
796
|
+
*/
|
797
|
+
Selenium.prototype.doOpenScApplication = function(appRootPath, timeoutInSeconds) {
|
798
|
+
|
799
|
+
var bot = selenium.browserbot;
|
800
|
+
var win = bot.getCurrentWindow();
|
801
|
+
var loc = bot.baseUrl + appRootPath;
|
802
|
+
|
803
|
+
if (win.location.href !== loc) win.location.href = loc;
|
804
|
+
|
805
|
+
// First set up closure
|
806
|
+
var appName = this._sc_applicationName;
|
807
|
+
|
808
|
+
var timeout = timeoutInSeconds ? (parseInt(timeoutInSeconds, 10) * 1000) : Selenium.DEFAULT_TIMEOUT;
|
809
|
+
|
810
|
+
// Now run the wait function which will keep checking until
|
811
|
+
// either the SproutCore framework and application are found or
|
812
|
+
// the time out is reached. The function will also set up some
|
813
|
+
// core global variables to make it easier to program againt
|
814
|
+
// the SC framework and application
|
815
|
+
return Selenium.decorateFunctionWithTimeout(function () {
|
816
|
+
|
817
|
+
var win = selenium.browserbot.getCurrentWindow();
|
818
|
+
|
819
|
+
// First check if there is a SproutCore framework
|
820
|
+
if (!win.SC) return false;
|
821
|
+
$SC = win.SC; // Set up the first core global variable
|
822
|
+
|
823
|
+
// Found SC. Now check for the root application object
|
824
|
+
var application = win[appName];
|
825
|
+
if (!application) return false;
|
826
|
+
$App = application; // Set up the second core global variable
|
827
|
+
|
828
|
+
return true;
|
829
|
+
}, timeout, this);
|
830
|
+
};
|
831
|
+
|
832
|
+
///////////////////// Selenium Core API Extensions - Actions ////////////////////////////////////
|
833
|
+
|
834
|
+
/**
|
835
|
+
Move the application window to a given x-y coordinate
|
836
|
+
*/
|
837
|
+
Selenium.prototype.doScWindowMoveTo = function(x, y) {
|
838
|
+
var win = selenium.browserbot.getCurrentWindow();
|
839
|
+
win.moveTo(x*1, y*1);
|
840
|
+
};
|
841
|
+
|
842
|
+
/**
|
843
|
+
Resize the application window by a width and height
|
844
|
+
*/
|
845
|
+
Selenium.prototype.doScWindowResizeTo = function(width, height) {
|
846
|
+
var win = selenium.browserbot.getCurrentWindow();
|
847
|
+
win.resizeTo(width*1, height*1);
|
848
|
+
};
|
849
|
+
|
850
|
+
/**
|
851
|
+
Maximizes the applicaiton window
|
852
|
+
*/
|
853
|
+
Selenium.prototype.doScWindowMaximize = function() {
|
854
|
+
var win = selenium.browserbot.getCurrentWindow();
|
855
|
+
win.resizeTo(window.screen.availWidth, window.screen.availHeight);
|
856
|
+
win.moveTo(1,1); // Slight offset from origin so that Firefox will actually move the window
|
857
|
+
};
|
858
|
+
|
859
|
+
/**
|
860
|
+
Action to call a view's scrollToVisible method. The path must point
|
861
|
+
to an actual SC view object.
|
862
|
+
*/
|
863
|
+
Selenium.prototype.doScViewScrollToVisible = function(path) {
|
864
|
+
var view = $ScPath.getPath(path, $SC.View);
|
865
|
+
if (!view) return;
|
866
|
+
ScExt.viewScrollToVisible(view);
|
867
|
+
};
|
868
|
+
|
869
|
+
/**
|
870
|
+
Action to raise a mouse down event
|
871
|
+
*/
|
872
|
+
Selenium.prototype.doScMouseDown = function(locator) {
|
873
|
+
try {
|
874
|
+
this.doMouseDown(locator);
|
875
|
+
} catch (ex) {}
|
876
|
+
};
|
877
|
+
|
878
|
+
/**
|
879
|
+
Action to raise a mouse up event
|
880
|
+
*/
|
881
|
+
Selenium.prototype.doScMouseUp = function(locator) {
|
882
|
+
try {
|
883
|
+
this.doMouseUp(locator);
|
884
|
+
} catch (ex) {}
|
885
|
+
};
|
886
|
+
|
887
|
+
/**
|
888
|
+
Action to raise a right mouse down event
|
889
|
+
*/
|
890
|
+
Selenium.prototype.doScMouseDownRight = function(locator) {
|
891
|
+
try {
|
892
|
+
this.doMouseDownRight(locator);
|
893
|
+
} catch (ex) {}
|
894
|
+
};
|
895
|
+
|
896
|
+
/**
|
897
|
+
Action to raise a right mouse up event
|
898
|
+
*/
|
899
|
+
Selenium.prototype.doScMouseUpRight = function(locator) {
|
900
|
+
try {
|
901
|
+
this.doMouseUpRight(locator);
|
902
|
+
} catch (ex) {}
|
903
|
+
};
|
904
|
+
|
905
|
+
/**
|
906
|
+
Action performs a single click that is recognized by the SproutCore framework.
|
907
|
+
*/
|
908
|
+
Selenium.prototype.doScClick = function(locator) {
|
909
|
+
this.doScMouseDown(locator);
|
910
|
+
this.doScMouseUp(locator);
|
911
|
+
};
|
912
|
+
|
913
|
+
/**
|
914
|
+
Action performs a single right click that is recognized by the SproutCore framework.
|
915
|
+
*/
|
916
|
+
Selenium.prototype.doScRightClick = function(locator) {
|
917
|
+
this.doScMouseDownRight(locator);
|
918
|
+
this.doScMouseUpRight(locator);
|
919
|
+
};
|
920
|
+
|
921
|
+
/**
|
922
|
+
Action performs a double click that is recognized by the SproutCore framework.
|
923
|
+
*/
|
924
|
+
Selenium.prototype.doScDoubleClick = function(locator) {
|
925
|
+
this.doScClick(locator);
|
926
|
+
this.doScClick(locator);
|
927
|
+
};
|
928
|
+
|
929
|
+
/** @private
|
930
|
+
Check that the element is a valid text entry field
|
931
|
+
*/
|
932
|
+
Selenium.prototype._validTextEntryField = function(element) {
|
933
|
+
var isTextInputField = element.tagName.toLowerCase() === 'input' && element.type.toLowerCase() === "text";
|
934
|
+
var isTextArea = element.tagName.toLowerCase() === 'textarea';
|
935
|
+
return isTextInputField || isTextArea;
|
936
|
+
};
|
937
|
+
|
938
|
+
/**
|
939
|
+
Action performs a key down on a printable character
|
940
|
+
*/
|
941
|
+
Selenium.prototype.doScKeyDown = function(locator, key) {
|
942
|
+
ScExt.KeyEventSimulation.keyDown(locator, key);
|
943
|
+
|
944
|
+
var element = this.browserbot.findElement(locator);
|
945
|
+
|
946
|
+
if (this._validTextEntryField(element)) {
|
947
|
+
var value = element.value;
|
948
|
+
if (this.browserbot.shiftKeyDown) key = key.toUpperCase();
|
949
|
+
value = value + key;
|
950
|
+
element.value = value;
|
951
|
+
$SC.Event.trigger(element, 'change');
|
952
|
+
}
|
953
|
+
};
|
954
|
+
|
955
|
+
/**
|
956
|
+
Action performs a key up on a printable character
|
957
|
+
*/
|
958
|
+
Selenium.prototype.doScKeyUp = function(locator, key) {
|
959
|
+
ScExt.KeyEventSimulation.keyUp(locator, key);
|
960
|
+
};
|
961
|
+
|
962
|
+
/**
|
963
|
+
Action performs a key down on a function key
|
964
|
+
*/
|
965
|
+
Selenium.prototype.doScFunctionKeyDown = function(locator, key) {
|
966
|
+
ScExt.KeyEventSimulation.functionKeyDown(locator, key);
|
967
|
+
|
968
|
+
var element = this.browserbot.findElement(locator);
|
969
|
+
|
970
|
+
if (this._validTextEntryField(element)) {
|
971
|
+
// Need to simulate special function keys
|
972
|
+
var value = element.value;
|
973
|
+
if (key === 'backspace' || key === 'delete') {
|
974
|
+
if (value.length > 0) {
|
975
|
+
element.value = value.slice(0, value.length - 1);
|
976
|
+
$SC.Event.trigger(element, 'change');
|
977
|
+
}
|
978
|
+
}
|
979
|
+
else if (key === 'insert' || key === 'return') {
|
980
|
+
value = value + '\n';
|
981
|
+
element.value = value;
|
982
|
+
$SC.Event.trigger(element, 'change');
|
983
|
+
}
|
984
|
+
}
|
985
|
+
};
|
986
|
+
|
987
|
+
/**
|
988
|
+
Action performs a key up on a function key
|
989
|
+
*/
|
990
|
+
Selenium.prototype.doScFunctionKeyUp = function(locator, key) {
|
991
|
+
ScExt.KeyEventSimulation.functionKeyUp(locator, key);
|
992
|
+
};
|
993
|
+
|
994
|
+
/**
|
995
|
+
Action performs a typing of an individual function key. A key down followed by
|
996
|
+
a key up event.
|
997
|
+
*/
|
998
|
+
Selenium.prototype.doScTypeFunctionKey = function(locator, key) {
|
999
|
+
this.doScFunctionKeyDown(locator, key);
|
1000
|
+
this.doScFunctionKeyUp(locator, key);
|
1001
|
+
};
|
1002
|
+
|
1003
|
+
/**
|
1004
|
+
Action performs a typing of an individual printable character. A key down followed by
|
1005
|
+
a key up event.
|
1006
|
+
*/
|
1007
|
+
Selenium.prototype.doScTypeKey = function(locator, key) {
|
1008
|
+
this.doScKeyDown(locator, key);
|
1009
|
+
this.doScKeyUp(locator, key);
|
1010
|
+
};
|
1011
|
+
|
1012
|
+
/**
|
1013
|
+
Used to perform clean up on a core query object when no longer used
|
1014
|
+
|
1015
|
+
@see #getScCoreQuery
|
1016
|
+
*/
|
1017
|
+
Selenium.prototype.doScCoreQueryDone = function(handle) {
|
1018
|
+
this._unregisterCoreQueryObject(handle);
|
1019
|
+
};
|
1020
|
+
|
1021
|
+
///////////////////// Selenium Core API Extensions - Accessors ////////////////////////////////////
|
1022
|
+
|
1023
|
+
/**
|
1024
|
+
Returns the SproutCore type for the given path. If the path does not
|
1025
|
+
point to a SproutCore object the null is returned.
|
1026
|
+
*/
|
1027
|
+
Selenium.prototype.getScTypeOf = function(path) {
|
1028
|
+
var value = $ScPath.getPath(path);
|
1029
|
+
return $SC.typeOf(value);
|
1030
|
+
};
|
1031
|
+
|
1032
|
+
/**
|
1033
|
+
Returns the basic SproutCore type for the content that make up an
|
1034
|
+
array. If the path does not point to an array then an empty string
|
1035
|
+
is returned.
|
1036
|
+
*/
|
1037
|
+
Selenium.prototype.getScTypeOfArrayContent = function(path) {
|
1038
|
+
var array = $ScPath.getPath(path);
|
1039
|
+
if ($SC.typeOf(array) !== $SC.T_ARRAY) return "";
|
1040
|
+
|
1041
|
+
return ScExt.typeOfArrayContent(array);
|
1042
|
+
};
|
1043
|
+
|
1044
|
+
/**
|
1045
|
+
Returns a SproutCore object's GUID
|
1046
|
+
*/
|
1047
|
+
Selenium.prototype.getScGuid = function(path) {
|
1048
|
+
var value = $ScPath.getPath(path, $SC.Object);
|
1049
|
+
var guid = $SC.guidFor(value);
|
1050
|
+
return guid;
|
1051
|
+
};
|
1052
|
+
|
1053
|
+
/**
|
1054
|
+
Returns a SproutCore object's type
|
1055
|
+
*/
|
1056
|
+
Selenium.prototype.getScObjectClassName = function(path) {
|
1057
|
+
var obj = $ScPath.getPath(path, $SC.Object);
|
1058
|
+
if (!obj) return "";
|
1059
|
+
var className = $SC._object_className(obj.constructor);
|
1060
|
+
return (className === 'Anonymous') ? "" : className;
|
1061
|
+
};
|
1062
|
+
|
1063
|
+
/**
|
1064
|
+
Checks if a SproutCore object is a kind of type
|
1065
|
+
*/
|
1066
|
+
Selenium.prototype.isScObjectKindOfClass = function(path, className) {
|
1067
|
+
var obj = $ScPath.getPath(path, $SC.Object);
|
1068
|
+
if (!obj) return false;
|
1069
|
+
|
1070
|
+
var klass = ScExt.string2ScClass(className);
|
1071
|
+
if (!klass) return false;
|
1072
|
+
|
1073
|
+
return obj.kindOf(klass);
|
1074
|
+
};
|
1075
|
+
|
1076
|
+
/**
|
1077
|
+
Returns an array of strings representing all the classes a SproutCore
|
1078
|
+
object derives from.
|
1079
|
+
*/
|
1080
|
+
Selenium.prototype.getScObjectClassNames = function(path) {
|
1081
|
+
var obj = $ScPath.getPath(path, $SC.Object);
|
1082
|
+
var classNames = ScExt.getScObjectClassNames(obj);
|
1083
|
+
return classNames;
|
1084
|
+
};
|
1085
|
+
|
1086
|
+
/**
|
1087
|
+
Accessor gets a value from a given property path
|
1088
|
+
*/
|
1089
|
+
Selenium.prototype.getScPropertyValue = function(path) {
|
1090
|
+
var value = $ScPath.getPath(path);
|
1091
|
+
return value;
|
1092
|
+
};
|
1093
|
+
|
1094
|
+
/**
|
1095
|
+
Accessor gets a localized string from the given string provided. This is done
|
1096
|
+
through the SproutCore framework.
|
1097
|
+
*/
|
1098
|
+
Selenium.prototype.getScLocalizedString = function(str) {
|
1099
|
+
var win = this.browserbot.getCurrentWindow();
|
1100
|
+
return win.eval("'" + str + "'.loc()");
|
1101
|
+
};
|
1102
|
+
|
1103
|
+
/**
|
1104
|
+
Gets the layer of a view. In order to transfer data back to server, the
|
1105
|
+
method actually returns the outer HTML of the layer instead of having translate
|
1106
|
+
DOM elements into a string.
|
1107
|
+
*/
|
1108
|
+
Selenium.prototype.getScViewLayer = function(path) {
|
1109
|
+
var view = $ScPath.getPath(path, $SC.View);
|
1110
|
+
if (!view) return "";
|
1111
|
+
return view.get('layer').outerHTML;
|
1112
|
+
};
|
1113
|
+
|
1114
|
+
/**
|
1115
|
+
Gets a SproutCore view's frame
|
1116
|
+
*/
|
1117
|
+
Selenium.prototype.getScViewFrame = function(path) {
|
1118
|
+
var view = $ScPath.getPath(path, $SC.View);
|
1119
|
+
if (!view) return "";
|
1120
|
+
var frame = view.get('frame');
|
1121
|
+
if (!frame) return null;
|
1122
|
+
return [frame.width, frame.height, frame.x, frame.y];
|
1123
|
+
};
|
1124
|
+
|
1125
|
+
/**
|
1126
|
+
Gets a DOM element's current window position
|
1127
|
+
*/
|
1128
|
+
Selenium.prototype.getScElementWindowPosition = function(path) {
|
1129
|
+
var x = this.getElementPositionLeft(path);
|
1130
|
+
var y = this.getElementPositionTop(path);
|
1131
|
+
return [x, y];
|
1132
|
+
};
|
1133
|
+
|
1134
|
+
/**
|
1135
|
+
Gets the indexes of objects in an array that match a given lookup filter
|
1136
|
+
*/
|
1137
|
+
Selenium.prototype.getScObjectArrayIndexLookup = function(path, lookupParams) {
|
1138
|
+
var params = ScExt.ObjectDecoder.decodeHash(lookupParams);
|
1139
|
+
var array = $ScPath.getPath(path);
|
1140
|
+
var indexes = ScExt.ObjectArrayLookup.lookupIndexes(array, params);
|
1141
|
+
return indexes;
|
1142
|
+
};
|
1143
|
+
|
1144
|
+
/////// SC Core Query Specific Selenium Calls /////////////////
|
1145
|
+
|
1146
|
+
/**
|
1147
|
+
Accessor is used to create a core query object based on a given CSS selector. The core query
|
1148
|
+
object is obtained through a view using a SC property path. Once the core query object is
|
1149
|
+
created an numeric handle ID is returned so that you can use subsequent commands to use
|
1150
|
+
the object.
|
1151
|
+
|
1152
|
+
Always call this method first before using any other related methods since they all require
|
1153
|
+
a handler to a core query object.
|
1154
|
+
|
1155
|
+
When finished with the core query, call doScCoreQueryDone to release the handle
|
1156
|
+
|
1157
|
+
@return a positive handle ID if the path provided points to a view object,
|
1158
|
+
otherwise -1 is returned
|
1159
|
+
*/
|
1160
|
+
Selenium.prototype.getScCoreQuery = function(path, selector) {
|
1161
|
+
var view = $ScPath.getPath(path, $SC.View);
|
1162
|
+
if (!view) return -1;
|
1163
|
+
|
1164
|
+
var cq = null;
|
1165
|
+
cq = (!selector || selector === "") ? view.$() : view.$(selector);
|
1166
|
+
|
1167
|
+
var handle = this._registerCoreQueryObject(cq);
|
1168
|
+
|
1169
|
+
return handle;
|
1170
|
+
};
|
1171
|
+
|
1172
|
+
/**
|
1173
|
+
Will register a SC core query object. Once registered a handler will be
|
1174
|
+
returned in order to access the core object for subsequent use.
|
1175
|
+
|
1176
|
+
@return a numeric handler that is used to access the core query object
|
1177
|
+
|
1178
|
+
@see #_getCoreQueryObject
|
1179
|
+
*/
|
1180
|
+
Selenium.prototype._registerCoreQueryObject = function(cq) {
|
1181
|
+
if (!this._registeredCoreQueries) this._registeredCoreQueries = {};
|
1182
|
+
if (!this._nextCoreQueryHandle) this._nextCoreQueryHandle = 0;
|
1183
|
+
|
1184
|
+
// Using ++ operator has a bizarre quirk in JS
|
1185
|
+
this._nextCoreQueryHandle = this._nextCoreQueryHandle + 1;
|
1186
|
+
|
1187
|
+
this._registeredCoreQueries["handle_" + this._nextCoreQueryHandle] = cq;
|
1188
|
+
|
1189
|
+
return this._nextCoreQueryHandle;
|
1190
|
+
};
|
1191
|
+
|
1192
|
+
/**
|
1193
|
+
Will unregister a registered core query object. This will help free it from
|
1194
|
+
memory.
|
1195
|
+
|
1196
|
+
@see #_registerCoreQueryObject
|
1197
|
+
*/
|
1198
|
+
Selenium.prototype._unregisterCoreQueryObject = function(handle) {
|
1199
|
+
if (!this._registeredCoreQueries) return;
|
1200
|
+
delete this._registeredCoreQueries["handle_" + handle];
|
1201
|
+
};
|
1202
|
+
|
1203
|
+
/**
|
1204
|
+
Used to retrieved a registered core query object using a handler. If
|
1205
|
+
the handler does not point to a core query object then null is returned.
|
1206
|
+
|
1207
|
+
@see #_registerCoreQueryObject
|
1208
|
+
*/
|
1209
|
+
Selenium.prototype._getCoreQueryObject = function(handle) {
|
1210
|
+
if (!this._registeredCoreQueries) return null;
|
1211
|
+
return this._registeredCoreQueries["handle_" + handle];
|
1212
|
+
};
|
1213
|
+
|
1214
|
+
/**
|
1215
|
+
Will get the number of elements contained in the core query object based on
|
1216
|
+
the selector used.
|
1217
|
+
|
1218
|
+
@param handle {Number} the handle to the core query object
|
1219
|
+
@see #getScCoreQuery
|
1220
|
+
*/
|
1221
|
+
Selenium.prototype.getScCoreQuerySize = function(handle) {
|
1222
|
+
var cq = this._getCoreQueryObject(handle);
|
1223
|
+
return cq.size();
|
1224
|
+
};
|
1225
|
+
|
1226
|
+
/**
|
1227
|
+
Will get an element's classes. The element comes from a core query object.
|
1228
|
+
|
1229
|
+
@param handle {Number} the handle to the core query object
|
1230
|
+
@param elemIndex {Number} index to the element in the core query object
|
1231
|
+
@see #getScCoreQuery
|
1232
|
+
*/
|
1233
|
+
Selenium.prototype.getScCoreQueryElementClasses = function(handle, elemIndex) {
|
1234
|
+
var cq = this._getCoreQueryObject(handle);
|
1235
|
+
var elem = cq.get(elemIndex);
|
1236
|
+
if (!elem) return "";
|
1237
|
+
return elem.className;
|
1238
|
+
};
|
1239
|
+
|
1240
|
+
/**
|
1241
|
+
Will get an element's outer HTML. The element comes from a core query object.
|
1242
|
+
|
1243
|
+
@param handle {Number} the handle to the core query object
|
1244
|
+
@param elemIndex {Number} index to the element in the core query object
|
1245
|
+
@see #getScCoreQuery
|
1246
|
+
*/
|
1247
|
+
Selenium.prototype.getScCoreQueryElementHTML = function(handle, elemIndex) {
|
1248
|
+
var cq = this._getCoreQueryObject(handle);
|
1249
|
+
var elem = cq.get(elemIndex);
|
1250
|
+
if (!elem) return "";
|
1251
|
+
return elem.outerHTML;
|
1252
|
+
};
|
1253
|
+
|
1254
|
+
/**
|
1255
|
+
Will get an element's attribute. The element comes from a core query object. The
|
1256
|
+
attribute can be any attribute you find on a DOM element.
|
1257
|
+
|
1258
|
+
@param handle {Number} the handle to the core query object
|
1259
|
+
@param elemIndexPlusAttr {String} must follow the patter <elem index>:<attribute>, where
|
1260
|
+
<elem index> is the index to the DOM element in the core query object and
|
1261
|
+
<attribute> is the attribute you want to get from the element. Because of the way
|
1262
|
+
the Selenium Core works, you are only allowed to provide a command two argments,
|
1263
|
+
which is very silly. So the second and third argument had to be combined into one.
|
1264
|
+
Hence the need to follow a pattern. For more details see the following function:
|
1265
|
+
|
1266
|
+
_createCommandFromRequest
|
1267
|
+
|
1268
|
+
located in the selenium-remoterunner.js file
|
1269
|
+
|
1270
|
+
@see #getScCoreQuery
|
1271
|
+
*/
|
1272
|
+
Selenium.prototype.getScCoreQueryElementAttribute = function(handle, elemIndexPlusAttr) {
|
1273
|
+
var arg2 = elemIndexPlusAttr.split(':');
|
1274
|
+
var elemIndex = arg2[0];
|
1275
|
+
var attr = arg2[1];
|
1276
|
+
|
1277
|
+
var cq = this._getCoreQueryObject(handle);
|
1278
|
+
var elem = cq.get(elemIndex);
|
1279
|
+
if (!elem) return "";
|
1280
|
+
return elem.getAttribute(attr);
|
1281
|
+
};
|
1282
|
+
|
1283
|
+
/**
|
1284
|
+
Will get an element's text. The element comes from a core query object.
|
1285
|
+
|
1286
|
+
@param handle {Number} the handle to the core query object
|
1287
|
+
@param elemIndex {Number} index to the element in the core query object
|
1288
|
+
@see #getScCoreQuery
|
1289
|
+
*/
|
1290
|
+
Selenium.prototype.getScCoreQueryElementText = function(handle, elemIndex) {
|
1291
|
+
var cq = this._getCoreQueryObject(handle);
|
1292
|
+
var elem = cq.get(elemIndex);
|
1293
|
+
if (!elem) return "";
|
1294
|
+
if (elem.textContent) return elem.textContent;
|
1295
|
+
if (elem.text) return elem.text;
|
1296
|
+
return "";
|
1297
|
+
};
|
1298
|
+
|
1299
|
+
/**
|
1300
|
+
Will get an element's HTML tag. The element comes from a core query object.
|
1301
|
+
|
1302
|
+
@param handle {Number} the handle to the core query object
|
1303
|
+
@param elemIndex {Number} index to the element in the core query object
|
1304
|
+
@see #getScCoreQuery
|
1305
|
+
*/
|
1306
|
+
Selenium.prototype.getScCoreQueryElementTag = function(handle, elemIndex) {
|
1307
|
+
var cq = this._getCoreQueryObject(handle);
|
1308
|
+
var elem = cq.get(elemIndex);
|
1309
|
+
if (!elem) return "";
|
1310
|
+
return elem.tagName;
|
1311
|
+
};
|
1312
|
+
|
1313
|
+
/////// SC Collection View Specific Selenium Calls /////////////////
|
1314
|
+
|
1315
|
+
Selenium.prototype.getScCollectionViewContentGroupIndexes = function(path) {
|
1316
|
+
var collectionView = $ScPath.getPath(path, $SC.CollectionView);
|
1317
|
+
if (!collectionView) return [];
|
1318
|
+
return ScExt.CollectionView.getContentGroupIndexes(collectionView);
|
1319
|
+
};
|
1320
|
+
|
1321
|
+
Selenium.prototype.getScCollectionViewContentSelectedIndexes = function(path) {
|
1322
|
+
var collectionView = $ScPath.getPath(path, $SC.CollectionView);
|
1323
|
+
if (!collectionView) return [];
|
1324
|
+
var selectionSet = collectionView.get('selection');
|
1325
|
+
if (!selectionSet) return [];
|
1326
|
+
var content = collectionView.get('content');
|
1327
|
+
return ScExt.indexSet2Array(selectionSet.indexSetForSource(content));
|
1328
|
+
};
|
1329
|
+
|
1330
|
+
Selenium.prototype.getScCollectionViewContentNowShowingIndexes = function(path) {
|
1331
|
+
var collectionView = $ScPath.getPath(path, $SC.CollectionView);
|
1332
|
+
if (!collectionView) return [];
|
1333
|
+
return ScExt.indexSet2Array(collectionView.get('nowShowing'));
|
1334
|
+
};
|
1335
|
+
|
1336
|
+
Selenium.prototype.getScCollectionViewContentIsGroup = function(path, index) {
|
1337
|
+
var collectionView = $ScPath.getPath(path, $SC.CollectionView);
|
1338
|
+
if (!collectionView) -1;
|
1339
|
+
return ScExt.CollectionView.getContentIsGroup(collectionView, parseInt(index, 0));
|
1340
|
+
};
|
1341
|
+
|
1342
|
+
Selenium.prototype.getScCollectionViewContentIsSelected = function(path, index) {
|
1343
|
+
var collectionView = $ScPath.getPath(path, $SC.CollectionView);
|
1344
|
+
if (!collectionView) -1;
|
1345
|
+
return ScExt.CollectionView.getContentIsSelected(collectionView, parseInt(index, 0));
|
1346
|
+
};
|
1347
|
+
|
1348
|
+
Selenium.prototype.getScCollectionViewContentDisclosureState = function(path, index) {
|
1349
|
+
var collectionView = $ScPath.getPath(path, $SC.CollectionView);
|
1350
|
+
if (!collectionView) -1;
|
1351
|
+
return ScExt.CollectionView.getContentDisclosureState(collectionView, parseInt(index, 0));
|
1352
|
+
};
|
1353
|
+
|
1354
|
+
Selenium.prototype.getScCollectionViewContentOutlineLevel = function(path, index) {
|
1355
|
+
var collectionView = $ScPath.getPath(path, $SC.CollectionView);
|
1356
|
+
if (!collectionView) -1;
|
1357
|
+
return ScExt.CollectionView.getContentOutlineLevel(collectionView, parseInt(index, 0));
|
1358
|
+
};
|
1359
|
+
|
1360
|
+
///////////////////// Selenium Core API Extensions - Locators ////////////////////////////////////
|
1361
|
+
|
1362
|
+
/**
|
1363
|
+
This locator is used access a SC view's root DOM element. ONLY use this locator for actions
|
1364
|
+
that expect a DOM element.
|
1365
|
+
|
1366
|
+
A SC path follows the standard property dot-path notation, such as the following:
|
1367
|
+
|
1368
|
+
MyApp.mainPage.mainPane.someButton
|
1369
|
+
|
1370
|
+
For all paths supplied they must be relative to the root application object.
|
1371
|
+
|
1372
|
+
To use this locator via the Selenium RC, the locator text must follow the given
|
1373
|
+
pattern:
|
1374
|
+
|
1375
|
+
scPath=<your.sc.path.to.a.view.goes.here>
|
1376
|
+
|
1377
|
+
As an example:
|
1378
|
+
|
1379
|
+
clientDriver.click('scPath=mainPage.mainPane.someButton')
|
1380
|
+
|
1381
|
+
If the root application object is called MyApp, then the view will be accessed
|
1382
|
+
like so:
|
1383
|
+
|
1384
|
+
view = MyApp.getPath('mainPage.mainPane.someButton)
|
1385
|
+
|
1386
|
+
*/
|
1387
|
+
PageBot.prototype.locateElementByScPath = function(path) {
|
1388
|
+
|
1389
|
+
var obj = $ScPath.getPath(path, $SC.View);
|
1390
|
+
if (!obj) return null;
|
1391
|
+
|
1392
|
+
// Return the view's layer. The layer is the root DOM element of the view
|
1393
|
+
return obj.get('layer');
|
1394
|
+
|
1395
|
+
};
|
1396
|
+
|
1397
|
+
/**
|
1398
|
+
This locator is used to access a DOM element the belongs to a core query object. ONLY use
|
1399
|
+
this locator for actions that expect a DOM element.
|
1400
|
+
|
1401
|
+
To use this locator via the Selenium RC, the locator text must follow the given pattern:
|
1402
|
+
|
1403
|
+
scCoreQuery=<core query handle>:<element index>
|
1404
|
+
|
1405
|
+
where <core query handle> is the handle to the core query object and <element index> is
|
1406
|
+
the index to the element in the CQ object. As an example:
|
1407
|
+
|
1408
|
+
clientDriver.click('scCoreQuery=2:1')
|
1409
|
+
*/
|
1410
|
+
PageBot.prototype.locateElementByScCoreQuery = function(text) {
|
1411
|
+
|
1412
|
+
var args = text.split(':');
|
1413
|
+
var handle = args[0];
|
1414
|
+
var index = args[1];
|
1415
|
+
|
1416
|
+
var cq = selenium._getCoreQueryObject(handle);
|
1417
|
+
var elem = cq.get(index);
|
1418
|
+
|
1419
|
+
return elem;
|
1420
|
+
|
1421
|
+
};
|