locatine 0.02637 → 0.03050
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +266 -297
- data/bin/locatine-daemon.rb +4 -2
- data/lib/locatine.rb +16 -2
- data/lib/locatine/daemon.rb +45 -59
- data/lib/locatine/daemon_helpers/methods.rb +93 -0
- data/lib/locatine/element.rb +46 -0
- data/lib/locatine/error.rb +14 -0
- data/lib/locatine/logger.rb +73 -0
- data/lib/locatine/results.rb +124 -0
- data/lib/locatine/results_helpers/common.rb +61 -0
- data/lib/locatine/results_helpers/comparing.rb +51 -0
- data/lib/locatine/results_helpers/config.rb +48 -0
- data/lib/locatine/results_helpers/find_by_magic.rb +123 -0
- data/lib/locatine/results_helpers/guess.rb +47 -0
- data/lib/locatine/results_helpers/info_generator.rb +77 -0
- data/lib/locatine/{for_search → results_helpers}/xpath_generator.rb +19 -18
- data/lib/locatine/scripts/element.js +40 -0
- data/lib/locatine/scripts/page.js +54 -0
- data/lib/locatine/scripts/parent.js +6 -0
- data/lib/locatine/session.rb +147 -0
- data/lib/locatine/version.rb +4 -2
- metadata +42 -49
- data/lib/locatine/app/background.js +0 -8
- data/lib/locatine/app/content.css +0 -38
- data/lib/locatine/app/content.js +0 -152
- data/lib/locatine/app/devtools.html +0 -1
- data/lib/locatine/app/devtools.js +0 -3
- data/lib/locatine/app/manifest.json +0 -20
- data/lib/locatine/app/popup.css +0 -47
- data/lib/locatine/app/popup.html +0 -19
- data/lib/locatine/app/popup.js +0 -65
- data/lib/locatine/daemon_helpers.rb +0 -52
- data/lib/locatine/for_search.rb +0 -6
- data/lib/locatine/for_search/data_generate.rb +0 -67
- data/lib/locatine/for_search/data_logic.rb +0 -98
- data/lib/locatine/for_search/defaults.rb +0 -40
- data/lib/locatine/for_search/dialog_logic.rb +0 -107
- data/lib/locatine/for_search/element_selection.rb +0 -80
- data/lib/locatine/for_search/file_work.rb +0 -67
- data/lib/locatine/for_search/find_by_guess.rb +0 -67
- data/lib/locatine/for_search/find_by_locator.rb +0 -59
- data/lib/locatine/for_search/find_by_magic.rb +0 -65
- data/lib/locatine/for_search/find_logic.rb +0 -79
- data/lib/locatine/for_search/helpers.rb +0 -106
- data/lib/locatine/for_search/highlight.rb +0 -41
- data/lib/locatine/for_search/listening.rb +0 -48
- data/lib/locatine/for_search/merge.rb +0 -40
- data/lib/locatine/for_search/name_helper.rb +0 -51
- data/lib/locatine/for_search/page_work.rb +0 -126
- data/lib/locatine/for_search/public.rb +0 -179
- data/lib/locatine/for_search/saying.rb +0 -199
- data/lib/locatine/large_scripts/css.js +0 -21
- data/lib/locatine/large_scripts/dimensions.js +0 -17
- data/lib/locatine/large_scripts/element.js +0 -30
- data/lib/locatine/large_scripts/page.js +0 -60
- data/lib/locatine/scope.rb +0 -88
- data/lib/locatine/search.rb +0 -67
@@ -1,59 +0,0 @@
|
|
1
|
-
module Locatine
|
2
|
-
module ForSearch
|
3
|
-
##
|
4
|
-
# Methods related to the most simple search by ready locator.
|
5
|
-
module FindByLocator
|
6
|
-
private
|
7
|
-
|
8
|
-
def collection?(the_class)
|
9
|
-
case the_class.superclass.to_s
|
10
|
-
when 'Watir::Element'
|
11
|
-
false
|
12
|
-
when 'Watir::ElementCollection'
|
13
|
-
true
|
14
|
-
else
|
15
|
-
collection?(the_class.superclass)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
##
|
20
|
-
# Getting all the elements matching a locator
|
21
|
-
def find_by_locator(locator)
|
22
|
-
method = @type.nil? ? :elements : @type
|
23
|
-
begin
|
24
|
-
engine.element(locator).wait_until(timeout: @cold_time, &:exists?)
|
25
|
-
rescue StandardError
|
26
|
-
return nil
|
27
|
-
end
|
28
|
-
results = engine.send(method, locator)
|
29
|
-
return correct_method_detected(results) if collection?(results.class)
|
30
|
-
|
31
|
-
acceptable_method_detected(results, method, locator)
|
32
|
-
end
|
33
|
-
|
34
|
-
def correct_method_detected(results)
|
35
|
-
return nil if results.empty?
|
36
|
-
|
37
|
-
all = results.reject(&:stale?)
|
38
|
-
return all unless all.empty?
|
39
|
-
|
40
|
-
nil
|
41
|
-
end
|
42
|
-
|
43
|
-
def acceptable_method_detected(results, method, locator)
|
44
|
-
warn_acceptable_type(method)
|
45
|
-
the_class = results.class
|
46
|
-
results = engine.elements(locator)
|
47
|
-
.to_a
|
48
|
-
.select { |item| item.to_subtype.class == the_class }
|
49
|
-
correct_method_detected(results)
|
50
|
-
end
|
51
|
-
|
52
|
-
##
|
53
|
-
# Getting all the elements via stored information
|
54
|
-
def find_by_data(data, vars)
|
55
|
-
find_by_locator(xpath: generate_xpath(data, vars))
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
@@ -1,65 +0,0 @@
|
|
1
|
-
module Locatine
|
2
|
-
module ForSearch
|
3
|
-
##
|
4
|
-
# Logic for finding lost element
|
5
|
-
module FindByMagic
|
6
|
-
private
|
7
|
-
|
8
|
-
##
|
9
|
-
# Getting all the elements via black magic
|
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)
|
31
|
-
warn_element_lost(name, scope, data, vars)
|
32
|
-
all = select_from_page(page, data, vars)
|
33
|
-
raise_not_found(name, scope) if all.empty? && !@current_no_f
|
34
|
-
suggested = most_common_of(all).map do |element|
|
35
|
-
engine.elements(tag_name: element['tag'])[element['index'].to_i]
|
36
|
-
end
|
37
|
-
suggested
|
38
|
-
end
|
39
|
-
|
40
|
-
def similar_enough(data, attributes)
|
41
|
-
same = same_entries(data['0'], attributes, '0').length
|
42
|
-
all = data['0'].length
|
43
|
-
sameness = (same * 100) / all
|
44
|
-
sameness >= 100 - @current_t
|
45
|
-
end
|
46
|
-
|
47
|
-
def final_of_all(suggest, vars)
|
48
|
-
attributes = generate_data(suggest, vars) unless suggest.empty?
|
49
|
-
return suggest, attributes
|
50
|
-
end
|
51
|
-
|
52
|
-
def suggest_by_all(all, data, vars, name, scope)
|
53
|
-
suggest, attributes = final_of_all(all, vars)
|
54
|
-
ok = similar_enough(data, attributes) unless suggest.empty?
|
55
|
-
raise_not_similar(name, scope) if !ok && !@current_no_f
|
56
|
-
if ok
|
57
|
-
warn_lost_found(name, scope, attributes, vars)
|
58
|
-
return suggest, attributes
|
59
|
-
end
|
60
|
-
warn_not_found(name, scope)
|
61
|
-
return nil, nil
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
@@ -1,79 +0,0 @@
|
|
1
|
-
module Locatine
|
2
|
-
module ForSearch
|
3
|
-
##
|
4
|
-
# Methods explaining find logic
|
5
|
-
module FindLogic
|
6
|
-
private
|
7
|
-
|
8
|
-
def set_name(simple_name, name)
|
9
|
-
name ||= simple_name
|
10
|
-
raise_no_name unless name
|
11
|
-
name
|
12
|
-
end
|
13
|
-
|
14
|
-
def data_search(name, scope, vars, exact)
|
15
|
-
result = find_by_data(@data[scope][name], vars)
|
16
|
-
attributes = generate_data(result, vars) if result
|
17
|
-
if !result && !exact
|
18
|
-
@autolearn = true if @autolearn.nil?
|
19
|
-
@save = true
|
20
|
-
result, attributes = find_by_magic(name, scope,
|
21
|
-
@data[scope][name], vars)
|
22
|
-
end
|
23
|
-
return result, attributes
|
24
|
-
end
|
25
|
-
|
26
|
-
def core_search(name, scope, vars, exact)
|
27
|
-
if @data[scope][name].to_h != {}
|
28
|
-
result, attributes = data_search(name, scope, vars, exact)
|
29
|
-
end
|
30
|
-
return result, attributes
|
31
|
-
end
|
32
|
-
|
33
|
-
def full_search(name, scope, vars, locator, exact)
|
34
|
-
@save = @autolearn
|
35
|
-
result, attributes = search_steps(name, scope, vars, locator, exact)
|
36
|
-
raise_not_found(name, scope) if !result && !@current_no_f
|
37
|
-
store(attributes, scope, name) if result && (@save || @learn)
|
38
|
-
return result, attributes
|
39
|
-
end
|
40
|
-
|
41
|
-
def search_steps(name, scope, vars, locator, exact)
|
42
|
-
result, attributes = locator_search(locator, vars)
|
43
|
-
ok = result || ((locator != {}) && exact)
|
44
|
-
result, attributes = core_search(name, scope, vars, exact) unless ok
|
45
|
-
if @learn
|
46
|
-
answer = ask(scope, name, result, vars)
|
47
|
-
result = answer[:element]
|
48
|
-
attributes = answer[:attributes]
|
49
|
-
end
|
50
|
-
return result, attributes
|
51
|
-
end
|
52
|
-
|
53
|
-
def locator_search(locator, vars)
|
54
|
-
result = find_by_locator(locator) unless locator == {}
|
55
|
-
attributes = generate_data(result, vars) if result
|
56
|
-
warn_broken_locator(locator) if locator.to_h != {} && !result
|
57
|
-
return result, attributes
|
58
|
-
end
|
59
|
-
|
60
|
-
##
|
61
|
-
# Returning subtype of the only element of collection OR collection
|
62
|
-
#
|
63
|
-
# Params:
|
64
|
-
# +result+ must be Watir::HTMLElementCollection or Array
|
65
|
-
#
|
66
|
-
# +collection+ nil, true or false
|
67
|
-
def to_subtype(result, collection)
|
68
|
-
result = result.to_a
|
69
|
-
to_return = result.map(&:to_subtype)
|
70
|
-
case collection
|
71
|
-
when true
|
72
|
-
to_return
|
73
|
-
when false
|
74
|
-
to_return.first
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
@@ -1,106 +0,0 @@
|
|
1
|
-
module Locatine
|
2
|
-
module ForSearch
|
3
|
-
##
|
4
|
-
# Different methods to make life easier
|
5
|
-
module Helpers
|
6
|
-
private
|
7
|
-
|
8
|
-
def enforce(inject, *args)
|
9
|
-
inject = args.last.merge(inject) if args.last.class == Hash
|
10
|
-
ok = (args.first.class == String) && inject[:name].nil?
|
11
|
-
inject[:name] = args.first if ok
|
12
|
-
find(inject)
|
13
|
-
end
|
14
|
-
|
15
|
-
def engine
|
16
|
-
(@iframe || @browser)
|
17
|
-
end
|
18
|
-
|
19
|
-
def take_html
|
20
|
-
engine.locate
|
21
|
-
engine.html.gsub(/<div.*id="locatine_magic_div".*>/, '')
|
22
|
-
end
|
23
|
-
|
24
|
-
def time
|
25
|
-
t = Time.now
|
26
|
-
t.strftime('%F %T')
|
27
|
-
end
|
28
|
-
|
29
|
-
def fix_iframe
|
30
|
-
@iframe = @browser.iframe(@iframe.selector) if @iframe && @iframe.stale?
|
31
|
-
end
|
32
|
-
|
33
|
-
def set_env_for_search(look_in,
|
34
|
-
iframe,
|
35
|
-
tolerance,
|
36
|
-
no_fail,
|
37
|
-
trusted,
|
38
|
-
untrusted)
|
39
|
-
@type = look_in
|
40
|
-
@iframe = iframe
|
41
|
-
@current_t = tolerance || @tolerance
|
42
|
-
@current_no_f = no_fail || @no_fail
|
43
|
-
@trust_now = trusted || @trusted
|
44
|
-
@untrust_now = untrusted || @untrusted
|
45
|
-
end
|
46
|
-
|
47
|
-
def not_magic_div
|
48
|
-
"[not(@id = 'locatine_magic_div')]"
|
49
|
-
end
|
50
|
-
|
51
|
-
def push_hash(name, value, type)
|
52
|
-
{ 'name' => name,
|
53
|
-
'value' => value,
|
54
|
-
'type' => type }
|
55
|
-
end
|
56
|
-
|
57
|
-
def window_size
|
58
|
-
b_w = engine.execute_script('return window.innerWidth')
|
59
|
-
b_h = engine.execute_script('return window.innerHeight')
|
60
|
-
"#{b_w}x#{b_h}"
|
61
|
-
end
|
62
|
-
|
63
|
-
def visual?
|
64
|
-
@visual_search
|
65
|
-
end
|
66
|
-
|
67
|
-
def right_browser
|
68
|
-
Watir::Browser.new(:chrome, switches: ["--load-extension=#{HOME}/app"])
|
69
|
-
end
|
70
|
-
|
71
|
-
def css_text_to_hash(text)
|
72
|
-
almost_hash = []
|
73
|
-
array = text[0..-2].split('; ')
|
74
|
-
array.each do |item|
|
75
|
-
almost_hash.push item.split(': ')
|
76
|
-
end
|
77
|
-
almost_hash.to_h
|
78
|
-
end
|
79
|
-
|
80
|
-
def default_styles
|
81
|
-
css =
|
82
|
-
engine.execute_script("const dummy = document.createElement('dummy');
|
83
|
-
document.body.appendChild(dummy);
|
84
|
-
return getComputedStyle(dummy).cssText;")
|
85
|
-
css_text_to_hash(css)
|
86
|
-
end
|
87
|
-
|
88
|
-
def process_string(str, vars)
|
89
|
-
str = str.to_s
|
90
|
-
thevar = str.match(/\#{([^\#{]*)}/)[1] unless str.match(/\#{(.*)}/).nil?
|
91
|
-
return str unless thevar
|
92
|
-
|
93
|
-
value = vars[thevar.to_sym] || vars[thevar]
|
94
|
-
raise_no_var(thevar) unless value
|
95
|
-
process_string(str.gsub('#{' + thevar + '}', value.to_s), vars)
|
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
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
module Locatine
|
2
|
-
module ForSearch
|
3
|
-
##
|
4
|
-
# Locatine can highlight elements
|
5
|
-
module Highlight
|
6
|
-
private
|
7
|
-
|
8
|
-
##
|
9
|
-
# We can highlight an element
|
10
|
-
def highlight(element)
|
11
|
-
script = "arguments[0].setAttribute('locatineclass','foundbylocatine')"
|
12
|
-
engine.execute_script(script, element)
|
13
|
-
rescue StandardError
|
14
|
-
warn_cannot_highlight(element.selector)
|
15
|
-
end
|
16
|
-
|
17
|
-
##
|
18
|
-
# We can unhighlight an element
|
19
|
-
def unhighlight(element)
|
20
|
-
script = "arguments[0].removeAttribute('locatineclass')"
|
21
|
-
engine.execute_script(script, element)
|
22
|
-
rescue StandardError
|
23
|
-
false
|
24
|
-
# watir is not allowing to play with attributes of some elements
|
25
|
-
end
|
26
|
-
|
27
|
-
##
|
28
|
-
# We can highlight\unhighlight tons of elements at once
|
29
|
-
def mass_highlight_turn(mass, turn_on = true)
|
30
|
-
warn_much_highlight(mass.length) if turn_on && mass.length > 50
|
31
|
-
mass[0..49].each do |element|
|
32
|
-
if turn_on
|
33
|
-
highlight element
|
34
|
-
else
|
35
|
-
unhighlight element
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
module Locatine
|
2
|
-
module ForSearch
|
3
|
-
##
|
4
|
-
# Simple actions about communicating with chrome extension (and user)
|
5
|
-
module Listening
|
6
|
-
private
|
7
|
-
|
8
|
-
##
|
9
|
-
# Getting attribute of locatine div (way to communicate)
|
10
|
-
def get_from_app(what)
|
11
|
-
fix_iframe
|
12
|
-
result = engine.wd.execute_script(
|
13
|
-
%[if (document.getElementById('locatine_magic_div')) {
|
14
|
-
const magic_div = document.getElementById('locatine_magic_div');
|
15
|
-
return magic_div.getAttribute("#{what}")}]
|
16
|
-
)
|
17
|
-
fix_iframe
|
18
|
-
result
|
19
|
-
end
|
20
|
-
|
21
|
-
##
|
22
|
-
# Sending request to locatine app
|
23
|
-
def start_listening(scope, name)
|
24
|
-
send_to_app('locatinestyle', 'set_false', @browser) if @iframe
|
25
|
-
send_to_app('locatinestyle', 'set_true')
|
26
|
-
send_to_app('locatineconfirmed', 'ok')
|
27
|
-
send_selecting(name, scope)
|
28
|
-
sleep 0.5
|
29
|
-
end
|
30
|
-
|
31
|
-
def tag_index
|
32
|
-
tag = get_from_app('tag')
|
33
|
-
tag = tag.downcase unless tag.nil?
|
34
|
-
index = get_from_app('index').to_i
|
35
|
-
return tag, index
|
36
|
-
end
|
37
|
-
|
38
|
-
def response_action(element)
|
39
|
-
send_to_app('locatineconfirmed', 'ok')
|
40
|
-
send_has_response
|
41
|
-
mass_highlight_turn(element, false) if element
|
42
|
-
send_to_app('locatinestyle', 'set_false')
|
43
|
-
send_to_app('locatinestyle', 'ok', @browser) if @iframe
|
44
|
-
sleep 1
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
module Locatine
|
2
|
-
module ForSearch
|
3
|
-
##
|
4
|
-
# Getting commons of two piles of elements data. To find all similar to them
|
5
|
-
module Merge
|
6
|
-
private
|
7
|
-
|
8
|
-
def select_same(where, hash)
|
9
|
-
where.select do |item|
|
10
|
-
(item['name'] == hash['name']) &&
|
11
|
-
(item['value'] == hash['value']) &&
|
12
|
-
(item['type'] == hash['type'])
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def same_entries(array, second, depth, stability_up = false)
|
17
|
-
result = []
|
18
|
-
array.each do |hash|
|
19
|
-
item = second[depth]
|
20
|
-
to_add = item.nil? ? [] : select_same(second[depth], hash)
|
21
|
-
max = max_stability(second[depth]).to_i + 1
|
22
|
-
to_add = stability_bump(to_add, hash, max) if stability_up
|
23
|
-
result += to_add
|
24
|
-
end
|
25
|
-
result
|
26
|
-
end
|
27
|
-
|
28
|
-
##
|
29
|
-
# Merging data of two elements (new data is to find both)
|
30
|
-
def get_commons(first, second)
|
31
|
-
second = first if second == {}
|
32
|
-
final = Hash.new { |hash, key| hash[key] = [] }
|
33
|
-
first.each_pair do |depth, array|
|
34
|
-
final[depth] = same_entries(array, second, depth)
|
35
|
-
end
|
36
|
-
final
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|