locatine 0.02637 → 0.03050

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +266 -297
  3. data/bin/locatine-daemon.rb +4 -2
  4. data/lib/locatine.rb +16 -2
  5. data/lib/locatine/daemon.rb +45 -59
  6. data/lib/locatine/daemon_helpers/methods.rb +93 -0
  7. data/lib/locatine/element.rb +46 -0
  8. data/lib/locatine/error.rb +14 -0
  9. data/lib/locatine/logger.rb +73 -0
  10. data/lib/locatine/results.rb +124 -0
  11. data/lib/locatine/results_helpers/common.rb +61 -0
  12. data/lib/locatine/results_helpers/comparing.rb +51 -0
  13. data/lib/locatine/results_helpers/config.rb +48 -0
  14. data/lib/locatine/results_helpers/find_by_magic.rb +123 -0
  15. data/lib/locatine/results_helpers/guess.rb +47 -0
  16. data/lib/locatine/results_helpers/info_generator.rb +77 -0
  17. data/lib/locatine/{for_search → results_helpers}/xpath_generator.rb +19 -18
  18. data/lib/locatine/scripts/element.js +40 -0
  19. data/lib/locatine/scripts/page.js +54 -0
  20. data/lib/locatine/scripts/parent.js +6 -0
  21. data/lib/locatine/session.rb +147 -0
  22. data/lib/locatine/version.rb +4 -2
  23. metadata +42 -49
  24. data/lib/locatine/app/background.js +0 -8
  25. data/lib/locatine/app/content.css +0 -38
  26. data/lib/locatine/app/content.js +0 -152
  27. data/lib/locatine/app/devtools.html +0 -1
  28. data/lib/locatine/app/devtools.js +0 -3
  29. data/lib/locatine/app/manifest.json +0 -20
  30. data/lib/locatine/app/popup.css +0 -47
  31. data/lib/locatine/app/popup.html +0 -19
  32. data/lib/locatine/app/popup.js +0 -65
  33. data/lib/locatine/daemon_helpers.rb +0 -52
  34. data/lib/locatine/for_search.rb +0 -6
  35. data/lib/locatine/for_search/data_generate.rb +0 -67
  36. data/lib/locatine/for_search/data_logic.rb +0 -98
  37. data/lib/locatine/for_search/defaults.rb +0 -40
  38. data/lib/locatine/for_search/dialog_logic.rb +0 -107
  39. data/lib/locatine/for_search/element_selection.rb +0 -80
  40. data/lib/locatine/for_search/file_work.rb +0 -67
  41. data/lib/locatine/for_search/find_by_guess.rb +0 -67
  42. data/lib/locatine/for_search/find_by_locator.rb +0 -59
  43. data/lib/locatine/for_search/find_by_magic.rb +0 -65
  44. data/lib/locatine/for_search/find_logic.rb +0 -79
  45. data/lib/locatine/for_search/helpers.rb +0 -106
  46. data/lib/locatine/for_search/highlight.rb +0 -41
  47. data/lib/locatine/for_search/listening.rb +0 -48
  48. data/lib/locatine/for_search/merge.rb +0 -40
  49. data/lib/locatine/for_search/name_helper.rb +0 -51
  50. data/lib/locatine/for_search/page_work.rb +0 -126
  51. data/lib/locatine/for_search/public.rb +0 -179
  52. data/lib/locatine/for_search/saying.rb +0 -199
  53. data/lib/locatine/large_scripts/css.js +0 -21
  54. data/lib/locatine/large_scripts/dimensions.js +0 -17
  55. data/lib/locatine/large_scripts/element.js +0 -30
  56. data/lib/locatine/large_scripts/page.js +0 -60
  57. data/lib/locatine/scope.rb +0 -88
  58. data/lib/locatine/search.rb +0 -67
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 8d969a9d3fc96c105d69b1d41f7ff8df92198d6d
4
- data.tar.gz: 93ec07af0558fd08a13224686c9844775f3b36c9
2
+ SHA256:
3
+ metadata.gz: 6ec7ffd6c0a45544ae5cbd20581f72190bbfa3ee5dd2c5e444740c1d03d7ba04
4
+ data.tar.gz: 7552e77cfc41106c4679a99554980f67ad4decc354348ee0d01479c9799687fa
5
5
  SHA512:
6
- metadata.gz: 6f7d7af25a50b8aeb293f3795574999e87debe5adfd3149bb3e9855257a5cf4f47c2f980c57a8582b7b65890abafc665c3d96ab39604eb4ae615bdc636489d11
7
- data.tar.gz: 3883f1e882b88f038dcacd6520d408126e9c82b9178a5cfb2d5069d4b14a7327e25651d08a45ab447f8b4a0366341482aefc7414668d06e08aeebaa13ef2ffd0
6
+ metadata.gz: 6d42ad78b43ff2335357546b50daa98734e1d06527e26fc6608540135ffb73d4f1aebcf9b5e3fb44c74b3125a31045b3206a8ddabdc0c08211f28f71390fdc9f
7
+ data.tar.gz: 2b752ff4e3a7b60ae8eb12f585eef9df6445f3c659169e7470bcfbc18f848938762815a8a977ce4825be250f527a425b0c2ff1b96d52240db4223e9d1d8a201e
data/README.md CHANGED
@@ -1,26 +1,24 @@
1
1
  # Locatine
2
2
 
3
- Element location tool based on Watir.
3
+ Locatine is a proxy between your code and selenium.
4
4
 
5
- You are asking Locatine to find element for you.
5
+ It remembers element when you are finding it via selenium.
6
6
 
7
- It is asking you what element do you mean.
7
+ It stores the findings to the json file for the future use.
8
8
 
9
- It is remembering your answer and collecting information about selected element.
9
+ On the next search if element is lost Locatine will try to find it anyway using previously collected information.
10
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.
11
+ So your locator will be a little bit more stable than before.
14
12
 
15
13
  That's it.
16
14
 
17
15
  ## Stage of development:
18
16
 
19
- Version of Locatine is **0.02637** only. It means so far this is an alfa. You can use it in a real project if you are a risky person.
17
+ Version of Locatine is **0.03050**. The 4th version since rewriting. 5-15 next versions is dedicated to bug fixing, tweaking.
20
18
 
21
19
  ## Attention
22
20
 
23
- Beginning with the next version locatine (especially locatine-daemon) will be refactored dramatically. It is highly possible that the next version will be not completable with that one.
21
+ This version of Locatine is not compatible to previous.
24
22
 
25
23
  ## Installation
26
24
 
@@ -40,469 +38,440 @@ Or install it yourself as:
40
38
 
41
39
  ## Usage
42
40
 
43
- 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
44
- 2. Write the code
41
+ 1. Be sure that you have [Chrome browser](https://www.google.com/chrome/browser/desktop/) installed.
42
+ 2. Be sure that you have [chromedriver](https://chromedriver.chromium.org/) installed.
43
+ 3. Write the code (using your language - ruby is an example)
45
44
 
46
45
  ```ruby
47
- require 'locatine'
48
- s = Locatine::Search.new
49
- s.browser.goto("yourpage.com.com")
50
- s.find(name: "element", scope: "Main").click
46
+ driver = Selenium::WebDriver.for :remote, url: "http://localhost:7733/wd/hub", desired_capabilities: :chrome
47
+ driver.navigate.to("yourpage.com.com")
48
+ driver.find_element(xpath: "//*[@id = 'element']")
49
+ driver.quit
51
50
  ```
52
51
 
53
- 3. Run it in terminal with parameter LEARN=1 approximately like:
52
+ 4. Run the locatine daemon
54
53
 
55
- $ LEARN=1 ruby path_to_your_test.rb
54
+ ```
55
+ $ SELENIUM=http://localhost:4444 locatine-daemon.rb --port=7733
56
+ ```
56
57
 
57
- 4. It will open the browser and transfer you to the yourpage.com.com
58
- 5. Click Locatine application icon at the browser panel
59
- 6. Select element to represent *element* in the *Main* scope (you can click on it or select it in devtools)
60
- 7. And confirm the selection
58
+ 5. SELENIUM - is for url where selenium hub is started, port is the port for your code to connect. 4444 and 7733 are defaults.
59
+ 6. Run your code
60
+ 7. Data of element will be stored to ./default.json file.
61
+ 8. Now if id of your element is changed on the page Locatine will show a warning and gonna try to retrieve it.
62
+ 9. See [example](https://github.com/sseleznevqa/locatine/tree/master/examples) to see how it really works.
61
63
 
62
- ![U can see an image here. On github)](readme/567.png)
64
+ ## Session
63
65
 
64
- 8. Now you can run the test without LEARN parameter and it will work.
66
+ Each time when you initializing new selenium session, locatine session is created as well.
65
67
 
66
- ## Locatine app window
68
+ It is used to store default options for search.
67
69
 
68
- ### Element name
70
+ There are two ways to set options of the session:
69
71
 
70
- You can ask the app to save element with any name. This name should be used for element finding later.
72
+ The most simple is to send it via desired capabilities like (ruby example again)
71
73
 
72
- ### Waiting for click
74
+ ```ruby
75
+ caps = Selenium::WebDriver::Remote::Capabilities.
76
+ chrome('locatine' => {'json' => '/your/path/elements.json'})
77
+ driver = Selenium::WebDriver.
78
+ for :remote, url: "http://localhost:7733/wd/hub", desired_capabilities: caps
79
+ ```
73
80
 
74
- 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.
81
+ This way is recommended because of the simplicity.
75
82
 
76
- ### Single\\Collection mode
83
+ Another way is to set options after the session was created by making [POST request to '/locatine/session/%session_id%'](https://github.com/sseleznevqa/locatine#post-to-wdhubsession)
77
84
 
78
- 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
85
+ ## Settings to pass
79
86
 
80
- ### Clear selection
87
+ ### json
81
88
 
82
- Click it to start element selection process from the very beginning.
89
+ Provide a string here.
83
90
 
84
- ### Abort selection
91
+ Default is 'locatine-working-dir/locatine_files/default.json'
85
92
 
86
- 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.
93
+ **json** is the path of the file that will be used to store data collected about elements by locatine-daemon.
87
94
 
88
- ### Confirm selection
95
+ It is recommended to use a different json for every test (or test group) in order to have lots of small and easily readable files and not the giant one.
89
96
 
90
- When you've selected a correct element - confirm it in order to save.
97
+ **NOTE! Only unix filesystems are supported so far.**
91
98
 
92
- ## Locatine::Search options
99
+ ### depth
93
100
 
94
- ```ruby
95
- Locatine::Search.new(json: "./Locatine_files/default.json",
96
- depth: 3,
97
- browser: nil,
98
- learn: ENV['LEARN'].nil? ? false : true,
99
- stability_limit: 1000,
100
- scope: "Default",
101
- tolerance: 33,
102
- visual_search: false,
103
- no_fail: false,
104
- trusted: [],
105
- untrusted: [],
106
- autolearn: nil)
107
- ```
101
+ Provide a non negative integer here.
108
102
 
109
- ### json
103
+ Default is 3
110
104
 
111
- the file where data collected about elements will be stored
105
+ **depth** value shows how deeply locatine will research the element.
112
106
 
113
- ### depth
107
+ 0 - means that only the retrieved element will be remembered.
114
108
 
115
- shows how many info will be stored about each element
109
+ ```html
110
+ <div id="element to find"></div>
111
+ ```
116
112
 
117
- - 0 = everything about the element
118
- - 1 = everything about the element and the parent of it
119
- - 2 = everything about the element and the parent of it + one more parent
113
+ 1 - means that the retrieved element and its ancestor will be examined:
120
114
 
121
- ### browser
115
+ ```html
116
+ <div id="ancestor">
117
+ <div id="element to find"></div>
118
+ </div>
119
+ ```
122
120
 
123
- if not provided new Watir::Browser will be started. Do not provide browser if you are going to use learn mode
121
+ 2 - means that two ancestors will be remembered.
124
122
 
125
- ### learn
123
+ And so on and so forth.
126
124
 
127
- mode is used to train locatine to search your elements. By default is false. But if you are starting your test like:
125
+ The higher depth the more easily locatine-daemon will find the element including the case when the element is lost (attributes are changed).
128
126
 
129
- $ LEARN=true ruby path_to_your_test.rb
127
+ On the other side large depth is making internal locator more unstable since it is requires stability of all the ancestors as well.
130
128
 
131
- it will turn learn to true by default.
129
+ Read more about [finding elements](https://github.com/sseleznevqa/locatine#finding-elements)
132
130
 
133
- ### scope
131
+ **NOTE! Taking additional information (larger depth) costs an additional time while retrieving element and when looking for the lost one**
134
132
 
135
- is a setting that is representing default scope (group) where elements will be stored by default
133
+ ### trusted
136
134
 
137
- ### stability_limit
135
+ Provide an array of strings here.
138
136
 
139
- 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.
137
+ Default is []
140
138
 
141
- ### tolerance
139
+ Usually if some attribute or tag or text is changing locatine is marking it as untrusted data and not using it in search till it appears enough times to be considered stable again.
142
140
 
143
- 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).
141
+ But locatine will always use for search for element something that is listed as trusted here.
144
142
 
145
- ### visual_search
143
+ Use attribute name (like ['id']) for id attribute, use ['text'] for text and ['tag'] to trust the tag of element.
146
144
 
147
- 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.
145
+ **NOTE! ['text'] will affect both - text of element and attribute of element that is named 'text'**
148
146
 
149
- 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.
147
+ ### untrusted
150
148
 
151
- Be careful! Set true only if appearance of your page is pretty stable.
149
+ Provide an array of strings here.
152
150
 
153
- ### no_fail
151
+ Default is []
154
152
 
155
- 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.
153
+ Working completely opposite to [trusted](https://github.com/sseleznevqa/locatine#trusted)
156
154
 
157
- ### trusted/untrusted
155
+ Locatine will never consider reliable attributes or text or tag if it is listed as untrusted. It will be never used to search elements.
158
156
 
159
- 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)
157
+ Use attribute name (like ['id']) for id attribute, use ['text'] for text and ['tag'] to untrust the tag of element.
160
158
 
161
- On the other hand you can force locatine to always trust something with trusted.
159
+ **NOTE! ['text'] will affect both - text of element and attribute of element that is named 'text'**
162
160
 
163
- ### autolearn
161
+ ### stability
164
162
 
165
- Determines wether Locatine will study elements by default or not.
163
+ Provide a positive integer here.
166
164
 
167
- 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.
165
+ Default is 10
168
166
 
169
- If false Locatine will study element only in case when it is lost.
167
+ At first locatine trusts everything except [untrusted](https://github.com/sseleznevqa/locatine#untrusted). But sometimes attribute value, text or tag changes. In that case those new values will be not used in search anymore.
170
168
 
171
- 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).
169
+ **stability** shows how much times new attribute or text or tag should be found without changes to be considered reliable to be used for search once again.
172
170
 
173
- Notice that if autolearn is false Locatine is not bumping the stability values of attributes for elements which were found normally.
171
+ ### tolerance
174
172
 
175
- ## Changing options on fly
173
+ Provide an integer in a range from 0 to 100
176
174
 
177
- You can get or set these values on fly. Like:
175
+ Default is 50
178
176
 
179
- ```ruby
180
- s = Locatine::Search.new(learn: true)
181
- s.learn #=> true
182
- s.learn = false
183
- ```
177
+ If your element is changed locatine is trying to find it anyway. Tolerance shows how similar the new element should be to the ild one to be returned.
184
178
 
185
- ## Locatine::Search find options
179
+ Example:
186
180
 
187
- ```ruby
188
- s.find(name: "some name",
189
- scope: "Default",
190
- exact: false,
191
- locator: {},
192
- vars: {},
193
- look_in: nil,
194
- iframe: nil,
195
- return_locator: false,
196
- collection: false,
197
- tolerance: nil,
198
- no_fail: nil,
199
- trusted: [],
200
- untrusted: [])
181
+ ```html
182
+ <!--Old element-->
183
+ <div id="too" class="old one">element</div>
184
+ <!--New element-->
185
+ <div id="too" class="new one">element</div>
201
186
  ```
202
- ### name
203
-
204
- 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.
205
187
 
206
- ### scope
188
+ 5 pieces of information will be collected about an old element (tag == div, id == too, class == old, class ==one, text == element)
207
189
 
208
- group of elements. Must be uniq per file. This is to help to store elements with same names from different pages in one file
190
+ 4 parts are staying the same after changes. So similarity will be counted like (4*100/5 == 80)
209
191
 
210
- ### locator
192
+ Since similarity(80)>=100-tolerance(50) the new element will be returned.
211
193
 
212
- you may provide your own locator to use. Same syntax as in Watir:
194
+ But for the case:
213
195
 
214
- ```ruby
215
- find(name: "element with custom locator", locator: {xpath: "//custom"})
196
+ ```html
197
+ <!--Old element-->
198
+ <div id="too" class="old one">element</div>
199
+ <!--New element-->
200
+ <div id="lost" class="new two">element</div>
216
201
  ```
217
202
 
218
- ### vars
203
+ Similarity will be only 40 which is less than 100-50. It means that no element will be returned.
219
204
 
220
- are used to pass dynamic attributes.
205
+ So default tolerance == 50 means that at least 50% of element data should be equal to stored data for element to be found.
221
206
 
222
- For example you have created an account on your site with
207
+ Only data of the element itself is counted here.
223
208
 
224
- ```ruby
225
- name == "stablePart_qljcrt24jh"
226
- ```
209
+ **NOTE! 0 (zero tolerance) means that locatine will not even try to find the lost element. 100 tolerance is too opportunistic**
227
210
 
228
- where
211
+ ### timeout
229
212
 
230
- ```ruby
231
- random_string == "qljcrt24jh"
232
- ```
213
+ Provide an positive integer here (up to 25 is recommended)
233
214
 
234
- was generated by random. Now you need to find the element with this part on the page. You can do
215
+ Default is 25
235
216
 
236
- ```ruby
237
- random_string #=> "qljcrt24jh"
238
- find(name: "account name", vars: {text: random_string})
239
- ```
217
+ **timeout** shows the maximum amount of seconds locatine will try to find something.
240
218
 
241
- Next time when your test will generate another random_string it will use new value.
219
+ Since default net timeout for most selenium implementations is 30 seconds, 25 is a good idea.
242
220
 
243
- ```ruby
244
- vars = {text: random_substring} # If you want the text of element to be dynamic
245
- vars = {tag: random_tag} # The tag
246
- vars = {attribute_name: random_attr} # If attribute is dynamic (use name of the attribute)
247
- # And two lines work with visual_search == true only
248
- vars = {css_option: random_value} # If your css is dynamic
249
- vars = {x: random_x} # x, y for element position
250
- ```
221
+ **NOTE! It's not an exact time. When timeout is reached it means for locatine that it is time to finish the party. But it cannot be finished instantly and the speed of the process may slightly vary.**
251
222
 
252
- And if you do not like it you can do:
223
+ ## Finding elements
224
+
225
+ All the requests that are retrieving elements are wrapped by locatine-daemon.
253
226
 
254
227
  ```ruby
255
- random_string #=> "qljcrt24jh"
256
- find(name: "account name", locator:{text: "stablePart_#{random_string}")
228
+ caps = Selenium::WebDriver::Remote::Capabilities.chrome('locatine' =>
229
+ {'json' => "#{Dir.pwd}/elements.json"})
230
+ driver = Selenium::WebDriver.for :remote,
231
+ url: "http://localhost:7733/wd/hub",
232
+ desired_capabilities: caps
233
+ # Page that has <div id='element' class='1 2 3'>Text</div>
234
+ driver.navigate.to page 1
235
+ # Getting element for the first ешьу
236
+ element = driver.find_element(xpath: "//*[@id='element']")
237
+ # We are changing id of the element
238
+ driver.execute_script("arguments[0].setAttribute('id', 'lost')", element)
239
+ # Element is gonna be found. Even with locator that is broken
240
+ expect(driver.find_element(xpath: "//*[@id='element']").text).to eq "Text"
241
+ driver.quit
257
242
  ```
258
243
 
259
- ### look_in
260
-
261
- 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.
262
-
263
- ### iframe
244
+ When locatine sees some locator for the first time it is not only returning the element& It is collecting some info about it. As the result if locator will suddenly become broken locatine will make an attempt to find the element using the data collected.
264
245
 
265
- that is in order to find element inside of an iframe
246
+ ### Magic comments
266
247
 
267
- ### return_locator
248
+ If the usual locator only is provided locatine will treat it like a name for element. But if you want you can force locatine to remember it by name defined by you.
268
249
 
269
- 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.
250
+ Just add name at the end of xpath like ['*name*'] or like /\**name*\*/ after css selector. For example:
270
251
 
271
- ### collection
272
-
273
- if true array of elements will be returned. If false only the one element (the first one found) will be returned.
252
+ ```ruby
253
+ element = driver.find_element(xpath: "//*[@id='element']['test element']")
254
+ element = driver.find_element(css: "#element/*test element*/")
255
+ ```
274
256
 
275
- ### tolerance
257
+ **NOTE! Those locators are valid. If you will switch back to selenium-webdriver it will work normally. The 'test element' text will be treated like a comment that is not affecting the locator body.**
276
258
 
277
- You can state custom tolerance for the element.
259
+ Once defined with name it can be called without locator part at all:
278
260
 
279
- ### exact
261
+ ```ruby
262
+ element = driver.find_element(xpath: "['test element']")
263
+ element = driver.find_element(css: "/*test element*/")
264
+ ```
280
265
 
281
- It is disabling attempts to find element by advanced algorithms. If locator is provided find method will use only locator. If there is no locator only the basic search will be performed.
266
+ **NOTE! Locators above will work only with locatine!**
282
267
 
283
- ### no_fail
268
+ ### Dynamic locators
284
269
 
285
- no_fail option that will work for that search only.
270
+ Always add names to dynamic locators. For example if you have some account_id which is new for every test and goes to an id attribute do
286
271
 
287
- ### trusted//untrusted
272
+ ```ruby
273
+ account_id #=> 1234567890
274
+ element = driver.find_element(xpath: "//*[@id='#{account_id}']['test element']")
275
+ element = driver.find_element(css: "##{account_id}/*test element*/")
276
+ ```
288
277
 
289
- You can set trusted elements just for search
278
+ **NOTE! If there will be no name for the element that is changing locator for each run locatine will treat it like a new element each time. That will lead to overcrowding of your json file**
290
279
 
291
- ## Scope
280
+ ### Zero tolerance shortcut
292
281
 
293
- If you want to define a whole bunch of elements at once you can do:
282
+ If you need to check that element exists or not most probably you do not want locatine to look for the similar one. You can set zero tolerance (return only 100% same element) by adding word 'exactly' to the name.
294
283
 
295
284
  ```ruby
296
- search = Locatine::Search.new(learn: true)
297
- search.get_scope(name: "Name of scope") # Will ask you about all the elements in scope
298
- # or
299
- scope = Locatine::Scope.new('Name of scope', search)
300
- scope.define
285
+ element = driver.
286
+ find_element(xpath: "//*[@id='element']['exactly test element']")
287
+ element = driver.find_element(css: "#element/*exactly test element*/")
301
288
  ```
302
289
 
303
- 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.
290
+ **NOTE! Zero tolerance will be used for that particular search only. Other searches will use session values**
291
+
292
+ ### Other ways to pass data to locatine
304
293
 
305
- You can force use dynamic variables on define where is possible (same rules as for find):
294
+ There is another way to pass data to locatine. You can provide a json string as a comment.
306
295
 
307
296
  ```ruby
308
- vars = {text: "dynamic text",
309
- tag: "span",
310
- attrName: "part of dynamic attr-value"}
311
- scope.define(vars) # Will ask you about all the elements in scope
312
- # Same
313
- search.get_scope(name: "Name of scope", vars: vars) # Will ask you...
314
- # Finally when scope is defined
315
- search.find(scope: "Name of scope",
316
- name: "Name of element in scope",
317
- vars: vars # Necessary if elements were defined with vars)
297
+ require 'json' # That is to make everything a little bit simpler
298
+ params = {name: "test element", tolerance: 0}.to_json
299
+ #=> {\"name\":\"test element\",\"tolerance\":0}
300
+ element = driver.find_element(xpath: "//*[@id='element'][#{params}']")
301
+ element = driver.find_element(css: "#element/*#{params}*/")
318
302
  ```
319
303
 
320
- ### Methods of scope
304
+ **NOTE! Those requests will provide same results as previous because they have identical meaning**
321
305
 
322
- If you want to get a hash with all elements that are defined in scope:
306
+ Like that you can set for each search any custom options (except json)
323
307
 
324
- ```ruby
325
- # Lost elements will be found if possible!
326
- search.get_scope(name: "Name of scope").all
327
- # => {"element name": {
328
- # elements: Array of Watir::Element,
329
- # locator: valid xpath locator
330
- # },
331
- # "next element name":...
332
- # }
333
- ```
308
+ For more information about possible options read [here](https://github.com/sseleznevqa/locatine#session)
334
309
 
335
- If you want to check presence of all elements:
310
+ Additionally you can provide a custom locator inside of the comment json string.
311
+
312
+ We are using 'json' library to make json string here.
336
313
 
337
314
  ```ruby
338
- # Will raise an error if something lost!
339
- search.get_scope(name: "Name of scope").check
315
+ require 'json'
316
+ xpath_params = {name: "test element",
317
+ tolerance: 0,
318
+ locator: {using: "xpath", value: "//*[@id='element']"}}.to_json
319
+ css_params = {name: "test element",
320
+ tolerance: 0,
321
+ locator: {using: "css selector", value: "#element"}}.to_json
322
+ element = driver.find_element(xpath: "['#{xpath_params}']")
323
+ element = driver.find_element(css: "/*#{css_params}*/")
340
324
  ```
341
325
 
342
- ## Other ways to use find
326
+ For more information about locators read about [locator strategies](https://www.w3.org/TR/webdriver/#locator-strategies)
343
327
 
344
- If the scope is set and you do not want to provide any additional options you can do:
328
+ ### locatine locator strategy
345
329
 
346
- ```ruby
347
- s = Locatine::Search.new
348
- s.find("just name of element")
349
- ```
330
+ Locatine also provides its own locator strategy == 'locatine'. In order to use it you need to inject it to the code of selenium-webdriver implementation.
350
331
 
351
- Also you can do:
332
+ See it's done for ruby [here](https://github.com/sseleznevqa/locatine/tree/master/spec/e2e_spec.rb#L5-L11)
333
+
334
+ When it's done you can use:
352
335
 
353
336
  ```ruby
354
- s = Locatine::Search.new
355
- s.browser.button(s.lctr("name of the button"))
356
- # or
357
- s.browser.button(s.lctr(name: "name of the button", scope: "Some form"))
358
- # or
359
- s.browser.button(s.lctr("name of the button", scope: "Some form"))
337
+ require 'json'
338
+ xpath_params = {name: "test element",
339
+ tolerance: 0,
340
+ locator: {using: "xpath", value: "//*[@id='element']"}}.to_json
341
+ css_params = {name: "test element",
342
+ tolerance: 0,
343
+ locator: {using: "css selector", value: "#element"}}.to_json
344
+ element = driver.find_element(locatine: xpath_params)
345
+ element = driver.find_element(locatine: css_params)
346
+ # As well as
347
+ element = driver.find_element(locatine: "test element")
348
+ # And also
349
+ element = driver.find_element(locatine: "exactly test element")
360
350
  ```
361
351
 
362
- That may be helpful in case of migration from plain watir to watir + locatine
352
+ ### Locatine locators
363
353
 
364
- If you want to find collection of elements you can use:
354
+ In some cases you can even forget about classic locators
365
355
 
366
- ```ruby
367
- s = Locatine::Search.new
368
- s.collect("group of elements") # Will return an array
369
- # or
370
- s.collect(name: "group of elements")
356
+ For example have element
357
+
358
+ ```html
359
+ <input id="important" type="button" value="click me"></input>
371
360
  ```
372
361
 
373
- Also:
362
+ Let's pretend that id == important is a uniq attribute for the page (it should be so). In that case you can do:
374
363
 
375
364
  ```ruby
376
- s.exact_collection(name: "something") == s.collect(exact: true, name: "something")
377
- s.exact(name: "something") == s.find(name: "something", exact: true)
378
- s.check(name: "something") == s.find(name: "something", tolerance: 0)
379
- s.check_collection(name: "something") == s.collect(name: "something", tolerance: 0)
365
+ element = driver.find_element(css: "/*important input*/")
380
366
  ```
381
367
 
382
- ## Using as a daemon
368
+ Locatine will try to find it by those two words. If the id is really uniq it will return the desired element.
383
369
 
384
- Locatine daemon is a web server based on sinatra. You can run it from your code like:
370
+ There is a [Locatine Name Helper chrome extension](https://chrome.google.com/webstore/detail/locatine-locator-helper/heeoaalghiamfjphdlieoblmodpcficg). This app is for creating good locatine locators (it is creating a pair - most uniq attribute value + tag for selected element, elements). Note that the app is an early draft. It's gonne be better with time.
385
371
 
386
- ```ruby
387
- require 'locatine'
388
- Locatine::Daemon.set :port, 7733 #Your port goes here
389
- Locatine::Daemon.run!
390
- ```
372
+ **NOTE! Locatine locators case insensitive.**
391
373
 
392
- Also you can do it with terminal:
374
+ **NOTE! Locatine does not count text while looking for element.**
393
375
 
394
- ```bash
395
- locatine-daemon.rb -port=7733
396
- ```
376
+ **NOTE! Locatine tends to think that the last word of your locator is a tag**
377
+
378
+ ## Locatine daemon API
397
379
 
398
- You can see a python3 example in the [example](https://github.com/sseleznevqa/locatine/tree/master/example) folder. Main idea is
380
+ When locatine-daemon is started it is reacting to several requests:
381
+ Almost all post data should be transfered as a valid json.
399
382
 
400
- 1. Run daemon
401
- 2. Ask daemon for the app path
402
- 3. Run your browser with the app as extension
403
- 4. Turn on the learn
404
- 5. Provide data to the daemon for connect (browser name, session_id, connect url, proxy)
405
- 6. Use API calls to teach daemon how to find elements
406
- 7. After that you can start browser without the app
407
- 8. Provide data for connect
408
- 9. Now you can ask daemon to find your element via API call. And it will answer with a valid xpath you can use.
383
+ ### GET to '/'
409
384
 
410
- ### API
385
+ Redirect to this page.
411
386
 
412
- #### GET call to /app
387
+ ### GET to '/locatine/stop'
413
388
 
414
- returns path to locatine application in order to start chrome with it.
389
+ Stops locatine-daemon
415
390
 
416
- Example of response:
391
+ Returns
417
392
 
418
393
  ```
419
- {"app": "/some/path/to/app"}
394
+ {"result": "dead"}
420
395
  ```
421
396
 
422
- #### GET call to /stop
423
-
424
- stops Locatine daemon.
397
+ ### POST to '/locatine/session/%session_id%'
425
398
 
426
- Returns:
399
+ Data to post (example):
427
400
 
428
401
  ```
429
- {"result": "dead"}
402
+ "{\"json\":\"./daemon.json\"}"
430
403
  ```
431
404
 
432
- #### POST call to /connect
405
+ That will force session with *%session_id%* number to read\write data using ./daemon.json file.
433
406
 
434
- allows Locatine Daemon to connect existing browser instance
407
+ For more information about possible options read [here](https://github.com/sseleznevqa/locatine#session)
435
408
 
436
- POST data:
409
+ Response:
437
410
 
438
411
  ```
439
- {'browser': 'chrome', 'session_id': session_id, 'url': 'http://whatever_is_browser_ip:port_opened_by_browser_for_selenium', 'proxy': 'optionally' }
412
+ { \"results\": {\"json\":\"./daemon.json\"} }
440
413
  ```
441
414
 
442
- Answer:
415
+ ### POST to '/wd/hub/session'
416
+
417
+ Just the same rules as for [usual selenium session](https://www.w3.org/TR/webdriver/#new-session-0)
418
+
419
+ The only change that you can set locatine defaults via providing it in desired capabilities like:
443
420
 
444
421
  ```
445
- {"result": "true"}
422
+ {...
423
+ "desiredCapabilities": {
424
+ ...
425
+ "locatine": {"json": "./daemon.json"},
426
+ ...
427
+ }
428
+ }
446
429
  ```
447
430
 
448
- #### POST call to /set
431
+ That will force new session to read\write data using ./daemon.json file.
449
432
 
450
- is to control options of locatine search. Sending to set data ==
433
+ For more information about possible options read [here](https://github.com/sseleznevqa/locatine#session)
451
434
 
452
- ```
453
- {"learn": "true"}
454
- ```
435
+ ### POST to '/wd/hub/session/%session_id%/element'
455
436
 
456
- Answer:
437
+ That will try to return element using %session_id%.
457
438
 
458
- ```
459
- {"result": "true"}
460
- ```
439
+ Rules are the same as for [usual selenium call](https://www.w3.org/TR/webdriver/#find-element)
461
440
 
462
- is the same as
441
+ But you can provide magic locator comments for xpath and css. Or use 'locatine' element retrieve strategy.
463
442
 
464
- ```ruby
465
- search.learn = true
466
- ```
443
+ More information is [here](https://github.com/sseleznevqa/locatine#finding-elements)
467
444
 
468
- #### POST call to /lctr
445
+ ### POST to '/wd/hub/session/%session_id%/element/%element_id%/element'
469
446
 
470
- is to find and return locator of an element found by locatine
447
+ That will try to return element under %element_id% using %session_id%.
471
448
 
472
- POST data just the same as for find or lctr method. It's like:
449
+ Rules are the same as for [usual selenium call](https://www.w3.org/TR/webdriver/#find-element-from-element)
473
450
 
474
- ```
475
- {"name": "some name", "scope": "Default", "exact": "false" ...}
476
- ```
451
+ But you can provide magic locator comments for xpath and css. Or use 'locatine' element retrieve strategy.
477
452
 
478
- Answer:
453
+ More information is [here](https://github.com/sseleznevqa/locatine#finding-elements)
479
454
 
480
- ```
481
- {"xpath": "//YOUR[@xpath='goes here']"}
482
- ```
455
+ ### POST to '/wd/hub/session/%session_id%/elements'
483
456
 
484
- ### GET /chromedriver || /geckodriver || /iedriver
457
+ That will try to return element using %session_id%.
485
458
 
486
- returns path to the binary retrieved by locatine (using webdrivers gem)
459
+ Rules are the same as for [usual selenium call](https://www.w3.org/TR/webdriver/#find-elements)
487
460
 
488
- Answer:
461
+ But you can provide magic locator comments for xpath and css. Or use 'locatine' element retrieve strategy.
489
462
 
490
- ```
491
- {"path": "path/to/the/binary"}
492
- ```
463
+ More information is [here](https://github.com/sseleznevqa/locatine#finding-elements)
493
464
 
494
- ### POST call to /chromedriver || /geckodriver || /iedriver
465
+ ### POST to '/wd/hub/session/%session_id%/element/%element_id%/elements'
495
466
 
496
- is to force locatine to use your webdriver (for example for using old version of browser)
467
+ That will try to return element under %element_id% using %session_id%.
497
468
 
498
- POST data:
469
+ Rules are the same as for [usual selenium call](https://www.w3.org/TR/webdriver/#find-elements-from-element)
499
470
 
500
- ```
501
- {"version": "2.46"}
502
- ```
471
+ But you can provide magic locator comments for xpath and css. Or use 'locatine' element retrieve strategy.
503
472
 
504
- Answer:
473
+ More information is [here](https://github.com/sseleznevqa/locatine#finding-elements)
505
474
 
506
- ```
507
- {"version": "2.46"}
508
- ```
475
+ ### Other calls to /wd/hub...
476
+
477
+ Any other call will be simply redirected to selenium webdriver hub.