Compare commits

..

2 Commits

Author SHA1 Message Date
dgtlmoon
2483e26593 woops 2025-02-09 21:12:46 +01:00
dgtlmoon
c4001ec3ee UI - "Browser Steps" tab should be always available with helpful info 2025-02-09 21:02:46 +01:00
6 changed files with 63 additions and 99 deletions

View File

@@ -112,35 +112,6 @@ def build_watch_json_schema(d):
schema['properties']['time_between_check'] = build_time_between_check_json_schema()
schema['properties']['browser_steps'] = {
"anyOf": [
{
"type": "array",
"items": {
"type": "object",
"properties": {
"operation": {
"type": ["string", "null"],
"maxLength": 5000 # Allows null and any string up to 5000 chars (including "")
},
"selector": {
"type": ["string", "null"],
"maxLength": 5000
},
"optional_value": {
"type": ["string", "null"],
"maxLength": 5000
}
},
"required": ["operation", "selector", "optional_value"],
"additionalProperties": False # No extra keys allowed
}
},
{"type": "null"}, # Allows null for `browser_steps`
{"type": "array", "maxItems": 0} # Allows empty array []
]
}
# headers ?
return schema

View File

@@ -52,8 +52,6 @@ class steppable_browser_interface():
page = None
start_url = None
action_timeout = 10 * 1000
def __init__(self, start_url):
self.start_url = start_url
@@ -104,7 +102,7 @@ class steppable_browser_interface():
return
elem = self.page.get_by_text(value)
if elem.count():
elem.first.click(delay=randint(200, 500), timeout=self.action_timeout)
elem.first.click(delay=randint(200, 500), timeout=3000)
def action_click_element_containing_text_if_exists(self, selector=None, value=''):
logger.debug("Clicking element containing text if exists")
@@ -113,7 +111,7 @@ class steppable_browser_interface():
elem = self.page.get_by_text(value)
logger.debug(f"Clicking element containing text - {elem.count()} elements found")
if elem.count():
elem.first.click(delay=randint(200, 500), timeout=self.action_timeout)
elem.first.click(delay=randint(200, 500), timeout=3000)
else:
return
@@ -121,7 +119,7 @@ class steppable_browser_interface():
if not len(selector.strip()):
return
self.page.fill(selector, value, timeout=self.action_timeout)
self.page.fill(selector, value, timeout=10 * 1000)
def action_execute_js(self, selector, value):
response = self.page.evaluate(value)
@@ -132,7 +130,7 @@ class steppable_browser_interface():
if not len(selector.strip()):
return
self.page.click(selector=selector, timeout=self.action_timeout + 20 * 1000, delay=randint(200, 500))
self.page.click(selector=selector, timeout=30 * 1000, delay=randint(200, 500))
def action_click_element_if_exists(self, selector, value):
import playwright._impl._errors as _api_types
@@ -140,7 +138,7 @@ class steppable_browser_interface():
if not len(selector.strip()):
return
try:
self.page.click(selector, timeout=self.action_timeout, delay=randint(200, 500))
self.page.click(selector, timeout=10 * 1000, delay=randint(200, 500))
except _api_types.TimeoutError as e:
return
except _api_types.Error as e:
@@ -187,10 +185,10 @@ class steppable_browser_interface():
self.page.keyboard.press("PageDown", delay=randint(200, 500))
def action_check_checkbox(self, selector, value):
self.page.locator(selector).check(timeout=self.action_timeout)
self.page.locator(selector).check(timeout=1000)
def action_uncheck_checkbox(self, selector, value):
self.page.locator(selector).uncheck(timeout=self.action_timeout)
self.page.locator(selector, timeout=1000).uncheck(timeout=1000)
# Responsible for maintaining a live 'context' with the chrome CDP

View File

@@ -1,66 +1,48 @@
(function ($) {
$.fn.hashTabs = function (options) {
var settings = $.extend({
tabContainer: ".tabs ul",
tabSelector: "li a",
tabContent: ".tab-pane-inner",
activeClass: "active",
errorClass: ".messages .error",
bodyClassToggle: "full-width"
}, options);
// Rewrite this is a plugin.. is all this JS really 'worth it?'
var $tabs = $(settings.tabContainer).find(settings.tabSelector);
window.addEventListener('hashchange', function () {
var tabs = document.getElementsByClassName('active');
while (tabs[0]) {
tabs[0].classList.remove('active');
document.body.classList.remove('full-width');
}
set_active_tab();
}, false);
function setActiveTab() {
var hash = window.location.hash;
var $activeTab = $tabs.filter("[href='" + hash + "']");
var has_errors = document.querySelectorAll(".messages .error");
if (!has_errors.length) {
if (document.location.hash == "") {
location.replace(document.querySelector(".tabs ul li:first-child a").hash);
} else {
set_active_tab();
}
} else {
focus_error_tab();
}
// Remove active class from all tabs
$(settings.tabContainer).find("li").removeClass(settings.activeClass);
function set_active_tab() {
document.body.classList.remove('full-width');
var tab = document.querySelectorAll("a[href='" + location.hash + "']");
if (tab.length) {
tab[0].parentElement.className = "active";
}
// Add active class to selected tab
if ($activeTab.length) {
$activeTab.parent().addClass(settings.activeClass);
}
}
// Show the correct content
$(settings.tabContent).hide();
if (hash) {
$(hash).show();
}
function focus_error_tab() {
// time to use jquery or vuejs really,
// activate the tab with the error
var tabs = document.querySelectorAll('.tabs li a'), i;
for (i = 0; i < tabs.length; ++i) {
var tab_name = tabs[i].hash.replace('#', '');
var pane_errors = document.querySelectorAll('#' + tab_name + ' .error')
if (pane_errors.length) {
document.location.hash = '#' + tab_name;
return true;
}
function focusErrorTab() {
$tabs.each(function () {
var tabName = this.hash.replace("#", "");
if ($("#" + tabName).find(settings.errorClass).length) {
window.location.hash = "#" + tabName;
return false; // Stop loop on first error tab
}
});
}
function initializeTabs() {
if ($(settings.errorClass).length) {
focusErrorTab();
} else if (!window.location.hash) {
window.location.replace($tabs.first().attr("href"));
} else {
setActiveTab();
}
}
// Listen for hash changes
$(window).on("hashchange", setActiveTab);
// Initialize on page load
initializeTabs();
return this; // Enable jQuery chaining
};
})(jQuery);
}
return false;
}
$(document).ready(function () {
$(".tabs").hashTabs();
});

View File

@@ -945,7 +945,15 @@ $form-edge-padding: 20px;
}
.tab-pane-inner {
display: none;
&:not(:target) {
display: none;
}
&:target {
display: block;
}
// doesnt need padding because theres another row of buttons/activity
padding: 0px;
}

View File

@@ -1159,8 +1159,11 @@ textarea::placeholder {
border-radius: 5px; }
.tab-pane-inner {
display: none;
padding: 0px; }
.tab-pane-inner:not(:target) {
display: none; }
.tab-pane-inner:target {
display: block; }
.beta-logo {
height: 50px;

View File

@@ -280,7 +280,9 @@ nav
</div>
</div>
<p>
Your proxy provider may need to whitelist our IP of <code>204.15.192.195</code>
</p>
<p><strong>Tip</strong>: "Residential" and "Mobile" proxy type can be more successfull than "Data Center" for blocked websites.
<div class="pure-control-group" id="extra-proxies-setting">