BrowserSteps - Make the UI require an extra step so it doesnt slow down the experience when clicking through the tabs (#1175)

pull/1176/head
dgtlmoon 2022-11-30 19:40:15 +01:00 zatwierdzone przez GitHub
rodzic 0b8c3add34
commit b4d79839bf
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
6 zmienionych plików z 149 dodań i 96 usunięć

Wyświetl plik

@ -131,21 +131,11 @@ def construct_blueprint(datastore: ChangeDetectionStore):
this_session.call_action(action_name=step_operation, this_session.call_action(action_name=step_operation,
selector=step_selector, selector=step_selector,
optional_value=step_optional_value) optional_value=step_optional_value)
except playwright._impl._api_types.TimeoutError as e:
print("Element wasnt found :-(", step_operation)
return make_response("Element was not found on page", 401)
except playwright._impl._api_types.Error as e:
# Browser/playwright level error
print("Browser error - got playwright._impl._api_types.Error, try reloading the session/browser")
print (str(e))
except Exception as e:
print("Exception when calling step operation", step_operation, str(e))
# Try to find something of value to give back to the user # Try to find something of value to give back to the user
for l in str(e).splitlines(): return make_response(str(e).splitlines()[0], 401)
if 'DOMException' in l:
return make_response(l, 401)
return make_response('Browser session ran out of time :( Please reload this page.', 401)
# Get visual selector ready/update its data (also use the current filter info from the page?) # Get visual selector ready/update its data (also use the current filter info from the page?)
# When the last 'apply' button was pressed # When the last 'apply' button was pressed
@ -205,6 +195,10 @@ def construct_blueprint(datastore: ChangeDetectionStore):
cleanup_playwright_session() cleanup_playwright_session()
return make_response('Browser session ran out of time :( Please reload this page.', 401) return make_response('Browser session ran out of time :( Please reload this page.', 401)
response = None
if request.method == 'POST':
# Screenshots and other info only needed on requesting a step (POST)
try: try:
state = this_session.get_current_state() state = this_session.get_current_state()
except playwright._impl._api_types.Error as e: except playwright._impl._api_types.Error as e:
@ -232,6 +226,13 @@ def construct_blueprint(datastore: ChangeDetectionStore):
# No longer needed # No longer needed
os.unlink(tmp_file) os.unlink(tmp_file)
elif request.method == 'GET':
# Just enough to get the session rolling, it will call for goto-site via POST next
response = make_response({
'session_age_start': this_session.age_start,
'browser_time_remaining': round(remaining)
})
return response return response
return browser_steps_blueprint return browser_steps_blueprint

Wyświetl plik

@ -90,7 +90,7 @@ class steppable_browser_interface():
return return
elem = self.page.get_by_text(value) elem = self.page.get_by_text(value)
if elem.count(): if elem.count():
elem.first.click(delay=randint(200, 500)) elem.first.click(delay=randint(200, 500), timeout=3000)
def action_enter_text_in_field(self, selector, value): def action_enter_text_in_field(self, selector, value):
if not len(selector.strip()): if not len(selector.strip()):
@ -146,10 +146,10 @@ class steppable_browser_interface():
self.page.keyboard.press("PageDown", delay=randint(200, 500)) self.page.keyboard.press("PageDown", delay=randint(200, 500))
def action_check_checkbox(self, selector, value): def action_check_checkbox(self, selector, value):
self.page.locator(selector).check() self.page.locator(selector).check(timeout=1000)
def action_uncheck_checkbox(self, selector, value): def action_uncheck_checkbox(self, selector, value):
self.page.locator(selector).uncheck() self.page.locator(selector, timeout=1000).uncheck(timeout=1000)
# Responsible for maintaining a live 'context' with browserless # Responsible for maintaining a live 'context' with browserless
@ -211,7 +211,7 @@ class browsersteps_live_ui(steppable_browser_interface):
# Listen for all console events and handle errors # Listen for all console events and handle errors
self.page.on("console", lambda msg: print(f"Browser steps console - {msg.type}: {msg.text} {msg.args}")) self.page.on("console", lambda msg: print(f"Browser steps console - {msg.type}: {msg.text} {msg.args}"))
print("time to browser setup", time.time() - now) print("Time to browser setup", time.time() - now)
self.page.wait_for_timeout(1 * 1000) self.page.wait_for_timeout(1 * 1000)
def mark_as_closed(self): def mark_as_closed(self):

Wyświetl plik

@ -13,7 +13,7 @@ $(document).ready(function () {
var browserless_seconds_remaining = 0; var browserless_seconds_remaining = 0;
var apply_buttons_disabled = false; var apply_buttons_disabled = false;
var include_text_elements = $("#include_text_elements"); var include_text_elements = $("#include_text_elements");
var xpath_data; var xpath_data = false;
var current_selected_i; var current_selected_i;
var state_clicked = false; var state_clicked = false;
var c; var c;
@ -25,11 +25,42 @@ $(document).ready(function () {
$(window).resize(function () { $(window).resize(function () {
set_scale(); set_scale();
}); });
// Should always be disabled
$('#browser_steps >li:first-child select').val('Goto site').attr('disabled', 'disabled');
$('a#browsersteps-tab').click(function () { $('#browsersteps-click-start').click(function () {
$("#browsersteps-click-start").fadeOut();
$("#browsersteps-selector-wrapper .spinner").fadeIn();
start(); start();
}); });
$('a#browsersteps-tab').click(function () {
reset();
});
window.addEventListener('hashchange', function () {
if (window.location.hash == '#browser-steps') {
reset();
}
});
function reset() {
xpath_data = false;
$('#browsersteps-img').removeAttr('src');
$("#browsersteps-click-start").show();
$("#browsersteps-selector-wrapper .spinner").hide();
browserless_seconds_remaining = 0;
browsersteps_session_id = false;
apply_buttons_disabled = false;
ctx.clearRect(0, 0, c.width, c.height);
set_first_gotosite_disabled();
}
function set_first_gotosite_disabled() {
$('#browser_steps >li:first-child select').val('Goto site').attr('disabled', 'disabled');
$('#browser_steps >li:first-child').css('opacity', '0.5');
}
// Show seconds remaining until playwright/browserless needs to restart the session // Show seconds remaining until playwright/browserless needs to restart the session
// (See comment at the top of changedetectionio/blueprint/browser_steps/__init__.py ) // (See comment at the top of changedetectionio/blueprint/browser_steps/__init__.py )
setInterval(() => { setInterval(() => {
@ -40,21 +71,6 @@ $(document).ready(function () {
}, "1000") }, "1000")
if (window.location.hash == '#browser-steps') {
start();
}
window.addEventListener('hashchange', function () {
if (window.location.hash == '#browser-steps') {
start();
}
// For when the page loads
if (!window.location.hash || window.location.hash != '#browser-steps') {
$("img#browsersteps-img").attr('src', '');
return;
}
});
function set_scale() { function set_scale() {
// some things to check if the scaling doesnt work // some things to check if the scaling doesnt work
@ -87,7 +103,6 @@ $(document).ready(function () {
// @todo is click better? // @todo is click better?
$('#browsersteps-selector-canvas').off("mousemove mousedown click"); $('#browsersteps-selector-canvas').off("mousemove mousedown click");
// Undo disable_browsersteps_ui // Undo disable_browsersteps_ui
$("#browser_steps select,input").removeAttr('disabled').css('opacity', '1.0');
$("#browser-steps-ui").css('opacity', '1.0'); $("#browser-steps-ui").css('opacity', '1.0');
// init // init
@ -118,6 +133,10 @@ $(document).ready(function () {
}); });
$('#browsersteps-selector-canvas').bind('mousemove', function (e) { $('#browsersteps-selector-canvas').bind('mousemove', function (e) {
if (!xpath_data) {
return;
}
// checkbox if find elements is enabled // checkbox if find elements is enabled
ctx.clearRect(0, 0, c.width, c.height); ctx.clearRect(0, 0, c.width, c.height);
ctx.fillStyle = 'rgba(255,0,0, 0.1)'; ctx.fillStyle = 'rgba(255,0,0, 0.1)';
@ -175,7 +194,6 @@ $(document).ready(function () {
// }); // });
// callback for clicking on an xpath on the canvas // callback for clicking on an xpath on the canvas
function process_selected(xpath_data_index) { function process_selected(xpath_data_index) {
found_something = false; found_something = false;
@ -233,8 +251,8 @@ $(document).ready(function () {
browsersteps_session_id = Date.now(); browsersteps_session_id = Date.now();
// @todo This setting of the first one should be done at the datalayer but wtforms doesnt wanna play nice // @todo This setting of the first one should be done at the datalayer but wtforms doesnt wanna play nice
$('#browser_steps >li:first-child').removeClass('empty'); $('#browser_steps >li:first-child').removeClass('empty');
$('#browser_steps >li:first-child select').val('Goto site').attr('disabled', 'disabled'); set_first_gotosite_disabled();
$('#browser-steps-ui .loader').show(); $('#browser-steps-ui .loader .spinner').show();
$('.clear,.remove', $('#browser_steps >li:first-child')).hide(); $('.clear,.remove', $('#browser_steps >li:first-child')).hide();
$.ajax({ $.ajax({
type: "GET", type: "GET",
@ -247,11 +265,12 @@ $(document).ready(function () {
} }
}).done(function (data) { }).done(function (data) {
xpath_data = data.xpath_data; xpath_data = data.xpath_data;
$('#browsersteps-img').attr('src', data.screenshot);
$("#loading-status-text").fadeIn(); $("#loading-status-text").fadeIn();
// This should trigger 'Goto site' // This should trigger 'Goto site'
console.log("Got startup response, requesting Goto-Site (first) step fake click");
$('#browser_steps >li:first-child .apply').click(); $('#browser_steps >li:first-child .apply').click();
browserless_seconds_remaining = data.browser_time_remaining; browserless_seconds_remaining = data.browser_time_remaining;
set_first_gotosite_disabled();
}).fail(function (data) { }).fail(function (data) {
console.log(data); console.log(data);
alert('There was an error communicating with the server.'); alert('There was an error communicating with the server.');
@ -260,7 +279,7 @@ $(document).ready(function () {
} }
function disable_browsersteps_ui() { function disable_browsersteps_ui() {
$("#browser_steps select,input").attr('disabled', 'disabled').css('opacity', '0.5'); set_first_gotosite_disabled();
$("#browser-steps-ui").css('opacity', '0.3'); $("#browser-steps-ui").css('opacity', '0.3');
$('#browsersteps-selector-canvas').off("mousemove mousedown click"); $('#browsersteps-selector-canvas').off("mousemove mousedown click");
} }
@ -307,11 +326,14 @@ $(document).ready(function () {
// Add the extra buttons to the steps // Add the extra buttons to the steps
$('ul#browser_steps li').each(function (i) { $('ul#browser_steps li').each(function (i) {
$(this).append('<div class="control">' + var s = '<div class="control">' + '<a data-step-index=' + i + ' class="pure-button button-secondary button-green button-xsmall apply" >Apply</a>&nbsp;';
'<a data-step-index=' + i + ' class="pure-button button-secondary button-green button-xsmall apply" >Apply</a>&nbsp;' + if (i > 0) {
'<a data-step-index=' + i + ' class="pure-button button-secondary button-xsmall clear" >Clear</a>&nbsp;' + // The first step never gets these (Goto-site)
'<a data-step-index=' + i + ' class="pure-button button-secondary button-red button-xsmall remove" >Remove</a>' + s += '<a data-step-index=' + i + ' class="pure-button button-secondary button-xsmall clear" >Clear</a>&nbsp;' +
'</div>') '<a data-step-index=' + i + ' class="pure-button button-secondary button-red button-xsmall remove" >Remove</a>';
}
s += '</div>';
$(this).append(s)
} }
); );
@ -353,7 +375,7 @@ $(document).ready(function () {
} }
var current_data = $(event.currentTarget).closest('li'); var current_data = $(event.currentTarget).closest('li');
$('#browser-steps-ui .loader').fadeIn(); $('#browser-steps-ui .loader .spinner').fadeIn();
apply_buttons_disabled = true; apply_buttons_disabled = true;
$('ul#browser_steps li .control .apply').css('opacity', 0.5); $('ul#browser_steps li .control .apply').css('opacity', 0.5);
$("#browsersteps-img").css('opacity', 0.65); $("#browsersteps-img").css('opacity', 0.65);
@ -374,7 +396,7 @@ $(document).ready(function () {
is_last_step = false; is_last_step = false;
} }
console.log("Requesting step via POST " + $("select[id$='operation']", current_data).first().val());
// POST the currently clicked step form widget back and await response, redraw // POST the currently clicked step form widget back and await response, redraw
$.ajax({ $.ajax({
method: "POST", method: "POST",
@ -391,18 +413,26 @@ $(document).ready(function () {
// More than likely the CSRF token was lost when the server restarted // More than likely the CSRF token was lost when the server restarted
alert("There was a problem processing the request, please reload the page."); alert("There was a problem processing the request, please reload the page.");
$("#loading-status-text").hide(); $("#loading-status-text").hide();
$('#browser-steps-ui .loader .spinner').fadeOut();
},
401: function (data) {
// More than likely the CSRF token was lost when the server restarted
alert(data.responseText);
$("#loading-status-text").hide();
$('#browser-steps-ui .loader .spinner').fadeOut();
} }
} }
}).done(function (data) { }).done(function (data) {
// it should return the new state (selectors available and screenshot) // it should return the new state (selectors available and screenshot)
xpath_data = data.xpath_data; xpath_data = data.xpath_data;
$('#browsersteps-img').attr('src', data.screenshot); $('#browsersteps-img').attr('src', data.screenshot);
$('#browser-steps-ui .loader').fadeOut(); $('#browser-steps-ui .loader .spinner').fadeOut();
apply_buttons_disabled = false; apply_buttons_disabled = false;
$("#browsersteps-img").css('opacity', 1); $("#browsersteps-img").css('opacity', 1);
$('ul#browser_steps li .control .apply').css('opacity', 1); $('ul#browser_steps li .control .apply').css('opacity', 1);
browserless_seconds_remaining = data.browser_time_remaining; browserless_seconds_remaining = data.browser_time_remaining;
$("#loading-status-text").hide(); $("#loading-status-text").hide();
set_first_gotosite_disabled();
}).fail(function (data) { }).fail(function (data) {
console.log(data); console.log(data);
if (data.responseText.includes("Browser session expired")) { if (data.responseText.includes("Browser session expired")) {
@ -412,7 +442,6 @@ $(document).ready(function () {
$("#loading-status-text").hide(); $("#loading-status-text").hide();
$('ul#browser_steps li .control .apply').css('opacity', 1); $('ul#browser_steps li .control .apply').css('opacity', 1);
$("#browsersteps-img").css('opacity', 1); $("#browsersteps-img").css('opacity', 1);
//$('#browsersteps-selector-wrapper .loader').fadeOut(2500);
}); });
}); });

Wyświetl plik

@ -6,6 +6,11 @@
} }
li { li {
&:not(:first-child) {
&:hover {
opacity: 1.0;
}
}
list-style: decimal; list-style: decimal;
padding: 5px; padding: 5px;
.control { .control {
@ -70,6 +75,8 @@
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
margin-left: -40px; margin-left: -40px;
z-index: 100; z-index: 100;
max-width: 350px;
text-align: center;
} }
/* nice tall skinny one */ /* nice tall skinny one */
@ -78,4 +85,10 @@
height: 80px; height: 80px;
font-size: 3px; font-size: 3px;
} }
#browsersteps-click-start {
&:hover {
cursor: pointer;
}
}
} }

Wyświetl plik

@ -50,6 +50,8 @@ nvm use v14.18.1 && npm install && npm run build
#browser_steps li { #browser_steps li {
list-style: decimal; list-style: decimal;
padding: 5px; } padding: 5px; }
#browser_steps li:not(:first-child):hover {
opacity: 1.0; }
#browser_steps li .control { #browser_steps li .control {
padding-left: 5px; padding-left: 5px;
padding-right: 5px; } padding-right: 5px; }
@ -96,11 +98,15 @@ nvm use v14.18.1 && npm install && npm run build
top: 50%; top: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
margin-left: -40px; margin-left: -40px;
z-index: 100; } z-index: 100;
max-width: 350px;
text-align: center; }
#browsersteps-selector-wrapper .spinner, #browsersteps-selector-wrapper .spinner:after { #browsersteps-selector-wrapper .spinner, #browsersteps-selector-wrapper .spinner:after {
width: 80px; width: 80px;
height: 80px; height: 80px;
font-size: 3px; } font-size: 3px; }
#browsersteps-selector-wrapper #browsersteps-click-start:hover {
cursor: pointer; }
.arrow { .arrow {
border: solid #1b98f8; border: solid #1b98f8;

Wyświetl plik

@ -167,7 +167,11 @@ User-Agent: wonderbra 1.0") }}
<div class="noselect" id="browsersteps-selector-wrapper" style="width: 100%"> <div class="noselect" id="browsersteps-selector-wrapper" style="width: 100%">
<span class="loader" > <span class="loader" >
<div class="spinner"></div> <span id="browsersteps-click-start">
<h2 >Click here to Start</h2>
Please allow 10-15 seconds for the browser to connect.
</span>
<div class="spinner" style="display: none;"></div>
</span> </span>
<img class="noselect" id="browsersteps-img" src="" style="max-width: 100%; width: 100%;" /> <img class="noselect" id="browsersteps-img" src="" style="max-width: 100%; width: 100%;" />
<canvas class="noselect" id="browsersteps-selector-canvas" style="max-width: 100%; width: 100%;"></canvas> <canvas class="noselect" id="browsersteps-selector-canvas" style="max-width: 100%; width: 100%;"></canvas>