lebowski 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/History.md +17 -1
  2. data/Manifest.txt +8 -0
  3. data/README.md +6 -0
  4. data/lib/lebowski/foundation/application.rb +340 -74
  5. data/lib/lebowski/foundation/core.rb +23 -0
  6. data/lib/lebowski/foundation/dom_element.rb +15 -0
  7. data/lib/lebowski/foundation/errors/timeout.rb +5 -0
  8. data/lib/lebowski/foundation/mixins/collection_item_view_support.rb +5 -0
  9. data/lib/lebowski/foundation/mixins/define_paths_support.rb +318 -0
  10. data/lib/lebowski/foundation/mixins/delegate_support.rb +5 -0
  11. data/lib/lebowski/foundation/mixins/frame_application_context_support.rb +37 -0
  12. data/lib/lebowski/foundation/mixins/inline_text_field_support.rb +5 -0
  13. data/lib/lebowski/foundation/mixins/key_check.rb +5 -0
  14. data/lib/lebowski/foundation/mixins/list_item_view_support.rb +5 -0
  15. data/lib/lebowski/foundation/mixins/positioned_element.rb +24 -0
  16. data/lib/lebowski/foundation/mixins/stall_support.rb +5 -0
  17. data/lib/lebowski/foundation/mixins/user_actions.rb +37 -8
  18. data/lib/lebowski/foundation/mixins/wait_actions.rb +5 -0
  19. data/lib/lebowski/foundation/object_array.rb +96 -1
  20. data/lib/lebowski/foundation/proxy_factory.rb +2 -0
  21. data/lib/lebowski/foundation/proxy_object.rb +128 -130
  22. data/lib/lebowski/foundation/views/select_button.rb +69 -0
  23. data/lib/lebowski/foundation/views/view.rb +10 -0
  24. data/lib/lebowski/foundation/views/web.rb +26 -0
  25. data/lib/lebowski/foundation.rb +10 -0
  26. data/lib/lebowski/runtime/errors/remote_control_command_execution_error.rb +5 -0
  27. data/lib/lebowski/runtime/errors/remote_control_command_timeout_error.rb +5 -0
  28. data/lib/lebowski/runtime/errors/remote_control_error.rb +5 -0
  29. data/lib/lebowski/runtime/errors/selenium_server_error.rb +5 -0
  30. data/lib/lebowski/runtime/object_encoder.rb +5 -0
  31. data/lib/lebowski/runtime/sprout_core_driver.rb +5 -0
  32. data/lib/lebowski/runtime/sprout_core_extensions.rb +114 -0
  33. data/lib/lebowski/runtime.rb +5 -0
  34. data/lib/lebowski/scui/mixins/link_support.rb +32 -0
  35. data/lib/lebowski/scui/mixins/node_item_view_support.rb +50 -13
  36. data/lib/lebowski/scui/mixins/terminal_view_support.rb +30 -0
  37. data/lib/lebowski/scui/views/color_well.rb +48 -0
  38. data/lib/lebowski/scui/views/combo_box.rb +90 -87
  39. data/lib/lebowski/scui/views/content_editable.rb +422 -0
  40. data/lib/lebowski/scui/views/date_picker.rb +70 -58
  41. data/lib/lebowski/scui/views/linkit.rb +8 -0
  42. data/lib/lebowski/scui/views/select_field_tab.rb +30 -0
  43. data/lib/lebowski/scui.rb +18 -0
  44. data/lib/lebowski/version.rb +2 -2
  45. data/resources/user-extensions.js +293 -11
  46. metadata +69 -25
@@ -0,0 +1,422 @@
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 SCUI
8
+ module Views
9
+
10
+ #
11
+ # Represents a proxy to a SCUI content editable view (SCUI.ContentEditableView)
12
+ #
13
+ class ContentEditableView < Lebowski::Foundation::Views::WebView
14
+
15
+ representing_sc_class 'SCUI.ContentEditableView'
16
+
17
+ def empty_selection?()
18
+ value = self['selection']
19
+ return (value == '' or value.nil?)
20
+ end
21
+
22
+ def image_selected?()
23
+ return (not self['selectedImage'].nil?)
24
+ end
25
+
26
+ def hyperlink_selected?()
27
+ return (not self['selectedHyperlink'].nil?)
28
+ end
29
+
30
+ def find_elements(selector)
31
+ return ContentEditableViewSupport::DOMElementList.new self, selector
32
+ end
33
+
34
+ def find_element(selector)
35
+ elems = ContentEditableViewSupport::DOMElementList.new self, selector
36
+ return nil if elems.empty?
37
+ return elems[0]
38
+ end
39
+
40
+ def create_range(*params)
41
+ return ContentEditableViewSupport::Range.new frame, *params
42
+ end
43
+
44
+ def select_all()
45
+ body = find_element('body')
46
+ body.select_content
47
+ end
48
+
49
+ def select_none()
50
+ set_cursor_to_end
51
+ end
52
+
53
+ def set_cursor_to_start()
54
+ body = find_element('body')
55
+ range = create_range
56
+ range.set_start body, 0
57
+ range.set_end body, 0
58
+ range.collapse
59
+ return range
60
+ end
61
+
62
+ def set_cursor_to_end()
63
+ body = find_element('body')
64
+ nodes = body.child_nodes_count
65
+ range = create_range
66
+ range.set_start body, nodes
67
+ range.set_end body, nodes
68
+ range.collapse
69
+ return range
70
+ end
71
+
72
+ def delete_all_content()
73
+ body = find_element('body')
74
+ nodes = body.child_nodes_count
75
+ range = create_range
76
+ range.set_start body, 0
77
+ range.set_end body, nodes
78
+ range.delete_content
79
+ end
80
+
81
+ def insert_content_at_start(content)
82
+ range = set_cursor_to_start
83
+ range.insert_content content
84
+ end
85
+
86
+ def insert_content_at_end(content)
87
+ range = set_cursor_to_end
88
+ range.insert_content content
89
+ end
90
+
91
+ end
92
+
93
+ #
94
+ # TODO: This needs to be refactored so that the classes are generic and not specific
95
+ # to the content editable view proxy.
96
+ #
97
+ module ContentEditableViewSupport
98
+
99
+ class Range
100
+ include Lebowski::Foundation
101
+
102
+ attr_reader :start_element, :end_element, :start_offset, :end_offset
103
+
104
+ def initialize(app, *params)
105
+ if not app.kind_of? Application
106
+ raise ArgumentInvalidTypeError.new "app", app, "class < Application"
107
+ end
108
+
109
+ @start_element = nil
110
+ @end_element = nil
111
+ @start_offset = 0
112
+ @end_offset = 0
113
+ @start_before = false
114
+ @start_after = false
115
+ @end_before = false
116
+ @end_after = false
117
+
118
+ if params[0].kind_of? Hash
119
+ hash = params[0]
120
+ @start_element = hash[:start_element]
121
+ @end_element = hash[:end_element]
122
+ @start_offset = hash[:start_offset].nil? ? 0 : hash[:start_offset]
123
+ @end_offset = hash[:end_offset].nil? ? 0 : hash[:end_offset]
124
+ elsif params.length == 4
125
+ @start_element = params[0]
126
+ @start_offset = params[1]
127
+ @end_element = params[2]
128
+ @end_offset = params[3]
129
+ elsif params.length != 0
130
+ raise ArgumentError.new "invalid number of arguments supplied"
131
+ end
132
+
133
+ assert_element_is_valid(@start_element, "start element")
134
+ assert_element_is_valid(@end_element, "end element")
135
+ assert_offset_is_valid(@start_offset, "start offset")
136
+ assert_offset_is_valid(@end_offset, "end offset")
137
+
138
+ @app = app
139
+ end
140
+
141
+ def set_start(elem, offset=0)
142
+ assert_element_is_valid(elem, "elem")
143
+ assert_offset_is_valid(offset, "offset")
144
+
145
+ @start_element = elem
146
+ @start_offset = offset
147
+ @start_before = false
148
+ @start_after = false
149
+ end
150
+
151
+ def set_end(elem, offset=0)
152
+ assert_element_is_valid(elem, "elem")
153
+ assert_offset_is_valid(offset, "offset")
154
+
155
+ @end_element = elem
156
+ @end_offset = offset
157
+ @end_before = false
158
+ @end_after = false
159
+ end
160
+
161
+ def set_start_before(elem)
162
+ assert_element_is_valid(elem, "elem")
163
+
164
+ @start_element = elem
165
+ @start_offset = 0
166
+ @start_before = true
167
+ @start_after = false
168
+ end
169
+
170
+ def set_start_after(elem)
171
+ assert_element_is_valid(elem, "elem")
172
+
173
+ @start_element = elem
174
+ @start_offset = 0
175
+ @start_before = false
176
+ @start_after = true
177
+ end
178
+
179
+ def set_end_before(elem)
180
+ assert_element_is_valid(elem, "elem")
181
+
182
+ @end_element = elem
183
+ @end_offset = 0
184
+ @end_before = true
185
+ @end_after = false
186
+ end
187
+
188
+ def set_end_after(elem)
189
+ assert_element_is_valid(elem, "elem")
190
+
191
+ @end_element = elem
192
+ @end_offset = 0
193
+ @end_before = false
194
+ @end_after = true
195
+ end
196
+
197
+ def has_start_defined?()
198
+ return ((not @start_element.nil?) or @start_before == true or @start_after == true)
199
+ end
200
+
201
+ def has_end_defined?()
202
+ return ((not @end_element.nil?) or @end_before == true or @end_after == true)
203
+ end
204
+
205
+ def has_boundaries_defined?()
206
+ start_defined = has_start_defined? and has_end_defined?
207
+ end
208
+
209
+ def select()
210
+ if @start_element.nil? or @end_element.nil?
211
+ raise StandardError.new "unable to select range. start and end elements must be defined"
212
+ end
213
+
214
+ @app.exec_driver_in_context do |driver|
215
+ driver.select_range create_range_hash_object
216
+ driver.mouse_up('css=body')
217
+ end
218
+ end
219
+
220
+ def collapse(to_start=true)
221
+ if not has_boundaries_defined?
222
+ raise StandardError.new "unable to collapse range. boundaries must be defined"
223
+ end
224
+
225
+ hash = create_range_hash_object
226
+ if to_start == true
227
+ hash[:collapseToStart] = true
228
+ else
229
+ hash[:collapseToEnd] = true
230
+ end
231
+
232
+ @app.exec_driver_in_context do |driver|
233
+ driver.select_range hash
234
+ driver.mouse_up('css=body')
235
+ end
236
+ end
237
+
238
+ def delete_content()
239
+ if not has_boundaries_defined?
240
+ raise StandardError.new "unable to delete content. boundaries must be defined"
241
+ end
242
+
243
+ @app.exec_driver_in_context do |driver|
244
+ driver.range_delete_content create_range_hash_object
245
+ driver.mouse_up('css=body')
246
+ end
247
+ end
248
+
249
+ def insert_content(content)
250
+ if not content.kind_of? String
251
+ raise ArgumentError.new "unable to insert content. content must be a string"
252
+ end
253
+
254
+ if not has_boundaries_defined?
255
+ raise StandardError.new "unable to insert content. boundaries must be defined"
256
+ end
257
+
258
+ hash = create_range_hash_object
259
+ hash[:content] = content
260
+
261
+ @app.exec_driver_in_context do |driver|
262
+ driver.range_insert_content hash
263
+ driver.mouse_up('css=body')
264
+ end
265
+ end
266
+
267
+ private
268
+
269
+ def assert_element_is_valid(element, name)
270
+ if not (element.nil? or element.kind_of? DOMElement)
271
+ raise ArgumentError.new "a valid #{name} must supplied"
272
+ end
273
+ end
274
+
275
+ def assert_offset_is_valid(offset, name)
276
+ if not ((offset.kind_of? Integer and offset >= 0) or offset.kind_of? String)
277
+ raise ArgumentError.new "#{name} must be an integer greater than or equal to 0"
278
+ end
279
+ end
280
+
281
+ def create_range_hash_object()
282
+ return {
283
+ :startElementSelector => @start_element.selector,
284
+ :startElementIndex => @start_element.index,
285
+ :startOffset => @start_offset,
286
+ :startBefore => @start_before,
287
+ :startAfter => @start_after,
288
+ :endElementSelector => @end_element.selector,
289
+ :endElementIndex => @end_element.index,
290
+ :endOffset => @end_offset,
291
+ :endBefore => @end_before,
292
+ :endAfter => @end_after
293
+ }
294
+ end
295
+
296
+ end
297
+
298
+ class DOMElement
299
+
300
+ attr_reader :index, :selector
301
+
302
+ def initialize(view, selector, index)
303
+ @selector = selector
304
+ @index = index
305
+ @view = view
306
+ end
307
+
308
+ def to_s()
309
+ return "DOMElement<selector=#{selector},index=#{index}>"
310
+ end
311
+
312
+ def tag()
313
+ value = ''
314
+ @view.frame.exec_driver_in_context do |driver|
315
+ value = driver.get_element_tag_name @selector, @index
316
+ end
317
+ return value
318
+ end
319
+
320
+ def child_nodes_count()
321
+ value = 0
322
+ @view.frame.exec_driver_in_context do |driver|
323
+ value = driver.get_element_child_nodes_count @selector, @index
324
+ end
325
+ return value
326
+ end
327
+
328
+ def select()
329
+ range = @view.create_range
330
+ range.set_start_before self
331
+ range.set_end_after self
332
+ range.select
333
+ return range
334
+ end
335
+
336
+ def select_content()
337
+ range = @view.create_range
338
+ range.set_start self, 0
339
+ range.set_end self, child_nodes_count
340
+ range.select
341
+ return range
342
+ end
343
+
344
+ def set_cursor_before()
345
+ range = @view.create_range
346
+ range.set_start_before self
347
+ range.set_end_after self
348
+ range.collapse
349
+ return range
350
+ end
351
+
352
+ def set_cursor_after()
353
+ range = @view.create_range
354
+ range.set_start_before self
355
+ range.set_end_after self
356
+ range.collapse false
357
+ return range
358
+ end
359
+
360
+ def delete()
361
+ range = @view.create_range
362
+ range.set_start_before self
363
+ range.set_end_after self
364
+ range.delete_content
365
+ end
366
+
367
+ def insert_content_before(content)
368
+ range = set_cursor_before
369
+ range.insert_content content
370
+ end
371
+
372
+ def insert_content_after(content)
373
+ range = set_cursor_after
374
+ range.insert_content content
375
+ end
376
+
377
+ end
378
+
379
+ class DOMElementList
380
+
381
+ attr_reader :selector
382
+
383
+ def initialize(view, selector)
384
+ @selector = selector
385
+ @view = view
386
+ end
387
+
388
+ def empty?()
389
+ return (count == 0)
390
+ end
391
+
392
+ def count()
393
+ value = 0
394
+ @view.frame.exec_driver_in_context do |driver|
395
+ value = driver.get_css_selector_count(@selector)
396
+ end
397
+ return value
398
+ end
399
+
400
+ def [](index)
401
+ if not index.kind_of? Integer or index < 0 or index >= count
402
+ raise ArgumentError.new "index is out of bounds: #{index}"
403
+ end
404
+
405
+ return DOMElement.new @view, @selector, index
406
+ end
407
+
408
+ def each(&block)
409
+ return if empty?
410
+
411
+ (0..count).each do |index|
412
+ yield DOMElement.new(@view, @selector, index)
413
+ end
414
+ end
415
+
416
+ end
417
+
418
+ end
419
+
420
+ end
421
+ end
422
+ end
@@ -1,10 +1,79 @@
1
+ # ==========================================================================
2
+ # Project: Lebowski Framework - The SproutCore Test Automation Framework
3
+ # License: Licensed under MIT license (see License.txt)
4
+ # ==========================================================================
5
+
1
6
  require 'date'
2
7
 
3
8
  module Lebowski
4
9
  module SCUI
5
10
  module Views
6
11
 
12
+ #
13
+ # Represents a proxy to a SCUI date picker view (SCUI.DatePickerView)
14
+ #
15
+ class DatePickerView < Lebowski::Foundation::Views::View
16
+ representing_sc_class 'SCUI.DatePickerView'
17
+
18
+ def date_selected?(date)
19
+ return calendar.date_selected?(date)
20
+ end
21
+
22
+ def showing_month?(month)
23
+ return calendar.showing_month?(month)
24
+ end
25
+
26
+ def showing_year?(year)
27
+ return calendar.showing_year?(year)
28
+ end
29
+
30
+ def select_date(date)
31
+ calendar.select_date(date)
32
+ end
33
+
34
+ def select_previous_month
35
+ calendar.select_previous_month
36
+ end
37
+
38
+ def select_next_month
39
+ calendar.select_next_month
40
+ end
41
+
42
+ def select_none
43
+ calendar.select_none
44
+ end
45
+
46
+ def select_today
47
+ calendar.select_today
48
+ end
49
+
50
+ def today_selected?
51
+ return date_selected?(Time.now)
52
+ end
53
+
54
+ def display_calendar
55
+ click_button if !self['isShowingCalendar']
56
+ end
57
+
58
+ def hide_calendar
59
+ click_button if self['isShowingCalendar']
60
+ end
61
+
62
+ private
63
+
64
+ def calendar
65
+ @calendar = DatePickerCalendar.new(self) if @calendar.nil?
66
+ return @calendar
67
+ end
68
+
69
+ def click_button
70
+ self['_date_button'].click
71
+ end
72
+
73
+ end
74
+
7
75
  class DatePickerCalendar
76
+
8
77
  def initialize(parent)
9
78
  @today_button = parent['_calendar_popup.contentView.todayButton']
10
79
  @none_button = parent['_calendar_popup.contentView.noneButton']
@@ -85,64 +154,7 @@ module Lebowski
85
154
  @today_button.click
86
155
  end
87
156
  end
88
-
89
- class DatePickerView < Lebowski::Foundation::Views::View
90
- representing_sc_class 'SCUI.DatePickerView'
91
-
92
- def date_selected?(date)
93
- return calendar.date_selected?(date)
94
- end
95
-
96
- def showing_month?(month)
97
- return calendar.showing_month?(month)
98
- end
99
-
100
- def showing_year?(year)
101
- return calendar.showing_year?(year)
102
- end
103
-
104
- def select_date(date)
105
- calendar.select_date(date)
106
- end
107
-
108
- def select_previous_month
109
- calendar.select_previous_month
110
- end
111
-
112
- def select_next_month
113
- calendar.select_next_month
114
- end
115
-
116
- def select_none
117
- calendar.select_none
118
- end
119
-
120
- def select_today
121
- calendar.select_today
122
- end
123
-
124
- def today_selected?
125
- return date_selected?(Time.now)
126
- end
127
-
128
- def display_calendar
129
- click_button if !self['isShowingCalendar']
130
- end
131
-
132
- def hide_calendar
133
- click_button if self['isShowingCalendar']
134
- end
135
-
136
- private
137
- def calendar
138
- @calendar = DatePickerCalendar.new(self) if @calendar.nil?
139
- return @calendar
140
- end
141
-
142
- def click_button
143
- self['_date_button'].click
144
- end
145
- end
157
+
146
158
  end
147
159
  end
148
160
  end
@@ -1,7 +1,15 @@
1
+ # ==========================================================================
2
+ # Project: Lebowski Framework - The SproutCore Test Automation Framework
3
+ # License: Licensed under MIT license (see License.txt)
4
+ # ==========================================================================
5
+
1
6
  module Lebowski
2
7
  module SCUI
3
8
  module Views
4
9
 
10
+ #
11
+ # Represents a proxy to a SCUI LinkIt canvas view (LinkIt.CanvasView)
12
+ #
5
13
  class CanvasView < Lebowski::Foundation::Views::CollectionView
6
14
  representing_sc_class 'LinkIt.CanvasView'
7
15
 
@@ -0,0 +1,30 @@
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 SCUI
8
+ module Views
9
+
10
+ #
11
+ # Represents a proxy to a SCUI select field tab view (SCUI.SelectFieldTab)
12
+ #
13
+ class SelectFieldTabView < Lebowski::Foundation::Views::View
14
+ representing_sc_class 'SCUI.SelectFieldTab'
15
+
16
+ def select_field
17
+ @select_field = child_views.find_first(SelectFieldView) if @select_field.nil?
18
+ return @select_field
19
+ end
20
+
21
+ def container
22
+ @container = child_views.find_first(ContainerView) if @container.nil?
23
+ return @container
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+ end
30
+ end
data/lib/lebowski/scui.rb CHANGED
@@ -1,10 +1,25 @@
1
+ # ==========================================================================
2
+ # Project: Lebowski Framework - The SproutCore Test Automation Framework
3
+ # License: Licensed under MIT license (see License.txt)
4
+ # ==========================================================================
5
+
1
6
  require File.expand_path(File.dirname(__FILE__) + '/scui/views/date_picker')
2
7
  require File.expand_path(File.dirname(__FILE__) + '/scui/views/combo_box')
3
8
  require File.expand_path(File.dirname(__FILE__) + '/scui/views/linkit')
9
+ require File.expand_path(File.dirname(__FILE__) + '/scui/views/select_field_tab')
10
+ require File.expand_path(File.dirname(__FILE__) + '/scui/views/color_well')
11
+ require File.expand_path(File.dirname(__FILE__) + '/scui/views/content_editable')
4
12
  require File.expand_path(File.dirname(__FILE__) + '/scui/mixins/node_item_view_support')
5
13
  require File.expand_path(File.dirname(__FILE__) + '/scui/mixins/terminal_view_support')
14
+ require File.expand_path(File.dirname(__FILE__) + '/scui/mixins/link_support')
6
15
 
7
16
  module Lebowski
17
+
18
+ #
19
+ # The SCUI module contains proxies for views that are part of the SCUI framework
20
+ #
21
+ # http://github.com/etgryphon/sproutcore-ui
22
+ #
8
23
  module SCUI
9
24
  module Views
10
25
  include Lebowski::Foundation
@@ -12,6 +27,9 @@ module Lebowski
12
27
  ProxyFactory.proxy DatePickerView
13
28
  ProxyFactory.proxy ComboBoxView
14
29
  ProxyFactory.proxy CanvasView
30
+ ProxyFactory.proxy SelectFieldTabView
31
+ ProxyFactory.proxy ColorWellView
32
+ ProxyFactory.proxy ContentEditableView
15
33
 
16
34
  end
17
35
  end
@@ -6,8 +6,8 @@
6
6
  module Lebowski # :nodoc:
7
7
  module VERSION # :nodoc:
8
8
  MAJOR = 0
9
- MINOR = 1
10
- TINY = 1
9
+ MINOR = 2
10
+ TINY = 0
11
11
  PRE = nil
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')