78 lines
3.9 KiB
Python
78 lines
3.9 KiB
Python
"""Private GTK update confirmation and keyboard workflow; no installed packages."""
|
|
import select
|
|
|
|
from native_x11 import wait_for
|
|
|
|
|
|
def update_snapshot():
|
|
repositoryUrl = "https://example.org/tentacles"
|
|
return {
|
|
"Entries": [{"Entry": {
|
|
"ID": "probe", "LocalID": "echo", "Name": "Update probe",
|
|
"Repository": repositoryUrl, "RepositoryID": "example.source",
|
|
"Version": "1.0", "Enabled": True, "Presentation": True,
|
|
}, "State": "active"}],
|
|
"Repositories": [{"URL": repositoryUrl, "ID": "example.source", "Name": "Example",
|
|
"Tentacles": [{"ID": "echo", "Name": "Update probe", "Version": "1.1",
|
|
"Capabilities": ["presentation", "interaction"]}]}],
|
|
"Updates": [{"ID": "probe", "LocalID": "echo", "Name": "Update probe",
|
|
"InstalledVersion": "1.0", "AvailableVersion": "1.1",
|
|
"RepositoryURL": repositoryUrl, "RepositoryID": "example.source"}],
|
|
}
|
|
|
|
|
|
def check_page(pyatspi, process, window, named, walk, focus, key, request, send, initial):
|
|
tab = next(node for node in walk(window)
|
|
if node.name == "Tentacles" and node.getRole() == pyatspi.ROLE_PAGE_TAB)
|
|
assert tab.parent.querySelection().selectChild(tab.getIndexInParent()), "cannot select Tentacles tab"
|
|
table = next(node for node in walk(window)
|
|
if node.name == "Tentacles" and node.getRole() == pyatspi.ROLE_TABLE)
|
|
focus(table)
|
|
key("Home")
|
|
update = named("Update")
|
|
wait_for(lambda: update.getState().contains(pyatspi.STATE_SENSITIVE), "Update did not enable")
|
|
# Tab from the table skips disabled Install and reaches Update.
|
|
key("Tab")
|
|
wait_for(lambda: update.getState().contains(pyatspi.STATE_FOCUSED), "Tab did not reach Update")
|
|
key("Tab")
|
|
wait_for(lambda: named("Settings").getState().contains(pyatspi.STATE_FOCUSED), "Update trapped Tab")
|
|
key("shift+Tab")
|
|
wait_for(lambda: update.getState().contains(pyatspi.STATE_FOCUSED), "Shift+Tab did not reach Update")
|
|
|
|
def confirmation():
|
|
for app in pyatspi.Registry.getDesktop(0):
|
|
for node in app:
|
|
if node and node.getRole() in (pyatspi.ROLE_DIALOG, pyatspi.ROLE_ALERT):
|
|
if any("Current capabilities:" in child.name for child in walk(node)):
|
|
return node
|
|
return None
|
|
|
|
key("space")
|
|
dialog = wait_for(confirmation, "keyboard activation did not open confirmation")
|
|
message = " ".join(node.name for node in walk(dialog))
|
|
assert "version 1.0 to version 1.1" in message, message
|
|
assert "Current capabilities: presentation." in message, message
|
|
assert "Repository declares: presentation, interaction." in message, message
|
|
cancel = next(node for node in walk(dialog) if node.name == "Cancel")
|
|
focus(cancel)
|
|
key("space")
|
|
wait_for(lambda: not confirmation(), "Cancel did not close confirmation")
|
|
assert not select.select([process.stdout], [], [], 0.2)[0], "Cancel sent a management request"
|
|
|
|
# The native button action uses the same path as keyboard activation.
|
|
update.queryAction().doAction(0)
|
|
dialog = wait_for(confirmation, "button action did not open confirmation")
|
|
accept = next(node for node in walk(dialog)
|
|
if node.name == "Update" and node.getRole() == pyatspi.ROLE_PUSH_BUTTON)
|
|
focus(accept)
|
|
key("Return")
|
|
value = request()
|
|
assert value["Tentacle"] == {"Action": "upgrade", "ID": "echo", "Entry": None,
|
|
"URL": "https://example.org/tentacles"}, value["Tentacle"]
|
|
send(dict(initial, Message="Update accepted."))
|
|
wait_for(lambda: update.getState().contains(pyatspi.STATE_SENSITIVE), "response left controls busy")
|
|
focus(named("Cancel"))
|
|
key("space")
|
|
assert process.wait(timeout=5) == 0
|
|
print("PASS: Update Tab/Shift+Tab, keyboard/button activation, versions/capabilities, Cancel, upgrade request")
|