locatine 0.01822 → 0.01839
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +323 -0
- data/lib/locatine/app/content.js +16 -1
- data/lib/locatine/app/manifest.json +1 -1
- data/lib/locatine/app/popup.css +6 -0
- data/lib/locatine/app/popup.html +6 -4
- data/lib/locatine/app/popup.js +13 -0
- data/lib/locatine/for_search/data_logic.rb +2 -2
- data/lib/locatine/for_search/dialog_logic.rb +14 -8
- data/lib/locatine/for_search/find_by_locator.rb +10 -8
- data/lib/locatine/for_search/find_logic.rb +5 -1
- data/lib/locatine/for_search/highlight.rb +4 -5
- data/lib/locatine/for_search/listening.rb +1 -1
- data/lib/locatine/for_search/name_helper.rb +51 -0
- data/lib/locatine/for_search/public.rb +11 -1
- data/lib/locatine/for_search/saying.rb +6 -0
- data/lib/locatine/scope.rb +20 -0
- data/lib/locatine/search.rb +3 -1
- data/lib/locatine/version.rb +1 -1
- data/lib/locatine.rb +1 -0
- data/readme/567.png +0 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6085cdba5bc5b209bf24330ded9e2458c045d3e3
|
4
|
+
data.tar.gz: '0678ca74613066d304d6d8408ec18ef2dc5575da'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 10b9daf2cdd2139ddaf3f3054957ff467ba607681c692c6c7f3ea12c04b9d77845ab4a216003ccacdd6a84a5de901675f2708b2a17df967889d2a808c7dfbcfd
|
7
|
+
data.tar.gz: dfb7c5746b8219d8c90215c239d8aab91de686959911cd087d442a23d92e8564a8ff917f339cf2c22303b5722542f8fd32d61fea292b06b8196426741c680609
|
data/README.md
ADDED
@@ -0,0 +1,323 @@
|
|
1
|
+
# Locatine
|
2
|
+
|
3
|
+
Element location tool based on Watir.
|
4
|
+
|
5
|
+
You are asking Locatine to find element for you.
|
6
|
+
|
7
|
+
It is asking you what element do you mean.
|
8
|
+
|
9
|
+
It is remembering your answer and collecting information about selected element.
|
10
|
+
|
11
|
+
After that it is finding element by itself.
|
12
|
+
|
13
|
+
If your element will be lost (due to id change for example) locatine will locate the most similar element.
|
14
|
+
|
15
|
+
That's it.
|
16
|
+
|
17
|
+
## Stage of development:
|
18
|
+
|
19
|
+
Version of Locatine is **0.01839** only. It means so far this is an early alfa. You can use it in a real project if you are really risky person.
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
Add this line to your application's Gemfile:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
gem 'locatine'
|
27
|
+
```
|
28
|
+
|
29
|
+
And then execute:
|
30
|
+
|
31
|
+
$ bundle
|
32
|
+
|
33
|
+
Or install it yourself as:
|
34
|
+
|
35
|
+
$ gem install locatine
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
|
39
|
+
1. Be sure that you have [Chrome browser](https://www.google.com/chrome/browser/desktop/) installed. It should work with any browser but something you can do in Chrome only
|
40
|
+
2. Write the code
|
41
|
+
```ruby
|
42
|
+
require 'locatine'
|
43
|
+
s = Locatine::Search.new
|
44
|
+
s.browser.goto("yourpage.com.com")
|
45
|
+
s.find(name: "element", scope: "Main").click
|
46
|
+
```
|
47
|
+
3. Run it in terminal with parameter LEARN=1 approximately like:
|
48
|
+
|
49
|
+
$ LEARN=1 ruby path_to_your_test.rb
|
50
|
+
|
51
|
+
4. It will open the browser and transfer you to the yourpage.com.com
|
52
|
+
5. Select element to represent *element* in the *Main* scope (you can click on it or select it in devtools)
|
53
|
+
6. Click Locatine application icon at the browser panel
|
54
|
+
7. And confirm the selection
|
55
|
+
|
56
|
+
![Steps 4-5-6](readme/567.png)
|
57
|
+
|
58
|
+
8. Now you can run the test without LEARN parameter and it will work.
|
59
|
+
|
60
|
+
## Locatine app window
|
61
|
+
|
62
|
+
### Element name
|
63
|
+
|
64
|
+
You can ask the app to save element with any name. This name should be used for element finding later.
|
65
|
+
|
66
|
+
### Waiting for click
|
67
|
+
|
68
|
+
If you need to do some actions on page for debug purposes before defining the element you can turn off waiting for click. If Locatine is waiting for click every click is gonna be counted as element selection.
|
69
|
+
|
70
|
+
### Single\\Collection mode
|
71
|
+
|
72
|
+
If you need to find a collection of elements. Turn collection mode on. And click two elements of the kind. Locatine will automatically select all the elements that are similar to selected
|
73
|
+
|
74
|
+
### Clear selection
|
75
|
+
|
76
|
+
Click it to start element selection process from the very beginning.
|
77
|
+
|
78
|
+
### Abort selection
|
79
|
+
|
80
|
+
Will forcedly stop the selection process. Use with care since ruby methods will return nils and errors since element is not selected properly. Use it when you finish to define a scope.
|
81
|
+
|
82
|
+
### Confirm selection
|
83
|
+
|
84
|
+
When you've selected a correct element - confirm it in order to save.
|
85
|
+
|
86
|
+
## Locatine::Search options
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
Locatine::Search.new(json: "./Locatine_files/default.json",
|
90
|
+
depth: 3,
|
91
|
+
browser: nil,
|
92
|
+
learn: ENV['LEARN'].nil? ? false : true,
|
93
|
+
stability_limit: 1000,
|
94
|
+
scope: "Default",
|
95
|
+
tolerance: 33,
|
96
|
+
visual_search: false)
|
97
|
+
```
|
98
|
+
|
99
|
+
### json
|
100
|
+
|
101
|
+
the file where data collected about elements will be stored
|
102
|
+
|
103
|
+
### depth
|
104
|
+
|
105
|
+
shows how many info will be stored about each element
|
106
|
+
|
107
|
+
- 0 = everything about the element
|
108
|
+
- 1 = everything about the element and the parent of it
|
109
|
+
- 2 = everything about the element and the parent of it + one more parent
|
110
|
+
|
111
|
+
### browser
|
112
|
+
|
113
|
+
if not provided new Watir::Browser will be started. Do not provide browser if you are going to use learn mode
|
114
|
+
|
115
|
+
### learn
|
116
|
+
|
117
|
+
mode is used to train locatine to search your elements. By default is false. But if you are starting your test like:
|
118
|
+
|
119
|
+
$ LEARN=true ruby path_to_your_test.rb
|
120
|
+
|
121
|
+
it will turn learn to true by default.
|
122
|
+
|
123
|
+
### scope
|
124
|
+
|
125
|
+
is a setting that is representing default scope (group) where elements will be stored by default
|
126
|
+
|
127
|
+
### stability_limit
|
128
|
+
|
129
|
+
shows how much times attribute should be present to be considered a trusted one. The extremely large value means that locatine will hardly trust your code. Extremely low means that locatine will always believe that nothing in the code gonna be changed.
|
130
|
+
|
131
|
+
### tolerance
|
132
|
+
|
133
|
+
If stored metrics of element (including attributes, text, css values and tags) were changed Locatine will find and suggest the most similar one. Tolerance is showing how different in per cent new element may be to the old one. If 0 (zero tolerance) - locatine will find nothing if element lost. If 50 it is enough for element to have only half of parameters of old element we are looking for to be returned. If 100 - at least something is found - it goes. Default if 67 (means only 33% of element should stay in element to be found and returned).
|
134
|
+
|
135
|
+
### visual_search
|
136
|
+
|
137
|
+
locatine will count css values and position of element only if true. In that case locatine will not only read html code (attributes, tags, texts) but it will get css stylesheet for element, its position and size. In the most common case locatine is using attributes+tag+text to find the element. It is starting to use css styles of element and position only if element is lost in order to provide a better result and to mesure the similarity of the lost element and one that is found.
|
138
|
+
|
139
|
+
Position and size for element will be stored for the current resolution only. Start with new browser resolution will lead to deletion of all previous location\\size data.
|
140
|
+
|
141
|
+
Be careful! Set true only if appearance of your page is pretty stable.
|
142
|
+
|
143
|
+
## Changing options on fly
|
144
|
+
|
145
|
+
You can get or set these values on fly. Like:
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
s = Locatine::Search.new(learn: true)
|
149
|
+
s.learn #=> true
|
150
|
+
s.learn = false
|
151
|
+
```
|
152
|
+
|
153
|
+
## Locatine::Search find options
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
s.find(name: "some name",
|
157
|
+
scope: "Default",
|
158
|
+
exact: false,
|
159
|
+
locator: {},
|
160
|
+
vars: {},
|
161
|
+
look_in: nil,
|
162
|
+
iframe: nil,
|
163
|
+
return_locator: false,
|
164
|
+
collection: false,
|
165
|
+
tolerance: nil)
|
166
|
+
```
|
167
|
+
### name
|
168
|
+
|
169
|
+
should be always provided. Name of element to look for. Must be uniq one per scope. Ideally name should be made of 2-4 words separated by spaces describing its nature ("pay bill button", "search input", etc.) It will help Locatine to find them.
|
170
|
+
|
171
|
+
### scope
|
172
|
+
|
173
|
+
group of elements. Must be uniq per file. This is to help to store elements with same names from different pages in one file
|
174
|
+
|
175
|
+
### exact
|
176
|
+
|
177
|
+
unless it is true locatine will always try to find lost element using all the power it has. Use exact: true if you want element to be lost in case of any significant change. If it is impossible to find element when exact: true locatine will return nil.
|
178
|
+
|
179
|
+
Be carefull: exact is working only when element is stable (has at least one parameter persistent for stability_limit times == well known by locatine). If element is not stable yet locatine will search for it anyway and maybe it will find something. So if you want to ensure that element does not exist you should use locator and exact at the same time. You may also set zero tolerance. Check it:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
# Will return nil if there is no element id = 'not welcome'
|
183
|
+
s.find(name: "unexpected element", locator:{id: "not welcome"}, exact: true)
|
184
|
+
|
185
|
+
# Will return nil if well known element "unexpected element" is not present
|
186
|
+
# Will try to find and return at least something if "unexpected element" is not stable (well known)
|
187
|
+
# If there is nothing similar to "unexpected element" returns nil
|
188
|
+
s.find(name: "unexpected something", exact: true)
|
189
|
+
|
190
|
+
# Will return element only if the same element is present
|
191
|
+
# Changing of any attribute which is trusted by locatine will produce nil
|
192
|
+
s.find(name: "unexpected something", exact: true, tolerance: 0)
|
193
|
+
```
|
194
|
+
|
195
|
+
### locator
|
196
|
+
|
197
|
+
you may provide your own locator to use. Same syntax as in Watir:
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
find(name: "element with custom locator", locator: {xpath: "//custom"})
|
201
|
+
```
|
202
|
+
|
203
|
+
### vars
|
204
|
+
|
205
|
+
are used to pass dynamic attributes.
|
206
|
+
|
207
|
+
For example you have created an account on your site with
|
208
|
+
|
209
|
+
```ruby
|
210
|
+
name == "stablePart_qljcrt24jh"
|
211
|
+
```
|
212
|
+
|
213
|
+
where
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
random_string == "qljcrt24jh"
|
217
|
+
```
|
218
|
+
|
219
|
+
was generated by random. Now you need to find the element with this part on the page. You can do
|
220
|
+
|
221
|
+
```ruby
|
222
|
+
random_string #=> "qljcrt24jh"
|
223
|
+
find(name: "account name", vars: {text: random_string})
|
224
|
+
```
|
225
|
+
|
226
|
+
Next time when your test will generate another random_string it will use new value.
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
vars = {text: random_substring} # If you want the text of element to be dynamic
|
230
|
+
vars = {tag: random_tag} # The tag
|
231
|
+
vars = {attribute_name: random_attr} # If attribute is dynamic (use name of the attribute)
|
232
|
+
# And two lines work with visual_search == true only
|
233
|
+
vars = {css_option: random_value} # If your css is dynamic
|
234
|
+
vars = {x: random_x} # x, y, width, height for element size and position
|
235
|
+
```
|
236
|
+
|
237
|
+
And if you do not like it you can do:
|
238
|
+
|
239
|
+
```ruby
|
240
|
+
random_string #=> "qljcrt24jh"
|
241
|
+
find(name: "account name", locator:{text: "stablePart_#{random_string}")
|
242
|
+
```
|
243
|
+
|
244
|
+
### look_in
|
245
|
+
|
246
|
+
is for method name taken from Watir::Browser item. It should be a method that returns collection of elements like (text_fields, divs, links, etc.). If this option is stated locatine will look for your element only among elements of that kind. Be careful with it in a learn mode. If your look_in setting and real element are from different types. Locatine will be unable to find it.
|
247
|
+
|
248
|
+
### iframe
|
249
|
+
|
250
|
+
that is in order to find element inside of an iframe
|
251
|
+
|
252
|
+
### return_locator
|
253
|
+
|
254
|
+
true is returning the locator of the element instead of element. Use with care if attributes of your elements are dynamic and you are in a learning mode.
|
255
|
+
|
256
|
+
### collection
|
257
|
+
|
258
|
+
if true array of elements will be returned. If false only the one element (the first one found) will be returned.
|
259
|
+
|
260
|
+
### tolerance
|
261
|
+
|
262
|
+
You can state custom tolerance for the element.
|
263
|
+
|
264
|
+
## Scope
|
265
|
+
|
266
|
+
If you want to define a whole bunch of elements at once you can do:
|
267
|
+
|
268
|
+
```ruby
|
269
|
+
search = Locatine::Search.new(learn: true)
|
270
|
+
search.get_scope(name: "Name of scope") # Will ask you about all the elements in scope
|
271
|
+
# or
|
272
|
+
scope = Locatine::Scope.new('Name of scope', search)
|
273
|
+
scope.define
|
274
|
+
```
|
275
|
+
|
276
|
+
You will be able to select all the elements and collections for scope one by one. When it is finished click "Abort Selection" button to exit the loop.
|
277
|
+
|
278
|
+
```ruby
|
279
|
+
# You can force use dynamic variables on define where is possible (same rules as for find)
|
280
|
+
vars = {text: "dynamic text",
|
281
|
+
tag: "span",
|
282
|
+
attrName: "part of dynamic attr-value"}
|
283
|
+
scope.define(vars) # Will ask you about all the elements in scope
|
284
|
+
# Same
|
285
|
+
search.get_scope(name: "Name of scope", vars: vars) # Will ask you...
|
286
|
+
# Finally when scope is defined
|
287
|
+
search.find(scope: "Name of scope",
|
288
|
+
name: "Name of element in scope",
|
289
|
+
vars: vars # Necessary if elements were defined with vars)
|
290
|
+
```
|
291
|
+
|
292
|
+
Scope itself is not very useful now. But we are looking forward.
|
293
|
+
|
294
|
+
## Other ways to use find
|
295
|
+
|
296
|
+
If the scope is set and you do not want to provide any additional options you can do:
|
297
|
+
|
298
|
+
```ruby
|
299
|
+
s = Locatine::Search.new
|
300
|
+
s.find("just name of element")
|
301
|
+
```
|
302
|
+
|
303
|
+
Also you can do:
|
304
|
+
|
305
|
+
```ruby
|
306
|
+
s = Locatine::Search.new
|
307
|
+
s.browser.button(s.lctr("name of the button"))
|
308
|
+
# or
|
309
|
+
s.browser.button(s.lctr(name: "name of the button", scope: "Some form"))
|
310
|
+
# or
|
311
|
+
s.browser.button(s.lctr("name of the button", scope: "Some form"))
|
312
|
+
```
|
313
|
+
|
314
|
+
That may be helpful in case of migration from plain watir to watir + locatine
|
315
|
+
|
316
|
+
If you want to find collection of elements you can use:
|
317
|
+
|
318
|
+
```ruby
|
319
|
+
s = Locatine::Search.new
|
320
|
+
s.collect("group of elements") # Will return an array
|
321
|
+
# or
|
322
|
+
s.collect(name: "group of elements")
|
323
|
+
```
|
data/lib/locatine/app/content.js
CHANGED
@@ -14,7 +14,8 @@ async function creatingDiv(){
|
|
14
14
|
"id":"locatine_magic_div",
|
15
15
|
"locatinestyle": await get_value('magic_div') || "false",
|
16
16
|
"locatinetitle": "ok",
|
17
|
-
"locatinehint": "ok"
|
17
|
+
"locatinehint": "ok",
|
18
|
+
"locatine_name": ""
|
18
19
|
};
|
19
20
|
locatine_create_element(document.body, "div", options, "");
|
20
21
|
const magic_cover = document.getElementById('locatine_magic_div');
|
@@ -53,13 +54,27 @@ async function setConfirm(magicDiv) {
|
|
53
54
|
magicDiv.setAttribute("locatineconfirmed", confirmed);
|
54
55
|
}
|
55
56
|
|
57
|
+
async function setName(magicDiv) {
|
58
|
+
const selectionName = magicDiv.getAttribute("locatine_name");
|
59
|
+
const nameMark = magicDiv.getAttribute("locatine_name_mark");
|
60
|
+
if (nameMark != "true") {
|
61
|
+
magicDiv.setAttribute("locatine_name", await get_value("locatineName"));
|
62
|
+
}
|
63
|
+
if ((selectionName != "") && (nameMark == "true")){
|
64
|
+
await set_value("locatineName", selectionName);
|
65
|
+
magicDiv.setAttribute("locatine_name_mark", "");
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
56
69
|
async function refreshData(){
|
57
70
|
const magicDiv = document.getElementById("locatine_magic_div");
|
58
71
|
if (!document.getElementById("locatine_magic_div")){
|
59
72
|
creatingDiv();
|
73
|
+
set_value("locatineName", "");
|
60
74
|
} else {
|
61
75
|
setStyle(magicDiv.getAttribute("locatinestyle"), magicDiv);
|
62
76
|
setTitleHint(magicDiv);
|
77
|
+
setName(magicDiv);
|
63
78
|
magicDiv.setAttribute("locatinecollection", await get_value("locatine_collection"));
|
64
79
|
setConfirm(magicDiv);
|
65
80
|
};
|
data/lib/locatine/app/popup.css
CHANGED
data/lib/locatine/app/popup.html
CHANGED
@@ -7,10 +7,12 @@
|
|
7
7
|
<h2 class="header" id="mainTitle">Right now you are defining nothing. So no button will work</h2>
|
8
8
|
<h3 class="hint" id="hint">But you can click it anyway :)</h3>
|
9
9
|
<div class="block">
|
10
|
-
<input class="
|
11
|
-
<input class="blue button" id="
|
12
|
-
<input class="
|
13
|
-
<input class="
|
10
|
+
<input title="You can set your own element name here. Do not forget to use it in your code!" class="usual field" id="nameHandler" type="text" placeholder="Element name" value=""/>
|
11
|
+
<input title="If you need to do some actions on page for debug purposes before defining the element you can turn off waiting for click. If Locatine is waiting for click every click is gonna be counted as element selection." class="blue button" id="watchSwitch" type="button" value="Do not watch"/>
|
12
|
+
<input title="If you need to find a collection of elements. Turn collection mode on. And click two elements of the kind. Locatine will automatically select all the elements that are similar to selected" class="blue button" id="mode" type="button" value="Adding mode is enabled"/>
|
13
|
+
<input title="Click it to start element selection process from the very beginning." class="red button" id="clearMark" type="button" value="Clear selection"/>
|
14
|
+
<input title="Will forcedly stop the selection process. Use with care since ruby methods will return nils and errors since element is not selected properly. Use it when you finish to define a scope." class="red button" id="abort" type="button" value="Abort selection"/>
|
15
|
+
<input title="When you've selected a correct element - confirm it in order to save." class="green button" id="confirm" type="button" value="Confirm selection"/>
|
14
16
|
</div>
|
15
17
|
<script src="popup.js"></script>
|
16
18
|
</body>
|
data/lib/locatine/app/popup.js
CHANGED
@@ -22,6 +22,9 @@ async function correct_buttons() {
|
|
22
22
|
};
|
23
23
|
document.getElementById("mainTitle").innerText = await get_value("locatine_title");
|
24
24
|
document.getElementById("hint").innerText = await get_value("locatine_hint");
|
25
|
+
if ((document.getElementById("nameHandler").value != await get_value("locatineName")) && (!document.hasFocus())){
|
26
|
+
document.getElementById("nameHandler").value = (await get_value("locatineName") || "");
|
27
|
+
}
|
25
28
|
}
|
26
29
|
|
27
30
|
async function watch() {
|
@@ -36,14 +39,24 @@ function confirm() {
|
|
36
39
|
set_value("locatine_confirm", true);
|
37
40
|
}
|
38
41
|
|
42
|
+
function abort() {
|
43
|
+
set_value("locatine_confirm", 'abort');
|
44
|
+
}
|
45
|
+
|
39
46
|
async function mode() {
|
40
47
|
await set_value("locatine_collection", !(await get_value("locatine_collection")));
|
41
48
|
}
|
42
49
|
|
50
|
+
async function doName() {
|
51
|
+
await set_value("locatineName", document.getElementById("nameHandler").value);
|
52
|
+
}
|
53
|
+
|
43
54
|
document.getElementById("watchSwitch").onclick = function() {watch()};
|
44
55
|
document.getElementById("clearMark").onclick = function() {clear()};
|
45
56
|
document.getElementById("confirm").onclick = function() {confirm()};
|
46
57
|
document.getElementById("mode").onclick = function() {mode()};
|
58
|
+
document.getElementById("abort").onclick = function() {abort()};
|
59
|
+
document.getElementById("nameHandler").oninput = function() {doName()};
|
47
60
|
|
48
61
|
setInterval(function(){
|
49
62
|
correct_buttons();
|
@@ -37,12 +37,12 @@ module Locatine
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def equal_elements?(one, another)
|
40
|
-
good = true
|
40
|
+
good = true unless one == {}
|
41
41
|
one.each_pair do |depth, array|
|
42
42
|
trusted = get_trusted(array).map do |i|
|
43
43
|
i.reject { |k| k == 'stability' }
|
44
44
|
end
|
45
|
-
good &&= (trusted - another[depth] == [])
|
45
|
+
good &&= ((trusted - another[depth] == []) && !trusted.empty?)
|
46
46
|
end
|
47
47
|
good
|
48
48
|
end
|
@@ -48,7 +48,7 @@ module Locatine
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def return_old_selection(attrs, vars)
|
51
|
-
return find_by_data(attrs, vars).to_a,
|
51
|
+
return find_by_data(attrs, vars).to_a, attrs.to_h if attrs.to_h != {}
|
52
52
|
|
53
53
|
return nil, {}
|
54
54
|
end
|
@@ -62,12 +62,11 @@ module Locatine
|
|
62
62
|
return element, new_attributes
|
63
63
|
end
|
64
64
|
|
65
|
-
def what_was_selected(element, attributes, vars
|
65
|
+
def what_was_selected(element, attributes, vars)
|
66
66
|
tag, index = tag_index
|
67
67
|
send_to_app('locatineconfirmed', 'ok')
|
68
68
|
mass_highlight_turn(element, false) if element
|
69
69
|
element, attributes = working_on_selected(tag, index, vars, attributes)
|
70
|
-
show_element(element, attributes, name, scope) if element
|
71
70
|
return element, attributes
|
72
71
|
end
|
73
72
|
|
@@ -88,7 +87,9 @@ module Locatine
|
|
88
87
|
def user_selection(els, attrs, vars, name, scope)
|
89
88
|
case get_from_app('locatineconfirmed')
|
90
89
|
when 'selected'
|
91
|
-
els, attrs = what_was_selected(els, attrs, vars
|
90
|
+
els, attrs = what_was_selected(els, attrs, vars)
|
91
|
+
name = suggest_name(name, attrs, vars)
|
92
|
+
show_element(els, attrs, name, scope) if els
|
92
93
|
when 'declined'
|
93
94
|
els, attrs = decline(els, name, scope)
|
94
95
|
end
|
@@ -96,13 +97,16 @@ module Locatine
|
|
96
97
|
end
|
97
98
|
|
98
99
|
def listening(els, attrs, vars, name, scope)
|
99
|
-
until get_from_app('locatineconfirmed')
|
100
|
+
until %w[true abort].include?(get_from_app('locatineconfirmed'))
|
100
101
|
sleep(0.1)
|
101
102
|
els, attrs = user_selection(els, attrs, vars, name, scope)
|
102
103
|
end
|
103
|
-
|
104
|
+
result = get_from_app('locatineconfirmed')
|
105
|
+
return els, attrs if els && result != 'abort'
|
106
|
+
|
107
|
+
els, attrs = decline(els, name, scope)
|
108
|
+
return els, attrs if result == 'abort'
|
104
109
|
|
105
|
-
decline(els, name, scope)
|
106
110
|
listening(els, attrs, vars, name, scope)
|
107
111
|
end
|
108
112
|
|
@@ -114,8 +118,10 @@ module Locatine
|
|
114
118
|
@cold_time = 0
|
115
119
|
element, attributes = listening(element, attributes, vars, name, scope)
|
116
120
|
@cold_time = nil
|
121
|
+
name_from_app = get_from_app('locatine_name')
|
122
|
+
name = name_from_app unless name_from_app.to_s.empty?
|
117
123
|
response_action(element)
|
118
|
-
|
124
|
+
{ element: element, attributes: attributes, name: name }
|
119
125
|
end
|
120
126
|
end
|
121
127
|
end
|
@@ -20,6 +20,11 @@ module Locatine
|
|
20
20
|
# Getting all the elements matching a locator
|
21
21
|
def find_by_locator(locator)
|
22
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
|
23
28
|
results = engine.send(method, locator)
|
24
29
|
return correct_method_detected(results) if collection?(results.class)
|
25
30
|
|
@@ -27,15 +32,12 @@ module Locatine
|
|
27
32
|
end
|
28
33
|
|
29
34
|
def correct_method_detected(results)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
rescue StandardError
|
34
|
-
nil
|
35
|
-
end
|
36
|
-
results.each { |item| all.push item if item.present? }
|
35
|
+
return nil if results.empty?
|
36
|
+
|
37
|
+
all = results.reject(&:stale?)
|
37
38
|
return all unless all.empty?
|
38
|
-
|
39
|
+
|
40
|
+
nil
|
39
41
|
end
|
40
42
|
|
41
43
|
def acceptable_method_detected(results, method, locator)
|
@@ -47,7 +47,11 @@ module Locatine
|
|
47
47
|
result, attributes = locator_search(locator, vars)
|
48
48
|
ok = result || ((locator != {}) && exact)
|
49
49
|
result, attributes = core_search(name, scope, vars, exact) unless ok
|
50
|
-
|
50
|
+
if @learn
|
51
|
+
answer = ask(scope, name, result, vars)
|
52
|
+
result = answer[:element]
|
53
|
+
attributes = answer[:attributes]
|
54
|
+
end
|
51
55
|
return result, attributes
|
52
56
|
end
|
53
57
|
|
@@ -9,8 +9,7 @@ module Locatine
|
|
9
9
|
# We can highlight an element
|
10
10
|
def highlight(element)
|
11
11
|
script = "arguments[0].setAttribute('locatineclass','foundbylocatine')"
|
12
|
-
|
13
|
-
engine.execute_script(script, element) if ok
|
12
|
+
engine.execute_script(script, element)
|
14
13
|
rescue StandardError
|
15
14
|
warn_cannot_highlight(element.selector)
|
16
15
|
end
|
@@ -19,8 +18,7 @@ module Locatine
|
|
19
18
|
# We can unhighlight an element
|
20
19
|
def unhighlight(element)
|
21
20
|
script = "arguments[0].removeAttribute('locatineclass')"
|
22
|
-
|
23
|
-
engine.execute_script(script, element) if ok
|
21
|
+
engine.execute_script(script, element)
|
24
22
|
rescue StandardError
|
25
23
|
false
|
26
24
|
# watir is not allowing to play with attributes of some elements
|
@@ -29,7 +27,8 @@ module Locatine
|
|
29
27
|
##
|
30
28
|
# We can highlight\unhighlight tons of elements at once
|
31
29
|
def mass_highlight_turn(mass, turn_on = true)
|
32
|
-
mass.
|
30
|
+
warn_much_highlight if turn_on && mass.length > 50
|
31
|
+
mass[0..49].each do |element|
|
33
32
|
if turn_on
|
34
33
|
highlight element
|
35
34
|
else
|
@@ -37,7 +37,7 @@ module Locatine
|
|
37
37
|
def response_action(element)
|
38
38
|
send_to_app('locatineconfirmed', 'ok')
|
39
39
|
send_has_response
|
40
|
-
mass_highlight_turn(element, false)
|
40
|
+
mass_highlight_turn(element, false) if element
|
41
41
|
send_to_app('locatinestyle', 'set_false')
|
42
42
|
send_to_app('locatinestyle', 'ok', @browser) if @iframe
|
43
43
|
sleep 1
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Locatine
|
2
|
+
module ForSearch
|
3
|
+
##
|
4
|
+
# We have a module that helps name elements
|
5
|
+
module NameHelper
|
6
|
+
private
|
7
|
+
|
8
|
+
def good_name(main, vars)
|
9
|
+
good = %w[name title id role text]
|
10
|
+
tmp = main.select { |i| good.any? { |k| i['name'].include?(k) } }
|
11
|
+
words = (tmp.map { |i| process_string(i['value'], vars) }).uniq
|
12
|
+
words.sample
|
13
|
+
end
|
14
|
+
|
15
|
+
def so_so_name(main, vars)
|
16
|
+
all = main.select { |i| i['type'] == 'attribute' }
|
17
|
+
words = all.map { |i| process_string(i['value'], vars) }
|
18
|
+
words.sample
|
19
|
+
end
|
20
|
+
|
21
|
+
def some_name(main, vars)
|
22
|
+
result = good_name(main, vars)
|
23
|
+
result = so_so_name(main, vars) if result.nil?
|
24
|
+
result = "undescribed #{generate_word}" if result.nil?
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
def suggest_name(name, attrs, vars)
|
29
|
+
if name.to_s.empty?
|
30
|
+
tag = attrs['0'].select { |i| i['type'] == 'tag' }
|
31
|
+
tag = process_string(tag[0]['value'], vars)
|
32
|
+
name = "#{some_name(attrs['0'], vars)} #{tag}"
|
33
|
+
end
|
34
|
+
suggest = name
|
35
|
+
send_to_app('locatine_name', suggest)
|
36
|
+
send_to_app('locatine_name_mark', 'true')
|
37
|
+
suggest
|
38
|
+
end
|
39
|
+
|
40
|
+
def generate_word(pairs = 3)
|
41
|
+
ss = 'qwrtpsdfghjklzxcvbnm'.split('')
|
42
|
+
sa = 'eyuioa'.split('')
|
43
|
+
str = ''
|
44
|
+
pairs.times do
|
45
|
+
str += ss.sample + sa.sample
|
46
|
+
end
|
47
|
+
str
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -7,6 +7,7 @@ module Locatine
|
|
7
7
|
# Creates a new instance of Search
|
8
8
|
#
|
9
9
|
# Params:
|
10
|
+
#
|
10
11
|
# +json+ is the name of file to store//read data. Default =>
|
11
12
|
# "./Locatine_files/default.json"
|
12
13
|
#
|
@@ -24,7 +25,7 @@ module Locatine
|
|
24
25
|
# +scope+ will be used in search (if not provided) defaulkt is "Default"
|
25
26
|
#
|
26
27
|
# +tolerance+ Shows how similar must be an element found as alternative
|
27
|
-
# to the lost one. Default is
|
28
|
+
# to the lost one. Default is 67 which means that if less than 33% of
|
28
29
|
# metrics of alternative elements are the same as of the lost element
|
29
30
|
# will not be returned
|
30
31
|
def initialize(json: './Locatine_files/default.json',
|
@@ -74,6 +75,9 @@ module Locatine
|
|
74
75
|
#
|
75
76
|
# +collection+ when true an array will be returned. When false - a
|
76
77
|
# single element
|
78
|
+
#
|
79
|
+
# +tolerance+ It is possible to set a custom tolerance for every find. See
|
80
|
+
# examples in README
|
77
81
|
def find(simple_name = nil,
|
78
82
|
name: nil,
|
79
83
|
scope: nil,
|
@@ -113,6 +117,12 @@ module Locatine
|
|
113
117
|
def browser=(value)
|
114
118
|
import_browser(value)
|
115
119
|
end
|
120
|
+
|
121
|
+
def get_scope(name: 'Default', vars: {})
|
122
|
+
answer = Scope.new(name, self)
|
123
|
+
answer.define(vars) if @learn
|
124
|
+
answer
|
125
|
+
end
|
116
126
|
end
|
117
127
|
end
|
118
128
|
end
|
@@ -60,6 +60,7 @@ module Locatine
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def send_clear(name, scope)
|
63
|
+
name = 'some element' if name.to_s.empty?
|
63
64
|
push_title "Now nothing is selected as #{name} in #{scope}"
|
64
65
|
end
|
65
66
|
|
@@ -72,6 +73,7 @@ module Locatine
|
|
72
73
|
end
|
73
74
|
|
74
75
|
def send_selecting(name, scope)
|
76
|
+
name = 'some element' if name.to_s.empty?
|
75
77
|
push_title "You are selecting #{name} in #{scope}"
|
76
78
|
send_to_app('locatinehint', 'Toggle single//collection mode button if '\
|
77
79
|
'you need. If you want to do some actions on the page toggle'\
|
@@ -120,6 +122,10 @@ module Locatine
|
|
120
122
|
send_warn "Something was found as #{data} but we cannot highlight it"
|
121
123
|
end
|
122
124
|
|
125
|
+
def warn_much_highlight(size)
|
126
|
+
send_warn "Only the first 50 elements of #{size} were highlighted."
|
127
|
+
end
|
128
|
+
|
123
129
|
def warn_lost_found(name, scope)
|
124
130
|
send_warn "Something was found as #{name} in #{scope}."
|
125
131
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Locatine
|
2
|
+
##
|
3
|
+
# Scope is the class representing group of elements
|
4
|
+
#
|
5
|
+
# Locatine has scopes
|
6
|
+
class Scope
|
7
|
+
def initialize(scope, search)
|
8
|
+
@search = search
|
9
|
+
@scope = scope
|
10
|
+
end
|
11
|
+
|
12
|
+
def define(vars = {})
|
13
|
+
item = @search.send(:ask, @scope, '', nil, vars)
|
14
|
+
return if item[:element].nil?
|
15
|
+
|
16
|
+
@search.send(:store, item[:attributes], @scope, item[:name])
|
17
|
+
define(vars)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/locatine/search.rb
CHANGED
@@ -9,15 +9,16 @@ require 'locatine/for_search/public'
|
|
9
9
|
require 'locatine/for_search/saying'
|
10
10
|
require 'locatine/for_search/helpers'
|
11
11
|
require 'locatine/for_search/file_work'
|
12
|
+
require 'locatine/for_search/listening'
|
12
13
|
require 'locatine/for_search/highlight'
|
13
14
|
require 'locatine/for_search/data_logic'
|
14
15
|
require 'locatine/for_search/find_logic'
|
15
16
|
require 'locatine/for_search/find_by_css'
|
17
|
+
require 'locatine/for_search/name_helper'
|
16
18
|
require 'locatine/for_search/dialog_logic'
|
17
19
|
require 'locatine/for_search/find_by_magic'
|
18
20
|
require 'locatine/for_search/find_by_guess'
|
19
21
|
require 'locatine/for_search/data_generate'
|
20
|
-
require 'locatine/for_search/listening'
|
21
22
|
require 'locatine/for_search/xpath_generator'
|
22
23
|
require 'locatine/for_search/find_by_locator'
|
23
24
|
|
@@ -37,6 +38,7 @@ module Locatine
|
|
37
38
|
include Locatine::ForSearch::FindLogic
|
38
39
|
include Locatine::ForSearch::Highlight
|
39
40
|
include Locatine::ForSearch::FindByCss
|
41
|
+
include Locatine::ForSearch::NameHelper
|
40
42
|
include Locatine::ForSearch::FindByMagic
|
41
43
|
include Locatine::ForSearch::DialogLogic
|
42
44
|
include Locatine::ForSearch::FindByGuess
|
data/lib/locatine/version.rb
CHANGED
data/lib/locatine.rb
CHANGED
data/readme/567.png
ADDED
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: locatine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.01839'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sergei Seleznev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -114,6 +114,7 @@ executables: []
|
|
114
114
|
extensions: []
|
115
115
|
extra_rdoc_files: []
|
116
116
|
files:
|
117
|
+
- README.md
|
117
118
|
- lib/locatine.rb
|
118
119
|
- lib/locatine/app/background.js
|
119
120
|
- lib/locatine/app/content.css
|
@@ -138,13 +139,16 @@ files:
|
|
138
139
|
- lib/locatine/for_search/highlight.rb
|
139
140
|
- lib/locatine/for_search/listening.rb
|
140
141
|
- lib/locatine/for_search/merge.rb
|
142
|
+
- lib/locatine/for_search/name_helper.rb
|
141
143
|
- lib/locatine/for_search/public.rb
|
142
144
|
- lib/locatine/for_search/saying.rb
|
143
145
|
- lib/locatine/for_search/xpath_generator.rb
|
144
146
|
- lib/locatine/large_scripts/css.js
|
145
147
|
- lib/locatine/large_scripts/dimensions.js
|
148
|
+
- lib/locatine/scope.rb
|
146
149
|
- lib/locatine/search.rb
|
147
150
|
- lib/locatine/version.rb
|
151
|
+
- readme/567.png
|
148
152
|
homepage: https://github.com/sseleznevqa/locatine
|
149
153
|
licenses:
|
150
154
|
- MIT
|