This commit is contained in:
James Read 2026-07-17 16:29:45 +00:00 committed by GitHub
commit 5c3d34228e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
191 changed files with 2659 additions and 424 deletions

View File

@ -1,48 +1,29 @@
---
name: Support request
about: Need some help? Got an error message?
title: ""
type: support
labels:
- "waiting-on-developer"
about: Ask for help with OliveTin
title: ''
labels: ''
assignees: ''
---
**What seems to be the problem?!**
**What is the problem you are having?**
If you are getting an error message, then please copy/paste, or better, provide
a screenshot to show us exactly what is wrong.
**Can you provide a sosreport?**
**What OliveTin version are you running?**
A sosreport really helps us to help you, by providing critical information about your install. If you can generate a sosreport, please copy and paste the output here.
How to generate a sosreport: https://docs.olivetin.app/sosreport.html
**Can you provide Server Diagnostics?**
**What package/file/container did you use to install OliveTin?**
Server Diagnostics really helps us to help you, by providing critical information about your install. If you can generate Server Diagnostics, please copy and paste the output here.
eg: OliveTin-1234-x86_64.rpm
eg: Container from Dockerhub - please include the version number
How to generate Server Diagnostics: https://docs.olivetin.app/troubleshooting/server-diagnostics.html
**Your config.yaml**
```
Please copy-paste your config.yaml here
**What is your config.yaml?**
You don't need to include anything under "actions:" unless that is where your
error probably is.
```
**OliveTin logs**
**What are the OliveTin service logs showing?**
If possible, please copy and paste your OliveTin logs from when the error happened.
**Screenshot of WebDeveloper console logs**
If you know how, and if you think it's relevant, a screenshot of the
WebDeveloper console from when you clicked a button is often really helpful.
**Anything else?**
Add any other context about the problem here.
**Screenshots (if appropriate)**

6
.gitignore vendored
View File

@ -18,12 +18,12 @@ integration-tests/flakey-test-runs.jsonl
service/flakey-test-runs.log
service/flakey-test-runs.jsonl
.vscode/
webui/
/webui/
server.log
OliveTin
integration-tests/configs/authRequireGuestsToLogin/sessions.yaml
webui
webui.dev
/webui
/webui.dev
sessions.yaml
docs/build/
build/

View File

@ -163,9 +163,13 @@ nfpms:
- src: var/openrc/OliveTin
dst: /etc/init.d/OliveTin
- src: webui/
dst: /var/www/olivetin/
type: tree
# Avoid type: tree for apk — nfpm writes GNU base-256 modes for walked
# dirs (ModeDir), which apk-tools rejects (#761 / goreleaser/nfpm#1112).
- src: webui/index.html
dst: /var/www/olivetin/index.html
- src: webui/assets/*
dst: /var/www/olivetin/assets/
- src: config.yaml
dst: /etc/OliveTin/config.yaml

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,2 @@
custom-webui/
__pycache__/

View File

@ -0,0 +1,2 @@
CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../../screenshots.mk

View File

@ -0,0 +1,14 @@
---
listenAddressSingleHTTPFrontend: 0.0.0.0:11337
logLevel: "WARN"
checkForUpdates: false
showFooter: false
actions:
- title: date
shell: date
icon: clock
maxRate:
- limit: 3
duration: 5m

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -0,0 +1,11 @@
[DEFAULT]
base_url = http://localhost:11337/
dir = .
width = 980
height = 620
post_script_sleep = 0.5
[max-rate]
url = .
name = maxRate
script = setup_max_rate.py

View File

@ -0,0 +1,92 @@
#!/usr/bin/env python3
"""Show blocked rate-limit log entries for the date action."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
_START_ACTION_REPEATEDLY_JS = """
const done = arguments[arguments.length - 1];
const title = arguments[0];
const count = arguments[1];
function bindingIdForTitle(actionTitle) {
const button = document.querySelector('[title="' + actionTitle + '"]');
if (!button) {
throw new Error('Action button not found: ' + actionTitle);
}
return button.closest('.action-button').id.replace('actionButton-', '');
}
function uniqueTrackingId() {
if (window.isSecureContext && window.crypto?.randomUUID) {
return window.crypto.randomUUID();
}
return 'doc-screenshot-' + Date.now() + '-' + Math.random();
}
async function startAndWait(actionTitle) {
const bindingId = bindingIdForTitle(actionTitle);
const trackingId = uniqueTrackingId();
const response = await window.client.startAction({
bindingId,
arguments: [],
uniqueTrackingId: trackingId,
});
const executionTrackingId = response.executionTrackingId || trackingId;
while (true) {
const result = await window.client.executionStatus({
executionTrackingId,
});
if (result.logEntry?.executionFinished) {
return result.logEntry;
}
await new Promise((resolve) => setTimeout(resolve, 100));
}
}
async function startRepeatedly(actionTitle, runs) {
for (let i = 0; i < runs; i++) {
await startAndWait(actionTitle);
}
}
startRepeatedly(title, count).then(() => done(true)).catch((err) => done(String(err)));
"""
def _wait_for_dashboard(driver, timeout=15):
WebDriverWait(driver, timeout).until(
lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard"))
)
WebDriverWait(driver, timeout).until(
lambda d: d.execute_script("return !!window.client")
)
def _wait_for_rate_limited_logs(driver, timeout=20):
def ready(d):
try:
rows = d.find_elements(By.CSS_SELECTOR, ".logs-table tbody tr")
blocked = d.find_elements(By.CSS_SELECTOR, ".logs-table .status-blocked")
completed = d.find_elements(By.CSS_SELECTOR, ".logs-table .status-success")
except Exception:
return False
return len(rows) >= 5 and len(blocked) >= 2 and len(completed) >= 3
WebDriverWait(driver, timeout).until(ready)
def run(driver):
_wait_for_dashboard(driver)
driver.execute_async_script(_START_ACTION_REPEATEDLY_JS, "date", 5)
time.sleep(1)
driver.execute_script("window.location.href = '/logs'")
_wait_for_rate_limited_logs(driver)
time.sleep(0.2)

View File

@ -0,0 +1,2 @@
custom-webui/
__pycache__/

View File

@ -0,0 +1,2 @@
CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../../screenshots.mk

View File

@ -0,0 +1,16 @@
---
listenAddressSingleHTTPFrontend: 0.0.0.0:11337
logLevel: "WARN"
checkForUpdates: false
showFooter: false
actions:
- title: Check disk space
icon: disk
onclick: execution-dialog
shell: |
echo "Filesystem Size Used Avail Use% Mounted on"
echo "/dev/mapper/fedora_mindstorm-root 99G 82G 12G 88% /"
echo "Filesystem Size Used Avail Use% Mounted on"
echo "/dev/mapper/fedora_mindstorm-root 99G 82G 12G 88% /"

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -0,0 +1,11 @@
[DEFAULT]
base_url = http://localhost:11337/
dir = .
width = 900
height = 620
post_script_sleep = 0.5
[popup-output-only]
url = .
name = popupOutputOnly
script = setup_popup_output_only.py

View File

@ -0,0 +1,47 @@
#!/usr/bin/env python3
"""Open the execution dialog for Check disk space."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
def _wait_for_body_attr(driver, attr, timeout=15):
WebDriverWait(driver, timeout).until(
lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute(attr))
)
def _wait_for_logs_page(driver, timeout=15):
WebDriverWait(driver, timeout).until(
lambda d: "/logs/" in d.current_url and not d.current_url.rstrip("/").endswith("/logs")
)
def _wait_for_execution_complete(driver, timeout=15):
def finished(d):
try:
status = d.find_element(By.CSS_SELECTOR, ".execution-dialog-status").text
except Exception:
return False
return "Still running" not in status and "Queued" not in status
WebDriverWait(driver, timeout).until(finished)
def run(driver):
_wait_for_body_attr(driver, "loaded-dashboard")
driver.find_element(By.CSS_SELECTOR, '[title="Check disk space"]').click()
_wait_for_logs_page(driver)
_wait_for_execution_complete(driver)
WebDriverWait(driver, 15).until(
lambda d: "fedora_mindstorm-root" in d.find_element(
By.CSS_SELECTOR, "#execution-results-popup .xterm-rows"
).text
)
time.sleep(0.2)

View File

@ -0,0 +1,2 @@
custom-webui/
__pycache__/

View File

@ -0,0 +1,2 @@
CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../../screenshots.mk

View File

@ -0,0 +1,11 @@
---
listenAddressSingleHTTPFrontend: 0.0.0.0:11337
logLevel: "WARN"
checkForUpdates: false
showFooter: false
actions:
- title: date
icon: clock
shell: date

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -0,0 +1,11 @@
[DEFAULT]
base_url = http://localhost:11337/
dir = .
width = 980
height = 640
post_script_sleep = 0.5
[diagnostics]
url = /diagnostics
name = diagnostics
script = setup_diagnostics.py

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python3
"""Open the diagnostics page from advanced_configuration/diagnostics.adoc."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
def _wait_for_diagnostics_page(driver, timeout=30):
WebDriverWait(driver, timeout).until(
lambda d: d.execute_script("return !!window.client")
)
def ready(d):
try:
ssh_heading = d.find_element(
By.XPATH,
'//*[self::h2 or self::h3][contains(normalize-space(), "SSH")]',
)
server_diagnostics_heading = d.find_element(
By.XPATH,
'//*[self::h2 or self::h3][contains(normalize-space(), "Server Diagnostics")]',
)
key_value = d.find_element(
By.XPATH,
'//dt[contains(normalize-space(), "Found Key")]/following-sibling::dd[1]',
)
config_value = d.find_element(
By.XPATH,
'//dt[contains(normalize-space(), "Found Config")]/following-sibling::dd[1]',
)
except Exception:
return False
return all(
element.is_displayed()
for element in (
ssh_heading,
server_diagnostics_heading,
key_value,
config_value,
)
) and all(
element.text.strip() not in ("", "?")
for element in (key_value, config_value)
)
WebDriverWait(driver, timeout).until(ready)
def run(driver):
_wait_for_diagnostics_page(driver)
time.sleep(0.2)

View File

@ -0,0 +1,2 @@
custom-webui/
__pycache__/

View File

@ -0,0 +1,2 @@
CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../../../screenshots.mk

View File

@ -0,0 +1,48 @@
---
listenAddressSingleHTTPFrontend: 0.0.0.0:11337
logLevel: "WARN"
checkForUpdates: false
showFooter: false
showNavigation: false
actions:
- title: Ping the Internet
icon: ping
shell: ping -c 1 127.0.0.1
- title: Check disk space
icon: disk
shell: df -h
- title: check dmesg logs
icon: logs
shell: dmesg | tail -n 1
- title: date
icon: clock
shell: date
- title: Run backup script
icon: backup
shell: echo backup
- title: Ping host
icon: ping
shell: ping -c 1 127.0.0.1
- title: Restart Docker Container
icon: restart
shell: echo restart
- title: Delete old backups
icon: ashtonished
shell: echo delete
- title: Get OliveTin Theme
icon: theme
shell: echo theme
- title: Setup easy SSH
icon: ssh
shell: echo ssh
- title: Restart httpd on server1
icon: restart
shell: echo restart httpd
- title: Toggle GPIO light
icon: light
shell: echo toggle
- title: Run Automation Playbook
icon: robot
shell: echo ansible

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -0,0 +1,11 @@
[DEFAULT]
base_url = http://localhost:11337/
dir = .
width = 1180
height = 760
post_script_sleep = 0.5
[hide-navigation]
url = .
name = hide-navigation
script = setup_hide_navigation.py

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
"""Capture the Actions view with navigation hidden."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
def run(driver):
WebDriverWait(driver, 15).until(
lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard"))
)
WebDriverWait(driver, 15).until(
lambda d: len(d.find_elements(By.ID, "mainnav")) == 0
)
WebDriverWait(driver, 15).until(
lambda d: len(d.find_elements(By.CSS_SELECTOR, ".action-button button")) >= 8
)
time.sleep(0.2)

View File

@ -0,0 +1,2 @@
custom-webui/
__pycache__/

View File

@ -0,0 +1,2 @@
CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../../../screenshots.mk

View File

@ -0,0 +1,19 @@
---
listenAddressSingleHTTPFrontend: 0.0.0.0:11337
logLevel: "WARN"
checkForUpdates: false
showFooter: false
pageTitle: My OliveTin Instance
actions:
- title: Ping the Internet
icon: ping
shell: ping -c 1 127.0.0.1
- title: Check disk space
icon: disk
shell: df -h /
- title: check dmesg logs
icon: logs
shell: dmesg | tail -n 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1,11 @@
[DEFAULT]
base_url = http://localhost:11337/
dir = .
width = 980
height = 220
post_script_sleep = 0.5
[page-title]
url = .
name = page-title
script = setup_page_title.py

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
"""Capture the custom page title from advanced_configuration/webui.adoc."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
def run(driver):
WebDriverWait(driver, 15).until(
lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard"))
)
WebDriverWait(driver, 15).until(
lambda d: "My OliveTin Instance" in d.find_element(By.CSS_SELECTOR, "header h1").text
)
WebDriverWait(driver, 15).until(
lambda d: d.find_element(By.CSS_SELECTOR, '[title="Ping the Internet"]').is_displayed()
)
time.sleep(0.2)

View File

@ -0,0 +1,2 @@
custom-webui/
__pycache__/

View File

@ -0,0 +1,2 @@
CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../../../screenshots.mk

View File

@ -0,0 +1,30 @@
---
listenAddressSingleHTTPFrontend: 0.0.0.0:11337
logLevel: "WARN"
checkForUpdates: false
showFooter: false
sectionNavigationStyle: sidebar
actions:
- title: Check disk space
icon: disk
shell: df -h /
- title: Setup easy SSH
icon: ssh
shell: echo ssh
- title: My Servers ping
icon: ping
shell: echo ping
- title: My Containers list
icon: disk
shell: echo list
dashboards:
- title: My Servers
contents:
- title: My Servers ping
- title: My Containers
contents:
- title: My Containers list

View File

@ -0,0 +1,11 @@
[DEFAULT]
base_url = http://localhost:11337/
dir = .
width = 980
height = 520
post_script_sleep = 0.5
[sidebar]
url = .
name = sidebar
script = setup_sidebar.py

View File

@ -0,0 +1,44 @@
#!/usr/bin/env python3
"""Capture the default sidebar navigation style."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
def _wait_for_sidebar_navigation(driver, timeout=30):
WebDriverWait(driver, timeout).until(
lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard"))
)
driver.find_element(By.ID, "sidebar-toggler-button").click()
WebDriverWait(driver, timeout).until(
lambda d: d.find_element(By.ID, "mainnav").is_displayed()
)
driver.find_element(By.CSS_SELECTOR, "#mainnav .stick-toggle").click()
def ready(d):
try:
my_servers = d.find_element(
By.XPATH,
'//*[@id="mainnav"]//a[contains(normalize-space(), "My Servers")]',
)
my_containers = d.find_element(
By.XPATH,
'//*[@id="mainnav"]//a[contains(normalize-space(), "My Containers")]',
)
disk_space = d.find_element(By.CSS_SELECTOR, '[title="Check disk space"]')
ssh = d.find_element(By.CSS_SELECTOR, '[title="Setup easy SSH"]')
except Exception:
return False
return all(element.is_displayed() for element in (my_servers, my_containers, disk_space, ssh))
WebDriverWait(driver, timeout).until(ready)
def run(driver):
_wait_for_sidebar_navigation(driver)
time.sleep(0.2)

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -0,0 +1,2 @@
custom-webui/
__pycache__/

View File

@ -0,0 +1,2 @@
CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../../../screenshots.mk

View File

@ -0,0 +1,33 @@
---
listenAddressSingleHTTPFrontend: 0.0.0.0:11337
logLevel: "WARN"
checkForUpdates: false
showFooter: false
sectionNavigationStyle: topbar
actions:
- title: Ping host
icon: ping
shell: ping -c 1 127.0.0.1
- title: Restart Docker Container
icon: restart
shell: echo restart
- title: Delete old backups
icon: ashtonished
shell: echo delete
- title: Server ping
icon: ping
shell: echo ping
- title: Container list
icon: disk
shell: echo list
dashboards:
- title: My Servers
contents:
- title: Server ping
- title: My Containers
contents:
- title: Container list

View File

@ -0,0 +1,11 @@
[DEFAULT]
base_url = http://localhost:11337/
dir = .
width = 980
height = 420
post_script_sleep = 0.5
[topbar]
url = .
name = topbar
script = setup_topbar.py

View File

@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""Capture the topbar section navigation style."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
def _wait_for_topbar_navigation(driver, timeout=30):
WebDriverWait(driver, timeout).until(
lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard"))
)
def ready(d):
try:
topbar = d.find_element(By.CSS_SELECTOR, "nav.topbar")
my_servers = d.find_element(
By.XPATH,
'//nav[contains(@class, "topbar")]//a[contains(normalize-space(), "My Servers")]',
)
my_containers = d.find_element(
By.XPATH,
'//nav[contains(@class, "topbar")]//a[contains(normalize-space(), "My Containers")]',
)
diagnostics = d.find_element(
By.XPATH,
'//nav[contains(@class, "topbar")]//a[contains(normalize-space(), "Diagnostics")]',
)
logs = d.find_element(
By.XPATH,
'//nav[contains(@class, "topbar")]//a[contains(normalize-space(), "Logs")]',
)
ping_host = d.find_element(By.CSS_SELECTOR, '[title="Ping host"]')
restart = d.find_element(By.CSS_SELECTOR, '[title="Restart Docker Container"]')
delete_backups = d.find_element(By.CSS_SELECTOR, '[title="Delete old backups"]')
except Exception:
return False
return topbar.is_displayed() and all(
element.is_displayed()
for element in (
my_servers,
my_containers,
diagnostics,
logs,
ping_host,
restart,
delete_backups,
)
)
WebDriverWait(driver, timeout).until(ready)
def run(driver):
_wait_for_topbar_navigation(driver)
time.sleep(0.2)

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,2 @@
custom-webui/
__pycache__/

View File

@ -0,0 +1,2 @@
CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../../../screenshots.mk

View File

@ -0,0 +1,47 @@
---
listenAddressSingleHTTPFrontend: 0.0.0.0:11337
logLevel: "WARN"
checkForUpdates: false
showFooter: false
actions:
- title: Ping the Internet
icon: ping
shell: ping -c 1 127.0.0.1
- title: Check disk space
icon: disk
shell: df -h
- title: check dmesg logs
icon: logs
shell: dmesg | tail -n 1
- title: date
icon: clock
shell: date
- title: Run backup script
icon: backup
shell: echo backup
- title: Ping host
icon: ping
shell: ping -c 1 127.0.0.1
- title: Restart Docker Container
icon: restart
shell: echo restart
- title: Delete old backups
icon: ashtonished
shell: echo delete
- title: Get OliveTin Theme
icon: theme
shell: echo theme
- title: Setup easy SSH
icon: ssh
shell: echo ssh
- title: Restart httpd on server1
icon: restart
shell: echo restart httpd
- title: Toggle GPIO light
icon: light
shell: echo toggle
- title: Run Automation Playbook
icon: robot
shell: echo ansible

View File

@ -0,0 +1,11 @@
[DEFAULT]
base_url = http://localhost:11337/
dir = .
width = 1180
height = 760
post_script_sleep = 0.5
[with-navigation]
url = .
name = with-navigation
script = setup_with_navigation.py

View File

@ -0,0 +1,23 @@
#!/usr/bin/env python3
"""Capture the default Actions view with sidebar navigation visible."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
def run(driver):
WebDriverWait(driver, 15).until(
lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard"))
)
WebDriverWait(driver, 15).until(
lambda d: len(d.find_elements(By.CSS_SELECTOR, ".action-button button")) >= 8
)
driver.find_element(By.ID, "sidebar-toggler-button").click()
WebDriverWait(driver, 15).until(
lambda d: d.find_element(By.ID, "mainnav").is_displayed()
)
time.sleep(0.2)

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,2 @@
custom-webui/
__pycache__/

View File

@ -0,0 +1,2 @@
CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../../screenshots.mk

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,22 @@
---
listenAddressSingleHTTPFrontend: 0.0.0.0:11337
logLevel: "WARN"
checkForUpdates: false
showFooter: false
actions:
- title: Backup selected directories
shell: echo "Backing up {{ directories }}"
arguments:
- name: directories
title: Directories to back up
type: checklist
choices:
- title: Documents
value: documents
- title: Photos
value: photos
- title: Music
value: music
default: '["documents","photos"]'

View File

@ -0,0 +1,11 @@
[DEFAULT]
base_url = http://localhost:11337/
dir = .
width = 800
height = 520
post_script_sleep = 0.5
[checklist]
url = .
name = checklist
script = setup_checklist.py

View File

@ -0,0 +1,31 @@
#!/usr/bin/env python3
"""Prepare the checklist argument form screenshot."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
def _wait_for_body_attr(driver, attr, timeout=15):
WebDriverWait(driver, timeout).until(
lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute(attr))
)
def run(driver):
_wait_for_body_attr(driver, "loaded-dashboard")
driver.find_element(
By.CSS_SELECTOR, '[title="Backup selected directories"]'
).click()
_wait_for_body_attr(driver, "loaded-argument-form")
WebDriverWait(driver, 15).until(
lambda d: len(
d.find_elements(By.CSS_SELECTOR, ".choice-checklist-item input[type='checkbox']")
)
>= 3
)
time.sleep(0.2)

View File

@ -0,0 +1,2 @@
custom-webui/
__pycache__/

View File

@ -0,0 +1,2 @@
CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../../screenshots.mk

View File

@ -0,0 +1,14 @@
---
listenAddressSingleHTTPFrontend: 0.0.0.0:11337
logLevel: "WARN"
checkForUpdates: false
showFooter: false
actions:
- title: Delete old backups
icon: ashtonished
shell: echo "Deleted old backups"
arguments:
- type: confirmation
title: Are you sure?!

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,11 @@
[DEFAULT]
base_url = http://localhost:11337/
dir = .
width = 800
height = 420
post_script_sleep = 0.5
[confirmation]
url = .
name = confirmation
script = setup_confirmation.py

View File

@ -0,0 +1,25 @@
#!/usr/bin/env python3
"""Prepare the confirmation argument screenshot."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
def _wait_for_body_attr(driver, attr, timeout=15):
WebDriverWait(driver, timeout).until(
lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute(attr))
)
def run(driver):
_wait_for_body_attr(driver, "loaded-dashboard")
driver.find_element(By.CSS_SELECTOR, '[title="Delete old backups"]').click()
_wait_for_body_attr(driver, "loaded-argument-form")
WebDriverWait(driver, 15).until(
lambda d: not d.find_element(By.CSS_SELECTOR, 'button[name="start"]').is_enabled()
)
time.sleep(0.2)

View File

@ -0,0 +1,2 @@
custom-webui/
__pycache__/

View File

@ -0,0 +1,2 @@
CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../../screenshots.mk

View File

@ -0,0 +1,32 @@
---
listenAddressSingleHTTPFrontend: 0.0.0.0:11337
logLevel: "WARN"
checkForUpdates: false
showFooter: false
entities:
- file: entities/containers.json
name: container
actions:
- title: Print a message
icon: smile
shell: echo "{{ message }}"
arguments:
- name: message
description: The message you want to print out.
choices:
- title: Hello
value: Hello there!
- title: Goodbye
value: Aww, goodbye. :-(
- title: restart container
shell: 'docker restart {{ containerToRestart }}'
arguments:
- name: containerToRestart
entity: container
title: Select Container
choices:
- value: '{{ container.Names }}'
title: '{{ container.Names }}'

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,2 @@
{"Names":"media-indexer"}
{"Names":"game-server"}

View File

@ -0,0 +1,29 @@
[DEFAULT]
base_url = http://localhost:11337/
dir = .
width = 800
height = 480
post_script_sleep = 0.5
[dropdown]
url = .
name = dropdown
script = setup_dropdown.py
[dropdown-logs-list]
url = .
name = dropdown-logs-list
script = setup_dropdown_logs_list.py
width = 960
height = 640
[dropdown-logs-detail]
url = .
name = dropdown-logs-detail
script = setup_dropdown_logs_detail.py
height = 560
[dropdown-entities]
url = .
name = dropdown-entities
script = setup_dropdown_entities.py

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python3
"""Prepare the dropdown argument form screenshot."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
def _wait_for_body_attr(driver, attr, timeout=15):
WebDriverWait(driver, timeout).until(
lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute(attr))
)
def _open_choice_list(driver, input_id):
combobox_input = driver.find_element(By.ID, input_id)
combobox_input.click()
WebDriverWait(driver, 15).until(
lambda d: len(
d.find_elements(
By.CSS_SELECTOR,
f"#{input_id}-listbox li, #{input_id} + input + ul li",
)
)
>= 2
or len(d.find_elements(By.CSS_SELECTOR, ".choice-combobox-list li")) >= 2
)
def run(driver):
_wait_for_body_attr(driver, "loaded-dashboard")
driver.find_element(By.CSS_SELECTOR, '[title="Print a message"]').click()
_wait_for_body_attr(driver, "loaded-argument-form")
_open_choice_list(driver, "message")
time.sleep(0.2)

View File

@ -0,0 +1,30 @@
#!/usr/bin/env python3
"""Prepare the entity-backed dropdown screenshot."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
def _wait_for_body_attr(driver, attr, timeout=15):
WebDriverWait(driver, timeout).until(
lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute(attr))
)
def _open_choice_list(driver, input_id):
combobox_input = driver.find_element(By.ID, input_id)
combobox_input.click()
WebDriverWait(driver, 15).until(
lambda d: len(d.find_elements(By.CSS_SELECTOR, ".choice-combobox-list li")) >= 2
)
def run(driver):
_wait_for_body_attr(driver, "loaded-dashboard")
driver.find_element(By.CSS_SELECTOR, '[title="restart container"]').click()
_wait_for_body_attr(driver, "loaded-argument-form")
_open_choice_list(driver, "containerToRestart")
time.sleep(0.2)

View File

@ -0,0 +1,73 @@
#!/usr/bin/env python3
"""Prepare the execution results screenshot for a dropdown action."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
def _wait_for_dashboard(driver, timeout=15):
WebDriverWait(driver, timeout).until(
lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard"))
)
WebDriverWait(driver, timeout).until(
lambda d: d.execute_script("return !!window.client")
)
def _wait_for_logs_page(driver, timeout=15):
WebDriverWait(driver, timeout).until(
lambda d: "/logs/" in d.current_url and not d.current_url.rstrip("/").endswith("/logs")
)
def _wait_for_execution_complete(driver, timeout=15):
def finished(d):
try:
status = d.find_element(By.CSS_SELECTOR, ".execution-dialog-status").text
except Exception:
return False
return "Still running" not in status and "Queued" not in status
WebDriverWait(driver, timeout).until(finished)
def _wait_for_terminal_output(driver, expected, timeout=15):
WebDriverWait(driver, timeout).until(
lambda d: expected
in d.execute_script(
"""
if (!window.terminal || !window.terminal.getBufferAsString) {
return '';
}
return window.terminal.getBufferAsString();
"""
)
)
def run(driver):
_wait_for_dashboard(driver)
driver.execute_async_script(
"""
const done = arguments[arguments.length - 1];
const button = document.querySelector('[title="Print a message"]');
const bindingId = button.closest('.action-button').id.replace('actionButton-', '');
window.client.startAction({
bindingId: bindingId,
arguments: [{ name: 'message', value: 'Hello there!' }],
uniqueTrackingId: 'doc-screenshot-' + Date.now(),
}).then((response) => {
window.location.href = '/logs/' + response.executionTrackingId;
done(true);
}).catch((err) => done(String(err)));
"""
)
_wait_for_logs_page(driver)
_wait_for_execution_complete(driver)
_wait_for_terminal_output(driver, "Hello there!")
time.sleep(0.2)

View File

@ -0,0 +1,45 @@
#!/usr/bin/env python3
"""Prepare the logs list screenshot after running a dropdown action."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
def _wait_for_dashboard(driver, timeout=15):
WebDriverWait(driver, timeout).until(
lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard"))
)
WebDriverWait(driver, timeout).until(
lambda d: d.execute_script("return !!window.client")
)
def _wait_for_logs_table(driver, timeout=15):
WebDriverWait(driver, timeout).until(
lambda d: len(d.find_elements(By.CSS_SELECTOR, ".logs-table tbody tr")) >= 1
)
def run(driver):
_wait_for_dashboard(driver)
driver.execute_async_script(
"""
const done = arguments[arguments.length - 1];
const button = document.querySelector('[title="Print a message"]');
const bindingId = button.closest('.action-button').id.replace('actionButton-', '');
window.client.startAction({
bindingId: bindingId,
arguments: [{ name: 'message', value: 'Hello there!' }],
uniqueTrackingId: 'doc-screenshot-' + Date.now(),
}).then(() => done(true)).catch((err) => done(String(err)));
"""
)
time.sleep(0.5)
driver.execute_script("window.location.href = '/logs'")
_wait_for_logs_table(driver)
time.sleep(0.2)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -19,3 +19,4 @@ script = setup_args2.py
url = .
name = args3
script = setup_args3.py
height = 560

View File

@ -65,5 +65,15 @@ def run(driver):
_wait_for_logs_page(driver)
_wait_for_execution_complete(driver)
WebDriverWait(driver, 15).until(
lambda d: "Hello World"
in d.execute_script(
"""
if (!window.terminal || !window.terminal.getBufferAsString) {
return '';
}
return window.terminal.getBufferAsString();
"""
)
)
time.sleep(0.2)

View File

@ -0,0 +1,2 @@
custom-webui/
__pycache__/

View File

@ -0,0 +1,2 @@
CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../../screenshots.mk

View File

@ -0,0 +1,17 @@
---
listenAddressSingleHTTPFrontend: 0.0.0.0:11337
logLevel: "WARN"
checkForUpdates: false
showFooter: false
actions:
- title: Save text to file
shell: 'echo "{{ content }}" > file'
arguments:
- type: raw_string_multiline
name: content
title: Content
default: |
Line one of the message
Line two of the message

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,11 @@
[DEFAULT]
base_url = http://localhost:11337/
dir = .
width = 800
height = 520
post_script_sleep = 0.5
[multiline-text]
url = .
name = multiline-text
script = setup_textarea.py

View File

@ -0,0 +1,27 @@
#!/usr/bin/env python3
"""Prepare the multiline textarea argument screenshot."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
def _wait_for_body_attr(driver, attr, timeout=15):
WebDriverWait(driver, timeout).until(
lambda d: bool(d.find_element(By.TAG_NAME, "body").get_attribute(attr))
)
def run(driver):
_wait_for_body_attr(driver, "loaded-dashboard")
driver.find_element(By.CSS_SELECTOR, '[title="Save text to file"]').click()
WebDriverWait(driver, 15).until(
lambda d: len(
d.find_elements(By.CSS_SELECTOR, "#argument-popup textarea#content")
)
>= 1
)
time.sleep(0.2)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,2 @@
custom-webui/
__pycache__/

View File

@ -0,0 +1,2 @@
CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../../screenshots.mk

View File

@ -0,0 +1,25 @@
---
listenAddressSingleHTTPFrontend: 0.0.0.0:11337
logLevel: "WARN"
checkForUpdates: false
showFooter: false
actions:
- title: Placeholder 1
shell: echo "placeholder 1"
- title: Placeholder 2
shell: echo "placeholder 2"
dashboards:
- title: My First Dashboard
contents:
- title: Fieldset 1
type: fieldset
contents:
- title: Folder 1
contents:
- title: Placeholder 1
- title: Folder 2
contents:
- title: Placeholder 2

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -0,0 +1,11 @@
[DEFAULT]
base_url = http://localhost:11337/
dir = .
width = 980
height = 400
post_script_sleep = 0.5
[fieldset]
url = /dashboards/My%%20First%%20Dashboard
name = fieldset
script = setup_fieldset.py

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python3
"""Open My First Dashboard from dashboards/2-fieldsets.adoc."""
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
def _wait_for_fieldset_dashboard(driver, timeout=30):
WebDriverWait(driver, timeout).until(
lambda d: d.find_element(By.TAG_NAME, "body").get_attribute("loaded-dashboard")
== "My First Dashboard"
)
def ready(d):
try:
folder1 = d.find_element(
By.XPATH,
'//button[contains(@class, "directory-button")]//span[contains(@class, "title") and text()="Folder 1"]',
)
folder2 = d.find_element(
By.XPATH,
'//button[contains(@class, "directory-button")]//span[contains(@class, "title") and text()="Folder 2"]',
)
except Exception:
return False
return all(element.is_displayed() for element in (folder1, folder2))
WebDriverWait(driver, timeout).until(ready)
def run(driver):
_wait_for_fieldset_dashboard(driver)
time.sleep(0.2)

View File

@ -0,0 +1,2 @@
custom-webui/
__pycache__/

View File

@ -0,0 +1,2 @@
CONFIGDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
include ../../screenshots.mk

Some files were not shown because too many files have changed in this diff Show More