safariwatir 0.3.8 → 0.4.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.
@@ -0,0 +1,15 @@
1
+ module ElementAttributes
2
+ def html_method_reader(m_name, html_attribute = nil)
3
+ attr_to_reference = (html_attribute ? html_attribute : m_name.to_s)
4
+ define_method(m_name) do
5
+ html_method(attr_to_reference) ? html_method(attr_to_reference) : ""
6
+ end
7
+ end
8
+
9
+ def html_attr_reader(m_name, html_attribute = nil)
10
+ attr_to_reference = (html_attribute ? html_attribute : m_name.to_s)
11
+ define_method(m_name) do
12
+ attr(attr_to_reference) ? attr(attr_to_reference) : ""
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,127 @@
1
+ require File.dirname(__FILE__) + '/../watir/exceptions'
2
+
3
+ module Locators
4
+
5
+ def document_locator
6
+ parent.document_locator
7
+ end
8
+
9
+ def locator
10
+ self.send("locator_by_#{how.to_s}".to_sym)
11
+ end
12
+
13
+ def locator_by_text
14
+ locator_by_method("innerText")
15
+ end
16
+
17
+ def locator_by_value
18
+ locator_by_method("value")
19
+ end
20
+
21
+ def locator_by_src
22
+ locator_by_attribute("src")
23
+ end
24
+
25
+ def locator_by_alt
26
+ locator_by_attribute("alt")
27
+ end
28
+
29
+ def locator_by_src
30
+ locator_by_attribute("src")
31
+ end
32
+
33
+ def locator_by_href
34
+ locator_by_attribute("href")
35
+ end
36
+
37
+ def locator_by_url
38
+ locator_by_attribute("href")
39
+ end
40
+
41
+ def locator_by_title
42
+ locator_by_attribute("title")
43
+ end
44
+
45
+ def locator_by_method(m_name)
46
+ "findByMethodValue(#{parent.locator}, #{tag_names}, \"#{m_name}\", #{encode_what})[0]"
47
+ end
48
+
49
+ def locator_by_attribute(attribute_name)
50
+ "findByAttributeValue(#{parent.locator}, #{tag_names}, \"#{attribute_name}\", #{encode_what})[0]"
51
+ end
52
+
53
+ def locator_by_xpath
54
+ xpath = what.gsub(/"/, "\'")
55
+ "findByXPath(#{document_locator}, #{parent.locator}, \"#{xpath}\")"
56
+ end
57
+
58
+ def locator_by_name
59
+ locator_by_attribute("name")
60
+ end
61
+
62
+ def locator_by_index
63
+ "findByTagNames(#{parent.locator}, #{tag_names})[#{what.to_i - 1}]"
64
+ end
65
+
66
+ def locator_by_class
67
+ locator_by_attribute("class")
68
+ end
69
+
70
+ def locator_by_id
71
+ locator_by_method("id")
72
+ end
73
+
74
+ def tag_names
75
+ t_names = tag.kind_of?(Array) ? tag : [tag]
76
+ "[" + (t_names.map { |t_name| "\"#{t_name.upcase}\"" }.join(", ")) + "]"
77
+ end
78
+
79
+ def encode_what
80
+ what.kind_of?(Regexp) ? "new RegexValueMatcher(/#{what.source}/)" : "new ExactValueMatcher(\"#{what.to_s}\")"
81
+ end
82
+
83
+ def method_missing(*args)
84
+ if args[0].to_s =~ /locator_by_/
85
+ raise Watir::Exception::MissingWayOfFindingObjectException
86
+ end
87
+ super(*args)
88
+ end
89
+
90
+ end
91
+
92
+ module InputLocators
93
+ include Locators
94
+
95
+ def locator_by_method(m_name)
96
+ "findInputByMethodValue(#{parent.locator}, \"#{input_type}\", \"#{m_name}\", #{encode_what})[0]"
97
+ end
98
+
99
+ def locator_by_attribute(attribute_name)
100
+ "findInputByAttributeValue(#{parent.locator}, \"#{input_type}\", \"#{attribute_name}\", #{encode_what})[0]"
101
+ end
102
+
103
+ def locator_by_index
104
+ "findInputsByType(#{parent.locator}, \"#{input_type}\")[#{what.to_i - 1}]"
105
+ end
106
+
107
+ end
108
+
109
+ module ButtonLocators
110
+ include Locators
111
+
112
+ def locator_by_index
113
+ "findAllMatching(#{parent.locator}, new ButtonMatcher())[#{what.to_i - 1}]"
114
+ end
115
+
116
+ def locator_by_attribute(attribute_name)
117
+ "filterToAttributeValue(findAllMatching(#{parent.locator}, new ButtonMatcher()), \"#{attribute_name}\", #{encode_what})[0]"
118
+ end
119
+
120
+ def locator_by_method(attribute_name)
121
+ "filterToMethodValue(findAllMatching(#{parent.locator}, new ButtonMatcher()), \"#{attribute_name}\", #{encode_what})[0]"
122
+ end
123
+
124
+ def locator_by_value
125
+ locator_by_attribute("value")
126
+ end
127
+ end
@@ -9,12 +9,144 @@ module Watir # :nodoc:
9
9
  TABLE_CELL_NOT_FOUND = "__safari_watir_cell_unfound__"
10
10
  EXTRA_ACTION_SUCCESS = "__safari_watir_extra_action__"
11
11
 
12
- JS_LIBRARY = %|
13
- function dispatchOnChange(element) {
14
- var event = document.createEvent('HTMLEvents');
12
+ JS_LIBRARY = <<JSCODE
13
+ /* Library functions */
14
+
15
+ var ButtonMatcher = function() {
16
+ this.match = function(ele) {
17
+ if (ele.tagName == "BUTTON") {
18
+ return(true);
19
+ }
20
+
21
+ if (ele.tagName == 'INPUT') {
22
+ return(["submit", "button", "reset"].indexOf(ele.type) != -1);
23
+ }
24
+
25
+ return(false);
26
+ }
27
+ }
28
+
29
+
30
+ var ExactValueMatcher = function(exactVal) {
31
+ this.match = function (val) {
32
+ return(val == exactVal);
33
+ }
34
+ }
35
+
36
+ var RegexValueMatcher = function(regEx) {
37
+ this.match = function (val) {
38
+ return(regEx.test(val));
39
+ }
40
+ }
41
+
42
+ function dispatchOnChange(scope, element) {
43
+ var event = scope.createEvent('HTMLEvents');
15
44
  event.initEvent('change', true, true);
16
45
  element.dispatchEvent(event);
17
- }|
46
+ }
47
+
48
+ function findByXPath(dscope, scope, expr) {
49
+ var result = dscope.evaluate(expr, scope, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
50
+ return(result ? result.singleNodeValue : null );
51
+ }
52
+
53
+ function filterToMethodValue(found_tags, m_name, m_value) {
54
+ var result_array = [];
55
+
56
+ for (var i = 0; i < found_tags.length; i++) {
57
+ if (m_value.match(found_tags[i][m_name])) {
58
+ result_array.push(found_tags[i]);
59
+ }
60
+ }
61
+ return(result_array);
62
+ }
63
+
64
+ function filterToAttributeValue(found_tags, attr_name, attr_value) {
65
+ var result_array = [];
66
+
67
+ for (var i = 0; i < found_tags.length; i++) {
68
+ if (attr_value.match(found_tags[i].getAttribute(attr_name))) {
69
+ result_array.push(found_tags[i]);
70
+ }
71
+ }
72
+ return(result_array);
73
+ }
74
+
75
+ function filterToTagNames(found_tags, tags) {
76
+ var result_array = [];
77
+
78
+ for (var i = 0; i < found_tags.length; i++) {
79
+ if (found_tags[i].tagName != 'META' && tags.indexOf(found_tags[i].tagName) != -1) {
80
+ result_array.push(found_tags[i]);
81
+ }
82
+ }
83
+ return(result_array);
84
+ }
85
+
86
+ function findByClassName(scope, cname, tags) {
87
+ var found_tags = scope.getElementsByClassName(cname);
88
+ return( filterToTagNames(tags) );
89
+ }
90
+
91
+ function findInputByMethodValue(scope, input_type, attribute, value) {
92
+ var found_tags = findInputsByType(scope, input_type);
93
+ return( filterToMethodValue(found_tags, attribute, value) );
94
+ }
95
+
96
+ function findInputByAttributeValue(scope, input_type, attribute, value) {
97
+ var found_tags = findInputsByType(scope, input_type);
98
+ return( filterToAttributeValue(found_tags, attribute, value) );
99
+ }
100
+
101
+ function findByMethodValue(scope, names, attribute, value) {
102
+ var found_tags = findByTagNames(scope, names);
103
+ return( filterToMethodValue(found_tags, attribute, value) );
104
+ }
105
+
106
+ function findByAttributeValue(scope, names, attribute, value) {
107
+ var found_tags = findByTagNames(scope, names);
108
+ return( filterToAttributeValue(found_tags, attribute, value) );
109
+ }
110
+
111
+ function findAllMatching(scope, ele_matcher) {
112
+ var result_array = [];
113
+ var found_tags = scope.getElementsByTagName('*');
114
+
115
+ for (var i = 0; i < found_tags.length; i++) {
116
+ if (ele_matcher.match(found_tags[i])) {
117
+ result_array.push(found_tags[i]);
118
+ }
119
+ }
120
+ return(result_array);
121
+ }
122
+
123
+ function findInputsByType(scope, input_type) {
124
+ var result_array = [];
125
+ var found_tags = scope.getElementsByTagName("input");
126
+
127
+ for (var i = 0; i < found_tags.length; i++) {
128
+ if (found_tags[i].type == input_type) {
129
+ result_array.push(found_tags[i]);
130
+ }
131
+ }
132
+ return(result_array);
133
+ }
134
+
135
+ function findByTagNames(scope, names) {
136
+ var result_array = [];
137
+ var found_tags = null;
138
+
139
+ for (var i = 0; i < names.length; i++) {
140
+ found_tags = scope.getElementsByTagName(names[i]);
141
+ for (var j = 0; j < found_tags.length; j++) {
142
+ result_array.push(found_tags[j]);
143
+ }
144
+ }
145
+ return(result_array);
146
+ }
147
+
148
+ /* Action Code */
149
+ JSCODE
18
150
 
19
151
  class JavaScripter # :nodoc:
20
152
  def operate(locator, operation)
@@ -64,19 +196,6 @@ if (element) {
64
196
  end
65
197
  end
66
198
 
67
- class FrameJavaScripter < JavaScripter # :nodoc:
68
- def initialize(frame)
69
- @page_container = "parent.#{frame.name}"
70
- end
71
-
72
- def wrap(script)
73
- # add in frame name when referencing parent or document
74
- script.gsub! /\bparent\b/, "parent.#{@page_container}"
75
- script.gsub! /\bdocument\b/, "#{@page_container}.document"
76
- super(script)
77
- end
78
- end
79
-
80
199
  class TableJavaScripter < JavaScripter # :nodoc:
81
200
  def_init :cell
82
201
 
@@ -100,6 +219,7 @@ if (element) {
100
219
  @appname = opts[:appname] || "Safari"
101
220
  @app = Appscript.app(@appname)
102
221
  @document = @app.documents[1]
222
+ @typing_lag = 0.08
103
223
  end
104
224
 
105
225
  def ensure_window_ready
@@ -161,10 +281,14 @@ if (element == undefined) {
161
281
  def get_attribute(name, element = @element)
162
282
  execute(element.operate { %|return element.getAttribute('#{name}')| }, element)
163
283
  end
284
+
285
+ def get_method_value(name, element = @element)
286
+ execute(element.operate { %|return element.#{name}| }, element)
287
+ end
164
288
 
165
289
 
166
- def document_text
167
- execute(%|return document.getElementsByTagName('BODY').item(0).innerText;|)
290
+ def document_text(element)
291
+ execute(%|return #{element.locator}.getElementsByTagName('BODY').item(0).innerText;|)
168
292
  end
169
293
 
170
294
  def document_html
@@ -197,32 +321,32 @@ element.style.backgroundColor = 'yellow';|
197
321
  end
198
322
 
199
323
  def element_exists?(element = @element, &block)
200
- block ||= Proc.new {}
201
- execute(element.operate(&block), element)
202
- return true
203
- rescue UnknownObjectException
324
+ response = eval_js(element.operate do
325
+ <<JSCODE
326
+ if (element == undefined || element == null) {
327
+ return(false);
328
+ }
329
+ return(true);
330
+ JSCODE
331
+ end)
332
+ case response
333
+ when ELEMENT_NOT_FOUND
204
334
  return false
335
+ else
336
+ return response
337
+ end
205
338
  end
206
339
 
207
340
  def select_option(element = @element)
208
- execute(element.operate do
209
- %|var selected = -1;
210
- var previous_selection = -2;
211
- for (var i = 0; i < element.options.length; i++) {
212
- if (element.options[i].selected) {
213
- previous_selection = i;
214
- }
215
- if (element.options[i].#{element.how} == '#{element.what}') {
216
- element.options[i].selected = true;
217
- selected = i;
341
+ execute(element.operate do %|
342
+ var other_options = element.parentNode.options;
343
+ for (var i = 0; i < other_options.length; i++) {
344
+ other_options[i].selected = undefined;
218
345
  }
219
- }
220
- if (selected == -1) {
221
- return '#{ELEMENT_NOT_FOUND}';
222
- } else if (previous_selection != selected) {
223
- element.selectedIndex = selected;
224
- dispatchOnChange(element.options[selected]);
225
- }
346
+ element.selected = "selected";
347
+ element.parentNode.selectedIndex = element.index;
348
+ dispatchOnChange(#{element.document_locator}, element);
349
+ dispatchOnChange(#{element.document_locator}, element.parentNode);
226
350
  |
227
351
  end, element)
228
352
  end
@@ -267,11 +391,32 @@ return selected;|
267
391
  sleep typing_lag
268
392
  execute(element.operate do
269
393
  %|element.value += '#{value}';
270
- dispatchOnChange(element);
271
- element.setSelectionRange(element.value.length, element.value.length);|
394
+ dispatchOnChange(#{element.document_locator}, element);
395
+ element.setSelectionRange(element.value.length, element.value.length);
396
+ |
272
397
  end, element)
273
398
  end
274
399
 
400
+ def set_file_field(element, value)
401
+ file_base_name = File.basename(value)
402
+ click_element(element)
403
+ sleep 0.4
404
+ se = Appscript.app("System Events")
405
+ open_popup = se.processes["Safari"].windows[1].sheets[1]
406
+ choose_button = open_popup.buttons[1]
407
+ search_field = open_popup.groups[1].text_fields[1]
408
+ search_by_file_name = open_popup.groups[1].splitter_groups[1].radio_groups[1].checkboxes[6]
409
+ sr_outline = open_popup.groups[1].splitter_groups[1].scroll_areas[2].outlines[1]
410
+ confirm_search_action = search_field.actions[2].get
411
+ search_field.value.set(file_base_name)
412
+ search_field.perform(confirm_search_action)
413
+ sleep 2
414
+ search_by_file_name.click
415
+ sleep 1
416
+ sr_outline.rows[1].select
417
+ choose_button.click
418
+ end
419
+
275
420
  def click_element(element = @element)
276
421
  page_load do
277
422
  # Not sure if these events should be either/or. But if you have an image with an onclick, it fires twice without the else clause.
@@ -384,6 +529,11 @@ for (var i = 0; i < document.links.length; i++) {
384
529
  def element_disabled?(element = @element)
385
530
  execute(element.operate { %|return element.disabled;| }, element)
386
531
  end
532
+
533
+ def operate_by_locator(element)
534
+ js.operate(%|var element = #{element.locator};
535
+ |, yield)
536
+ end
387
537
 
388
538
  def operate_by_input_value(element)
389
539
  js.operate(%|
@@ -397,23 +547,6 @@ for (var i = 0; i < elements.length; i++) {
397
547
  }|, yield)
398
548
  end
399
549
 
400
- def operate_by_name(element)
401
- js.operate(%|
402
- var elements = document.getElementsByName('#{element.what}');
403
- var element = undefined;
404
- for (var i = 0; i < elements.length; i++) {
405
- if (elements[i].tagName != 'META' && elements[i].tagName == '#{element.tag}') {
406
- #{handle_form_element_name_match(element)}
407
- }
408
- }|, yield)
409
- end
410
-
411
- def operate_by_class(element)
412
- js.operate(%|
413
- var elements = document.getElementsByClassName('#{element.what}');
414
- var element = elements[0];|, yield)
415
- end
416
-
417
550
  # Checkboxes/Radios have the same name, different values
418
551
  def handle_form_element_name_match(element)
419
552
  element_capture = %|element = elements[i];break;|
@@ -427,22 +560,6 @@ var element = elements[0];|, yield)
427
560
  end
428
561
  private :handle_form_element_name_match
429
562
 
430
- def operate_by_id(element)
431
- js.operate("var element = document.getElementById('#{element.what}');", yield)
432
- end
433
-
434
- def operate_by_index(element)
435
- js.operate(%|var element = document.getElementsByTagName('#{element.tag}')[#{element.what-1}];|, yield)
436
- end
437
-
438
- def operate_by_src(element, &block)
439
- operate_by(element, 'src', &block)
440
- end
441
-
442
- def operate_by_alt(element, &block)
443
- operate_by(element, 'alt', &block)
444
- end
445
-
446
563
  def operate_by_title(element, &block)
447
564
  operate_by(element, 'title', &block)
448
565
  end
@@ -455,14 +572,6 @@ var element = elements[0];|, yield)
455
572
  operate_by(element, 'innerText', &block)
456
573
  end
457
574
 
458
- def operate_by_xpath(element)
459
- xpath = element.what.gsub(/"/, "\'")
460
- js.operate(%|
461
- var result = document.evaluate("#{xpath}", document.documentElement, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
462
- var element = result ? result.singleNodeValue : null;
463
- |, yield)
464
- end
465
-
466
575
  def operate_by(element, attribute)
467
576
  js.operate(%|var elements = document.getElementsByTagName('#{element.tag}');
468
577
  var element = undefined;
@@ -507,15 +616,6 @@ end tell|, true)
507
616
  AppleScripter.new(TableJavaScripter.new(element))
508
617
  end
509
618
 
510
- def for_frame(frame)
511
- # verify the frame exists
512
- execute(
513
- %|if (parent.#{frame.name} == undefined) {
514
- return '#{FRAME_NOT_FOUND}';
515
- }|, frame)
516
- AppleScripter.new(FrameJavaScripter.new(frame))
517
- end
518
-
519
619
  def speak_value_of(element = @element)
520
620
  speak(get_value_for(element))
521
621
  end
@@ -547,13 +647,18 @@ SCRIPT`
547
647
 
548
648
  private
549
649
 
650
+ def tag_names(element = @element)
651
+ t_names = element.tag.kind_of?(Array) ? element.tag : [element.tag]
652
+ "[" + (t_names.map { |t_name| "\"#{t_name.downcase}\"" }.join(", ")) + "]"
653
+ end
654
+
550
655
  def execute(script, element = nil)
551
656
  response = eval_js(script)
552
657
  case response
553
658
  when NO_RESPONSE
554
659
  nil
555
660
  when ELEMENT_NOT_FOUND
556
- raise UnknownObjectException, "Unable to locate #{element.element_name} element with #{element.how} of #{element.what}"
661
+ raise element_not_found_exception(element)
557
662
  when TABLE_CELL_NOT_FOUND
558
663
  raise UnknownCellException, "Unable to locate a table cell with #{element.how} of #{element.what}"
559
664
  when FRAME_NOT_FOUND
@@ -562,6 +667,13 @@ SCRIPT`
562
667
  response
563
668
  end
564
669
  end
670
+
671
+ def element_not_found_exception(element)
672
+ if element.is_frame?
673
+ return UnknownFrameException.new("Unable to locate a frame, using :#{element.how} and \"#{element.what}\"")
674
+ end
675
+ UnknownObjectException.new("Unable to locate #{element.element_name}, using :#{element.how} and \"#{element.what}\"")
676
+ end
565
677
 
566
678
  def execute_and_ignore(script)
567
679
  eval_js(script)