locatine 0.02058 → 0.02539
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +159 -4
- data/bin/locatine-daemon.rb +8 -0
- data/lib/locatine.rb +1 -0
- data/lib/locatine/app/manifest.json +1 -1
- data/lib/locatine/daemon.rb +93 -0
- data/lib/locatine/daemon_helpers.rb +52 -0
- data/lib/locatine/for_search/data_generate.rb +12 -45
- data/lib/locatine/for_search/data_logic.rb +32 -10
- data/lib/locatine/for_search/defaults.rb +40 -0
- data/lib/locatine/for_search/dialog_logic.rb +1 -44
- data/lib/locatine/for_search/element_selection.rb +80 -0
- data/lib/locatine/for_search/file_work.rb +11 -10
- data/lib/locatine/for_search/find_by_guess.rb +41 -38
- data/lib/locatine/for_search/find_by_locator.rb +0 -30
- data/lib/locatine/for_search/find_by_magic.rb +30 -81
- data/lib/locatine/for_search/find_logic.rb +6 -2
- data/lib/locatine/for_search/helpers.rb +20 -10
- data/lib/locatine/for_search/highlight.rb +1 -1
- data/lib/locatine/for_search/merge.rb +4 -2
- data/lib/locatine/for_search/page_work.rb +126 -0
- data/lib/locatine/for_search/public.rb +35 -22
- data/lib/locatine/for_search/saying.rb +28 -3
- data/lib/locatine/for_search/xpath_generator.rb +10 -11
- data/lib/locatine/large_scripts/element.js +31 -0
- data/lib/locatine/large_scripts/page.js +60 -0
- data/lib/locatine/scope.rb +1 -0
- data/lib/locatine/search.rb +11 -4
- data/lib/locatine/version.rb +1 -1
- metadata +26 -12
- data/lib/locatine/for_search/find_by_css.rb +0 -47
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 63c1d452b58e6881483141749a4cd5fc572a1a34
|
4
|
+
data.tar.gz: 5436ddbbd222888ab16ef121750e871be2d0b330
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 875b40b1ebc3294dc60868c4f029a3eb2c37b984509002c30fadecad20d7e26f0df207eab8ed3466cc7021777d9a89baa6a0a6c4ba7dc655bea1109b573031cb
|
7
|
+
data.tar.gz: 9c36756f4c0388acf60d1009ed79ebed732b2401daa387930a3f61c8c97b745f2c1bc0bdeeced30e300543d88b09459c7bdf0f7320ef4630dbd47e83c9b58550
|
data/README.md
CHANGED
@@ -16,7 +16,7 @@ That's it.
|
|
16
16
|
|
17
17
|
## Stage of development:
|
18
18
|
|
19
|
-
Version of Locatine is **0.
|
19
|
+
Version of Locatine is **0.02539** only. It means so far this is an alfa. You can use it in a real project if you are a risky person.
|
20
20
|
|
21
21
|
## Installation
|
22
22
|
|
@@ -96,7 +96,10 @@ Locatine::Search.new(json: "./Locatine_files/default.json",
|
|
96
96
|
scope: "Default",
|
97
97
|
tolerance: 33,
|
98
98
|
visual_search: false,
|
99
|
-
no_fail: false
|
99
|
+
no_fail: false,
|
100
|
+
trusted: [],
|
101
|
+
untrusted: [],
|
102
|
+
autolearn: nil)
|
100
103
|
```
|
101
104
|
|
102
105
|
### json
|
@@ -147,6 +150,24 @@ Be careful! Set true only if appearance of your page is pretty stable.
|
|
147
150
|
|
148
151
|
When element is lost and no_fail is true you will get nil for single element and [] for collection. If no_fail is false (which is default) and locatine cannot find something you will face an error.
|
149
152
|
|
153
|
+
### trusted/untrusted
|
154
|
+
|
155
|
+
If you are sure that some attribute (or something) is not good(generated by random, not uniq, etc.) you can forbid to use it in search. You can write attribute name, "text", "tag", or css attribute name (if you are using visual_search)
|
156
|
+
|
157
|
+
On the other hand you can force locatine to always trust something with trusted.
|
158
|
+
|
159
|
+
### autolearn
|
160
|
+
|
161
|
+
Determines wether Locatine will study elements by default or not.
|
162
|
+
|
163
|
+
If true Locatine will always check element for changes even if it is found properly (to catch for example adding of a new attribute). This is slow.
|
164
|
+
|
165
|
+
If false Locatine will study element only in case when it is lost.
|
166
|
+
|
167
|
+
If not stated Locatine will use false until it is not facing any lost element. After the first lost element it will be studying everything (true).
|
168
|
+
|
169
|
+
Notice that if autolearn is false Locatine is not bumping the stability values of attributes for elements which were found normally.
|
170
|
+
|
150
171
|
## Changing options on fly
|
151
172
|
|
152
173
|
You can get or set these values on fly. Like:
|
@@ -170,7 +191,9 @@ s.find(name: "some name",
|
|
170
191
|
return_locator: false,
|
171
192
|
collection: false,
|
172
193
|
tolerance: nil,
|
173
|
-
no_fail: nil
|
194
|
+
no_fail: nil,
|
195
|
+
trusted: [],
|
196
|
+
untrusted: [])
|
174
197
|
```
|
175
198
|
### name
|
176
199
|
|
@@ -219,7 +242,7 @@ vars = {tag: random_tag} # The tag
|
|
219
242
|
vars = {attribute_name: random_attr} # If attribute is dynamic (use name of the attribute)
|
220
243
|
# And two lines work with visual_search == true only
|
221
244
|
vars = {css_option: random_value} # If your css is dynamic
|
222
|
-
vars = {x: random_x} # x, y
|
245
|
+
vars = {x: random_x} # x, y for element position
|
223
246
|
```
|
224
247
|
|
225
248
|
And if you do not like it you can do:
|
@@ -257,6 +280,10 @@ It is disabling attempts to find element by advanced algorithms. If locator is p
|
|
257
280
|
|
258
281
|
no_fail option that will work for that search only.
|
259
282
|
|
283
|
+
### trusted//untrusted
|
284
|
+
|
285
|
+
You can set trusted elements just for search
|
286
|
+
|
260
287
|
## Scope
|
261
288
|
|
262
289
|
If you want to define a whole bunch of elements at once you can do:
|
@@ -347,3 +374,131 @@ s.exact(name: "something") == s.find(name: "something", exact: true)
|
|
347
374
|
s.check(name: "something") == s.find(name: "something", tolerance: 0)
|
348
375
|
s.check_collection(name: "something") == s.collect(name: "something", tolerance: 0)
|
349
376
|
```
|
377
|
+
|
378
|
+
## Using as a daemon
|
379
|
+
|
380
|
+
Locatine daemon is a web server based on sinatra. You can run it from your code like:
|
381
|
+
|
382
|
+
```ruby
|
383
|
+
require 'locatine'
|
384
|
+
Locatine::Daemon.set :port, 7733 #Your port goes here
|
385
|
+
Locatine::Daemon.run!
|
386
|
+
```
|
387
|
+
|
388
|
+
Also you can do it with terminal:
|
389
|
+
|
390
|
+
```bash
|
391
|
+
locatine-daemon.rb -port=7733
|
392
|
+
```
|
393
|
+
|
394
|
+
You can see a python3 example in the [example](https://github.com/sseleznevqa/locatine/tree/master/example) folder. Main idea is
|
395
|
+
|
396
|
+
1. Run daemon
|
397
|
+
2. Ask daemon for the app path
|
398
|
+
3. Run your browser with the app as extension
|
399
|
+
4. Turn on the learn
|
400
|
+
5. Provide data to the daemon for connect (browser name, session_id, connect url, proxy)
|
401
|
+
6. Use API calls to teach daemon how to find elements
|
402
|
+
7. After that you can start browser without the app
|
403
|
+
8. Provide data for connect
|
404
|
+
9. Now you can ask daemon to find your element via API call. And it will answer with a valid xpath you can use.
|
405
|
+
|
406
|
+
### API
|
407
|
+
|
408
|
+
#### GET call to /app
|
409
|
+
|
410
|
+
returns path to locatine application in order to start chrome with it.
|
411
|
+
|
412
|
+
Example of response:
|
413
|
+
|
414
|
+
```
|
415
|
+
{"app": "/some/path/to/app"}
|
416
|
+
```
|
417
|
+
|
418
|
+
#### GET call to /stop
|
419
|
+
|
420
|
+
stops Locatine daemon.
|
421
|
+
|
422
|
+
Returns:
|
423
|
+
|
424
|
+
```
|
425
|
+
{"result": "dead"}
|
426
|
+
```
|
427
|
+
|
428
|
+
#### POST call to /connect
|
429
|
+
|
430
|
+
allows Locatine Daemon to connect existing browser instance
|
431
|
+
|
432
|
+
POST data:
|
433
|
+
|
434
|
+
```
|
435
|
+
{'browser': 'chrome', 'session_id': session_id, 'url': 'http://whatever_is_browser_ip:port_opened_by_browser_for_selenium', 'proxy': 'optionally' }
|
436
|
+
```
|
437
|
+
|
438
|
+
Answer:
|
439
|
+
|
440
|
+
```
|
441
|
+
{"result": "true"}
|
442
|
+
```
|
443
|
+
|
444
|
+
#### POST call to /set
|
445
|
+
|
446
|
+
is to control options of locatine search. Sending to set data ==
|
447
|
+
|
448
|
+
```
|
449
|
+
{"learn": "true"}
|
450
|
+
```
|
451
|
+
|
452
|
+
Answer:
|
453
|
+
|
454
|
+
```
|
455
|
+
{"result": "true"}
|
456
|
+
```
|
457
|
+
|
458
|
+
is the same as
|
459
|
+
|
460
|
+
```ruby
|
461
|
+
search.learn = true
|
462
|
+
```
|
463
|
+
|
464
|
+
#### POST call to /lctr
|
465
|
+
|
466
|
+
is to find and return locator of an element found by locatine
|
467
|
+
|
468
|
+
POST data just the same as for find or lctr method. It's like:
|
469
|
+
|
470
|
+
```
|
471
|
+
{"name": "some name", "scope": "Default", "exact": "false" ...}
|
472
|
+
```
|
473
|
+
|
474
|
+
Answer:
|
475
|
+
|
476
|
+
```
|
477
|
+
{"xpath": "//YOUR[@xpath='goes here']"}
|
478
|
+
```
|
479
|
+
|
480
|
+
### GET /chromedriver || /geckodriver || /iedriver
|
481
|
+
|
482
|
+
returns path to the binary retrieved by locatine (using webdrivers gem)
|
483
|
+
|
484
|
+
Answer:
|
485
|
+
|
486
|
+
```
|
487
|
+
{"path": "path/to/the/binary"}
|
488
|
+
```
|
489
|
+
|
490
|
+
### POST call to /chromedriver || /geckodriver || /iedriver
|
491
|
+
|
492
|
+
is to force locatine to use your webdriver (for example for using old version of browser)
|
493
|
+
|
494
|
+
POST data:
|
495
|
+
|
496
|
+
```
|
497
|
+
{"version": "2.46"}
|
498
|
+
```
|
499
|
+
|
500
|
+
Answer:
|
501
|
+
|
502
|
+
```
|
503
|
+
{"version": "2.46"}
|
504
|
+
```
|
@@ -0,0 +1,8 @@
|
|
1
|
+
#!Locatine-daemon...
|
2
|
+
require 'locatine'
|
3
|
+
args = Hash[ ARGV.join(' ').scan(/--?([^=\s]+)(?:=(\S+))?/) ]
|
4
|
+
args = args.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
5
|
+
args.each_pair do |key, value|
|
6
|
+
Locatine::Daemon.set key, value
|
7
|
+
end
|
8
|
+
Locatine::Daemon.run!
|
data/lib/locatine.rb
CHANGED
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'json'
|
3
|
+
require 'locatine/daemon_helpers'
|
4
|
+
|
5
|
+
module Locatine
|
6
|
+
#
|
7
|
+
# Locatine daemon based on sinatra
|
8
|
+
#
|
9
|
+
# run Locatine::Daemon.run!
|
10
|
+
class Daemon < Sinatra::Base
|
11
|
+
include Locatine::DaemonHelpers
|
12
|
+
configure do
|
13
|
+
set :search, nil
|
14
|
+
end
|
15
|
+
|
16
|
+
get '/app' do
|
17
|
+
{ app: File.join(Locatine::HOME, 'app').to_s }.to_json
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/' do
|
21
|
+
redirect 'https://github.com/sseleznevqa/locatine#using-as-a-daemon'
|
22
|
+
end
|
23
|
+
|
24
|
+
get '/stop' do
|
25
|
+
Locatine::Daemon.quit!
|
26
|
+
{ result: 'dead' }.to_json
|
27
|
+
end
|
28
|
+
|
29
|
+
post '/chromedriver' do
|
30
|
+
Webdrivers::Chromedriver.required_version = params['version']
|
31
|
+
{ version: Webdrivers::Chromedriver.required_version }.to_json
|
32
|
+
end
|
33
|
+
|
34
|
+
get '/chromedriver' do
|
35
|
+
{ path: Webdrivers::Chromedriver.update }.to_json
|
36
|
+
end
|
37
|
+
|
38
|
+
post '/geckodriver' do
|
39
|
+
Webdrivers::Geckodriver.required_version = params['version']
|
40
|
+
{ version: Webdrivers::Geckodriver.required_version }.to_json
|
41
|
+
end
|
42
|
+
|
43
|
+
get '/geckodriver' do
|
44
|
+
{ path: Webdrivers::Geckodriver.update }.to_json
|
45
|
+
end
|
46
|
+
|
47
|
+
post 'iedriver' do
|
48
|
+
Webdrivers::IEdriver.required_version = params['version']
|
49
|
+
{ version: Webdrivers::IEdriver.required_version }.to_json
|
50
|
+
end
|
51
|
+
|
52
|
+
get '/iedriver' do
|
53
|
+
{ path: Webdrivers::IEdriver.update }.to_json
|
54
|
+
end
|
55
|
+
|
56
|
+
post '/connect' do
|
57
|
+
steal
|
58
|
+
{ result: true }.to_json
|
59
|
+
end
|
60
|
+
|
61
|
+
post '/lctr' do
|
62
|
+
data = Hash[params.map { |k, v| [k.to_sym, v] }]
|
63
|
+
data.each { |k, v| data[k] = false if v == 'false' }
|
64
|
+
search.lctr(data).to_json
|
65
|
+
end
|
66
|
+
|
67
|
+
post '/set' do
|
68
|
+
hash = params
|
69
|
+
search.json = hash['json'] if hash['json']
|
70
|
+
warn 'You cannot set browser like this. Use /connect' if hash['browser']
|
71
|
+
params.each_pair do |key, value|
|
72
|
+
unless (key == 'browser') || (key == 'json')
|
73
|
+
value = false if value == 'false'
|
74
|
+
search.instance_variable_set("@#{key}", value)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
{ result: true }.to_json
|
78
|
+
end
|
79
|
+
|
80
|
+
def search
|
81
|
+
return settings.search unless settings.search.nil?
|
82
|
+
|
83
|
+
settings.search = Locatine::Search.new
|
84
|
+
settings.search.browser.quit
|
85
|
+
settings.search
|
86
|
+
end
|
87
|
+
|
88
|
+
def params
|
89
|
+
request.body.rewind
|
90
|
+
JSON.parse request.body.read
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Locatine
|
2
|
+
#
|
3
|
+
# Usefull things daemon can do
|
4
|
+
module DaemonHelpers
|
5
|
+
private
|
6
|
+
|
7
|
+
def steal
|
8
|
+
cast_ghost_browser
|
9
|
+
disguise_session
|
10
|
+
disguise_server_url
|
11
|
+
disguise_http
|
12
|
+
disguise_proxy unless params['proxy'].to_s.empty?
|
13
|
+
end
|
14
|
+
|
15
|
+
def bridge
|
16
|
+
search.browser.wd.send(:bridge)
|
17
|
+
end
|
18
|
+
|
19
|
+
def b_http
|
20
|
+
bridge.send(:http)
|
21
|
+
end
|
22
|
+
|
23
|
+
def disguise_session
|
24
|
+
bridge.instance_variable_set('@session_id', params['session_id'])
|
25
|
+
end
|
26
|
+
|
27
|
+
def disguise_server_url
|
28
|
+
uri = URI.parse(params['url'])
|
29
|
+
b_http.instance_variable_set('@server_url', uri)
|
30
|
+
end
|
31
|
+
|
32
|
+
def disguise_http
|
33
|
+
b_http.instance_variable_set('@http', make_net)
|
34
|
+
end
|
35
|
+
|
36
|
+
def disguise_proxy
|
37
|
+
b_http.instance_variable_set('@proxy', params['proxy'])
|
38
|
+
end
|
39
|
+
|
40
|
+
def make_net
|
41
|
+
parsed = URI.parse(params['url'])
|
42
|
+
path = parsed.path == '/' ? '' : parsed.path
|
43
|
+
Net::HTTP.new("#{parsed.host}#{path}", parsed.port)
|
44
|
+
end
|
45
|
+
|
46
|
+
def cast_ghost_browser
|
47
|
+
search.browser = Watir::Browser.new(params['browser'].to_sym)
|
48
|
+
search.browser.quit
|
49
|
+
search.browser.instance_variable_set('@closed', false)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -5,53 +5,36 @@ module Locatine
|
|
5
5
|
module DataGenerate
|
6
6
|
private
|
7
7
|
|
8
|
-
def real_text_of(element)
|
9
|
-
element.text == element.inner_html ? element.text : ''
|
10
|
-
end
|
11
|
-
|
12
8
|
def mesure(element)
|
13
9
|
xy = element.location
|
14
10
|
wh = element.size
|
15
11
|
return xy.x, xy.y, wh.width, wh.height
|
16
12
|
end
|
17
13
|
|
18
|
-
def get_dynamic_tag(
|
19
|
-
tag =
|
20
|
-
tag = "\#{tag}" if vars[:tag] == tag
|
14
|
+
def get_dynamic_tag(tag, vars)
|
15
|
+
tag = "\#{tag}" if vars[:tag].to_s.casecmp(tag).zero?
|
21
16
|
push_hash('tag', tag, 'tag')
|
22
17
|
end
|
23
18
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
19
|
+
def text_array(text)
|
20
|
+
text.to_s.tr("\n", ' ').split(/['" ]/)
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_dynamic_text(text, vars)
|
24
|
+
attrs = text_array(text).map do |word|
|
27
25
|
final = if !vars[:text].to_s.strip.empty?
|
28
26
|
word.gsub(vars[:text].to_s, "\#{text}")
|
29
27
|
else
|
30
28
|
word
|
31
29
|
end
|
32
|
-
|
30
|
+
push_hash('text', final, 'text') unless final.empty?
|
33
31
|
end
|
34
|
-
attrs
|
35
|
-
end
|
36
|
-
|
37
|
-
def process_dimension(name, value, vars)
|
38
|
-
s_name = name.to_s
|
39
|
-
value = value.to_s.gsub(vars[name], "\#{#{s_name}}") if vars[name]
|
40
|
-
value
|
32
|
+
attrs.compact
|
41
33
|
end
|
42
34
|
|
43
|
-
def
|
44
|
-
x, y, width, height = mesure(element)
|
45
|
-
x = process_dimension(:x, x, vars)
|
46
|
-
y = process_dimension(:y, y, vars)
|
47
|
-
width = process_dimension(:width, width, vars)
|
48
|
-
height = process_dimension(:height, height, vars)
|
49
|
-
return x, y, width, height
|
50
|
-
end
|
51
|
-
|
52
|
-
def get_dimensions(element, vars)
|
35
|
+
def get_dimensions(element)
|
53
36
|
resolution = window_size
|
54
|
-
x, y, w, h =
|
37
|
+
x, y, w, h = mesure(element)
|
55
38
|
push_hash(resolution, "#{x}*#{y}*#{w}*#{h}", 'dimensions')
|
56
39
|
end
|
57
40
|
|
@@ -79,22 +62,6 @@ module Locatine
|
|
79
62
|
end
|
80
63
|
attrs
|
81
64
|
end
|
82
|
-
|
83
|
-
##
|
84
|
-
# Collecting attributes of the element
|
85
|
-
def get_attributes(element)
|
86
|
-
attributes = element.attributes
|
87
|
-
array = []
|
88
|
-
attributes.each_pair do |name, value|
|
89
|
-
next if name.to_s == 'locatineclass' # Should never happen
|
90
|
-
|
91
|
-
value.split(/['" ]/).reject(&:empty?).uniq.each do |part|
|
92
|
-
array.push('name' => name.to_s, 'type' => 'attribute',
|
93
|
-
'value' => part)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
array
|
97
|
-
end
|
98
65
|
end
|
99
66
|
end
|
100
67
|
end
|