locatine 0.01084 → 0.01100
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/locatine/app/background.js +2 -2
- data/lib/locatine/app/content.js +14 -5
- data/lib/locatine/app/manifest.json +1 -1
- data/lib/locatine/data_generate.rb +91 -0
- data/lib/locatine/dialog_actions.rb +71 -0
- data/lib/locatine/dialog_logic.rb +94 -0
- data/lib/locatine/file_work.rb +54 -0
- data/lib/locatine/find_by_guess.rb +62 -0
- data/lib/locatine/find_by_locator.rb +98 -0
- data/lib/locatine/find_by_magic.rb +43 -0
- data/lib/locatine/find_logic.rb +61 -0
- data/lib/locatine/helpers.rb +43 -0
- data/lib/locatine/highlight.rb +41 -0
- data/lib/locatine/merge.rb +36 -0
- data/lib/locatine/public.rb +103 -0
- data/lib/locatine/search.rb +40 -587
- data/lib/locatine/version.rb +8 -3
- data/lib/locatine/xpath_generator.rb +47 -0
- data/lib/locatine.rb +2 -2
- metadata +15 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ca422d8fbe87acacb5b19639d98c509fbdb04a2
|
4
|
+
data.tar.gz: b81058cdc3aa26f76a5521312de44f11f5b299bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3435a0ae9f55f95e2419d47f37e066c9af65c4cc630b222229a5057aec35060a4a3d24504a5bb1eed21bee6c2c59611b692eb14dec12a945648da1f75bb8fd0
|
7
|
+
data.tar.gz: 96236870dffc079e8fa474b8fab27d5276c57ddbb1655ff0b8edf598a7eea1e45476e8cfb41b27e6feb16fc992c0f66bca6f80a84afdfe45df623ae75f9b9cc2
|
data/lib/locatine/app/content.js
CHANGED
@@ -29,6 +29,10 @@ async function refreshData(){
|
|
29
29
|
await set_value('magic_div', true);
|
30
30
|
magicDiv.setAttribute("locatinestyle", "true");
|
31
31
|
};
|
32
|
+
if (magicDiv.getAttribute("locatinestyle") === "set_false") {
|
33
|
+
await set_value('magic_div', false);
|
34
|
+
magicDiv.setAttribute("locatinestyle", "false");
|
35
|
+
};
|
32
36
|
if (magicDiv.getAttribute("locatinetitle") != "ok") {
|
33
37
|
await set_value('locatine_title', magicDiv.getAttribute("locatinetitle"));
|
34
38
|
await set_value('locatine_hint', magicDiv.getAttribute("locatinehint"));
|
@@ -43,7 +47,6 @@ async function refreshData(){
|
|
43
47
|
magicDiv.removeAttribute("tag");
|
44
48
|
magicDiv.removeAttribute("index");
|
45
49
|
await set_value("locatine_confirm", false);
|
46
|
-
await set_value('magic_div', false);
|
47
50
|
}
|
48
51
|
const confirmed = await get_value('locatine_confirm');
|
49
52
|
magicDiv.setAttribute("locatineconfirmed", confirmed);
|
@@ -52,21 +55,27 @@ async function refreshData(){
|
|
52
55
|
magic_cover.onclick = function(e) {locatine_magic_click(e)};
|
53
56
|
};
|
54
57
|
|
55
|
-
function getSelected(value){
|
58
|
+
async function getSelected(value){
|
59
|
+
const magic_div = document.getElementById("locatine_magic_div");
|
56
60
|
const tagName = value.tagName;
|
57
61
|
const array = Array.prototype.slice.call( document.getElementsByTagName(tagName) );
|
58
62
|
const index = array.indexOf(value);
|
59
|
-
document.
|
60
|
-
|
63
|
+
if (document.locatine_selected != value) {
|
64
|
+
document.locatine_selected = value;
|
65
|
+
await set_value("locatine_confirm", "selected");
|
66
|
+
magic_div.setAttribute("tag", tagName);
|
67
|
+
magic_div.setAttribute("index", index);
|
68
|
+
}
|
61
69
|
};
|
62
70
|
|
63
|
-
function locatine_magic_click(e) {
|
71
|
+
async function locatine_magic_click(e) {
|
64
72
|
document.getElementById("locatine_magic_div").setAttribute("locatinestyle", "blocked");
|
65
73
|
const value = document.elementFromPoint(e.clientX, e.clientY);
|
66
74
|
document.getElementById("locatine_magic_div").setAttribute("locatinestyle", "true");
|
67
75
|
const tagName = value.tagName;
|
68
76
|
const array = Array.prototype.slice.call( document.getElementsByTagName(tagName) );
|
69
77
|
const index = array.indexOf(value);
|
78
|
+
await set_value("locatine_confirm", "selected");
|
70
79
|
document.getElementById("locatine_magic_div").setAttribute("TAG", tagName);
|
71
80
|
document.getElementById("locatine_magic_div").setAttribute("INDEX", index);
|
72
81
|
};
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Locatine
|
2
|
+
##
|
3
|
+
# Generating locatine json info from element itself
|
4
|
+
module DataGenerate
|
5
|
+
private
|
6
|
+
|
7
|
+
def get_dynamic_attributes(element, vars)
|
8
|
+
attrs = []
|
9
|
+
get_attributes(element).each do |hash|
|
10
|
+
if vars[hash['name'].to_sym]
|
11
|
+
hash['value'].gsub!(vars[hash['name'].to_sym], "\#{#{hash['name']}}")
|
12
|
+
end
|
13
|
+
attrs.push hash
|
14
|
+
end
|
15
|
+
attrs
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_dynamic_tag(element, vars)
|
19
|
+
tag = element.tag_name
|
20
|
+
tag = "\#{tag}" if vars[:tag] == tag
|
21
|
+
{ 'name' => 'tag', 'value' => tag, 'type' => 'tag' }
|
22
|
+
end
|
23
|
+
|
24
|
+
def real_text_of(element)
|
25
|
+
element.text == element.inner_html ? element.text : ''
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_dynamic_text(element, vars)
|
29
|
+
attrs = []
|
30
|
+
real_text_of(element).split(' ').each do |word|
|
31
|
+
final_word = if !vars[:text].to_s.strip.empty?
|
32
|
+
word.gsub(vars[:text].to_s, "\#{text}")
|
33
|
+
else
|
34
|
+
word
|
35
|
+
end
|
36
|
+
attrs.push('name' => 'text', 'value' => final_word, 'type' => 'text')
|
37
|
+
end
|
38
|
+
attrs
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Generating array of hashes representing data of the element
|
43
|
+
def get_element_info(element, vars)
|
44
|
+
attrs = get_dynamic_attributes(element, vars)
|
45
|
+
attrs.push get_dynamic_tag(element, vars)
|
46
|
+
attrs += get_dynamic_text(element, vars)
|
47
|
+
attrs
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Generating data for group of elements
|
52
|
+
def generate_data(result, vars)
|
53
|
+
family = {}
|
54
|
+
result.each do |item|
|
55
|
+
family = get_commons(get_family_info(item, vars), family)
|
56
|
+
end
|
57
|
+
family
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Getting element\\parents information
|
62
|
+
def get_family_info(element, vars)
|
63
|
+
current_depth = 0
|
64
|
+
attributes = {}
|
65
|
+
while current_depth != @depth
|
66
|
+
attributes[current_depth.to_s] = get_element_info(element, vars)
|
67
|
+
current_depth += 1
|
68
|
+
element = element.parent
|
69
|
+
# Sometimes watir is not returning a valid parent that's why:
|
70
|
+
current_depth = @depth unless element.parent.exists?
|
71
|
+
end
|
72
|
+
attributes
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Collecting attributes of the element
|
77
|
+
def get_attributes(element)
|
78
|
+
attributes = element.attributes
|
79
|
+
array = []
|
80
|
+
attributes.each_pair do |name, value|
|
81
|
+
next if name.to_s == 'locatineclass'
|
82
|
+
|
83
|
+
value.split(' ').uniq.each do |part|
|
84
|
+
array.push('name' => name.to_s, 'type' => 'attribute',
|
85
|
+
'value' => part)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
array
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Locatine
|
2
|
+
##
|
3
|
+
# Simple actions about communicating with chrome extension (and user)
|
4
|
+
module DialogActions
|
5
|
+
private
|
6
|
+
|
7
|
+
##
|
8
|
+
# Setting attribute of locatine div (way to communicate)
|
9
|
+
def send_to_app(what, value, bro = engine)
|
10
|
+
fix_iframe
|
11
|
+
bro.wd.execute_script(
|
12
|
+
%[if (document.getElementById('locatine_magic_div')){
|
13
|
+
const magic_div = document.getElementById('locatine_magic_div');
|
14
|
+
return magic_div.setAttribute("#{what}", "#{value}")}]
|
15
|
+
)
|
16
|
+
fix_iframe
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# Getting attribute of locatine div (way to communicate)
|
21
|
+
def get_from_app(what)
|
22
|
+
fix_iframe
|
23
|
+
result = engine.wd.execute_script(
|
24
|
+
%[if (document.getElementById('locatine_magic_div')) {
|
25
|
+
const magic_div = document.getElementById('locatine_magic_div');
|
26
|
+
return magic_div.getAttribute("#{what}")}]
|
27
|
+
)
|
28
|
+
fix_iframe
|
29
|
+
result
|
30
|
+
end
|
31
|
+
|
32
|
+
def fix_iframe
|
33
|
+
@iframe = @browser.iframe(@iframe.selector) if @iframe
|
34
|
+
end
|
35
|
+
|
36
|
+
def push_title(text)
|
37
|
+
puts text
|
38
|
+
send_to_app('locatinetitle', text)
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Sending request to locatine app
|
43
|
+
def start_listening(_scope, _name)
|
44
|
+
send_to_app('locatinestyle', 'blocked', @browser) if @iframe
|
45
|
+
send_to_app('locatinehint', 'Toggle single//collection mode button if '\
|
46
|
+
'you need. If you want to do some actions on the page toggle Locatine'\
|
47
|
+
' waiting button. You also can select element on devtools -> Elements.'\
|
48
|
+
' Do not forget to confirm your selection.')
|
49
|
+
send_to_app('locatinestyle', 'set_true')
|
50
|
+
sleep 0.5
|
51
|
+
end
|
52
|
+
|
53
|
+
def tag_index
|
54
|
+
tag = get_from_app('tag')
|
55
|
+
tag = tag.downcase unless tag.nil?
|
56
|
+
index = get_from_app('index').to_i
|
57
|
+
return tag, index
|
58
|
+
end
|
59
|
+
|
60
|
+
def response_action(element)
|
61
|
+
send_to_app('locatineconfirmed', 'ok')
|
62
|
+
send_to_app('locatinetitle', 'Right now you are defining nothing. '\
|
63
|
+
'So no button will work')
|
64
|
+
send_to_app('locatinehint', 'Place for a smart hint here')
|
65
|
+
mass_highlight_turn(element, false)
|
66
|
+
send_to_app('locatinestyle', 'set_false')
|
67
|
+
send_to_app('locatinestyle', 'ok', @browser) if @iframe
|
68
|
+
sleep 0.5
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Locatine
|
2
|
+
##
|
3
|
+
# Logic of recieving element selected by user
|
4
|
+
module DialogLogic
|
5
|
+
private
|
6
|
+
|
7
|
+
def suggest_element(element, vars, name, scope)
|
8
|
+
attributes = {}
|
9
|
+
if !element.nil?
|
10
|
+
attributes = generate_data(element, vars)
|
11
|
+
push_title("#{element.length} elements found as #{name} in #{scope}.")
|
12
|
+
elsif name.length >= 5
|
13
|
+
push_title("Locatine is trying to guess what is #{name} in #{scope}.")
|
14
|
+
element, attributes = find_by_guess(scope, name, vars)
|
15
|
+
end
|
16
|
+
mass_highlight_turn(element) if element
|
17
|
+
return element, attributes
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_selected_attributes(new_attributes, attributes)
|
21
|
+
if get_from_app('locatinecollection') == 'true'
|
22
|
+
get_commons(new_attributes, attributes.to_h)
|
23
|
+
else
|
24
|
+
new_attributes
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def selected_element_attributes(tag, index, vars)
|
29
|
+
generate_data([engine.elements(tag_name: tag)[index]], vars).to_h
|
30
|
+
end
|
31
|
+
|
32
|
+
def selected_element(tag, index, vars, attributes)
|
33
|
+
new_attributes = selected_element_attributes(tag, index, vars)
|
34
|
+
new_attributes = add_selected_attributes(new_attributes, attributes)
|
35
|
+
element = find_by_data(new_attributes, vars)
|
36
|
+
return element, new_attributes
|
37
|
+
end
|
38
|
+
|
39
|
+
def working_on_selected(tag, index, vars, attributes)
|
40
|
+
push_title "You've selected //#{tag}[#{index}]. Wait while Locatine works"
|
41
|
+
element, new_attributes = selected_element(tag, index, vars, attributes)
|
42
|
+
warn 'Cannot proceed with selected. Dropping it.' unless element
|
43
|
+
|
44
|
+
warn "Maybe #{tag} can't be found as a #{@type}?" if @type && !element
|
45
|
+
|
46
|
+
return find_by_data(attributes, vars).to_a, attributes.to_h unless element
|
47
|
+
|
48
|
+
return element, new_attributes
|
49
|
+
end
|
50
|
+
|
51
|
+
def what_was_selected(element, attributes, vars, name, scope)
|
52
|
+
send_to_app('locatineconfirmed', 'ok')
|
53
|
+
tag, index = tag_index
|
54
|
+
mass_highlight_turn(element, false) if element
|
55
|
+
element, attributes = working_on_selected(tag, index, vars, attributes)
|
56
|
+
mass_highlight_turn(element) if element
|
57
|
+
push_title "#{element.length} elements were selected as #{name} in "\
|
58
|
+
"#{scope}. If it is correct - confirm the selection."
|
59
|
+
return element, attributes
|
60
|
+
end
|
61
|
+
|
62
|
+
def decline(element, name, scope)
|
63
|
+
mass_highlight_turn(element, false) if element
|
64
|
+
send_to_app('locatineconfirmed', 'ok')
|
65
|
+
push_title "Nothing is selected as #{name} in #{scope}"
|
66
|
+
return nil, {}
|
67
|
+
end
|
68
|
+
|
69
|
+
def listening(els, attrs, vars, name, scope)
|
70
|
+
until get_from_app('locatineconfirmed') == 'true'
|
71
|
+
sleep(0.1)
|
72
|
+
case get_from_app('locatineconfirmed')
|
73
|
+
when 'selected'
|
74
|
+
els, attrs = what_was_selected(els, attrs, vars, name, scope)
|
75
|
+
when 'declined'
|
76
|
+
els, attrs = decline(els, name, scope)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
return els, attrs
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# request send and waiting for an answer
|
84
|
+
def ask(scope, name, element, vars)
|
85
|
+
start_listening(scope, name)
|
86
|
+
element, attributes = suggest_element(element, vars, name, scope)
|
87
|
+
@cold_time = 0
|
88
|
+
element, attributes = listening(element, attributes, vars, name, scope)
|
89
|
+
@cold_time = nil
|
90
|
+
response_action(element)
|
91
|
+
return element, attributes
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Locatine
|
2
|
+
##
|
3
|
+
# Methods about creating, reading and writing files
|
4
|
+
module FileWork
|
5
|
+
private
|
6
|
+
|
7
|
+
##
|
8
|
+
# Reading data from provided file which is set on init of the class instance
|
9
|
+
#
|
10
|
+
# If there is no dir or\and file they will be created
|
11
|
+
def read_create
|
12
|
+
FileUtils.mkdir_p(@folder) unless File.directory?(@folder)
|
13
|
+
hash = Hash.new { |h, k| h[k] = Hash.new { |hi, ki| hi[ki] = {} } }
|
14
|
+
create_json_file unless File.exist?(@json)
|
15
|
+
hash.merge(JSON.parse(File.read(@json))['data'])
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_json_file
|
19
|
+
f = File.new(@json, 'w')
|
20
|
+
f.puts '{"data" : {}}'
|
21
|
+
f.close
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Setting stability
|
26
|
+
def set_stability(first, second)
|
27
|
+
second = first if second.to_h == {}
|
28
|
+
final = Hash.new { |hash, key| hash[key] = [] }
|
29
|
+
first.each_pair do |depth, array|
|
30
|
+
final[depth] = same_entries(array, second, depth, true).uniq
|
31
|
+
end
|
32
|
+
final
|
33
|
+
end
|
34
|
+
|
35
|
+
def stability_bump(to_add, hash)
|
36
|
+
if to_add.empty? # new ones
|
37
|
+
hash['stability'] = '1'
|
38
|
+
elsif to_add[0]['stability'].to_i < @stability_limit # old ones
|
39
|
+
to_add[0]['stability'] = (to_add[0]['stability'].to_i + 1).to_s
|
40
|
+
end
|
41
|
+
to_add.empty? ? [hash] : to_add
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Saving json
|
46
|
+
def store(attributes, scope, name)
|
47
|
+
@data[scope][name] = set_stability(attributes, @data[scope][name])
|
48
|
+
to_write = { 'data' => @data }
|
49
|
+
File.open(@json, 'w') do |f|
|
50
|
+
f.write(JSON.pretty_generate(to_write))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Locatine
|
2
|
+
##
|
3
|
+
# If html code is good and name is related to the code, Locatine can guess it
|
4
|
+
#
|
5
|
+
# Methods for finding element by name only
|
6
|
+
module FindByGuess
|
7
|
+
private
|
8
|
+
|
9
|
+
def main_guess(name)
|
10
|
+
all = []
|
11
|
+
name.split(' ').each do |part|
|
12
|
+
all += guess_by_part(part)
|
13
|
+
end
|
14
|
+
all
|
15
|
+
end
|
16
|
+
|
17
|
+
def guess_by_part(part)
|
18
|
+
all = []
|
19
|
+
tag_xpath = "//#{part}#{not_magic_div}"
|
20
|
+
text_xpath = "//*[contains(text(),'#{part}')]#{not_magic_div}"
|
21
|
+
attr_xpath = "//*[@*[contains(., '#{part}')]]#{not_magic_div}"
|
22
|
+
all += find_by_locator(xpath: tag_xpath).to_a
|
23
|
+
all += find_by_locator(xpath: text_xpath).to_a
|
24
|
+
all += find_by_locator(xpath: attr_xpath).to_a
|
25
|
+
all
|
26
|
+
end
|
27
|
+
|
28
|
+
def full_guess(all, vars, name)
|
29
|
+
max = all.count(all.max_by { |i| all.count(i) })
|
30
|
+
if max >= name.split(' ').length
|
31
|
+
guess = (all.select { |i| all.count(i) == max }).uniq
|
32
|
+
guess_data = generate_data(guess, vars)
|
33
|
+
found_by_data = find_by_data(guess_data, vars)
|
34
|
+
end
|
35
|
+
return found_by_data, guess_data.to_h
|
36
|
+
end
|
37
|
+
|
38
|
+
def check_guess(all, vars, name, scope)
|
39
|
+
guess, guess_data = full_guess(all, vars, name)
|
40
|
+
if guess.nil? || (engine.elements.length / guess.length <= 4)
|
41
|
+
push_title "Locatine has no good guess for #{name} in #{scope}."
|
42
|
+
guess = nil
|
43
|
+
guess_data = {}
|
44
|
+
else
|
45
|
+
push_title "#{guess.length} elements guessed as #{name} in #{scope}."
|
46
|
+
end
|
47
|
+
return guess, guess_data
|
48
|
+
end
|
49
|
+
|
50
|
+
def find_by_guess(scope, name, vars)
|
51
|
+
@cold_time = 0
|
52
|
+
all = main_guess(name)
|
53
|
+
if !all.empty?
|
54
|
+
guess, guess_data = check_guess(all, vars, name, scope)
|
55
|
+
else
|
56
|
+
push_title "Locatine has no guess for #{name} in #{scope}."
|
57
|
+
end
|
58
|
+
@cold_time = nil
|
59
|
+
return guess, guess_data.to_h
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Locatine
|
2
|
+
##
|
3
|
+
# Methods related to the most simple search by ready locator.
|
4
|
+
module FindByLocator
|
5
|
+
private
|
6
|
+
|
7
|
+
def collection?(the_class)
|
8
|
+
case the_class.superclass.to_s
|
9
|
+
when 'Object'
|
10
|
+
nil
|
11
|
+
when 'Watir::Element'
|
12
|
+
false
|
13
|
+
when 'Watir::ElementCollection'
|
14
|
+
true
|
15
|
+
else
|
16
|
+
collection?(the_class.superclass)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Getting all the elements matching a locator
|
22
|
+
def find_by_locator(locator)
|
23
|
+
method = @type.nil? ? :elements : @type
|
24
|
+
results = engine.send(method, locator)
|
25
|
+
case collection?(results.class)
|
26
|
+
when nil
|
27
|
+
wrong_method_detected(method)
|
28
|
+
when true
|
29
|
+
return correct_method_detected(results)
|
30
|
+
when false
|
31
|
+
return acceptable_method_detected(results, method)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def wrong_method_detected(method)
|
36
|
+
@type = nil
|
37
|
+
raise ArgumentError, "#{method} is not good for :look_in property. Use"\
|
38
|
+
' a method of Watir::Browser that returns a collection (like :divs,'\
|
39
|
+
' :links, etc.)'
|
40
|
+
end
|
41
|
+
|
42
|
+
def correct_method_detected(results)
|
43
|
+
all = []
|
44
|
+
begin
|
45
|
+
results[0].wait_until(timeout: @cold_time, &:present?)
|
46
|
+
rescue StandardError
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
results.each { |item| all.push item if item.present? }
|
50
|
+
return all unless all.empty?
|
51
|
+
return nil if all.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
def acceptable_method_detected(results, method, locator)
|
55
|
+
warn "#{method} works for :look_in. But it is better to use a method of"\
|
56
|
+
' Watir::Browser that returns a collection (like :divs, :links, etc.)'
|
57
|
+
the_class = results.class
|
58
|
+
results = engine.elements(locator)
|
59
|
+
.to_a.select { |item| item.to_subtype.class == the_class }
|
60
|
+
correct_method_detected(results)
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Getting elements by tag
|
65
|
+
def find_by_tag(hash, vars, depth = 0)
|
66
|
+
correction = '/*' * depth.to_i
|
67
|
+
xpath = "//*[self::#{process_string(hash['value'], vars)}]"
|
68
|
+
find_by_locator(xpath: "#{xpath}#{correction}#{not_magic_div}")
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Getting elements by text
|
73
|
+
def find_by_text(hash, vars, depth = 0)
|
74
|
+
correction = '/*' * depth.to_i
|
75
|
+
xpath = "//*[contains(text(), '#{process_string(hash['value'], vars)}')]"
|
76
|
+
find_by_locator(xpath: "#{xpath}#{correction}#{not_magic_div}")
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Getting elements by attribute
|
81
|
+
def find_by_attribute(hash, vars, depth = 0)
|
82
|
+
correction = '/*' * depth.to_i
|
83
|
+
full_part = '//*[@*'
|
84
|
+
hash['name'].split('_').each do |part|
|
85
|
+
full_part += "[contains(name(), '#{part}')]"
|
86
|
+
end
|
87
|
+
value = process_string(hash['value'], vars)
|
88
|
+
xpath = full_part + "[contains(., '#{value}')]]"
|
89
|
+
find_by_locator(xpath: "#{xpath}#{correction}#{not_magic_div}")
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Getting all the elements via stored information
|
94
|
+
def find_by_data(data, vars)
|
95
|
+
find_by_locator(xpath: generate_xpath(data, vars))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Locatine
|
2
|
+
##
|
3
|
+
# Logic for finding lost element
|
4
|
+
module FindByMagic
|
5
|
+
private
|
6
|
+
|
7
|
+
##
|
8
|
+
# Getting all the elements via black magic
|
9
|
+
def find_by_magic(name, scope, data, vars)
|
10
|
+
warn "#{name} in #{scope} is lost. Looking for it."
|
11
|
+
@cold_time = 0
|
12
|
+
all = all_options(data, vars)
|
13
|
+
@cold_time = nil
|
14
|
+
raise "Unable to find element #{name} in #{scope}" if all.empty?
|
15
|
+
|
16
|
+
max = all.count(all.max_by { |i| all.count(i) })
|
17
|
+
suggestion = (all.select { |i| all.count(i) == max }).uniq
|
18
|
+
attributes = generate_data(suggestion, vars)
|
19
|
+
return suggestion, attributes
|
20
|
+
end
|
21
|
+
|
22
|
+
def all_options(data, vars)
|
23
|
+
all = []
|
24
|
+
data.each_pair do |depth, array|
|
25
|
+
get_trusted(array).each do |hash|
|
26
|
+
all += one_option_array(hash, vars, depth)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
all
|
30
|
+
end
|
31
|
+
|
32
|
+
def one_option_array(hash, vars, depth)
|
33
|
+
case hash['type']
|
34
|
+
when 'tag'
|
35
|
+
find_by_tag(hash, vars, depth).to_a
|
36
|
+
when 'text'
|
37
|
+
find_by_text(hash, vars, depth).to_a
|
38
|
+
when 'attribute'
|
39
|
+
find_by_attribute(hash, vars, depth).to_a
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Locatine
|
2
|
+
##
|
3
|
+
# Methods explaining find logic
|
4
|
+
module FindLogic
|
5
|
+
private
|
6
|
+
|
7
|
+
def set_name(simple_name, name)
|
8
|
+
name ||= simple_name
|
9
|
+
raise ArgumentError, ':name should be provided' unless name
|
10
|
+
|
11
|
+
name
|
12
|
+
end
|
13
|
+
|
14
|
+
def core_search(result, name, scope, vars, exact)
|
15
|
+
if @data[scope][name].to_h != {}
|
16
|
+
result = find_by_data(@data[scope][name], vars)
|
17
|
+
attributes = generate_data(result, vars) if result
|
18
|
+
if !result && !exact
|
19
|
+
result, attributes = find_by_magic(name, scope,
|
20
|
+
@data[scope][name], vars)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
return result, attributes
|
24
|
+
end
|
25
|
+
|
26
|
+
def full_search(name, scope, vars, locator, exact)
|
27
|
+
result, attributes = locator_search(locator, vars)
|
28
|
+
unless result
|
29
|
+
result, attributes = core_search(result, name, scope,
|
30
|
+
vars, exact)
|
31
|
+
end
|
32
|
+
result, attributes = ask(scope, name, result, vars) if @learn
|
33
|
+
raise "Nothing was found for #{scope} #{name}" if !result && !exact
|
34
|
+
|
35
|
+
store(attributes, scope, name) if result
|
36
|
+
return result, attributes
|
37
|
+
end
|
38
|
+
|
39
|
+
def locator_search(locator, vars)
|
40
|
+
result = find_by_locator(locator) if locator != {}
|
41
|
+
attributes = generate_data(result, vars) if result
|
42
|
+
return result, attributes
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Returning subtype of the only element of collection OR collection
|
47
|
+
#
|
48
|
+
# Params:
|
49
|
+
# +result+ must be Watir::HTMLElementCollection or Array
|
50
|
+
#
|
51
|
+
# +collection+ nil, true or false
|
52
|
+
def to_subtype(result, collection)
|
53
|
+
case collection
|
54
|
+
when true
|
55
|
+
result
|
56
|
+
when false
|
57
|
+
result.first.to_subtype
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|