locatine 0.02247 → 0.02542

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +159 -4
  3. data/bin/locatine-daemon.rb +8 -0
  4. data/lib/locatine.rb +1 -0
  5. data/lib/locatine/app/background.js +0 -1
  6. data/lib/locatine/app/content.js +1 -0
  7. data/lib/locatine/app/manifest.json +1 -1
  8. data/lib/locatine/app/popup.js +4 -2
  9. data/lib/locatine/daemon.rb +93 -0
  10. data/lib/locatine/daemon_helpers.rb +52 -0
  11. data/lib/locatine/for_search/data_generate.rb +12 -45
  12. data/lib/locatine/for_search/data_logic.rb +32 -10
  13. data/lib/locatine/for_search/defaults.rb +40 -0
  14. data/lib/locatine/for_search/dialog_logic.rb +31 -8
  15. data/lib/locatine/for_search/element_selection.rb +4 -4
  16. data/lib/locatine/for_search/file_work.rb +10 -10
  17. data/lib/locatine/for_search/find_by_guess.rb +40 -38
  18. data/lib/locatine/for_search/find_by_locator.rb +0 -30
  19. data/lib/locatine/for_search/find_by_magic.rb +30 -81
  20. data/lib/locatine/for_search/find_logic.rb +6 -2
  21. data/lib/locatine/for_search/helpers.rb +21 -12
  22. data/lib/locatine/for_search/listening.rb +1 -0
  23. data/lib/locatine/for_search/merge.rb +2 -1
  24. data/lib/locatine/for_search/page_work.rb +126 -0
  25. data/lib/locatine/for_search/public.rb +35 -22
  26. data/lib/locatine/for_search/saying.rb +15 -2
  27. data/lib/locatine/for_search/xpath_generator.rb +10 -11
  28. data/lib/locatine/large_scripts/element.js +30 -0
  29. data/lib/locatine/large_scripts/page.js +60 -0
  30. data/lib/locatine/scope.rb +1 -0
  31. data/lib/locatine/search.rb +8 -3
  32. data/lib/locatine/version.rb +1 -1
  33. metadata +23 -16
  34. data/lib/locatine/for_search/find_by_css.rb +0 -47
@@ -15,6 +15,8 @@ module Locatine
15
15
  result = find_by_data(@data[scope][name], vars)
16
16
  attributes = generate_data(result, vars) if result
17
17
  if !result && !exact
18
+ @autolearn = true if @autolearn.nil?
19
+ @save = true
18
20
  result, attributes = find_by_magic(name, scope,
19
21
  @data[scope][name], vars)
20
22
  end
@@ -29,9 +31,10 @@ module Locatine
29
31
  end
30
32
 
31
33
  def full_search(name, scope, vars, locator, exact)
34
+ @save = @autolearn
32
35
  result, attributes = search_steps(name, scope, vars, locator, exact)
33
36
  raise_not_found(name, scope) if !result && !@current_no_f
34
- store(attributes, scope, name) if result
37
+ store(attributes, scope, name) if result && (@save || @learn)
35
38
  return result, attributes
36
39
  end
37
40
 
@@ -48,8 +51,9 @@ module Locatine
48
51
  end
49
52
 
50
53
  def locator_search(locator, vars)
51
- result = find_by_locator(locator) if locator != {}
54
+ result = find_by_locator(locator) unless locator == {}
52
55
  attributes = generate_data(result, vars) if result
56
+ warn_broken_locator(locator) if locator.to_h != {} && !result
53
57
  return result, attributes
54
58
  end
55
59
 
@@ -16,21 +16,32 @@ module Locatine
16
16
  (@iframe || @browser)
17
17
  end
18
18
 
19
+ def take_html
20
+ engine.locate
21
+ engine.html.gsub(/<div.*id="locatine_magic_div".*>/, '')
22
+ end
23
+
19
24
  def time
20
25
  t = Time.now
21
- "#{t.year}.#{t.month}.#{t.day} #{t.hour.to_s.rjust(2, '0')}:"\
22
- "#{t.min.to_s.rjust(2, '0')}:#{t.sec.to_s.rjust(2, '0')}"
26
+ t.strftime('%F %T')
23
27
  end
24
28
 
25
29
  def fix_iframe
26
30
  @iframe = @browser.iframe(@iframe.selector) if @iframe && @iframe.stale?
27
31
  end
28
32
 
29
- def set_env_for_search(look_in, iframe, tolerance, no_fail)
33
+ def set_env_for_search(look_in,
34
+ iframe,
35
+ tolerance,
36
+ no_fail,
37
+ trusted,
38
+ untrusted)
30
39
  @type = look_in
31
40
  @iframe = iframe
32
41
  @current_t = tolerance || @tolerance
33
42
  @current_no_f = no_fail || @no_fail
43
+ @trust_now = trusted || @trusted
44
+ @untrust_now = untrusted || @untrusted
34
45
  end
35
46
 
36
47
  def not_magic_div
@@ -57,15 +68,6 @@ module Locatine
57
68
  Watir::Browser.new(:chrome, switches: ["--load-extension=#{HOME}/app"])
58
69
  end
59
70
 
60
- def import_browser(browser)
61
- selenium = browser.class.superclass == Selenium::WebDriver::Driver
62
- b = right_browser unless browser
63
- b = browser if browser.class == Watir::Browser
64
- b = Watir::Browser.new(browser) if selenium
65
- @browser = b
66
- @default_styles = default_styles.to_a
67
- end
68
-
69
71
  def css_text_to_hash(text)
70
72
  almost_hash = []
71
73
  array = text[0..-2].split('; ')
@@ -92,6 +94,13 @@ module Locatine
92
94
  raise_no_var(thevar) unless value
93
95
  process_string(str.gsub('#{' + thevar + '}', value.to_s), vars)
94
96
  end
97
+
98
+ def most_common_of(all)
99
+ max = all.count(all.max_by { |i| all.count(i) })
100
+ return (all.select { |i| all.count(i) == max }).uniq unless max.zero?
101
+
102
+ []
103
+ end
95
104
  end
96
105
  end
97
106
  end
@@ -23,6 +23,7 @@ module Locatine
23
23
  def start_listening(scope, name)
24
24
  send_to_app('locatinestyle', 'blocked', @browser) if @iframe
25
25
  send_to_app('locatinestyle', 'set_true')
26
+ send_to_app('locatineconfirmed', 'ok')
26
27
  send_selecting(name, scope)
27
28
  sleep 0.5
28
29
  end
@@ -18,7 +18,8 @@ module Locatine
18
18
  array.each do |hash|
19
19
  item = second[depth]
20
20
  to_add = item.nil? ? [] : select_same(second[depth], hash)
21
- to_add = stability_bump(to_add, hash) if stability_up
21
+ max = max_stability(second[depth]).to_i + 1
22
+ to_add = stability_bump(to_add, hash, max) if stability_up
22
23
  result += to_add
23
24
  end
24
25
  result
@@ -0,0 +1,126 @@
1
+ module Locatine
2
+ module ForSearch
3
+ ##
4
+ # Methods about getting full information about opened page>
5
+ # And methods for stalkibg around the page in oreder to find something.
6
+ module PageWork
7
+ private
8
+
9
+ def matcher
10
+ { 'tag' => ->(data) { take_by_tag(*data) },
11
+ 'text' => ->(data) { take_by_text(*data) },
12
+ 'attribute' => ->(data) { take_by_attribute(*data) },
13
+ 'css' => ->(data) { take_by_css(*data) } }
14
+ end
15
+
16
+ def take_dom
17
+ script = File.read("#{HOME}/large_scripts/page.js")
18
+ engine.execute_script(script)
19
+ end
20
+
21
+ def select_from_page(page, data, vars)
22
+ all = [] # No result is a valid result too
23
+ data.each_pair do |depth, array|
24
+ get_trusted(array).each do |hash|
25
+ all += catch(page, hash, vars, depth)
26
+ end
27
+ end
28
+ all
29
+ end
30
+
31
+ def catch(page, hash, vars, depth)
32
+ all = []
33
+ hash['value'] = process_string(hash['value'], vars)
34
+ page.each do |element|
35
+ all += take_match(element, depth, hash, vars)
36
+ all += catch(element['children'], hash, vars, depth)
37
+ end
38
+ all.uniq
39
+ end
40
+
41
+ def take_match(element, depth, hash, vars)
42
+ if hash['type'] == 'dimensions'
43
+ return take_by_dimensions(hash, element, depth, vars)
44
+ end
45
+
46
+ matcher.fetch(hash['type']).call([hash, element, depth])
47
+ end
48
+
49
+ def take_by_tag(hash, elt, depth)
50
+ return kids([elt], depth) if elt['tag'].downcase.include?(hash['value'])
51
+
52
+ []
53
+ end
54
+
55
+ def take_by_text(hash, elt, depth)
56
+ return kids([elt], depth) if elt['text'].include?(hash['value'])
57
+
58
+ []
59
+ end
60
+
61
+ def take_attribute_check(hash, elt)
62
+ if !hash['name'].to_s.empty? && !hash['value'].to_s.empty?
63
+ return elt['attrs'][hash['name']].to_s
64
+ end
65
+
66
+ elt['attrs'].to_s
67
+ end
68
+
69
+ def take_by_attribute(hash, elt, depth)
70
+ str = hash['value'].to_s.empty? ? hash['name'].to_s : hash['value'].to_s
71
+ ok = take_attribute_check(hash, elt).include?(str)
72
+ return kids([elt], depth) if ok
73
+
74
+ []
75
+ end
76
+
77
+ def dimensions_for_search(hash, vars)
78
+ values = hash['value'].split('*').map(&:to_i)
79
+ values[0] = vars[:x].to_i if vars[:x]
80
+ values[1] = vars[:y].to_i if vars[:y]
81
+ values
82
+ end
83
+
84
+ def dimensions_from_page(elt)
85
+ [elt['coordinates']['top'].to_i, elt['coordinates']['bottom'].to_i,
86
+ elt['coordinates']['left'].to_i, elt['coordinates']['right'].to_i]
87
+ end
88
+
89
+ def take_by_dimensions(hash, elt, depth, vars)
90
+ return [] if !visual? || hash['name'] != window_size
91
+
92
+ return kids([elt], depth) if in_recatngle?(hash, elt, vars)
93
+
94
+ []
95
+ end
96
+
97
+ def in_recatngle?(hash, elt, vars)
98
+ cleft, ctop, cwidth, cheight = dimensions_for_search(hash, vars)
99
+ top, bottom, left, right = dimensions_from_page(elt)
100
+ (cleft >= left) && (cleft + cwidth <= right) &&
101
+ (ctop >= top) && (ctop + cheight <= bottom)
102
+ end
103
+
104
+ def take_by_css(hash, elt, depth)
105
+ return [] unless visual?
106
+
107
+ string = "#{hash['name']}: #{hash['value']}"
108
+ return kids([elt], depth) if elt['style'].include?(string)
109
+
110
+ []
111
+ end
112
+
113
+ # If depth != 0 we should return all children subchildren.
114
+ def kids(array, depth)
115
+ answer = []
116
+ return array if depth.to_i.zero?
117
+
118
+ array.each do |one|
119
+ answer += one['children']
120
+ answer += kids(one['children'], depth)
121
+ end
122
+ answer
123
+ end
124
+ end
125
+ end
126
+ end
@@ -19,8 +19,8 @@ module Locatine
19
19
  # +learn+ shows will locatine ask for assistance from user or will fail
20
20
  # on error. learn is true when LEARN parameter is set in environment.
21
21
  #
22
- # +stability_limit+ shows max times attribute should be present to
23
- # consider it trusted.
22
+ # +stability_limit+ shows max times attribute should be present and
23
+ # checked to consider it trusted.
24
24
  #
25
25
  # +scope+ will be used in search (if not provided) defaulkt is "Default"
26
26
  #
@@ -28,24 +28,26 @@ module Locatine
28
28
  # to the lost one. Default is 67 which means that if less than 33% of
29
29
  # metrics of alternative elements are the same as of the lost element
30
30
  # will not be returned
31
- def initialize(json: './Locatine_files/default.json',
32
- depth: 3,
33
- browser: nil,
34
- learn: ENV['LEARN'].nil? ? false : true,
35
- stability_limit: 1000,
36
- scope: 'Default',
37
- tolerance: 67,
38
- visual_search: false,
39
- no_fail: false)
40
- import_browser browser
41
- import_file(json)
42
- @depth = depth
43
- @learn = learn
44
- @stability_limit = stability_limit
45
- @scope = scope
46
- @tolerance = tolerance
47
- @visual_search = visual_search
48
- @no_fail = no_fail
31
+ #
32
+ # +visual_search+ locatine will use position and style if true
33
+ #
34
+ # +no_fail+ if true locatine is not producing errors on element loss.
35
+ #
36
+ # +trusted+ array of names of attributes and element params to use
37
+ # in search always.
38
+ #
39
+ # +untrusted+ array of names of attributes and element params to use
40
+ # in search never.
41
+ #
42
+ # +autolearn+ determines will locatine study an element or not. true means
43
+ # locatine will always study it (slow). false means it won't study it
44
+ # unless it was lost and found. If not stated locatine will turn set it
45
+ # true if at least one element was lost.
46
+ def initialize(config = {})
47
+ init_config = default_init_config.merge(config)
48
+ import_browser init_config.delete :browser
49
+ import_file init_config.delete :json
50
+ import_config init_config
49
51
  end
50
52
 
51
53
  ##
@@ -80,6 +82,14 @@ module Locatine
80
82
  #
81
83
  # +tolerance+ It is possible to set a custom tolerance for every find. See
82
84
  # examples in README
85
+ #
86
+ # +no_fail+ if true locatine is not producing errors on element loss.
87
+ #
88
+ # +trusted+ array of names of attributes and element params to use
89
+ # in search always.
90
+ #
91
+ # +untrusted+ array of names of attributes and element params to use
92
+ # in search never.
83
93
  def find(simple_name = nil,
84
94
  name: nil,
85
95
  scope: nil,
@@ -91,9 +101,12 @@ module Locatine
91
101
  return_locator: false,
92
102
  collection: false,
93
103
  tolerance: nil,
94
- no_fail: nil)
104
+ no_fail: nil,
105
+ trusted: nil,
106
+ untrusted: nil)
95
107
  name = set_name(simple_name, name)
96
- set_env_for_search(look_in, iframe, tolerance, no_fail)
108
+ set_env_for_search(look_in, iframe, tolerance,
109
+ no_fail, trusted, untrusted)
97
110
  scope ||= @scope.nil? ? 'Default' : @scope
98
111
  result, attributes = full_search(name, scope, vars, locator, exact)
99
112
  return { xpath: generate_xpath(attributes, vars) } if result &&
@@ -68,8 +68,8 @@ module Locatine
68
68
  push_title "Locatine has no good guess for #{name} in #{scope}."
69
69
  end
70
70
 
71
- def send_has_guess(length, name, scope)
72
- push_title "#{length} #{verb(length)} guessed as #{name} in #{scope}."
71
+ def send_has_guess(name, scope)
72
+ push_title "Something is guessed as #{name} in #{scope}."
73
73
  end
74
74
 
75
75
  def send_selecting(name, scope)
@@ -137,6 +137,19 @@ module Locatine
137
137
  send_warn "Something was found as #{data} but we cannot highlight it"
138
138
  end
139
139
 
140
+ def warn_broken_locator(locator)
141
+ send_warn "Can find nothing using #{locator}"
142
+ end
143
+
144
+ def warn_unstable
145
+ send_warn 'It seems that page is unstable. It may lead to problems '\
146
+ 'with resolving elements'
147
+ end
148
+
149
+ def warn_highly_unstable
150
+ send_warn 'It seems that page is HIGHLY unstable. No guaranties now.'
151
+ end
152
+
140
153
  def warn_much_highlight(size)
141
154
  send_warn "Only the first 50 elements of #{size} were highlighted."
142
155
  end
@@ -7,14 +7,20 @@ module Locatine
7
7
 
8
8
  def get_trusted(array)
9
9
  if !array.empty?
10
- max_stability = (array
11
- .max_by { |i| i['stability'].to_i })['stability']
12
- (array.select { |i| i['stability'].to_i == max_stability.to_i }).uniq
10
+ max = max_stability(array)
11
+ (array.select { |i| i['stability'].to_i == max.to_i }).uniq
13
12
  else
14
13
  []
15
14
  end
16
15
  end
17
16
 
17
+ def max_stability(array)
18
+ max = (array.max_by { |i| i['stability'].to_i }) if array
19
+ return max['stability'] if max
20
+
21
+ 0
22
+ end
23
+
18
24
  def generate_xpath(data, vars)
19
25
  xpath = "[not(@id = 'locatine_magic_div')]"
20
26
  data.each_pair do |_depth, array|
@@ -35,16 +41,9 @@ module Locatine
35
41
  when 'text'
36
42
  "[contains(text(), '#{value}')]"
37
43
  when 'attribute'
38
- generate_xpath_part_from_attribute(hash, value)
44
+ "[contains(@#{hash['name']}, '#{value}')]"
39
45
  end
40
46
  end
41
-
42
- def generate_xpath_part_from_attribute(hash, value)
43
- full = '[@*'
44
- hash['name'].split('_')
45
- .each { |part| full += "[contains(name(), '#{part}')]" }
46
- full + "[contains(., '#{value}')]]"
47
- end
48
47
  end
49
48
  end
50
49
  end
@@ -0,0 +1,30 @@
1
+
2
+ function one_element(target){
3
+
4
+ let element = {};
5
+ let attribute = {};
6
+
7
+ element = {attrs: [], text: "", tag: ""};
8
+ if (target.childNodes){
9
+ for (let i = 0; i < target.childNodes.length; ++i){
10
+ if (target.childNodes[i].nodeType === 3){
11
+ element.text += target.childNodes[i].textContent;
12
+ }
13
+ }
14
+ } else {
15
+ element.text = target.textContent
16
+ }
17
+ element.tag = target.tagName.toLowerCase();
18
+ let atts = target.attributes;
19
+ if (atts) {
20
+ for (var k = 0, n = atts.length; k < n; k++){
21
+ att = atts[k];
22
+ attribute = {};
23
+ attribute[att.nodeName] = att.nodeValue;
24
+ element.attrs.push(attribute);
25
+ }
26
+ }
27
+ return element;
28
+ }
29
+ let x = one_element(arguments[0]);
30
+ return x;
@@ -0,0 +1,60 @@
1
+
2
+ function walk(elm) {
3
+ let node;
4
+
5
+ const tagName = elm.tagName.toLowerCase();
6
+ const array = Array.prototype.slice.call( document.getElementsByTagName(tagName) );
7
+ const index = array.indexOf(elm);
8
+ const relative = elm.getBoundingClientRect();
9
+
10
+ // init item
11
+ const item = {tag: tagName,
12
+ index: index,
13
+ style: getComputedStyle(elm).cssText,
14
+ text: "",
15
+ attrs: {},
16
+ coordinates: {top:0, bottom:0, left:0, right:0},
17
+ children: []};
18
+
19
+ // text for item
20
+ if (elm.childNodes) {
21
+ for (let z = 0; z < elm.childNodes.length; ++z){
22
+ if (elm.childNodes[z].nodeType === 3){
23
+ item.text += elm.childNodes[z].textContent;
24
+ }
25
+ }
26
+ } else {
27
+ item.text = elm.textContent
28
+ }
29
+
30
+ // attributes for item
31
+ atts = elm.attributes;
32
+ if (atts) {
33
+ for (var att, k = 0, n = atts.length; k < n; k++){
34
+ att = atts[k];
35
+ item.attrs[att.nodeName] = att.nodeValue;
36
+ }
37
+ }
38
+
39
+ item.coordinates.top = relative["top"] + window.scrollY;
40
+ item.coordinates.bottom = relative["bottom"] + window.scrollY;
41
+ item.coordinates.left = relative["left"] + window.scrollX;
42
+ item.coordinates.right = relative["right"] + window.scrollX;
43
+
44
+ // Handle child elements (not magic ones)
45
+ for (node = elm.firstChild; node; node = node.nextSibling) {
46
+ if (node.nodeType === 1) { // 1 == Element
47
+ if (node.attributes['id']) {
48
+ if (node.attributes['id'].value !== 'locatine_magic_div') {
49
+ item.children.push(walk(node))
50
+ }
51
+ } else {
52
+ item.children.push(walk(node))
53
+ }
54
+ }
55
+ }
56
+
57
+ return item;
58
+ }
59
+ let result = walk(document.body);
60
+ return [result];