locatine 0.02247 → 0.02542

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 (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
@@ -5,27 +5,49 @@ module Locatine
5
5
  module DataLogic
6
6
  private
7
7
 
8
- def get_dynamic_attributes(element, vars)
8
+ def get_dynamic_attributes(data, vars)
9
9
  attrs = []
10
- get_attributes(element).each do |hash|
11
- value = vars[hash['name'].to_sym]
12
- hash['value'].gsub!(value, "\#{#{hash['name']}}") if value
13
- attrs.push hash
10
+ data.each do |hash|
11
+ hash.each_pair do |key, full_value|
12
+ full_value.split(' ').each do |value|
13
+ next if key == 'locatineclass'
14
+
15
+ attrs.push some_attribute(value, key, vars)
16
+ end
17
+ end
14
18
  end
15
19
  attrs
16
20
  end
17
21
 
22
+ def some_attribute(value, key, vars)
23
+ replace = vars[key.to_sym]
24
+ push_hash(key,
25
+ replace ? value.gsub(replace, "\#{#{key}}") : value,
26
+ 'attribute')
27
+ end
28
+
29
+ def get_element_raw(element)
30
+ element = element.to_subtype.wd.wd if element.tag_name == 'iframe'
31
+ script = File.read("#{HOME}/large_scripts/element.js")
32
+ engine.execute_script(script, element)
33
+ end
34
+
18
35
  ##
19
36
  # Generating array of hashes representing data of the element
20
37
  def get_element_info(element, vars, depth)
21
- attrs = get_dynamic_attributes(element, vars)
22
- attrs.push get_dynamic_tag(element, vars)
23
- attrs += get_dynamic_text(element, vars)
24
- attrs += get_dynamic_css(element, vars) if depth.to_i.zero? && visual?
25
- attrs.push get_dimensions(element, vars) if depth.to_i.zero? && visual?
38
+ data = get_element_raw(element)
39
+ attrs = get_dynamic_attributes(data['attrs'], vars)
40
+ attrs.push get_dynamic_tag(data['tag'], vars)
41
+ attrs += get_dynamic_text(data['text'], vars)
42
+ attrs += get_dynamic_css(element, vars) if looking?(depth)
43
+ attrs.push get_dimensions(element) if looking?(depth)
26
44
  attrs
27
45
  end
28
46
 
47
+ def looking?(depth)
48
+ depth.to_i.zero? && visual?
49
+ end
50
+
29
51
  ##
30
52
  # Generating data for group of elements
31
53
  def generate_data(result, vars)
@@ -0,0 +1,40 @@
1
+ module Locatine
2
+ module ForSearch
3
+ ##
4
+ # Default settings for search are living here
5
+ module Defaults
6
+ private
7
+
8
+ def default_init_config
9
+ { json: './Locatine_files/default.json', depth: 3,
10
+ browser: nil, learn: ENV['LEARN'].nil? ? false : true,
11
+ stability_limit: 10, scope: 'Default',
12
+ tolerance: 67, visual_search: false,
13
+ no_fail: false, trusted: [],
14
+ untrusted: [], autolearn: nil }
15
+ end
16
+
17
+ def import_browser(browser)
18
+ selenium = browser.class.superclass == Selenium::WebDriver::Driver
19
+ b = right_browser unless browser
20
+ b = browser if browser.class == Watir::Browser
21
+ b = Watir::Browser.new(browser) if selenium
22
+ @browser = b
23
+ @default_styles = default_styles.to_a
24
+ end
25
+
26
+ def import_file(json)
27
+ @json = json
28
+ @folder = File.dirname(@json)
29
+ @name = File.basename(@json)
30
+ @data = read_create
31
+ end
32
+
33
+ def import_config(config)
34
+ config.each_pair do |key, value|
35
+ instance_variable_set("@#{key}", value)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -20,6 +20,7 @@ module Locatine
20
20
 
21
21
  def what_was_selected(element, attributes, vars)
22
22
  tag, index = tag_index
23
+ # TODO: ELEMENT COULD BE ALREADY LOST HERE. WE HAVE BUG AROUND THAT CODE
23
24
  send_to_app('locatineconfirmed', 'ok')
24
25
  mass_highlight_turn(element, false) if element
25
26
  element, attributes = working_on_selected(tag, index, vars, attributes)
@@ -52,18 +53,40 @@ module Locatine
52
53
  return els, attrs
53
54
  end
54
55
 
56
+ # get_from_app('locatineconfirmed') 'selected' or 'declined' or 'true'
57
+ # or 'abort' or 'new' or anything else (ok or false)
55
58
  def listening(els, attrs, vars, name, scope)
56
- until %w[true abort].include?(get_from_app('locatineconfirmed'))
57
- sleep(0.1)
58
- els, attrs = user_selection(els, attrs, vars, name, scope)
59
+ sleep(0.1)
60
+ result = hearing
61
+ els, attrs = understanding(els, attrs, vars, name, scope, result)
62
+ if (!result[:done] || !els) && !result[:abort]
63
+ return listening(els, attrs, vars, name, scope)
59
64
  end
60
- result = get_from_app('locatineconfirmed')
61
- return els, attrs if els && result != 'abort'
62
65
 
63
- els, attrs = decline(els, name, scope)
64
- return els, attrs if result == 'abort'
66
+ return els, attrs
67
+ end
68
+
69
+ def understanding(els, attrs, vars, name, scope, result)
70
+ if result[:selected]
71
+ els, attrs = user_selection(els, attrs, vars, name, scope)
72
+ else
73
+ start_listening(scope, name) if result[:new]
74
+ els, attrs = decline(els, name, scope) if result[:declined]
75
+ end
76
+ return els, attrs
77
+ end
65
78
 
66
- listening(els, attrs, vars, name, scope)
79
+ def hearing
80
+ answer = {}
81
+ result = get_from_app('locatineconfirmed')
82
+ answer[:selected] = ((result == 'selected') || (result == 'true'))
83
+ answer[:new] = result == 'new'
84
+ answer[:done] = result == 'true'
85
+ answer[:abort] = result == 'abort'
86
+ answer[:declined] = ((result == 'new') ||
87
+ (result == 'declined') ||
88
+ (result == 'abort'))
89
+ answer
67
90
  end
68
91
 
69
92
  ##
@@ -19,7 +19,7 @@ module Locatine
19
19
  return element, attrs
20
20
  end
21
21
 
22
- def negative_needed(element, vars, old_depth)
22
+ def negative_need(element, vars, old_depth)
23
23
  @depth = old_depth
24
24
  warn_no_negatives
25
25
  generate_data([element], vars).to_h
@@ -27,10 +27,10 @@ module Locatine
27
27
 
28
28
  def complex_attrs(element, vars, old_depth = @depth)
29
29
  attrs = get_family_info(element, vars).to_h
30
- return negative_needed(element, vars, old_depth) if attrs.length < @depth
30
+ return negative_need(element, vars, old_depth) if attrs.length < @depth
31
31
 
32
32
  if find_by_data(attrs, vars).length > 1
33
- @depth +=1
33
+ @depth += 1
34
34
  return complex_attrs(element, vars, old_depth)
35
35
  end
36
36
  @depth = old_depth
@@ -54,7 +54,7 @@ module Locatine
54
54
  def working_on_selected(tag, index, vars, attributes)
55
55
  send_working(tag, index)
56
56
  element, new_attributes = selected_element(tag, index, vars, attributes)
57
- warn_dropping(tag, index) unless element
57
+ warn_dropping(tag, index) unless element[0].locate.located?
58
58
 
59
59
  warn_type(tag) if @type && !element
60
60
 
@@ -17,13 +17,6 @@ module Locatine
17
17
  hash.merge(JSON.parse(File.read(@json))['data'])
18
18
  end
19
19
 
20
- def import_file(json)
21
- @json = json
22
- @folder = File.dirname(@json)
23
- @name = File.basename(@json)
24
- @data = read_create
25
- end
26
-
27
20
  def create_json_file
28
21
  f = File.new(@json, 'w')
29
22
  f.puts '{"data" : {}}'
@@ -43,11 +36,18 @@ module Locatine
43
36
  final
44
37
  end
45
38
 
46
- def stability_bump(to_add, hash)
39
+ def stability_value(name, max, init = 0)
40
+ result = @trust_now.include?(name) ? max.to_s : (init.to_i + 1).to_s
41
+ result = '0' if @untrust_now.include?(name)
42
+ result
43
+ end
44
+
45
+ def stability_bump(to_add, hash, max)
47
46
  if to_add.empty? # new ones
48
- hash['stability'] = '1'
47
+ hash['stability'] = stability_value(hash['name'], max)
49
48
  elsif to_add[0]['stability'].to_i < @stability_limit # old ones
50
- to_add[0]['stability'] = (to_add[0]['stability'].to_i + 1).to_s
49
+ to_add[0]['stability'] = stability_value(to_add[0]['name'],
50
+ max, to_add[0]['stability'])
51
51
  end
52
52
  to_add.empty? ? [hash] : to_add
53
53
  end
@@ -8,57 +8,59 @@ module Locatine
8
8
  module FindByGuess
9
9
  private
10
10
 
11
- def main_guess(name)
11
+ def all_similar(name, page, vars)
12
12
  all = []
13
- name.split(' ').each do |part|
14
- all += guess_by_part(part)
13
+ array = generate_hash_array(name)
14
+ array.each do |hash|
15
+ all += catch(page, hash, vars, 0)
15
16
  end
16
17
  all
17
18
  end
18
19
 
19
- def guess_by_part(part)
20
- all = []
21
- tag_xpath = "//#{part}#{not_magic_div}"
22
- text_xpath = "//*[contains(text(),'#{part}')]#{not_magic_div}"
23
- attr_xpath = "//*[@*[contains(., '#{part}')]]#{not_magic_div}"
24
- all += find_by_locator(xpath: tag_xpath).to_a
25
- all += find_by_locator(xpath: text_xpath).to_a
26
- all += find_by_locator(xpath: attr_xpath).to_a
27
- all
20
+ def all_suggested(all, name, scope)
21
+ if all.empty?
22
+ send_no_guess(name, scope)
23
+ return nil
24
+ end
25
+ suggested = most_common_of(all).map do |element|
26
+ engine.elements(tag_name: element['tag'])[element['index'].to_i]
27
+ end
28
+ suggested
28
29
  end
29
30
 
30
- def full_guess(all, vars, name)
31
- max = all.count(all.max_by { |i| all.count(i) })
32
- if max >= name.split(' ').length
33
- guess = (all.select { |i| all.count(i) == max }).uniq
34
- guess_data = generate_data(guess, vars)
35
- found_by_data = find_by_data(guess_data, vars)
36
- end
37
- return found_by_data, guess_data.to_h
31
+ def main_guess(name, scope, page, vars)
32
+ all = all_similar(name, page, vars)
33
+ all_suggested(all, name, scope)
38
34
  end
39
35
 
40
- def check_guess(all, vars, name, scope)
41
- guess, guess_data = full_guess(all, vars, name)
42
- if guess.nil? || (engine.elements.length / guess.length <= 4)
43
- send_no_guess(name, scope)
44
- guess = nil
45
- guess_data = {}
46
- else
47
- send_has_guess(guess.length, name, scope)
36
+ def guessing(name, scope, page, vars)
37
+ suggested = main_guess(name, scope, page, vars)
38
+ suggest, attributes = final_of_all(suggested, vars) if suggested
39
+ return suggest, attributes
40
+ end
41
+
42
+ def find_by_guess(scope, name, vars, iteration = 0)
43
+ html = take_html
44
+ page = take_dom
45
+ suggest, attributes = guessing(name, scope, page, vars)
46
+ if html != take_html && iteration < 5
47
+ return find_by_guess(scope, name, vars, iteration + 1)
48
48
  end
49
- return guess, guess_data
49
+
50
+ warn_highly_unstable if iteration == 5
51
+ send_has_guess(name, scope) if suggest
52
+ return suggest, attributes.to_h
50
53
  end
51
54
 
52
- def find_by_guess(scope, name, vars)
53
- @cold_time = 0
54
- all = main_guess(name)
55
- if all.empty?
56
- send_no_guess(name, scope)
57
- else
58
- guess, guess_data = check_guess(all, vars, name, scope)
55
+ def generate_hash_array(name)
56
+ array = []
57
+ name.split(' ').each do |part|
58
+ array.push push_hash(part, '', 'attribute')
59
+ array.push push_hash('', part, 'attribute')
60
+ array.push push_hash('text', part, 'text')
61
+ array.push push_hash('tag', part, 'tag')
59
62
  end
60
- @cold_time = nil
61
- return guess, guess_data.to_h
63
+ array
62
64
  end
63
65
  end
64
66
  end
@@ -49,36 +49,6 @@ module Locatine
49
49
  correct_method_detected(results)
50
50
  end
51
51
 
52
- ##
53
- # Getting elements by tag
54
- def find_by_tag(hash, vars, depth = 0)
55
- correction = '//*' if depth.to_i > 0
56
- xpath = "//*[self::#{process_string(hash['value'], vars)}]"
57
- find_by_locator(xpath: "#{xpath}#{correction}#{not_magic_div}")
58
- end
59
-
60
- ##
61
- # Getting elements by text
62
- def find_by_text(hash, vars, depth = 0)
63
- correction = '//*' if depth.to_i > 0
64
- value = process_string(hash['value'], vars)
65
- xpath = "//*[contains(text(), '#{value}')]"
66
- find_by_locator(xpath: "#{xpath}#{correction}#{not_magic_div}")
67
- end
68
-
69
- ##
70
- # Getting elements by attribute
71
- def find_by_attribute(hash, vars, depth = 0)
72
- correction = '//*' if depth.to_i > 0
73
- full_part = '//*[@*'
74
- hash['name'].split('_').each do |part|
75
- full_part += "[contains(name(), '#{part}')]"
76
- end
77
- value = process_string(hash['value'], vars)
78
- xpath = full_part + "[contains(., '#{value}')]]"
79
- find_by_locator(xpath: "#{xpath}#{correction}#{not_magic_div}")
80
- end
81
-
82
52
  ##
83
53
  # Getting all the elements via stored information
84
54
  def find_by_data(data, vars)
@@ -7,13 +7,34 @@ module Locatine
7
7
 
8
8
  ##
9
9
  # Getting all the elements via black magic
10
- def find_by_magic(name, scope, data, vars)
10
+ def find_by_magic(name, scope, data, vars, iteration = 0)
11
+ html = take_html
12
+ page = take_dom
13
+ suggested = magic_elements(name, scope, data, vars, page)
14
+ warn_unstable if page_changed?(page, html)
15
+ if html != take_html && iteration < 5
16
+ return find_by_magic(name, scope, data, vars, iteration + 1)
17
+ end
18
+
19
+ warn_highly_unstable if iteration == 5
20
+ suggest_by_all(suggested, data, vars, name, scope)
21
+ end
22
+
23
+ def page_changed?(page, html)
24
+ html != take_html || page != take_dom
25
+ end
26
+
27
+ ##
28
+ # We are taking every element that look at least a bit similar to one we
29
+ # are looking for
30
+ def magic_elements(name, scope, data, vars, page)
11
31
  warn_element_lost(name, scope)
12
- @cold_time = 0
13
- all = all_options(data, vars)
14
- @cold_time = nil
32
+ all = select_from_page(page, data, vars)
15
33
  raise_not_found(name, scope) if all.empty? && !@current_no_f
16
- suggest_by_all(all, data, vars, name, scope)
34
+ suggested = most_common_of(all).map do |element|
35
+ engine.elements(tag_name: element['tag'])[element['index'].to_i]
36
+ end
37
+ suggested
17
38
  end
18
39
 
19
40
  def similar_enough(data, attributes)
@@ -23,16 +44,14 @@ module Locatine
23
44
  sameness >= 100 - @current_t
24
45
  end
25
46
 
26
- def best_of_all(all, vars)
27
- max = all.count(all.max_by { |i| all.count(i) })
28
- suggest = (all.select { |i| all.count(i) == max }).uniq unless max.zero?
29
- attributes = generate_data(suggest, vars) unless suggest.nil?
47
+ def final_of_all(suggest, vars)
48
+ attributes = generate_data(suggest, vars) unless suggest.empty?
30
49
  return suggest, attributes
31
50
  end
32
51
 
33
52
  def suggest_by_all(all, data, vars, name, scope)
34
- suggest, attributes = best_of_all(all, vars)
35
- ok = similar_enough(data, attributes) unless suggest.nil?
53
+ suggest, attributes = final_of_all(all, vars)
54
+ ok = similar_enough(data, attributes) unless suggest.empty?
36
55
  raise_not_similar(name, scope) if !ok && !@current_no_f
37
56
  if ok
38
57
  warn_lost_found(name, scope)
@@ -41,76 +60,6 @@ module Locatine
41
60
  warn_not_found(name, scope)
42
61
  return nil, nil
43
62
  end
44
-
45
- def all_options(data, vars)
46
- all = []
47
- data.each_pair do |depth, array|
48
- get_trusted(array).each do |hash|
49
- all += one_option_array(hash, vars, depth).to_a
50
- end
51
- end
52
- all += full_find_by_css(data, vars) if visual?
53
- all += find_by_dimensions(data, vars) if visual?
54
- all
55
- end
56
-
57
- def min_max_by_size(middle, size)
58
- min = middle - (size.to_i * (200 - @current_t)) / 200
59
- max = middle + (size.to_i * (200 - @current_t)) / 200
60
- return min, max
61
- end
62
-
63
- def middle(sizes)
64
- x = sizes[0].to_i + (sizes[2].to_i / 2)
65
- y = sizes[1].to_i + (sizes[3].to_i / 2)
66
- return x, y
67
- end
68
-
69
- def dimension_search_field(sizes)
70
- x, y = middle(sizes)
71
- x_min, x_max = min_max_by_size(x, sizes[2])
72
- y_min, y_max = min_max_by_size(y, sizes[3])
73
- return x_min, x_max, y_min, y_max
74
- end
75
-
76
- def retrieve_mesures(data)
77
- size = window_size
78
- dimensions = data['0'].map do |i|
79
- i if (i['type'] == 'dimensions') && (i['name'] == size)
80
- end
81
- dimensions.compact
82
- end
83
-
84
- def sizez_from_dimensions(dimensions, vars)
85
- result = []
86
- dimensions.first['value'].split('*').each do |value|
87
- result.push(process_string(value, vars))
88
- end
89
- result
90
- end
91
-
92
- def find_by_dimensions(data, vars)
93
- dimensions = retrieve_mesures(data)
94
- if !dimensions.empty?
95
- sizes = sizez_from_dimensions(dimensions, vars)
96
- xmi, xma, ymi, yma = dimension_search_field(sizes)
97
- script = File.read("#{HOME}/large_scripts/dimensions.js")
98
- engine.execute_script(script, xmi, xma, ymi, yma).compact
99
- else
100
- []
101
- end
102
- end
103
-
104
- def one_option_array(hash, vars, depth)
105
- case hash['type']
106
- when 'tag'
107
- find_by_tag(hash, vars, depth)
108
- when 'text'
109
- find_by_text(hash, vars, depth)
110
- when 'attribute'
111
- find_by_attribute(hash, vars, depth)
112
- end
113
- end
114
63
  end
115
64
  end
116
65
  end