Skip to content

Commit d663857

Browse files
committed
Generate better selectors in "--recorder" mode
1 parent 601d298 commit d663857

File tree

2 files changed

+108
-83
lines changed

2 files changed

+108
-83
lines changed

seleniumbase/extensions/recorder.zip

276 Bytes
Binary file not shown.

seleniumbase/js_code/recorder_js.py

Lines changed: 108 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
if (el.hasAttribute(attr) &&
3737
el.getAttribute(attr).length > 0) {
3838
the_attr = el.getAttribute(attr);
39+
if (the_attr.includes('"'))
40+
the_attr = the_attr.replace('"', '\\"');
41+
if (the_attr.includes("'"))
42+
the_attr = the_attr.replace("'", "\\'");
3943
selector += '[' + attr + '="' + the_attr + '"]';
4044
path.unshift(selector);
4145
break;
@@ -56,9 +60,8 @@
5660
var ssOccurrences = function(string, subString, allowOverlapping) {
5761
string += '';
5862
subString += '';
59-
if (subString.length <= 0) {
63+
if (subString.length <= 0)
6064
return (string.length + 1);
61-
}
6265
var n = 0;
6366
var pos = 0;
6467
var step = allowOverlapping ? 1 : subString.length;
@@ -142,36 +145,57 @@
142145
contains_tags.push('a');
143146
contains_tags.push('b');
144147
contains_tags.push('i');
145-
contains_tags.push('td');
146148
contains_tags.push('h1');
147149
contains_tags.push('h2');
148150
contains_tags.push('h3');
149151
contains_tags.push('h4');
152+
contains_tags.push('td');
150153
contains_tags.push('code');
151154
contains_tags.push('button');
155+
contains_tags.push('strong');
152156
all_by_tag = [];
157+
inner_text = el.innerText.trim();
153158
for (var i = 0; i < contains_tags.length; i++) {
154159
if (tag_name == contains_tags[i] &&
155-
el.innerText.trim().length > 1 &&
156-
el.innerText.trim().length <= 64)
160+
inner_text.length >= 2 && inner_text.length <= 64)
157161
{
158162
t_count = 0;
159-
inner_text = el.innerText.trim();
160163
all_by_tag[i] = document.querySelectorAll(contains_tags[i]);
161164
for (var j = 0; j < all_by_tag[i].length; j++) {
162165
if (all_by_tag[i][j].innerText.includes(inner_text))
163-
{
164166
t_count += 1;
165-
}
166167
}
167-
if (t_count === 1 && !inner_text.includes('\n'))
168-
{
168+
if (t_count === 1 && !inner_text.includes('\n')) {
169169
inner_text = inner_text.replace("'", "\\'");
170170
inner_text = inner_text.replace('"', '\\"');
171171
return tag_name += ':contains("'+inner_text+'")';
172172
}
173173
}
174174
}
175+
if (tag_name == "span" && inner_text.length > 1 && inner_text.length <= 64)
176+
{
177+
parent_element = el.parentElement;
178+
parent_tag_name = parent_element.tagName.toLowerCase();
179+
grand_element = parent_element.parentElement;
180+
grand_tag_name = grand_element.tagName.toLowerCase();
181+
if (parent_tag_name == "button" || grand_tag_name == "button") {
182+
qsa_element = "span";
183+
if (parent_tag_name == "button")
184+
qsa_element = "button > span";
185+
else { qsa_element = "button > "+parent_tag_name+" > span" }
186+
t_count = 0;
187+
all_el_found = document.querySelectorAll(qsa_element);
188+
for (var j = 0; j < all_el_found.length; j++) {
189+
if (all_el_found[j].innerText.includes(inner_text))
190+
t_count += 1;
191+
}
192+
if (t_count === 1 && !inner_text.includes('\n')) {
193+
inner_text = inner_text.replace("'", "\\'");
194+
inner_text = inner_text.replace('"', '\\"');
195+
return qsa_element += ':contains("'+inner_text+'")';
196+
}
197+
}
198+
}
175199
best_selector = selector_by_id;
176200
lowest_child_count = child_count_by_id;
177201
for (var i = 0; i < non_id_attributes.length; i++) {
@@ -187,41 +211,36 @@
187211
188212
var AllTheAnchorTags = document.getElementsByTagName("a");
189213
for (var i = 0; i < AllTheAnchorTags.length; i++) {
190-
AllTheAnchorTags[i].addEventListener('click',
191-
function (event) {
192-
if (this.origin &&
193-
this.origin != 'null' &&
194-
this.origin != document.location.origin)
195-
{
196-
if (this.hasAttribute('href'))
197-
{
198-
event.preventDefault();
199-
window.open(this.href, '_blank').focus();
200-
}
214+
AllTheAnchorTags[i].addEventListener('click', function (event) {
215+
if (this.origin &&
216+
this.origin != 'null' &&
217+
this.origin != document.location.origin)
218+
{
219+
if (this.hasAttribute('href')) {
220+
event.preventDefault();
221+
window.open(this.href, '_blank').focus();
201222
}
202-
},
223+
}
224+
},
203225
false);
204226
}
205227
206228
var reset_recorder_state = function() {
207229
document.recorded_actions = [];
208230
sessionStorage.setItem('pause_recorder', 'no');
209231
const d_now = Date.now();
210-
if (sessionStorage.getItem('recorder_activated') === 'yes')
211-
{
232+
if (sessionStorage.getItem('recorder_activated') === 'yes') {
212233
ss_ra = JSON.parse(sessionStorage.getItem('recorded_actions'));
213234
document.recorded_actions = ss_ra;
214235
w_orig = window.location.origin;
215236
w_href = window.location.href;
216237
ra_len = document.recorded_actions.length;
217-
if (ra_len > 0 &&
218-
document.recorded_actions[ra_len-1][0] === 'begin')
219-
{
238+
if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'begin') {
220239
document.recorded_actions.pop();
221240
document.recorded_actions.push(['begin', w_orig, w_href, d_now]);
222241
}
223242
else if (ra_len > 0 &&
224-
document.recorded_actions[ra_len-1][0] === '_url_')
243+
document.recorded_actions[ra_len-1][0] === '_url_')
225244
{
226245
document.recorded_actions.pop();
227246
document.recorded_actions.push(['_url_', w_orig, w_href, d_now]);
@@ -230,9 +249,8 @@
230249
document.recorded_actions.push(['_url_', w_orig, w_href, d_now]);
231250
}
232251
}
233-
else
234-
{
235-
sessionStorage.setItem('recorder_activated', 'yes')
252+
else {
253+
sessionStorage.setItem('recorder_activated', 'yes');
236254
w_orig = window.location.origin;
237255
w_href = window.location.href;
238256
document.recorded_actions.push(['begin', w_orig, w_href, d_now]);
@@ -280,9 +298,8 @@
280298
{
281299
document.recorded_actions.pop();
282300
}
283-
if (element.draggable === true) {
301+
if (element.draggable === true)
284302
document.recorded_actions.push(['drags', selector, '', d_now]);
285-
}
286303
json_rec_act = JSON.stringify(document.recorded_actions);
287304
sessionStorage.setItem('recorded_actions', json_rec_act);
288305
});
@@ -292,8 +309,7 @@
292309
if (sessionStorage.getItem('pause_recorder') === 'yes')
293310
return;
294311
ra_len = document.recorded_actions.length;
295-
if (ra_len > 0 &&
296-
document.recorded_actions[ra_len-1][0] === 'drags')
312+
if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'drags')
297313
{
298314
document.recorded_actions.pop();
299315
json_rec_act = JSON.stringify(document.recorded_actions);
@@ -309,8 +325,7 @@
309325
const element = event.target;
310326
const selector = getBestSelector(element);
311327
ra_len = document.recorded_actions.length;
312-
if (ra_len > 0 &&
313-
document.recorded_actions[ra_len-1][0] === 'drags')
328+
if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'drags')
314329
{
315330
drg_s = document.recorded_actions[ra_len-1][1];
316331
document.recorded_actions.pop();
@@ -337,26 +352,22 @@
337352
}
338353
else if (tag_name === 'input' && element.type === 'range')
339354
{
340-
if (ra_len > 0 &&
341-
document.recorded_actions[ra_len-1][1] === selector)
355+
if (ra_len > 0 && document.recorded_actions[ra_len-1][1] === selector)
342356
{
343357
document.recorded_actions.pop();
344358
ra_len = document.recorded_actions.length;
345359
}
346360
// Do it twice for click and multiple changes.
347-
if (ra_len > 0 &&
348-
document.recorded_actions[ra_len-1][1] === selector)
361+
if (ra_len > 0 && document.recorded_actions[ra_len-1][1] === selector)
349362
{
350363
document.recorded_actions.pop();
351364
ra_len = document.recorded_actions.length;
352365
}
353366
value = element.value;
354367
document.recorded_actions.push(['set_v', selector, value, d_now]);
355368
}
356-
else if (tag_name === 'input' && element.type === 'file')
357-
{
358-
if (ra_len > 0 &&
359-
document.recorded_actions[ra_len-1][1] === selector)
369+
else if (tag_name === 'input' && element.type === 'file') {
370+
if (ra_len > 0 && document.recorded_actions[ra_len-1][1] === selector)
360371
{
361372
document.recorded_actions.pop();
362373
ra_len = document.recorded_actions.length;
@@ -368,19 +379,14 @@
368379
document.recorded_actions[ra_len-1][1] === selector &&
369380
tag_name === 'input' && element.type === 'checkbox')
370381
{
371-
// The checkbox state only needs to be set once.
382+
// The checkbox state only needs to be set once. (Pop duplicates.)
372383
document.recorded_actions.pop();
373384
ra_len = document.recorded_actions.length;
374-
if (ra_len > 0 &&
375-
document.recorded_actions[ra_len-1][1] === selector)
376-
{
377-
// Pop the duplicate if present.
385+
if (ra_len > 0 && document.recorded_actions[ra_len-1][1] === selector)
378386
document.recorded_actions.pop();
379-
}
380387
}
381388
// Go back to `if`, not `else if`.
382-
if (tag_name === 'input' && element.type === 'checkbox' &&
383-
element.checked)
389+
if (tag_name === 'input' && element.type === 'checkbox' && element.checked)
384390
{
385391
document.recorded_actions.push(['c_box', selector, 'yes', d_now]);
386392
}
@@ -402,14 +408,11 @@
402408
const selector = getBestSelector(element);
403409
ra_len = document.recorded_actions.length;
404410
tag_name = element.tagName.toLowerCase();
405-
if (ra_len > 0 &&
406-
document.recorded_actions[ra_len-1][0] === 'mo_dn')
407-
{
411+
if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'mo_dn') {
408412
document.recorded_actions.pop();
409413
}
410-
if (tag_name === 'select')
411-
{
412-
// Do Nothing (Handle select in 'change' action.)
414+
if (tag_name === 'select') {
415+
// Do Nothing. (Handle select in 'change' action.)
413416
}
414417
else {
415418
document.recorded_actions.push(['mo_dn', selector, '', d_now]);
@@ -427,53 +430,76 @@
427430
const selector = getBestSelector(element);
428431
ra_len = document.recorded_actions.length;
429432
tag_name = element.tagName.toLowerCase();
433+
parent_element = element.parentElement;
434+
parent_tag_name = parent_element.tagName.toLowerCase();
435+
grand_element = "";
436+
grand_tag_name = "";
437+
origin = "";
438+
if (parent_element.parentElement != 'null') {
439+
grand_element = parent_element.parentElement;
440+
grand_tag_name = grand_element.tagName.toLowerCase();
441+
}
430442
if (ra_len > 0 &&
431443
document.recorded_actions[ra_len-1][1] === selector &&
432444
(document.recorded_actions[ra_len-1][0] === 'mo_dn' ||
433-
tag_name === 'a') && tag_name !== 'select')
445+
tag_name === 'a' || parent_tag_name === 'a') &&
446+
tag_name !== 'select')
434447
{
435-
ahref = '';
448+
href = '';
436449
if (tag_name === 'a' &&
437450
element.hasAttribute('href') &&
438451
element.getAttribute('href').length > 0 &&
439452
element.origin != 'null')
440453
{
441-
// Because getAttribute('href') could start with '/'.
442-
ahref = element.href;
454+
href = element.href;
455+
origin = element.origin;
456+
}
457+
else if (parent_tag_name === 'a' &&
458+
parent_element.hasAttribute('href') &&
459+
parent_element.getAttribute('href').length > 0 &&
460+
parent_element.origin != 'null')
461+
{
462+
href = parent_element.href;
463+
origin = parent_element.origin;
464+
}
465+
else if (grand_tag_name === 'a' &&
466+
grand_element.hasAttribute('href') &&
467+
grand_element.getAttribute('href').length > 0 &&
468+
grand_element.origin != 'null')
469+
{
470+
href = grand_element.href;
471+
origin = grand_element.origin;
443472
}
444473
document.recorded_actions.pop();
445474
child_sep = ' > ';
446-
if (tag_name === "a" && !element.hasAttribute('onclick') &&
447-
selector.includes(child_sep) && ahref.length > 0)
475+
child_count = ssOccurrences(selector, child_sep);
476+
if ((tag_name === "a" && !element.hasAttribute('onclick') &&
477+
child_count > 0 && href.length > 0) ||
478+
(parent_tag_name === "a" && href.length > 0 &&
479+
child_count > 1 && !parent_element.hasAttribute('onclick')) ||
480+
(grand_tag_name === "a" && href.length > 0 &&
481+
child_count > 2 && !grand_element.hasAttribute('onclick')))
448482
{
449-
origin = element.origin;
450483
w_orig = window.location.origin;
451-
if (origin === w_orig)
452-
{
453-
document.recorded_actions.push(
454-
['_url_', origin, ahref, d_now]);
484+
if (origin === w_orig) {
485+
document.recorded_actions.push(['_url_', origin, href, d_now]);
455486
}
456487
else {
457-
document.recorded_actions.push(
458-
['begin', origin, ahref, d_now]);
488+
document.recorded_actions.push(['begin', origin, href, d_now]);
459489
}
460490
}
461491
else {
462-
document.recorded_actions.push(
463-
['click', selector, ahref, d_now]);
492+
document.recorded_actions.push(['click', selector, href, d_now]);
464493
}
465494
// Switch to hover_click() if in a dropdown.
466-
if (element.parentElement.classList.contains(
467-
'dropdown-content') &&
468-
element.parentElement.parentElement.classList.contains(
469-
'dropdown'))
495+
if (element.parentElement.classList.contains('dropdown-content') &&
496+
element.parentElement.parentElement.classList.contains('dropdown'))
470497
{
471498
ch_s = selector;
472499
pa_el = element.parentElement.parentElement;
473500
pa_s = getBestSelector(pa_el);
474501
if (pa_el.childElementCount >= 2 &&
475-
!pa_el.firstElementChild.classList.contains(
476-
'dropdown-content'))
502+
!pa_el.firstElementChild.classList.contains('dropdown-content'))
477503
{
478504
pa_el = pa_el.firstElementChild;
479505
pa_s = getBestSelector(pa_el);
@@ -532,7 +558,7 @@
532558
});
533559
document.body.addEventListener('keyup', function (event) {
534560
if (typeof document.recorded_actions === 'undefined')
535-
reset_recorder_state();
561+
reset_recorder_state();
536562
// Controls for Pausing and Resuming the Recorder.
537563
if (event.key.toLowerCase() === 'escape' &&
538564
sessionStorage.getItem('pause_recorder') === 'no')
@@ -582,8 +608,7 @@
582608
{
583609
skip_input = true;
584610
}
585-
if (!skip_input)
586-
{
611+
if (!skip_input) {
587612
document.recorded_actions.push(
588613
['input', selector, element.value, d_now]);
589614
}

0 commit comments

Comments
 (0)