lebowski 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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('.')