allwright 0.2.0__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {allwright-0.2.0 → allwright-0.2.1}/PKG-INFO +1 -1
- {allwright-0.2.0 → allwright-0.2.1}/allwright/__init__.py +4 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/_bootstrap.py +1 -1
- {allwright-0.2.0 → allwright-0.2.1}/allwright/_desktop.py +110 -4
- {allwright-0.2.0 → allwright-0.2.1}/allwright/_hooks.py +3 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/client.py +2 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/proto/core/v1/context.proto +7 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/proto/surfaces/desktop/v1/desktop.proto +23 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright.egg-info/PKG-INFO +1 -1
- {allwright-0.2.0 → allwright-0.2.1}/pyproject.toml +1 -1
- {allwright-0.2.0 → allwright-0.2.1}/README.md +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/_browser.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/_config.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/_locator.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/_mobile.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/_mobile_selectors.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/_page.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/_proto.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/_runtime.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/_selectors.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/_transport.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/_types.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/_web_locators.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/proto/core/v1/common.proto +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/proto/core/v1/surface.proto +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/proto/engine/v1/engine.proto +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/proto/surfaces/mobile/v1/mobile.proto +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright/proto/surfaces/web/v1/web.proto +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright.egg-info/SOURCES.txt +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright.egg-info/dependency_links.txt +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright.egg-info/requires.txt +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/allwright.egg-info/top_level.txt +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/setup.cfg +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/tests/test_accessibility_snapshot.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/tests/test_capture.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/tests/test_dialogs.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/tests/test_hooks.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/tests/test_pointer_actions.py +0 -0
- {allwright-0.2.0 → allwright-0.2.1}/tests/test_web_locators.py +0 -0
|
@@ -11,6 +11,8 @@ from .client import (
|
|
|
11
11
|
AndroidDevice,
|
|
12
12
|
NativeApp,
|
|
13
13
|
AndroidApp,
|
|
14
|
+
DesktopApp,
|
|
15
|
+
DesktopWindow,
|
|
14
16
|
DesktopMacConnectOptions,
|
|
15
17
|
DesktopMacLaunchOptions,
|
|
16
18
|
MacApp,
|
|
@@ -81,6 +83,8 @@ __all__ = [
|
|
|
81
83
|
"AndroidDevice",
|
|
82
84
|
"NativeApp",
|
|
83
85
|
"AndroidApp",
|
|
86
|
+
"DesktopApp",
|
|
87
|
+
"DesktopWindow",
|
|
84
88
|
"DesktopMacConnectOptions",
|
|
85
89
|
"DesktopMacLaunchOptions",
|
|
86
90
|
"MacApp",
|
|
@@ -25,7 +25,7 @@ ALLWRIGHT_HOME_ENV_VAR = "ALLWRIGHT_HOME"
|
|
|
25
25
|
ALLWRIGHT_REPOSITORY_ENV_VAR = "ALLWRIGHT_REPOSITORY"
|
|
26
26
|
ALLWRIGHT_VERSION_ENV_VAR = "ALLWRIGHT_VERSION"
|
|
27
27
|
DEFAULT_RELEASE_REPOSITORY = "allwright-dev/allwright"
|
|
28
|
-
DEFAULT_RELEASE_VERSION = "0.2.
|
|
28
|
+
DEFAULT_RELEASE_VERSION = "0.2.1"
|
|
29
29
|
|
|
30
30
|
_bootstrap_lock = threading.Lock()
|
|
31
31
|
_managed_server: subprocess.Popen[bytes] | None = None
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import threading
|
|
4
4
|
|
|
5
|
+
from ._hooks import Hook, HookType
|
|
5
6
|
from ._mobile import NativeApp, NativeLocator
|
|
6
7
|
from ._proto import engine_pb2
|
|
7
8
|
from ._transport import RuntimeClient, StreamHandle
|
|
@@ -26,12 +27,117 @@ class DesktopMacLaunchOptions:
|
|
|
26
27
|
self.timeout_ms = timeout_ms
|
|
27
28
|
|
|
28
29
|
|
|
29
|
-
MacApp = NativeApp
|
|
30
30
|
MacLocator = NativeLocator
|
|
31
|
-
WindowsApp = NativeApp
|
|
32
31
|
WindowsLocator = NativeLocator
|
|
33
32
|
|
|
34
33
|
|
|
34
|
+
class DesktopWindow(NativeApp):
|
|
35
|
+
def __init__(self, runtime: RuntimeClient, surface_session_id: str, session_id: str,
|
|
36
|
+
window_id: str, title: str, is_main: bool) -> None:
|
|
37
|
+
super().__init__(runtime, surface_session_id, session_id)
|
|
38
|
+
self.window_id, self.title, self.is_main = window_id, title, is_main
|
|
39
|
+
|
|
40
|
+
def close(self) -> None:
|
|
41
|
+
with self._lock:
|
|
42
|
+
handle = self._ensure_handle()
|
|
43
|
+
self._ensure_open()
|
|
44
|
+
handle.send(engine_pb2.ContextSessionCommand(
|
|
45
|
+
surface_session_id=self._surface_session_id,
|
|
46
|
+
context_session_id=self._session_id,
|
|
47
|
+
close_desktop_window=engine_pb2.CloseDesktopWindowCommand(),
|
|
48
|
+
))
|
|
49
|
+
while True:
|
|
50
|
+
event = handle.recv("close desktop window")
|
|
51
|
+
kind = event.WhichOneof("event")
|
|
52
|
+
if kind == "desktop_window_closed" and event.desktop_window_closed.window_id == self.window_id:
|
|
53
|
+
self._closed = True
|
|
54
|
+
return
|
|
55
|
+
if kind == "error":
|
|
56
|
+
raise AllwrightError(event.error.message)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class DesktopApp(NativeApp):
|
|
60
|
+
def windows(self) -> list[DesktopWindow]:
|
|
61
|
+
with self._lock:
|
|
62
|
+
handle = self._ensure_handle()
|
|
63
|
+
self._ensure_open()
|
|
64
|
+
handle.send(engine_pb2.ContextSessionCommand(
|
|
65
|
+
surface_session_id=self._surface_session_id,
|
|
66
|
+
context_session_id=self._session_id,
|
|
67
|
+
list_desktop_windows=engine_pb2.ListDesktopWindowsCommand(),
|
|
68
|
+
))
|
|
69
|
+
while True:
|
|
70
|
+
event = handle.recv("list desktop windows")
|
|
71
|
+
kind = event.WhichOneof("event")
|
|
72
|
+
if kind == "desktop_windows_resolved":
|
|
73
|
+
return [self._window(result) for result in event.desktop_windows_resolved.windows]
|
|
74
|
+
if kind == "error":
|
|
75
|
+
raise AllwrightError(event.error.message)
|
|
76
|
+
|
|
77
|
+
def main_window(self) -> DesktopWindow:
|
|
78
|
+
for window in self.windows():
|
|
79
|
+
if window.is_main:
|
|
80
|
+
return window
|
|
81
|
+
raise AllwrightError("desktop application does not currently expose a main window")
|
|
82
|
+
|
|
83
|
+
def register_hook(self, hook_type: HookType):
|
|
84
|
+
if hook_type.name != "window":
|
|
85
|
+
return super().register_hook(hook_type)
|
|
86
|
+
with self._lock:
|
|
87
|
+
handle = self._ensure_handle()
|
|
88
|
+
self._ensure_open()
|
|
89
|
+
handle.send(engine_pb2.ContextSessionCommand(
|
|
90
|
+
surface_session_id=self._surface_session_id,
|
|
91
|
+
context_session_id=self._session_id,
|
|
92
|
+
register_hook=engine_pb2.RegisterHookCommand(
|
|
93
|
+
desktop_window=engine_pb2.RegisterDesktopWindowHook()
|
|
94
|
+
),
|
|
95
|
+
))
|
|
96
|
+
while True:
|
|
97
|
+
event = handle.recv("register desktop window hook")
|
|
98
|
+
kind = event.WhichOneof("event")
|
|
99
|
+
if kind == "hook_registered":
|
|
100
|
+
return Hook(self, event.hook_registered.hook_id, hook_type)
|
|
101
|
+
if kind == "error":
|
|
102
|
+
raise AllwrightError(event.error.message)
|
|
103
|
+
|
|
104
|
+
def _wait_for_hook(self, hook_id, hook_type, options):
|
|
105
|
+
if hook_type.name != "window":
|
|
106
|
+
return super()._wait_for_hook(hook_id, hook_type, options)
|
|
107
|
+
from ._runtime import retry_options
|
|
108
|
+
|
|
109
|
+
with self._lock:
|
|
110
|
+
handle = self._ensure_handle()
|
|
111
|
+
self._ensure_open()
|
|
112
|
+
handle.send(engine_pb2.ContextSessionCommand(
|
|
113
|
+
surface_session_id=self._surface_session_id,
|
|
114
|
+
context_session_id=self._session_id,
|
|
115
|
+
wait_for_hook=engine_pb2.WaitForHookCommand(
|
|
116
|
+
hook_id=hook_id,
|
|
117
|
+
retry_options=retry_options(getattr(options, "timeout_ms", None)),
|
|
118
|
+
),
|
|
119
|
+
))
|
|
120
|
+
while True:
|
|
121
|
+
event = handle.recv("wait for desktop window hook")
|
|
122
|
+
kind = event.WhichOneof("event")
|
|
123
|
+
if kind == "hook_completed" and event.hook_completed.hook_id == hook_id:
|
|
124
|
+
if event.hook_completed.WhichOneof("result") != "desktop_window":
|
|
125
|
+
raise AllwrightError("desktop window hook returned an invalid result")
|
|
126
|
+
return self._window(event.hook_completed.desktop_window.window)
|
|
127
|
+
if kind == "error":
|
|
128
|
+
raise AllwrightError(event.error.message)
|
|
129
|
+
|
|
130
|
+
def _window(self, result) -> DesktopWindow:
|
|
131
|
+
if not result.context_session_id or not result.window_id:
|
|
132
|
+
raise AllwrightError("desktop window result is missing its context or window id")
|
|
133
|
+
return DesktopWindow(self._runtime, self._surface_session_id, result.context_session_id,
|
|
134
|
+
result.window_id, result.title, result.is_main)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
MacApp = DesktopApp
|
|
138
|
+
WindowsApp = DesktopApp
|
|
139
|
+
|
|
140
|
+
|
|
35
141
|
class MacDesktop:
|
|
36
142
|
def __init__(
|
|
37
143
|
self,
|
|
@@ -47,7 +153,7 @@ class MacDesktop:
|
|
|
47
153
|
self._closed = False
|
|
48
154
|
self._session_id = session_id
|
|
49
155
|
self._surface_session_id = surface_session_id
|
|
50
|
-
self._app =
|
|
156
|
+
self._app = DesktopApp(runtime, surface_session_id, initial_app_session_id)
|
|
51
157
|
|
|
52
158
|
@property
|
|
53
159
|
def session_id(self) -> str:
|
|
@@ -73,7 +179,7 @@ class MacDesktop:
|
|
|
73
179
|
event = self._stream.recv("receive macOS app launch event")
|
|
74
180
|
match event.WhichOneof("event"):
|
|
75
181
|
case "desktop_app_launched":
|
|
76
|
-
self._app =
|
|
182
|
+
self._app = DesktopApp(
|
|
77
183
|
self._runtime,
|
|
78
184
|
self._surface_session_id,
|
|
79
185
|
event.desktop_app_launched.app_session_id,
|
|
@@ -4,6 +4,7 @@ from dataclasses import dataclass
|
|
|
4
4
|
from typing import TYPE_CHECKING, Generic, Iterable, TypeVar
|
|
5
5
|
|
|
6
6
|
if TYPE_CHECKING:
|
|
7
|
+
from ._desktop import DesktopWindow
|
|
7
8
|
from ._page import Page
|
|
8
9
|
from ._types import CommandOptions
|
|
9
10
|
|
|
@@ -80,12 +81,14 @@ class _Hooks:
|
|
|
80
81
|
new_page: HookType[Page]
|
|
81
82
|
file_chooser: HookType[FileChooser]
|
|
82
83
|
download: HookType[Download]
|
|
84
|
+
window: HookType[DesktopWindow]
|
|
83
85
|
|
|
84
86
|
def __init__(self) -> None:
|
|
85
87
|
self.dialog = HookType("dialog")
|
|
86
88
|
self.new_page = HookType("new_page")
|
|
87
89
|
self.file_chooser = HookType("file_chooser")
|
|
88
90
|
self.download = HookType("download")
|
|
91
|
+
self.window = HookType("window")
|
|
89
92
|
|
|
90
93
|
|
|
91
94
|
hooks = _Hooks()
|
|
@@ -6,6 +6,7 @@ option java_package = "dev.allwright.engine.v1";
|
|
|
6
6
|
option java_multiple_files = true;
|
|
7
7
|
|
|
8
8
|
import "core/v1/common.proto";
|
|
9
|
+
import "surfaces/desktop/v1/desktop.proto";
|
|
9
10
|
import "surfaces/mobile/v1/mobile.proto";
|
|
10
11
|
import "surfaces/web/v1/web.proto";
|
|
11
12
|
|
|
@@ -40,6 +41,8 @@ message ContextSessionCommand {
|
|
|
40
41
|
ResolveFrameCommand resolve_frame = 26;
|
|
41
42
|
CaptureCommand capture = 27;
|
|
42
43
|
HandleDialogCommand handle_dialog = 28;
|
|
44
|
+
ListDesktopWindowsCommand list_desktop_windows = 29;
|
|
45
|
+
CloseDesktopWindowCommand close_desktop_window = 30;
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
|
|
@@ -82,6 +85,8 @@ message ContextSessionEvent {
|
|
|
82
85
|
FrameResolvedEvent frame_resolved = 28;
|
|
83
86
|
CaptureResolvedEvent capture_resolved = 29;
|
|
84
87
|
DialogHandledEvent dialog_handled = 30;
|
|
88
|
+
DesktopWindowsResolvedEvent desktop_windows_resolved = 31;
|
|
89
|
+
DesktopWindowClosedEvent desktop_window_closed = 32;
|
|
85
90
|
}
|
|
86
91
|
}
|
|
87
92
|
|
|
@@ -109,6 +114,7 @@ message RegisterHookCommand {
|
|
|
109
114
|
RegisterMobileFileChooserHook mobile_file_chooser = 4;
|
|
110
115
|
RegisterMobileDownloadHook mobile_download = 5;
|
|
111
116
|
RegisterDialogHook dialog = 6;
|
|
117
|
+
RegisterDesktopWindowHook desktop_window = 7;
|
|
112
118
|
}
|
|
113
119
|
}
|
|
114
120
|
|
|
@@ -131,6 +137,7 @@ message HookCompletedEvent {
|
|
|
131
137
|
MobileFileChooserHookResult mobile_file_chooser = 5;
|
|
132
138
|
MobileDownloadHookResult mobile_download = 6;
|
|
133
139
|
DialogHookResult dialog = 7;
|
|
140
|
+
DesktopWindowHookResult desktop_window = 8;
|
|
134
141
|
}
|
|
135
142
|
}
|
|
136
143
|
|
|
@@ -39,3 +39,26 @@ message DesktopAppLaunchedEvent {
|
|
|
39
39
|
string app_id = 2;
|
|
40
40
|
string note = 3;
|
|
41
41
|
}
|
|
42
|
+
|
|
43
|
+
message ListDesktopWindowsCommand {}
|
|
44
|
+
message CloseDesktopWindowCommand {}
|
|
45
|
+
message RegisterDesktopWindowHook {}
|
|
46
|
+
|
|
47
|
+
message DesktopWindowInfo {
|
|
48
|
+
string context_session_id = 1;
|
|
49
|
+
string window_id = 2;
|
|
50
|
+
string title = 3;
|
|
51
|
+
bool is_main = 4;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
message DesktopWindowsResolvedEvent {
|
|
55
|
+
repeated DesktopWindowInfo windows = 1;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
message DesktopWindowClosedEvent {
|
|
59
|
+
string window_id = 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
message DesktopWindowHookResult {
|
|
63
|
+
DesktopWindowInfo window = 1;
|
|
64
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|